chiark / gitweb /
t/: Add a test suite.
[catacomb-python] / t / t-algorithms.py
CommitLineData
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
30import catacomb as C
31import unittest as U
32import testutils as T
33
34###--------------------------------------------------------------------------
35### Utilities.
36
37def 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
51def 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
64class 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###--------------------------------------------------------------------------
139class 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###--------------------------------------------------------------------------
246class 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
308TestCipher.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###--------------------------------------------------------------------------
313class 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
350class TestHash (BaseTestHash, T.GenericTestMixin):
351 """Test hash functions."""
352 def _test_hash(me, hcls): me.check_hash(hcls, need_bufsz = True)
353
354TestHash.generate_testcases((name, C.gchashes[name]) for name in
355 ["md5", "sha", "whirlpool", "sha256", "sha512/224", "sha3-384", "shake256",
356 "crc32"])
357
358###--------------------------------------------------------------------------
359class 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
378TestMessageAuthentication.generate_testcases \
379 ((name, C.gcmacs[name]) for name in
380 ["sha-hmac", "rijndael-cmac", "twofish-pmac1", "kmac128"])
381
382class 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###--------------------------------------------------------------------------
431class 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###--------------------------------------------------------------------------
448class 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###--------------------------------------------------------------------------
544class 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
571TestPRP.generate_testcases((name, C.gcprps[name]) for name in
572 ["desx", "blowfish", "rijndael"])
573
574###----- That's all, folks --------------------------------------------------
575
576if __name__ == "__main__": U.main()