mirror of
https://github.com/morgan9e/noiseprotocol
synced 2026-04-14 00:14:05 +09:00
Switching source of crypto, adding DH
requirements.py * Removing pycryptodome and ed25519 - the latter lacked ability to perform DH * Replacing aforementioned with cryptography package noise/functions.py * Switched hash-functions to ones from cryptography * Added ed25519 method for DH wrapper and added compatible constructors for KeyPair tests/test_vectors.py * Omit test vectors with ed448 * Parse hexstrings from JSONs properly into bytearrays.
This commit is contained in:
@@ -1,18 +1,26 @@
|
||||
from .crypto import ed448
|
||||
|
||||
from Crypto.Cipher import AES, ChaCha20
|
||||
from Crypto.Hash import BLAKE2b, BLAKE2s, SHA256, SHA512
|
||||
import ed25519
|
||||
from cryptography.hazmat.backends import default_backend
|
||||
from cryptography.hazmat.primitives import hashes
|
||||
from cryptography.hazmat.primitives.asymmetric import x25519
|
||||
|
||||
|
||||
class DH(object):
|
||||
def __init__(self, method):
|
||||
self.method = method
|
||||
self.dhlen = 0
|
||||
self.dh = None
|
||||
if method == 'ed25519':
|
||||
self.method = method
|
||||
self.dhlen = 32
|
||||
self.generate_keypair = self._25519_generate_keypair
|
||||
self.dh = self._25519_dh
|
||||
elif method == 'ed448':
|
||||
raise NotImplementedError
|
||||
|
||||
def generate_keypair(self) -> 'KeyPair':
|
||||
pass
|
||||
def _25519_generate_keypair(self) -> 'KeyPair':
|
||||
private_key = x25519.X25519PrivateKey.generate()
|
||||
return KeyPair(private_key, private_key.public_key())
|
||||
|
||||
def _25519_dh(self, keypair: 'x25519.X25519PrivateKey', public_key: 'x25519.X25519PublicKey') -> bytes:
|
||||
return keypair.exchange(public_key)
|
||||
|
||||
|
||||
class Cipher(object):
|
||||
@@ -46,28 +54,40 @@ class Hash(object):
|
||||
self.hash = self._hash_blake2b
|
||||
|
||||
def _hash_sha256(self, data):
|
||||
return SHA256.new(data).digest()
|
||||
digest = hashes.Hash(hashes.SHA256(), default_backend())
|
||||
digest.update(data)
|
||||
return digest.finalize()
|
||||
|
||||
def _hash_sha512(self, data):
|
||||
return SHA512.new(data).digest()
|
||||
digest = hashes.Hash(hashes.SHA512(), default_backend())
|
||||
digest.update(data)
|
||||
return digest.finalize()
|
||||
|
||||
def _hash_blake2s(self, data):
|
||||
return BLAKE2s.new(data=data, digest_bytes=self.hashlen).digest()
|
||||
digest = hashes.Hash(hashes.BLAKE2s(digest_size=self.hashlen), default_backend())
|
||||
digest.update(data)
|
||||
return digest.finalize()
|
||||
|
||||
def _hash_blake2b(self, data):
|
||||
return BLAKE2b.new(data=data, digest_bytes=self.hashlen).digest()
|
||||
digest = hashes.Hash(hashes.BLAKE2b(digest_size=self.hashlen), default_backend())
|
||||
digest.update(data)
|
||||
return digest.finalize()
|
||||
|
||||
|
||||
class KeyPair(object):
|
||||
def __init__(self, public=b'', private=b''):
|
||||
# TODO: Maybe switch to properties?
|
||||
self.public = public
|
||||
def __init__(self, private=None, public=None):
|
||||
self.private = private
|
||||
if private and not public:
|
||||
self.derive_public_key()
|
||||
self.public = public
|
||||
|
||||
def derive_public_key(self):
|
||||
pass
|
||||
@classmethod
|
||||
def _25519_from_private_bytes(cls, private_bytes):
|
||||
private = x25519.X25519PrivateKey._from_private_bytes(private_bytes)
|
||||
public = private.public_key().public_bytes()
|
||||
return cls(private=private, public=public)
|
||||
|
||||
@classmethod
|
||||
def _25519_from_public_bytes(cls, public_bytes):
|
||||
return cls(public=x25519.X25519PublicKey.from_public_bytes(public_bytes).public_bytes())
|
||||
|
||||
|
||||
# Available crypto functions
|
||||
@@ -75,7 +95,7 @@ class KeyPair(object):
|
||||
# If not - switch to partials(?)
|
||||
dh_map = {
|
||||
'25519': DH('ed25519'),
|
||||
'448': DH('ed448')
|
||||
# '448': DH('ed448') # TODO uncomment when ed448 is implemented
|
||||
}
|
||||
|
||||
cipher_map = {
|
||||
|
||||
@@ -1,3 +1,2 @@
|
||||
pytest
|
||||
pycryptodome
|
||||
ed25519
|
||||
cryptography
|
||||
|
||||
@@ -14,8 +14,9 @@ vector_files = ['vectors/cacophony.txt']
|
||||
|
||||
# As in test vectors specification (https://github.com/noiseprotocol/noise_wiki/wiki/Test-vectors)
|
||||
# We use this to cast read strings into bytes
|
||||
string_fields = ['protocol_name', 'init_prologue', 'init_static', 'init_ephemeral', 'init_remote_static',
|
||||
'resp_prologue', 'resp_static', 'resp_ephemeral', 'resp_remote_static', 'handshake_hash']
|
||||
byte_fields = ['protocol_name']
|
||||
hexbyte_fields = ['init_prologue', 'init_static', 'init_ephemeral', 'init_remote_static', 'resp_static',
|
||||
'resp_prologue', 'resp_ephemeral', 'resp_remote_static', 'handshake_hash']
|
||||
list_fields = ['init_psks', 'resp_psks']
|
||||
dict_field = 'messages'
|
||||
|
||||
@@ -28,9 +29,13 @@ def _prepare_test_vectors():
|
||||
vectors_list = json.load(fd)
|
||||
|
||||
for vector in vectors_list:
|
||||
if '_448_' in vector['protocol_name']:
|
||||
continue # TODO REMOVE WHEN ed448 IS IMPLEMENTED
|
||||
for key, value in vector.copy().items():
|
||||
if key in string_fields:
|
||||
if key in byte_fields:
|
||||
vector[key] = value.encode()
|
||||
if key in hexbyte_fields:
|
||||
vector[key] = bytes.fromhex(value)
|
||||
if key in list_fields:
|
||||
vector[key] = [k.encode() for k in value]
|
||||
if key == dict_field:
|
||||
@@ -54,9 +59,9 @@ class TestVectors(object):
|
||||
role_key = role + '_' + key
|
||||
if role_key in vector:
|
||||
if key in ['static', 'ephemeral']:
|
||||
kwargs[role][kwarg] = KeyPair(private=vector[role_key])
|
||||
else:
|
||||
kwargs[role][kwarg] = KeyPair(public=vector[role_key])
|
||||
kwargs[role][kwarg] = KeyPair._25519_from_private_bytes(vector[role_key]) # TODO unify after adding 448
|
||||
elif key == 'remote_static':
|
||||
kwargs[role][kwarg] = KeyPair._25519_from_public_bytes(vector[role_key]) # TODO unify after adding 448
|
||||
return kwargs
|
||||
|
||||
def test_vector(self, vector):
|
||||
|
||||
Reference in New Issue
Block a user