Skip to content

Commit 083ff3d

Browse files
author
Dave Hein
committed
Initial checkin
0 parents  commit 083ff3d

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

67 files changed

+17800
-0
lines changed

.gitignore

+122
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
# -------------------------------------------------------
2+
# Project specific stuff
3+
#
4+
5+
/build
6+
/cli.py
7+
/dist
8+
/hotp.egg-info
9+
10+
# end Project specific stuff
11+
# -------------------------------------------------------
12+
13+
14+
# -------------------------------------------------------
15+
# Miscellaneous stuff
16+
17+
# Apple detrius
18+
#
19+
.DS_Store
20+
21+
# Sublime Text
22+
#
23+
*.sublime-workspace
24+
25+
# end Miscellaneous stuff
26+
# -------------------------------------------------------
27+
28+
# -------------------------------------------------------
29+
# Virtual environment stuff
30+
#
31+
deactivate-project.src
32+
deactivate-project.ps1
33+
venv*/
34+
35+
# end Virtual environment stuff
36+
# -------------------------------------------------------
37+
38+
39+
# -------------------------------------------------------
40+
# Python stuff
41+
#
42+
43+
# Python linter
44+
#
45+
fixme.lint.txt
46+
47+
# Byte-compiled / optimized / DLL files
48+
#
49+
__pycache__/
50+
*.py[cod]
51+
*$py.class
52+
53+
# C extensions
54+
#
55+
*.so
56+
57+
# Distribution / packaging
58+
#
59+
.Python
60+
env/
61+
build/
62+
develop-eggs/
63+
dist/
64+
downloads/
65+
eggs/
66+
.eggs/
67+
lib/
68+
lib64/
69+
parts/
70+
sdist/
71+
var/
72+
*.egg-info/
73+
.installed.cfg
74+
*.egg
75+
76+
# PyInstaller
77+
# Usually these files are written by a python script from a template
78+
# before PyInstaller builds the exe, so as to inject date/other infos into it.
79+
#
80+
*.manifest
81+
*.spec
82+
83+
# Installer logs
84+
#
85+
pip-log.txt
86+
pip-delete-this-directory.txt
87+
88+
# Unit test / coverage reports
89+
#
90+
htmlcov/
91+
.tox/
92+
.coverage
93+
.coverage.*
94+
.cache
95+
nosetests.xml
96+
coverage.xml
97+
*,cover
98+
.hypothesis/
99+
100+
# Translations
101+
#
102+
*.mo
103+
*.pot
104+
105+
# Django stuff:
106+
#
107+
*.log
108+
109+
# Sphinx documentation
110+
#
111+
docs/_build/
112+
113+
# PyBuilder
114+
#
115+
target/
116+
117+
#Ipython Notebook
118+
#
119+
.ipynb_checkpoints
120+
121+
# end Python stuff
122+
# -------------------------------------------------------

LICENSE

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
The MIT License (MIT)
2+
3+
Copyright (c) 2016 Dave Hein
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

README.md

