summaryrefslogtreecommitdiff
path: root/src/otp2289/__main__.py
diff options
context:
space:
mode:
Diffstat (limited to 'src/otp2289/__main__.py')
-rw-r--r--src/otp2289/__main__.py153
1 files changed, 89 insertions, 64 deletions
diff --git a/src/otp2289/__main__.py b/src/otp2289/__main__.py
index 0fdcc52..c5d28b6 100644
--- a/src/otp2289/__main__.py
+++ b/src/otp2289/__main__.py
@@ -51,6 +51,56 @@ def eprint(
51 print(*value, sep=sep, end=end, file=sys.stderr, flush=True) 51 print(*value, sep=sep, end=end, file=sys.stderr, flush=True)
52 52
53 53
54def get_rnd_seed() -> str:
55 """
56 Returns a random seed in the format:
57
58 2 random letters (capitalize()) + 5 random digits
59 """
60 rnd = secrets.SystemRandom()
61 return ''.join(
62 rnd.choices(string.ascii_lowercase, k=2)
63 ).capitalize() + ''.join(rnd.choices(string.digits, k=5))
64
65
66def initiate_new_sequence(args: argparse.Namespace) -> str:
67 """
68 Generates a new sequence based on the parameters sent from the parser.
69
70 :param args: The arguments assigned from argparse
71 :type args: argparse.Namespace
72
73 :raises otp2289.OTPChallengeError: If the challenge is invalid
74
75 :raises otp2289.OTPGeneratorError: If generator parameters are wrong
76
77 :raises otp2289.OTPResponseError: If tokens or hex can not be generated
78
79 :return: The response string
80 :rtype: str
81 """
82 if not args.seed:
83 args.seed = get_rnd_seed()
84
85 header = ''
86 if not args.quiet:
87 header = (
88 f'Seed: {args.seed}, Step: {args.step}, '
89 f'Hash: {args.hash_algo}{os.linesep}'
90 )
91 generator = otp2289.OTPGenerator(
92 args.password.encode(), args.seed, args.hash_algo
93 )
94 if args.challenge_string:
95 return (
96 header
97 + generator.generate_otp_response_from_challenge(
98 args.challenge_string
99 ).hexdigest
100 )
101 return header + generator.generate_otp_response(args.step).hexdigest
102
103
54def generate_otp_response(args: argparse.Namespace) -> str: 104def generate_otp_response(args: argparse.Namespace) -> str:
55 """ 105 """
56 Generates a response based on the parameters sent from the parser 106 Generates a response based on the parameters sent from the parser
@@ -62,20 +112,22 @@ def generate_otp_response(args: argparse.Namespace) -> str:
62 112
63 :raises otp2289.OTPGeneratorError: If generator parameters are wrong 113 :raises otp2289.OTPGeneratorError: If generator parameters are wrong
64 114
115 :raises otp2289.OTPResponseError: If tokens or hex can not be generated
116
65 :return: The response string 117 :return: The response string
66 :rtype: str 118 :rtype: str
67 """ 119 """
68 generator = otp2289.generator.OTPGenerator( 120 generator = otp2289.OTPGenerator(
69 args.password.encode(), args.seed, args.hash_algo 121 args.password.encode(), args.seed, args.hash_algo
70 ) 122 )
71 if args.challenge_string: 123 if args.challenge_string:
72 if args.output_format == 'token': 124 response = generator.generate_otp_response_from_challenge(
73 return generator.generate_otp_words_from_challenge( 125 args.challange_string
74 args.challenge_string
75 )
76 return generator.generate_otp_hexdigest_from_challenge(
77 args.challenge_string
78 ) 126 )
127 if args.output_format == 'token':
128 return response.words
129 return response.hexdigest
130
79 # regular parameters 131 # regular parameters
80 header = '' 132 header = ''
81 if not args.quiet: 133 if not args.quiet:
@@ -83,9 +135,12 @@ def generate_otp_response(args: argparse.Namespace) -> str:
83 f'Seed: {args.seed}, Step: {args.step}, ' 135 f'Seed: {args.seed}, Step: {args.step}, '
84 f'Hash: {args.hash_algo}{os.linesep}' 136 f'Hash: {args.hash_algo}{os.linesep}'
85 ) 137 )
138
139 response = generator.generate_otp_response(args.step)
140
86 if args.output_format == 'token': 141 if args.output_format == 'token':
87 return header + generator.generate_otp_words(args.step) 142 return header + response.words
88 return header + generator.generate_otp_hexdigest(args.step) 143 return header + response.hexdigest
89 144
90 145
91def generate_otp_range(args: argparse.Namespace) -> str: 146def generate_otp_range(args: argparse.Namespace) -> str:
@@ -99,20 +154,24 @@ def generate_otp_range(args: argparse.Namespace) -> str:
99 154
100 :raises otp2289.OTPGeneratorError: If generator parameters are wrong 155 :raises otp2289.OTPGeneratorError: If generator parameters are wrong
101 156
157 :raises otp2289.OTPResponseError: If tokens or hex can not be generated
158
102 :return: The responses string 159 :return: The responses string
103 :rtype: str 160 :rtype: str
104 """ 161 """
105 generator = otp2289.generator.OTPGenerator( 162 generator = otp2289.OTPGenerator(
106 args.password.encode(), args.seed, args.hash_algo 163 args.password.encode(), args.seed, args.hash_algo
107 ) 164 )
108 if args.output_format == 'token':
109 method = generator.generate_otp_words
110 else:
111 method = generator.generate_otp_hexdigest
112 165
113 # handle most cases explicitly 166 # handle most cases explicitly
114 if args.range == 1: 167 if args.range == 1:
115 return f'{args.step}: ' + method(args.step) 168 response = generator.generate_otp_response(args.step)
169 if args.output_format == 'token':
170 response_str = response.words
171 else:
172 response_str = response.hexdigest
173 return f'{args.step}: ' + response_str
174
116 args.range = min(args.range, args.step + 1) 175 args.range = min(args.range, args.step + 1)
117 176
118 # any need for quiet? 177 # any need for quiet?
@@ -122,9 +181,19 @@ def generate_otp_range(args: argparse.Namespace) -> str:
122 f'Seed: {args.seed}, Step: {args.step}, ' 181 f'Seed: {args.seed}, Step: {args.step}, '
123 f'Hash: {args.hash_algo}, Range: {args.range}{os.linesep}' 182 f'Hash: {args.hash_algo}, Range: {args.range}{os.linesep}'
124 ) 183 )
184
185 if args.output_format == 'token':
186 return header + os.linesep.join(
187 [
188 f'{step}: ' + generator.generate_otp_response(step).words
189 for step in range(args.step, args.step - args.range, -1)
190 ]
191 )
192
193 # assume hex
125 return header + os.linesep.join( 194 return header + os.linesep.join(
126 [ 195 [
127 f'{step}: ' + method(step) 196 f'{step}: ' + generator.generate_otp_response(step).hexdigest
128 for step in range(args.step, args.step - args.range, -1) 197 for step in range(args.step, args.step - args.range, -1)
129 ] 198 ]
130 ) 199 )
@@ -172,50 +241,6 @@ def get_password(args: argparse.Namespace) -> str:
172 return args.password 241 return args.password
173 242
174 243
175def get_rnd_seed() -> str:
176 """
177 Returns a random seed in the format:
178
179 2 random letters (capitalize()) + 5 random digits
180 """
181 rnd = secrets.SystemRandom()
182 return ''.join(
183 rnd.choices(string.ascii_lowercase, k=2)
184 ).capitalize() + ''.join(rnd.choices(string.digits, k=5))
185
186
187def initiate_new_sequence(args: argparse.Namespace) -> str:
188 """
189 Generates a new sequence based on the parameters sent from the parser.
190
191 :param args: The arguments assigned from argparse
192 :type args: argparse.Namespace
193
194 :raises otp2289.OTPChallengeError: If the challenge is invalid
195
196 :raises otp2289.OTPGeneratorError: If generator parameters are wrong
197
198 :return: The response string
199 :rtype: str
200 """
201 if not args.seed:
202 args.seed = get_rnd_seed()
203 header = ''
204 if not args.quiet:
205 header = (
206 f'Seed: {args.seed}, Step: {args.step}, '
207 f'Hash: {args.hash_algo}{os.linesep}'
208 )
209 generator = otp2289.generator.OTPGenerator(
210 args.password.encode(), args.seed, args.hash_algo
211 )
212 if args.challenge_string:
213 return header + generator.generate_otp_hexdigest_from_challenge(
214 args.challenge_string
215 )
216 return header + generator.generate_otp_hexdigest(args.step)
217
218
219def main(inargs: list[str] | None = None) -> None: 244def main(inargs: list[str] | None = None) -> None:
220 """the main entry point""" 245 """the main entry point"""
221 parser = argparse.ArgumentParser( 246 parser = argparse.ArgumentParser(
@@ -278,8 +303,8 @@ def main(inargs: list[str] | None = None) -> None:
278 parser.add_argument( 303 parser.add_argument(
279 '-a', 304 '-a',
280 '--hash-algorithm', 305 '--hash-algorithm',
281 metavar='<md5 | sha1>',
282 type=str, 306 type=str,
307 choices=['md5', 'sha1'],
283 dest='hash_algo', 308 dest='hash_algo',
284 default='md5', 309 default='md5',
285 help='The hash algorithm to use. Possible values: md5 (default), sha1', 310 help='The hash algorithm to use. Possible values: md5 (default), sha1',
@@ -296,8 +321,8 @@ def main(inargs: list[str] | None = None) -> None:
296 parser.add_argument( 321 parser.add_argument(
297 '-f', 322 '-f',
298 '--output-format', 323 '--output-format',
299 metavar='<hex | token>',
300 type=str, 324 type=str,
325 choices=['hex', 'token'],
301 dest='output_format', 326 dest='output_format',
302 default='hex', 327 default='hex',
303 help='The output format to use. Possible values: hex (default), token', 328 help='The output format to use. Possible values: hex (default), token',
@@ -368,9 +393,9 @@ def main(inargs: list[str] | None = None) -> None:
368 if args.generate_otp_response: 393 if args.generate_otp_response:
369 print(generate_otp_response(args)) 394 print(generate_otp_response(args))
370 sys.exit(0) 395 sys.exit(0)
371 except otp2289.generator.OTPGeneratorError as exp: 396 except otp2289.OTPGeneratorError as exp:
372 eprint(f'GeneratorException: {exp}') 397 eprint(f'GeneratorException: {exp}')
373 except otp2289.generator.OTPChallengeError as exp: 398 except otp2289.OTPChallengeError as exp:
374 eprint(f'ChallengeException: {exp}') 399 eprint(f'ChallengeException: {exp}')
375 except Exception as exp: 400 except Exception as exp:
376 eprint(f'Unknown error: {exp}') 401 eprint(f'Unknown error: {exp}')