From ebb0df1d8203932379849c05bf8281b99985bd22 Mon Sep 17 00:00:00 2001 From: Mark Wooding Date: Mon, 5 Nov 2018 17:34:41 +0000 Subject: [PATCH] utils/advmodes: Implement (only) a toy version of OCB2. I doubt this will ever end up as a high-quality mode implementation in Catacomb, because it doesn't actually provide authenticity. See `Cryptanalysis of OCB2' by Akiko Inoue and Kazuhiko Minamatsu, https://eprint.iacr.org/2018/1040. This is enough to confirm their result. * First, choose an arbitrary key and nonce, and encrypt a two-block message whose first block contains len(0^{128}) = 128; the second block is arbitrary. $ ./advmodes ocb2-enc rijndael 00112233445566778899aabbccddeeff 00112233445566778899aabbccddeeff "" 0000000000000000000000000000008000112233445566778899aabbccddeeff 0e6475201e14155a2744eb78f396581c3ffbfcf1d7a2505ef8f5e56b2824f4bb 5973f3fdd62e411b05c9d9d982769bbc * Ask Python to XOR pieces of message and ciphertext: >>> import catacomb as C >>> C.bytes('00000000000000000000000000000080') ^ C.bytes('0e6475201e14155a2744eb78f396581c') bytes('0e6475201e14155a2744eb78f396589c') >>> C.bytes('00112233445566778899aabbccddeeff') ^ C.bytes('3ffbfcf1d7a2505ef8f5e56b2824f4bb') bytes('3feadec293f73629706c4fd0e4f91a44') * Use the first result as the ciphertext and the second as the MAC. $ ./advmodes ocb2-dec rijndael 00112233445566778899aabbccddeeff 00112233445566778899aabbccddeeff "" 0e6475201e14155a2744eb78f396589c 3feadec293f73629706c4fd0e4f91a44 c5ecf37c57e1b262c83c0739468037e4 Oops. --- symm/t/rijndael.local | 70 ++++++++++++++++++++++++++++++++++++++++ utils/advmodes | 89 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 159 insertions(+) diff --git a/symm/t/rijndael.local b/symm/t/rijndael.local index d0b337a0..9a1d2e62 100644 --- a/symm/t/rijndael.local +++ b/symm/t/rijndael.local @@ -1569,3 +1569,73 @@ rijndael-pmac1 { 9c0f16e39815d4e9cfce3ed1ecdf3d264a7f16cb16c2e815f422cdf0c8e30308be3c31e6bc58c0b7cadcb6 0638371148ba4e00b0cf138e026a7740; } + +rijndael-ocb2 { + ## Taken from https://tools.ietf.org/html/draft-krovetz-ocb-00. + + 000102030405060708090a0b0c0d0e0f + 000102030405060708090a0b0c0d0e0f + "" + "" + "" + bf3108130773ad5ec70ec69e7875a7b0; + 000102030405060708090a0b0c0d0e0f + 000102030405060708090a0b0c0d0e0f + "" + 0001020304050607 + c636b3a868f429bb + a45f5fdea5c088d1d7c8be37cabc8c5c; + 000102030405060708090a0b0c0d0e0f + 000102030405060708090a0b0c0d0e0f + "" + 000102030405060708090a0b0c0d0e0f + 52e48f5d19fe2d9869f0c4a4b3d2be57 + f7ee49ae7aa5b5e6645db6b3966136f9; + 000102030405060708090a0b0c0d0e0f + 000102030405060708090a0b0c0d0e0f + "" + 000102030405060708090a0b0c0d0e0f1011121314151617 + f75d6bc8b4dc8d66b836a2b08b32a636cc579e145d323beb + a1a50f822819d6e0a216784ac24ac84c; + 000102030405060708090a0b0c0d0e0f + 000102030405060708090a0b0c0d0e0f + "" + 000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f + f75d6bc8b4dc8d66b836a2b08b32a636cec3c555037571709da25e1bb0421a27 + 09ca6c73f0b5c6c5fd587122d75f2aa3; + 000102030405060708090a0b0c0d0e0f + 000102030405060708090a0b0c0d0e0f + "" + 000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f2021222324252627 + f75d6bc8b4dc8d66b836a2b08b32a6369f1cd3c5228d79fd6c267f5f6aa7b231c7dfb9d59951ae9c + 9db0cdf880f73e3e10d4eb3217766688; + 000102030405060708090a0b0c0d0e0f + 000102030405060708090a0b0c0d0e0f + 0001020304050607 + 0001020304050607 + c636b3a868f429bb + 8d059589ec3b6ac00ca31624bc3af2c6; + 000102030405060708090a0b0c0d0e0f + 000102030405060708090a0b0c0d0e0f + 000102030405060708090a0b0c0d0e0f + 52e48f5d19fe2d9869f0c4a4b3d2be57 + 4da4391bcac39d278c7a3f1fd39041e6; + 000102030405060708090a0b0c0d0e0f + 000102030405060708090a0b0c0d0e0f + 000102030405060708090a0b0c0d0e0f1011121314151617 + 000102030405060708090a0b0c0d0e0f1011121314151617 + f75d6bc8b4dc8d66b836a2b08b32a636cc579e145d323beb + 24b9ac3b9574d2202678e439d150f633; + 000102030405060708090a0b0c0d0e0f + 000102030405060708090a0b0c0d0e0f + 000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f + 000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f + f75d6bc8b4dc8d66b836a2b08b32a636cec3c555037571709da25e1bb0421a27 + 41a977c91d66f62c1e1fc30bc93823ca; + 000102030405060708090a0b0c0d0e0f + 000102030405060708090a0b0c0d0e0f + 000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f2021222324252627 + 000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f2021222324252627 + f75d6bc8b4dc8d66b836a2b08b32a6369f1cd3c5228d79fd6c267f5f6aa7b231c7dfb9d59951ae9c + 65a92715a028acd4ae6aff4bfaa0d396; +} diff --git a/utils/advmodes b/utils/advmodes index e0dd3868..c65a9338 100755 --- a/utils/advmodes +++ b/utils/advmodes @@ -38,6 +38,19 @@ def poly(nbits): if p.irreduciblep(): POLYMAP[nbits] = p; return p raise ValueError, nbits +def prim(nbits): + ## No fancy way to do this: I'd need a much cleverer factoring algorithm + ## than I have in my pockets. + if nbits == 64: cc = [64, 4, 3, 1, 0] + elif nbits == 96: cc = [96, 10, 9, 6, 0] + elif nbits == 128: cc = [128, 7, 2, 1, 0] + elif nbits == 192: cc = [192, 15, 11, 5, 0] + elif nbits == 256: cc = [256, 10, 5, 2, 0] + else: raise ValueError, 'no field for %d bits' % nbits + p = C.GF(0) + for c in cc: p = p.setbit(c) + return p + def Z(n): return C.ByteString.zero(n) @@ -560,6 +573,20 @@ def pmac1(E, m): else: a ^= pad10star(tl, blksz) return E.encrypt(a) +def pmac2(E, m): + blksz = E.__class__.blksz + p = prim(8*blksz) + L = E.encrypt(Z(blksz)) + o = mul_blk_gf(L, 10, p) + a = Z(blksz) + v, tl = blocks(m, blksz) + for x in v: + a ^= E.encrypt(x ^ o) + o = mul_blk_gf(o, 2, p) + if len(tl) == blksz: a ^= tl ^ mul_blk_gf(o, 3, p) + else: a ^= pad10star(tl, blksz) ^ mul_blk_gf(o, 5, p) + return E.encrypt(a) + def pmac1_pub(E, m): if VERBOSE: dump_ocb(E) return pmac1(E, m), @@ -647,6 +674,66 @@ def ocb1dec(E, n, h, y, t): if t == u[:len(t)]: return C.ByteString(m), else: return None, +def ocb2enc(E, n, h, m, tsz = None): + ## For OCB2, it's important for security that n = log_x (x + 1) is large in + ## the field representations of GF(2^w) used -- in fact, we need more, that + ## i n (mod 2^w - 1) is large for i in {4, -3, -2, -1, 1, 2, 3, 4}. The + ## original paper lists the values for 64 and 128, but we support other + ## block sizes, so here's the result of the (rather large, in some cases) + ## computation. + ## + ## Block size log_x (x + 1) + ## + ## 64 9686038906114705801 + ## 96 63214690573408919568138788065 + ## 128 338793687469689340204974836150077311399 + ## 192 161110085006042185925119981866940491651092686475226538785 + ## 256 22928580326165511958494515843249267194111962539778797914076675796261938307298 + + blksz = E.__class__.blksz + if tsz is None: tsz = blksz + p = prim(8*blksz) + L = E.encrypt(n) + o = mul_blk_gf(L, 2, p) + a = Z(blksz) + v, tl = blocks(m, blksz) + y = C.WriteBuffer() + for x in v: + a ^= x + y.put(E.encrypt(x ^ o) ^ o) + o = mul_blk_gf(o, 2, p) + n = len(tl) + yfinal = E.encrypt(C.MP(8*n).storeb(blksz) ^ o) + cfinal = tl ^ yfinal[:n] + a ^= (tl + yfinal[n:]) ^ mul_blk_gf(o, 3, p) + y.put(cfinal) + t = E.encrypt(a) + if h: t ^= pmac2(E, h) + return C.ByteString(y), C.ByteString(t[:tsz]) + +def ocb2dec(E, n, h, y, t): + blksz = E.__class__.blksz + p = prim(8*blksz) + L = E.encrypt(n) + o = mul_blk_gf(L, 2, p) + a = Z(blksz) + v, tl = blocks(y, blksz) + m = C.WriteBuffer() + for x in v: + u = E.encrypt(x ^ o) ^ o + y.put(u) + a ^= u + o = mul_blk_gf(o, 2, p) + n = len(tl) + yfinal = E.encrypt(C.MP(8*n).storeb(blksz) ^ o) + mfinal = tl ^ yfinal[:n] + a ^= (mfinal + yfinal[n:]) ^ mul_blk_gf(o, 3, p) + m.put(mfinal) + u = E.encrypt(a) + if h: u ^= pmac2(E, h) + if t == u[:len(t)]: return C.ByteString(m), + else: return None, + def ocbgen(bc): w = bc.blksz return [(w, 0, 0), (w, 1, 0), (w, 0, 1), @@ -674,6 +761,8 @@ MODEMAP = { 'eax-enc': (eaxgen, 3*[binarg] + [intarg], eaxenc), 'gcm-dec': (dummygen, 4*[binarg], gcmdec), 'ocb1-enc': (ocbgen, 3*[binarg] + [intarg], ocb1enc), 'ocb1-dec': (dummygen, 4*[binarg], ocb1dec), + 'ocb2-enc': (ocbgen, 3*[binarg] + [intarg], ocb2enc), + 'ocb2-dec': (dummygen, 4*[binarg], ocb2dec), 'pmac1': (pmacgen, [binarg], pmac1_pub) } mode = argv[1] -- 2.11.0