def test_okp_key_construction(crv): key = OKPKey(crv=crv, x=os.urandom(32), d=os.urandom(32), optional_params={'ALG': 'EDDSA'}) assert _is_valid_okp_key(key) serialized = key.encode() _ = CoseKey.decode(serialized)
def test_set_curve_in_key(): with pytest.raises(CoseException) as excinfo: key = OKPKey(crv='Ed25519', d=os.urandom(32)) assert "Unknown COSE header or key attribute" in str(excinfo) key = OKPKey(crv='ED25519', d=os.urandom(32)) assert key.crv == Ed25519
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_fail_on_illegal_kty(crv, kty): params = {KpKty: kty} with pytest.raises(CoseIllegalKeyType) as excinfo: _ = OKPKey(crv=crv, x=os.urandom(32), d=os.urandom(32), optional_params=params) assert "Illegal key type in OKP COSE Key" in str(excinfo.value)
def remote_pubkey(self) -> RPK: """ Returns the remote ephemeral public key. """ if self.cipher_suite.dh_curve in [X448, X25519]: return OKPKey(x=self.g_y, crv=self.cipher_suite.dh_curve) else: return EC2Key(x=self.g_y, crv=self.cipher_suite.dh_curve)
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 test_okp_key_generation_encoding_decoding(crv): trails = 256 for i in range(trails): okp_test = OKPKey.generate_key(crv=crv) okp_encoded = okp_test.encode() okp_decoded = CoseKey.decode(okp_encoded) assert _is_valid_okp_key(okp_decoded)
def local_pubkey(self) -> RPK: """ Returns the local ephemeral public key. """ if self.cipher_suite.dh_curve in [X448, X25519]: return OKPKey(x=self.g_x, crv=self.cipher_suite.dh_curve) else: # TODO: pass
def remote_pubkey(self) -> RPK: """ Returns the remote ephemeral public key. """ 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
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: return EC2Key(x=self.g_y, crv=self.cipher_suite.dh_curve)
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_key_ops_setter_getter(): key = OKPKey.generate_key('ED25519') key.key_ops = [SignOp] assert SignOp in key.key_ops with pytest.raises(CoseIllegalKeyOps) as excinfo: key.key_ops = [MacVerifyOp] assert "Invalid COSE key operation" in str(excinfo)
def __init__(self, cred_idr, cred, auth_key): super().__init__() # test with static connection identifier and static ephemeral key self.ephemeral_key = OKPKey( crv=X25519, x=unhexlify("71a3d599c21da18902a1aea810b2b6382ccd8d5f9bf0195281754c5ebcaf301e"), d=unhexlify("fd8cd877c9ea386e6af34ff7e606c4b64ca831c8ba33134fd4cd7167cabaecda")) self.cred_idr = cred_idr self.cred = cred self.auth_key = auth_key self.supported = [CipherSuite0, CipherSuite1] self.resp = self.create_responder()
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 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 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)
async def main(): parser = argparse.ArgumentParser() # 51.75.194.248 parser.add_argument("ip", help="IP address of EDHOC responder", type=str) parser.add_argument("--epk", help="Use a preset ephemeral key", action="store_true") args = parser.parse_args() context = await Context.create_client_context() supported = [CipherSuite0] if args.epk: ephemeral_key = OKPKey( crv=X25519, x=unhexlify( "898ff79a02067a16ea1eccb90fa52246f5aa4dd6ec076bba0259d904b7ec8b0c" ), d=unhexlify( "8f781a095372f85b6d9f6109ae422611734d7dbfa0069a2df2935bb2e053bf35" )) else: ephemeral_key = None init = Initiator(corr=Correlation.CORR_1, method=Method.SIGN_SIGN, conn_idi=unhexlify(b''), cred_idi=cred_id_initiator, auth_key=initiator_authkey, cred=initiator_cert, remote_cred_cb=get_peer_cred, supported_ciphers=supported, selected_cipher=CipherSuite0, ephemeral_key=ephemeral_key) msg_1 = init.create_message_one() # assert msg_1 == unhexlify(b"01005820898ff79a02067a16ea1eccb90fa52246f5aa4dd6ec076bba0259d904b7ec8b0c40") request = Message(code=Code.POST, payload=msg_1, uri=f"coap://[{args.ip}]/.well-known/edhoc") logging.info("POST (%s) %s", init.edhoc_state, request.payload) response = await context.request(request).response logging.info("CHANGED (%s) %s", init.edhoc_state, response.payload) msg_3 = init.create_message_three(response.payload) # assert msg_3 == unhexlify(_msg_3) logging.info("POST (%s) %s", init.edhoc_state, request.payload) request = Message(code=Code.POST, payload=msg_3, uri=f"coap://[{args.ip}]/.well-known/edhoc") response = await context.request(request).response conn_idi, conn_idr, aead, hashf = init.finalize() logging.info('EDHOC key exchange successfully completed:') logging.info(f" - connection IDr: {conn_idr}") logging.info(f" - connection IDi: {conn_idi}") logging.info(f" - aead algorithm: {aead}") logging.info(f" - hash algorithm: {hashf}") logging.info( f" - OSCORE secret : {init.exporter('OSCORE Master Secret', 16)}") logging.info( f" - OSCORE salt : {init.exporter('OSCORE Master Salt', 8)}")
def test(self): print('\nTest: ' + __name__ + '.' + type(self).__name__) private_key = OKPKey( crv=curves.Ed25519, x=binascii.unhexlify('64f38ea84b153c7be87349f78261ca46e90f1613a3ceb2ae02c010193631e07d'), d=binascii.unhexlify('6820977a9be08d676dac7ee19e1595d0552894ee2d71feb1d7b1d2a9f31754fd'), optional_params={ keyparam.KpKid: b'ExampleEd25519', keyparam.KpKeyOps: [keyops.SignOp, keyops.VerifyOp], } ) print('Private Key: {}'.format(encode_diagnostic(cbor2.loads(private_key.encode())))) # Primary block prim_dec = self._get_primary_item() prim_enc = cbor2.dumps(prim_dec) print('Primary Block: {}'.format(encode_diagnostic(prim_dec))) print('Encoded: {}'.format(encode_diagnostic(prim_enc))) # Security target block target_dec = self._get_target_item() target_enc = cbor2.dumps(target_dec) content_plaintext = target_dec[4] print('Target Block: {}'.format(encode_diagnostic(target_dec))) print('Plaintext: {}'.format(encode_diagnostic(content_plaintext))) # Combined AAD ext_aad_dec = self._get_aad_item() ext_aad_enc = cbor2.dumps(ext_aad_dec) print('External AAD: {}'.format(encode_diagnostic(ext_aad_dec))) print('Encoded: {}'.format(encode_diagnostic(ext_aad_enc))) msg_obj = Sign1Message( phdr={ headers.Algorithm: algorithms.EdDSA, }, uhdr={ headers.KID: private_key.kid, }, payload=content_plaintext, # Non-encoded parameters external_aad=ext_aad_enc, ) msg_obj.key = private_key # COSE internal structure cose_struct_enc = msg_obj._sig_structure cose_struct_dec = cbor2.loads(cose_struct_enc) print('COSE Structure: {}'.format(encode_diagnostic(cose_struct_dec))) print('Encoded: {}'.format(encode_diagnostic(cose_struct_enc))) # Encoded message message_enc = msg_obj.encode(tag=False) message_dec = cbor2.loads(message_enc) # Detach the payload content_signature = message_dec[2] message_dec[2] = None self._print_message(message_dec, recipient_idx=4) message_enc = cbor2.dumps(message_dec) # ASB structure asb_dec = self._get_asb_item([ msg_obj.cbor_tag, message_enc ]) asb_enc = self._get_asb_enc(asb_dec) print('ASB: {}'.format(encode_diagnostic(asb_dec))) print('Encoded: {}'.format(encode_diagnostic(asb_enc))) bpsec_dec = self._get_bpsec_item( block_type=BlockType.BIB, asb_dec=asb_dec, ) bpsec_enc = cbor2.dumps(bpsec_dec) print('BPSec block: {}'.format(encode_diagnostic(bpsec_dec))) print('Encoded: {}'.format(encode_diagnostic(bpsec_enc))) # Change from detached payload message_dec[2] = content_signature decode_obj = Sign1Message.from_cose_obj(message_dec) decode_obj.external_aad = ext_aad_enc decode_obj.key = private_key verify_valid = decode_obj.verify_signature() self.assertTrue(verify_valid) print('Loopback verify:', verify_valid) bundle = self._assemble_bundle([prim_enc, bpsec_enc, target_enc]) print('Total bundle: {}'.format(encode_diagnostic(bundle)))
def test_fail_on_missing_key_values(crv): with pytest.raises(CoseInvalidKey) as excinfo: _ = OKPKey(crv=crv) assert "Either the public values or the private value must be specified" in str(excinfo.value)
def test_okp_key_generation(crv): key = OKPKey.generate_key(crv) assert _is_valid_okp_key(key)
def test_key_generation_with_optional_parameters(): key = OKPKey.generate_key(crv='ED25519', optional_params={'KpKid': 4})
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)
with open("responder-cert.pem", "rb") as f: c = b"".join(f.readlines()) responder_cert = x509.load_pem_x509_certificate(c) cert_hash = X5T.from_certificate(Sha256Trunc64, responder_cert.tbs_certificate_bytes).encode() cred_id_responder = {headers.X5t: cert_hash} with open("responder-authkey.pem", "rb") as f: k = b"".join(f.readlines()) key = load_pem_private_key(k, password=None) responder_authkey = OKPKey(crv=Ed25519, d=key.private_bytes(serialization.Encoding.Raw, serialization.PrivateFormat.Raw, serialization.NoEncryption()), x=key.public_key().public_bytes(serialization.Encoding.Raw, serialization.PublicFormat.Raw)) class EdhocResponder(resource.Resource): def __init__(self, cred_idr, cred, auth_key): super().__init__() # test with static connection identifier and static ephemeral key self.ephemeral_key = OKPKey( crv=X25519, x=unhexlify("71a3d599c21da18902a1aea810b2b6382ccd8d5f9bf0195281754c5ebcaf301e"), d=unhexlify("fd8cd877c9ea386e6af34ff7e606c4b64ca831c8ba33134fd4cd7167cabaecda"))