summaryrefslogtreecommitdiff
path: root/contrib/revelation2pass.py
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/revelation2pass.py')
-rwxr-xr-xcontrib/revelation2pass.py172
1 files changed, 0 insertions, 172 deletions
diff --git a/contrib/revelation2pass.py b/contrib/revelation2pass.py
deleted file mode 100755
index f04c1a8..0000000
--- a/contrib/revelation2pass.py
+++ /dev/null
@@ -1,172 +0,0 @@
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-#
-# Copyright (C) 2013 Emanuele Aina <em@nerd.ocracy.org>. All Rights Reserved.
-# Copyright (C) 2011 Toni Corvera. All Rights Reserved.
-# This file is licensed under the BSD 2-clause license:
-# http://www.opensource.org/licenses/BSD-2-Clause
-#
-# Import script for the Revelation password manager:
-# http://revelation.olasagasti.info/
-# Heavily based on the Relevation command line tool:
-# http://p.outlyer.net/relevation/
-
-import os, sys, argparse, zlib, getpass, traceback
-from subprocess import Popen, PIPE, STDOUT, CalledProcessError
-from collections import OrderedDict
-try:
- from lxml import etree
-except ImportError:
- from xml.etree import ElementTree as etree
-
-USE_PYCRYPTO = True
-try:
- from Crypto.Cipher import AES
-except ImportError:
- USE_PYCRYPTO = False
- try:
- from crypto.cipher import rijndael, cbc
- from crypto.cipher.base import noPadding
- except ImportError:
- sys.stderr.write('Either PyCrypto or cryptopy are required\n')
- raise
-
-def path_for(element, path=None):
- """ Generate path name from elements name and current path """
- name = element.find('name').text
- name = name.replace('/', '-').replace('\\', '-')
- path = path if path else ''
- return os.path.join(path, name)
-
-def format_password_data(data):
- """ Format the secret data that will be handed to Pass in multi-line mode:
- $password
- $fieldname: $fielddata
- ...
- $multi_line_notes_with_leading_spaces"""
- password = data.pop('password', None) or ''
- ret = password + '\n'
- notes = data.pop('notes', None)
- for label, text in data.iteritems():
- ret += label + ': ' + text + '\n'
- if notes:
- ret += ' ' + notes.replace('\n', '\n ').strip() + '\n'
- return ret
-
-def password_data(element):
- """ Return password data and additional info if available from
- password entry element. """
- data = OrderedDict()
- data['password'] = element.find('field[@id="generic-password"]').text
- data['type'] = element.attrib['type']
- for field in element.findall('field'):
- field_id = field.attrib['id']
- if field_id == 'generic-password':
- continue
- if field.text is not None:
- data[field_id] = field.text
- for tag in ('description', 'notes'):
- field = element.find(tag)
- if field is not None and field.text:
- data[tag] = field.text
- return format_password_data(data)
-
-
-def import_entry(element, path=None, verbose=0):
- """ Import new password entry to password-store using pass insert
- command """
- cmd = ['pass', 'insert', '--multiline', '--force', path_for(element, path)]
- if verbose:
- print 'cmd:\n ' + ' '.join(cmd)
- proc = Popen(cmd, stdin=PIPE, stdout=PIPE, stderr=STDOUT)
- stdin = password_data(element).encode('utf8')
- if verbose:
- print 'input:\n ' + stdin.replace('\n', '\n ').strip()
- stdout, _ = proc.communicate(stdin)
- retcode = proc.poll()
- if retcode:
- raise CalledProcessError(retcode, cmd, output=stdout)
-
-def import_folder(element, path=None, verbose=0):
- path = path_for(element, path)
- import_subentries(element, path, verbose)
-
-def import_subentries(element, path=None, verbose=0):
- """ Import all sub entries of the current folder element """
- for entry in element.findall('entry'):
- if entry.attrib['type'] == 'folder':
- import_folder(entry, path, verbose)
- else:
- import_entry(entry, path, verbose)
-
-def decrypt_gz(key, cipher_text):
- ''' Decrypt cipher_text using key.
- decrypt(str, str) -> cleartext (gzipped xml)
-
- This function will use the underlying, available, cipher module.
- '''
- if USE_PYCRYPTO:
- # Extract IV
- c = AES.new(key)
- iv = c.decrypt(cipher_text[12:28])
- # Decrypt data, CBC mode
- c = AES.new(key, AES.MODE_CBC, iv)
- ct = c.decrypt(cipher_text[28:])
- else:
- # Extract IV
- c = rijndael.Rijndael(key, keySize=len(key), padding=noPadding())
- iv = c.decrypt(cipher_text[12:28])
- # Decrypt data, CBC mode
- bc = rijndael.Rijndael(key, keySize=len(key), padding=noPadding())
- c = cbc.CBC(bc, padding=noPadding())
- ct = c.decrypt(cipher_text[28:], iv=iv)
- return ct
-
-def main(datafile, verbose=False):
- f = None
- with open(datafile, "rb") as f:
- # Encrypted data
- data = f.read()
- password = getpass.getpass()
- # Pad password
- password += (chr(0) * (32 - len(password)))
- # Decrypt. Decrypted data is compressed
- cleardata_gz = decrypt_gz(password, data)
- # 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)
- root = etree.fromstring(xmldata)
- import_subentries(root, verbose=verbose)
-
-if __name__ == '__main__':
- parser = argparse.ArgumentParser()
- parser.add_argument('--verbose', '-v', action='count')
- parser.add_argument('FILE', help="the file storing the Revelation passwords")
- args = parser.parse_args()
-
- def err(s):
- sys.stderr.write(s+'\n')
-
- try:
- main(args.FILE, verbose=args.verbose)
- except KeyboardInterrupt:
- if args.verbose:
- traceback.print_exc()
- err(str(e))
- except zlib.error:
- err('Failed to decompress decrypted data. Wrong password?')
- sys.exit(os.EX_DATAERR)
- except CalledProcessError as e:
- if args.verbose:
- traceback.print_exc()
- print 'output:\n ' + e.output.replace('\n', '\n ').strip()
- else:
- err('CalledProcessError: ' + str(e))
- sys.exit(os.EX_IOERR)
- except IOError as e:
- if args.verbose:
- traceback.print_exc()
- else:
- err('IOError: ' + str(e))
- sys.exit(os.EX_IOERR)