+239
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,239 @@
1+
# Authenticator
2+
3+
`authenticator` is a CLI analog to the Google Authenticator phone app, or the LastPass Authenticator phone app. It is a TOTP/HOTP client that can generate the numeric codes needed for authentication with sites that support Two-Factor Authentication (TFA) or Multi-Factor Authentication (MFA).
4+
5+
* [Benefit](#benefit)
6+
* [Authentication secret (READ THIS)](#authentication-secret-read-this)
7+
* [System Requirements](#system-requirements)
8+
* [Installation](#installation)
9+
* [Usage](#usage)
10+
* [Implementation](#implementation)
11+
* [Development](#development)
12+
* [License](#license)
13+
14+
## Benefit
15+
16+
The benefit of using `authenticator` over a phone app is that this CLI utility can run anywhere Python 3.5 can run from a command line interface (e.g., a terminal window), and the database of accounts and secrets is a platform-independent passphrase-protected encrypted file that can be backed up and can be copied to multiple systems without fear of bad actors gaining access to the second factor authentication.
17+
18+
Another benefit is that `authenticator` can act as a backup in case you loose your phone or tablet (running Google Authenticator) or Google breaks the app or withdraws it.
19+
20+
Personally, I use both Google Authenticator on my iPhone and iPad, and run `authenticator` on several different computer systems. I keep a copy of the accounts file in a variety of places. If my phone bricks, is lost or stolen, I can still get access to my TFA-protected accounts if I can access any of those other computers, or any other computer on which I can install and run `authenticator` and access a copy of my accounts file.
21+
22+
## Authentication secret (READ THIS)
23+
24+
TFA/MFA clients that produce a 'one time' numeric code require a secret that they share with the server validating the authentication. Typically this secret is generated by the server and captured by the client in the form of a QR code that can be scanned by the client app on your phone.
25+
26+
But this CLI utility doesn't have the ability to capture or process QR images. It needs the secret in the form of a text string. If the server cannot provide a text string equivalent of the shared secret then you cannot use `authenticator` to generate one-time codes for that account.
27+
28+
Each account will use a different secret, a different text string. If you are already using a phone app to generate one-time TFA codes for some accounts then you'll need to generate new secrets for those accounts (and update the info in the phone app) so that you can capture the secret as a text string.
29+
30+
### GMail TFA example
31+
32+
If you enable Two-Factor Authentication for GMail, you'll go through a setup process that first configures your account to send your phone a code by text or voice. At the end of the setup you'll have an opportunity to add Google Authenticator as an alternative code generator. Select that and scan the QR code with your phone's Authenticator app (Google Authenticator, LastPass Authenticator, et cetera). Then click the link labeled "CAN'T SCAN IT" under the QR code ... that will give you a 32-character secret string that you'll use to add configure this account in `authenticator`.
33+
34+
It is important that you capture the QR code and _then_ click the link for the secret string secret code. If you capture the secret and click "back" to get the QR code then a new secret will be generated and the new QR won't match the previously captured secret string. (With Google Authenticator you actually don't need the QR code; you can also provide the secret as a text string using the 'manual entry' option when adding an account to the app. LastPass Authenticator doesn't have that option.)
35+
36+
You add the account and secret to `authenticator` like so:
37+
38+
```nohighlight
39+
$ authenticator add Google:[email protected]
40+
Enter passphrase:
41+
Enter shared secret: xj6p kokw ipvk usc6 bveu sz3b csir xhbu
42+
OK
43+
```
44+
45+
You then generate codes like so (use Ctrl-C to stop the generation):
46+
47+
```nohighlight
48+
$ authenticator generate
49+
Enter passphrase:
50+
Google:[email protected] 162534 (expires in 12 seconds)
51+
52+
Google:[email protected] 162534 (expires in 7 seconds)
53+
54+
Google:[email protected] 162534 (expires in 2 seconds)
55+
56+
Google:[email protected] 996752 (expires in 27 seconds)
57+
58+
Google:[email protected] 996752 (expires in 22 seconds)
59+
^C
60+
```
61+
62+
## System Requirements
63+
64+
This requires Python 3.5 or later.
65+
66+
It has been tested on OS X 11.9.5, Windows 10, and Ubuntu 14.04. As none of those systems come with Python 3.5 out of the box, you'll need to install that yourself.
67+
68+
And I recommend setting up a Python 3.5 virtual environment in which to install `authenticator`.
69+
70+
## Installation
71+
72+
Installation is simple.
73+
74+
```bash
75+
pip install authenticator
76+
77+
authenticator --help
78+
```
79+
80+
## Usage
81+
82+
### Add an account
83+
84+
To add a new account, do something like:
85+
86+
```nohighlight
87+
authenticator add Google:[email protected]
88+
```
89+
90+
You can use any string there as the name. I recommend the format 'vendor:userid', where _vendor_ is some string indicating the organization or server that will check your TFA credentials, and _userid_ is the user account id that is being authenticated.
91+
92+
You'll get prompted for the passphrase to unlock the file in which all the account secrets are stored. And then you'll get prompted for a secret string.
93+
94+
### Generate current codes
95+
96+
To get the current code for all the accounts, do:
97+
98+
```nohighlight
99+
authenticator generate
100+
```
101+
102+
You'll get prompted for the passphrase, and then the program will start generating the current passcode for all the accounts. It will continue to generate current codes every 5 seconds until you stop it with ctrl-C.
103+
104+
### Other commands and options
105+
106+
There's a lot more, just enter `authenticator --help` for a list of all the commands and something like `authenticator add --help` for detailed help on a specific command.
107+
108+
```nohighlight
109+
$ authenticator --help
110+
usage: authenticator [-h] [--version] [--data ALTDATAFILE]
111+
{add,delete,del,generate,gen,info,list,set} ...
112+
113+
Run or manage HOTP/TOTP calculations
114+
115+
optional arguments:
116+
-h, --help show this help message and exit
117+
--version show the software version
118+
--data ALTDATAFILE Specify the path to an alternate data file
119+
120+
Sub-commands:
121+
Valid Sub-Commands
122+
123+
{add,delete,del,generate,gen,info,list,set}
124+
Sub-command Help
125+
add add a HOTP/TOTP configuration
126+
delete (del) delete a HOTP/TOTP configuration
127+
generate (gen) generate passwords for one or more HOTP/TOTP
128+
configurations
129+
info show information about this software and your data
130+
list list HOTP/TOTP configurations
131+
set set HOPT configuration values
132+
```
133+
134+
```nohighlight
135+
$ authenticator add --help
136+
usage: authenticator add [-h] [--counter COUNTER] [--length PASSWORDLENGTH]
137+
[--period PERIOD]
138+
clientIdToAdd
139+
140+
Add a new HOTP/TOTP configuration to the data file.
141+
142+
positional arguments:
143+
clientIdToAdd a unique identifier for the HOTP/TOTP configuration
144+
145+
optional arguments:
146+
-h, --help show this help message and exit
147+
--counter COUNTER initial counter value for a counter-based HOTP
148+
calculation (no default)
149+
--length PASSWORDLENGTH
150+
length of the generated password (default: 6)
151+
--period PERIOD length of the time period in seconds for a time-based
152+
HOTP calculation (default: 30)
153+
```
154+
155+
## Implementation details
156+
157+
This is a simple attempt to implement the "Pseudocode for Time OTP"and "Pseudocode for Event/Counter OTP" given in the [Wikipedia article on Google Authenticator][gauth]. That pseudocode is reproduced here ...
158+
159+
[gauth]: https://en.wikipedia.org/wiki/Google_Authenticator
160+
161+
### Pseudocode for Time OTP
162+
163+
```nohighlight
164+
function GoogleAuthenticatorCode(string secret)
165+
key := base32decode(secret)
166+
message := current Unix time ÷ 30
167+
hash := HMAC-SHA1(key, message)
168+
offset := last nibble of hash
169+
//4 bytes starting at the offset
170+
truncatedHash := hash[offset..offset+3]
171+
//remove the most significant bit
172+
Set the first bit of truncatedHash to zero
173+
code := truncatedHash mod 1000000
174+
pad code with 0 until length of code is 6
175+
return code
176+
```
177+
178+
### Pseudocode for Event/Counter OTP
179+
180+
```nohighlight
181+
function GoogleAuthenticatorCode(string secret)
182+
key := base32decode(secret)
183+
message := counter encoded on 8 bytes
184+
hash := HMAC-SHA1(key, message)
185+
offset := last nibble of hash
186+
//4 bytes starting at the offset
187+
truncatedHash := hash[offset..offset+3]
188+
//remove the most significant bit
189+
Set the first bit of truncatedHash to zero
190+
code := truncatedHash mod 1000000
191+
pad code with 0 until length of code is 6
192+
return code
193+
```
194+
195+
I've validated the pseudocode and this implementation against [RFC6238][rfc6238] (TOTP), [RFC4226][rfc4226] (HOTP) and [RFC4648][rfc4648] (Base32 encoding).
196+
197+
[rfc6238]: http://tools.ietf.org/html/rfc6238
198+
[rfc4226]: http://tools.ietf.org/html/rfc4226
199+
[rfc4648]: http://tools.ietf.org/html/rfc4648
200+
201+
### Dependencies
202+
203+
This implementation requires:
204+
205+
* Python 3.5 or later
206+
* [cryptography 1.4][cryptography]
207+
* [iso8601 0.1.11][iso8601]
208+
209+
[cryptography]: https://pypi.python.org/pypi/cryptography/1.4
210+
[iso8601]: https://pypi.python.org/pypi/iso8601/0.1.11
211+
212+
## Development
213+
214+
To setup the development environment on OS X, clone the repo from GitHub, and then `cd` in Terminal to the root of the cloned repository and do:
215+
216+
1. `dev/venv/make-venv.sh`
217+
2. `. dev/venv/activate-project.src`
218+
3. `dev/venv/provision-venv.sh`
219+
4. `dev/lint.sh`
220+
5. `dev/runtests.sh`
221+
222+
To setup the development environment on OS X, clone the repo from GitHub, and then `cd` in Terminal to the root of the cloned repository and do:
223+
224+
1. `dev/venv/make-venv.ps1`
225+
2. `dev/venv/activate-project.ps1`
226+
3. `dev/venv/provision-venv.ps1`
227+
4. `dev/lint.ps1`
228+
5. `dev/runtests.ps1`
229+
230+
You can find out more about why the virtual environment is setup and managed that way by looking at these blog posts:
231+
232+
* [Using Virtual Environments - Python I](https://www.develves.net/blogs/asd/2016-02-11-using-virtual-environments-python-1/)
233+
* [Using Virtual Environments - Python II](https://www.develves.net/blogs/asd/2016-02-25-using-virtual-environments-python-2/)
234+
235+
I build the distribution using `dev/build/make-package.sh`.
236+
237+
## License
238+
239+
This project uses the MIT license.

0 commit comments

Comments
 (0)