def setup_dh_key(selected_cipher: int, private_bytes: bytes): if CipherSuite.from_id(selected_cipher).dh_curve in [X448, X25519]: return OKPKey(d=private_bytes, crv=CipherSuite.from_id(selected_cipher).dh_curve) elif CipherSuite.from_id(selected_cipher).dh_curve in [P256]: return EC2Key(d=private_bytes, crv=CipherSuite.from_id(selected_cipher).sign_curve) else: raise ValueError("Illegal DH keys.")
def test_responder_finalize(responder, test_vectors): responder.msg_1 = MessageOne.decode(test_vectors['S']['message_1']) responder.msg_2 = MessageTwo.decode( responder.create_message_two(test_vectors['S']['message_1'])) responder.msg_3 = MessageThree.decode(test_vectors['S']['message_3']) decoded = EdhocMessage.decode( responder._decrypt(responder.msg_3.ciphertext)) if KID.identifier in cbor2.loads(test_vectors['I']['cred_id']): assert decoded[0] == EdhocMessage.encode_bstr_id( cbor2.loads(test_vectors['I']['cred_id'])[KID.identifier]) else: assert decoded[0] == cbor2.loads(test_vectors['I']['cred_id']) assert decoded[1] == test_vectors['S']['signature_3'] if getattr(responder, 'remote_authkey', None) is None: warnings.warn(NoRemoteKey()) return c_i, c_r, app_aead, app_hash = responder.finalize( test_vectors['S']['message_3']) assert c_i == test_vectors['I']['conn_id'] assert c_r == test_vectors['R']['conn_id'] assert app_aead == CipherSuite.from_id( test_vectors['I']['selected']).app_aead.identifier assert app_hash == CipherSuite.from_id( test_vectors['I']['selected']).app_hash.identifier
def test_responder_message2(responder, test_vectors): responder.msg_1 = MessageOne.decode(test_vectors['S']['message_1']) hash_func = CipherSuite.from_id( responder.msg_1.selected_cipher).hash.hash_cls crv = CipherSuite.from_id(responder.msg_1.selected_cipher).dh_curve assert responder.shared_secret( responder.ephemeral_key, OKPKey(x=responder.g_x, crv=crv)) == test_vectors['S']['g_xy'] assert responder._prk2e == test_vectors['S']['prk_2e'] assert responder._prk3e2m == test_vectors['S']['prk_3e2m'] assert responder.data_2 == test_vectors['S']['data_2'] assert responder._th2_input == test_vectors['S']['input_th_2'] assert responder.cred_id == cbor2.loads(test_vectors['R']['cred_id']) assert responder.transcript( hash_func, responder._th2_input) == test_vectors['S']['th_2'] assert responder._hkdf2( 16, 'K_2m', prk=responder._prk3e2m) == test_vectors['S']['k_2m'] assert responder._hkdf2( 13, 'IV_2m', prk=responder._prk3e2m) == test_vectors['S']['iv_2m'] assert responder._mac(responder._hkdf2, 'K_2m', 16, 'IV_2m', 13, responder._th2_input, responder._prk3e2m, responder.aad2_cb) == test_vectors['S']['mac_2'] assert responder.signature_or_mac2( test_vectors['S']['mac_2']) == test_vectors['S']['signature_2'] assert responder._p_2e == test_vectors['S']['p_2e'] assert responder._hkdf2( len(responder._p_2e), 'KEYSTREAM_2', prk=responder._prk2e) == test_vectors['S']['keystream_2'] assert responder.ciphertext_2 == test_vectors['S']['ciphertext_2'] assert responder.create_message_two( test_vectors['S']['message_1']) == test_vectors['S']['message_2']
def setup_sign_key(selected_cipher: int, private_bytes: bytes): if CipherSuite.from_id(selected_cipher).sign_curve in [Ed448, Ed25519]: return OKPKey(d=private_bytes, crv=CipherSuite.from_id(selected_cipher).sign_curve, optional_params={KpAlg: CipherSuite.from_id(selected_cipher).sign_alg}) elif CipherSuite.from_id(selected_cipher).sign_alg == Es256: return EC2Key(d=private_bytes, alg=Es256, crv=CipherSuite.from_id(selected_cipher).sign_curve) else: raise ValueError("Illegal COSE curve.")
def test_message1_encode(test_vectors): msg = MessageOne( method_corr=test_vectors["S"]["method_corr"], cipher_suites=[ CipherSuite.from_id(x) for x in test_vectors["I"]["supported"] ], selected_cipher=CipherSuite.from_id(test_vectors["I"]["selected"]), g_x=test_vectors["I"]["g_x"], conn_idi=test_vectors["I"]["conn_id"], external_aad=test_vectors["I"]["ad_1"]) assert msg.encode( test_vectors["S"]["corr"]) == test_vectors["S"]["message_1"]
def test_initiator_finalize(initiator, test_vectors): initiator.msg_1 = MessageOne.decode(initiator.create_message_one()) initiator.msg_2 = MessageTwo.decode(test_vectors['S']['message_2']) initiator.msg_3 = MessageThree.decode( initiator.create_message_three(test_vectors['S']['message_2'])) c_i, c_r, app_aead, app_hash = initiator.finalize() assert c_i == test_vectors['I']['conn_id'] assert c_r == test_vectors['R']['conn_id'] assert app_aead == CipherSuite.from_id( test_vectors['I']['selected']).app_aead.identifier assert app_hash == CipherSuite.from_id( test_vectors['I']['selected']).app_hash.identifier
def test_initiator_finalize(initiator, test_vectors): initiator.msg_1 = MessageOne.decode(initiator.create_message_one()) initiator.msg_2 = MessageTwo.decode(test_vectors['S']['message_2']) if getattr(initiator, 'remote_authkey', None) is None: warnings.warn(NoRemoteKey()) return initiator.msg_3 = MessageThree.decode(initiator.create_message_three(test_vectors['S']['message_2'])) c_i, c_r, app_aead, app_hash = initiator.finalize() assert c_i == test_vectors['I']['conn_id'] assert c_r == test_vectors['R']['conn_id'] assert app_aead == CipherSuite.from_id(test_vectors['I']['selected']).app_aead.identifier assert app_hash == CipherSuite.from_id(test_vectors['I']['selected']).app_hash.identifier
def responder(ephemeral_responder_key, test_vectors): if test_vectors['R']['cred_type'] == 0: local_cred = cbor2.loads(test_vectors['R']['cred']) local_auth_key = None else: local_cred = CoseKey.decode(test_vectors['R']['cred']) local_auth_key = CoseKey.decode(test_vectors['R']['cred']) if test_vectors['I']['cred_type'] == 0: remote_cred = cbor2.loads(test_vectors['I']['cred']) remote_auth_key = None else: remote_cred = CoseKey.decode(test_vectors['I']['cred']) remote_auth_key = CoseKey.decode(test_vectors['I']['cred']) responder = Responder( conn_idr=test_vectors["R"]["conn_id"], cred_idr=cbor2.loads(test_vectors['R']['cred_id']), auth_key=CoseKey.decode(test_vectors['R']['auth_key']), cred=(local_cred, local_auth_key), supported_ciphers=[ CipherSuite.from_id(c) for c in test_vectors["R"]["supported"] ], remote_cred_cb=lambda arg: (remote_cred, remote_auth_key), ephemeral_key=ephemeral_responder_key) responder.cred_idi = test_vectors['I']['cred_id'] return responder
def cipher_suite(self) -> 'CS': if self.msg_1 is None: raise EdhocException("Message 1 not received. Cannot derive selected cipher suite.") else: if not self._verify_cipher_selection(self.msg_1.selected_cipher, self.msg_1.cipher_suites): raise EdhocException("Invalid cipher suite setup") return CipherSuite.from_id(self.msg_1.selected_cipher)
def initiator(ephemeral_initiator_key, test_vectors): if test_vectors['I']['cred_type'] == 0: local_auth_key = None local_cred = cbor2.loads(test_vectors['I']['cred']) else: local_auth_key = CoseKey.decode(test_vectors['I']['cred']) local_cred = CoseKey.decode(test_vectors['I']['cred']) if test_vectors['R']['cred_type'] == 0: remote_auth_key = None remote_cred = cbor2.loads(test_vectors['R']['cred']) else: remote_auth_key = CoseKey.decode(test_vectors['R']['cred']) remote_cred = CoseKey.decode(test_vectors['R']['cred']) return Initiator( corr=test_vectors['S']['corr'], method=test_vectors['S']['method'], cred=(local_cred, local_auth_key), cred_idi=cbor2.loads(test_vectors['I']['cred_id']), auth_key=CoseKey.decode(test_vectors['I']['auth_key']), selected_cipher=test_vectors['I']['selected'], supported_ciphers=[CipherSuite.from_id(c) for c in test_vectors["I"]["supported"]], conn_idi=test_vectors['I']['conn_id'], remote_cred_cb=lambda x: (remote_cred, remote_auth_key), ephemeral_key=ephemeral_initiator_key, )
def decode(cls, received: bytes) -> 'MessageOne': """ Tries to decode the bytes as an EDHOC MessageOne. :param received: Bytes to decode. :raises EdhocInvalidMessage: Decoding routine for MessageOne failed. :returns: An EDHOC MessageOne object. """ decoded = super().decode(received) method_corr = decoded[cls.METHOD_CORR] if isinstance(decoded[cls.CIPHERS], int): selected_cipher = decoded[cls.CIPHERS] supported_ciphers = [decoded[cls.CIPHERS]] elif isinstance(decoded[cls.CIPHERS], list): selected_cipher = decoded[cls.CIPHERS][0] supported_ciphers = decoded[cls.CIPHERS][1:] else: raise EdhocInvalidMessage("Failed to decode bytes as MessageOne") g_x = decoded[cls.G_X] if decoded[cls.CONN_ID] != b'': if isinstance(decoded[cls.CONN_ID], int): conn_idi = EdhocMessage.decode_bstr_id(decoded[cls.CONN_ID]) else: conn_idi = decoded[cls.CONN_ID] else: conn_idi = b'' msg = cls( method_corr=method_corr, selected_cipher=CipherSuite.from_id(selected_cipher), cipher_suites=[CipherSuite.from_id(c) for c in supported_ciphers], g_x=g_x, conn_idi=conn_idi) try: msg.aad1 = decoded[cls.AAD1] except IndexError: pass return msg
def test_initiator_message3(initiator, test_vectors): initiator.msg_1 = MessageOne.decode(test_vectors['S']['message_1']) initiator.msg_2 = MessageTwo.decode(test_vectors['S']['message_2']) crv = CipherSuite.from_id(initiator._selected_cipher).dh_curve hash_func = CipherSuite.from_id(initiator._selected_cipher).hash.hash_cls assert initiator.data_2 == test_vectors['S']['data_2'] assert initiator._th2_input == test_vectors['S']['input_th_2'] assert initiator._prk2e == test_vectors['S']['prk_2e'] assert initiator._prk3e2m == test_vectors['S']['prk_3e2m'] assert initiator.transcript(hash_func, initiator._th2_input) == test_vectors['S']['th_2'] assert initiator._decrypt(initiator.msg_2.ciphertext) == test_vectors['S']['p_2e'] assert initiator.shared_secret(initiator.ephemeral_key, OKPKey(x=initiator.g_y, crv=crv)) == test_vectors['S'][ 'g_xy'] assert initiator.data_3 == test_vectors['S']['data_3'] assert initiator._th3_input == test_vectors['S']['input_th_3'] assert initiator.transcript(hash_func, initiator._th3_input) == test_vectors['S']['th_3'] assert initiator.cred_id == cbor2.loads(test_vectors['I']['cred_id']) assert initiator._prk4x3m == test_vectors['S']['prk_4x3m'] assert initiator._hkdf3(16, 'K_3m', initiator._prk4x3m) == test_vectors['S']['k_3m'] assert initiator._hkdf3(13, 'IV_3m', initiator._prk4x3m) == test_vectors['S']['iv_3m'] assert initiator._mac( initiator.cred_idi, initiator.cred, initiator._hkdf3, 'K_3m', 16, 'IV_3m', 13, initiator._th3_input, initiator._prk4x3m, initiator.aad2_cb) == test_vectors['S']['mac_3'] assert initiator.signature_or_mac3(test_vectors['S']['mac_3']) == test_vectors['S']['signature_3'] assert initiator._p_3ae == test_vectors['S']['p_3ae'] assert initiator._hkdf3(16, 'K_3ae', initiator._prk3e2m) == test_vectors['S']['k_3ae'] assert initiator._hkdf3(13, 'IV_3ae', initiator._prk3e2m) == test_vectors['S']['iv_3ae'] assert initiator.ciphertext_3 == test_vectors['S']['ciphertext_3'] if initiator.remote_authkey is None: warnings.warn(NoRemoteKey()) return assert initiator.create_message_three(test_vectors['S']['message_2']) == test_vectors['S']['message_3']
def test_message1_decode(test_vectors): msg = MessageOne.decode(test_vectors['S']['message_1']) assert msg.corr == test_vectors["S"]["corr"] assert msg.method == test_vectors["S"]["method"] assert msg.cipher_suites == [ CipherSuite.from_id(x) for x in test_vectors["I"]["supported"] ] assert msg.selected_cipher.identifier == test_vectors["I"]["selected"] assert msg.g_x == test_vectors["I"]["g_x"] assert msg.conn_idi == test_vectors['I']['conn_id'] assert msg.aad1 == test_vectors['I']['ad_1']
def _generate_ephemeral_key(self) -> None: """ Generate a new ephemeral key if the key was not already set. :return: None """ if self.ephemeral_key is not None: return chosen_suite = CipherSuite.from_id(self.cipher_suite) if chosen_suite.dh_curve in [X25519, X448]: self.ephemeral_key = OKPKey.generate_key(crv=chosen_suite.dh_curve) else: self.ephemeral_key = EC2Key.generate_key(crv=chosen_suite.dh_curve)
def __init__(self, corr: Correlation, method: Method, cred: Union[RPK, Certificate], cred_idi: CoseHeaderMap, auth_key: RPK, selected_cipher: Type['CS'], supported_ciphers: List[Type['CS']], remote_cred_cb: Callable[[CoseHeaderMap], Union[Certificate, RPK]], conn_idi: Optional[bytes] = None, aad1_cb: Optional[Callable[..., bytes]] = None, aad2_cb: Optional[Callable[..., bytes]] = None, aad3_cb: Optional[Callable[..., bytes]] = None, ephemeral_key: Optional['CK'] = None): """ Create an EDHOC Initiator. :param corr: Correlation value (depends on the transport protocol). :param method: EDHOC method type (signatures, static DH or a mix). :param cred: The public authentication credentials of the Initiator. :param cred_idi: The Initiator's credential identifier (a CBOR encoded COSE header map) :param auth_key: The private authentication key (CoseKey) of the Responder. :param selected_cipher: Provide the selected cipher. :param supported_ciphers: A list of ciphers supported by the Responder. :param conn_idi: The connection identifier to be used :param remote_cred_cb: A callback that fetches the remote credentials. :param aad1_cb: A callback to pass received additional data to the application protocol. :param aad2_cb: A callback to pass additional data to the remote endpoint. :param aad3_cb: A callback to pass received additional data to the application protocol. :param ephemeral_key: Preload an (CoseKey) ephemeral key (if unset a random key will be generated). """ if conn_idi is None: conn_idi = os.urandom(1) super().__init__(cred, cred_idi, auth_key, supported_ciphers, conn_idi, remote_cred_cb, aad1_cb, aad2_cb, aad3_cb, ephemeral_key) self._selected_cipher = CipherSuite.from_id(selected_cipher) self._corr = Correlation(corr) self._method = Method(method) self._cred_idr = None
def ephemeral_initiator_key(test_vectors): return OKPKey( x=test_vectors['I']['g_x'], d=test_vectors['I']['x'], crv=CipherSuite.from_id(test_vectors['I']['selected']).dh_curve)
def ephemeral_responder_key(test_vectors): return OKPKey( x=test_vectors['R']['g_y'], d=test_vectors['R']['y'], crv=CipherSuite.from_id(test_vectors['I']['selected']).dh_curve)