6 from sys
import argv
, exit
, stdin
, stdout
, stderr
7 from getopt
import getopt
, GetoptError
9 from fnmatch
import fnmatch
11 if 'PWSAFE' in environ
:
12 file = environ
['PWSAFE']
14 file = '%s/.pwsafe' % environ
['HOME']
16 class DecryptError (Exception):
19 class Crypto (object):
20 def __init__(me
, c
, h
, m
, ck
, mk
):
25 if me
.c
.__class__
.blksz
:
26 iv
= C
.rand
.block(me
.c
.__class__
.blksz
)
30 y
= iv
+ me
.c
.encrypt(pt
)
31 t
= me
.m().hash(y
).done()
34 t
= ct
[:me
.m
.__class__
.tagsz
]
35 y
= ct
[me
.m
.__class__
.tagsz
:]
36 if t
!= me
.m().hash(y
).done():
38 iv
= y
[:me
.c
.__class__
.blksz
]
39 if me
.c
.__class__
.blksz
: me
.c
.setiv(iv
)
40 return me
.c
.decrypt(y
[me
.c
.__class__
.blksz
:])
43 def __init__(me
, pp
, c
, h
, m
, salt
= None):
44 if not salt
: salt
= C
.rand
.block(h
.hashsz
)
45 tag
= '%s\0%s' %
(pp
, salt
)
46 Crypto
.__init__(me
, c
, h
, m
,
47 h().hash('cipher:' + tag
).done(),
48 h().hash('mac:' + tag
).done())
51 class Buffer (object):
57 if n
+ i
> len(me
.str):
58 raise IndexError, 'buffer underflow'
60 return me
.str[i
:i
+ n
]
64 return struct
.unpack(fmt
, me
.get(struct
.calcsize(fmt
)))
66 return me
.get(me
.unpack('>H')[0])
68 if me
.i
!= len(me
.str):
69 raise ValueError, 'junk at end of buffer'
72 return struct
.pack('>H', len(s
)) + s
74 class PWIter (object):
77 me
.k
= me
.pw
.db
.firstkey()
85 k
= me
.pw
.db
.nextkey(k
)
86 me
.k
= me
.pw
.db
.nextkey(k
)
87 return me
.pw
.unpack(me
.pw
.db
[k
])[0]
89 def __init__(me
, file, mode
= 'r'):
90 me
.db
= gdbm
.open(file, mode
)
91 c
= C
.gcciphers
[me
.db
['cipher']]
92 h
= C
.gchashes
[me
.db
['hash']]
93 m
= C
.gcmacs
[me
.db
['mac']]
95 ppk
= PPK(C
.ppread(tag
), c
, h
, m
, me
.db
['salt'])
97 buf
= Buffer(ppk
.decrypt(me
.db
['key']))
101 me
.ck
= buf
.getstring()
102 me
.mk
= buf
.getstring()
104 me
.k
= Crypto(c
, h
, m
, me
.ck
, me
.mk
)
105 me
.magic
= me
.k
.decrypt(me
.db
['magic'])
106 def keyxform(me
, key
):
107 return '$' + me
.k
.h().hash(me
.magic
).hash(key
).done()
111 ppk
= PPK(C
.ppread(tag
, C
.PMODE_VERIFY
),
112 me
.k
.c
.__class__
, me
.k
.h
, me
.k
.m
.__class__
)
113 me
.db
['key'] = ppk
.encrypt(wrapstr(me
.ck
) + wrapstr(me
.mk
))
114 me
.db
['salt'] = ppk
.salt
115 def pack(me
, key
, value
):
116 w
= wrapstr(key
) + wrapstr(value
)
117 pl
= (len(w
) + 255) & ~
255
118 w
+= '\0' * (pl
- len(w
))
119 return me
.k
.encrypt(w
)
121 buf
= Buffer(me
.k
.decrypt(p
))
122 key
= buf
.getstring()
123 value
= buf
.getstring()
125 def __getitem__(me
, key
):
126 return me
.unpack(me
.db
[me
.keyxform(key
)])[1]
127 def __setitem__(me
, key
, value
):
128 me
.db
[me
.keyxform(key
)] = me
.pack(key
, value
)
129 def __delitem__(me
, key
):
130 del me
.db
[me
.keyxform(key
)]
135 cipher
= 'blowfish-cbc'
139 opts
, args
= getopt(av
, 'c:h:m:', ['cipher=', 'mac=', 'hash='])
143 if o
in ('-c', '--cipher'):
145 elif o
in ('-m', '--mac'):
147 elif o
in ('-h', '--hash'):
157 db
= gdbm
.open(file, 'n', 0600)
158 pp
= C
.ppread(tag
, C
.PMODE_VERIFY
)
159 if not mac
: mac
= hash + '-hmac'
160 c
= C
.gcciphers
[cipher
]
163 ppk
= PPK(pp
, c
, h
, m
)
164 ck
= C
.rand
.block(c
.keysz
.default
)
165 mk
= C
.rand
.block(m
.keysz
.default
)
166 k
= Crypto(c
, h
, m
, ck
, mk
)
168 db
['salt'] = ppk
.salt
169 db
['cipher'] = cipher
172 db
['key'] = ppk
.encrypt(wrapstr(ck
) + wrapstr(mk
))
173 db
['magic'] = k
.encrypt(C
.rand
.block(h
.hashsz
))
175 def cmd_changepp(av
):
188 if len(av
) < 1 or len(av
) > 2:
192 pp
= C
.getpass("Enter passphrase `%s': " % tag
)
193 vpp
= C
.getpass("Confirm passphrase `%s': " % tag
)
195 raise ValueError, "passphrases don't match"
197 pp
= stdin
.readline()
204 if len(av
) < 1 or len(av
) > 2:
207 pw_out
= PW(av
[0], 'w')
213 if pat
is None or fnmatch(k
, pat
):
225 if pat
is None or fnmatch(k
, pat
):
229 if len(av
) < 1 or len(av
) > 2:
237 C
.Pixie().set(pptag
, pw
[tag
])
248 if ch
< ' ' or ch
> '~': return False
251 if asciip(s
): return s
252 return C
.ByteString(s
)
254 db
= gdbm
.open(file, 'r')
258 print '%r: %r' %
(present(k
), present(db
[k
]))
261 commands
= { 'create': [cmd_create
,
262 '[-c CIPHER] [-h HASH] [-m MAC] [PP-TAG]'],
263 'find' : [cmd_find
, 'LABEL'],
264 'store' : [cmd_store
, 'LABEL [VALUE]'],
265 'list' : [cmd_list
, '[GLOB-PATTERN]'],
266 'changepp' : [cmd_changepp
, ''],
267 'copy' : [cmd_copy
, 'DEST-FILE [GLOB-PATTERN]'],
268 'to-pixie' : [cmd_topixie
, 'TAG [PIXIE-TAG]'],
269 'delete' : [cmd_del
, 'TAG'],
270 'dump' : [cmd_dump
, '']}
275 print >>fp
, 'Usage: pwsafe COMMAND [ARGS...]'
281 Maintains passwords or other short secrets securely.
285 -h, --help Show this help text.
286 -v, --version Show program version number.
287 -u, --usage Show short usage message.
292 print '%s %s' %
(c
, commands
[c
][1])
295 opts
, argv
= getopt(argv
[1:],
297 ['help', 'version', 'usage', 'file='])
302 if o
in ('-h', '--help'):
305 elif o
in ('-v', '--version'):
308 elif o
in ('-u', '--usage'):
311 elif o
in ('-f', '--file'):
319 if argv
[0] in commands
:
324 if commands
[c
][0](argv
):
325 print >>stderr
, 'Usage: pwsafe %s %s' %
(c
, commands
[c
][1])