Commit | Line | Data |
---|---|---|
553d59fe MW |
1 | ### -*- mode: python, coding: utf-8 -*- |
2 | ### | |
3 | ### Test symmetric algorithms | |
4 | ### | |
5 | ### (c) 2019 Straylight/Edgeware | |
6 | ### | |
7 | ||
8 | ###----- Licensing notice --------------------------------------------------- | |
9 | ### | |
10 | ### This file is part of the Python interface to Catacomb. | |
11 | ### | |
12 | ### Catacomb/Python is free software: you can redistribute it and/or | |
13 | ### modify it under the terms of the GNU General Public License as | |
14 | ### published by the Free Software Foundation; either version 2 of the | |
15 | ### License, or (at your option) any later version. | |
16 | ### | |
17 | ### Catacomb/Python is distributed in the hope that it will be useful, but | |
18 | ### WITHOUT ANY WARRANTY; without even the implied warranty of | |
19 | ### MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
20 | ### General Public License for more details. | |
21 | ### | |
22 | ### You should have received a copy of the GNU General Public License | |
23 | ### along with Catacomb/Python. If not, write to the Free Software | |
24 | ### Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, | |
25 | ### USA. | |
26 | ||
27 | ###-------------------------------------------------------------------------- | |
28 | ### Imported modules. | |
29 | ||
30 | import catacomb as C | |
31 | import unittest as U | |
32 | import testutils as T | |
33 | ||
34 | ###-------------------------------------------------------------------------- | |
35 | ### Utilities. | |
36 | ||
37 | def bad_key_size(ksz): | |
38 | if isinstance(ksz, C.KeySZAny): return None | |
39 | elif isinstance(ksz, C.KeySZRange): | |
40 | if ksz.mod != 1: return ksz.min + 1 | |
41 | elif ksz.max != 0: return ksz.max + 1 | |
42 | elif ksz.min != 0: return ksz.min - 1 | |
43 | else: return None | |
44 | elif isinstance(ksz, C.KeySZSet): | |
45 | for sz in sorted(ksz.set): | |
46 | if sz + 1 not in ksz.set: return sz + 1 | |
47 | assert False, "That should have worked." | |
48 | else: | |
49 | return None | |
50 | ||
51 | def different_key_size(ksz, sz): | |
52 | if isinstance(ksz, C.KeySZAny): return sz + 1 | |
53 | elif isinstance(ksz, C.KeySZRange): | |
54 | if sz > ksz.min: return sz - ksz.mod | |
55 | elif ksz.max == 0 or sz < ksz.max: return sz + ksz.mod | |
56 | else: return None | |
57 | elif isinstance(ksz, C.KeySZSet): | |
58 | for sz1 in sorted(ksz.set): | |
59 | if sz != sz1: return sz1 | |
60 | return None | |
61 | else: | |
62 | return None | |
63 | ||
64 | class HashBufferTestMixin (U.TestCase): | |
65 | """Mixin class for testing all of the various `hash...' methods.""" | |
66 | ||
67 | def check_hashbuffer_hashn(me, w, bigendp, makefn, hashfn): | |
68 | """Check `hashuN'.""" | |
69 | ||
70 | ## Check encoding an integer. | |
71 | h0, donefn0 = makefn(w + 2) | |
72 | hashfn(h0.hashu8(0x00), T.bytes_as_int(w, bigendp)).hashu8(w + 1) | |
73 | h1, donefn1 = makefn(w + 2) | |
74 | h1.hash(T.span(w + 2)) | |
75 | me.assertEqual(donefn0(), donefn1()) | |
76 | ||
77 | ## Check overflow detection. | |
78 | h0, _ = makefn(w) | |
79 | me.assertRaises((OverflowError, ValueError), | |
80 | hashfn, h0, 1 << 8*w) | |
81 | ||
82 | def check_hashbuffer_bufn(me, w, bigendp, makefn, hashfn): | |
83 | """Check `hashbufN'.""" | |
84 | ||
85 | ## Go through a number of different sizes. | |
86 | for n in [0, 1, 7, 8, 19, 255, 12345, 65535, 123456]: | |
87 | if n >= 1 << 8*w: continue | |
88 | h0, donefn0 = makefn(2 + w + n) | |
89 | hashfn(h0.hashu8(0x00), T.span(n)).hashu8(0xff) | |
90 | h1, donefn1 = makefn(2 + w + n) | |
91 | h1.hash(T.prep_lenseq(w, n, bigendp, True)) | |
92 | me.assertEqual(donefn0(), donefn1()) | |
93 | ||
94 | ## Check blocks which are too large for the length prefix. | |
95 | if w <= 3: | |
96 | n = 1 << 8*w | |
97 | h0, _ = makefn(w + n) | |
98 | me.assertRaises((ValueError, OverflowError, TypeError), | |
99 | hashfn, h0, C.ByteString.zero(n)) | |
100 | ||
101 | def check_hashbuffer(me, makefn): | |
102 | """Test the various `hash...' methods.""" | |
103 | ||
104 | ## Check `hashuN'. | |
105 | me.check_hashbuffer_hashn(1, True, makefn, lambda h, n: h.hashu8(n)) | |
106 | me.check_hashbuffer_hashn(2, True, makefn, lambda h, n: h.hashu16(n)) | |
107 | me.check_hashbuffer_hashn(2, True, makefn, lambda h, n: h.hashu16b(n)) | |
108 | me.check_hashbuffer_hashn(2, False, makefn, lambda h, n: h.hashu16l(n)) | |
109 | if hasattr(makefn(0)[0], "hashu24"): | |
110 | me.check_hashbuffer_hashn(3, True, makefn, lambda h, n: h.hashu24(n)) | |
111 | me.check_hashbuffer_hashn(3, True, makefn, lambda h, n: h.hashu24b(n)) | |
112 | me.check_hashbuffer_hashn(3, False, makefn, lambda h, n: h.hashu24l(n)) | |
113 | me.check_hashbuffer_hashn(4, True, makefn, lambda h, n: h.hashu32(n)) | |
114 | me.check_hashbuffer_hashn(4, True, makefn, lambda h, n: h.hashu32b(n)) | |
115 | me.check_hashbuffer_hashn(4, False, makefn, lambda h, n: h.hashu32l(n)) | |
116 | if hasattr(makefn(0)[0], "hashu64"): | |
117 | me.check_hashbuffer_hashn(8, True, makefn, lambda h, n: h.hashu64(n)) | |
118 | me.check_hashbuffer_hashn(8, True, makefn, lambda h, n: h.hashu64b(n)) | |
119 | me.check_hashbuffer_hashn(8, False, makefn, lambda h, n: h.hashu64l(n)) | |
120 | ||
121 | ## Check `hashbufN'. | |
122 | me.check_hashbuffer_bufn(1, True, makefn, lambda h, x: h.hashbuf8(x)) | |
123 | me.check_hashbuffer_bufn(2, True, makefn, lambda h, x: h.hashbuf16(x)) | |
124 | me.check_hashbuffer_bufn(2, True, makefn, lambda h, x: h.hashbuf16b(x)) | |
125 | me.check_hashbuffer_bufn(2, False, makefn, lambda h, x: h.hashbuf16l(x)) | |
126 | if hasattr(makefn(0)[0], "hashbuf24"): | |
127 | me.check_hashbuffer_bufn(3, True, makefn, lambda h, x: h.hashbuf24(x)) | |
128 | me.check_hashbuffer_bufn(3, True, makefn, lambda h, x: h.hashbuf24b(x)) | |
129 | me.check_hashbuffer_bufn(3, False, makefn, lambda h, x: h.hashbuf24l(x)) | |
130 | me.check_hashbuffer_bufn(4, True, makefn, lambda h, x: h.hashbuf32(x)) | |
131 | me.check_hashbuffer_bufn(4, True, makefn, lambda h, x: h.hashbuf32b(x)) | |
132 | me.check_hashbuffer_bufn(4, False, makefn, lambda h, x: h.hashbuf32l(x)) | |
133 | if hasattr(makefn(0)[0], "hashbuf64"): | |
134 | me.check_hashbuffer_bufn(8, True, makefn, lambda h, x: h.hashbuf64(x)) | |
135 | me.check_hashbuffer_bufn(8, True, makefn, lambda h, x: h.hashbuf64b(x)) | |
136 | me.check_hashbuffer_bufn(8, False, makefn, lambda h, x: h.hashbuf64l(x)) | |
137 | ||
138 | ###-------------------------------------------------------------------------- | |
139 | class TestKeysize (U.TestCase): | |
140 | ||
141 | def test_any(me): | |
142 | ||
143 | ## A typical one-byte spec. | |
144 | ksz = C.seal.keysz | |
145 | me.assertEqual(type(ksz), C.KeySZAny) | |
146 | me.assertEqual(ksz.default, 20) | |
147 | me.assertEqual(ksz.min, 0) | |
148 | me.assertEqual(ksz.max, 0) | |
149 | for n in [0, 12, 20, 5000]: | |
150 | me.assertTrue(ksz.check(n)) | |
151 | me.assertEqual(ksz.best(n), n) | |
606677f6 | 152 | me.assertEqual(ksz.pad(n), n) |
553d59fe MW |
153 | |
154 | ## A typical two-byte spec. (No published algorithms actually /need/ a | |
155 | ## two-byte key-size spec, but all of the HMAC variants use one anyway.) | |
156 | ksz = C.sha256_hmac.keysz | |
157 | me.assertEqual(type(ksz), C.KeySZAny) | |
158 | me.assertEqual(ksz.default, 32) | |
159 | me.assertEqual(ksz.min, 0) | |
160 | me.assertEqual(ksz.max, 0) | |
161 | for n in [0, 12, 20, 5000]: | |
162 | me.assertTrue(ksz.check(n)) | |
163 | me.assertEqual(ksz.best(n), n) | |
606677f6 | 164 | me.assertEqual(ksz.pad(n), n) |
553d59fe MW |
165 | |
166 | ## Check construction. | |
167 | ksz = C.KeySZAny(15) | |
168 | me.assertEqual(ksz.default, 15) | |
169 | me.assertEqual(ksz.min, 0) | |
170 | me.assertEqual(ksz.max, 0) | |
171 | me.assertRaises(ValueError, lambda: C.KeySZAny(-8)) | |
172 | me.assertEqual(C.KeySZAny(0).default, 0) | |
173 | ||
174 | def test_set(me): | |
175 | ## Note that no published algorithm uses a 16-bit `set' spec. | |
176 | ||
177 | ## A typical spec. | |
178 | ksz = C.salsa20.keysz | |
179 | me.assertEqual(type(ksz), C.KeySZSet) | |
180 | me.assertEqual(ksz.default, 32) | |
181 | me.assertEqual(ksz.min, 10) | |
182 | me.assertEqual(ksz.max, 32) | |
183 | me.assertEqual(set(ksz.set), set([10, 16, 32])) | |
184 | for x, best, pad in [(9, None, 10), (10, 10, 10), (11, 10, 16), | |
185 | (15, 10, 16), (16, 16, 16), (17, 16, 32), | |
186 | (31, 16, 32), (32, 32, 32), (33, 32, None)]: | |
187 | if x == best == pad: me.assertTrue(ksz.check(x)) | |
188 | else: me.assertFalse(ksz.check(x)) | |
189 | if best is None: me.assertRaises(ValueError, ksz.best, x) | |
190 | else: me.assertEqual(ksz.best(x), best) | |
606677f6 MW |
191 | if pad is None: me.assertRaises(ValueError, ksz.pad, x) |
192 | else: me.assertEqual(ksz.pad(x), pad) | |
553d59fe MW |
193 | |
194 | ## Check construction. | |
195 | ksz = C.KeySZSet(7) | |
196 | me.assertEqual(ksz.default, 7) | |
197 | me.assertEqual(set(ksz.set), set([7])) | |
198 | me.assertEqual(ksz.min, 7) | |
199 | me.assertEqual(ksz.max, 7) | |
200 | ksz = C.KeySZSet(7, [3, 6, 9]) | |
201 | me.assertEqual(ksz.default, 7) | |
202 | me.assertEqual(set(ksz.set), set([3, 6, 7, 9])) | |
203 | me.assertEqual(ksz.min, 3) | |
204 | me.assertEqual(ksz.max, 9) | |
205 | ||
206 | def test_range(me): | |
207 | ## Note that no published algorithm uses a 16-bit `range' spec, or an | |
208 | ## unbounded `range'. | |
209 | ||
210 | ## A typical spec. | |
211 | ksz = C.rijndael.keysz | |
212 | me.assertEqual(type(ksz), C.KeySZRange) | |
213 | me.assertEqual(ksz.default, 32) | |
214 | me.assertEqual(ksz.min, 4) | |
215 | me.assertEqual(ksz.max, 32) | |
216 | me.assertEqual(ksz.mod, 4) | |
606677f6 MW |
217 | for x, best, pad in [(3, None, 4), (4, 4, 4), (5, 4, 8), |
218 | (15, 12, 16), (16, 16, 16), (17, 16, 20), | |
219 | (31, 28, 32), (32, 32, 32), (33, 32, None)]: | |
220 | if x == best == pad: me.assertTrue(ksz.check(x)) | |
553d59fe MW |
221 | else: me.assertFalse(ksz.check(x)) |
222 | if best is None: me.assertRaises(ValueError, ksz.best, x) | |
223 | else: me.assertEqual(ksz.best(x), best) | |
606677f6 MW |
224 | if pad is None: me.assertRaises(ValueError, ksz.pad, x) |
225 | else: me.assertEqual(ksz.pad(x), pad) | |
553d59fe MW |
226 | |
227 | ## Check construction. | |
228 | ksz = C.KeySZRange(28, 21, 35, 7) | |
229 | me.assertEqual(ksz.default, 28) | |
230 | me.assertEqual(ksz.min, 21) | |
231 | me.assertEqual(ksz.max, 35) | |
232 | me.assertEqual(ksz.mod, 7) | |
233 | me.assertRaises(ValueError, C.KeySZRange, 29, 21, 35, 7) | |
234 | me.assertRaises(ValueError, C.KeySZRange, 28, 20, 35, 7) | |
235 | me.assertRaises(ValueError, C.KeySZRange, 28, 21, 34, 7) | |
236 | me.assertRaises(ValueError, C.KeySZRange, 28, -7, 35, 7) | |
237 | me.assertRaises(ValueError, C.KeySZRange, 28, 35, 21, 7) | |
238 | me.assertRaises(ValueError, C.KeySZRange, 35, 21, 28, 7) | |
239 | me.assertRaises(ValueError, C.KeySZRange, 21, 28, 35, 7) | |
240 | ||
241 | def test_conversions(me): | |
242 | me.assertEqual(C.KeySZ.fromec(256), 128) | |
243 | me.assertEqual(C.KeySZ.fromschnorr(256), 128) | |
244 | me.assertEqual(round(C.KeySZ.fromdl(2958.6875)), 128) | |
245 | me.assertEqual(round(C.KeySZ.fromif(2958.6875)), 128) | |
246 | me.assertEqual(C.KeySZ.toec(128), 256) | |
247 | me.assertEqual(C.KeySZ.toschnorr(128), 256) | |
248 | me.assertEqual(C.KeySZ.todl(128), 2958.6875) | |
249 | me.assertEqual(C.KeySZ.toif(128), 2958.6875) | |
250 | ||
251 | ###-------------------------------------------------------------------------- | |
252 | class TestCipher (T.GenericTestMixin): | |
253 | """Test basic symmetric ciphers.""" | |
254 | ||
255 | def _test_cipher(me, ccls): | |
256 | ||
257 | ## Check the class properties. | |
258 | me.assertEqual(type(ccls.name), str) | |
259 | me.assertTrue(isinstance(ccls.keysz, C.KeySZ)) | |
260 | me.assertEqual(type(ccls.blksz), int) | |
261 | ||
262 | ## Check round-tripping. | |
263 | k = T.span(ccls.keysz.default) | |
264 | iv = T.span(ccls.blksz) | |
265 | m = T.span(253) | |
266 | enc = ccls(k) | |
267 | dec = ccls(k) | |
268 | try: enc.setiv(iv) | |
269 | except ValueError: can_setiv = False | |
270 | else: | |
271 | can_setiv = True | |
272 | dec.setiv(iv) | |
273 | c0 = enc.encrypt(m[0:57]) | |
274 | m0 = dec.decrypt(c0) | |
275 | c1 = enc.encrypt(m[57:189]) | |
276 | m1 = dec.decrypt(c1) | |
277 | try: enc.bdry() | |
278 | except ValueError: can_bdry = False | |
279 | else: | |
280 | dec.bdry() | |
281 | can_bdry = True | |
282 | c2 = enc.encrypt(m[189:253]) | |
283 | m2 = dec.decrypt(c2) | |
284 | me.assertEqual(len(c0) + len(c1) + len(c2), len(m)) | |
285 | me.assertEqual(m0, m[0:57]) | |
286 | me.assertEqual(m1, m[57:189]) | |
287 | me.assertEqual(m2, m[189:253]) | |
288 | ||
289 | ## Check the `enczero' and `deczero' methods. | |
290 | c3 = enc.enczero(32) | |
291 | me.assertEqual(dec.decrypt(c3), C.ByteString.zero(32)) | |
292 | m4 = dec.deczero(32) | |
293 | me.assertEqual(enc.encrypt(m4), C.ByteString.zero(32)) | |
294 | ||
295 | ## Check that ciphers which support a `boundary' operation actually | |
296 | ## need it. | |
297 | if can_bdry: | |
298 | dec = ccls(k) | |
299 | if can_setiv: dec.setiv(iv) | |
300 | m01 = dec.decrypt(c0 + c1) | |
301 | me.assertEqual(m01, m[0:189]) | |
302 | ||
303 | ## Check that the boundary actually does something. | |
304 | if can_bdry: | |
305 | dec = ccls(k) | |
306 | if can_setiv: dec.setiv(iv) | |
307 | m012 = dec.decrypt(c0 + c1 + c2) | |
308 | me.assertNotEqual(m012, m) | |
309 | ||
310 | ## Check that bad key lengths are rejected. | |
311 | badlen = bad_key_size(ccls.keysz) | |
312 | if badlen is not None: me.assertRaises(ValueError, ccls, T.span(badlen)) | |
313 | ||
314 | TestCipher.generate_testcases((name, C.gcciphers[name]) for name in | |
315 | ["des-ecb", "rijndael-cbc", "twofish-cfb", "serpent-ofb", | |
316 | "blowfish-counter", "rc4", "seal", "salsa20/8", "shake128-xof"]) | |
317 | ||
318 | ###-------------------------------------------------------------------------- | |
319 | class BaseTestHash (HashBufferTestMixin): | |
320 | """Base class for testing hash functions.""" | |
321 | ||
322 | def check_hash(me, hcls, need_bufsz = True): | |
323 | """ | |
324 | Check hash class HCLS. | |
325 | ||
326 | If NEED_BUFSZ is false, then don't insist that HCLS have working `bufsz', | |
327 | `name', or `hashsz' attributes. This test is mostly reused for MACs, | |
328 | which don't have these attributes. | |
329 | """ | |
330 | ## Check the class properties. | |
331 | if need_bufsz: | |
332 | me.assertEqual(type(hcls.name), str) | |
333 | me.assertEqual(type(hcls.bufsz), int) | |
334 | me.assertEqual(type(hcls.hashsz), int) | |
335 | ||
336 | ## Set some initial values. | |
337 | m = T.span(131) | |
338 | h = hcls().hash(m).done() | |
339 | ||
340 | ## Check that hash length comes out right. | |
341 | if need_bufsz: me.assertEqual(len(h), hcls.hashsz) | |
342 | ||
343 | ## Check that we get the same answer if we split the message up. | |
344 | me.assertEqual(h, hcls().hash(m[0:73]).hash(m[73:131]).done()) | |
345 | ||
346 | ## Check the `check' method. | |
347 | me.assertTrue(hcls().hash(m).check(h)) | |
348 | me.assertFalse(hcls().hash(m).check(h ^ len(h)*C.bytes("aa"))) | |
349 | ||
350 | ## Check the menagerie of random hashing methods. | |
351 | def mkhash(_): | |
352 | h = hcls() | |
353 | return h, h.done | |
354 | me.check_hashbuffer(mkhash) | |
355 | ||
356 | class TestHash (BaseTestHash, T.GenericTestMixin): | |
357 | """Test hash functions.""" | |
358 | def _test_hash(me, hcls): me.check_hash(hcls, need_bufsz = True) | |
359 | ||
360 | TestHash.generate_testcases((name, C.gchashes[name]) for name in | |
361 | ["md5", "sha", "whirlpool", "sha256", "sha512/224", "sha3-384", "shake256", | |
362 | "crc32"]) | |
363 | ||
364 | ###-------------------------------------------------------------------------- | |
365 | class TestMessageAuthentication (BaseTestHash, T.GenericTestMixin): | |
366 | """Test message authentication codes.""" | |
367 | ||
368 | def _test_mac(me, mcls): | |
369 | ||
370 | ## Check the MAC properties. | |
371 | me.assertEqual(type(mcls.name), str) | |
372 | me.assertTrue(isinstance(mcls.keysz, C.KeySZ)) | |
373 | me.assertEqual(type(mcls.tagsz), int) | |
374 | ||
375 | ## Test hashing. | |
376 | k = T.span(mcls.keysz.default) | |
377 | key = mcls(k) | |
378 | me.check_hash(key, need_bufsz = False) | |
379 | ||
380 | ## Check that bad key lengths are rejected. | |
381 | badlen = bad_key_size(mcls.keysz) | |
382 | if badlen is not None: me.assertRaises(ValueError, mcls, T.span(badlen)) | |
383 | ||
384 | TestMessageAuthentication.generate_testcases \ | |
385 | ((name, C.gcmacs[name]) for name in | |
386 | ["sha-hmac", "rijndael-cmac", "twofish-pmac1", "kmac128"]) | |
387 | ||
388 | class TestPoly1305 (HashBufferTestMixin): | |
389 | """Check the Poly1305 one-time message authentication function.""" | |
390 | ||
391 | def test_poly1305(me): | |
392 | ||
393 | ## Check the MAC properties. | |
394 | me.assertEqual(C.poly1305.name, "poly1305") | |
395 | me.assertEqual(type(C.poly1305.keysz), C.KeySZSet) | |
396 | me.assertEqual(C.poly1305.keysz.default, 16) | |
397 | me.assertEqual(set(C.poly1305.keysz.set), set([16])) | |
398 | me.assertEqual(C.poly1305.tagsz, 16) | |
399 | me.assertEqual(C.poly1305.masksz, 16) | |
400 | ||
401 | ## Set some initial values. | |
402 | k = T.span(16) | |
403 | u = T.span(64)[-16:] | |
404 | m = T.span(149) | |
405 | key = C.poly1305(k) | |
406 | t = key(u).hash(m).done() | |
407 | ||
408 | ## Check the key properties. | |
409 | me.assertEqual(len(t), 16) | |
410 | ||
411 | ## Check that we get the same answer if we split the message up. | |
412 | me.assertEqual(t, key(u).hash(m[0:86]).hash(m[86:149]).done()) | |
413 | ||
414 | ## Check the `check' method. | |
415 | me.assertTrue(key(u).hash(m).check(t)) | |
416 | me.assertFalse(key(u).hash(m).check(t ^ 16*C.bytes("cc"))) | |
417 | ||
418 | ## Check the menagerie of random hashing methods. | |
419 | def mkhash(_): | |
420 | h = key(u) | |
421 | return h, h.done | |
422 | me.check_hashbuffer(mkhash) | |
423 | ||
424 | ## Check that we can't complete hashing without a mask. | |
425 | me.assertRaises(ValueError, key().hash(m).done) | |
426 | ||
427 | ## Check `concat'. | |
428 | h0 = key().hash(m[0:96]) | |
429 | h1 = key().hash(m[96:117]) | |
430 | me.assertEqual(t, key(u).concat(h0, h1).hash(m[117:149]).done()) | |
431 | key1 = C.poly1305(k) | |
432 | me.assertRaises(TypeError, key().concat, key1().hash(m[0:96]), h1) | |
433 | me.assertRaises(TypeError, key().concat, h0, key1().hash(m[96:117])) | |
434 | me.assertRaises(ValueError, key().concat, key().hash(m[0:93]), h1) | |
435 | ||
436 | ###-------------------------------------------------------------------------- | |
437 | class TestHLatin (U.TestCase): | |
438 | """Test the `hsalsa20' and `hchacha20' functions.""" | |
439 | ||
440 | def test_hlatin(me): | |
441 | kk = [T.span(sz) for sz in [32]] | |
442 | n = T.span(16) | |
443 | bad_k = T.span(18) | |
444 | bad_n = T.span(13) | |
445 | for fn in [C.hsalsa208_prf, C.hsalsa2012_prf, C.hsalsa20_prf, | |
446 | C.hchacha8_prf, C.hchacha12_prf, C.hchacha20_prf]: | |
447 | for k in kk: | |
448 | h = fn(k, n) | |
449 | me.assertEqual(len(h), 32) | |
450 | me.assertRaises(ValueError, fn, bad_k, n) | |
451 | me.assertRaises(ValueError, fn, k, bad_n) | |
452 | ||
453 | ###-------------------------------------------------------------------------- | |
454 | class TestKeccak (HashBufferTestMixin): | |
455 | """Test the Keccak-p[1600, n] sponge function.""" | |
456 | ||
457 | def test_keccak(me): | |
458 | ||
459 | ## Make a state and feed some stuff into it. | |
460 | m0 = T.bin("some initial string") | |
461 | m1 = T.bin("awesome follow-up string") | |
462 | st0 = C.Keccak1600() | |
463 | me.assertEqual(st0.nround, 24) | |
464 | st0.mix(m0).step() | |
465 | ||
466 | ## Make another step with a different round count. | |
467 | st1 = C.Keccak1600(23) | |
468 | st1.mix(m0).step() | |
469 | me.assertNotEqual(st0.extract(32), st1.extract(32)) | |
470 | ||
471 | ## Check error conditions. | |
472 | _ = st0.extract(200) | |
473 | me.assertRaises(ValueError, st0.extract, 201) | |
474 | st0.mix(T.span(200)) | |
475 | me.assertRaises(ValueError, st0.mix, T.span(201)) | |
476 | ||
477 | def check_shake(me, xcls, c, done_matches_xof = True): | |
478 | """ | |
479 | Test the SHAKE and cSHAKE XOFs. | |
480 | ||
481 | This is also used for testing KMAC, but that sets DONE_MATCHES_XOF false | |
482 | to indicate that the XOF output is range-separated from the fixed-length | |
483 | outputs (unlike the basic SHAKE functions). | |
484 | """ | |
485 | ||
486 | ## Check the hash attributes. | |
487 | x = xcls() | |
488 | me.assertEqual(x.rate, 200 - c) | |
489 | me.assertEqual(x.buffered, 0) | |
490 | me.assertEqual(x.state, "absorb") | |
491 | ||
492 | ## Set some initial values. | |
493 | func = T.bin("TESTXOF") | |
494 | perso = T.bin("catacomb-python test") | |
495 | m = T.span(167) | |
496 | h0 = xcls().hash(m).done(193) | |
497 | me.assertEqual(len(h0), 193) | |
498 | h1 = xcls(func = func, perso = perso).hash(m).done(193) | |
499 | me.assertEqual(len(h1), 193) | |
500 | me.assertNotEqual(h0, h1) | |
501 | ||
502 | ## Check input and output in pieces, and the state machine. | |
503 | if done_matches_xof: h = h0 | |
504 | else: h = xcls().hash(m).xof().get(len(h0)) | |
505 | x = xcls().hash(m[0:76]).hash(m[76:167]).xof() | |
506 | me.assertEqual(h, x.get(98) + x.get(95)) | |
507 | ||
508 | ## Check masking. | |
509 | x = xcls().hash(m).xof() | |
510 | me.assertEqual(x.mask(m), C.ByteString(m) ^ C.ByteString(h[0:len(m)])) | |
511 | ||
512 | ## Check the `check' method. | |
513 | me.assertTrue(xcls().hash(m).check(h0)) | |
514 | me.assertFalse(xcls().hash(m).check(h1)) | |
515 | ||
516 | ## Check the menagerie of random hashing methods. | |
517 | def mkhash(_): | |
518 | x = xcls(func = func, perso = perso) | |
519 | return x, lambda: x.done(100 - x.rate//2) | |
520 | me.check_hashbuffer(mkhash) | |
521 | ||
522 | ## Check the state machine tracking. | |
523 | x = xcls(); me.assertEqual(x.state, "absorb") | |
524 | x.hash(m); me.assertEqual(x.state, "absorb") | |
525 | xx = x.copy() | |
526 | h = xx.done(100 - x.rate//2) | |
527 | me.assertEqual(xx.state, "dead") | |
528 | me.assertRaises(ValueError, xx.done, 1) | |
529 | me.assertRaises(ValueError, xx.get, 1) | |
530 | me.assertEqual(x.state, "absorb") | |
531 | me.assertRaises(ValueError, x.get, 1) | |
532 | x.xof(); me.assertEqual(x.state, "squeeze") | |
533 | me.assertRaises(ValueError, x.done, 1) | |
534 | _ = x.get(1) | |
535 | yy = x.copy(); me.assertEqual(yy.state, "squeeze") | |
536 | ||
537 | def test_shake128(me): me.check_shake(C.Shake128, 32) | |
538 | def test_shake256(me): me.check_shake(C.Shake256, 64) | |
539 | ||
540 | def check_kmac(me, mcls, c): | |
541 | k = T.span(32) | |
542 | me.check_shake(lambda func = None, perso = T.bin(""): | |
543 | mcls(k, perso = perso), | |
544 | c, done_matches_xof = False) | |
545 | ||
546 | def test_kmac128(me): me.check_kmac(C.KMAC128, 32) | |
547 | def test_kmac256(me): me.check_kmac(C.KMAC256, 64) | |
548 | ||
549 | ###-------------------------------------------------------------------------- | |
550 | class TestPRP (T.GenericTestMixin): | |
551 | """Test pseudorandom permutations (PRPs).""" | |
552 | ||
553 | def _test_prp(me, pcls): | |
554 | ||
555 | ## Check the PRP properties. | |
556 | me.assertEqual(type(pcls.name), str) | |
557 | me.assertTrue(isinstance(pcls.keysz, C.KeySZ)) | |
558 | me.assertEqual(type(pcls.blksz), int) | |
559 | ||
560 | ## Check round-tripping. | |
561 | k = T.span(pcls.keysz.default) | |
562 | key = pcls(k) | |
563 | m = T.span(pcls.blksz) | |
564 | c = key.encrypt(m) | |
565 | me.assertEqual(len(c), pcls.blksz) | |
566 | me.assertEqual(m, key.decrypt(c)) | |
567 | ||
568 | ## Check that bad key lengths are rejected. | |
569 | badlen = bad_key_size(pcls.keysz) | |
570 | if badlen is not None: me.assertRaises(ValueError, pcls, T.span(badlen)) | |
571 | ||
572 | ## Check that bad blocks are rejected. | |
573 | badblk = T.span(pcls.blksz + 1) | |
574 | me.assertRaises(ValueError, key.encrypt, badblk) | |
575 | me.assertRaises(ValueError, key.decrypt, badblk) | |
576 | ||
577 | TestPRP.generate_testcases((name, C.gcprps[name]) for name in | |
578 | ["desx", "blowfish", "rijndael"]) | |
579 | ||
580 | ###----- That's all, folks -------------------------------------------------- | |
581 | ||
582 | if __name__ == "__main__": U.main() |