Moved Reticulum to Curve25519 for ECDH exchanges and signatures

This commit is contained in:
Mark Qvist 2021-05-17 14:05:13 +02:00
parent 4109cbc33d
commit 8478782c18
2 changed files with 36 additions and 17 deletions

View File

@ -1,7 +1,8 @@
from cryptography.hazmat.backends import default_backend from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import hashes from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives import serialization from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.asymmetric import ec from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey, Ed25519PublicKey
from cryptography.hazmat.primitives.asymmetric.x25519 import X25519PrivateKey, X25519PublicKey
from cryptography.hazmat.primitives.kdf.hkdf import HKDF from cryptography.hazmat.primitives.kdf.hkdf import HKDF
from cryptography.fernet import Fernet from cryptography.fernet import Fernet
from time import sleep from time import sleep
@ -31,13 +32,15 @@ class Link:
:param owner: Internal use by :ref:`RNS.Transport<api-Transport>`, ignore this argument. :param owner: Internal use by :ref:`RNS.Transport<api-Transport>`, ignore this argument.
:param peer_pub_bytes: Internal use by :ref:`RNS.Transport<api-Transport>`, ignore this argument. :param peer_pub_bytes: Internal use by :ref:`RNS.Transport<api-Transport>`, ignore this argument.
""" """
CURVE = ec.SECP256R1() CURVE = "Curve25519"
""" """
The curve used for Elliptic Curve DH key exchanges The curve used for Elliptic Curve DH key exchanges
""" """
ECPUBSIZE = 91 ECPUBSIZE = 32
BLOCKSIZE = 16 BLOCKSIZE = 16
KEYSIZE = 32
AES_HMAC_OVERHEAD = 58 AES_HMAC_OVERHEAD = 58
MDU = math.floor((RNS.Reticulum.MDU-AES_HMAC_OVERHEAD)/BLOCKSIZE)*BLOCKSIZE - 1 MDU = math.floor((RNS.Reticulum.MDU-AES_HMAC_OVERHEAD)/BLOCKSIZE)*BLOCKSIZE - 1
@ -133,12 +136,22 @@ class Link:
self.initiator = False self.initiator = False
else: else:
self.initiator = True self.initiator = True
self.fernet = None
self.prv = ec.generate_private_key(Link.CURVE, default_backend()) self.prv = X25519PrivateKey.generate()
self.sig_prv = Ed25519PrivateKey.from_private_bytes(
self.prv.private_bytes(
encoding=serialization.Encoding.Raw,
format=serialization.PrivateFormat.Raw,
encryption_algorithm=serialization.NoEncryption()
)
)
self.pub = self.prv.public_key() self.pub = self.prv.public_key()
self.pub_bytes = self.pub.public_bytes( self.pub_bytes = self.pub.public_bytes(
encoding=serialization.Encoding.DER, encoding=serialization.Encoding.Raw,
format=serialization.PublicFormat.SubjectPublicKeyInfo format=serialization.PublicFormat.Raw
) )
if peer_pub_bytes == None: if peer_pub_bytes == None:
@ -162,7 +175,11 @@ class Link:
def load_peer(self, peer_pub_bytes): def load_peer(self, peer_pub_bytes):
self.peer_pub_bytes = peer_pub_bytes self.peer_pub_bytes = peer_pub_bytes
self.peer_pub = serialization.load_der_public_key(peer_pub_bytes, backend=default_backend()) self.peer_pub = X25519PublicKey.from_public_bytes(self.peer_pub_bytes)
self.peer_sig_pub_bytes = peer_pub_bytes
self.peer_sig_pub = Ed25519PublicKey.from_public_bytes(self.peer_sig_pub_bytes)
if not hasattr(self.peer_pub, "curve"): if not hasattr(self.peer_pub, "curve"):
self.peer_pub.curve = Link.CURVE self.peer_pub.curve = Link.CURVE
@ -172,13 +189,12 @@ class Link:
def handshake(self): def handshake(self):
self.status = Link.HANDSHAKE self.status = Link.HANDSHAKE
self.shared_key = self.prv.exchange(ec.ECDH(), self.peer_pub) self.shared_key = self.prv.exchange(self.peer_pub)
self.derived_key = HKDF( self.derived_key = HKDF(
algorithm=hashes.SHA256(), algorithm=hashes.SHA256(),
length=32, length=32,
salt=self.get_salt(), salt=self.get_salt(),
info=self.get_context(), info=self.get_context(),
backend=default_backend()
).derive(self.shared_key) ).derive(self.shared_key)
def prove(self): def prove(self):
@ -487,9 +503,10 @@ class Link:
if self.__encryption_disabled: if self.__encryption_disabled:
return plaintext return plaintext
try: try:
# TODO: Optimise this re-allocation if not self.fernet:
fernet = Fernet(base64.urlsafe_b64encode(self.derived_key)) self.fernet = Fernet(base64.urlsafe_b64encode(self.derived_key))
ciphertext = base64.urlsafe_b64decode(fernet.encrypt(plaintext))
ciphertext = base64.urlsafe_b64decode(self.fernet.encrypt(plaintext))
return ciphertext return ciphertext
except Exception as e: except Exception as e:
RNS.log("Encryption on link "+str(self)+" failed. The contained exception was: "+str(e), RNS.LOG_ERROR) RNS.log("Encryption on link "+str(self)+" failed. The contained exception was: "+str(e), RNS.LOG_ERROR)
@ -499,8 +516,10 @@ class Link:
if self.__encryption_disabled: if self.__encryption_disabled:
return ciphertext return ciphertext
try: try:
fernet = Fernet(base64.urlsafe_b64encode(self.derived_key)) if not self.fernet:
plaintext = fernet.decrypt(base64.urlsafe_b64encode(ciphertext)) self.fernet = Fernet(base64.urlsafe_b64encode(self.derived_key))
plaintext = self.fernet.decrypt(base64.urlsafe_b64encode(ciphertext))
return plaintext return plaintext
except Exception as e: except Exception as e:
RNS.log("Decryption failed on link "+str(self)+". The contained exception was: "+str(e), RNS.LOG_ERROR) RNS.log("Decryption failed on link "+str(self)+". The contained exception was: "+str(e), RNS.LOG_ERROR)
@ -510,11 +529,11 @@ class Link:
def sign(self, message): def sign(self, message):
return self.prv.sign(message, ec.ECDSA(hashes.SHA256())) return self.sig_prv.sign(message)
def validate(self, signature, message): def validate(self, signature, message):
try: try:
self.peer_pub.verify(signature, message, ec.ECDSA(hashes.SHA256())) self.peer_sig_pub.verify(signature, message)
return True return True
except Exception as e: except Exception as e:
return False return False

View File

@ -18,6 +18,6 @@ setuptools.setup(
"License :: OSI Approved :: MIT License", "License :: OSI Approved :: MIT License",
"Operating System :: OS Independent", "Operating System :: OS Independent",
], ],
install_requires=['cryptography', 'pyserial'], install_requires=['cryptography>=3.4.7', 'pyserial'],
python_requires='>=3.5', python_requires='>=3.5',
) )