| 1 | # -*-python-*- |
| 2 | |
| 3 | import catacomb as _C |
| 4 | import gdbm as _G |
| 5 | import struct as _S |
| 6 | |
| 7 | class DecryptError (Exception): |
| 8 | pass |
| 9 | |
| 10 | class Crypto (object): |
| 11 | def __init__(me, c, h, m, ck, mk): |
| 12 | me.c = c(ck) |
| 13 | me.m = m(mk) |
| 14 | me.h = h |
| 15 | def encrypt(me, pt): |
| 16 | if me.c.__class__.blksz: |
| 17 | iv = _C.rand.block(me.c.__class__.blksz) |
| 18 | me.c.setiv(iv) |
| 19 | else: |
| 20 | iv = '' |
| 21 | y = iv + me.c.encrypt(pt) |
| 22 | t = me.m().hash(y).done() |
| 23 | return t + y |
| 24 | def decrypt(me, ct): |
| 25 | t = ct[:me.m.__class__.tagsz] |
| 26 | y = ct[me.m.__class__.tagsz:] |
| 27 | if t != me.m().hash(y).done(): |
| 28 | raise DecryptError |
| 29 | iv = y[:me.c.__class__.blksz] |
| 30 | if me.c.__class__.blksz: me.c.setiv(iv) |
| 31 | return me.c.decrypt(y[me.c.__class__.blksz:]) |
| 32 | |
| 33 | class PPK (Crypto): |
| 34 | def __init__(me, pp, c, h, m, salt = None): |
| 35 | if not salt: salt = _C.rand.block(h.hashsz) |
| 36 | tag = '%s\0%s' % (pp, salt) |
| 37 | Crypto.__init__(me, c, h, m, |
| 38 | h().hash('cipher:' + tag).done(), |
| 39 | h().hash('mac:' + tag).done()) |
| 40 | me.salt = salt |
| 41 | |
| 42 | class Buffer (object): |
| 43 | def __init__(me, s): |
| 44 | me.str = s |
| 45 | me.i = 0 |
| 46 | def get(me, n): |
| 47 | i = me.i |
| 48 | if n + i > len(me.str): |
| 49 | raise IndexError, 'buffer underflow' |
| 50 | me.i += n |
| 51 | return me.str[i:i + n] |
| 52 | def getbyte(me): |
| 53 | return ord(me.get(1)) |
| 54 | def unpack(me, fmt): |
| 55 | return _S.unpack(fmt, me.get(_S.calcsize(fmt))) |
| 56 | def getstring(me): |
| 57 | return me.get(me.unpack('>H')[0]) |
| 58 | def checkend(me): |
| 59 | if me.i != len(me.str): |
| 60 | raise ValueError, 'junk at end of buffer' |
| 61 | |
| 62 | def _wrapstr(s): |
| 63 | return _S.pack('>H', len(s)) + s |
| 64 | |
| 65 | class PWIter (object): |
| 66 | def __init__(me, pw): |
| 67 | me.pw = pw |
| 68 | me.k = me.pw.db.firstkey() |
| 69 | def next(me): |
| 70 | k = me.k |
| 71 | while True: |
| 72 | if k is None: |
| 73 | raise StopIteration |
| 74 | if k[0] == '$': |
| 75 | break |
| 76 | k = me.pw.db.nextkey(k) |
| 77 | me.k = me.pw.db.nextkey(k) |
| 78 | return me.pw.unpack(me.pw.db[k])[0] |
| 79 | class PW (object): |
| 80 | def __init__(me, file, mode = 'r'): |
| 81 | me.db = _G.open(file, mode) |
| 82 | c = _C.gcciphers[me.db['cipher']] |
| 83 | h = _C.gchashes[me.db['hash']] |
| 84 | m = _C.gcmacs[me.db['mac']] |
| 85 | tag = me.db['tag'] |
| 86 | ppk = PPK(_C.ppread(tag), c, h, m, me.db['salt']) |
| 87 | try: |
| 88 | buf = Buffer(ppk.decrypt(me.db['key'])) |
| 89 | except DecryptError: |
| 90 | _C.ppcancel(tag) |
| 91 | raise |
| 92 | me.ck = buf.getstring() |
| 93 | me.mk = buf.getstring() |
| 94 | buf.checkend() |
| 95 | me.k = Crypto(c, h, m, me.ck, me.mk) |
| 96 | me.magic = me.k.decrypt(me.db['magic']) |
| 97 | def keyxform(me, key): |
| 98 | return '$' + me.k.h().hash(me.magic).hash(key).done() |
| 99 | def changepp(me): |
| 100 | tag = me.db['tag'] |
| 101 | _C.ppcancel(tag) |
| 102 | ppk = PPK(_C.ppread(tag, _C.PMODE_VERIFY), |
| 103 | me.k.c.__class__, me.k.h, me.k.m.__class__) |
| 104 | me.db['key'] = ppk.encrypt(_wrapstr(me.ck) + _wrapstr(me.mk)) |
| 105 | me.db['salt'] = ppk.salt |
| 106 | def pack(me, key, value): |
| 107 | w = _wrapstr(key) + _wrapstr(value) |
| 108 | pl = (len(w) + 255) & ~255 |
| 109 | w += '\0' * (pl - len(w)) |
| 110 | return me.k.encrypt(w) |
| 111 | def unpack(me, p): |
| 112 | buf = Buffer(me.k.decrypt(p)) |
| 113 | key = buf.getstring() |
| 114 | value = buf.getstring() |
| 115 | return key, value |
| 116 | def __getitem__(me, key): |
| 117 | try: |
| 118 | return me.unpack(me.db[me.keyxform(key)])[1] |
| 119 | except KeyError: |
| 120 | raise KeyError, key |
| 121 | def __setitem__(me, key, value): |
| 122 | me.db[me.keyxform(key)] = me.pack(key, value) |
| 123 | def __delitem__(me, key): |
| 124 | try: |
| 125 | del me.db[me.keyxform(key)] |
| 126 | except KeyError: |
| 127 | raise KeyError, key |
| 128 | def __iter__(me): |
| 129 | return PWIter(me) |
| 130 | |