From c11909012a32a0d3dbd5ca1685393d8cecca9410 Mon Sep 17 00:00:00 2001 From: Luke Murphy Date: Sat, 5 Oct 2019 14:11:14 +0200 Subject: [PATCH] Rework to use pysodium, document and test further Still on key_pair, sign and verify --- hypercore_crypto/__init__.py | 7 ------- hypercore_crypto/crypto.py | 39 +++++++++++++++++++++--------------- setup.cfg | 2 +- test/test_crypto.py | 28 +++++++++++++++++++++++--- 4 files changed, 49 insertions(+), 27 deletions(-) diff --git a/hypercore_crypto/__init__.py b/hypercore_crypto/__init__.py index 89b29d3..d0ad693 100644 --- a/hypercore_crypto/__init__.py +++ b/hypercore_crypto/__init__.py @@ -1,15 +1,8 @@ """hypercore-crypto module.""" -# Note(decentral1se): Included to match export list of the original Javascript -# reference implementation. However, we don't implement this function -# ourselves. -from nacl.hash import blake2b # noqa - from hypercore_crypto.crypto import ( # noqa - blake2b, data, discovery_key, - encode_unsigned_int64, key_pair, leaf, parent, diff --git a/hypercore_crypto/crypto.py b/hypercore_crypto/crypto.py index 3ab3e01..d493ed8 100644 --- a/hypercore_crypto/crypto.py +++ b/hypercore_crypto/crypto.py @@ -2,14 +2,13 @@ from typing import Optional, Tuple -import nacl.exceptions -from nacl.bindings import ( - crypto_sign, +from pysodium import ( + crypto_sign_detached, crypto_sign_keypair, crypto_sign_seed_keypair, + crypto_sign_SEEDBYTES, + crypto_sign_verify_detached, ) -from nacl.hash import blake2b -from nacl.signing import VerifyKey # https://en.wikipedia.org/wiki/Merkle_tree#Second_preimage_attack LEAF_TYPE = bytearray([0]) @@ -21,23 +20,35 @@ HYPERCORE = bytearray('hypercore', encoding='utf-8') def key_pair(seed: Optional[bytes] = None) -> Tuple[bytes, bytes]: """A new public key and secret key pair. - The seed must be at least 32 characters in length. + :param seed: Seed value. Must be at least 32 characters in length """ if seed: + if len(seed) < crypto_sign_SEEDBYTES: + message = "'seed' argument must be of length > {}" + raise ValueError(message.format(crypto_sign_SEEDBYTES)) return crypto_sign_seed_keypair(seed) return crypto_sign_keypair() def sign(message: bytes, secret_key: bytes) -> bytes: - """Signed message from a secret key.""" - return crypto_sign(message, secret_key) + """A message signature. + + :param message: The message to be signed + :param secret_key: The secret key to use during signing + """ + return crypto_sign_detached(message, secret_key) -def verify(signed_message: bytes, signature: bytes, public_key: bytes) -> bool: - """Verify a signed message.""" +def verify(message: bytes, signature: bytes, public_key: bytes) -> bool: + """Verify an unsigned message with accompanying signature. + + :param message: The unsigned message to verify + :param signature: The signature to be verified + :param public_key: The public key to use during verifying + """ try: - VerifyKey(public_key).verify(signed_message, signature=signature) - except (nacl.exceptions.TypeError, nacl.exceptions.BadSignatureError): + crypto_sign_verify_detached(signature, message, public_key) + except ValueError: return False return True @@ -64,7 +75,3 @@ def random_bytes(): def discovery_key(): pass - - -def encode_unsigned_int64(): - pass diff --git a/setup.cfg b/setup.cfg index 55c9a98..2a92700 100644 --- a/setup.cfg +++ b/setup.cfg @@ -43,7 +43,7 @@ package_dir = packages = find: zip_safe = False install_requires = - PyNaCl >= 1.3.0, < 2.0 + pysodium >= 0.7.2, < 0.8 [options.packages.find] where = . diff --git a/test/test_crypto.py b/test/test_crypto.py index 18bd364..dff0e24 100644 --- a/test/test_crypto.py +++ b/test/test_crypto.py @@ -1,9 +1,31 @@ """Cryptography primitives test module.""" -from hypercore_crypto import key_pair +import pytest + +from hypercore_crypto import key_pair, sign, verify +from pysodium import crypto_sign_PUBLICKEYBYTES, crypto_sign_SECRETKEYBYTES + + +def test_key_pair_seed_length(): + with pytest.raises(ValueError) as exception: + key_pair(b'wrong') + assert 'must be of length' in str(exception) def test_key_pair_length(): public_key, secret_key = key_pair() - assert len(public_key) == 32 - assert len(secret_key) == 64 + assert len(public_key) == crypto_sign_PUBLICKEYBYTES + assert len(secret_key) == crypto_sign_SECRETKEYBYTES + + +def test_sign(): + message = b'mymessage' + _, secret_key = key_pair() + assert message not in sign(message, secret_key) + + +def test_verify(): + message = b'mymessage' + public_key, secret_key = key_pair() + signature = sign(message, secret_key) + assert verify(message, signature, public_key)