Commit | Line | Data |
---|---|---|
54482987 MW |
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 | |
86082bbc | 28 | from contextlib import contextmanager |
d305618f MW |
29 | try: from kalyna import Kalyna |
30 | except ImportError: Kalyna = None | |
54482987 MW |
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 | ###-------------------------------------------------------------------------- | |
d305618f MW |
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 | ###-------------------------------------------------------------------------- | |
54482987 MW |
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 | ||
86082bbc MW |
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 | ||
54482987 MW |
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 | |
86082bbc | 203 | with muffle(): E = bc(k) |
54482987 MW |
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: | |
86082bbc | 210 | with muffle(): x = E.encrypt(i.storeb(bc.blksz)) |
54482987 MW |
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)) | |
86082bbc | 216 | with muffle(): me.f.append(bc(kj)) |
54482987 MW |
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) | |
86082bbc | 224 | with muffle(): t = me.f[j].encrypt(l0) |
54482987 MW |
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) | |
86082bbc | 236 | with muffle(): t = me.f[j].encrypt(l0) |
54482987 MW |
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) | |
86082bbc | 247 | LRAES['dlraes512'] = LubyRackoffCipher(LubyRackoffCipher(C.rijndael, 32), 64) |
54482987 MW |
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), | |
86082bbc MW |
432 | 32: (7, 120), |
433 | 64: (8, 240) } | |
54482987 MW |
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 | |
d305618f | 583 | for d in LRAES, KALYNA, C.gcprps: |
54482987 MW |
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 | ||
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 | ||
631 | c = E.encrypt(m) | |
632 | print 'E\'(K, m) = %s' % hex(c) | |
633 | ||
634 | else: | |
635 | usage() | |
636 | ||
637 | ###----- That's all, folks -------------------------------------------------- |