def test_set_curve_in_key(): with pytest.raises(CoseException) as excinfo: key = EC2Key(crv='P257', d=os.urandom(32)) assert "Unknown COSE header or key attribute" in str(excinfo) with pytest.raises(CoseException) as excinfo: key = EC2Key(crv='Ed25519', d=os.urandom(32)) assert "Unknown COSE header or key attribute" in str(excinfo) key = EC2Key(crv='P_256', d=os.urandom(32)) assert key.crv == P256
def test_set_curve_in_key(): with pytest.raises(CoseException) as excinfo: _ = EC2Key(crv='P257', d=p256_d) assert "Unknown COSE attribute with value: [CoseCurve - P257]" in str( excinfo) with pytest.raises(CoseUnsupportedCurve) as excinfo: _ = EC2Key(crv='Ed25519', d=p256_d) assert "Invalid COSE curve <class 'cose.keys.curves.Ed25519'> for key type EC2Key" in str( excinfo) key = EC2Key(crv='P_256', d=p256_d) assert key.crv == P256
def test_unknown_key_attribute(): key = EC2Key(crv='P_256', d=p256_d, optional_params={"subject_name": "signing key"}) assert "subject_name" in key assert key['subject_name'] == "signing key"
def test_fail_on_illegal_kty(crv, kty): params = {KpKty: kty} with pytest.raises(CoseIllegalKeyType) as excinfo: _ = EC2Key(crv=crv, x=os.urandom(32), y=os.urandom(32), d=os.urandom(32), optional_params=params) assert "Illegal key type in EC2 COSE Key" in str(excinfo.value)
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 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 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_fail_on_illegal_kty(crv, kty): params = {KpKty: kty} # NOTE: the stuff in params will override the parameters of the function if they are specified twice # Here the KpKty value which is set by the constructor gets overwritten by the params dict with pytest.raises(CoseIllegalKeyType) as excinfo: _ = EC2Key(crv=crv, x=p256_x, y=p256_y, d=p256_d, optional_params=params) assert "Illegal key type in EC2 COSE Key" in str(excinfo.value)
def create_cose_1_sign_msg(payload, private_key): crv = P384 d_value = private_key.private_numbers().private_value x_coor = private_key.public_key().public_numbers().x y_coor = private_key.public_key().public_numbers().y key = EC2Key( crv=crv, d=d_value.to_bytes(crv.size, "big"), x=x_coor.to_bytes(crv.size, "big"), y=y_coor.to_bytes(crv.size, "big"), ) phdr = {1: Es384} msg = Sign1Message(phdr=phdr, payload=payload, key=key) sig = msg.compute_signature() msg._signature = sig return msg.encode(tag=False)
def verify_attestation_signature(payload, cert): logger.debug("* Verifying attestation certificate signature...") cert = load_der_x509_certificate(cert) cert_public_numbers = cert.public_key().public_numbers() x = cert_public_numbers.x y = cert_public_numbers.y x = _long_to_bytes(x) y = _long_to_bytes(y) # Create the EC2 key from public key parameters key = EC2Key(x=x, y=y, crv=P384) cose_obj = cbor2.loads(payload) msg = Sign1Message.from_cose_obj(cose_obj, allow_unknown_attributes=True) msg.key = key # Verify the signature using the EC2 key verified = msg.verify_signature() if not verified: errmsg = "Malformed attestation doc: incorrect certificate signature." logger.error(errmsg) raise RuntimeError(errmsg) logger.debug("* Attestation certificate signature verified.")
def test_on_missing_public_y_values(crv, x): key = EC2Key(crv=crv, x=x) assert key.y
def test_fail_with_d_and_missing_public_y_values(crv): with pytest.raises(CoseInvalidKey) as excinfo: _ = EC2Key(crv=crv, x=os.urandom(32), d=os.urandom(32)) assert "Missing public coordinate X/Y" in str(excinfo.value)
def test(self): print('\nTest: ' + __name__ + '.' + type(self).__name__) private_key = EC2Key( crv=curves.P256, x=binascii.unhexlify('44c1fa63b84f172b50541339c50beb0e630241ecb4eebbddb8b5e4fe0a1787a8'), y=binascii.unhexlify('059451c7630d95d0b550acbd02e979b3f4f74e645b74715fafbc1639960a0c7a'), d=binascii.unhexlify('dd6e7d8c4c0e0c0bd3ae1b4a2fa86b9a09b7efee4a233772cf5189786ea63842'), optional_params={ keyparam.KpKid: b'ExampleEC2', 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.Es256, }, 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 extract_cose_key(keyobj): ''' Get a COSE version of the local private key. :param keyobj: The cryptography key object. :return: The associated COSE key. :rtype: :py:cls:`CoseKey` ''' if isinstance(keyobj, (rsa.RSAPrivateKey, rsa.RSAPublicKey)): if hasattr(keyobj, 'private_numbers'): priv_nums = keyobj.private_numbers() pub_nums = keyobj.public_key().public_numbers() else: priv_nums = None pub_nums = keyobj.public_numbers() kwargs = dict() if pub_nums: def convert(name, attr=None): val = getattr(pub_nums, attr or name) kwargs[name] = val.to_bytes((val.bit_length() + 7) // 8, byteorder="big") convert('n') convert('e') if priv_nums: def convert(name, attr=None): val = getattr(priv_nums, attr or name) kwargs[name] = val.to_bytes((val.bit_length() + 7) // 8, byteorder="big") convert('d') convert('p') convert('q') convert('dp', 'dmp1') convert('dq', 'dmq1') convert('qinv', 'iqmp') cose_key = RSAKey(**kwargs) cose_key.alg = algorithms.Ps256 elif isinstance( keyobj, (ec.EllipticCurvePrivateKey, ec.EllipticCurvePublicKey)): CURVE_CLS_MAP = { ec.SECP256R1: curves.P256, ec.SECP384R1: curves.P384, ec.SECP521R1: curves.P521, } CURVE_ALG_MAP = { ec.SECP256R1: algorithms.Es256, ec.SECP384R1: algorithms.Es384, ec.SECP521R1: algorithms.Es512, } try: curve_cls = CURVE_CLS_MAP[type(keyobj.curve)] except KeyError: raise CoseUnsupportedCurve('Cannot match curve for {}'.format( repr(keyobj))) LOGGER.debug('Found COSE curve %s', curve_cls) try: alg_cls = CURVE_ALG_MAP[type(keyobj.curve)] except KeyError: raise CoseUnsupportedCurve( 'Cannot match algorithm for {}'.format(repr(keyobj))) LOGGER.debug('Found COSE algorithm %s', alg_cls) if hasattr(keyobj, 'private_numbers'): priv_nums = keyobj.private_numbers() pub_nums = keyobj.public_key().public_numbers() else: priv_nums = None pub_nums = keyobj.public_numbers() kwargs = dict( crv=curve_cls, optional_params={ keyparam.KpAlg: alg_cls, }, ) if pub_nums: x_coor = pub_nums.x y_coor = pub_nums.y kwargs.update( dict(x=x_coor.to_bytes((x_coor.bit_length() + 7) // 8, byteorder="big"), y=y_coor.to_bytes((y_coor.bit_length() + 7) // 8, byteorder="big"))) if priv_nums: d_value = priv_nums.private_value kwargs.update( dict(d=d_value.to_bytes((d_value.bit_length() + 7) // 8, byteorder="big"), )) cose_key = EC2Key(**kwargs) else: raise TypeError('Cannot handle key {}'.format(repr(keyobj))) return cose_key
def test(self): print('\nTest: ' + __name__ + '.' + type(self).__name__) private_key = EC2Key( crv=curves.P256, x=binascii.unhexlify( '44c1fa63b84f172b50541339c50beb0e630241ecb4eebbddb8b5e4fe0a1787a8' ), y=binascii.unhexlify( '059451c7630d95d0b550acbd02e979b3f4f74e645b74715fafbc1639960a0c7a' ), d=binascii.unhexlify( 'dd6e7d8c4c0e0c0bd3ae1b4a2fa86b9a09b7efee4a233772cf5189786ea63842' ), optional_params={ keyparam.KpKid: b'ExampleEC2', keyparam.KpKeyOps: [keyops.DeriveKeyOp], }) print('Private Key: {}'.format( encode_diagnostic(cbor2.loads(private_key.encode())))) # 256-bit content encryption key cek = SymmetricKey(k=binascii.unhexlify( '13BF9CEAD057C0ACA2C9E52471CA4B19DDFAF4C0784E3F3E8E3999DBAE4CE45C' ), optional_params={ keyparam.KpKid: b'ExampleCEK', keyparam.KpKeyOps: [keyops.EncryptOp, keyops.DecryptOp], }) print('CEK: {}'.format(encode_diagnostic(cbor2.loads(cek.encode())))) # session IV iv = binascii.unhexlify('6F3093EBA5D85143C3DC484A') print('IV: {}'.format(binascii.hexlify(iv))) # Would be random ephemeral key, but test constant sender_key = EC2Key( crv=curves.P256, x=binascii.unhexlify( 'fedaba748882050d1bef8ba992911898f554450952070aeb4788ca57d1df6bcc' ), y=binascii.unhexlify( 'ceaa8e7ff4751a4f81c70e98f1713378b0bd82a1414a2f493c1c9c0670f28d62' ), d=binascii.unhexlify( 'a2e4ed4f2e21842999b0e9ebdaad7465efd5c29bd5761f5c20880f9d9c3b122a' ), ) print('Sender Private Key: {}'.format( encode_diagnostic(cbor2.loads(sender_key.encode())))) sender_public = EC2Key(crv=sender_key.crv, x=sender_key.x, y=sender_key.y) # 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() 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 = EncMessage( phdr={ headers.Algorithm: algorithms.A256GCM, }, uhdr={ headers.IV: iv, }, payload=content_plaintext, recipients=[ KeyAgreementWithKeyWrap( uhdr={ headers.Algorithm: algorithms.EcdhEsA256KW, headers.KID: private_key.kid, headers.EphemeralKey: sender_public, }, payload=cek.k, ), ], # Non-encoded parameters external_aad=ext_aad_enc, ) recip = msg_obj.recipients[0] recip.key = sender_key recip.local_attrs = { headers.StaticKey: private_key, } # COSE internal structure cose_struct_enc = msg_obj._enc_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_ciphertext = message_dec[2] message_dec[2] = None self._print_message(message_dec, recipient_idx=3) print('Ciphertext: {}'.format(encode_diagnostic(content_ciphertext))) 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.BCB, 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_ciphertext decode_obj = EncMessage.from_cose_obj(message_dec) decode_obj.external_aad = ext_aad_enc recip = decode_obj.recipients[0] recip.key = private_key decode_plaintext = decode_obj.decrypt(recipient=recip) print('Loopback plaintext:', encode_diagnostic(decode_plaintext)) self.assertEqual(content_plaintext, decode_plaintext) print('Loopback CEK:', encode_diagnostic(cbor2.loads(decode_obj.key.encode()))) self.assertEqual(cek.k, decode_obj.key.k) target_dec[4] = content_ciphertext target_enc = cbor2.dumps(target_dec) bundle = self._assemble_bundle([prim_enc, bpsec_enc, target_enc]) print('Total bundle: {}'.format(encode_diagnostic(bundle)))
def test_ec2_key_construction(crv): key = EC2Key(crv=crv, x=os.urandom(32), y=os.urandom(32), d=os.urandom(32)) assert _is_valid_ec2_key(key)
def test_ec2_key_construction(crv, x, y, d): key = EC2Key(crv=crv, x=x, y=y, d=d) assert _is_valid_ec2_key(key)
def test_fail_with_d_and_missing_public_y_values(crv, x, d): key = EC2Key(crv=crv, x=x, d=d) assert key.y
def test_with_d_and_missing_public_x_values(crv, y, d): key = EC2Key(crv=crv, y=y, d=d) assert key.x
def test_fail_on_missing_key_values(crv): with pytest.raises(CoseInvalidKey) as excinfo: _ = EC2Key(crv=crv) assert "Either the public values or the private value must be specified" in str( excinfo.value)
def test_fail_on_missing_public_x_values(crv, y): with pytest.raises(CoseInvalidKey) as excinfo: _ = EC2Key(crv=crv, y=y) assert "Missing public coordinate X" in str(excinfo.value)