| 1 | #! /usr/bin/python |
| 2 | |
| 3 | import catacomb as CAT |
| 4 | |
| 5 | I = 1 |
| 6 | A = 2 |
| 7 | C = 4 |
| 8 | T = 8 |
| 9 | M = 16 |
| 10 | INIT = 256 |
| 11 | |
| 12 | def dump_keccak_state(what, state): |
| 13 | buf = CAT.ReadBuffer(state + CAT.ByteString.zero(200 - len(state))) |
| 14 | print ";; %s [round 0]" % what |
| 15 | print ";; uncomplemented state..." |
| 16 | for i in xrange(5): |
| 17 | print ";;\t%s" % " ".join(["%016x" % buf.getu64l() for j in xrange(5)]) |
| 18 | print |
| 19 | |
| 20 | class Strobe (object): |
| 21 | def __init__(me, sec = 128, _copy = None): |
| 22 | if _copy is None: |
| 23 | me.k = CAT.Keccak1600() |
| 24 | me.r = (1600 - 2*sec)/8 |
| 25 | me.f = 0 |
| 26 | me.p0 = 0 |
| 27 | buf = CAT.WriteBuffer() |
| 28 | buf.putu8(1).putu8(me.r) |
| 29 | buf.putu8(1).putu8(0) |
| 30 | buf.putu8(1).putu8(12*8).put('STROBEv1.0.2') |
| 31 | ##dump_keccak_state("init", buf.contents) |
| 32 | me.k.mix(buf.contents).step() |
| 33 | me.buf = CAT.WriteBuffer() |
| 34 | me.mask = me.k.extract(me.r - 2) |
| 35 | ##dump_keccak_state("final", me.mask) |
| 36 | else: |
| 37 | me.k = _copy.k.dup() |
| 38 | me.r = _copy.r |
| 39 | me.f = _copy.f |
| 40 | me.p0 = _copy.p0 |
| 41 | me.buf = CAT.WriteBuffer().put(_copy.buf.contents) |
| 42 | me.mask = me._copy.mask |
| 43 | |
| 44 | def _crank(me): |
| 45 | me.buf.putu8(me.p0); me.p0 = 0 |
| 46 | if me.buf.size == me.r - 1: me.buf.putu8(0x84) |
| 47 | else: me.buf.putu8(0x04).zero(me.r - me.buf.size - 1).putu8(0x80) |
| 48 | ##dump_keccak_state("buffer", me.buf.contents) |
| 49 | ##dump_keccak_state("init", me.buf.contents ^ me.k.extract(me.r)) |
| 50 | me.k.mix(me.buf.contents).step() |
| 51 | me.mask = me.k.extract(me.r - 2) |
| 52 | ##dump_keccak_state("final", me.mask) |
| 53 | me.buf = CAT.WriteBuffer() |
| 54 | |
| 55 | def process(me, op, input): |
| 56 | if not op&T: |
| 57 | opf = op |
| 58 | else: |
| 59 | if not me.f&INIT: me.f |= INIT | (op&I) |
| 60 | opf = op ^ (me.f&255) |
| 61 | me.buf.putu8(me.p0); me.p0 = me.buf.size |
| 62 | if me.buf.size >= me.r - 2: me._crank() |
| 63 | me.buf.putu8(opf) |
| 64 | if op&C or me.buf.size >= me.r - 2: me._crank() |
| 65 | |
| 66 | if op&(I | T) == I or op&(I | A) == 0: buf = CAT.ByteString.zero(input) |
| 67 | else: buf = CAT.ByteString(input) |
| 68 | out = CAT.WriteBuffer() |
| 69 | i, j, sz = 0, me.buf.size, len(buf) |
| 70 | |
| 71 | while i < sz: |
| 72 | spare = me.r - j - 2 |
| 73 | n = min(spare, sz - i); x = buf[i:i + n]; i += n |
| 74 | if not op&C: out.put(x); me.buf.put(x) |
| 75 | else: |
| 76 | y = x ^ me.mask[j:j + n]; out.put(y) |
| 77 | if op&I or not op&T: me.buf.put(y) |
| 78 | else: me.buf.put(x) |
| 79 | if n == spare: me._crank(); j = 0 |
| 80 | |
| 81 | if op&(I | A | C | T) != (I | C | T): return out.contents |
| 82 | elif out.contents == CAT.ByteString.zero(out.size): return me |
| 83 | else: raise ValueError, 'verify failed' |
| 84 | |
| 85 | st0 = Strobe() |
| 86 | st1 = Strobe() |
| 87 | |
| 88 | st0.process(A | M, 'testing') |
| 89 | st1.process(A | M, 'testing') |
| 90 | |
| 91 | print 'prf(10) -> %s' % hex(st0.process(I | A | C, 10)) |
| 92 | st1.process(I | A | C, 10) |
| 93 | |
| 94 | print 'ad("Hello")' |
| 95 | st0.process(A, 'Hello') |
| 96 | st1.process(A, 'Hello') |
| 97 | |
| 98 | c0 = st0.process(A | C | T, 'World'); |
| 99 | st1.process(I | A | C | T, c0) |
| 100 | print 'encout("World") -> %s' % hex(c0) |
| 101 | |
| 102 | print 'clrout("foo")' |
| 103 | st0.process(A | T, 'foo'); |
| 104 | st1.process(I | A | T, 'foo') |
| 105 | |
| 106 | print 'clrin("bar")' |
| 107 | st1.process(A | T, 'bar') |
| 108 | st0.process(I | A | T, 'bar') |
| 109 | |
| 110 | m1 = st0.process(I | A | C | T, 'baz') |
| 111 | st1.process(A | C | T, m1) # backwards in time |
| 112 | print 'encin("baz") -> %s' % hex(m1) |
| 113 | |
| 114 | for i in xrange(200): |
| 115 | c = st0.process(A | C | T, i*'X') |
| 116 | m = st1.process(I | A | C | T, c) |
| 117 | print " 0 encout\n\t=%s\n\t!%s;" % (i*'X', hex(c)) |
| 118 | print " 1 encin\n\t!%s\n\t=%s;" % (hex(c), i*'X') |
| 119 | |
| 120 | r0 = st0.process(I | A | C, 123) |
| 121 | r1 = st1.process(I | A | C, 123) |
| 122 | print 'prf(123) -> %s' % hex(r0) |
| 123 | assert r0 == r1 |
| 124 | |
| 125 | t = st0.process(C | T, 16) |
| 126 | print 'macout(16) -> %s' % hex(t) |
| 127 | st1.process(I | C | T, t) |
| 128 | |
| 129 | k = 'this is my super-secret key' |
| 130 | print 'key("%s")' % k |
| 131 | st0.process(A | C, k) |
| 132 | st1.process(A | C, k) |
| 133 | |
| 134 | r0 = st0.process(I | A | C, 32) |
| 135 | r1 = st1.process(I | A | C, 32) |
| 136 | print 'prf(32) -> %s' % hex(r0) |
| 137 | assert r0 == r1 |
| 138 | |
| 139 | print 'ratchet(32)' |
| 140 | st0.process(C, 32) |
| 141 | st1.process(C, 32) |
| 142 | |
| 143 | t0 = CAT.bytes('b2084ebdfabd50768c91eebc190132cc') |
| 144 | st0.process(I | C | T, t0) |
| 145 | t1 = st1.process(C | T, 16) |
| 146 | assert t1 == t0 |
| 147 | print 'macin(%s)' % hex(t0) |