From 67594e9f53f80ce1e2d709a7b47d68ee221af2c3 Mon Sep 17 00:00:00 2001 From: Dmitri Bogomolov <4glitch@gmail.com> Date: Thu, 21 Sep 2017 18:36:35 +0300 Subject: [PATCH] class Keystorage implements common key storage logic. .fetch_key(address, key_type) fetches a key of given type .push_keys(address, keys) stores a pair of keys Workflow depends on plugin availability and settings: keysencrypted = True - encrypt keys with plugin keystore = keys: - use Plugin to store keys keystore = password: - store encryption password --- src/class_addressGenerator.py | 7 ++- src/class_singleWorker.py | 9 +--- src/shared.py | 89 +++++++++++++++++++++++++++++++++-- 3 files changed, 89 insertions(+), 16 deletions(-) diff --git a/src/class_addressGenerator.py b/src/class_addressGenerator.py index d930fc99..33f8fbf5 100644 --- a/src/class_addressGenerator.py +++ b/src/class_addressGenerator.py @@ -179,12 +179,11 @@ class addressGenerator(StoppableThread): nonceTrialsPerByte)) BMConfigParser().set(address, 'payloadlengthextrabytes', str( payloadLengthExtraBytes)) - BMConfigParser().set( - address, 'privsigningkey', privSigningKeyWIF) - BMConfigParser().set( - address, 'privencryptionkey', privEncryptionKeyWIF) BMConfigParser().save() + shared.keystore.push_keys( + address, (privEncryptionKeyWIF, privSigningKeyWIF)) + # The API and the join and create Chan functionality # both need information back from the address generator. queues.apiAddressGeneratorReturnQueue.put(address) diff --git a/src/class_singleWorker.py b/src/class_singleWorker.py index 0798296e..e59cc6be 100644 --- a/src/class_singleWorker.py +++ b/src/class_singleWorker.py @@ -183,16 +183,11 @@ class singleWorker(StoppableThread): logger.info("Quitting...") def _getKeysForAddress(self, address): - privSigningKeyBase58 = BMConfigParser().get( + privSigningKeyHex = shared.keystore.fetch_key( address, 'privsigningkey') - privEncryptionKeyBase58 = BMConfigParser().get( + privEncryptionKeyHex = shared.keystore.fetch_key( address, 'privencryptionkey') - privSigningKeyHex = hexlify(shared.decodeWalletImportFormat( - privSigningKeyBase58)) - privEncryptionKeyHex = hexlify(shared.decodeWalletImportFormat( - privEncryptionKeyBase58)) - # The \x04 on the beginning of the public keys are not sent. # This way there is only one acceptable way to encode # and send a public key. diff --git a/src/shared.py b/src/shared.py index 6d03bcca..821b3555 100644 --- a/src/shared.py +++ b/src/shared.py @@ -7,8 +7,8 @@ import stat import threading import hashlib import subprocess -from binascii import hexlify -from pyelliptic import arithmetic +from binascii import hexlify, unhexlify +from pyelliptic import arithmetic, Cipher # Project imports. import state @@ -18,6 +18,10 @@ from debug import logger from addresses import decodeAddress, encodeVarint from helper_sql import sqlQuery +try: + from plugins.plugin import get_plugin +except ImportError: + get_plugin = False verbose = 1 # This is obsolete with the change to protocol v3 @@ -114,6 +118,83 @@ def decodeWalletImportFormat(WIFstring): os._exit(0) +class Keystore(object): + """Class implementing common key storage workflow""" + def __init__(self): + self.config = BMConfigParser() + keysencrypted = self.config.safeGetBoolean( + 'bitmessagesettings', 'keysencrypted') + + def noop(key): + return key + + self.fetch = self._get_key + self.push = self._set_keys + self.encrypt = self.decrypt = noop + + try: + content, plugin = self.config.safeGet( + 'bitmessagesettings', 'keystore').split(':') + plugin = get_plugin('keystore', name=plugin)(self) + except (ValueError, TypeError): + plugin = None + + if not plugin: + if keysencrypted: + logger.warning( + 'Key encryption plugin not found or unimplemented!') + return + + try: + if content == 'password' and keysencrypted: + self.decrypt = plugin.decrypt + self.encrypt = plugin.encrypt + elif content == 'keys': + self.fetch = plugin.fetch + self.push = plugin.push + except AttributeError: + pass + + def fetch_key(self, address, key_type='privencryptionkey'): + """Fetch address key of type key_type from keystore""" + try: + return hexlify(decodeWalletImportFormat( + self.decrypt(self.fetch(address, key_type)) + )) + except TypeError: + pass # handle in reloadMyAddressHashes etc + + def push_keys(self, address, keys): + """Push the address keys in WIF into keystore""" + self.push(address, [self.encrypt(key) for key in keys]) + + def _get_key(self, address, key_type='privencryptionkey'): + return self.config.get(address, key_type) + + def _set_keys(self, address, keys): + for key, key_type in zip( + keys, ('privencryptionkey', 'privsigningkey')): + self.config.set(address, key_type, key) + self.config.save() + + # simmetric encryption from pyelliptic example: + # https://github.com/yann2192/pyelliptic + def _encrypt_AES_CFB(self, data, password): + nonce = Cipher.gen_IV('aes-256-cfb') + ctx = Cipher(password, nonce, 1, ciphername='aes-256-cfb') + encrypted = ctx.update(data) + encrypted += ctx.final() + return ':'.join(hexlify(i) for i in (encrypted, nonce)) + + def _decrypt_AES_CFB(self, data, password): + encrypted, nonce = [unhexlify(part) for part in data.split(':')] + ctx = Cipher(password, nonce, 0, ciphername='aes-256-cfb') + return ctx.ciphering(encrypted) + + +keystore = Keystore() + + def reloadMyAddressHashes(): logger.debug('reloading keys from keys.dat file') myECCryptorObjects.clear() @@ -133,9 +214,7 @@ def reloadMyAddressHashes(): if addressVersionNumber in (2, 3, 4): # Returns a simple 32 bytes of information encoded # in 64 Hex characters, or null if there was an error. - privEncryptionKey = hexlify(decodeWalletImportFormat( - BMConfigParser().get(addressInKeysFile, 'privencryptionkey')) - ) + privEncryptionKey = keystore.fetch_key(addressInKeysFile) # It is 32 bytes encoded as 64 hex characters if len(privEncryptionKey) == 64: