Subversion Repositories pub

Compare Revisions

Regard whitespace Rev 121 → Rev 122

/relevation/branches/0.2/CHANGELOG
0,0 → 1,8
0.2.0 (2011-06-26) (unreleased)
- Allow multiple queries in one run
- Consider positional arguments as queries
- Feedback enhancements
 
0.1.0 (2011-06-24) (unreleased)
- Initial proof of concept
 
/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,10 → 42,59
# p(' -a, --ask Ask password interactively\n')
p('\n')
 
def search(xmldata, search, caseInsensitive=True):
doc = libxml2.parseDoc(xmldata)
ctxt = doc.xpathNewContext()
commonXPath = '//revelationdata//entry[@type!="folder"]//text()'
caseSensitiveXPath = '[contains(., "%s")]/../..' % search
caseInsensitiveXPath = '[contains(translate(., "ABCDEFGHIJKLMNOPQRSTUVWXYZ", "abcdefghijklmnopqrstuvwxyz"), "%s")]/../..' % search
if caseInsensitive:
xpath = commonXPath + caseInsensitiveXPath
else:
xpath = commonXPath + caseSensitiveXPath
res = ctxt.xpathEval(xpath)
print '-> Search "%s": ' % search,
if not len(res):
print 'No matches'
sys.exit(2)
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 '- * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - '
print ''
for attr in x.properties: # Is it accessible directly?
if attr.name == 'type':
print 'Type:',attr.children
for chld in x.children:
n = chld.name
val = chld.content
if n == 'name':
print 'Name:',val
elif n == 'description':
print 'Description:',val
elif n == 'field':
for attr in chld.properties:
if attr.name == 'id':
idv = attr.content
if idv in tagnames:
idv = tagnames[idv]
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
search = None
searches = []
caseInsensitive = True
 
sys.stderr.write('Relevation v%s, (c) 2011 Toni Corvera\n\n' % __version__)
53,6 → 106,9
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
63,7 → 119,7
p = sys.stdin.readline()
p = p[:-1]
elif opt in ( '-s', '--search' ):
search = arg
searches.append(arg)
elif opt in ( '-i', '--case-insensitive' ):
caseInsensitive = True
elif opt in ( '-c', '--case-sensitive' ):
73,70 → 129,38
if not datafile or not password:
usage(sys.stderr)
sys.exit(os.EX_USAGE)
# read data from file
# Encrypted data
try:
f = open(datafile, "rb")
data = f.read()
finally:
f.close()
# pad the password
# Pad password
password += (chr(0) * (32 - len(password)))
# get the IV
# Data IV
c = AES.new(password)
iv = c.decrypt(data[12:28])
# decrypt the data
# Decrypt. Decrypted data is compressed
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:
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)
 
doc = libxml2.parseDoc(xmldata)
ctxt = doc.xpathNewContext()
commonXPath = '//revelationdata//entry[@type!="folder"]//text()'
caseSensitiveXPath = '[contains(., "%s")]/../..' % search
caseInsensitiveXPath = '[contains(translate(., "ABCDEFGHIJKLMNOPQRSTUVWXYZ", "abcdefghijklmnopqrstuvwxyz"), "%s")]/../..' % search
if caseInsensitive:
xpath = commonXPath + caseInsensitiveXPath
else:
xpath = commonXPath + caseSensitiveXPath
res = ctxt.xpathEval(xpath)
if not len(res):
sys.stderr.write('No matches\n')
sys.exit(2)
print '%d matches:' % len(res)
tagnames ={ 'generic-url': 'Url:',
'generic-username': 'Username:',
'generic-password': 'Password:',
}
for x in res:
print '- * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - '
print ''
for attr in x.properties: # Is it accessible directly?
if attr.name == 'type':
print 'Type:',attr.children
for chld in x.children:
n = chld.name
val = chld.content
if n == 'name':
print 'Name:',val
elif n == 'description':
print 'Description:',val
elif n == 'field':
for attr in chld.properties:
if attr.name == 'id':
idv = attr.content
if idv in tagnames:
idv = tagnames[idv]
print idv,chld.content
print ''
# for chld in x.children
doc.freeDoc()
ctxt.xpathFreeContext()
 
 
if __name__ == '__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: #