diff options
| author | Simeon Simeonov | 2020-04-06 10:28:46 +0200 |
|---|---|---|
| committer | Simeon Simeonov | 2020-04-06 10:28:46 +0200 |
| commit | e297c0696cf594f8f700dd82fdcdda582e348eda (patch) | |
| tree | deea1cfef4041dc492a38d8304c20be14d18c8ba /README.md | |
| parent | 097aaa49fa99cffec9db74432d7025457c34199e (diff) | |
Add a simple CLI interface with tests and update README.md
Diffstat (limited to 'README.md')
| -rw-r--r-- | README.md | 93 |
1 files changed, 86 insertions, 7 deletions
| @@ -70,20 +70,99 @@ one-time password." | |||
| 70 | 70 | ||
| 71 | ## Examples | 71 | ## Examples |
| 72 | 72 | ||
| 73 | We define the two entities: *client* and *server*. The entire application of | ||
| 74 | RFC-2289 consists of interactions between them. | ||
| 75 | |||
| 73 | ```python | 76 | ```python |
| 74 | import getpass | 77 | # |
| 78 | import getpass # client only | ||
| 79 | |||
| 80 | import otp2289 # client and server | ||
| 75 | 81 | ||
| 76 | import otp2289 | 82 | # the server starts by picking: |
| 83 | # - algorithm (MD5 or SHA1) to use | ||
| 84 | # - seed - 1 to 16 alphanumeric characters. The seed must never be reused. | ||
| 85 | # - initial step - number (int) that will be decremented for each OTP. | ||
| 86 | # In FreeBSD, the following default values are used: | ||
| 87 | # - MD5 | ||
| 88 | # - the first two letters of the hostname + 5 random digits for seed | ||
| 89 | # - initial step: 500 | ||
| 77 | 90 | ||
| 78 | # create a generator object | 91 | # the client receives those values, chooses a strong password and creates |
| 79 | passwd_bytes = getpass.getpass().encode() # Type: This is a test. | 92 | # initialization digest (hash). The password 'This is a test.' will give you |
| 93 | # the same results as in the following example. | ||
| 94 | passwd_bytes = getpass.getpass().encode() # Fetch the password as bytes | ||
| 80 | generator = otp2289.generator.OTPGenerator(passwd_bytes, | 95 | generator = otp2289.generator.OTPGenerator(passwd_bytes, |
| 81 | 'TesT', | 96 | 'TesT', |
| 82 | otp2289.OTP_ALGO_MD5) | 97 | otp2289.OTP_ALGO_MD5) |
| 83 | generator.generate_otp_hexdigest(0) | 98 | digest = generator.generate_otp_hexdigest(500) |
| 84 | generator.gen.generate_otp_words(0) | 99 | # digest is now: 0x2b8d82b6ac14346c |
| 100 | # the client sends it to the server | ||
| 101 | |||
| 102 | # the server creates the first state. Note that step is decremented by 1: | ||
| 103 | state = otp2289.server.OTPState(digest, 499, 'TesT', otp2289.OTP_ALGO_MD5) | ||
| 104 | # the state can be stored in a OTPStore container: | ||
| 105 | store = otp2289.server.OTPStore() | ||
| 106 | # key can be any str that can be used to reference the state (f.i username) | ||
| 107 | store.add_state('myusername', state) # where key can be any str that can be | ||
| 108 | # OTPStore is provided only for convenience as it is not part of RFC-2289. | ||
| 109 | # The server can store states any way it wants. A normal dict is also fine. | ||
| 110 | # Once the initial state is set on the server, the client can authenticate. | ||
| 111 | |||
| 112 | # Upon authentication request (f.i. login), the server issues a challenge | ||
| 113 | # based on the state: | ||
| 114 | challenge = state.challenge_string # challenge is now 'otp-md5 499 TesT ' | ||
| 115 | |||
| 116 | # the client can now respond by using (or recreating) the same generator | ||
| 117 | # created earlier. RFC-2289 defines two types of responses: | ||
| 118 | # - hex (like '0x2b8d82b6ac14346c') - more suited for automation | ||
| 119 | # - tokens consisting of 6 short words - better when responding manually | ||
| 120 | hex_response = generator.generate_otp_hexdigest(499) # '0x6323f96296a2526b' | ||
| 121 | token_response = generator.generate_otp_words(499) | ||
| 122 | # token_response is now: 'CANT JAW BITS NU LO PUP' | ||
| 123 | # a possible shortcut may be to use the challenge-string directly: | ||
| 124 | hex_response = generator.generate_otp_hexdigest_from_challenge(challenge) | ||
| 125 | token_response = generator.generate_otp_words_from_challenge(challenge) | ||
| 126 | # ... giving the same results. | ||
| 127 | |||
| 128 | # once the response is received, the server validates it by yet again using | ||
| 129 | # the current state: | ||
| 130 | result = state.response_validates(hex_response) | ||
| 131 | # or | ||
| 132 | result = state.response_validates(token_response) | ||
| 133 | # result should be True if the response matches the state, False if not | ||
| 134 | # in case of invalid response or response checksum doesn't match, a | ||
| 135 | # otp2289.server.OTPInvalidResponse exception is raised. | ||
| 136 | |||
| 137 | # once the state has successfully validated the corresponding response, | ||
| 138 | the state **must never be used again** and a state corresponding to the | ||
| 139 | "next" (498) step created. | ||
| 140 | state = state.get_next_state() | ||
| 141 | |||
| 142 | # the next authentication attempt... | ||
| 143 | challenge = state.challenge_string # challenge is now 'otp-md5 498 TesT ' | ||
| 144 | # ... and on the client side... | ||
| 145 | hex_response = generator.generate_otp_hexdigest_from_challenge(challenge) | ||
| 146 | # etc. etc... | ||
| 85 | ``` | 147 | ``` |
| 86 | 148 | ||
| 149 | If you don't care about developing applications in Python and only care about | ||
| 150 | generating one-time passwords (tokens / hex digests) and authenticating with | ||
| 151 | existing solutions (f.i. FreeBSD servers), pyotp2289 comes with a simple CLI: | ||
| 152 | |||
| 153 | ```bash | ||
| 154 | python -m otp2289 --generate-otp-response -f token -i 498 -s TesT | ||
| 155 | ``` | ||
| 156 | |||
| 157 | ... will prompt for password and generate a 6 words (token) response. | ||
| 158 | |||
| 159 | ```bash | ||
| 160 | python -m otp2289 --generate-otp-range -f token -i 498 -s TesT | ||
| 161 | ``` | ||
| 162 | |||
| 163 | ... will prompt for password and generate a range of 4 one-time passwords | ||
| 164 | starting from (and including) 498. | ||
| 165 | |||
| 87 | 166 | ||
| 88 | ## Author | 167 | ## Author |
| 89 | 168 | ||
| @@ -95,5 +174,5 @@ Simeon Simeonov - sgs @ Freenode | |||
| 95 | Copyright (c) 2020, Simeon Simeonov | 174 | Copyright (c) 2020, Simeon Simeonov |
| 96 | All rights reserved. | 175 | All rights reserved. |
| 97 | 176 | ||
| 98 | [licensed](LICENSE) under the BSD 2-clause. | 177 | [Licensed](LICENSE) under the BSD 2-clause. |
| 99 | SPDX-License-Identifier: BSD-2-Clause-FreeBSD | 178 | SPDX-License-Identifier: BSD-2-Clause-FreeBSD |
