utils/advmodes: Implement (only) a toy version of OCB2.
[catacomb] / utils / advmodes
index 62b4cde..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)
 
@@ -522,6 +535,214 @@ def eaxgen(bc):
           (bc.blksz - 1, 3*bc.blksz - 5, 3*bc.blksz + 5)]
 
 ###--------------------------------------------------------------------------
+### PMAC.
+
+def ocb_masks(E):
+  blksz = E.__class__.blksz
+  p = poly(8*blksz)
+  x = C.GF(2); xinv = p.modinv(x)
+  z = Z(blksz)
+  L = E.encrypt(z)
+  Lxinv = mul_blk_gf(L, xinv, p)
+  Lgamma = 66*[L]
+  for i in xrange(1, len(Lgamma)):
+    Lgamma[i] = mul_blk_gf(Lgamma[i - 1], x, p)
+  return Lgamma, Lxinv
+
+def dump_ocb(E):
+  Lgamma, Lxinv = ocb_masks(E)
+  print 'L x^-1 = %s' % hex(Lxinv)
+  for i, lg in enumerate(Lgamma[:16]):
+    print 'L x^%d = %s' % (i, hex(lg))
+
+def pmac1(E, m):
+  blksz = E.__class__.blksz
+  Lgamma, Lxinv = ocb_masks(E)
+  a = o = Z(blksz)
+  i = 0
+  v, tl = blocks(m, blksz)
+  for x in v:
+    i += 1
+    b = ntz(i)
+    o ^= Lgamma[b]
+    a ^= E.encrypt(x ^ o)
+    if VERBOSE:
+      print 'Z[%d]: %d -> %s' % (i, b, hex(o))
+      print 'A[%d]: %s' % (i, hex(a))
+  if len(tl) == blksz: a ^= tl ^ Lxinv
+  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),
+
+def pmacgen(bc):
+  return [(0,), (1,),
+          (3*bc.blksz,),
+          (3*bc.blksz - 5,)]
+
+###--------------------------------------------------------------------------
+### OCB.
+
+def ocb1enc(E, n, h, m, tsz = None):
+  ## This is OCB1.PMAC1 from Rogaway's `Authenticated-Encryption with
+  ## Associated-Data'.
+  blksz = E.__class__.blksz
+  if VERBOSE: dump_ocb(E)
+  Lgamma, Lxinv = ocb_masks(E)
+  if tsz is None: tsz = blksz
+  a = Z(blksz)
+  o = E.encrypt(n ^ Lgamma[0])
+  if VERBOSE: print 'R = %s' % hex(o)
+  i = 0
+  y = C.WriteBuffer()
+  v, tl = blocks(m, blksz)
+  for x in v:
+    i += 1
+    b = ntz(i)
+    o ^= Lgamma[b]
+    a ^= x
+    if VERBOSE:
+      print 'Z[%d]: %d -> %s' % (i, b, hex(o))
+      print 'A[%d]: %s' % (i, hex(a))
+    y.put(E.encrypt(x ^ o) ^ o)
+  i += 1
+  b = ntz(i)
+  o ^= Lgamma[b]
+  n = len(tl)
+  if VERBOSE:
+    print 'Z[%d]: %d -> %s' % (i, b, hex(o))
+    print 'LEN = %s' % hex(C.MP(8*n).storeb(blksz))
+  yfinal = E.encrypt(C.MP(8*n).storeb(blksz) ^ Lxinv ^ o)
+  cfinal = tl ^ yfinal[:n]
+  a ^= o ^ (tl + yfinal[n:])
+  y.put(cfinal)
+  t = E.encrypt(a)
+  if h: t ^= pmac1(E, h)
+  return C.ByteString(y), C.ByteString(t[:tsz])
+
+def ocb1dec(E, n, h, y, t):
+  ## This is OCB1.PMAC1 from Rogaway's `Authenticated-Encryption with
+  ## Associated-Data'.
+  blksz = E.__class__.blksz
+  if VERBOSE: dump_ocb(E)
+  Lgamma, Lxinv = ocb_masks(E)
+  a = Z(blksz)
+  o = E.encrypt(n ^ Lgamma[0])
+  if VERBOSE: print 'R = %s' % hex(o)
+  i = 0
+  m = C.WriteBuffer()
+  v, tl = blocks(y, blksz)
+  for x in v:
+    i += 1
+    b = ntz(i)
+    o ^= Lgamma[b]
+    if VERBOSE:
+      print 'Z[%d]: %d -> %s' % (i, b, hex(o))
+      print 'A[%d]: %s' % (i, hex(a))
+    u = E.decrypt(x ^ o) ^ o
+    m.put(u)
+    a ^= u
+  i += 1
+  b = ntz(i)
+  o ^= Lgamma[b]
+  n = len(tl)
+  if VERBOSE:
+    print 'Z[%d]: %d -> %s' % (i, b, hex(o))
+    print 'LEN = %s' % hex(C.MP(8*n).storeb(blksz))
+  yfinal = E.encrypt(C.MP(8*n).storeb(blksz) ^ Lxinv ^ o)
+  mfinal = tl ^ yfinal[:n]
+  a ^= o ^ (mfinal + yfinal[n:])
+  m.put(mfinal)
+  u = E.encrypt(a)
+  if h: u ^= pmac1(E, h)
+  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),
+          (w, 0, 3*w),
+          (w, 3*w, 3*w),
+          (w, 0, 3*w + 5),
+          (w, 3*w - 5, 3*w + 5)]
+
+###--------------------------------------------------------------------------
 ### Main program.
 
 class struct (object):
@@ -537,7 +758,12 @@ MODEMAP = { 'eax-enc': (eaxgen, 3*[binarg] + [intarg], eaxenc),
             'ccm-dec': (dummygen, 4*[binarg], ccmdec),
             'cmac': (cmacgen, [binarg], cmac),
             'gcm-enc': (gcmgen, 3*[binarg] + [intarg], gcmenc),
-            'gcm-dec': (dummygen, 4*[binarg], gcmdec) }
+            '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]
 bc = None