def _th4_input(self) -> CBOR: hash_func = config_cose(self.cipher_suite.hash).hash input_th = [ self.transcript(hash_func, self._th3_input), self.msg_3.ciphertext ] return b''.join([cbor2.dumps(part) for part in input_th])
def ciphertext_3(self): # TODO: resolve magic key and IV lengths iv_bytes = self._hkdf3(13, 'IV_3ae', self._prk3e2m) hash_func = config_cose(self.cipher_suite.hash).hash # TODO: resolve magic key and IV lengths cose_key = self._create_cose_key(self._hkdf3, 16, 'K_3ae', self._prk3e2m, KeyOps.ENCRYPT) # create payload for the COSE_Encrypt0 payload = [self._p_3ae] if self.aad3_cb is not None: payload.append(self.aad3_cb()) payload = b''.join(payload) # create the external data for the COSE_Encrypt0 th_3 = self.transcript(hash_func, self._th3_input) # calculate the mac_2 using a COSE_Encrypt0 message ciphertext = Enc0Message(payload=payload, external_aad=th_3).encrypt( iv_bytes, cose_key) return ciphertext
def _decrypt(self, ciphertext: bytes) -> bytes: # TODO: resolve magic key and IV lengths iv_bytes = self._hkdf3(13, 'IV_3ae', self._prk3e2m) hash_func = config_cose(self.cipher_suite.hash).hash # TODO: resolve magic key and IV lengths cose_key = self._create_cose_key(self._hkdf3, 16, 'K_3ae', self._prk3e2m, KeyOps.DECRYPT) th_3 = self.transcript(hash_func, self._th3_input) return Enc0Message(payload=ciphertext, external_aad=th_3).decrypt(iv_bytes, cose_key)
def _prk(self, private_key: Key, pub_key: Key, salt: bytes) -> bytes: h = self.cipher_suite.hash secret = self.shared_secret(private_key, pub_key) prk_2e = hmac.HMAC(algorithm=config_cose(h).hash(), key=salt, backend=default_backend()) prk_2e.update(secret) prk = prk_2e.finalize() return prk
def shared_secret(private_key: Key, public_key: Key) -> bytes: """ Compute the shared secret. """ if public_key.crv == CoseEllipticCurves.X25519: d = X25519PrivateKey.from_private_bytes(private_key.d) x = X25519PublicKey.from_public_bytes(public_key.x) elif public_key.crv == CoseEllipticCurves.X448: d = X448PrivateKey.from_private_bytes(private_key.d) x = X448PublicKey.from_public_bytes(public_key.x) elif public_key.crv == CoseEllipticCurves.P_256: d = ec.derive_private_key(int(hexlify(private_key.d), 16), config_cose(public_key.crv).curve[1](), default_backend()) x = ec.EllipticCurvePublicNumbers( int(hexlify(public_key.x), 16), int(hexlify(public_key.y), 16), config_cose(public_key.crv).curve[1]()) else: raise CoseIllegalCurve(f"{public_key.crv} is unsupported") secret = d.exchange(x) return secret
def _external_aad(self, transcript: bytes, aad_cb: Callable[..., bytes]) -> CBOR: hash_func = config_cose(self.cipher_suite.hash).hash aad = [cbor2.dumps(self.transcript(hash_func, transcript)), self.cred] if aad_cb is not None: ad = aad_cb() if ad != b'': aad.append(ad) aad = b"".join(aad) return aad
def test_initiator_message3(initiator, test_vectors): initiator.msg_1 = MessageOne.decode(test_vectors['I']['message_1']) initiator.msg_2 = MessageTwo.decode(test_vectors['R']['message_2']) crv = CoseEllipticCurves(CipherSuite(initiator._selected_cipher).dh_curve) hash_func = config_cose(CipherSuite(initiator._selected_cipher).hash).hash assert initiator.data_2 == test_vectors['R']['data_2'] assert initiator._th2_input == test_vectors['R']['input_th_2'] assert initiator._prk2e == test_vectors['R']['prk_2e'] assert initiator._prk3e2m == test_vectors['R']['prk_3e2m'] assert initiator.transcript( hash_func, initiator._th2_input) == test_vectors['R']['th_2'] assert initiator._decrypt( initiator.msg_2.ciphertext) == test_vectors['R']['p_2e'] assert initiator.shared_secret(initiator.ephemeral_key, OKP(x=initiator.g_y, crv=crv)) == test_vectors['S']['g_xy'] assert initiator.data_3 == test_vectors['I']['data_3'] assert initiator._th3_input == test_vectors['I']['input_th_3'] assert initiator.transcript( hash_func, initiator._th3_input) == test_vectors['I']['th_3'] assert initiator.cred_id == test_vectors['I']['id_cred'] assert initiator._prk4x3m == test_vectors['I']['prk_4x3m'] assert initiator._external_aad( initiator._th3_input, initiator.aad3_cb) == test_vectors['I']['eaad_3m'] assert initiator._hkdf3(16, 'K_3m', initiator._prk4x3m) == test_vectors['I']['k_3m'] assert initiator._hkdf3(13, 'IV_3m', initiator._prk4x3m) == test_vectors['I']['iv_3m'] assert initiator._mac(initiator._hkdf3, 'K_3m', 16, 'IV_3m', 13, initiator._th3_input, initiator._prk4x3m, initiator.aad2_cb) == test_vectors['I']['mac3'] assert initiator.signature_or_mac3( test_vectors['I']['mac3']) == test_vectors['I']['sign_or_mac3'] assert initiator._p_3ae == test_vectors['I']['p_3ae'] assert initiator._hkdf3(16, 'K_3ae', initiator._prk3e2m) == test_vectors['I']['k_3ae'] assert initiator._hkdf3(13, 'IV_3ae', initiator._prk3e2m) == test_vectors['I']['iv_3ae'] assert initiator.ciphertext_3 == test_vectors['I']['ciphertext_3'] assert initiator.create_message_three( test_vectors['R']['message_2']) == test_vectors['I']['message_3']
def _hkdf_expand(self, length: int, label: str, prk: bytes, transcript: bytes) -> bytes: """ Derive the encryption key and the IV to protect the COSE_Encrypt0 message in the EDHOC message 2. :return: """ hash_func = config_cose(self.cipher_suite.hash).hash info = EdhocKDFInfo(edhoc_aead_id=self.cipher_suite.aead, transcript_hash=self.transcript( hash_func, transcript), label=label, length=length) derived_bytes = HKDFExpand(algorithm=hash_func(), length=info.length, info=info.encode(), backend=default_backend()).derive(prk) return derived_bytes
def test_responder_message2(responder, test_vectors): responder.msg_1 = MessageOne.decode(test_vectors['I']['message_1']) hash_func = config_cose(CipherSuite( responder.msg_1.selected_cipher).hash).hash crv = CoseEllipticCurves( CipherSuite(responder.msg_1.selected_cipher).dh_curve) assert responder.shared_secret(responder.ephemeral_key, OKP(x=responder.g_x, crv=crv)) == test_vectors['S']['g_xy'] assert responder._prk2e == test_vectors['R']['prk_2e'] assert responder._prk3e2m == test_vectors['R']['prk_3e2m'] assert responder.data_2 == test_vectors['R']['data_2'] assert responder._th2_input == test_vectors['R']['input_th_2'] assert responder.cred_id == test_vectors['R']['id_cred'] assert responder.transcript( hash_func, responder._th2_input) == test_vectors['R']['th_2'] assert responder._external_aad( responder._th2_input, responder.aad2_cb) == test_vectors['R']['eaad_2m'] assert responder._hkdf2( 16, 'K_2m', prk=responder._prk3e2m) == test_vectors['R']['k_2m'] assert responder._hkdf2( 13, 'IV_2m', prk=responder._prk3e2m) == test_vectors['R']['iv_2m'] assert responder._mac(responder._hkdf2, 'K_2m', 16, 'IV_2m', 13, responder._th2_input, responder._prk3e2m, responder.aad2_cb) == test_vectors['R']['mac2'] assert responder.signature_or_mac2( test_vectors['R']['mac2']) == test_vectors['R']['sign_or_mac2'] assert responder._p_2e == test_vectors['R']['p_2e'] assert responder._hkdf2(len(responder._p_2e), 'K_2e', prk=responder._prk2e) == test_vectors['R']['k_2e'] assert responder.ciphertext_2 == test_vectors['R']['ciphertext_2'] assert responder.create_message_two( test_vectors['I']['message_1']) == test_vectors['R']['message_2']
def exporter(self, label: str, length: int): hash_func = config_cose(self.cipher_suite.hash).hash return self._hkdf_expand(length, label, self._prk4x3m, self.transcript(hash_func, self._th4_input))