summaryrefslogtreecommitdiff
path: root/test/test_generator.py
diff options
context:
space:
mode:
authorSimeon Simeonov2026-04-28 14:27:05 +0200
committerSimeon Simeonov2026-04-28 14:27:05 +0200
commit751c74e7de1c78a151fdd8b76f220d8411a2108a (patch)
tree0b13345aad6949a3325ca8f72b4eece14a0fe973 /test/test_generator.py
parent82ef6adec6e59f9cc9dc9fa1a13a2b43e534bada (diff)
Restructure the entire project, enforce linting and add support for type checkers
Diffstat (limited to 'test/test_generator.py')
-rw-r--r--test/test_generator.py279
1 files changed, 0 insertions, 279 deletions
diff --git a/test/test_generator.py b/test/test_generator.py
deleted file mode 100644
index 4329ae4..0000000
--- a/test/test_generator.py
+++ /dev/null
@@ -1,279 +0,0 @@
1# SPDX-License-Identifier: BSD-2-Clause-FreeBSD
2#
3# Copyright (c) 2020-2025, Simeon Simeonov
4# All rights reserved.
5#
6# Redistribution and use in source and binary forms, with or without
7# modification, are permitted provided that the following conditions
8# are met:
9# 1. Redistributions of source code must retain the above copyright
10# notice, this list of conditions and the following disclaimer.
11# 2. Redistributions in binary form must reproduce the above copyright notice,
12# this list of conditions and the following disclaimer in the documentation
13# and/or other materials provided with the distribution.
14#
15# THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
16# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25"""Tests for otp2289.generator"""
26import pytest
27
28import otp2289
29
30
31def test_caller_exceptions():
32 """Tests the exceptions when calling an initialized object"""
33 gen = otp2289.OTPGenerator(
34 'This is a test.'.encode(),
35 'TeSt',
36 otp2289.OTP_ALGO_MD5,
37 )
38 with pytest.raises(otp2289.OTPGeneratorError) as exc_info:
39 gen.generate_otp_words('3')
40 assert exc_info.type is otp2289.OTPGeneratorError
41 assert exc_info.value.args[0] == 'Step value MUST be an int'
42 with pytest.raises(otp2289.OTPGeneratorError) as exc_info:
43 gen.generate_otp_hexdigest(-1)
44 assert exc_info.type is otp2289.OTPGeneratorError
45 assert exc_info.value.args[0] == 'Step value MUST be >= 0'
46 with pytest.raises(otp2289.OTPChallengeError) as exc_info:
47 gen.generate_otp_hexdigest_from_challenge(b'md5 fbd TeSt')
48 assert exc_info.type is otp2289.OTPChallengeError
49 assert exc_info.value.args[0] == 'Challenge must be str'
50 with pytest.raises(otp2289.OTPChallengeError) as exc_info:
51 gen.generate_otp_hexdigest_from_challenge('md5 fbd TeSt')
52 assert exc_info.type is otp2289.OTPChallengeError
53 assert exc_info.value.args[0] == 'Invalid challenge'
54 with pytest.raises(otp2289.generator.OTPChallengeError) as exc_info:
55 gen.generate_otp_hexdigest_from_challenge('otp-md5 fbd TeSt')
56 assert exc_info.type is otp2289.generator.OTPChallengeError
57 assert exc_info.value.args[0] == 'Invalid challenge'
58
59
60def test_constructor_exceptions():
61 """
62 Tests the exceptions when initializing a new object (in the constructor)
63 """
64 # test the otp2289.OTPGenerator __init__ and validators
65 with pytest.raises(otp2289.OTPGeneratorError) as exc_info:
66 otp2289.OTPGenerator(
67 'This is a test.'.encode(),
68 'TeStø'.encode(),
69 otp2289.OTP_ALGO_MD5,
70 )
71 assert exc_info.type is otp2289.OTPGeneratorError
72 assert exc_info.value.args[0] == 'Seed must be a string'
73 with pytest.raises(otp2289.OTPGeneratorError) as exc_info:
74 otp2289.OTPGenerator(
75 'This is a test.'.encode(),
76 'TeStøtEsTteSTteStTest',
77 otp2289.OTP_ALGO_SHA1,
78 )
79 assert exc_info.type is otp2289.OTPGeneratorError
80 assert exc_info.value.args[0] == (
81 'The seed MUST be of 1 to 16 characters in length'
82 )
83 with pytest.raises(otp2289.OTPGeneratorError) as exc_info:
84 otp2289.OTPGenerator(
85 'This is a test.'.encode(),
86 'TeStø',
87 otp2289.OTP_ALGO_SHA1,
88 )
89 assert exc_info.type is otp2289.OTPGeneratorError
90 assert exc_info.value.args[0] == (
91 'The seed MUST consist of purely alphanumeric characters'
92 )
93 with pytest.raises(otp2289.OTPGeneratorError) as exc_info:
94 otp2289.OTPGenerator(
95 'This is a test.'.encode(),
96 'TeSt',
97 9,
98 )
99 assert exc_info.type is otp2289.OTPGeneratorError
100 assert exc_info.value.args[0] == (
101 'hash_algo is not among the known algorithms'
102 )
103 with pytest.raises(otp2289.OTPGeneratorError) as exc_info:
104 otp2289.OTPGenerator(
105 'This is a test.'.encode(),
106 'TeSt',
107 b'md5',
108 )
109 assert exc_info.type is otp2289.OTPGeneratorError
110 assert exc_info.value.args[0] == 'hash_algo must be an int or a str'
111 # test the package structure as well
112 with pytest.raises(otp2289.generator.OTPGeneratorError) as exc_info:
113 otp2289.generator.OTPGenerator(
114 'This is a test.'.encode(),
115 'TeSt',
116 'foo',
117 )
118 assert exc_info.type is otp2289.generator.OTPGeneratorError
119 assert exc_info.value.args[0] == (
120 'foo is not supported by this version of the hashlib module'
121 )
122 with pytest.raises(otp2289.OTPGeneratorError) as exc_info:
123 otp2289.OTPGenerator('1234567', 'TeSt', otp2289.OTP_ALGO_MD5)
124 assert exc_info.type is otp2289.OTPGeneratorError
125 assert exc_info.value.args[0] == 'Password must be a byte-string'
126 with pytest.raises(otp2289.OTPGeneratorError) as exc_info:
127 otp2289.OTPGenerator(
128 '1234567'.encode(),
129 'TeSt',
130 otp2289.OTP_ALGO_MD5,
131 )
132 assert exc_info.type is otp2289.OTPGeneratorError
133 assert exc_info.value.args[0] == 'Password must be longer than 10 bytes'
134
135
136def test_md5():
137 """
138 Tests the MD5 functionality of the OTPGenerator as described in the RFC
139
140 Those are the tests from 'RFC-2289 Appendix C - OTP Verification Examples'
141 """
142 # We could run this in a loop, but I guess "Readability counts."
143 # pass='This is a test.', seed='TeSt'
144 gen = otp2289.OTPGenerator(
145 'This is a test.'.encode(),
146 'TeSt',
147 otp2289.OTP_ALGO_MD5,
148 )
149 res_words = gen.generate_otp_words(0)
150 res_hex = gen.generate_otp_hexdigest(0)
151 assert isinstance(res_words, str)
152 assert isinstance(res_hex, str)
153 assert res_hex == '0x9e876134d90499dd'
154 assert res_words == 'INCH SEA ANNE LONG AHEM TOUR'
155 # step 1
156 assert gen.generate_otp_hexdigest(1) == '0x7965e05436f5029f'
157 assert gen.generate_otp_words(1) == 'EASE OIL FUM CURE AWRY AVIS'
158 assert gen.generate_otp_hexdigest_from_challenge('otp-md5 1 TeSt') == (
159 '0x7965e05436f5029f'
160 )
161 assert gen.generate_otp_words_from_challenge('otp-md5 1 TeSt') == (
162 'EASE OIL FUM CURE AWRY AVIS'
163 )
164 # step 99
165 assert gen.generate_otp_hexdigest(99) == '0x50fe1962c4965880'
166 assert gen.generate_otp_words(99) == 'BAIL TUFT BITS GANG CHEF THY'
167 assert gen.generate_otp_hexdigest_from_challenge('otp-md5 99 TeSt') == (
168 '0x50fe1962c4965880'
169 )
170 assert gen.generate_otp_words_from_challenge('otp-md5 99 TeSt') == (
171 'BAIL TUFT BITS GANG CHEF THY'
172 )
173 # iterator test
174 hexdigests = list(gen.hexdigest_range(105)) # testing the range itself
175 words = list(gen.words_range(99))
176 hexdigests.reverse()
177 words.reverse()
178 assert hexdigests[0] == '0x9e876134d90499dd'
179 assert hexdigests[1] == '0x7965e05436f5029f'
180 assert hexdigests[99] == '0x50fe1962c4965880'
181 assert words[0] == 'INCH SEA ANNE LONG AHEM TOUR'
182 assert words[1] == 'EASE OIL FUM CURE AWRY AVIS'
183 assert words[99] == 'BAIL TUFT BITS GANG CHEF THY'
184 # pass='AbCdEfGhIjK', seed='alpha1'
185 gen = otp2289.OTPGenerator(
186 'AbCdEfGhIjK'.encode(),
187 'alpha1',
188 otp2289.OTP_ALGO_MD5,
189 )
190 assert gen.generate_otp_hexdigest(0) == '0x87066dd9644bf206'
191 assert gen.generate_otp_words(0) == 'FULL PEW DOWN ONCE MORT ARC'
192 assert gen.generate_otp_hexdigest(1) == '0x7cd34c1040add14b'
193 assert gen.generate_otp_words(1) == 'FACT HOOF AT FIST SITE KENT'
194 assert gen.generate_otp_hexdigest(99) == '0x5aa37a81f212146c'
195 assert gen.generate_otp_words(99) == 'BODE HOP JAKE STOW JUT RAP'
196 # pass="OTP's are good", seed='correct'
197 gen = otp2289.OTPGenerator(
198 "OTP's are good".encode(),
199 'correct',
200 otp2289.OTP_ALGO_MD5,
201 )
202 assert gen.generate_otp_hexdigest(0) == '0xf205753943de4cf9'
203 assert gen.generate_otp_words(0) == 'ULAN NEW ARMY FUSE SUIT EYED'
204 assert gen.generate_otp_hexdigest(1) == '0xddcdac956f234937'
205 assert gen.generate_otp_words(1) == 'SKIM CULT LOB SLAM POE HOWL'
206 assert gen.generate_otp_hexdigest(99) == '0xb203e28fa525be47'
207 assert gen.generate_otp_words(99) == 'LONG IVY JULY AJAR BOND LEE'
208
209
210def test_sha1():
211 """
212 Tests the SHA-1 functionality of the OTPGenerator as described in the RFC
213
214 Those are the tests from 'RFC-2289 Appendix C - OTP Verification Examples'
215 """
216 # pass='This is a test.', seed='TeSt'
217 gen = otp2289.OTPGenerator(
218 'This is a test.'.encode(),
219 'TeSt',
220 otp2289.OTP_ALGO_SHA1,
221 )
222 # step=0
223 res_hex = gen.generate_otp_hexdigest(0)
224 res_words = gen.generate_otp_words(0)
225 assert isinstance(res_words, str)
226 assert isinstance(res_hex, str)
227 assert res_hex == '0xbb9e6ae1979d8ff4'
228 assert res_words == 'MILT VARY MAST OK SEES WENT'
229 assert gen.generate_otp_hexdigest(1) == '0x63d936639734385b'
230 assert gen.generate_otp_words(1) == 'CART OTTO HIVE ODE VAT NUT'
231 assert gen.generate_otp_hexdigest_from_challenge('otp-sha1 1 TeSt') == (
232 '0x63d936639734385b'
233 )
234 assert gen.generate_otp_words_from_challenge('otp-sha1 1 TeSt') == (
235 'CART OTTO HIVE ODE VAT NUT'
236 )
237 assert gen.generate_otp_hexdigest(99) == '0x87fec7768b73ccf9'
238 assert gen.generate_otp_words(99) == 'GAFF WAIT SKID GIG SKY EYED'
239 assert gen.generate_otp_hexdigest_from_challenge('otp-sha1 99 TeSt') == (
240 '0x87fec7768b73ccf9'
241 )
242 assert gen.generate_otp_words_from_challenge('otp-sha1 99 TeSt') == (
243 'GAFF WAIT SKID GIG SKY EYED'
244 )
245 # iterator test
246 hexdigests = list(gen.hexdigest_range(105))
247 words = list(gen.words_range(99))
248 hexdigests.reverse()
249 words.reverse()
250 assert hexdigests[0] == '0xbb9e6ae1979d8ff4'
251 assert hexdigests[1] == '0x63d936639734385b'
252 assert hexdigests[99] == '0x87fec7768b73ccf9'
253 assert words[0] == 'MILT VARY MAST OK SEES WENT'
254 assert words[1] == 'CART OTTO HIVE ODE VAT NUT'
255 assert words[99] == 'GAFF WAIT SKID GIG SKY EYED'
256 # pass='AbCdEfGhIjK', seed='alpha1'
257 gen = otp2289.OTPGenerator(
258 'AbCdEfGhIjK'.encode(),
259 'alpha1',
260 otp2289.OTP_ALGO_SHA1,
261 )
262 assert gen.generate_otp_hexdigest(0) == '0xad85f658ebe383c9'
263 assert gen.generate_otp_words(0) == 'LEST OR HEEL SCOT ROB SUIT'
264 assert gen.generate_otp_hexdigest(1) == '0xd07ce229b5cf119b'
265 assert gen.generate_otp_words(1) == 'RITE TAKE GELD COST TUNE RECK'
266 assert gen.generate_otp_hexdigest(99) == '0x27bc71035aaf3dc6'
267 assert gen.generate_otp_words(99) == 'MAY STAR TIN LYON VEDA STAN'
268 # pass="OTP's are good", seed='correct'
269 gen = otp2289.OTPGenerator(
270 "OTP's are good".encode(),
271 'correct',
272 otp2289.OTP_ALGO_SHA1,
273 )
274 assert gen.generate_otp_hexdigest(0) == '0xd51f3e99bf8e6f0b'
275 assert gen.generate_otp_words(0) == 'RUST WELT KICK FELL TAIL FRAU'
276 assert gen.generate_otp_hexdigest(1) == '0x82aeb52d943774e4'
277 assert gen.generate_otp_words(1) == 'FLIT DOSE ALSO MEW DRUM DEFY'
278 assert gen.generate_otp_hexdigest(99) == '0x4f296a74fe1567ec'
279 assert gen.generate_otp_words(99) == 'AURA ALOE HURL WING BERG WAIT'