From b8d70f34d349a3b4c05bd3d43754826bc9718e7f Mon Sep 17 00:00:00 2001 From: diminator Date: Tue, 21 Mar 2017 15:26:57 +0100 Subject: [PATCH] keypair from secret --- bigchaindb/common/crypto.py | 48 ++++++++++++++++++++++++++++++++++--- tests/common/test_crypto.py | 13 ++++++++++ 2 files changed, 58 insertions(+), 3 deletions(-) create mode 100644 tests/common/test_crypto.py diff --git a/bigchaindb/common/crypto.py b/bigchaindb/common/crypto.py index 99663fe9..9bced92a 100644 --- a/bigchaindb/common/crypto.py +++ b/bigchaindb/common/crypto.py @@ -1,10 +1,10 @@ # Separate all crypto code so that we can easily test several implementations from collections import namedtuple +import nacl.signing import sha3 from cryptoconditions import crypto - CryptoKeypair = namedtuple('CryptoKeypair', ('private_key', 'public_key')) @@ -13,8 +13,46 @@ def hash_data(data): return sha3.sha3_256(data.encode()).hexdigest() -def generate_key_pair(): +class Ed25519SigningKeyFromHash(crypto.Ed25519SigningKey): + + def __init__(self, key, encoding='base58'): + super().__init__(key, encoding=encoding) + + @classmethod + def generate(cls, hash_bytes): + return cls(nacl.signing.SigningKey(hash_bytes).encode(encoder=crypto.Base58Encoder)) + + +def ed25519_generate_key_pair_from_secret(secret): + """ + Generate a new key pair. + + Args: + secret (:class:`string`): A secret that serves as a seed + + Returns: + A tuple of (private_key, public_key) encoded in base58. + """ + + # if you want to do this correctly, use a key derivation function! + if not isinstance(secret, bytes): + secret = secret.encode() + + hash_bytes = sha3.keccak_256(secret).digest() + sk = Ed25519SigningKeyFromHash.generate(hash_bytes=hash_bytes) + # Private key + private_value_base58 = sk.encode(encoding='base58') + + # Public key + public_value_compressed_base58 = sk.get_verifying_key().encode(encoding='base58') + + return private_value_base58, public_value_compressed_base58 + + +def generate_key_pair(secret=None): """Generates a cryptographic key pair. + Args: + secret (:class:`string`): A secret that serves as a seed Returns: :class:`~bigchaindb.common.crypto.CryptoKeypair`: A @@ -23,9 +61,13 @@ def generate_key_pair(): :attr:`~bigchaindb.common.crypto.CryptoKeypair.public_key`. """ + if secret: + keypair_raw = ed25519_generate_key_pair_from_secret(secret) + else: + keypair_raw = crypto.ed25519_generate_key_pair() # TODO FOR CC: Adjust interface so that this function becomes unnecessary return CryptoKeypair( - *(k.decode() for k in crypto.ed25519_generate_key_pair())) + *(k.decode() for k in keypair_raw)) PrivateKey = crypto.Ed25519SigningKey diff --git a/tests/common/test_crypto.py b/tests/common/test_crypto.py new file mode 100644 index 00000000..a594e3b8 --- /dev/null +++ b/tests/common/test_crypto.py @@ -0,0 +1,13 @@ + + +def test_crypto_keypair(): + from bigchaindb.common.crypto import generate_key_pair + + secret = 'much secret' + keypair = generate_key_pair(secret=secret) + + assert len(keypair.public_key) == len(generate_key_pair().public_key) + # TODO: 43 !== 44 -> need to check why + # assert len(keypair.private_key) == len(generate_key_pair().private_key) + assert keypair.public_key == generate_key_pair(secret).public_key + assert keypair.private_key == generate_key_pair(secret).private_key