utils/advmodes: Implement (only) a toy version of OCB2.
authorMark Wooding <mdw@distorted.org.uk>
Mon, 5 Nov 2018 17:34:41 +0000 (17:34 +0000)
committerMark Wooding <mdw@distorted.org.uk>
Thu, 5 Sep 2019 00:36:08 +0000 (01:36 +0100)
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
utils/advmodes

index d0b337a..9a1d2e6 100644 (file)
@@ -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;
+}
index e0dd386..c65a933 100755 (executable)
@@ -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]