added keymanager test

master
Kevin Froman 2019-07-27 15:29:15 -05:00
parent b24c683f5f
commit ef6bb8c1e9
6 changed files with 60 additions and 14 deletions

View File

@ -18,6 +18,7 @@
along with this program. If not, see <https://www.gnu.org/licenses/>. along with this program. If not, see <https://www.gnu.org/licenses/>.
''' '''
DENIABLE_PEER_ADDRESS = "OVPCZLOXD6DC5JHX4EQ3PSOGAZ3T24F75HQLIUZSDSMYPEOXCPFA====" DENIABLE_PEER_ADDRESS = "OVPCZLOXD6DC5JHX4EQ3PSOGAZ3T24F75HQLIUZSDSMYPEOXCPFA===="
PASSWORD_LENGTH = 25
class OnionrValues: class OnionrValues:
def __init__(self): def __init__(self):
self.passwordLength = 20 self.passwordLength = 20

View File

@ -25,6 +25,7 @@ class KeyManager:
self.keyFile = filepaths.keys_file self.keyFile = filepaths.keys_file
def addKey(self, pubKey=None, privKey=None): 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): if type(pubKey) is type(None) and type(privKey) is type(None):
pubKey, privKey = generate.generate_pub_key() pubKey, privKey = generate.generate_pub_key()
pubKey = bytesconverter.bytes_to_str(pubKey) pubKey = bytesconverter.bytes_to_str(pubKey)
@ -54,8 +55,11 @@ class KeyManager:
def getPubkeyList(self): def getPubkeyList(self):
'''Return a list of the user's keys''' '''Return a list of the user's keys'''
keyList = [] keyList = []
try:
with open(self.keyFile, "r") as keyFile: with open(self.keyFile, "r") as keyFile:
keyData = keyFile.read() keyData = keyFile.read()
except FileNotFoundError:
keyData = ''
keyData = keyData.split('\n') keyData = keyData.split('\n')
for pair in keyData: for pair in keyData:
if len(pair) > 0: keyList.append(pair.split(',')[0]) if len(pair) > 0: keyList.append(pair.split(',')[0])

View File

@ -77,10 +77,6 @@ class Onionr:
# Load global configuration data # Load global configuration data
data_exists = Onionr.setupConfig(self) 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 # Copy default plugins into plugins folder
if not os.path.exists(plugins.get_plugins_folder()): if not os.path.exists(plugins.get_plugins_folder()):
if os.path.exists('static-data/default-plugins/'): if os.path.exists('static-data/default-plugins/'):

View File

@ -23,31 +23,35 @@ import logger, onionrexceptions
from onionrutils import stringvalidators, bytesconverter from onionrutils import stringvalidators, bytesconverter
from onionrusers import onionrusers, contactmanager from onionrusers import onionrusers, contactmanager
from coredb import keydb from coredb import keydb
import keymanager, onionrcrypto
import unpaddedbase32 import unpaddedbase32
from etc import onionrvalues
DETERMINISTIC_REQUIREMENT = onionrvalues.PASSWORD_LENGTH
def add_ID(o_inst): def add_ID(o_inst):
key_manager = keymanager.KeyManager()
try: try:
sys.argv[2] sys.argv[2]
assert sys.argv[2] == 'true' assert sys.argv[2] == 'true'
except (IndexError, AssertionError) as e: except (IndexError, AssertionError) as e:
newID = o_inst.crypto.keyManager.addKey()[0] newID = key_manager.addKey()[0]
else: else:
logger.warn('Deterministic keys require random and long passphrases.', terminal=True) 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('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) 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: ') pass2 = getpass.getpass(prompt='Confirm entry: ')
if o_inst.crypto.safeCompare(pass1, pass2): if onionrcrypto.cryptoutils.safe_compare(pass1, pass2):
try: try:
logger.info('Generating deterministic key. This can take a while.', terminal=True) 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: 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) sys.exit(1)
else: else:
logger.error('Passwords do not match.', terminal=True) logger.error('Passwords do not match.', terminal=True)
sys.exit(1) sys.exit(1)
try: try:
o_inst.crypto.keyManager.addKey(pubKey=newID, key_manager.addKey(pubKey=newID,
privKey=privKey) privKey=privKey)
except ValueError: except ValueError:
logger.error('That ID is already available, you can change to it with the change-id command.', terminal=True) 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) logger.info('Added ID: %s' % (bytesconverter.bytes_to_str(newID),), terminal=True)
def change_ID(o_inst): def change_ID(o_inst):
key_manager = keymanager.KeyManager()
try: try:
key = sys.argv[2] key = sys.argv[2]
key = unpaddedbase32.repad(key.encode()).decode() key = unpaddedbase32.repad(key.encode()).decode()
@ -62,7 +67,7 @@ def change_ID(o_inst):
logger.warn('Specify pubkey to use', terminal=True) logger.warn('Specify pubkey to use', terminal=True)
else: else:
if stringvalidators.validate_pub_key(key): 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.set('general.public_key', key)
o_inst.config.save() o_inst.config.save()
logger.info('Set active key to: %s' % (key,), terminal=True) logger.info('Set active key to: %s' % (key,), terminal=True)

View File

@ -1,6 +1,7 @@
import nacl.signing, nacl.encoding, nacl.pwhash import nacl.signing, nacl.encoding, nacl.pwhash
import onionrexceptions import onionrexceptions
from onionrutils import bytesconverter from onionrutils import bytesconverter
from etc import onionrvalues
def generate_pub_key(): def generate_pub_key():
'''Generate a Ed25519 public key pair, return tuple of base32encoded pubkey, privkey''' '''Generate a Ed25519 public key pair, return tuple of base32encoded pubkey, privkey'''
private_key = nacl.signing.SigningKey.generate() private_key = nacl.signing.SigningKey.generate()
@ -9,7 +10,7 @@ def generate_pub_key():
def generate_deterministic(passphrase, bypassCheck=False): def generate_deterministic(passphrase, bypassCheck=False):
'''Generate a Ed25519 public key pair from a password''' '''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 passphrase = bytesconverter.str_to_bytes(passphrase) # Convert to bytes if not already
# Validate passphrase length # Validate passphrase length
if not bypassCheck: if not bypassCheck:

View File

@ -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()