def decrypt(file): gpg_home = find_gpg_keys() if ( gpg_home == ''): print 'GPG keys not found' sys.exit(1) gpg = GPG(gnupghome=gpg_home, use_agent=True) public_keys = gpg.list_keys() key_id = public_keys[0]['keyid'] if ( os.path.isfile(file)): if ( file.endswith('.gpg')): stream = open(file, 'rb') status = gpg.decrypt_file(stream, output=file[:-4]) if ( status.ok): os.remove(file) print file[:-4] + ' succesfully decrypted' else: print file + ' not encrypted' elif ( os.path.isdir(file) ): for root, dirs, files in os.walk(file, topdown=True): for name in files: current_file = (os.path.join(root, name)) if ( current_file.endswith('.gpg')): stream = open(current_file, "rb") status = gpg.decrypt_file(stream, output=current_file[:-4]) if ( status.ok ): os.remove(current_file) print current_file[:-4] + ' successfully decrypted' else: print current_file + ' not encrypted' else: print 'ERROR: file or directory not found'
class GpgExtractor(StoqExtractorPlugin): def __init__(self): super().__init__() def activate(self, stoq): self.stoq = stoq super().activate() if not os.path.exists(self.gpg_home): self.critical("GPG Home is not defined! Skipping...") return self.gpg = GPG(gnupghome=self.gpg_home, gpgbinary=self.gpg_bin, keyring=self.public_keyring, secret_keyring=self.secret_keyring) def extract(self, payload, **kwargs): """ Decrypt content from provided payload :param bytes payload: Payload to be decrypted :param **kwargs kwargs: Additional attributes (unused) :returns: Decrypted payload :rtype: list of tuples """ passphrase = None always_trust = False if self.passphrase: passphrase = self.passphrase if self.always_trust: always_trust = self.always_trust # Ensure the payload is a ByesIO object payload_object = BytesIO(payload) # Decrypt the payload and return a file object decrypted_payload = self.gpg.decrypt_file(payload_object, passphrase=passphrase, always_trust=always_trust) content = decrypted_payload.data if content: meta = {} meta['size'] = len(content) # Return the decrypted payload return [(meta, content)] else: self.log.error("Unable to decrypt payload: {}".format(kwargs)) return
class GpgExtractor(StoqExtractorPlugin): def __init__(self): super().__init__() def activate(self, stoq): self.stoq = stoq super().activate() if not os.path.exists(self.gpg_home): self.stoq.exception("GPG Home is not defined! Skipping...") self.gpg = GPG(gnupghome=self.gpg_home, gpgbinary=self.gpg_bin, keyring=self.public_keyring, secret_keyring=self.secret_keyring) def extract(self, payload, **kwargs): """ Decrypt content from provided payload :param bytes payload: Payload to be decrypted :param **kwargs kwargs: Additional attributes (unused) :returns: Decrypted payload :rtype: list of tuples """ passphrase = None always_trust = False if self.passphrase: passphrase = self.passphrase if self.always_trust: always_trust = self.always_trust # Ensure the payload is a ByesIO object payload_object = BytesIO(payload) # Decrypt the payload and return a file object decrypted_payload = self.gpg.decrypt_file(payload_object, passphrase=passphrase, always_trust=always_trust) content = decrypted_payload.data if content: meta = {} meta['size'] = len(content) # Return the decrypted payload return [(meta, content)] else: self.stoq.log.error("Unable to decrypt payload: {}".format(kwargs)) return None
class reqPGP(object): def __init__(self, path=None): self.gpg = GPG(gpgbinary=('../gpg.exe' if osName == 'nt' else 'gpg')) if not path: try: self.path = environ["HOME"] + '/' except KeyError: self.path = environ["HOMEPATH"] + '\\' else: if path[-1] != '\\' and osName == 'nt': path += '\\' elif path[-1] != '/' and osName == 'posix': path += '/' self.path = path def genKey(self, account, passphrase): input_data = self.gpg.gen_key_input( name_email=account, passphrase=passphrase) self.gpg.gen_key(input_data) def encryptFile(self, account, data): encryptedData = str(self.gpg.encrypt(data, account)) with open(self.path + '.' + account + '.req', 'w') as f: f.write(encryptedData) def decryptFile(self, account, passphrase): with open(self.path + '.' + account + '.req', 'rb') as f: decryptedData = str(self.gpg.decrypt_file(f, passphrase=passphrase)) return decryptedData def deleteKey(self, keyId): self.gpg.delete_keys(keyId, True) self.gpg.delete_keys(keyId)
class reqPGP(object): def __init__(self, path=None): self.gpg = GPG(gpgbinary=('../gpg.exe' if osName == 'nt' else 'gpg')) if not path: try: self.path = environ["HOME"] + '/' except KeyError: self.path = environ["HOMEPATH"] + '\\' else: if path[-1] != '\\' and osName == 'nt': path += '\\' elif path[-1] != '/' and osName == 'posix': path += '/' self.path = path def genKey(self, account, passphrase): input_data = self.gpg.gen_key_input(name_email=account, passphrase=passphrase) self.gpg.gen_key(input_data) def encryptFile(self, account, data): encryptedData = str(self.gpg.encrypt(data, account)) with open(self.path + '.' + account + '.req', 'w') as f: f.write(encryptedData) def decryptFile(self, account, passphrase): with open(self.path + '.' + account + '.req', 'rb') as f: decryptedData = str(self.gpg.decrypt_file(f, passphrase=passphrase)) return decryptedData def deleteKey(self, keyId): self.gpg.delete_keys(keyId, True) self.gpg.delete_keys(keyId)
def decrypt_file(gpg: gnupg.GPG, targer_folder: str): configuration_kwargs = utils.get_configuratation( path=os.path.join(targer_folder, 'configuration')) gpg = _get_decryption(gpg=gpg, configuration_kwargs=configuration_kwargs) encrypted_filepaths = utils.get_files( path=os.path.join(targer_folder, 'encrypted_files')) decrypted_path = os.path.join(targer_folder, 'decrypted_files') password = configuration_kwargs.get('array')['passphrase'] \ if settings.ENVIRONMENT == 'prod' else 'wow' for encryped_file in encrypted_filepaths: base_name = __set_file_name(path=encryped_file) logger.info(f'Decrypting file {base_name}') output_file = os.path.join(decrypted_path, base_name) with open(encryped_file, 'rb') as connection: status = gpg.decrypt_file(file=connection, passphrase=password, output=output_file) logger.info(f'Decrypting done') if not status.ok: logger.info(f'{status.stderr}') else: logger.info(f'Status [{status.status}]')
def __call__(self): fileName = as_human_readable(self.get_chosen_files()[0]) gpg = GPG() myPass, ok = show_prompt('Passphrase') # https://stackoverflow.com/questions/4444923/get-filename-without-extension-in-python fileNameOut, ok = show_prompt('Target', default=os.path.splitext(fileName)[0]) with open(fileName, 'rb') as f: status = gpg.decrypt_file(f, passphrase=myPass, output=fileNameOut) show_alert('Status: ' + status.status)
def load(file): if file.name[len(file.name) - 4:] in ('.gpg', '.pgp'): gpg = GPG() gpg.encoding = 'utf-8' data = gpg.decrypt_file(file.name) click.echo(data) else: data = file.read() return json.loads(data, 'utf-8')
def decrypt_files(path, gpg: gnupg.GPG): for files in os.listdir(path): actual_path = path + "/" + files if os.path.isfile(actual_path): if files.endswith(".gpg"): logger.debug(f"decrypting: {actual_path}") with open(actual_path, "rb") as reader: print(gpg.decrypt_file(reader, passphrase=__passphrase__, output=actual_path).status) logger.debug(f"encrypting: {actual_path}") with open(actual_path, "rb") as reader: print(gpg.encrypt_file(reader, recipients=__newkeyid__, output=actual_path).status) else: decrypt_files(actual_path, gpg)
def decrypt_and_load(filename, passphrase): # initialize gpg gpg = GPG(gnupghome=f"{os.environ['HOME']}/.gnupg") # open the encrypted file and decrypt it with open(filename, "rb") as f: data = gpg.decrypt_file(f, passphrase=passphrase) # check for errors if not data.ok: raise Exception(data.stderr) # load the contents as yaml yml = ruamel.yaml.YAML(typ="safe") return yml.load(data.data)
def read_key(path, gpg_bin, gpg_opts): """Read and decrypt a single key file. :param str path: The path to the key to decrypt. :param str gpg_bin: The path to the gpg binary. :param list gpg_opts: The options for gpg. :rtype: str :returns: The unencrypted content of the file at `path`. """ gpg = GPG(gpgbinary=gpg_bin, options=gpg_opts) with open(path, 'rb') as key_file: return str(gpg.decrypt_file(key_file))
def decrypt_file(filename, remove_source=None): cfg = ConfigFromJSON(section='gpg') if not op.exists(filename): raise ValueError('File {} not exisit'.format(filename)) gpg = GPG() target = filename.rstrip('.gpg') with open(filename, 'rb') as f: dec = gpg.decrypt_file(f, passphrase=cfg.passphrase, output=target) if not dec.ok: raise RuntimeError(dec.status) if remove_source: os.remove(filename) return target
class Vault(object): def __init__(self): self.vault = {} self._gpg = GPG() self._file_descriptor = None self.passphrase = None def reload(self): if None in (self._file_descriptor, self.passphrase): raise RuntimeError('Cannot reload before calling .load()') self.load(self._file_descriptor, self.passphrase) def load(self, file_descriptor, passphrase): self._file_descriptor = file_descriptor self.passphrase = passphrase self._file_descriptor.seek(0) json_ = self._gpg.decrypt_file(self._file_descriptor, passphrase=self.passphrase) self.vault = json.loads(str(json_)) def save(self, file_descriptor=None, passphrase=None): if file_descriptor is not None: self._file_descriptor = file_descriptor if passphrase is not None: self.passphrase = passphrase self._file_descriptor.seek(0) json_ = json.dumps(self.vault) encrypted = self._gpg.encrypt(json_, False, passphrase=self.passphrase, symmetric=True) self._file_descriptor.write(str(encrypted)) def __getitem__(self, key): return self.vault[key] def get(self, name, default=None): return self.vault.get(name, default) def __setitem__(self, key, value): self.vault[key] = value def __delitem__(self, key): del self._vault[key] def list(self): return sorted(self._vault.keys())
def execute(self): gpg = GPG(gnupghome=os.path.join(os.path.expanduser('~'), '.gnupg')) paths = [ os.path.basename(f.path) for f in self.fm.thistab.get_selection() ] for p in [p for p in paths if p.endswith('gpg')]: with open(p, 'rb') as enc: dec_b = gpg.decrypt_file(enc) out_fname = os.path.splitext(p)[0] with open(out_fname, 'wb+') as dec_f: dec_f.write(dec_b.data) if self.arg(1) != 'true': os.remove(p) if tarfile.is_tarfile(out_fname): tarfile.open(out_fname).extractall(path='.') os.remove(out_fname)
if __name__ == '__main__': get_env_vars() parser = argparse.ArgumentParser(description=DESCR, epilog=EPILOG) parser.add_argument('-k', '--key', dest='key', type=str, help=KEY_DSC) parser.add_argument('-l', '--list', action='store_true') parser.add_argument('-p', '--password', action='store_true') parser.add_argument('-u', '--user', action='store_true') parser.add_argument('-s', '--set', action='store_true') parser.add_argument('-r', '--remove', action='store_true') args = parser.parse_args() gpg_home, pass_loc = get_env_vars() gpg = GPG(gnupghome=gpg_home) with open(pass_loc, mode='rb') as f: decoded = gpg.decrypt_file(f, passphrase=getpass()) if decoded.data == b'': print("Incorrect Passphrase") exit() passes = loads(clean_gpg_json(decoded.data)) if args.list: for key in passes.keys(): print(key) if args.key is not None: if args.password: print_pass(passes, args.key) if args.user: print_user(passes, args.key) if args.set or args.remove: keys = gpg.list_keys() for index, key in enumerate(keys):
class Axolotl: def __init__(self, name, dbname='axolotl.db', dbpassphrase='', user_pathstring='~'): self.ratchetKey = None self.ratchetPKey = None self.name = name self.db = None self.mode = None self.staged_HK_mk = None self.state = None self.handshakeKey = None self.handshakePKey = None self.storeTime = None user_path = os.path.expanduser(user_pathstring) keyring = [user_path + '/.gnupg/pubring.gpg'] secret_keyring = [user_path + '/.gnupg/secring.gpg'] self.dbname = user_path + '/tmp/pyaxo_db/' + dbname self.gpg = GPG(gnupghome=user_path + '/.axolotl', gpgbinary='gpg', keyring=keyring, secret_keyring=secret_keyring, options=['--throw-keyids', '--personal-digest-preferences=sha256', '--s2k-digest-algo=sha256']) self.gpg.encoding = 'utf-8' if dbpassphrase != '' or dbpassphrase is None: self.dbpassphrase = dbpassphrase else: self.dbpassphrase = getpass( 'Database passphrase for ' + self.name + ': ').strip() self.db_init() def db_init(self): try: self.db = self.open_db() except sqlite3.OperationalError: raise (Axolotl_exception('Bad sql! Password problem - \ cannot create the database.')) self.mode = None self.staged_HK_mk = {} self.state = {} self.state['DHIs_priv'], self.state['DHIs'] = self.genKey() self.state['DHRs_priv'], self.state['DHRs'] = self.genKey() self.handshakeKey, self.handshakePKey = self.genKey() # minimum time (seconds) to store missed ephemeral message keys self.storeTime = 2 * 86400 with self.db: cur = self.db.cursor() cur.execute(""" CREATE TABLE IF NOT EXISTS skipped_mk (my_identity, to_identity, HKr TEXT, mk TEXT, timestamp INTEGER )""") cur.execute(""" CREATE UNIQUE INDEX IF NOT EXISTS message_keys ON skipped_mk (mk)""") cur.execute(""" CREATE TABLE IF NOT EXISTS conversations (my_identity TEXT, other_identity TEXT, RK TEXT, HKs TEXT, HKr TEXT, NHKs TEXT, NHKr TEXT, CKs TEXT, CKr TEXT, DHIs_priv TEXT, DHIs TEXT, DHIr TEXT, DHRs_priv TEXT, DHRs TEXT, DHRr TEXT, CONVid TEXT, Ns INTEGER, Nr INTEGER, PNs INTEGER, ratchet_flag INTEGER, mode INTEGER)""") cur.execute(""" CREATE UNIQUE INDEX IF NOT EXISTS conversation_route ON conversations (my_identity, other_identity)""") self.commit_skipped_mk() def triple_dh(self, a, a0, B, B0): if self.mode is None: raise (Axolotl_exception('Mode must be set')) if self.mode: return sha256( self.gen_dh(a, B0) + self.gen_dh(a0, B) + self.gen_dh(a0, B0)).digest() else: return sha256( self.gen_dh(a0, B) + self.gen_dh(a, B0) + self.gen_dh(a0, B0)).digest() def initState(self, other_name, other_identityKey, other_handshakeKey, other_ratchetKey, verify=True): if verify: print('Confirm ' + other_name + ' has identity key fingerprint:\n') fingerprint = sha224(other_identityKey).hexdigest().upper() fprint = '' for i in range(0, len(fingerprint), 4): fprint += fingerprint[i:i + 2] + ':' print(fprint[:-1] + '\n') print('Be sure to verify this fingerprint with ' + other_name + ' by some out-of-band method!') print('Otherwise, you may be subject to a \ Man-in-the-middle attack!\n') ans = raw_input('Confirm? y/N: ').strip() if ans != 'y': raise (Axolotl_exception('Key fingerprint \ not confirmed - exception')) if self.state['DHIs'] < other_identityKey: self.mode = True else: self.mode = False mkey = self.triple_dh(self.state['DHIs_priv'], self.handshakeKey, other_identityKey, other_handshakeKey) self.createState(other_name, mkey, mode=self.mode, other_identityKey=other_identityKey, other_ratchetKey=other_ratchetKey) def createState(self, other_name, mkey, mode=None, other_identityKey=None, other_ratchetKey=None): self.mode = mode if self.mode is None: # mode not selected raise (Axolotl_exception('Mode must be set')) if self.mode: # alice mode RK = pbkdf2(mkey, b'\x00', 10, prf='hmac-sha256') HKs = None HKr = pbkdf2(mkey, b'\x02', 10, prf='hmac-sha256') NHKs = pbkdf2(mkey, b'\x03', 10, prf='hmac-sha256') NHKr = pbkdf2(mkey, b'\x04', 10, prf='hmac-sha256') CKs = None CKr = pbkdf2(mkey, b'\x06', 10, prf='hmac-sha256') DHRs_priv = None DHRs = None DHRr = other_ratchetKey CONVid = pbkdf2(mkey, b'\x07', 10, prf='hmac-sha256') Ns = 0 Nr = 0 PNs = 0 ratchet_flag = True else: # bob mode RK = pbkdf2(mkey, b'\x00', 10, prf='hmac-sha256') HKs = pbkdf2(mkey, b'\x02', 10, prf='hmac-sha256') HKr = None NHKs = pbkdf2(mkey, b'\x04', 10, prf='hmac-sha256') NHKr = pbkdf2(mkey, b'\x03', 10, prf='hmac-sha256') CKs = pbkdf2(mkey, b'\x06', 10, prf='hmac-sha256') CKr = None DHRs_priv = self.state['DHRs_priv'] DHRs = self.state['DHRs'] DHRr = None CONVid = pbkdf2(mkey, b'\x07', 10, prf='hmac-sha256') Ns = 0 Nr = 0 PNs = 0 ratchet_flag = False DHIr = other_identityKey self.state = \ {'name': self.name, 'other_name': other_name, 'RK': RK, 'HKs': HKs, 'HKr': HKr, 'NHKs': NHKs, 'NHKr': NHKr, 'CKs': CKs, 'CKr': CKr, 'DHIs_priv': self.state['DHIs_priv'], 'DHIs': self.state['DHIs'], 'DHIr': DHIr, 'DHRs_priv': DHRs_priv, 'DHRs': DHRs, 'DHRr': DHRr, 'CONVid': CONVid, 'Ns': Ns, 'Nr': Nr, 'PNs': PNs, 'ratchet_flag': ratchet_flag, } self.ratchetKey = False self.ratchetPKey = False def encrypt(self, plaintext): if self.state['ratchet_flag']: self.state['DHRs_priv'], self.state['DHRs'] = self.genKey() self.state['HKs'] = self.state['NHKs'] self.state['RK'] = sha256(self.state['RK'] + self.gen_dh( self.state['DHRs_priv'], self.state['DHRr'])).digest() if self.mode: self.state['NHKs'] = pbkdf2(self.state['RK'], b'\x03', 10, prf='hmac-sha256') self.state['CKs'] = pbkdf2(self.state['RK'], b'\x05', 10, prf='hmac-sha256') else: self.state['NHKs'] = pbkdf2(self.state['RK'], b'\x04', 10, prf='hmac-sha256') self.state['CKs'] = pbkdf2(self.state['RK'], b'\x06', 10, prf='hmac-sha256') self.state['PNs'] = self.state['Ns'] self.state['Ns'] = 0 self.state['ratchet_flag'] = False mk = sha256(self.state['CKs'] + '0').digest() msg1 = self.enc(self.state['HKs'], str(self.state['Ns']).zfill(3) + str(self.state['PNs']).zfill(3) + self.state['DHRs']) msg2 = self.enc(mk, plaintext) pad_length = 106 - len(msg1) pad = os.urandom(pad_length - 1) + chr(pad_length) msg = msg1 + pad + msg2 self.state['Ns'] += 1 self.state['CKs'] = sha256(self.state['CKs'] + '1').digest() return msg def commit_skipped_mk(self): timestamp = int(time()) with self.db: cur = self.db.cursor() for mk, HKr in self.staged_HK_mk.iteritems(): cur.execute("""REPLACE INTO skipped_mk (my_identity, to_identity, HKr, mk, timestamp ) VALUES (?, ?, ?, ?, ?)""", self.state['name'], self.state['other_name'], b2a_base64(HKr).strip(), b2a_base64(mk).strip(), timestamp) rowtime = timestamp - self.storeTime cur.execute('DELETE FROM skipped_mk WHERE timestamp < ?', (rowtime,)) def trySkippedMK(self, msg, pad_length, name, other_name): with self.db: cur = self.db.cursor() cur.execute('SELECT * FROM skipped_mk') rows = cur.fetchall() for row in rows: if name == row[0] and other_name == row[1]: msg1 = msg[:106 - pad_length] msg2 = msg[106:] header = self.dec(a2b_base64(row[2]), msg1) body = self.dec(a2b_base64(row[3]), msg2) if header != '' and body != '': cur.execute('DELETE FROM skipped_mk WHERE mk = ?', (row[3],)) return body return False def stageSkippedMK(self, HKr, Nr, Np, CKr): CKp = CKr for i in range(Np - Nr): mk = sha256(CKp + '0').digest() CKp = sha256(CKp + '1').digest() self.staged_HK_mk[mk] = HKr mk = sha256(CKp + '0').digest() CKp = sha256(CKp + '1').digest() return CKp, mk def decrypt(self, msg): pad = msg[105:106] pad_length = ord(pad) msg1 = msg[:106 - pad_length] body = self.trySkippedMK(msg, pad_length, self.state['name'], self.state['other_name']) if body and body != '': return body header = None if self.state['HKr']: header = self.dec(self.state['HKr'], msg1) if header and header != '': Np = int(header[:3]) CKp, mk = self.stageSkippedMK(self.state['HKr'], self.state['Nr'], Np, self.state['CKr']) body = self.dec(mk, msg[106:]) if not body or body == '': raise (Axolotl_exception('Undecipherable message')) else: header = self.dec(self.state['NHKr'], msg1) if self.state['ratchet_flag'] or not header or header == '': raise (Axolotl_exception('Undecipherable message')) Np = int(header[:3]) PNp = int(header[3:6]) DHRp = header[6:] if self.state['CKr']: self.stageSkippedMK(self.state['HKr'], self.state['Nr'], PNp, self.state['CKr']) HKp = self.state['NHKr'] RKp = sha256( self.state['RK'] + self.gen_dh(self.state['DHRs_priv'], DHRp)).digest() if self.mode: NHKp = pbkdf2(RKp, b'\x04', 10, prf='hmac-sha256') CKp = pbkdf2(RKp, b'\x06', 10, prf='hmac-sha256') else: NHKp = pbkdf2(RKp, b'\x03', 10, prf='hmac-sha256') CKp = pbkdf2(RKp, b'\x05', 10, prf='hmac-sha256') CKp, mk = self.stageSkippedMK(HKp, 0, Np, CKp) body = self.dec(mk, msg[106:]) if not body or body == '': raise (Axolotl_exception('Undecipherable message')) self.state['RK'] = RKp self.state['HKr'] = HKp self.state['NHKr'] = NHKp self.state['DHRr'] = DHRp self.state['DHRs_priv'] = None self.state['DHRs'] = None self.state['ratchet_flag'] = True self.commit_skipped_mk() self.state['Nr'] = Np + 1 self.state['CKr'] = CKp return body def encrypt_file(self, filename): with open(filename, 'r') as f: plaintext = f.read() ciphertext = b2a_base64(self.encrypt(plaintext)) with open(filename + '.asc', 'w') as f: lines = [ciphertext[i:i + 64] for i in xrange(0, len(ciphertext), 64)] for line in lines: f.write(line + '\n') def decrypt_file(self, filename): with open(filename, 'r') as f: ciphertext = a2b_base64(f.read()) plaintext = self.decrypt(ciphertext) print(plaintext) def encrypt_pipe(self): plaintext = sys.stdin.read() ciphertext = b2a_base64(self.encrypt(plaintext)) sys.stdout.write(ciphertext) sys.stdout.flush() def decrypt_pipe(self): ciphertext = a2b_base64(sys.stdin.read()) plaintext = self.decrypt(ciphertext) sys.stdout.write(plaintext) sys.stdout.flush() def printKeys(self): print('Your Identity key is:\n' + b2a_base64( self.state['DHIs'])) fingerprint = sha224(self.state['DHIs']).hexdigest().upper() fprint = '' for i in range(0, len(fingerprint), 4): fprint += fingerprint[i:i + 2] + ':' print('Your identity key fingerprint is: ') print(fprint[:-1] + '\n') print('Your Ratchet key is:\n' + b2a_base64( self.state['DHRs'])) if self.handshakeKey: print('Your Handshake key is:\n' + b2a_base64( self.handshakePKey)) else: print('Your Handshake key is not available') def to_json(self): _j = {'name': self.name, 'identity_key': b2a_base64(self.state('DHIs')), 'fingerprint': sha224(self.state['DHIs']).hexdigest().upper(), 'ratchet_key': b2a_base64(self.state['DHRs'])} return json.dumps(_j) def init_from_json(self, _jsn): _k = json.loads(_jsn) self.initState(_k['name'], a2b_base64(_k['identity_key']), a2b_base64(_k['fingerprint']), a2b_base64(_k['ratchet_key'])) self.saveState() def saveState(self): ktup = ('HKs', 'HKr', 'CKs', 'CKR', 'DHIr', 'DHRs_priv', 'DHRs') v = {k: b2a_base64(self.state[k]).strip for k in ktup} ratchet_flag = 1 if self.state['ratchet_flag'] else 0 mode = 1 if self.mode else 0 with self.db: cur = self.db.cursor() cur.execute("""\ REPLACE INTO conversations ( my_identity, other_identity, RK,HKS, HKr, NHKs, NHKr, CKs, CKr, DHIs_priv, DHIs, DHIr, DHRs_priv, DHRs, DHRr, CONVid, Ns, Nr, PNs, ratchet_flag, mode) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)""", (self.state['name'], self.state['other_name'], b2a_base64(self.state['RK']).strip(), v['HKs'], v['HKr'], b2a_base64(self.state['NHKs']).strip(), b2a_base64(self.state['NHKr']).strip(), v['CKs'], v['CKr'], b2a_base64(self.state['DHIs_priv']).strip(), b2a_base64(self.state['DHIs']).strip(), v['DHIr'], v['DHRs_priv'], v['DHRs'], v['DHRr'], b2a_base64(self.state['CONVid']).strip(), self.state['Ns'], self.state['Nr'], self.state['PNs'], ratchet_flag, mode )) self.write_db() def loadState(self, name, other_name): self.db = self.open_db() with self.db: cur = self.db.cursor() try: cur.execute('SELECT * FROM conversations') except sqlite3.OperationalError: raise (Axolotl_exception('Bad sql! Password problem - \ cannot loadState()')) rows = cur.fetchall() for row in rows: if row[0] == name and row[1] == other_name: self.state = {'name': row[0], 'other_name': row[1], 'RK': a2b_base64(row[2]), 'NHKs': a2b_base64(row[5]), 'NHKr': a2b_base64(row[6]), 'DHIs_priv': a2b_base64(row[9]), 'DHIs': a2b_base64(row[10]), 'CONVid': a2b_base64(row[15]), 'Ns': row[16], 'Nr': row[17], 'PNs': row[18]} self.name = self.state['name'] def do_bin(loc): if row[loc] == '0': return None else: return a2b_base64(row[loc]) self.state['HKs'] = do_bin(3) self.state['HKr'] = do_bin(4) self.state['CKs'] = do_bin(7) self.state['CKr'] = do_bin(8) self.state['DHIr'] = do_bin(11) self.state['DHRs_priv'] = do_bin(12) self.state['DHRs'] = do_bin(13) self.state['DHRr'] = do_bin(14) ratchet_flag = row[19] if ratchet_flag == 1: self.state['ratchet_flag'] = True else: self.state['ratchet_flag'] = False mode = row[20] self.mode = True if mode == 1 else False return # exit at first match return False # if no matches def open_db(self): db = sqlite3.connect(':memory:') try: with open(self.dbname, 'rb') as f: if self.dbpassphrase is not None: sql = \ self.gpg.decrypt_file(f, passphrase=self.dbpassphrase) if sql is not None and sql != '': db.cursor().executescript(sql.data) return db else: raise (Axolotl_exception('Bad passphrase!')) else: sql = f.read() db.cursor().executescript(sql) return db except IOError: return db def write_db(self): sql = '' for item in self.db.iterdump(): sql = sql + item + '\n' if self.dbpassphrase is not None: crypt_sql = self.gpg.encrypt(sql, recipients=None, symmetric='AES256', armor=False, always_trust=True, passphrase=self.dbpassphrase) with open(self.dbname, 'wb') as f: f.write(crypt_sql.data) else: with open(self.dbname, 'w') as f: f.write(sql) def print_state(self): print('\nWarning: saving this data to disk is insecure!\n') for key in sorted(self.state): if 'priv' in key: pass else: if self.state[key] is None: print(key + ': None') elif type(self.state[key]) is bool: if self.state[key]: print(key + ': True') else: print(key + ': False') elif type(self.state[key]) is str: try: self.state[key].decode('ascii') print(key + ': ' + self.state[key]) except UnicodeDecodeError: print(key + ': ' + b2a_base64( self.state[key]).strip()) else: print(key + ': ' + str(self.state[key])) if self.mode: print('Mode: Alice') else: print('Mode: Bob') @staticmethod def genKey(): key = keys.Private() privkey = key.private pubkey = key.get_public().serialize() return privkey, pubkey @staticmethod def gen_dh(a, B): key = keys.Private(secret=a) return key.get_shared_key(keys.Public(B)) def dec(self, key, encrypted): key = hexlify(key) msg = self.gpg.decrypt(GPG_HEADER + encrypted, passphrase=key, always_trust=True) return msg.data def enc(self, key, plaintext): key = hexlify(key) msg = self.gpg.encrypt(plaintext, recipients=None, symmetric=GPG_CIPHER, armor=False, always_trust=True, passphrase=key) return msg.data[6:]
class gpg(object): """ module to wrap the gnupg library and gpg binary """ def __init__(self, folder_path=None, binary_path=None): # ugly but working if binary_path is None: binary_path = join(project_base, "gpg", "gpg.exe") if not isfile(binary_path): binary_path = join(project_base, 'gpg') if not isfile(binary_path): binary_path = find_executable('gpg') if not isfile(binary_path): raise RuntimeError('gpg not found') self.gpg = GPG( binary_path, # gpgbinary folder_path, # gnupghome verbose=False, options=["--allow-non-selfsigned-uid"]) def get_key(self, fingerprint, private, passphrase): """ returns the key belonging to the fingerprint given. if 'private' is True, the private key is returned. If 'private' is False, the public key will be returned. """ key = self.gpg.export_keys(fingerprint, private, armor=False, passphrase=passphrase) return b64encode(key) def add_keypair(self, public_key, private_key, site, user, passphrase): """ add a keypair into the gpg key database """ try: result1 = self.gpg.import_keys(b64decode(public_key)) result2 = self.gpg.import_keys(b64decode(private_key)) except TypeError as error: getLogger(__name__).critical("add_keypair TypeError " + str(error)) # make sure this is a key _pair_ try: assert result1.fingerprints[0] == result2.fingerprints[0] except (IndexError, AssertionError) as error: getLogger(__name__).exception( 'add_keypair IndexError/AssertionError: ' + str(error)) return None fingerprint = result1.fingerprints[0] if self.is_passphrase_valid(passphrase=passphrase, fingerprint=fingerprint): old_fingerprint = self.get_fingerprint(site, user) if not old_fingerprint: sql = "INSERT INTO keys (site, user, fingerprint) VALUES (?, ?, ?)" database_execute(sql, (site, user, fingerprint)) else: sql = "UPDATE keys SET SITE=? WHERE fingerprint=?" database_execute(sql, (site, old_fingerprint)) if fingerprint != old_fingerprint: getLogger(__name__).warn('updating %s fingerprint to %s' % (user, fingerprint)) return fingerprint else: return None def add_public_key(self, site, user, public_key): """ add a public key into the gpg key database """ try: result1 = self.gpg.import_keys(b64decode(public_key)) fingerprint = result1.fingerprints[0] sql = "INSERT INTO keys (site, user, fingerprint) VALUES (?, ?, ?)" database_execute(sql, (site, user, fingerprint)) return fingerprint except TypeError as error: getLogger(__name__).critical("add_public_key TypeError " + str(error)) except DatabaseError as error: getLogger(__name__).critical("add_public_key DatabaseError " + str(error)) def is_passphrase_valid(self, passphrase, label=None, user=None, fingerprint=None): if not fingerprint: fingerprint = self.get_fingerprint(label, user) sign_result = self.gpg.sign("test", keyid=fingerprint, passphrase=passphrase) return sign_result.data != '' def generate(self, passphrase, site, user): """ Generate a new 2048 bit GPG key and add it to the gpg manager. """ data = self.gpg.gen_key_input(key_length=2048, passphrase=passphrase) dat = self.gpg.gen_key(data) fingerprint = dat.fingerprint sql = "INSERT INTO keys (site, user, fingerprint) VALUES (?, ?, ?);" database_execute(sql, (site, user, fingerprint)) return fingerprint def get_fingerprint(self, site, user): sql = "select fingerprint from keys where site = ? and user = ?" result = database_execute(sql, (site, user)) if not len(result): return None return result[0][0] def has_key(self, site, user): """ Check whether a key is present for a certain site and user """ return self.get_fingerprint(site, user) is None def encrypt(self, data, site, user, armor=False): """ encrypt data for user at site. """ fingerprint = self.get_fingerprint(site, user) cryptdata = self.gpg.encrypt_file(StringIO(data), fingerprint, always_trust=True, armor=armor) return str(cryptdata) def decrypt(self, data, passphrase): """ decrypt data received from site. """ datafile = StringIO(data) result = self.gpg.decrypt_file(datafile, passphrase=passphrase, always_trust=True) return str(result) @staticmethod def add_pkcs7_padding(contents): # Input strings must be a multiple of the segment size 16 bytes in length segment_size = 16 # calculate how much padding is needed old_contents_length = len(contents) next_mult = old_contents_length + (segment_size - old_contents_length % segment_size) getLogger(__name__).debug( 'old contents length %s || new contents length %s' % (old_contents_length, next_mult)) # do the padding padding_byte = chr(next_mult - old_contents_length) contents = contents.ljust(next_mult, padding_byte) return contents @staticmethod def remove_pkcs7_padding(contents): """ Remove PKCS#7 padding bytes >>> gpg.remove_pkcs7_padding('some_content'.ljust(16, chr(4))) 'some_content' >>> gpg.remove_pkcs7_padding('some_content') 'some_content' >>> gpg.remove_pkcs7_padding('') '' :param contents: :return: contents without padding """ if len(contents) < 1: getLogger(__name__).debug( 'contents is empty. No PKCS#7 padding removed') return contents bytes_to_remove = ord(contents[-1]) # will work up to 255 bytes # check if contents have valid PKCS#7 padding if bytes_to_remove > 1 and contents[-2] != contents[-1]: getLogger(__name__).debug('no PKCS#7 padding detected') return contents getLogger(__name__).debug('removing %s bytes of PKCS#7 padding' % bytes_to_remove) return contents[0:(len(contents) - bytes_to_remove)]