From 43c0985125678d39a384362c7f0349bab812aa94 Mon Sep 17 00:00:00 2001 From: mdw Date: Tue, 21 Jun 2005 07:11:00 +0000 Subject: [PATCH] Modularize the password safe. --- catacomb/__init__.py | 2 + catacomb/pwsafe.py | 124 +++++++++++++++++++++++++++++++++++++++++ pwsafe | 153 ++++++++------------------------------------------- 3 files changed, 149 insertions(+), 130 deletions(-) create mode 100644 catacomb/pwsafe.py diff --git a/catacomb/__init__.py b/catacomb/__init__.py index 65c3fba..0b7f418 100644 --- a/catacomb/__init__.py +++ b/catacomb/__init__.py @@ -473,4 +473,6 @@ def kcdsaprime(pbits, qbits, rng = rand, p = 2 * q * h + 1 return p, q, h +import pwsafe + #----- That's all, folks ---------------------------------------------------- diff --git a/catacomb/pwsafe.py b/catacomb/pwsafe.py new file mode 100644 index 0000000..5503491 --- /dev/null +++ b/catacomb/pwsafe.py @@ -0,0 +1,124 @@ +# -*-python-*- + +import catacomb as _C +import gdbm as _G +import struct as _S + +class DecryptError (Exception): + pass + +class Crypto (object): + def __init__(me, c, h, m, ck, mk): + me.c = c(ck) + me.m = m(mk) + me.h = h + def encrypt(me, pt): + if me.c.__class__.blksz: + iv = _C.rand.block(me.c.__class__.blksz) + me.c.setiv(iv) + else: + iv = '' + y = iv + me.c.encrypt(pt) + t = me.m().hash(y).done() + return t + y + def decrypt(me, ct): + t = ct[:me.m.__class__.tagsz] + y = ct[me.m.__class__.tagsz:] + if t != me.m().hash(y).done(): + raise DecryptError + iv = y[:me.c.__class__.blksz] + if me.c.__class__.blksz: me.c.setiv(iv) + return me.c.decrypt(y[me.c.__class__.blksz:]) + +class PPK (Crypto): + def __init__(me, pp, c, h, m, salt = None): + if not salt: salt = _C.rand.block(h.hashsz) + tag = '%s\0%s' % (pp, salt) + Crypto.__init__(me, c, h, m, + h().hash('cipher:' + tag).done(), + h().hash('mac:' + tag).done()) + me.salt = salt + +class Buffer (object): + def __init__(me, s): + me.str = s + me.i = 0 + def get(me, n): + i = me.i + if n + i > len(me.str): + raise IndexError, 'buffer underflow' + me.i += n + return me.str[i:i + n] + def getbyte(me): + return ord(me.get(1)) + def unpack(me, fmt): + return _S.unpack(fmt, me.get(_S.calcsize(fmt))) + def getstring(me): + return me.get(me.unpack('>H')[0]) + def checkend(me): + if me.i != len(me.str): + raise ValueError, 'junk at end of buffer' + +def _wrapstr(s): + return _S.pack('>H', len(s)) + s + +class PWIter (object): + def __init__(me, pw): + me.pw = pw + me.k = me.pw.db.firstkey() + def next(me): + k = me.k + while True: + if k is None: + raise StopIteration + if k[0] == '$': + break + k = me.pw.db.nextkey(k) + me.k = me.pw.db.nextkey(k) + return me.pw.unpack(me.pw.db[k])[0] +class PW (object): + def __init__(me, file, mode = 'r'): + me.db = _G.open(file, mode) + c = _C.gcciphers[me.db['cipher']] + h = _C.gchashes[me.db['hash']] + m = _C.gcmacs[me.db['mac']] + tag = me.db['tag'] + ppk = PPK(_C.ppread(tag), c, h, m, me.db['salt']) + try: + buf = Buffer(ppk.decrypt(me.db['key'])) + except DecryptError: + _C.ppcancel(tag) + raise + me.ck = buf.getstring() + me.mk = buf.getstring() + buf.checkend() + me.k = Crypto(c, h, m, me.ck, me.mk) + me.magic = me.k.decrypt(me.db['magic']) + def keyxform(me, key): + return '$' + me.k.h().hash(me.magic).hash(key).done() + def changepp(me): + tag = me.db['tag'] + _C.ppcancel(tag) + ppk = PPK(_C.ppread(tag, _C.PMODE_VERIFY), + me.k.c.__class__, me.k.h, me.k.m.__class__) + me.db['key'] = ppk.encrypt(_wrapstr(me.ck) + _wrapstr(me.mk)) + me.db['salt'] = ppk.salt + def pack(me, key, value): + w = _wrapstr(key) + _wrapstr(value) + pl = (len(w) + 255) & ~255 + w += '\0' * (pl - len(w)) + return me.k.encrypt(w) + def unpack(me, p): + buf = Buffer(me.k.decrypt(p)) + key = buf.getstring() + value = buf.getstring() + return key, value + def __getitem__(me, key): + return me.unpack(me.db[me.keyxform(key)])[1] + def __setitem__(me, key, value): + me.db[me.keyxform(key)] = me.pack(key, value) + def __delitem__(me, key): + del me.db[me.keyxform(key)] + def __iter__(me): + return PWIter(me) + diff --git a/pwsafe b/pwsafe index 6b36bf4..e285380 100755 --- a/pwsafe +++ b/pwsafe @@ -2,10 +2,11 @@ # -*-python-*- import catacomb as C -import gdbm, struct +from catacomb.pwsafe import * +import gdbm as G +from os import environ from sys import argv, exit, stdin, stdout, stderr from getopt import getopt, GetoptError -from os import environ from fnmatch import fnmatch if 'PWSAFE' in environ: @@ -13,124 +14,6 @@ if 'PWSAFE' in environ: else: file = '%s/.pwsafe' % environ['HOME'] -class DecryptError (Exception): - pass - -class Crypto (object): - def __init__(me, c, h, m, ck, mk): - me.c = c(ck) - me.m = m(mk) - me.h = h - def encrypt(me, pt): - if me.c.__class__.blksz: - iv = C.rand.block(me.c.__class__.blksz) - me.c.setiv(iv) - else: - iv = '' - y = iv + me.c.encrypt(pt) - t = me.m().hash(y).done() - return t + y - def decrypt(me, ct): - t = ct[:me.m.__class__.tagsz] - y = ct[me.m.__class__.tagsz:] - if t != me.m().hash(y).done(): - raise DecryptError - iv = y[:me.c.__class__.blksz] - if me.c.__class__.blksz: me.c.setiv(iv) - return me.c.decrypt(y[me.c.__class__.blksz:]) - -class PPK (Crypto): - def __init__(me, pp, c, h, m, salt = None): - if not salt: salt = C.rand.block(h.hashsz) - tag = '%s\0%s' % (pp, salt) - Crypto.__init__(me, c, h, m, - h().hash('cipher:' + tag).done(), - h().hash('mac:' + tag).done()) - me.salt = salt - -class Buffer (object): - def __init__(me, s): - me.str = s - me.i = 0 - def get(me, n): - i = me.i - if n + i > len(me.str): - raise IndexError, 'buffer underflow' - me.i += n - return me.str[i:i + n] - def getbyte(me): - return ord(me.get(1)) - def unpack(me, fmt): - return struct.unpack(fmt, me.get(struct.calcsize(fmt))) - def getstring(me): - return me.get(me.unpack('>H')[0]) - def checkend(me): - if me.i != len(me.str): - raise ValueError, 'junk at end of buffer' - -def wrapstr(s): - return struct.pack('>H', len(s)) + s - -class PWIter (object): - def __init__(me, pw): - me.pw = pw - me.k = me.pw.db.firstkey() - def next(me): - k = me.k - while True: - if k is None: - raise StopIteration - if k[0] == '$': - break - k = me.pw.db.nextkey(k) - me.k = me.pw.db.nextkey(k) - return me.pw.unpack(me.pw.db[k])[0] -class PW (object): - def __init__(me, file, mode = 'r'): - me.db = gdbm.open(file, mode) - c = C.gcciphers[me.db['cipher']] - h = C.gchashes[me.db['hash']] - m = C.gcmacs[me.db['mac']] - tag = me.db['tag'] - ppk = PPK(C.ppread(tag), c, h, m, me.db['salt']) - try: - buf = Buffer(ppk.decrypt(me.db['key'])) - except DecryptError: - C.ppcancel(tag) - raise - me.ck = buf.getstring() - me.mk = buf.getstring() - buf.checkend() - me.k = Crypto(c, h, m, me.ck, me.mk) - me.magic = me.k.decrypt(me.db['magic']) - def keyxform(me, key): - return '$' + me.k.h().hash(me.magic).hash(key).done() - def changepp(me): - tag = me.db['tag'] - C.ppcancel(tag) - ppk = PPK(C.ppread(tag, C.PMODE_VERIFY), - me.k.c.__class__, me.k.h, me.k.m.__class__) - me.db['key'] = ppk.encrypt(wrapstr(me.ck) + wrapstr(me.mk)) - me.db['salt'] = ppk.salt - def pack(me, key, value): - w = wrapstr(key) + wrapstr(value) - pl = (len(w) + 255) & ~255 - w += '\0' * (pl - len(w)) - return me.k.encrypt(w) - def unpack(me, p): - buf = Buffer(me.k.decrypt(p)) - key = buf.getstring() - value = buf.getstring() - return key, value - def __getitem__(me, key): - return me.unpack(me.db[me.keyxform(key)])[1] - def __setitem__(me, key, value): - me.db[me.keyxform(key)] = me.pack(key, value) - def __delitem__(me, key): - del me.db[me.keyxform(key)] - def __iter__(me): - return PWIter(me) - def cmd_create(av): cipher = 'blowfish-cbc' hash = 'rmd160' @@ -154,13 +37,13 @@ def cmd_create(av): tag = args[0] else: tag = 'pwsafe' - db = gdbm.open(file, 'n', 0600) + db = G.open(file, 'n', 0600) pp = C.ppread(tag, C.PMODE_VERIFY) if not mac: mac = hash + '-hmac' c = C.gcciphers[cipher] h = C.gchashes[hash] m = C.gcmacs[mac] - ppk = PPK(pp, c, h, m) + ppk = PW.PPK(pp, c, h, m) ck = C.rand.block(c.keysz.default) mk = C.rand.block(m.keysz.default) k = Crypto(c, h, m, ck, mk) @@ -172,6 +55,11 @@ def cmd_create(av): db['key'] = ppk.encrypt(wrapstr(ck) + wrapstr(mk)) db['magic'] = k.encrypt(C.rand.block(h.hashsz)) +def chomp(pp): + if len(pp) > 0 and pp[-1] == '\n': + pp = pp[:-1] + return pp + def cmd_changepp(av): if len(av) != 0: return 1 @@ -198,7 +86,7 @@ def cmd_store(av): else: pp = av[1] pw = PW(file, 'w') - pw[av[0]] = pp + pw[av[0]] = chomp(pp) def cmd_copy(av): if len(av) < 1 or len(av) > 2: @@ -226,15 +114,20 @@ def cmd_list(av): print k def cmd_topixie(av): - if len(av) < 1 or len(av) > 2: + if len(av) > 2: return 1 pw = PW(file) - tag = av[0] - if len(av) >= 2: - pptag = av[1] + pix = C.Pixie() + if len(av) == 0: + for tag in pw: + pix.set(tag, pw[tag]) else: - pptag = av[0] - C.Pixie().set(pptag, pw[tag]) + tag = av[0] + if len(av) >= 2: + pptag = av[1] + else: + pptag = av[0] + pix.set(pptag, pw[tag]) def cmd_del(av): if len(av) != 1: @@ -265,7 +158,7 @@ commands = { 'create': [cmd_create, 'list' : [cmd_list, '[GLOB-PATTERN]'], 'changepp' : [cmd_changepp, ''], 'copy' : [cmd_copy, 'DEST-FILE [GLOB-PATTERN]'], - 'to-pixie' : [cmd_topixie, 'TAG [PIXIE-TAG]'], + 'to-pixie' : [cmd_topixie, '[TAG [PIXIE-TAG]]'], 'delete' : [cmd_del, 'TAG'], 'dump' : [cmd_dump, '']} -- 2.11.0