algorithms.c: Add a Keccak `set' method now there's upstream support.
[catacomb-python] / t / t-algorithms.py
CommitLineData
ffad1322
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
8d507e79 41 elif ksz.max is not None: return ksz.max + 1
ffad1322
MW
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
8d507e79 55 elif ksz.max is None or sz < ksz.max: return sz + ksz.mod
ffad1322
MW
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)
b4dbeed5 79 me.assertRaises(OverflowError, hashfn, h0, 1 << 8*w)
ffad1322
MW
80
81 def check_hashbuffer_bufn(me, w, bigendp, makefn, hashfn):
82 """Check `hashbufN'."""
83
84 ## Go through a number of different sizes.
85 for n in [0, 1, 7, 8, 19, 255, 12345, 65535, 123456]:
86 if n >= 1 << 8*w: continue
87 h0, donefn0 = makefn(2 + w + n)
88 hashfn(h0.hashu8(0x00), T.span(n)).hashu8(0xff)
89 h1, donefn1 = makefn(2 + w + n)
90 h1.hash(T.prep_lenseq(w, n, bigendp, True))
91 me.assertEqual(donefn0(), donefn1())
92
93 ## Check blocks which are too large for the length prefix.
94 if w <= 3:
95 n = 1 << 8*w
96 h0, _ = makefn(w + n)
37e55404 97 me.assertRaises(ValueError, hashfn, h0, C.ByteString.zero(n))
ffad1322
MW
98
99 def check_hashbuffer(me, makefn):
100 """Test the various `hash...' methods."""
101
102 ## Check `hashuN'.
103 me.check_hashbuffer_hashn(1, True, makefn, lambda h, n: h.hashu8(n))
104 me.check_hashbuffer_hashn(2, True, makefn, lambda h, n: h.hashu16(n))
105 me.check_hashbuffer_hashn(2, True, makefn, lambda h, n: h.hashu16b(n))
106 me.check_hashbuffer_hashn(2, False, makefn, lambda h, n: h.hashu16l(n))
385caa2f
MW
107 me.check_hashbuffer_hashn(3, True, makefn, lambda h, n: h.hashu24(n))
108 me.check_hashbuffer_hashn(3, True, makefn, lambda h, n: h.hashu24b(n))
109 me.check_hashbuffer_hashn(3, False, makefn, lambda h, n: h.hashu24l(n))
ffad1322
MW
110 me.check_hashbuffer_hashn(4, True, makefn, lambda h, n: h.hashu32(n))
111 me.check_hashbuffer_hashn(4, True, makefn, lambda h, n: h.hashu32b(n))
112 me.check_hashbuffer_hashn(4, False, makefn, lambda h, n: h.hashu32l(n))
113 if hasattr(makefn(0)[0], "hashu64"):
114 me.check_hashbuffer_hashn(8, True, makefn, lambda h, n: h.hashu64(n))
115 me.check_hashbuffer_hashn(8, True, makefn, lambda h, n: h.hashu64b(n))
116 me.check_hashbuffer_hashn(8, False, makefn, lambda h, n: h.hashu64l(n))
117
118 ## Check `hashbufN'.
119 me.check_hashbuffer_bufn(1, True, makefn, lambda h, x: h.hashbuf8(x))
120 me.check_hashbuffer_bufn(2, True, makefn, lambda h, x: h.hashbuf16(x))
121 me.check_hashbuffer_bufn(2, True, makefn, lambda h, x: h.hashbuf16b(x))
122 me.check_hashbuffer_bufn(2, False, makefn, lambda h, x: h.hashbuf16l(x))
385caa2f
MW
123 me.check_hashbuffer_bufn(3, True, makefn, lambda h, x: h.hashbuf24(x))
124 me.check_hashbuffer_bufn(3, True, makefn, lambda h, x: h.hashbuf24b(x))
125 me.check_hashbuffer_bufn(3, False, makefn, lambda h, x: h.hashbuf24l(x))
ffad1322
MW
126 me.check_hashbuffer_bufn(4, True, makefn, lambda h, x: h.hashbuf32(x))
127 me.check_hashbuffer_bufn(4, True, makefn, lambda h, x: h.hashbuf32b(x))
128 me.check_hashbuffer_bufn(4, False, makefn, lambda h, x: h.hashbuf32l(x))
129 if hasattr(makefn(0)[0], "hashbuf64"):
130 me.check_hashbuffer_bufn(8, True, makefn, lambda h, x: h.hashbuf64(x))
131 me.check_hashbuffer_bufn(8, True, makefn, lambda h, x: h.hashbuf64b(x))
132 me.check_hashbuffer_bufn(8, False, makefn, lambda h, x: h.hashbuf64l(x))
133
134###--------------------------------------------------------------------------
135class TestKeysize (U.TestCase):
136
137 def test_any(me):
138
139 ## A typical one-byte spec.
140 ksz = C.seal.keysz
141 me.assertEqual(type(ksz), C.KeySZAny)
142 me.assertEqual(ksz.default, 20)
143 me.assertEqual(ksz.min, 0)
8d507e79 144 me.assertEqual(ksz.max, None)
ffad1322
MW
145 for n in [0, 12, 20, 5000]:
146 me.assertTrue(ksz.check(n))
147 me.assertEqual(ksz.best(n), n)
1752830c 148 me.assertEqual(ksz.pad(n), n)
ffad1322
MW
149
150 ## A typical two-byte spec. (No published algorithms actually /need/ a
151 ## two-byte key-size spec, but all of the HMAC variants use one anyway.)
152 ksz = C.sha256_hmac.keysz
153 me.assertEqual(type(ksz), C.KeySZAny)
154 me.assertEqual(ksz.default, 32)
155 me.assertEqual(ksz.min, 0)
8d507e79 156 me.assertEqual(ksz.max, None)
ffad1322
MW
157 for n in [0, 12, 20, 5000]:
158 me.assertTrue(ksz.check(n))
159 me.assertEqual(ksz.best(n), n)
1752830c 160 me.assertEqual(ksz.pad(n), n)
ffad1322
MW
161
162 ## Check construction.
163 ksz = C.KeySZAny(15)
164 me.assertEqual(ksz.default, 15)
165 me.assertEqual(ksz.min, 0)
8d507e79 166 me.assertEqual(ksz.max, None)
ffad1322
MW
167 me.assertRaises(ValueError, lambda: C.KeySZAny(-8))
168 me.assertEqual(C.KeySZAny(0).default, 0)
169
170 def test_set(me):
171 ## Note that no published algorithm uses a 16-bit `set' spec.
172
173 ## A typical spec.
174 ksz = C.salsa20.keysz
175 me.assertEqual(type(ksz), C.KeySZSet)
176 me.assertEqual(ksz.default, 32)
177 me.assertEqual(ksz.min, 10)
178 me.assertEqual(ksz.max, 32)
10011e42 179 me.assertEqual(ksz.set, set([10, 16, 32]))
ffad1322
MW
180 for x, best, pad in [(9, None, 10), (10, 10, 10), (11, 10, 16),
181 (15, 10, 16), (16, 16, 16), (17, 16, 32),
182 (31, 16, 32), (32, 32, 32), (33, 32, None)]:
183 if x == best == pad: me.assertTrue(ksz.check(x))
184 else: me.assertFalse(ksz.check(x))
185 if best is None: me.assertRaises(ValueError, ksz.best, x)
186 else: me.assertEqual(ksz.best(x), best)
1752830c
MW
187 if pad is None: me.assertRaises(ValueError, ksz.pad, x)
188 else: me.assertEqual(ksz.pad(x), pad)
ffad1322
MW
189
190 ## Check construction.
191 ksz = C.KeySZSet(7)
192 me.assertEqual(ksz.default, 7)
10011e42 193 me.assertEqual(ksz.set, set([7]))
ffad1322
MW
194 me.assertEqual(ksz.min, 7)
195 me.assertEqual(ksz.max, 7)
10011e42 196 ksz = C.KeySZSet(7, iter([3, 6, 9]))
ffad1322 197 me.assertEqual(ksz.default, 7)
10011e42 198 me.assertEqual(ksz.set, set([3, 6, 7, 9]))
ffad1322
MW
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)
1752830c
MW
213 for x, best, pad in [(3, None, 4), (4, 4, 4), (5, 4, 8),
214 (15, 12, 16), (16, 16, 16), (17, 16, 20),
215 (31, 28, 32), (32, 32, 32), (33, 32, None)]:
216 if x == best == pad: me.assertTrue(ksz.check(x))
ffad1322
MW
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)
1752830c
MW
220 if pad is None: me.assertRaises(ValueError, ksz.pad, x)
221 else: me.assertEqual(ksz.pad(x), pad)
ffad1322
MW
222
223 ## Check construction.
224 ksz = C.KeySZRange(28, 21, 35, 7)
225 me.assertEqual(ksz.default, 28)
226 me.assertEqual(ksz.min, 21)
227 me.assertEqual(ksz.max, 35)
228 me.assertEqual(ksz.mod, 7)
8d507e79
MW
229 ksz = C.KeySZRange(28, 21, None, 7)
230 me.assertEqual(ksz.min, 21)
231 me.assertEqual(ksz.max, None)
232 me.assertEqual(ksz.mod, 7)
233 me.assertEqual(ksz.pad(36), 42)
ffad1322
MW
234 me.assertRaises(ValueError, C.KeySZRange, 29, 21, 35, 7)
235 me.assertRaises(ValueError, C.KeySZRange, 28, 20, 35, 7)
236 me.assertRaises(ValueError, C.KeySZRange, 28, 21, 34, 7)
237 me.assertRaises(ValueError, C.KeySZRange, 28, -7, 35, 7)
238 me.assertRaises(ValueError, C.KeySZRange, 28, 35, 21, 7)
239 me.assertRaises(ValueError, C.KeySZRange, 35, 21, 28, 7)
240 me.assertRaises(ValueError, C.KeySZRange, 21, 28, 35, 7)
241
242 def test_conversions(me):
243 me.assertEqual(C.KeySZ.fromec(256), 128)
244 me.assertEqual(C.KeySZ.fromschnorr(256), 128)
245 me.assertEqual(round(C.KeySZ.fromdl(2958.6875)), 128)
246 me.assertEqual(round(C.KeySZ.fromif(2958.6875)), 128)
247 me.assertEqual(C.KeySZ.toec(128), 256)
248 me.assertEqual(C.KeySZ.toschnorr(128), 256)
249 me.assertEqual(C.KeySZ.todl(128), 2958.6875)
250 me.assertEqual(C.KeySZ.toif(128), 2958.6875)
251
252###--------------------------------------------------------------------------
253class TestCipher (T.GenericTestMixin):
254 """Test basic symmetric ciphers."""
255
256 def _test_cipher(me, ccls):
257
258 ## Check the class properties.
259 me.assertEqual(type(ccls.name), str)
260 me.assertTrue(isinstance(ccls.keysz, C.KeySZ))
261 me.assertEqual(type(ccls.blksz), int)
262
263 ## Check round-tripping.
264 k = T.span(ccls.keysz.default)
265 iv = T.span(ccls.blksz)
266 m = T.span(253)
267 enc = ccls(k)
268 dec = ccls(k)
269 try: enc.setiv(iv)
270 except ValueError: can_setiv = False
271 else:
272 can_setiv = True
273 dec.setiv(iv)
274 c0 = enc.encrypt(m[0:57])
275 m0 = dec.decrypt(c0)
276 c1 = enc.encrypt(m[57:189])
277 m1 = dec.decrypt(c1)
278 try: enc.bdry()
279 except ValueError: can_bdry = False
280 else:
281 dec.bdry()
282 can_bdry = True
283 c2 = enc.encrypt(m[189:253])
284 m2 = dec.decrypt(c2)
285 me.assertEqual(len(c0) + len(c1) + len(c2), len(m))
286 me.assertEqual(m0, m[0:57])
287 me.assertEqual(m1, m[57:189])
288 me.assertEqual(m2, m[189:253])
289
290 ## Check the `enczero' and `deczero' methods.
291 c3 = enc.enczero(32)
292 me.assertEqual(dec.decrypt(c3), C.ByteString.zero(32))
293 m4 = dec.deczero(32)
294 me.assertEqual(enc.encrypt(m4), C.ByteString.zero(32))
295
296 ## Check that ciphers which support a `boundary' operation actually
297 ## need it.
298 if can_bdry:
299 dec = ccls(k)
300 if can_setiv: dec.setiv(iv)
301 m01 = dec.decrypt(c0 + c1)
302 me.assertEqual(m01, m[0:189])
303
304 ## Check that the boundary actually does something.
305 if can_bdry:
306 dec = ccls(k)
307 if can_setiv: dec.setiv(iv)
308 m012 = dec.decrypt(c0 + c1 + c2)
309 me.assertNotEqual(m012, m)
310
311 ## Check that bad key lengths are rejected.
312 badlen = bad_key_size(ccls.keysz)
313 if badlen is not None: me.assertRaises(ValueError, ccls, T.span(badlen))
314
315TestCipher.generate_testcases((name, C.gcciphers[name]) for name in
316 ["des-ecb", "rijndael-cbc", "twofish-cfb", "serpent-ofb",
317 "blowfish-counter", "rc4", "seal", "salsa20/8", "shake128-xof"])
318
319###--------------------------------------------------------------------------
77f6ff2d
MW
320class TestAuthenticatedEncryption \
321 (HashBufferTestMixin, T.GenericTestMixin):
322 """Test authenticated encryption schemes."""
323
324 def _test_aead(me, aecls):
325
326 ## Check the class properties.
327 me.assertEqual(type(aecls.name), str)
328 me.assertTrue(isinstance(aecls.keysz, C.KeySZ))
329 me.assertTrue(isinstance(aecls.noncesz, C.KeySZ))
330 me.assertTrue(isinstance(aecls.tagsz, C.KeySZ))
331 me.assertEqual(type(aecls.blksz), int)
332 me.assertEqual(type(aecls.bufsz), int)
333 me.assertEqual(type(aecls.ohd), int)
334 me.assertEqual(type(aecls.flags), int)
335
336 ## Check round-tripping, with full precommitment. First, select some
337 ## parameters. (It's conceivable that some AEAD schemes are more
338 ## restrictive than advertised by the various properties, but this works
339 ## out OK in practice.)
340 k = T.span(aecls.keysz.default)
341 n = T.span(aecls.noncesz.default)
342 if aecls.flags&C.AEADF_NOAAD: h = T.span(0)
343 else: h = T.span(131)
344 m = T.span(253)
345 tsz = aecls.tagsz.default
346 key = aecls(k)
347
348 ## Next, encrypt a message, checking that things are proper as we go.
349 enc = key.enc(nonce = n, hsz = len(h), msz = len(m), tsz = tsz)
350 me.assertEqual(enc.hsz, len(h))
351 me.assertEqual(enc.msz, len(m))
352 me.assertEqual(enc.mlen, 0)
353 me.assertEqual(enc.tsz, tsz)
354 aad = enc.aad()
355 if aecls.flags&C.AEADF_AADNDEP: me.assertEqual(aad.hsz, len(h))
356 else: me.assertEqual(aad.hsz, None)
357 me.assertEqual(aad.hlen, 0)
358 if not aecls.flags&C.AEADF_NOAAD:
359 aad.hash(h[0:83])
360 me.assertEqual(aad.hlen, 83)
361 aad.hash(h[83:131])
362 me.assertEqual(aad.hlen, 131)
363 c0 = enc.encrypt(m[0:57])
364 me.assertEqual(enc.mlen, 57)
365 me.assertTrue(57 - aecls.bufsz <= len(c0) <= 57 + aecls.ohd)
366 c1 = enc.encrypt(m[57:189])
367 me.assertEqual(enc.mlen, 189)
368 me.assertTrue(132 - aecls.bufsz <= len(c1) <=
369 132 + aecls.bufsz + aecls.ohd)
370 c2 = enc.encrypt(m[189:253])
371 me.assertEqual(enc.mlen, 253)
372 me.assertTrue(64 - aecls.bufsz <= len(c2) <=
373 64 + aecls.bufsz + aecls.ohd)
374 c3, t = enc.done(aad = aad)
375 me.assertTrue(len(c3) <= aecls.bufsz + aecls.ohd)
376 c = c0 + c1 + c2 + c3
377 me.assertTrue(len(m) <= len(c) <= len(m) + aecls.ohd)
378 me.assertEqual(len(t), tsz)
379
380 ## And now decrypt it again, with different record boundaries.
381 dec = key.dec(nonce = n, hsz = len(h), csz = len(c), tsz = tsz)
382 me.assertEqual(dec.hsz, len(h))
383 me.assertEqual(dec.csz, len(c))
384 me.assertEqual(dec.clen, 0)
385 me.assertEqual(dec.tsz, tsz)
386 aad = dec.aad()
387 if aecls.flags&C.AEADF_AADNDEP: me.assertEqual(aad.hsz, len(h))
388 else: me.assertEqual(aad.hsz, None)
389 me.assertEqual(aad.hlen, 0)
390 aad.hash(h)
391 m0 = dec.decrypt(c[0:156])
392 me.assertTrue(156 - aecls.bufsz <= len(m0) <= 156)
393 m1 = dec.decrypt(c[156:])
394 me.assertTrue(len(c) - 156 - aecls.bufsz <= len(m1) <=
395 len(c) - 156 + aecls.bufsz)
396 m2 = dec.done(tag = t, aad = aad)
397 me.assertEqual(m0 + m1 + m2, m)
398
399 ## And again, with the wrong tag.
400 dec = key.dec(nonce = n, hsz = len(h), csz = len(c), tsz = tsz)
401 aad = dec.aad(); aad.hash(h)
402 _ = dec.decrypt(c)
403 me.assertRaises(ValueError, dec.done, tag = t ^ tsz*C.bytes("55"))
404
405 ## Check that the all-in-one methods work.
406 me.assertEqual((c, t),
407 key.encrypt(n = n, h = h, m = m, tsz = tsz))
408 me.assertEqual(m,
409 key.decrypt(n = n, h = h, c = c, t = t))
410
411 ## Check that bad key, nonce, and tag lengths are rejected.
412 badlen = bad_key_size(aecls.keysz)
413 if badlen is not None: me.assertRaises(ValueError, aecls, T.span(badlen))
414 badlen = bad_key_size(aecls.noncesz)
415 if badlen is not None:
416 me.assertRaises(ValueError, key.enc, nonce = T.span(badlen),
417 hsz = len(h), msz = len(m), tsz = tsz)
418 me.assertRaises(ValueError, key.dec, nonce = T.span(badlen),
419 hsz = len(h), csz = len(c), tsz = tsz)
420 if not aecls.flags&C.AEADF_PCTSZ:
421 enc = key.enc(nonce = n, hsz = 0, msz = len(m))
422 _ = enc.encrypt(m)
423 me.assertRaises(ValueError, enc.done, tsz = badlen)
424 badlen = bad_key_size(aecls.tagsz)
425 if badlen is not None:
426 me.assertRaises(ValueError, key.enc, nonce = n,
427 hsz = len(h), msz = len(m), tsz = badlen)
428 me.assertRaises(ValueError, key.dec, nonce = n,
429 hsz = len(h), csz = len(c), tsz = badlen)
430
431 ## Check that we can't get a loose `aad' object from a scheme which has
432 ## nonce-dependent AAD processing.
433 if aecls.flags&C.AEADF_AADNDEP: me.assertRaises(ValueError, key.aad)
434
435 ## Check the menagerie of AAD hashing methods.
436 if not aecls.flags&C.AEADF_NOAAD:
437 def mkhash(hsz):
438 enc = key.enc(nonce = n, hsz = hsz, msz = 0, tsz = tsz)
439 aad = enc.aad()
440 return aad, lambda: enc.done(aad = aad)[1]
441 me.check_hashbuffer(mkhash)
442
443 ## Check that encryption/decryption works with the given precommitments.
444 def quick_enc_check(**kw):
445 enc = key.enc(**kw)
446 aad = enc.aad().hash(h)
447 c0 = enc.encrypt(m); c1, tt = enc.done(aad = aad, tsz = tsz)
448 me.assertEqual((c, t), (c0 + c1, tt))
449 def quick_dec_check(**kw):
450 dec = key.dec(**kw)
451 aad = dec.aad().hash(h)
452 m0 = dec.decrypt(c); m1 = dec.done(aad = aad, tag = t)
453 me.assertEqual(m, m0 + m1)
454
455 ## Check that we can get away without precommitting to the header length
456 ## if and only if the AEAD scheme says it will let us.
457 if aecls.flags&C.AEADF_PCHSZ:
458 me.assertRaises(ValueError, key.enc, nonce = n,
459 msz = len(m), tsz = tsz)
460 me.assertRaises(ValueError, key.dec, nonce = n,
461 csz = len(c), tsz = tsz)
462 else:
463 quick_enc_check(nonce = n, msz = len(m), tsz = tsz)
464 quick_dec_check(nonce = n, csz = len(c), tsz = tsz)
465
466 ## Check that we can get away without precommitting to the message/
467 ## ciphertext length if and only if the AEAD scheme says it will let us.
468 if aecls.flags&C.AEADF_PCMSZ:
469 me.assertRaises(ValueError, key.enc, nonce = n,
470 hsz = len(h), tsz = tsz)
471 me.assertRaises(ValueError, key.dec, nonce = n,
472 hsz = len(h), tsz = tsz)
473 else:
474 quick_enc_check(nonce = n, hsz = len(h), tsz = tsz)
475 quick_dec_check(nonce = n, hsz = len(h), tsz = tsz)
476
477 ## Check that we can get away without precommitting to the tag length if
478 ## and only if the AEAD scheme says it will let us.
479 if aecls.flags&C.AEADF_PCTSZ:
480 me.assertRaises(ValueError, key.enc, nonce = n,
481 hsz = len(h), msz = len(m))
482 me.assertRaises(ValueError, key.dec, nonce = n,
483 hsz = len(h), csz = len(c))
484 else:
485 quick_enc_check(nonce = n, hsz = len(h), msz = len(m))
486 quick_dec_check(nonce = n, hsz = len(h), csz = len(c))
487
488 ## Check that if we precommit to the header length, we're properly held
489 ## to the commitment.
490 if not aecls.flags&C.AEADF_NOAAD:
491
492 ## First, check encryption with underrun. If we must supply AAD first,
493 ## then the underrun will be reported when we start trying to encrypt;
494 ## otherwise, checking is delayed until `done'.
495 enc = key.enc(nonce = n, hsz = len(h), msz = len(m), tsz = tsz)
496 aad = enc.aad().hash(h[0:83])
497 if aecls.flags&C.AEADF_AADFIRST:
498 me.assertRaises(ValueError, enc.encrypt, m)
499 else:
500 _ = enc.encrypt(m)
501 me.assertRaises(ValueError, enc.done, aad = aad)
502
503 ## Next, check decryption with underrun. If we must supply AAD first,
504 ## then the underrun will be reported when we start trying to encrypt;
505 ## otherwise, checking is delayed until `done'.
506 dec = key.dec(nonce = n, hsz = len(h), csz = len(c), tsz = tsz)
507 aad = dec.aad().hash(h[0:83])
508 if aecls.flags&C.AEADF_AADFIRST:
509 me.assertRaises(ValueError, dec.decrypt, c)
510 else:
511 _ = dec.decrypt(c)
512 me.assertRaises(ValueError, dec.done, tag = t, aad = aad)
513
514 ## If AAD processing is nonce-dependent then an overrun will be
515 ## detected imediately.
516 if aecls.flags&C.AEADF_AADNDEP:
517 enc = key.enc(nonce = n, hsz = len(h), msz = len(m), tsz = tsz)
518 aad = enc.aad().hash(h[0:83])
519 me.assertRaises(ValueError, aad.hash, h[82:131])
520 dec = key.dec(nonce = n, hsz = len(h), csz = len(c), tsz = tsz)
521 aad = dec.aad().hash(h[0:83])
522 me.assertRaises(ValueError, aad.hash, h[82:131])
523
524 ## Some additional tests for nonce-dependent `aad' objects.
525 if aecls.flags&C.AEADF_AADNDEP:
526
527 ## Check that `aad' objects can't be used once their parents are gone.
528 enc = key.enc(nonce = n, hsz = len(h), msz = len(m), tsz = tsz)
529 aad = enc.aad()
530 del enc
531 me.assertRaises(ValueError, aad.hash, h)
532
533 ## Check that they can't be crossed over.
534 enc0 = key.enc(nonce = n, hsz = len(h), msz = len(m), tsz = tsz)
535 enc1 = key.enc(nonce = n, hsz = len(h), msz = len(m), tsz = tsz)
536 enc0.aad().hash(h)
537 aad1 = enc1.aad().hash(h)
538 _ = enc0.encrypt(m)
539 me.assertRaises(ValueError, enc0.done, tsz = tsz, aad = aad1)
540
541 ## Test copying AAD.
542 if not aecls.flags&C.AEADF_AADNDEP and not aecls.flags&C.AEADF_NOAAD:
543 aad0 = key.aad()
544 aad0.hash(h[0:83])
545 aad1 = aad0.copy()
546 aad2 = aad1.copy()
547 aad0.hash(h[83:131])
548 aad1.hash(h[83:131])
549 aad2.hash(h[83:131] ^ 48*C.bytes("ff"))
550 me.assertEqual(key.enc(nonce = n, hsz = len(h),
551 msz = 0, tsz = tsz).done(aad = aad0),
552 key.enc(nonce = n, hsz = len(h),
553 msz = 0, tsz = tsz).done(aad = aad1))
554 me.assertNotEqual(key.enc(nonce = n, hsz = len(h),
555 msz = 0, tsz = tsz).done(aad = aad0),
556 key.enc(nonce = n, hsz = len(h),
557 msz = 0, tsz = tsz).done(aad = aad2))
558
559 ## Check that if we precommit to the message length, we're properly held
560 ## to the commitment. (Fortunately, this is way simpler than the AAD
561 ## case above.) First, try an underrun.
562 enc = key.enc(nonce = n, hsz = 0, msz = len(m), tsz = tsz)
563 _ = enc.encrypt(m[0:183])
564 me.assertRaises(ValueError, enc.done, tsz = tsz)
565 dec = key.dec(nonce = n, hsz = 0, csz = len(c), tsz = tsz)
566 _ = dec.decrypt(c[0:183])
567 me.assertRaises(ValueError, dec.done, tag = t)
568
569 ## And now an overrun.
570 enc = key.enc(nonce = n, hsz = 0, msz = 183, tsz = tsz)
571 me.assertRaises(ValueError, enc.encrypt, m)
572 dec = key.dec(nonce = n, hsz = 0, csz = 183, tsz = tsz)
573 me.assertRaises(ValueError, dec.decrypt, c)
574
575 ## Finally, check that if we precommit to a tag length, we're properly
576 ## held to the commitment. This depends on being able to find a tag size
577 ## which isn't the default.
578 tsz1 = different_key_size(aecls.tagsz, tsz)
579 if tsz1 is not None:
580 enc = key.enc(nonce = n, hsz = 0, msz = len(m), tsz = tsz1)
581 _ = enc.encrypt(m)
582 me.assertRaises(ValueError, enc.done, tsz = tsz)
583 dec = key.dec(nonce = n, hsz = len(h), csz = len(c), tsz = tsz1)
584 aad = dec.aad().hash(h)
585 _ = dec.decrypt(c)
586 me.assertRaises(ValueError, enc.done, tsz = tsz, aad = aad)
587
588TestAuthenticatedEncryption.generate_testcases \
589 ((name, C.gcaeads[name]) for name in
590 ["des3-ccm", "blowfish-ocb1", "square-ocb3", "rijndael-gcm",
591 "serpent-eax", "salsa20-naclbox", "chacha20-poly1305"])
592
593###--------------------------------------------------------------------------
ffad1322
MW
594class BaseTestHash (HashBufferTestMixin):
595 """Base class for testing hash functions."""
596
597 def check_hash(me, hcls, need_bufsz = True):
598 """
599 Check hash class HCLS.
600
5d04dade
MW
601 If NEED_BUFSZ is false, then don't insist that HCLS has a working `bufsz'
602 attribute. This test is mostly reused for MACs, which don't have this
603 attribute.
ffad1322
MW
604 """
605 ## Check the class properties.
5d04dade
MW
606 me.assertEqual(type(hcls.name), str)
607 if need_bufsz: me.assertEqual(type(hcls.bufsz), int)
608 me.assertEqual(type(hcls.hashsz), int)
ffad1322
MW
609
610 ## Set some initial values.
611 m = T.span(131)
612 h = hcls().hash(m).done()
613
614 ## Check that hash length comes out right.
5d04dade 615 me.assertEqual(len(h), hcls.hashsz)
ffad1322
MW
616
617 ## Check that we get the same answer if we split the message up.
618 me.assertEqual(h, hcls().hash(m[0:73]).hash(m[73:131]).done())
619
620 ## Check the `check' method.
621 me.assertTrue(hcls().hash(m).check(h))
5d04dade 622 me.assertFalse(hcls().hash(m).check(h ^ hcls.hashsz*C.bytes("aa")))
ffad1322
MW
623
624 ## Check the menagerie of random hashing methods.
625 def mkhash(_):
626 h = hcls()
627 return h, h.done
628 me.check_hashbuffer(mkhash)
629
630class TestHash (BaseTestHash, T.GenericTestMixin):
631 """Test hash functions."""
632 def _test_hash(me, hcls): me.check_hash(hcls, need_bufsz = True)
633
634TestHash.generate_testcases((name, C.gchashes[name]) for name in
635 ["md5", "sha", "whirlpool", "sha256", "sha512/224", "sha3-384", "shake256",
636 "crc32"])
637
638###--------------------------------------------------------------------------
639class TestMessageAuthentication (BaseTestHash, T.GenericTestMixin):
640 """Test message authentication codes."""
641
642 def _test_mac(me, mcls):
643
644 ## Check the MAC properties.
645 me.assertEqual(type(mcls.name), str)
646 me.assertTrue(isinstance(mcls.keysz, C.KeySZ))
647 me.assertEqual(type(mcls.tagsz), int)
648
649 ## Test hashing.
650 k = T.span(mcls.keysz.default)
651 key = mcls(k)
5d04dade 652 me.assertEqual(key.hashsz, key.tagsz)
ffad1322
MW
653 me.check_hash(key, need_bufsz = False)
654
655 ## Check that bad key lengths are rejected.
656 badlen = bad_key_size(mcls.keysz)
657 if badlen is not None: me.assertRaises(ValueError, mcls, T.span(badlen))
658
659TestMessageAuthentication.generate_testcases \
660 ((name, C.gcmacs[name]) for name in
661 ["sha-hmac", "rijndael-cmac", "twofish-pmac1", "kmac128"])
662
663class TestPoly1305 (HashBufferTestMixin):
664 """Check the Poly1305 one-time message authentication function."""
665
666 def test_poly1305(me):
667
668 ## Check the MAC properties.
669 me.assertEqual(C.poly1305.name, "poly1305")
670 me.assertEqual(type(C.poly1305.keysz), C.KeySZSet)
671 me.assertEqual(C.poly1305.keysz.default, 16)
10011e42 672 me.assertEqual(C.poly1305.keysz.set, set([16]))
ffad1322
MW
673 me.assertEqual(C.poly1305.tagsz, 16)
674 me.assertEqual(C.poly1305.masksz, 16)
675
676 ## Set some initial values.
677 k = T.span(16)
678 u = T.span(64)[-16:]
679 m = T.span(149)
680 key = C.poly1305(k)
681 t = key(u).hash(m).done()
682
683 ## Check the key properties.
5d04dade
MW
684 me.assertEqual(key.name, "poly1305")
685 me.assertEqual(key.tagsz, 16)
686 me.assertEqual(key.tagsz, 16)
ffad1322
MW
687 me.assertEqual(len(t), 16)
688
689 ## Check that we get the same answer if we split the message up.
690 me.assertEqual(t, key(u).hash(m[0:86]).hash(m[86:149]).done())
691
692 ## Check the `check' method.
693 me.assertTrue(key(u).hash(m).check(t))
694 me.assertFalse(key(u).hash(m).check(t ^ 16*C.bytes("cc")))
695
696 ## Check the menagerie of random hashing methods.
697 def mkhash(_):
698 h = key(u)
699 return h, h.done
700 me.check_hashbuffer(mkhash)
701
702 ## Check that we can't complete hashing without a mask.
703 me.assertRaises(ValueError, key().hash(m).done)
704
705 ## Check `concat'.
706 h0 = key().hash(m[0:96])
707 h1 = key().hash(m[96:117])
708 me.assertEqual(t, key(u).concat(h0, h1).hash(m[117:149]).done())
709 key1 = C.poly1305(k)
710 me.assertRaises(TypeError, key().concat, key1().hash(m[0:96]), h1)
711 me.assertRaises(TypeError, key().concat, h0, key1().hash(m[96:117]))
712 me.assertRaises(ValueError, key().concat, key().hash(m[0:93]), h1)
713
714###--------------------------------------------------------------------------
715class TestHLatin (U.TestCase):
716 """Test the `hsalsa20' and `hchacha20' functions."""
717
718 def test_hlatin(me):
8854283a 719 kk = [T.span(sz) for sz in [10, 16, 32]]
ffad1322
MW
720 n = T.span(16)
721 bad_k = T.span(18)
722 bad_n = T.span(13)
723 for fn in [C.hsalsa208_prf, C.hsalsa2012_prf, C.hsalsa20_prf,
724 C.hchacha8_prf, C.hchacha12_prf, C.hchacha20_prf]:
725 for k in kk:
726 h = fn(k, n)
727 me.assertEqual(len(h), 32)
728 me.assertRaises(ValueError, fn, bad_k, n)
729 me.assertRaises(ValueError, fn, k, bad_n)
730
731###--------------------------------------------------------------------------
732class TestKeccak (HashBufferTestMixin):
733 """Test the Keccak-p[1600, n] sponge function."""
734
735 def test_keccak(me):
736
737 ## Make a state and feed some stuff into it.
738 m0 = T.bin("some initial string")
739 m1 = T.bin("awesome follow-up string")
740 st0 = C.Keccak1600()
741 me.assertEqual(st0.nround, 24)
742 st0.mix(m0).step()
743
744 ## Make another step with a different round count.
745 st1 = C.Keccak1600(23)
746 st1.mix(m0).step()
747 me.assertNotEqual(st0.extract(32), st1.extract(32))
748
c7191509 749 ## Check state copying and `mix' vs `set'.
f61394b9
MW
750 st1 = st0.copy()
751 mask = st1.extract(len(m1))
752 st0.mix(m1)
c7191509 753 st1.set(m1 ^ mask)
f61394b9
MW
754 me.assertEqual(st0.extract(32), st1.extract(32))
755
ffad1322
MW
756 ## Check error conditions.
757 _ = st0.extract(200)
758 me.assertRaises(ValueError, st0.extract, 201)
759 st0.mix(T.span(200))
760 me.assertRaises(ValueError, st0.mix, T.span(201))
c7191509
MW
761 st0.set(T.span(200))
762 me.assertRaises(ValueError, st0.set, T.span(201))
763 me.assertRaises(ValueError, st0.set, T.span(199))
ffad1322
MW
764
765 def check_shake(me, xcls, c, done_matches_xof = True):
766 """
767 Test the SHAKE and cSHAKE XOFs.
768
769 This is also used for testing KMAC, but that sets DONE_MATCHES_XOF false
770 to indicate that the XOF output is range-separated from the fixed-length
771 outputs (unlike the basic SHAKE functions).
772 """
773
774 ## Check the hash attributes.
775 x = xcls()
776 me.assertEqual(x.rate, 200 - c)
777 me.assertEqual(x.buffered, 0)
778 me.assertEqual(x.state, "absorb")
779
780 ## Set some initial values.
781 func = T.bin("TESTXOF")
782 perso = T.bin("catacomb-python test")
783 m = T.span(167)
784 h0 = xcls().hash(m).done(193)
785 me.assertEqual(len(h0), 193)
786 h1 = xcls(func = func, perso = perso).hash(m).done(193)
787 me.assertEqual(len(h1), 193)
788 me.assertNotEqual(h0, h1)
789
790 ## Check input and output in pieces, and the state machine.
791 if done_matches_xof: h = h0
792 else: h = xcls().hash(m).xof().get(len(h0))
793 x = xcls().hash(m[0:76]).hash(m[76:167]).xof()
794 me.assertEqual(h, x.get(98) + x.get(95))
795
796 ## Check masking.
797 x = xcls().hash(m).xof()
5a5e2e11 798 me.assertEqual(x.mask(m), m ^ h[0:len(m)])
ffad1322
MW
799
800 ## Check the `check' method.
801 me.assertTrue(xcls().hash(m).check(h0))
802 me.assertFalse(xcls().hash(m).check(h1))
803
804 ## Check the menagerie of random hashing methods.
805 def mkhash(_):
806 x = xcls(func = func, perso = perso)
7c342918 807 return x, x.done
ffad1322
MW
808 me.check_hashbuffer(mkhash)
809
810 ## Check the state machine tracking.
811 x = xcls(); me.assertEqual(x.state, "absorb")
812 x.hash(m); me.assertEqual(x.state, "absorb")
813 xx = x.copy()
7c342918 814 h = xx.done(); me.assertEqual(len(h), 100 - x.rate//2)
ffad1322
MW
815 me.assertEqual(xx.state, "dead")
816 me.assertRaises(ValueError, xx.done, 1)
817 me.assertRaises(ValueError, xx.get, 1)
818 me.assertEqual(x.state, "absorb")
819 me.assertRaises(ValueError, x.get, 1)
820 x.xof(); me.assertEqual(x.state, "squeeze")
821 me.assertRaises(ValueError, x.done, 1)
822 _ = x.get(1)
823 yy = x.copy(); me.assertEqual(yy.state, "squeeze")
824
825 def test_shake128(me): me.check_shake(C.Shake128, 32)
826 def test_shake256(me): me.check_shake(C.Shake256, 64)
827
828 def check_kmac(me, mcls, c):
829 k = T.span(32)
7ac8e86e 830 me.check_shake(lambda func = None, perso = None:
ffad1322
MW
831 mcls(k, perso = perso),
832 c, done_matches_xof = False)
833
834 def test_kmac128(me): me.check_kmac(C.KMAC128, 32)
835 def test_kmac256(me): me.check_kmac(C.KMAC256, 64)
836
837###--------------------------------------------------------------------------
838class TestPRP (T.GenericTestMixin):
839 """Test pseudorandom permutations (PRPs)."""
840
841 def _test_prp(me, pcls):
842
843 ## Check the PRP properties.
844 me.assertEqual(type(pcls.name), str)
845 me.assertTrue(isinstance(pcls.keysz, C.KeySZ))
846 me.assertEqual(type(pcls.blksz), int)
847
848 ## Check round-tripping.
849 k = T.span(pcls.keysz.default)
850 key = pcls(k)
851 m = T.span(pcls.blksz)
852 c = key.encrypt(m)
853 me.assertEqual(len(c), pcls.blksz)
854 me.assertEqual(m, key.decrypt(c))
855
856 ## Check that bad key lengths are rejected.
857 badlen = bad_key_size(pcls.keysz)
858 if badlen is not None: me.assertRaises(ValueError, pcls, T.span(badlen))
859
860 ## Check that bad blocks are rejected.
861 badblk = T.span(pcls.blksz + 1)
862 me.assertRaises(ValueError, key.encrypt, badblk)
863 me.assertRaises(ValueError, key.decrypt, badblk)
864
865TestPRP.generate_testcases((name, C.gcprps[name]) for name in
866 ["desx", "blowfish", "rijndael"])
867
868###----- That's all, folks --------------------------------------------------
869
870if __name__ == "__main__": U.main()