From ef6bb8c1e9b4b8da61fab02c2660fb5ee3d92c94 Mon Sep 17 00:00:00 2001 From: Kevin Froman Date: Sat, 27 Jul 2019 15:29:15 -0500 Subject: [PATCH] added keymanager test --- onionr/etc/onionrvalues.py | 1 + onionr/keymanager.py | 8 ++++-- onionr/onionr.py | 4 --- onionr/onionrcommands/pubkeymanager.py | 19 ++++++++----- onionr/onionrcrypto/generate.py | 3 +- onionr/tests/test_keymanager.py | 39 ++++++++++++++++++++++++++ 6 files changed, 60 insertions(+), 14 deletions(-) create mode 100644 onionr/tests/test_keymanager.py diff --git a/onionr/etc/onionrvalues.py b/onionr/etc/onionrvalues.py index 5df394db..836c6133 100755 --- a/onionr/etc/onionrvalues.py +++ b/onionr/etc/onionrvalues.py @@ -18,6 +18,7 @@ along with this program. If not, see . ''' DENIABLE_PEER_ADDRESS = "OVPCZLOXD6DC5JHX4EQ3PSOGAZ3T24F75HQLIUZSDSMYPEOXCPFA====" +PASSWORD_LENGTH = 25 class OnionrValues: def __init__(self): self.passwordLength = 20 diff --git a/onionr/keymanager.py b/onionr/keymanager.py index 4c6e628f..080795ec 100755 --- a/onionr/keymanager.py +++ b/onionr/keymanager.py @@ -25,6 +25,7 @@ class KeyManager: self.keyFile = filepaths.keys_file def addKey(self, pubKey=None, privKey=None): + '''Add a new key pair, either specified or none to generate a new pair automatically''' if type(pubKey) is type(None) and type(privKey) is type(None): pubKey, privKey = generate.generate_pub_key() pubKey = bytesconverter.bytes_to_str(pubKey) @@ -54,8 +55,11 @@ class KeyManager: def getPubkeyList(self): '''Return a list of the user's keys''' keyList = [] - with open(self.keyFile, "r") as keyFile: - keyData = keyFile.read() + try: + with open(self.keyFile, "r") as keyFile: + keyData = keyFile.read() + except FileNotFoundError: + keyData = '' keyData = keyData.split('\n') for pair in keyData: if len(pair) > 0: keyList.append(pair.split(',')[0]) diff --git a/onionr/onionr.py b/onionr/onionr.py index 8d5b13cb..d6c8fbc2 100755 --- a/onionr/onionr.py +++ b/onionr/onionr.py @@ -77,10 +77,6 @@ class Onionr: # Load global configuration data data_exists = Onionr.setupConfig(self) - # If block data folder does not exist - if not os.path.exists(self.dataDir + 'blocks/'): - os.mkdir(self.dataDir + 'blocks/') - # Copy default plugins into plugins folder if not os.path.exists(plugins.get_plugins_folder()): if os.path.exists('static-data/default-plugins/'): diff --git a/onionr/onionrcommands/pubkeymanager.py b/onionr/onionrcommands/pubkeymanager.py index e315c40d..325ad158 100755 --- a/onionr/onionrcommands/pubkeymanager.py +++ b/onionr/onionrcommands/pubkeymanager.py @@ -23,31 +23,35 @@ import logger, onionrexceptions from onionrutils import stringvalidators, bytesconverter from onionrusers import onionrusers, contactmanager from coredb import keydb +import keymanager, onionrcrypto import unpaddedbase32 +from etc import onionrvalues +DETERMINISTIC_REQUIREMENT = onionrvalues.PASSWORD_LENGTH def add_ID(o_inst): + key_manager = keymanager.KeyManager() try: sys.argv[2] assert sys.argv[2] == 'true' except (IndexError, AssertionError) as e: - newID = o_inst.crypto.keyManager.addKey()[0] + newID = key_manager.addKey()[0] else: logger.warn('Deterministic keys require random and long passphrases.', terminal=True) logger.warn('If a good passphrase is not used, your key can be easily stolen.', terminal=True) logger.warn('You should use a series of hard to guess words, see this for reference: https://www.xkcd.com/936/', terminal=True) - pass1 = getpass.getpass(prompt='Enter at least %s characters: ' % (o_inst.crypto.deterministicRequirement,)) + pass1 = getpass.getpass(prompt='Enter at least %s characters: ' % (DETERMINISTIC_REQUIREMENT,)) pass2 = getpass.getpass(prompt='Confirm entry: ') - if o_inst.crypto.safeCompare(pass1, pass2): + if onionrcrypto.cryptoutils.safe_compare(pass1, pass2): try: logger.info('Generating deterministic key. This can take a while.', terminal=True) - newID, privKey = o_inst.crypto.generateDeterministic(pass1) + newID, privKey = onionrcrypto.generate_deterministic(pass1) except onionrexceptions.PasswordStrengthError: - logger.error('Passphrase must use at least %s characters.' % (o_inst.crypto.deterministicRequirement,), terminal=True) + logger.error('Passphrase must use at least %s characters.' % (DETERMINISTIC_REQUIREMENT,), terminal=True) sys.exit(1) else: logger.error('Passwords do not match.', terminal=True) sys.exit(1) try: - o_inst.crypto.keyManager.addKey(pubKey=newID, + key_manager.addKey(pubKey=newID, privKey=privKey) except ValueError: logger.error('That ID is already available, you can change to it with the change-id command.', terminal=True) @@ -55,6 +59,7 @@ def add_ID(o_inst): logger.info('Added ID: %s' % (bytesconverter.bytes_to_str(newID),), terminal=True) def change_ID(o_inst): + key_manager = keymanager.KeyManager() try: key = sys.argv[2] key = unpaddedbase32.repad(key.encode()).decode() @@ -62,7 +67,7 @@ def change_ID(o_inst): logger.warn('Specify pubkey to use', terminal=True) else: if stringvalidators.validate_pub_key(key): - if key in o_inst.crypto.keyManager.getPubkeyList(): + if key in key_manager.getPubkeyList(): o_inst.config.set('general.public_key', key) o_inst.config.save() logger.info('Set active key to: %s' % (key,), terminal=True) diff --git a/onionr/onionrcrypto/generate.py b/onionr/onionrcrypto/generate.py index 37f40826..4523aab6 100644 --- a/onionr/onionrcrypto/generate.py +++ b/onionr/onionrcrypto/generate.py @@ -1,6 +1,7 @@ import nacl.signing, nacl.encoding, nacl.pwhash import onionrexceptions from onionrutils import bytesconverter +from etc import onionrvalues def generate_pub_key(): '''Generate a Ed25519 public key pair, return tuple of base32encoded pubkey, privkey''' private_key = nacl.signing.SigningKey.generate() @@ -9,7 +10,7 @@ def generate_pub_key(): def generate_deterministic(passphrase, bypassCheck=False): '''Generate a Ed25519 public key pair from a password''' - passStrength = 25 + passStrength = onionrvalues.PASSWORD_LENGTH passphrase = bytesconverter.str_to_bytes(passphrase) # Convert to bytes if not already # Validate passphrase length if not bypassCheck: diff --git a/onionr/tests/test_keymanager.py b/onionr/tests/test_keymanager.py new file mode 100644 index 00000000..0489fa98 --- /dev/null +++ b/onionr/tests/test_keymanager.py @@ -0,0 +1,39 @@ +#!/usr/bin/env python3 +import sys, os +sys.path.append(".") +import unittest, uuid + +TEST_DIR = 'testdata/%s-%s' % (uuid.uuid4(), os.path.basename(__file__)) + '/' +print("Test directory:", TEST_DIR) +os.environ["ONIONR_HOME"] = TEST_DIR +from utils import createdirs +from coredb import keydb +import setupconfig, keymanager, filepaths +from onionrutils import stringvalidators +createdirs.create_dirs() +setupconfig.setup_config() +pub_key = keymanager.KeyManager().getPubkeyList()[0] +class KeyManagerTest(unittest.TestCase): + def test_sane_default(self): + self.assertGreaterEqual(len(pub_key), 52) + self.assertLessEqual(len(pub_key), 56) + self.assertEqual(pub_key, keymanager.KeyManager().getPubkeyList()[0]) + stringvalidators.validate_pub_key(pub_key) + def test_change(self): + new_key = keymanager.KeyManager().addKey()[0] + self.assertNotEqual(new_key, pub_key) + self.assertEqual(new_key, keymanager.KeyManager().getPubkeyList()[1]) + stringvalidators.validate_pub_key(new_key) + def test_remove(self): + manager = keymanager.KeyManager() + new_key = manager.addKey()[0] + priv_key = manager.getPrivkey(new_key) + self.assertIn(new_key, manager.getPubkeyList()) + with open(filepaths.keys_file, 'r') as keyfile: + self.assertIn(new_key, keyfile.read()) + manager.removeKey(new_key) + with open(filepaths.keys_file, 'r') as keyfile: + self.assertNotIn(new_key, keyfile.read()) + + +unittest.main() \ No newline at end of file