chiark / gitweb /
Makefile, ocbgen: Support Ukrainian `Kalyna' block cipher.
[ocb-tv] / ocbgen
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 --------------------------------------------------