5 from sys import argv, exit, stdin, stdout, stderr
6 from getopt import getopt, GetoptError
8 from fnmatch import fnmatch
10 file = '%s/.pwsafe' % environ['HOME']
12 class DecryptError (Exception):
15 class Crypto (object):
16 def __init__(me, c, h, m, ck, mk):
21 if me.c.__class__.blksz:
22 iv = C.rand.block(me.c.__class__.blksz)
26 y = iv + me.c.encrypt(pt)
27 t = me.m().hash(y).done()
30 t = ct[:me.m.__class__.tagsz]
31 y = ct[me.m.__class__.tagsz:]
32 if t != me.m().hash(y).done():
34 iv = y[:me.c.__class__.blksz]
35 if me.c.__class__.blksz: me.c.setiv(iv)
36 return me.c.decrypt(y[me.c.__class__.blksz:])
39 def __init__(me, pp, c, h, m, salt = None):
40 if not salt: salt = C.rand.block(h.hashsz)
41 tag = '%s\0%s' % (pp, salt)
42 Crypto.__init__(me, c, h, m,
43 h().hash('cipher:' + tag).done(),
44 h().hash('mac:' + tag).done())
47 class Buffer (object):
53 if n + i > len(me.str):
54 raise IndexError, 'buffer underflow'
56 return me.str[i:i + n]
60 return struct.unpack(fmt, me.get(struct.calcsize(fmt)))
62 return me.get(me.unpack('>H')[0])
64 if me.i != len(me.str):
65 raise ValueError, 'junk at end of buffer'
68 return struct.pack('>H', len(s)) + s
70 class PWIter (object):
73 me.k = me.pw.db.firstkey()
81 k = me.pw.db.nextkey(k)
82 me.k = me.pw.db.nextkey(k)
83 return me.pw.unpack(me.pw.db[k])[0]
85 def __init__(me, file, mode = 'r'):
86 me.db = gdbm.open(file, mode)
87 c = C.gcciphers[me.db['cipher']]
88 h = C.gchashes[me.db['hash']]
89 m = C.gcmacs[me.db['mac']]
91 ppk = PPK(C.ppread(tag), c, h, m, me.db['salt'])
93 buf = Buffer(ppk.decrypt(me.db['key']))
97 me.ck = buf.getstring()
98 me.mk = buf.getstring()
100 me.k = Crypto(c, h, m, me.ck, me.mk)
101 me.magic = me.k.decrypt(me.db['magic'])
102 def keyxform(me, key):
103 return '$' + me.k.h().hash(me.magic).hash(key).done()
107 ppk = PPK(C.ppread(tag, C.PMODE_VERIFY),
108 me.k.c.__class__, me.k.h, me.k.m.__class__)
109 me.db['key'] = ppk.encrypt(wrapstr(me.ck) + wrapstr(me.mk))
110 me.db['salt'] = ppk.salt
111 def pack(me, key, value):
112 w = wrapstr(key) + wrapstr(value)
113 pl = (len(w) + 255) & ~255
114 w += '\0' * (pl - len(w))
115 return me.k.encrypt(w)
117 buf = Buffer(me.k.decrypt(p))
118 key = buf.getstring()
119 value = buf.getstring()
121 def __getitem__(me, key):
122 return me.unpack(me.db[me.keyxform(key)])[1]
123 def __setitem__(me, key, value):
124 me.db[me.keyxform(key)] = me.pack(key, value)
125 def __delitem__(me, key):
126 del me.db[me.keyxform(key)]
131 cipher = 'blowfish-cbc'
135 opts, args = getopt(av, 'c:h:m:', ['cipher=', 'mac=', 'hash='])
139 if o in ('-c', '--cipher'):
141 elif o in ('-m', '--mac'):
143 elif o in ('-h', '--hash'):
153 db = gdbm.open(file, 'n', 0600)
154 pp = C.ppread(tag, C.PMODE_VERIFY)
155 if not mac: mac = hash + '-hmac'
156 c = C.gcciphers[cipher]
159 ppk = PPK(pp, c, h, m)
160 ck = C.rand.block(c.keysz.default)
161 mk = C.rand.block(m.keysz.default)
162 k = Crypto(c, h, m, ck, mk)
164 db['salt'] = ppk.salt
165 db['cipher'] = cipher
168 db['key'] = ppk.encrypt(wrapstr(ck) + wrapstr(mk))
169 db['magic'] = k.encrypt(C.rand.block(h.hashsz))
171 def cmd_changepp(av):
184 if len(av) < 1 or len(av) > 2:
188 pp = C.getpass("Enter passphrase `%s': " % tag)
189 vpp = C.getpass("Confirm passphrase `%s': " % tag)
191 raise ValueError, "passphrases don't match"
193 pp = stdin.readline()
200 if len(av) < 1 or len(av) > 2:
203 pw_out = PW(av[0], 'w')
209 if pat is None or fnmatch(k, pat):
221 if pat is None or fnmatch(k, pat):
225 if len(av) < 1 or len(av) > 2:
233 C.Pixie().set(pptag, pw[tag])
237 if ch < ' ' or ch > '~': return False
240 if asciip(s): return s
241 return C.ByteString(s)
243 db = gdbm.open(file, 'r')
247 print '%r: %r' % (present(k), present(db[k]))
250 commands = { 'create': [cmd_create,
251 '[-c CIPHER] [-h HASH] [-m MAC] [PP-TAG]'],
252 'find' : [cmd_find, 'LABEL'],
253 'store' : [cmd_store, 'LABEL [VALUE]'],
254 'list' : [cmd_list, '[GLOB-PATTERN]'],
255 'changepp' : [cmd_changepp, ''],
256 'copy' : [cmd_copy, 'DEST-FILE [GLOB-PATTERN]'],
257 'to-pixie' : [cmd_topixie, 'TAG [PIXIE-TAG]'],
258 'dump' : [cmd_dump, '']}
263 print >>fp, 'Usage: pwsafe COMMAND [ARGS...]'
269 Maintains passwords or other short secrets securely.
273 -h, --help Show this help text.
274 -v, --version Show program version number.
275 -u, --usage Show short usage message.
280 print '%s %s' % (c, commands[c][1])
283 opts, argv = getopt(argv[1:],
285 ['help', 'version', 'usage', 'file='])
290 if o in ('-h', '--help'):
293 elif o in ('-v', '--version'):
296 elif o in ('-u', '--usage'):
299 elif o in ('-f', '--file'):
307 if argv[0] in commands:
312 if commands[c][0](argv):
313 print >>stderr, 'Usage: pwsafe %s %s' % (c, commands[c][1])