chiark / gitweb /
8e073f2cdd4aeae2cb3830df293f720da7df6ecc
[catacomb-python] / t / t-algorithms.py
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)
152
153     ## A typical two-byte spec.  (No published algorithms actually /need/ a
154     ## two-byte key-size spec, but all of the HMAC variants use one anyway.)
155     ksz = C.sha256_hmac.keysz
156     me.assertEqual(type(ksz), C.KeySZAny)
157     me.assertEqual(ksz.default, 32)
158     me.assertEqual(ksz.min, 0)
159     me.assertEqual(ksz.max, 0)
160     for n in [0, 12, 20, 5000]:
161       me.assertTrue(ksz.check(n))
162       me.assertEqual(ksz.best(n), n)
163
164     ## Check construction.
165     ksz = C.KeySZAny(15)
166     me.assertEqual(ksz.default, 15)
167     me.assertEqual(ksz.min, 0)
168     me.assertEqual(ksz.max, 0)
169     me.assertRaises(ValueError, lambda: C.KeySZAny(-8))
170     me.assertEqual(C.KeySZAny(0).default, 0)
171
172   def test_set(me):
173     ## Note that no published algorithm uses a 16-bit `set' spec.
174
175     ## A typical spec.
176     ksz = C.salsa20.keysz
177     me.assertEqual(type(ksz), C.KeySZSet)
178     me.assertEqual(ksz.default, 32)
179     me.assertEqual(ksz.min, 10)
180     me.assertEqual(ksz.max, 32)
181     me.assertEqual(set(ksz.set), set([10, 16, 32]))
182     for x, best, pad in [(9, None, 10), (10, 10, 10), (11, 10, 16),
183                          (15, 10, 16), (16, 16, 16), (17, 16, 32),
184                          (31, 16, 32), (32, 32, 32), (33, 32, None)]:
185       if x == best == pad: me.assertTrue(ksz.check(x))
186       else: me.assertFalse(ksz.check(x))
187       if best is None: me.assertRaises(ValueError, ksz.best, x)
188       else: me.assertEqual(ksz.best(x), best)
189
190     ## Check construction.
191     ksz = C.KeySZSet(7)
192     me.assertEqual(ksz.default, 7)
193     me.assertEqual(set(ksz.set), set([7]))
194     me.assertEqual(ksz.min, 7)
195     me.assertEqual(ksz.max, 7)
196     ksz = C.KeySZSet(7, [3, 6, 9])
197     me.assertEqual(ksz.default, 7)
198     me.assertEqual(set(ksz.set), set([3, 6, 7, 9]))
199     me.assertEqual(ksz.min, 3)
200     me.assertEqual(ksz.max, 9)
201
202   def test_range(me):
203     ## Note that no published algorithm uses a 16-bit `range' spec, or an
204     ## unbounded `range'.
205
206     ## A typical spec.
207     ksz = C.rijndael.keysz
208     me.assertEqual(type(ksz), C.KeySZRange)
209     me.assertEqual(ksz.default, 32)
210     me.assertEqual(ksz.min, 4)
211     me.assertEqual(ksz.max, 32)
212     me.assertEqual(ksz.mod, 4)
213     for x, best in [(3, None), (4, 4), (5, 4),
214                     (15, 12), (16, 16), (17, 16),
215                     (31, 28), (32, 32), (33, 32)]:
216       if x == best: me.assertTrue(ksz.check(x))
217       else: me.assertFalse(ksz.check(x))
218       if best is None: me.assertRaises(ValueError, ksz.best, x)
219       else: me.assertEqual(ksz.best(x), best)
220
221     ## Check construction.
222     ksz = C.KeySZRange(28, 21, 35, 7)
223     me.assertEqual(ksz.default, 28)
224     me.assertEqual(ksz.min, 21)
225     me.assertEqual(ksz.max, 35)
226     me.assertEqual(ksz.mod, 7)
227     me.assertRaises(ValueError, C.KeySZRange, 29, 21, 35, 7)
228     me.assertRaises(ValueError, C.KeySZRange, 28, 20, 35, 7)
229     me.assertRaises(ValueError, C.KeySZRange, 28, 21, 34, 7)
230     me.assertRaises(ValueError, C.KeySZRange, 28, -7, 35, 7)
231     me.assertRaises(ValueError, C.KeySZRange, 28, 35, 21, 7)
232     me.assertRaises(ValueError, C.KeySZRange, 35, 21, 28, 7)
233     me.assertRaises(ValueError, C.KeySZRange, 21, 28, 35, 7)
234
235   def test_conversions(me):
236     me.assertEqual(C.KeySZ.fromec(256), 128)
237     me.assertEqual(C.KeySZ.fromschnorr(256), 128)
238     me.assertEqual(round(C.KeySZ.fromdl(2958.6875)), 128)
239     me.assertEqual(round(C.KeySZ.fromif(2958.6875)), 128)
240     me.assertEqual(C.KeySZ.toec(128), 256)
241     me.assertEqual(C.KeySZ.toschnorr(128), 256)
242     me.assertEqual(C.KeySZ.todl(128), 2958.6875)
243     me.assertEqual(C.KeySZ.toif(128), 2958.6875)
244
245 ###--------------------------------------------------------------------------
246 class TestCipher (T.GenericTestMixin):
247   """Test basic symmetric ciphers."""
248
249   def _test_cipher(me, ccls):
250
251     ## Check the class properties.
252     me.assertEqual(type(ccls.name), str)
253     me.assertTrue(isinstance(ccls.keysz, C.KeySZ))
254     me.assertEqual(type(ccls.blksz), int)
255
256     ## Check round-tripping.
257     k = T.span(ccls.keysz.default)
258     iv = T.span(ccls.blksz)
259     m = T.span(253)
260     enc = ccls(k)
261     dec = ccls(k)
262     try: enc.setiv(iv)
263     except ValueError: can_setiv = False
264     else:
265       can_setiv = True
266       dec.setiv(iv)
267     c0 = enc.encrypt(m[0:57])
268     m0 = dec.decrypt(c0)
269     c1 = enc.encrypt(m[57:189])
270     m1 = dec.decrypt(c1)
271     try: enc.bdry()
272     except ValueError: can_bdry = False
273     else:
274       dec.bdry()
275       can_bdry = True
276     c2 = enc.encrypt(m[189:253])
277     m2 = dec.decrypt(c2)
278     me.assertEqual(len(c0) + len(c1) + len(c2), len(m))
279     me.assertEqual(m0, m[0:57])
280     me.assertEqual(m1, m[57:189])
281     me.assertEqual(m2, m[189:253])
282
283     ## Check the `enczero' and `deczero' methods.
284     c3 = enc.enczero(32)
285     me.assertEqual(dec.decrypt(c3), C.ByteString.zero(32))
286     m4 = dec.deczero(32)
287     me.assertEqual(enc.encrypt(m4), C.ByteString.zero(32))
288
289     ## Check that ciphers which support a `boundary' operation actually
290     ## need it.
291     if can_bdry:
292       dec = ccls(k)
293       if can_setiv: dec.setiv(iv)
294       m01 = dec.decrypt(c0 + c1)
295       me.assertEqual(m01, m[0:189])
296
297     ## Check that the boundary actually does something.
298     if can_bdry:
299       dec = ccls(k)
300       if can_setiv: dec.setiv(iv)
301       m012 = dec.decrypt(c0 + c1 + c2)
302       me.assertNotEqual(m012, m)
303
304     ## Check that bad key lengths are rejected.
305     badlen = bad_key_size(ccls.keysz)
306     if badlen is not None: me.assertRaises(ValueError, ccls, T.span(badlen))
307
308 TestCipher.generate_testcases((name, C.gcciphers[name]) for name in
309   ["des-ecb", "rijndael-cbc", "twofish-cfb", "serpent-ofb",
310    "blowfish-counter", "rc4", "seal", "salsa20/8", "shake128-xof"])
311
312 ###--------------------------------------------------------------------------
313 class BaseTestHash (HashBufferTestMixin):
314   """Base class for testing hash functions."""
315
316   def check_hash(me, hcls, need_bufsz = True):
317     """
318     Check hash class HCLS.
319
320     If NEED_BUFSZ is false, then don't insist that HCLS have working `bufsz',
321     `name', or `hashsz' attributes.  This test is mostly reused for MACs,
322     which don't have these attributes.
323     """
324     ## Check the class properties.
325     if need_bufsz:
326       me.assertEqual(type(hcls.name), str)
327       me.assertEqual(type(hcls.bufsz), int)
328       me.assertEqual(type(hcls.hashsz), int)
329
330     ## Set some initial values.
331     m = T.span(131)
332     h = hcls().hash(m).done()
333
334     ## Check that hash length comes out right.
335     if need_bufsz: me.assertEqual(len(h), hcls.hashsz)
336
337     ## Check that we get the same answer if we split the message up.
338     me.assertEqual(h, hcls().hash(m[0:73]).hash(m[73:131]).done())
339
340     ## Check the `check' method.
341     me.assertTrue(hcls().hash(m).check(h))
342     me.assertFalse(hcls().hash(m).check(h ^ len(h)*C.bytes("aa")))
343
344     ## Check the menagerie of random hashing methods.
345     def mkhash(_):
346       h = hcls()
347       return h, h.done
348     me.check_hashbuffer(mkhash)
349
350 class TestHash (BaseTestHash, T.GenericTestMixin):
351   """Test hash functions."""
352   def _test_hash(me, hcls):    me.check_hash(hcls, need_bufsz = True)
353
354 TestHash.generate_testcases((name, C.gchashes[name]) for name in
355   ["md5", "sha", "whirlpool", "sha256", "sha512/224", "sha3-384", "shake256",
356    "crc32"])
357
358 ###--------------------------------------------------------------------------
359 class TestMessageAuthentication (BaseTestHash, T.GenericTestMixin):
360   """Test message authentication codes."""
361
362   def _test_mac(me, mcls):
363
364     ## Check the MAC properties.
365     me.assertEqual(type(mcls.name), str)
366     me.assertTrue(isinstance(mcls.keysz, C.KeySZ))
367     me.assertEqual(type(mcls.tagsz), int)
368
369     ## Test hashing.
370     k = T.span(mcls.keysz.default)
371     key = mcls(k)
372     me.check_hash(key, need_bufsz = False)
373
374     ## Check that bad key lengths are rejected.
375     badlen = bad_key_size(mcls.keysz)
376     if badlen is not None: me.assertRaises(ValueError, mcls, T.span(badlen))
377
378 TestMessageAuthentication.generate_testcases \
379   ((name, C.gcmacs[name]) for name in
380    ["sha-hmac", "rijndael-cmac", "twofish-pmac1", "kmac128"])
381
382 class TestPoly1305 (HashBufferTestMixin):
383   """Check the Poly1305 one-time message authentication function."""
384
385   def test_poly1305(me):
386
387     ## Check the MAC properties.
388     me.assertEqual(C.poly1305.name, "poly1305")
389     me.assertEqual(type(C.poly1305.keysz), C.KeySZSet)
390     me.assertEqual(C.poly1305.keysz.default, 16)
391     me.assertEqual(set(C.poly1305.keysz.set), set([16]))
392     me.assertEqual(C.poly1305.tagsz, 16)
393     me.assertEqual(C.poly1305.masksz, 16)
394
395     ## Set some initial values.
396     k = T.span(16)
397     u = T.span(64)[-16:]
398     m = T.span(149)
399     key = C.poly1305(k)
400     t = key(u).hash(m).done()
401
402     ## Check the key properties.
403     me.assertEqual(len(t), 16)
404
405     ## Check that we get the same answer if we split the message up.
406     me.assertEqual(t, key(u).hash(m[0:86]).hash(m[86:149]).done())
407
408     ## Check the `check' method.
409     me.assertTrue(key(u).hash(m).check(t))
410     me.assertFalse(key(u).hash(m).check(t ^ 16*C.bytes("cc")))
411
412     ## Check the menagerie of random hashing methods.
413     def mkhash(_):
414       h = key(u)
415       return h, h.done
416     me.check_hashbuffer(mkhash)
417
418     ## Check that we can't complete hashing without a mask.
419     me.assertRaises(ValueError, key().hash(m).done)
420
421     ## Check `concat'.
422     h0 = key().hash(m[0:96])
423     h1 = key().hash(m[96:117])
424     me.assertEqual(t, key(u).concat(h0, h1).hash(m[117:149]).done())
425     key1 = C.poly1305(k)
426     me.assertRaises(TypeError, key().concat, key1().hash(m[0:96]), h1)
427     me.assertRaises(TypeError, key().concat, h0, key1().hash(m[96:117]))
428     me.assertRaises(ValueError, key().concat, key().hash(m[0:93]), h1)
429
430 ###--------------------------------------------------------------------------
431 class TestHLatin (U.TestCase):
432   """Test the `hsalsa20' and `hchacha20' functions."""
433
434   def test_hlatin(me):
435     kk = [T.span(sz) for sz in [32]]
436     n = T.span(16)
437     bad_k = T.span(18)
438     bad_n = T.span(13)
439     for fn in [C.hsalsa208_prf, C.hsalsa2012_prf, C.hsalsa20_prf,
440                C.hchacha8_prf, C.hchacha12_prf, C.hchacha20_prf]:
441       for k in kk:
442         h = fn(k, n)
443         me.assertEqual(len(h), 32)
444       me.assertRaises(ValueError, fn, bad_k, n)
445       me.assertRaises(ValueError, fn, k, bad_n)
446
447 ###--------------------------------------------------------------------------
448 class TestKeccak (HashBufferTestMixin):
449   """Test the Keccak-p[1600, n] sponge function."""
450
451   def test_keccak(me):
452
453     ## Make a state and feed some stuff into it.
454     m0 = T.bin("some initial string")
455     m1 = T.bin("awesome follow-up string")
456     st0 = C.Keccak1600()
457     me.assertEqual(st0.nround, 24)
458     st0.mix(m0).step()
459
460     ## Make another step with a different round count.
461     st1 = C.Keccak1600(23)
462     st1.mix(m0).step()
463     me.assertNotEqual(st0.extract(32), st1.extract(32))
464
465     ## Check error conditions.
466     _ = st0.extract(200)
467     me.assertRaises(ValueError, st0.extract, 201)
468     st0.mix(T.span(200))
469     me.assertRaises(ValueError, st0.mix, T.span(201))
470
471   def check_shake(me, xcls, c, done_matches_xof = True):
472     """
473     Test the SHAKE and cSHAKE XOFs.
474
475     This is also used for testing KMAC, but that sets DONE_MATCHES_XOF false
476     to indicate that the XOF output is range-separated from the fixed-length
477     outputs (unlike the basic SHAKE functions).
478     """
479
480     ## Check the hash attributes.
481     x = xcls()
482     me.assertEqual(x.rate, 200 - c)
483     me.assertEqual(x.buffered, 0)
484     me.assertEqual(x.state, "absorb")
485
486     ## Set some initial values.
487     func = T.bin("TESTXOF")
488     perso = T.bin("catacomb-python test")
489     m = T.span(167)
490     h0 = xcls().hash(m).done(193)
491     me.assertEqual(len(h0), 193)
492     h1 = xcls(func = func, perso = perso).hash(m).done(193)
493     me.assertEqual(len(h1), 193)
494     me.assertNotEqual(h0, h1)
495
496     ## Check input and output in pieces, and the state machine.
497     if done_matches_xof: h = h0
498     else: h = xcls().hash(m).xof().get(len(h0))
499     x = xcls().hash(m[0:76]).hash(m[76:167]).xof()
500     me.assertEqual(h, x.get(98) + x.get(95))
501
502     ## Check masking.
503     x = xcls().hash(m).xof()
504     me.assertEqual(x.mask(m), C.ByteString(m) ^ C.ByteString(h[0:len(m)]))
505
506     ## Check the `check' method.
507     me.assertTrue(xcls().hash(m).check(h0))
508     me.assertFalse(xcls().hash(m).check(h1))
509
510     ## Check the menagerie of random hashing methods.
511     def mkhash(_):
512       x = xcls(func = func, perso = perso)
513       return x, lambda: x.done(100 - x.rate//2)
514     me.check_hashbuffer(mkhash)
515
516     ## Check the state machine tracking.
517     x = xcls(); me.assertEqual(x.state, "absorb")
518     x.hash(m); me.assertEqual(x.state, "absorb")
519     xx = x.copy()
520     h = xx.done(100 - x.rate//2)
521     me.assertEqual(xx.state, "dead")
522     me.assertRaises(ValueError, xx.done, 1)
523     me.assertRaises(ValueError, xx.get, 1)
524     me.assertEqual(x.state, "absorb")
525     me.assertRaises(ValueError, x.get, 1)
526     x.xof(); me.assertEqual(x.state, "squeeze")
527     me.assertRaises(ValueError, x.done, 1)
528     _ = x.get(1)
529     yy = x.copy(); me.assertEqual(yy.state, "squeeze")
530
531   def test_shake128(me): me.check_shake(C.Shake128, 32)
532   def test_shake256(me): me.check_shake(C.Shake256, 64)
533
534   def check_kmac(me, mcls, c):
535     k = T.span(32)
536     me.check_shake(lambda func = None, perso = T.bin(""):
537                      mcls(k, perso = perso),
538                    c, done_matches_xof = False)
539
540   def test_kmac128(me): me.check_kmac(C.KMAC128, 32)
541   def test_kmac256(me): me.check_kmac(C.KMAC256, 64)
542
543 ###--------------------------------------------------------------------------
544 class TestPRP (T.GenericTestMixin):
545   """Test pseudorandom permutations (PRPs)."""
546
547   def _test_prp(me, pcls):
548
549     ## Check the PRP properties.
550     me.assertEqual(type(pcls.name), str)
551     me.assertTrue(isinstance(pcls.keysz, C.KeySZ))
552     me.assertEqual(type(pcls.blksz), int)
553
554     ## Check round-tripping.
555     k = T.span(pcls.keysz.default)
556     key = pcls(k)
557     m = T.span(pcls.blksz)
558     c = key.encrypt(m)
559     me.assertEqual(len(c), pcls.blksz)
560     me.assertEqual(m, key.decrypt(c))
561
562     ## Check that bad key lengths are rejected.
563     badlen = bad_key_size(pcls.keysz)
564     if badlen is not None: me.assertRaises(ValueError, pcls, T.span(badlen))
565
566     ## Check that bad blocks are rejected.
567     badblk = T.span(pcls.blksz + 1)
568     me.assertRaises(ValueError, key.encrypt, badblk)
569     me.assertRaises(ValueError, key.decrypt, badblk)
570
571 TestPRP.generate_testcases((name, C.gcprps[name]) for name in
572   ["desx", "blowfish", "rijndael"])
573
574 ###----- That's all, folks --------------------------------------------------
575
576 if __name__ == "__main__": U.main()