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])