Subversion Repositories pub

Compare Revisions

Ignore whitespace Rev 121 → Rev 122

/relevation/branches/0.2/relevation
13,22 → 13,26
a command line interface to Revelation Password Manager
 
Code based on Revelation's former BTS (no longer online, not archived?):
(1) code:
(ref1) code:
http://oss.wired-networks.net/bugzilla/attachment.cgi?id=13&action=view
(2) bug report:
(ref2) bug report:
http://oss.wired-networks.net/bugzilla/show_bug.cgi?id=111
-> http://web.archive.org/http://oss.wired-networks.net/bugzilla/show_bug.cgi?id=111
(ref3) http://docs.python.org/library/zlib.html
"""
 
__author__ = 'Toni Corvera'
__date__ = '$Date$'
__revision__ = '$Rev$'
__version_info__ = ( 0, 1, 0 )
__version_info__ = ( 0, 2, 0 )
__version__ = '.'.join(map(str, __version_info__))
RELEASE=True
 
def usage(channel):
def p(s):
channel.write(s)
p('%s {-f passwordfile} {-p password | -0} search [search2] [search3]\n')
p('\n')
p(' -f <FILE>, --file=<FILE> Revelation password file\n')
p(' -p <PASS>, --password=<PASS> Master password\n')
p(' -s <STRING>, --search=<STRING> Search for string\n')
38,60 → 42,7
# p(' -a, --ask Ask password interactively\n')
p('\n')
 
def main():
datafile = None
password = None
search = None
caseInsensitive = True
 
sys.stderr.write('Relevation v%s, (c) 2011 Toni Corvera\n\n' % __version__)
try:
ops, args = getopt.getopt(sys.argv[1:], 'f:p:s:0ci',
[ 'file=', 'password=', 'search=', 'stdin',
'case-sensitive', 'case-insensitive' ])
except getopt.GetoptError, err:
print str(err)
usage(sys.stderr)
sys.exit(os.EX_USAGE)
for opt, arg in ops:
if opt in ( '-f', '--file' ):
datafile = arg
elif opt in ( '-p', '--password' ):
password = arg
elif opt in ( '-0', '--stdin' ):
sys.stderr.write('Getting password from stdin\n')
p = sys.stdin.readline()
p = p[:-1]
elif opt in ( '-s', '--search' ):
search = arg
elif opt in ( '-i', '--case-insensitive' ):
caseInsensitive = True
elif opt in ( '-c', '--case-sensitive' ):
caseInsensitive = False
else:
assert False, "internal error parsing options"
if not datafile or not password:
usage(sys.stderr)
sys.exit(os.EX_USAGE)
# read data from file
f = open(datafile, "rb")
data = f.read()
f.close()
# pad the password
password += (chr(0) * (32 - len(password)))
# get the IV
c = AES.new(password)
iv = c.decrypt(data[12:28])
# decrypt the data
c = AES.new(password, AES.MODE_CBC, iv)
rawdata = c.decrypt(data[28:])
# fetch padlen, and decompress data
padlen = ord(rawdata[-1])
xmldata = zlib.decompress(rawdata[:-padlen], 15, 32768)
if not search:
print xmldata
sys.exit(os.EX_OK)
 
def search(xmldata, search, caseInsensitive=True):
doc = libxml2.parseDoc(xmldata)
ctxt = doc.xpathNewContext()
commonXPath = '//revelationdata//entry[@type!="folder"]//text()'
102,13 → 53,16
else:
xpath = commonXPath + caseSensitiveXPath
res = ctxt.xpathEval(xpath)
print '-> Search "%s": ' % search,
if not len(res):
sys.stderr.write('No matches\n')
print 'No matches'
sys.exit(2)
print '%d matches:' % len(res)
print '%d matches' % len(res)
tagnames ={ 'generic-url': 'Url:',
'generic-username': 'Username:',
'generic-password': 'Password:',
'generic-email': 'Email:',
'generic-hostname': 'Hostname:',
}
for x in res:
print '- * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - '
132,11 → 86,81
print idv,chld.content
print ''
# for chld in x.children
sys.stderr.write('<- (end of %d results for "%s")\n\n' % ( len(res), search ))
doc.freeDoc()
ctxt.xpathFreeContext()
 
 
def main():
datafile = None
password = None
searches = []
caseInsensitive = True
 
sys.stderr.write('Relevation v%s, (c) 2011 Toni Corvera\n\n' % __version__)
try:
ops, args = getopt.getopt(sys.argv[1:], 'f:p:s:0ci',
[ 'file=', 'password=', 'search=', 'stdin',
'case-sensitive', 'case-insensitive' ])
except getopt.GetoptError, err:
print str(err)
usage(sys.stderr)
sys.exit(os.EX_USAGE)
if args:
searches = args
 
for opt, arg in ops:
if opt in ( '-f', '--file' ):
datafile = arg
elif opt in ( '-p', '--password' ):
password = arg
elif opt in ( '-0', '--stdin' ):
sys.stderr.write('Getting password from stdin\n')
p = sys.stdin.readline()
p = p[:-1]
elif opt in ( '-s', '--search' ):
searches.append(arg)
elif opt in ( '-i', '--case-insensitive' ):
caseInsensitive = True
elif opt in ( '-c', '--case-sensitive' ):
caseInsensitive = False
else:
assert False, "internal error parsing options"
if not datafile or not password:
usage(sys.stderr)
sys.exit(os.EX_USAGE)
# Encrypted data
try:
f = open(datafile, "rb")
data = f.read()
finally:
f.close()
# Pad password
password += (chr(0) * (32 - len(password)))
# Data IV
c = AES.new(password)
iv = c.decrypt(data[12:28])
# Decrypt. Decrypted data is compressed
c = AES.new(password, AES.MODE_CBC, iv)
cleardata_gz = c.decrypt(data[28:])
# Length of data padding
padlen = ord(cleardata_gz[-1])
# Decompress actual data (15 is wbits [ref3] DON'T CHANGE, 2**15 is the (initial) buf size)
xmldata = zlib.decompress(cleardata_gz[:-padlen], 15, 2**15)
if not searches:
print xmldata
sys.exit(os.EX_OK)
for s in searches:
search(xmldata, s, caseInsensitive)
 
if __name__ == '__main__':
main()
try:
main()
except libxml2.parserError as e:
sys.stderr.write('XML parsing error\n')
if not RELEASE:
import traceback
traceback.print_exc()
sys.exit(os.EX_DATAERR)
 
# vim:set ts=4 et ai fileencoding=utf-8: #