From 7771c54b581fdcfce206a382c20a6a0bf7889bef Mon Sep 17 00:00:00 2001 From: chrysn Date: Mon, 26 Apr 2021 18:11:11 +0200 Subject: [PATCH 1/2] Add suport for P256 (cipher suite 2) --- edhoc/roles/edhoc.py | 5 ++++- edhoc/roles/initiator.py | 8 +++----- edhoc/roles/responder.py | 10 +++++----- 3 files changed, 12 insertions(+), 11 deletions(-) diff --git a/edhoc/roles/edhoc.py b/edhoc/roles/edhoc.py index 9f85504..5d77e64 100644 --- a/edhoc/roles/edhoc.py +++ b/edhoc/roles/edhoc.py @@ -127,20 +127,23 @@ def shared_secret(private_key: 'CK', public_key: 'CK') -> bytes: if public_key.crv == X25519: d = X25519PrivateKey.from_private_bytes(private_key.d) x = X25519PublicKey.from_public_bytes(public_key.x) + secret = d.exchange(x) elif public_key.crv == X448: d = X448PrivateKey.from_private_bytes(private_key.d) x = X448PublicKey.from_public_bytes(public_key.x) + secret = d.exchange(x) elif public_key.crv == P256: d = ec.derive_private_key(int(hexlify(private_key.d), 16), SECP256R1(), default_backend()) x = ec.EllipticCurvePublicNumbers(int(hexlify(public_key.x), 16), int(hexlify(public_key.y), 16), SECP256R1()) + x = x.public_key() + secret = d.exchange(ec.ECDH(), x) else: raise CoseIllegalCurve(f"{public_key.crv} is unsupported") - secret = d.exchange(x) return secret @property diff --git a/edhoc/roles/initiator.py b/edhoc/roles/initiator.py index 509febf..9bccbcc 100644 --- a/edhoc/roles/initiator.py +++ b/edhoc/roles/initiator.py @@ -7,7 +7,7 @@ from cose import headers from cose.curves import X448, X25519 from cose.headers import KID -from cose.keys import OKPKey +from cose.keys import OKPKey, EC2Key from cose.keys.keyops import EncryptOp from cose.messages import Enc0Message, Sign1Message @@ -140,8 +140,7 @@ def local_pubkey(self) -> RPK: if self.cipher_suite.dh_curve in [X448, X25519]: return OKPKey(x=self.g_x, crv=self.cipher_suite.dh_curve) else: - # TODO: - pass + return EC2Key(x=self.g_x, crv=self.cipher_suite.dh_curve) @property def remote_pubkey(self) -> RPK: @@ -150,8 +149,7 @@ def remote_pubkey(self) -> RPK: if self.cipher_suite.dh_curve in [X448, X25519]: return OKPKey(x=self.g_y, crv=self.cipher_suite.dh_curve) else: - # TODO: - pass + return EC2Key(x=self.g_y, crv=self.cipher_suite.dh_curve) @property def local_authkey(self) -> RPK: diff --git a/edhoc/roles/responder.py b/edhoc/roles/responder.py index 98c4996..1cce29e 100644 --- a/edhoc/roles/responder.py +++ b/edhoc/roles/responder.py @@ -7,7 +7,7 @@ from cose import headers from cose.curves import X25519, X448 from cose.headers import KID -from cose.keys import OKPKey +from cose.keys import OKPKey, EC2Key from cose.keys.keyops import DecryptOp from cose.messages import Enc0Message, Sign1Message @@ -151,11 +151,12 @@ def g_x(self) -> bytes: def local_pubkey(self) -> RPK: """ Returns the local ephemeral public key. """ + # Is this a good criterion? (Possibly there doesn't need to be a + # distinction; self.cipher_suite.dh_curve.keyclass(...) could do) if self.cipher_suite.dh_curve in [X448, X25519]: return OKPKey(x=self.g_y, crv=self.cipher_suite.dh_curve) else: - # TODO: implement NIST curves - pass + return EC2Key(x=self.g_y, crv=self.cipher_suite.dh_curve) @property def remote_pubkey(self) -> RPK: @@ -164,8 +165,7 @@ def remote_pubkey(self) -> RPK: if self.cipher_suite.dh_curve in [X448, X25519]: return OKPKey(x=self.g_x, crv=self.cipher_suite.dh_curve) else: - # TODO: implement NIST curves - pass + return EC2Key(x=self.g_x, crv=self.cipher_suite.dh_curve) @property def local_authkey(self) -> RPK: From 35b9c130250ed87e7ed0c646e69d66cee6584520 Mon Sep 17 00:00:00 2001 From: chrysn Date: Thu, 29 Apr 2021 21:32:47 +0200 Subject: [PATCH 2/2] Add cipher suites 4 and 5, with additional checks --- edhoc/definitions.py | 47 ++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 45 insertions(+), 2 deletions(-) diff --git a/edhoc/definitions.py b/edhoc/definitions.py index 15b6f55..23705e5 100644 --- a/edhoc/definitions.py +++ b/edhoc/definitions.py @@ -3,8 +3,8 @@ from typing import Callable, Any, TypeVar, NamedTuple import cbor2 -from cose.algorithms import AESCCM1664128, Sha256, EdDSA, AESCCM16128128, Es256 -from cose.curves import X25519, Ed25519, P256 +from cose.algorithms import AESCCM1664128, Sha256, EdDSA, AESCCM16128128, Es256, A128GCM, A256GCM, Sha384, Es384 +from cose.curves import X25519, Ed25519, P256, P384 from edhoc.exceptions import EdhocException @@ -109,6 +109,21 @@ def __ge__(self, other: 'CipherSuite'): def __repr__(self): return f'<{self.fullname}: {self.identifier}>' + @classmethod + def check_identifiers(cls): + """Return the algorithm names for the suite in the sequence in which + they are printed in the EDHOC specification, for easy validation of the + classes.""" + return ( + cls.aead.identifier, + cls.hash.identifier, + cls.dh_curve.identifier, + cls.sign_alg.identifier, + cls.sign_curve.identifier, + cls.app_aead.identifier, + cls.app_hash.identifier, + ) + @CipherSuite.register_ciphersuite() class CipherSuite0(CipherSuite): @@ -165,6 +180,34 @@ class CipherSuite3(CipherSuite): app_aead = AESCCM1664128 app_hash = Sha256 +@CipherSuite.register_ciphersuite() +class CipherSuite4(CipherSuite): + identifier = 4 + fullname = "SUITE_4" + + aead = A128GCM + hash = Sha256 + dh_curve = X25519 + sign_alg = Es256 + sign_curve = P256 + app_aead = A128GCM + app_hash = Sha256 +assert CipherSuite4.check_identifiers() == (1, -16, 4, -7, 1, 1, -16) + +@CipherSuite.register_ciphersuite() +class CipherSuite5(CipherSuite): + identifier = 5 + fullname = "SUITE_5" + + aead = A256GCM + hash = Sha384 + dh_curve = P384 + sign_alg = Es384 + sign_curve = P384 + app_aead = A256GCM + app_hash = Sha384 +assert CipherSuite5.check_identifiers() == (3, -43, 2, -35, 2, 3, -43) + class EdhocKDFInfo(NamedTuple): edhoc_aead_id: int