+OCB3_STRETCH = { 4: ( 4, 17),
+ 8: ( 5, 25),
+ 12: ( 6, 33),
+ 16: ( 6, 8),
+ 24: ( 7, 40),
+ 32: ( 8, 1),
+ 48: ( 8, 80),
+ 64: ( 8, 176),
+ 96: ( 9, 160),
+ 128: ( 9, 352),
+ 200: (10, 192) }
+
+def ocb3nonce(E, n, tsz):
+
+ ## Figure out how much we need to glue onto the nonce. This ends up being
+ ## [t mod w]_v || 0^p || 1 || N, where w is the block size in bits, t is
+ ## the tag length in bits, v = floor(log_2(w - 1)) + 1, and p = w - l(N) -
+ ## v - 1. But this is an annoying way to think about it because of the
+ ## byte misalignment. Instead, think of it as a byte-aligned prefix
+ ## encoding the tag and an `is the nonce full-length' flag, followed by
+ ## optional padding, and then the nonce:
+ ##
+ ## F || N if l(N) = w - f
+ ## F || 0^p || 1 || N otherwise
+ ##
+ ## where F is [t mod w]_v || 0^{f-v-1} || b; f = floor(log_2(w - 1)) + 2;
+ ## b is 1 if l(N) = w - f, or 0 otherwise; and p = w - f - l(N) - 1.
+ blksz = E.__class__.blksz
+ tszbits = min(C.MP(8*blksz - 1).nbits, 8)
+ fwd = tszbits/8 + 1
+ f = 8*(tsz%blksz) << + 8*fwd - tszbits
+
+ ## Form the augmented nonce.
+ nb = C.WriteBuffer()
+ nsz, nwd = len(n), blksz - fwd
+ if nsz == nwd: f |= 1
+ nb.put(C.MP(f).storeb(fwd))
+ if nsz < nwd: nb.zero(nwd - nsz - 1).putu8(1)
+ nb.put(n)
+ nn = C.ByteString(nb)
+ if VERBOSE: print 'aug-nonce = %s' % hex(nn)
+
+ ## Calculate the initial offset.
+ split, shift = OCB3_STRETCH[blksz]
+ t2pw = C.MP(0).setbit(8*blksz) - 1
+ lomask = (C.MP(0).setbit(split) - 1)
+ himask = ~lomask
+ top, bottom = nn&himask.storeb2c(blksz), C.MP.loadb(nn)&lomask
+ ktop = C.MP.loadb(E.encrypt(top))
+ stretch = (ktop << 8*blksz) | (ktop ^ (ktop << shift)&t2pw)
+ o = (stretch >> 8*blksz - bottom).storeb(blksz)
+ if VERBOSE:
+ print 'stretch = %s' % hex(stretch.storeb(2*blksz))
+ print 'Z[0] = %s' % hex(o)
+
+ return o
+
+def ocb3enc(E, n, h, m, tsz = None):
+ blksz = E.__class__.blksz
+ if tsz is None: tsz = blksz
+ Lstar, Ldollar, Lgamma = ocb3_masks(E)
+ if VERBOSE: dump_ocb3(E)
+
+ ## Set things up.
+ o = ocb3nonce(E, n, tsz)
+ a = C.ByteString.zero(blksz)
+
+ ## Split the message into blocks.
+ i = 0
+ y = C.WriteBuffer()
+ v, tl = blocks0(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)
+ if tl:
+ o ^= Lstar
+ n = len(tl)
+ pad = E.encrypt(o)
+ a ^= pad10star(tl, blksz)
+ if VERBOSE:
+ print 'Z[%d]: * -> %s' % (i, hex(o))
+ print 'A[%d]: %s' % (i, hex(a))
+ y.put(tl ^ pad[0:n])
+ o ^= Ldollar
+ t = E.encrypt(a ^ o) ^ pmac3(E, h)
+ return C.ByteString(y), C.ByteString(t[:tsz])
+
+def ocb3dec(E, n, h, y, t):
+ blksz = E.__class__.blksz
+ tsz = len(t)
+ Lstar, Ldollar, Lgamma = ocb3_masks(E)
+ if VERBOSE: dump_ocb3(E)
+
+ ## Set things up.
+ o = ocb3nonce(E, n, tsz)
+ a = C.ByteString.zero(blksz)
+
+ ## Split the message into blocks.
+ i = 0
+ m = C.WriteBuffer()
+ v, tl = blocks0(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.encrypt(x ^ o) ^ o
+ m.put(u)
+ a ^= u
+ if tl:
+ o ^= Lstar
+ n = len(tl)
+ pad = E.encrypt(o)
+ if VERBOSE:
+ print 'Z[%d]: * -> %s' % (i, hex(o))
+ print 'A[%d]: %s' % (i, hex(a))
+ u = tl ^ pad[0:n]
+ m.put(u)
+ a ^= pad10star(u, blksz)
+ o ^= Ldollar
+ u = E.encrypt(a ^ o) ^ pmac3(E, h)
+ if t == u[:tsz]: return C.ByteString(m),
+ else: return None,
+