Finalise the rest of the module
This commit is contained in:
parent
c11909012a
commit
a250b8391c
@ -1,13 +1,18 @@
|
|||||||
"""Cryptography primitives for Hypercore."""
|
"""Cryptography primitives for Hypercore."""
|
||||||
|
|
||||||
from typing import Optional, Tuple
|
from hashlib import blake2b
|
||||||
|
from typing import List, Optional, Tuple
|
||||||
|
|
||||||
|
from merkle_tree_stream import MerkleTreeNode
|
||||||
from pysodium import (
|
from pysodium import (
|
||||||
|
crypto_generichash,
|
||||||
|
crypto_generichash_BYTES,
|
||||||
crypto_sign_detached,
|
crypto_sign_detached,
|
||||||
crypto_sign_keypair,
|
crypto_sign_keypair,
|
||||||
crypto_sign_seed_keypair,
|
crypto_sign_seed_keypair,
|
||||||
crypto_sign_SEEDBYTES,
|
crypto_sign_SEEDBYTES,
|
||||||
crypto_sign_verify_detached,
|
crypto_sign_verify_detached,
|
||||||
|
randombytes,
|
||||||
)
|
)
|
||||||
|
|
||||||
# https://en.wikipedia.org/wiki/Merkle_tree#Second_preimage_attack
|
# https://en.wikipedia.org/wiki/Merkle_tree#Second_preimage_attack
|
||||||
@ -27,6 +32,7 @@ def key_pair(seed: Optional[bytes] = None) -> Tuple[bytes, bytes]:
|
|||||||
message = "'seed' argument must be of length > {}"
|
message = "'seed' argument must be of length > {}"
|
||||||
raise ValueError(message.format(crypto_sign_SEEDBYTES))
|
raise ValueError(message.format(crypto_sign_SEEDBYTES))
|
||||||
return crypto_sign_seed_keypair(seed)
|
return crypto_sign_seed_keypair(seed)
|
||||||
|
|
||||||
return crypto_sign_keypair()
|
return crypto_sign_keypair()
|
||||||
|
|
||||||
|
|
||||||
@ -50,28 +56,90 @@ def verify(message: bytes, signature: bytes, public_key: bytes) -> bool:
|
|||||||
crypto_sign_verify_detached(signature, message, public_key)
|
crypto_sign_verify_detached(signature, message, public_key)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
def data(data: bytes):
|
def data(data: bytes) -> bytes:
|
||||||
pass
|
"""The hashed digest of data input.
|
||||||
|
|
||||||
|
:param data: The data to be hashed
|
||||||
|
"""
|
||||||
|
return _blake2bify([LEAF_TYPE, _to_unsigned_64_int(len(data)), data])
|
||||||
|
|
||||||
|
|
||||||
def leaf():
|
def leaf(leaf: MerkleTreeNode) -> str:
|
||||||
pass
|
"""The hashed digest of the leaf.
|
||||||
|
|
||||||
|
:param leaf: The leaf data to be hashed
|
||||||
|
"""
|
||||||
|
return data(leaf.data)
|
||||||
|
|
||||||
|
|
||||||
def parent():
|
def parent(child: MerkleTreeNode, parent: MerkleTreeNode) -> str:
|
||||||
pass
|
if child.index > parent.index:
|
||||||
|
raise ValueError('Child index is greater than parent?')
|
||||||
|
|
||||||
|
values = [
|
||||||
|
PARENT_TYPE,
|
||||||
|
_to_unsigned_64_int(child.size + parent.size),
|
||||||
|
child.hash,
|
||||||
|
parent.hash,
|
||||||
|
]
|
||||||
|
|
||||||
|
return _blake2bify(values)
|
||||||
|
|
||||||
|
|
||||||
def tree():
|
def tree(roots: List[MerkleTreeNode]) -> bytes:
|
||||||
pass
|
"""Hashed tree roots.
|
||||||
|
|
||||||
|
:param roots: A list of root nodes
|
||||||
|
"""
|
||||||
|
to_be_hashed = []
|
||||||
|
to_be_hashed.append(ROOT_TYPE)
|
||||||
|
|
||||||
|
for root in roots:
|
||||||
|
to_be_hashed.append(root.hash)
|
||||||
|
to_be_hashed.append(root.index)
|
||||||
|
to_be_hashed.append(root.size)
|
||||||
|
|
||||||
|
return _blake2bify(to_be_hashed)
|
||||||
|
|
||||||
|
|
||||||
def random_bytes():
|
def random_bytes(size: int) -> bytes:
|
||||||
pass
|
"""Random bytes with specified length.
|
||||||
|
|
||||||
|
:param size The length of the random bytes
|
||||||
|
"""
|
||||||
|
return randombytes(size)
|
||||||
|
|
||||||
|
|
||||||
def discovery_key():
|
def discovery_key(public_key: bytes) -> bytes:
|
||||||
pass
|
"""The discovery key for a tree.
|
||||||
|
|
||||||
|
:param public_key: The public key for hashing
|
||||||
|
"""
|
||||||
|
return crypto_generichash(HYPERCORE, key=public_key)
|
||||||
|
|
||||||
|
|
||||||
|
def _to_unsigned_64_int(num: int) -> bytes:
|
||||||
|
"""Convert an integer to unsigned 64 bit bytes.
|
||||||
|
|
||||||
|
See https://stackoverflow.com/a/45434265.
|
||||||
|
|
||||||
|
:param num: The integer to be converted
|
||||||
|
"""
|
||||||
|
return int(num).to_bytes(8, byteorder='big', signed=False)
|
||||||
|
|
||||||
|
|
||||||
|
def _blake2bify(data: List[bytes]) -> bytes:
|
||||||
|
"""Hashed bytes from the Blake2b hash function.
|
||||||
|
|
||||||
|
:param data: A list of byte values to be hashed
|
||||||
|
"""
|
||||||
|
hash_func = blake2b(digest_size=crypto_generichash_BYTES)
|
||||||
|
|
||||||
|
for _data in data:
|
||||||
|
hash_func.update(_data)
|
||||||
|
|
||||||
|
return hash_func.digest()
|
||||||
|
@ -44,6 +44,7 @@ packages = find:
|
|||||||
zip_safe = False
|
zip_safe = False
|
||||||
install_requires =
|
install_requires =
|
||||||
pysodium >= 0.7.2, < 0.8
|
pysodium >= 0.7.2, < 0.8
|
||||||
|
merkle-tree-stream >= 0.0.1a1, < 1.0.0
|
||||||
|
|
||||||
[options.packages.find]
|
[options.packages.find]
|
||||||
where = .
|
where = .
|
||||||
|
@ -1,14 +1,15 @@
|
|||||||
"""Cryptography primitives test module."""
|
"""Cryptography primitives test module."""
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
from merkle_tree_stream import MerkleTreeNode
|
||||||
from hypercore_crypto import key_pair, sign, verify
|
|
||||||
from pysodium import crypto_sign_PUBLICKEYBYTES, crypto_sign_SECRETKEYBYTES
|
from pysodium import crypto_sign_PUBLICKEYBYTES, crypto_sign_SECRETKEYBYTES
|
||||||
|
|
||||||
|
from hypercore_crypto import data, key_pair, parent, random_bytes, sign, verify
|
||||||
|
|
||||||
|
|
||||||
def test_key_pair_seed_length():
|
def test_key_pair_seed_length():
|
||||||
with pytest.raises(ValueError) as exception:
|
with pytest.raises(ValueError) as exception:
|
||||||
key_pair(b'wrong')
|
key_pair(b'world hello')
|
||||||
assert 'must be of length' in str(exception)
|
assert 'must be of length' in str(exception)
|
||||||
|
|
||||||
|
|
||||||
@ -19,13 +20,42 @@ def test_key_pair_length():
|
|||||||
|
|
||||||
|
|
||||||
def test_sign():
|
def test_sign():
|
||||||
message = b'mymessage'
|
message = b'hello world'
|
||||||
_, secret_key = key_pair()
|
_, secret_key = key_pair()
|
||||||
assert message not in sign(message, secret_key)
|
signature = sign(message, secret_key)
|
||||||
|
assert message not in signature
|
||||||
|
assert len(signature) == 64
|
||||||
|
|
||||||
|
|
||||||
def test_verify():
|
def test_verify():
|
||||||
message = b'mymessage'
|
message = b'hello world'
|
||||||
public_key, secret_key = key_pair()
|
public_key, secret_key = key_pair()
|
||||||
signature = sign(message, secret_key)
|
signature = sign(message, secret_key)
|
||||||
assert verify(message, signature, public_key)
|
assert verify(message, signature, public_key)
|
||||||
|
|
||||||
|
|
||||||
|
def test_data_digest():
|
||||||
|
assert (
|
||||||
|
data(b'hello world').hex()
|
||||||
|
== 'ccfa4259ee7c41e411e5770973a49c5ceffb5272d6a37f2c6f2dac2190f7e2b7'
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_random_bytes():
|
||||||
|
assert len(random_bytes(32)) == 32
|
||||||
|
|
||||||
|
|
||||||
|
def test_parent_digest():
|
||||||
|
_data = b'hello world'
|
||||||
|
_parent = parent(
|
||||||
|
MerkleTreeNode(
|
||||||
|
index=0, size=11, hash=data(_data), parent=None, data=None
|
||||||
|
),
|
||||||
|
MerkleTreeNode(
|
||||||
|
index=2, size=11, hash=data(_data), parent=None, data=None
|
||||||
|
),
|
||||||
|
)
|
||||||
|
assert (
|
||||||
|
_parent.hex()
|
||||||
|
== '43563406adba8b34b133fdca32d0a458c5be769615e01df30e6535ccd3c075f0'
|
||||||
|
)
|
||||||
|
Loading…
Reference in New Issue
Block a user