| 1 | #! /usr/bin/python |
| 2 | ### -*-python-*- |
| 3 | ### |
| 4 | ### Generalization of OCB mode for other block sizes |
| 5 | ### |
| 6 | ### (c) 2017 Mark Wooding |
| 7 | ### |
| 8 | |
| 9 | ###----- Licensing notice --------------------------------------------------- |
| 10 | ### |
| 11 | ### This program is free software; you can redistribute it and/or modify |
| 12 | ### it under the terms of the GNU General Public License as published by |
| 13 | ### the Free Software Foundation; either version 2 of the License, or |
| 14 | ### (at your option) any later version. |
| 15 | ### |
| 16 | ### This program is distributed in the hope that it will be useful, |
| 17 | ### but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 18 | ### MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 19 | ### GNU General Public License for more details. |
| 20 | ### |
| 21 | ### You should have received a copy of the GNU General Public License |
| 22 | ### along with this program; if not, write to the Free Software Foundation, |
| 23 | ### Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. |
| 24 | |
| 25 | from sys import argv, stderr |
| 26 | from struct import pack |
| 27 | from itertools import izip |
| 28 | from contextlib import contextmanager |
| 29 | try: from kalyna import Kalyna |
| 30 | except ImportError: Kalyna = None |
| 31 | import catacomb as C |
| 32 | |
| 33 | R = C.FibRand(0) |
| 34 | |
| 35 | ###-------------------------------------------------------------------------- |
| 36 | ### Utilities. |
| 37 | |
| 38 | def combs(things, k): |
| 39 | ii = range(k) |
| 40 | n = len(things) |
| 41 | while True: |
| 42 | yield [things[i] for i in ii] |
| 43 | for j in xrange(k): |
| 44 | if j == k - 1: lim = n |
| 45 | else: lim = ii[j + 1] |
| 46 | i = ii[j] + 1 |
| 47 | if i < lim: |
| 48 | ii[j] = i |
| 49 | break |
| 50 | ii[j] = j |
| 51 | else: |
| 52 | return |
| 53 | |
| 54 | POLYMAP = {} |
| 55 | |
| 56 | def poly(nbits): |
| 57 | try: return POLYMAP[nbits] |
| 58 | except KeyError: pass |
| 59 | base = C.GF(0).setbit(nbits).setbit(0) |
| 60 | for k in xrange(1, nbits, 2): |
| 61 | for cc in combs(range(1, nbits), k): |
| 62 | p = base + sum(C.GF(0).setbit(c) for c in cc) |
| 63 | if p.irreduciblep(): POLYMAP[nbits] = p; return p |
| 64 | raise ValueError, nbits |
| 65 | |
| 66 | def prim(nbits): |
| 67 | ## No fancy way to do this: I'd need a much cleverer factoring algorithm |
| 68 | ## than I have in my pockets. |
| 69 | if nbits == 64: cc = [64, 4, 3, 1, 0] |
| 70 | elif nbits == 96: cc = [96, 10, 9, 6, 0] |
| 71 | elif nbits == 128: cc = [128, 7, 2, 1, 0] |
| 72 | elif nbits == 192: cc = [192, 15, 11, 5, 0] |
| 73 | elif nbits == 256: cc = [256, 10, 5, 2, 0] |
| 74 | else: raise ValueError, 'no field for %d bits' % nbits |
| 75 | p = C.GF(0) |
| 76 | for c in cc: p = p.setbit(c) |
| 77 | return p |
| 78 | |
| 79 | def Z(n): |
| 80 | return C.ByteString.zero(n) |
| 81 | |
| 82 | def mul_blk_gf(m, x, p): return ((C.GF.loadb(m)*x)%p).storeb((p.nbits + 6)/8) |
| 83 | |
| 84 | def with_lastp(it): |
| 85 | it = iter(it) |
| 86 | try: j = next(it) |
| 87 | except StopIteration: raise ValueError, 'empty iter' |
| 88 | lastp = False |
| 89 | while not lastp: |
| 90 | i = j |
| 91 | try: j = next(it) |
| 92 | except StopIteration: lastp = True |
| 93 | yield i, lastp |
| 94 | |
| 95 | def safehex(x): |
| 96 | if len(x): return hex(x) |
| 97 | else: return '""' |
| 98 | |
| 99 | def keylens(ksz): |
| 100 | sel = [] |
| 101 | if isinstance(ksz, C.KeySZSet): kk = ksz.set |
| 102 | elif isinstance(ksz, C.KeySZRange): kk = range(ksz.min, ksz.max, ksz.mod) |
| 103 | elif isinstance(ksz, C.KeySZAny): kk = range(64); sel = [0] |
| 104 | kk = list(kk); kk = kk[:] |
| 105 | n = len(kk) |
| 106 | while n and len(sel) < 4: |
| 107 | i = R.range(n) |
| 108 | n -= 1 |
| 109 | kk[i], kk[n] = kk[n], kk[i] |
| 110 | sel.append(kk[n]) |
| 111 | return sel |
| 112 | |
| 113 | def pad0star(m, w): |
| 114 | n = len(m) |
| 115 | if not n: r = w |
| 116 | else: r = (-len(m))%w |
| 117 | if r: m += Z(r) |
| 118 | return C.ByteString(m) |
| 119 | |
| 120 | def pad10star(m, w): |
| 121 | r = w - len(m)%w |
| 122 | if r: m += '\x80' + Z(r - 1) |
| 123 | return C.ByteString(m) |
| 124 | |
| 125 | def ntz(i): |
| 126 | j = 0 |
| 127 | while (i&1) == 0: i >>= 1; j += 1 |
| 128 | return j |
| 129 | |
| 130 | def blocks(x, w): |
| 131 | v, i, n = [], 0, len(x) |
| 132 | while n - i > w: |
| 133 | v.append(C.ByteString(x[i:i + w])) |
| 134 | i += w |
| 135 | return v, C.ByteString(x[i:]) |
| 136 | |
| 137 | EMPTY = C.bytes('') |
| 138 | |
| 139 | def blocks0(x, w): |
| 140 | v, tl = blocks(x, w) |
| 141 | if len(tl) == w: v.append(tl); tl = EMPTY |
| 142 | return v, tl |
| 143 | |
| 144 | ###-------------------------------------------------------------------------- |
| 145 | ### Kalyna decoration. |
| 146 | |
| 147 | KALYNA = {} |
| 148 | |
| 149 | if Kalyna is not None: |
| 150 | |
| 151 | class KalynaCipher (type): |
| 152 | def __new__(cls, blksz): |
| 153 | assert blksz in [16, 32, 64] |
| 154 | name = 'Kalyna-%d' % (8*blksz) |
| 155 | me = type(name, (KalynaBase,), {}) |
| 156 | me.name = name |
| 157 | me.blksz = blksz |
| 158 | if blksz == 64: me.keysz = C.KeySZSet(64) |
| 159 | else: me.keysz = C.KeySZSet(2*blksz, [blksz]) |
| 160 | return me |
| 161 | |
| 162 | class KalynaBase (object): |
| 163 | def __init__(me, k): |
| 164 | me._k = Kalyna(k, me.blksz) |
| 165 | def encrypt(me, m): |
| 166 | return C.ByteString(me._k.encrypt(m)) |
| 167 | def decrypt(me, m): |
| 168 | return C.ByteString(me._k.decrypt(m)) |
| 169 | |
| 170 | for i in [16, 32, 64]: |
| 171 | KALYNA['kalyna%d' % (8*i)] = KalynaCipher(i) |
| 172 | |
| 173 | ###-------------------------------------------------------------------------- |
| 174 | ### Luby--Rackoff large-block ciphers. |
| 175 | |
| 176 | class LubyRackoffCipher (type): |
| 177 | def __new__(cls, bc, blksz): |
| 178 | assert blksz%2 == 0 |
| 179 | assert blksz <= 2*bc.blksz |
| 180 | name = '%s-lr[%d]' % (bc.name, 8*blksz) |
| 181 | me = type(name, (LubyRackoffBase,), {}) |
| 182 | me.name = name |
| 183 | me.blksz = blksz |
| 184 | me.keysz = bc.keysz |
| 185 | me.bc = bc |
| 186 | return me |
| 187 | |
| 188 | @contextmanager |
| 189 | def muffle(): |
| 190 | global VERBOSE, LRVERBOSE |
| 191 | _v, _lrv = VERBOSE, LRVERBOSE |
| 192 | try: |
| 193 | VERBOSE = LRVERBOSE = False |
| 194 | yield None |
| 195 | finally: |
| 196 | VERBOSE, LRVERBOSE = _v, _lrv |
| 197 | |
| 198 | class LubyRackoffBase (object): |
| 199 | NR = 4 # for strong-PRP security |
| 200 | def __init__(me, k): |
| 201 | if LRVERBOSE: print 'K = %s' % hex(k) |
| 202 | bc, blksz = me.__class__.bc, me.__class__.blksz |
| 203 | with muffle(): E = bc(k) |
| 204 | me.f = [] |
| 205 | ksz = len(k) |
| 206 | i = C.MP(0) |
| 207 | for j in xrange(me.NR): |
| 208 | b = C.WriteBuffer() |
| 209 | while b.size < ksz: |
| 210 | with muffle(): x = E.encrypt(i.storeb(bc.blksz)) |
| 211 | b.put(x) |
| 212 | if LRVERBOSE: print 'E(K; [%d]) = %s' % (i, hex(x)) |
| 213 | i += 1 |
| 214 | kj = C.ByteString(C.ByteString(b)[0:ksz]) |
| 215 | if LRVERBOSE: print 'K_%d = %s' % (j, hex(kj)) |
| 216 | with muffle(): me.f.append(bc(kj)) |
| 217 | def encrypt(me, m): |
| 218 | bc, blksz = me.__class__.bc, me.__class__.blksz |
| 219 | assert len(m) == blksz |
| 220 | l, r = C.ByteString(m[:blksz/2]), C.ByteString(m[blksz/2:]) |
| 221 | if LRVERBOSE: print 'L_0, R_0 = %s, %s' % (hex(l), hex(r)) |
| 222 | for j in xrange(me.NR): |
| 223 | l0 = pad0star(l, bc.blksz) |
| 224 | with muffle(): t = me.f[j].encrypt(l0) |
| 225 | l, r = r ^ t[:blksz/2], l |
| 226 | if LRVERBOSE: |
| 227 | print 'E(K_%d; L_%d || 0^*) = %s' % (j, j, hex(t)) |
| 228 | print 'L_%d, R_%d = %s, %s' % (j + 1, j + 1, hex(l), hex(r)) |
| 229 | return C.ByteString(r + l) |
| 230 | def decrypt(me, c): |
| 231 | bc, blksz = me.__class__.bc, me.__class__.blksz |
| 232 | assert len(c) == blksz |
| 233 | l, r = C.ByteString(c[:blksz/2]), C.ByteString(c[blksz/2:]) |
| 234 | for j in xrange(me.NR - 1, -1, -1): |
| 235 | l0 = pad0star(l, bc.blksz) |
| 236 | with muffle(): t = me.f[j].encrypt(l0) |
| 237 | if LRVERBOSE: |
| 238 | print 'L_%d, R_%d = %s, %s' % (j + 1, j + 1, hex(l), hex(r)) |
| 239 | print 'E(K_%d; L_%d || 0^*) = %s' % (j + 1, j + 1, hex(t)) |
| 240 | l, r = r ^ t[:blksz/2], l |
| 241 | if LRVERBOSE: print 'L_0, R_0 = %s, %s' % (hex(l), hex(r)) |
| 242 | return C.ByteString(r + l) |
| 243 | |
| 244 | LRAES = {} |
| 245 | for i in [8, 12, 16, 24, 32]: |
| 246 | LRAES['lraes%d' % (8*i)] = LubyRackoffCipher(C.rijndael, i) |
| 247 | LRAES['dlraes512'] = LubyRackoffCipher(LubyRackoffCipher(C.rijndael, 32), 64) |
| 248 | |
| 249 | ###-------------------------------------------------------------------------- |
| 250 | ### PMAC. |
| 251 | |
| 252 | def ocb_masks(E): |
| 253 | blksz = E.__class__.blksz |
| 254 | p = poly(8*blksz) |
| 255 | x = C.GF(2); xinv = p.modinv(x) |
| 256 | z = Z(blksz) |
| 257 | L = E.encrypt(z) |
| 258 | Lxinv = mul_blk_gf(L, xinv, p) |
| 259 | Lgamma = 66*[L] |
| 260 | for i in xrange(1, len(Lgamma)): |
| 261 | Lgamma[i] = mul_blk_gf(Lgamma[i - 1], x, p) |
| 262 | return Lgamma, Lxinv |
| 263 | |
| 264 | def dump_ocb(E): |
| 265 | Lgamma, Lxinv = ocb_masks(E) |
| 266 | print 'L x^-1 = %s' % hex(Lxinv) |
| 267 | for i, lg in enumerate(Lgamma): |
| 268 | print 'L x^%d = %s' % (i, hex(lg)) |
| 269 | |
| 270 | def pmac1(E, m): |
| 271 | blksz = E.__class__.blksz |
| 272 | Lgamma, Lxinv = ocb_masks(E) |
| 273 | a = o = Z(blksz) |
| 274 | i = 1 |
| 275 | v, tl = blocks(m, blksz) |
| 276 | for x in v: |
| 277 | b = ntz(i) |
| 278 | o ^= Lgamma[b] |
| 279 | a ^= E.encrypt(x ^ o) |
| 280 | if VERBOSE: |
| 281 | print 'Z[%d]: %d -> %s' % (i - 1, b, hex(o)) |
| 282 | print 'A[%d]: %s' % (i - 1, hex(a)) |
| 283 | i += 1 |
| 284 | if len(tl) == blksz: a ^= tl ^ Lxinv |
| 285 | else: a ^= pad10star(tl, blksz) |
| 286 | return E.encrypt(a) |
| 287 | |
| 288 | def pmac2(E, m): |
| 289 | blksz = E.__class__.blksz |
| 290 | p = prim(8*blksz) |
| 291 | L = E.encrypt(Z(blksz)) |
| 292 | o = mul_blk_gf(L, 10, p) |
| 293 | a = Z(blksz) |
| 294 | v, tl = blocks(m, blksz) |
| 295 | for x in v: |
| 296 | a ^= E.encrypt(x ^ o) |
| 297 | o = mul_blk_gf(o, 2, p) |
| 298 | if len(tl) == blksz: a ^= tl ^ mul_blk_gf(o, 3, p) |
| 299 | else: a ^= pad10star(tl, blksz) ^ mul_blk_gf(o, 5, p) |
| 300 | return E.encrypt(a) |
| 301 | |
| 302 | def ocb3_masks(E): |
| 303 | Lgamma, _ = ocb_masks(E) |
| 304 | Lstar = Lgamma[0] |
| 305 | Ldollar = Lgamma[1] |
| 306 | return Lstar, Ldollar, Lgamma[2:] |
| 307 | |
| 308 | def dump_ocb3(E): |
| 309 | Lstar, Ldollar, Lgamma = ocb3_masks(E) |
| 310 | print 'L_* : %s' % hex(Lstar) |
| 311 | print 'L_$ : %s' % hex(Ldollar) |
| 312 | for i, lg in enumerate(Lgamma[:4]): |
| 313 | print 'L_%-8d: %s' % (i, hex(lg)) |
| 314 | |
| 315 | def pmac3(E, m): |
| 316 | blksz = E.__class__.blksz |
| 317 | Lstar, Ldollar, Lgamma = ocb3_masks(E) |
| 318 | a = o = Z(blksz) |
| 319 | i = 1 |
| 320 | v, tl = blocks0(m, blksz) |
| 321 | for x in v: |
| 322 | b = ntz(i) |
| 323 | o ^= Lgamma[b] |
| 324 | a ^= E.encrypt(x ^ o) |
| 325 | if VERBOSE: |
| 326 | print 'Offset\'_%-2d: %s' % (i, hex(o)) |
| 327 | print 'AuthSum_%-2d: %s' % (i, hex(a)) |
| 328 | i += 1 |
| 329 | if tl: |
| 330 | o ^= Lstar |
| 331 | a ^= E.encrypt(pad10star(tl, blksz) ^ o) |
| 332 | if VERBOSE: |
| 333 | print 'Offset\'_* : %s' % hex(o) |
| 334 | print 'AuthSum_* : %s' % hex(a) |
| 335 | return a |
| 336 | |
| 337 | def pmac1_pub(E, m): |
| 338 | if VERBOSE: dump_ocb(E) |
| 339 | return pmac1(E, m), |
| 340 | |
| 341 | def pmac2_pub(E, m): |
| 342 | return pmac2(E, m), |
| 343 | |
| 344 | def pmac3_pub(E, m): |
| 345 | return pmac3(E, m), |
| 346 | |
| 347 | def pmacgen(bc): |
| 348 | return [(0,), (1,), |
| 349 | (3*bc.blksz,), |
| 350 | (3*bc.blksz - 5,)] |
| 351 | |
| 352 | ###-------------------------------------------------------------------------- |
| 353 | ### OCB. |
| 354 | |
| 355 | ## For OCB2, it's important for security that n = log_x (x + 1) is large in |
| 356 | ## the field representations of GF(2^w) used -- in fact, we need more, that |
| 357 | ## i n (mod 2^w - 1) is large for i in {4, -3, -2, -1, 1, 2, 3, 4}. The |
| 358 | ## original paper lists the values for 64 and 128, but we support other block |
| 359 | ## sizes, so here's the result of the (rather large, in some cases) |
| 360 | ## computation. |
| 361 | ## |
| 362 | ## Block size log_x (x + 1) |
| 363 | ## |
| 364 | ## 64 9686038906114705801 |
| 365 | ## 96 63214690573408919568138788065 |
| 366 | ## 128 338793687469689340204974836150077311399 |
| 367 | ## 192 161110085006042185925119981866940491651092686475226538785 |
| 368 | ## 256 22928580326165511958494515843249267194111962539778797914076675796261938307298 |
| 369 | |
| 370 | def ocb1(E, n, h, m, tsz = None): |
| 371 | ## This is OCB1.PMAC1 from Rogaway's `Authenticated-Encryption with |
| 372 | ## Associated-Data'. |
| 373 | blksz = E.__class__.blksz |
| 374 | if VERBOSE: dump_ocb(E) |
| 375 | Lgamma, Lxinv = ocb_masks(E) |
| 376 | if tsz is None: tsz = blksz |
| 377 | a = Z(blksz) |
| 378 | o = E.encrypt(n ^ Lgamma[0]) |
| 379 | if VERBOSE: print 'R = %s' % hex(o) |
| 380 | i = 1 |
| 381 | y = C.WriteBuffer() |
| 382 | v, tl = blocks(m, blksz) |
| 383 | for x in v: |
| 384 | b = ntz(i) |
| 385 | o ^= Lgamma[b] |
| 386 | a ^= x |
| 387 | if VERBOSE: |
| 388 | print 'Z[%d]: %d -> %s' % (i - 1, b, hex(o)) |
| 389 | print 'A[%d]: %s' % (i - 1, hex(a)) |
| 390 | y.put(E.encrypt(x ^ o) ^ o) |
| 391 | i += 1 |
| 392 | b = ntz(i) |
| 393 | o ^= Lgamma[b] |
| 394 | n = len(tl) |
| 395 | if VERBOSE: |
| 396 | print 'Z[%d]: %d -> %s' % (i - 1, b, hex(o)) |
| 397 | print 'LEN = %s' % hex(C.MP(8*n).storeb(blksz)) |
| 398 | yfinal = E.encrypt(C.MP(8*n).storeb(blksz) ^ Lxinv ^ o) |
| 399 | cfinal = tl ^ yfinal[:n] |
| 400 | a ^= o ^ (tl + yfinal[n:]) |
| 401 | y.put(cfinal) |
| 402 | t = E.encrypt(a) |
| 403 | if h: t ^= pmac1(E, h) |
| 404 | return C.ByteString(y), C.ByteString(t[:tsz]) |
| 405 | |
| 406 | def ocb2(E, n, h, m, tsz = None): |
| 407 | blksz = E.__class__.blksz |
| 408 | if tsz is None: tsz = blksz |
| 409 | p = prim(8*blksz) |
| 410 | L = E.encrypt(n) |
| 411 | o = mul_blk_gf(L, 2, p) |
| 412 | a = Z(blksz) |
| 413 | v, tl = blocks(m, blksz) |
| 414 | y = C.WriteBuffer() |
| 415 | for x in v: |
| 416 | a ^= x |
| 417 | y.put(E.encrypt(x ^ o) ^ o) |
| 418 | o = mul_blk_gf(o, 2, p) |
| 419 | n = len(tl) |
| 420 | yfinal = E.encrypt(C.MP(8*n).storeb(blksz) ^ o) |
| 421 | cfinal = tl ^ yfinal[:n] |
| 422 | a ^= (tl + yfinal[n:]) ^ mul_blk_gf(o, 3, p) |
| 423 | y.put(cfinal) |
| 424 | t = E.encrypt(a) |
| 425 | if h: t ^= pmac2(E, h) |
| 426 | return C.ByteString(y), C.ByteString(t[:tsz]) |
| 427 | |
| 428 | OCB3_STRETCH = { 8: (5, 25), |
| 429 | 12: (6, 33), |
| 430 | 16: (6, 8), |
| 431 | 24: (7, 40), |
| 432 | 32: (7, 120), |
| 433 | 64: (8, 240) } |
| 434 | |
| 435 | def ocb3(E, n, h, m, tsz = None): |
| 436 | blksz = E.__class__.blksz |
| 437 | if tsz is None: tsz = blksz |
| 438 | Lstar, Ldollar, Lgamma = ocb3_masks(E) |
| 439 | if VERBOSE: dump_ocb3(E) |
| 440 | |
| 441 | ## Figure out how much we need to glue onto the nonce. This ends up being |
| 442 | ## [t mod w]_v || 0^p || 1 || N, where w is the block size in bits, t is |
| 443 | ## the tag length in bits, v = floor(log_2(w - 1)) + 1, and p = w - l(N) - |
| 444 | ## v - 1. But this is an annoying way to think about it because of the |
| 445 | ## byte misalignment. Instead, think of it as a byte-aligned prefix |
| 446 | ## encoding the tag and an `is the nonce full-length' flag, followed by |
| 447 | ## optional padding, and then the nonce: |
| 448 | ## |
| 449 | ## F || N if l(N) = w - f |
| 450 | ## F || 0^p || 1 || N otherwise |
| 451 | ## |
| 452 | ## where F is [t mod w]_v || 0^{f-v-1} || b; f = floor(log_2(w - 1)) + 2; |
| 453 | ## b is 1 if l(N) = w - f, or 0 otherwise; and p = w - f - l(N) - 1. |
| 454 | tszbits = C.MP(8*blksz - 1).nbits |
| 455 | fwd = tszbits/8 + 1 |
| 456 | f = tsz << 3 + 8*fwd - tszbits |
| 457 | |
| 458 | ## Form the augmented nonce. |
| 459 | nb = C.WriteBuffer() |
| 460 | nsz, nwd = len(n), blksz - fwd |
| 461 | if nsz == nwd: f |= 1 |
| 462 | nb.put(C.MP(f).storeb(fwd)) |
| 463 | if nsz < nwd: nb.zero(nwd - nsz - 1).putu8(1) |
| 464 | nb.put(n) |
| 465 | nn = C.ByteString(nb) |
| 466 | if VERBOSE: print 'N\' : %s' % hex(nn) |
| 467 | |
| 468 | ## Calculate the initial offset. |
| 469 | split, shift = OCB3_STRETCH[blksz] |
| 470 | splitbits = 1 << split |
| 471 | t2ps = C.MP(0).setbit(splitbits) |
| 472 | lomask = (C.MP(0).setbit(split) - 1) |
| 473 | himask = ~lomask |
| 474 | top, bottom = nn&himask.storeb2c(blksz), C.MP.loadb(nn)&lomask |
| 475 | ktop = C.MP.loadb(E.encrypt(top)) |
| 476 | stretch = (ktop << splitbits) | \ |
| 477 | (((ktop ^ (ktop << shift)) >> (8*blksz - splitbits))%t2ps) |
| 478 | o = (stretch >> splitbits - bottom).storeb(blksz) |
| 479 | a = C.ByteString.zero(blksz) |
| 480 | if VERBOSE: |
| 481 | print 'bottom : %d' % bottom |
| 482 | print 'Ktop : %s' % hex(ktop.storeb(blksz)) |
| 483 | print 'Stretch : %s' % hex(stretch.storeb(blksz + (1 << split - 3))) |
| 484 | print 'Offset_0 : %s' % hex(o) |
| 485 | |
| 486 | ## Split the message into blocks. |
| 487 | i = 1 |
| 488 | y = C.WriteBuffer() |
| 489 | v, tl = blocks0(m, blksz) |
| 490 | for x in v: |
| 491 | b = ntz(i) |
| 492 | o ^= Lgamma[b] |
| 493 | a ^= x |
| 494 | if VERBOSE: |
| 495 | print 'Offset_%-3d: %s' % (i, hex(o)) |
| 496 | print 'Checksum_%d: %s' % (i, hex(a)) |
| 497 | y.put(E.encrypt(x ^ o) ^ o) |
| 498 | i += 1 |
| 499 | if tl: |
| 500 | o ^= Lstar |
| 501 | n = len(tl) |
| 502 | pad = E.encrypt(o) |
| 503 | a ^= pad10star(tl, blksz) |
| 504 | if VERBOSE: |
| 505 | print 'Offset_* : %s' % hex(o) |
| 506 | print 'Checksum_*: %s' % hex(a) |
| 507 | y.put(tl ^ pad[0:n]) |
| 508 | o ^= Ldollar |
| 509 | t = E.encrypt(a ^ o) ^ pmac3(E, h) |
| 510 | return C.ByteString(y), C.ByteString(t[:tsz]) |
| 511 | |
| 512 | def ocbgen(bc): |
| 513 | w = bc.blksz |
| 514 | return [(w, 0, 0), (w, 1, 0), (w, 0, 1), |
| 515 | (w, 0, 3*w), |
| 516 | (w, 3*w, 3*w), |
| 517 | (w, 0, 3*w + 5), |
| 518 | (w, 3*w - 5, 3*w + 5)] |
| 519 | |
| 520 | def ocb3gen(bc): |
| 521 | w = bc.blksz |
| 522 | return [(w - 2, 0, 0), (w - 2, 1, 0), (w - 2, 0, 1), |
| 523 | (w - 5, 0, 3*w), |
| 524 | (w - 3, 3*w, 3*w), |
| 525 | (w - 2, 0, 3*w + 5), |
| 526 | (w - 2, 3*w - 5, 3*w + 5)] |
| 527 | |
| 528 | ###-------------------------------------------------------------------------- |
| 529 | ### Main program. |
| 530 | |
| 531 | VERBOSE = LRVERBOSE = False |
| 532 | |
| 533 | class struct (object): |
| 534 | def __init__(me, **kw): |
| 535 | me.__dict__.update(kw) |
| 536 | |
| 537 | def mct(ocb, bc, ksz, nsz, tsz): |
| 538 | k = C.MP(8*tsz).storeb(ksz) |
| 539 | E = bc(k) |
| 540 | e = C.ByteString('') |
| 541 | n = C.MP(1) |
| 542 | cbuf = C.WriteBuffer() |
| 543 | for i in xrange(128): |
| 544 | s = C.ByteString.zero(i) |
| 545 | y, t = ocb(E, n.storeb(nsz), s, s, tsz); n += 1; cbuf.put(y).put(t) |
| 546 | y, t = ocb(E, n.storeb(nsz), e, s, tsz); n += 1; cbuf.put(y).put(t) |
| 547 | y, t = ocb(E, n.storeb(nsz), s, e, tsz); n += 1; cbuf.put(y).put(t) |
| 548 | _, t = ocb(E, n.storeb(nsz), C.ByteString(cbuf), e, tsz) |
| 549 | print hex(t) |
| 550 | |
| 551 | argc = len(argv) |
| 552 | argi = 1 |
| 553 | |
| 554 | def usage(): |
| 555 | print >>stderr, """\ |
| 556 | usage: %s [-v] OCB BLKC OP ARGS... |
| 557 | mct KSZ NSZ TSZ |
| 558 | kat K N0 TSZ HSZ,MSZ ... |
| 559 | lraes W K M""" % argv[0] |
| 560 | exit(2) |
| 561 | |
| 562 | def arg(must = True, default = None): |
| 563 | global argi |
| 564 | if argi < argc: argi += 1; return argv[argi - 1] |
| 565 | elif not must: return default |
| 566 | else: usage() |
| 567 | |
| 568 | MODEMAP = { 'ocb1': ocb1, |
| 569 | 'ocb2': ocb2, |
| 570 | 'ocb3': ocb3 } |
| 571 | |
| 572 | def pat(sz): |
| 573 | b = C.WriteBuffer() |
| 574 | for i in xrange(sz): b.putu8(i%256) |
| 575 | return C.ByteString(b) |
| 576 | |
| 577 | opt = arg() |
| 578 | if opt == '-v': VERBOSE = True; opt = arg() |
| 579 | ocb = MODEMAP[opt] |
| 580 | |
| 581 | bcname = arg() |
| 582 | bc = None |
| 583 | for d in LRAES, KALYNA, C.gcprps: |
| 584 | try: bc = d[bcname] |
| 585 | except KeyError: pass |
| 586 | else: break |
| 587 | if bc is None: raise KeyError, bcname |
| 588 | |
| 589 | mode = arg() |
| 590 | if mode == 'mct': |
| 591 | ksz = int(arg()); nsz = int(arg()); tsz = int(arg()) |
| 592 | mct(ocb, bc, ksz, nsz, tsz) |
| 593 | exit(0) |
| 594 | |
| 595 | elif mode == 'kat': |
| 596 | k = C.bytes(arg()) |
| 597 | E = bc(k) |
| 598 | nspec = arg() |
| 599 | if nspec.endswith('+'): ninc = 1; nspec = nspec[:-1] |
| 600 | else: ninc = 0 |
| 601 | n0 = C.bytes(nspec) |
| 602 | nz = C.MP.loadb(n0) |
| 603 | nsz = len(n0) |
| 604 | tsz = int(arg()) |
| 605 | |
| 606 | print 'K: %s' % hex(k) |
| 607 | |
| 608 | while True: |
| 609 | hmsz = arg(must = False) |
| 610 | if hmsz is None: break |
| 611 | hsz, msz = map(int, hmsz.split(',')) |
| 612 | n = nz.storeb(nsz) |
| 613 | h = pat(hsz) |
| 614 | m = pat(msz) |
| 615 | y, t = ocb(E, n, h, m, tsz) |
| 616 | print |
| 617 | print 'N: %s' % hex(n) |
| 618 | print 'A: %s' % hex(h) |
| 619 | print 'P: %s' % hex(m) |
| 620 | print 'C: %s%s' % (hex(y), hex(t)) |
| 621 | nz += ninc |
| 622 | |
| 623 | elif mode == 'lraes': |
| 624 | w = int(arg()) |
| 625 | k = C.bytes(arg()) |
| 626 | m = C.bytes(arg()) |
| 627 | LRVERBOSE = True |
| 628 | lr = LubyRackoffCipher(bc, w) |
| 629 | E = lr(k) |
| 630 | print |
| 631 | c = E.encrypt(m) |
| 632 | print 'E\'(K, m) = %s' % hex(c) |
| 633 | |
| 634 | else: |
| 635 | usage() |
| 636 | |
| 637 | ###----- That's all, folks -------------------------------------------------- |