def _dgc(config_env: Dict) -> Sign1Message: if COSE in config_env.keys(): cbor_bytes = unhexlify(config_env[COSE]) cbor_object = loads(cbor_bytes, object_hook=_object_hook_e) if isinstance(cbor_object, CBORTag): # Tagged Cose Object if isinstance(cbor_object.value, CBORTag): # Double Tagged Cose Object decoded = Sign1Message.from_cose_obj(cbor_object.value.value) else: decoded = Sign1Message.decode(cbor_bytes) else: # Un-tagged Cose Object decoded = Sign1Message.from_cose_obj(cbor_object) return decoded
def test_simple_sign1message(): msg = Sign1Message( phdr={Algorithm: EdDSA, KID: b'kid2'}, payload='signed message'.encode('utf-8') ) assert str(msg) == "<COSE_Sign1: [{'Algorithm': 'EdDSA', 'KID': b'kid2'}, {}, b'signe' ... (14 B), b'' ... (0 B)]>" cose_key = { KpKty: KtyOKP, OKPKpCurve: Ed25519, KpKeyOps: [SignOp, VerifyOp], OKPKpD: unhexlify(b'9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60'), OKPKpX: unhexlify(b'd75a980182b10ab7d54bfed3c964073a0ee172f3daa62325af021a68f707511a')} cose_key = CoseKey.from_dict(cose_key) assert str(cose_key) == "<COSE_Key(OKPKey): {'OKPKpD': \"b'\\\\x9da\\\\xb1\\\\x9d\\\\xef' ... (32 B)\"," \ " 'OKPKpX': \"b'\\\\xd7Z\\\\x98\\\\x01\\\\x82' ... (32 B)\"," \ " 'OKPKpCurve': 'Ed25519', 'KpKty': 'KtyOKP', 'KpKeyOps': ['SignOp', 'VerifyOp']}>" msg.key = cose_key encoded = msg.encode() assert hexlify(encoded) == b'd28449a2012704446b696432a04e7369676e6564206d6573736167655840cc87665ffd3' \ b'fa33d96f3b606fcedeaef839423221872d0bfa196e069a189a607c2284924c3abb80e94' \ b'2466cd300cc5d18fe4e5ea1f3ebdb62ef8419109447d03' decoded = CoseMessage.decode(encoded) assert str(decoded) == "<COSE_Sign1: [{'Algorithm': 'EdDSA', 'KID': b'kid2'}, {}, b'signe' ... (14 B), " \ "b'\\xcc\\x87f_\\xfd' ... (64 B)]>" decoded.key = cose_key assert decoded.verify_signature() assert decoded.payload == b'signed message'
def _signature_or_mac(self, mac: bytes, transcript: bytes, aad_cb: Callable[..., bytes]) -> bytes: if not self.is_static_dh(self.role): cose_sign = Sign1Message( phdr=self.cred_id, uhdr={headers.Algorithm: self.cipher_suite.sign_alg}, payload=mac, key=self.auth_key, external_aad=self._external_aad(self.cred, transcript, aad_cb)) return cose_sign.compute_signature() else: return mac
def _verify_signature_or_mac2(self, signature_or_mac2: bytes) -> bool: mac_2 = self._mac(self.cred_idr, self.remote_cred, self._hkdf2, 'K_2m', 16, 'IV_2m', 13, self._th2_input, self._prk3e2m, self.aad2_cb) if not self.is_static_dh(self.remote_role): external_aad = self._external_aad(self.remote_cred, self._th2_input, self.aad2_cb) cose_sign = Sign1Message( phdr=self.cred_idr, uhdr={headers.Algorithm: self.cipher_suite.sign_alg}, payload=mac_2, external_aad=external_aad) # FIXME peeking into internals (probably best resolved at pycose level) cose_sign.key = self.remote_authkey cose_sign._signature = signature_or_mac2 return cose_sign.verify_signature() else: return signature_or_mac2 == mac_2
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(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 apply_bib(self, ctr): if not self._priv_key: LOGGER.warning('No private key') return addl_protected_map = {} addl_unprotected = {} aad_scope = 0x3 target_block_nums = [ blk.block_num for blk in ctr.bundle.blocks if blk.type_code in self._config.integrity_for_blocks ] if not target_block_nums: LOGGER.warning('No target blocks have matching type') return x5chain = [] for cert in self._cert_chain: x5chain.append(cert.public_bytes(serialization.Encoding.DER)) if self._config.integrity_include_chain: addl_protected_map[headers.X5chain.identifier] = X5Chain( x5chain).encode() # A little switcharoo to avoid cached overload_fields on `data` bib = CanonicalBlock( type_code=BlockIntegrityBlock._overload_fields[CanonicalBlock] ['type_code'], block_num=ctr.get_block_num(), crc_type=AbstractBlock.CrcType.CRC32, ) bib_data = BlockIntegrityBlock( targets=target_block_nums, context_id=BPSEC_COSE_CONTEXT_ID, context_flags=(AbstractSecurityBlock.Flag.PARAMETERS_PRESENT), source=self._config.node_id, parameters=[ TypeValuePair(type_code=5, value=aad_scope), ], ) # Inject optional additional headers addl_protected = cbor2.dumps( addl_protected_map) if addl_protected_map else b'' if addl_protected: bib_data.parameters.append( TypeValuePair(type_code=3, value=addl_protected)) if addl_unprotected: bib_data.parameters.append( TypeValuePair(type_code=4, value=addl_unprotected)) try: cose_key = self.extract_cose_key(self._priv_key) except Exception as err: LOGGER.error('Cannot handle private key: %s', repr(err)) return phdr = { headers.Algorithm: cose_key.alg, } uhdr = { headers.X5t: X5T.from_certificate(algorithms.Sha256, x5chain[0]).encode(), } # Sign each target with one result per target_result = [] for blk_num in bib_data.getfieldval('targets'): target_blk = ctr.block_num(blk_num) target_blk.ensure_block_type_specific_data() target_plaintext = target_blk.getfieldval('btsd') ext_aad_enc = CoseContext.get_bpsec_cose_aad( ctr, target_blk, bib, aad_scope, addl_protected) LOGGER.debug('Signing target %d AAD %s payload %s', blk_num, encode_diagnostic(ext_aad_enc), encode_diagnostic(target_plaintext)) msg_obj = Sign1Message( phdr=phdr, uhdr=uhdr, payload=target_plaintext, # Non-encoded parameters external_aad=ext_aad_enc, key=cose_key) LOGGER.debug('Signing with COSE key %s', repr(cose_key)) msg_enc = msg_obj.encode(tag=False) # detach payload msg_dec = cbor2.loads(msg_enc) msg_dec[2] = None msg_enc = cbor2.dumps(msg_dec) LOGGER.debug('Sending COSE message %s', encode_diagnostic(msg_dec)) target_result.append( TypeValuePair(type_code=msg_obj.cbor_tag, value=msg_enc)) # One result per target bib_data.setfieldval( 'results', [TargetResultList(results=[result]) for result in target_result]) bib.add_payload(bib_data) ctr.add_block(bib)
def test_unknown_header_attribute_encoding_decoding(): msg = Enc0Message(phdr={ Algorithm: AESCCM1664128, "Custom-Header-Attr1": 7879 }, uhdr={ KID: 8, IV: unhexlify(b'00000000000000000000000000'), "Custom-Header-Attr2": 879 }) msg.key = SymmetricKey.generate_key(key_len=16) assert "Custom-Header-Attr1" in msg.phdr assert "Custom-Header-Attr2" in msg.uhdr msg = msg.encode() msg_decoded = CoseMessage.decode(msg) assert "Custom-Header-Attr1" in msg_decoded.phdr assert "Custom-Header-Attr2" in msg_decoded.uhdr msg = EncMessage(phdr={ Algorithm: AESCCM1664128, "Custom-Header-Attr1": 7879 }, uhdr={ KID: 8, IV: unhexlify(b'00000000000000000000000000'), "Custom-Header-Attr2": 878 }, recipients=[ DirectEncryption(uhdr={ Algorithm: Direct, "Custom-Header-Attr3": 9999 }) ]) msg.key = SymmetricKey.generate_key(key_len=16) assert "Custom-Header-Attr1" in msg.phdr assert "Custom-Header-Attr2" in msg.uhdr assert "Custom-Header-Attr3" in msg.recipients[0].uhdr msg = msg.encode() msg_decoded = CoseMessage.decode(msg) assert "Custom-Header-Attr1" in msg_decoded.phdr assert "Custom-Header-Attr2" in msg_decoded.uhdr assert "Custom-Header-Attr3" in msg_decoded.recipients[0].uhdr msg = Mac0Message(phdr={ Algorithm: HMAC256, "Custom-Header-Attr1": 7879 }, uhdr={ KID: 8, IV: unhexlify(b'00000000000000000000000000'), "Custom-Header-Attr2": 878 }) msg.key = SymmetricKey.generate_key(key_len=16) assert "Custom-Header-Attr1" in msg.phdr assert "Custom-Header-Attr2" in msg.uhdr msg = msg.encode() msg_decoded = CoseMessage.decode(msg) assert "Custom-Header-Attr1" in msg_decoded.phdr assert "Custom-Header-Attr2" in msg_decoded.uhdr msg = MacMessage(phdr={ Algorithm: HMAC256, "Custom-Header-Attr1": 7879 }, uhdr={ KID: 8, IV: unhexlify(b'00000000000000000000000000'), "Custom-Header-Attr2": 878 }, recipients=[ DirectEncryption(uhdr={ Algorithm: Direct, "Custom-Header-Attr3": 9999 }) ]) msg.key = SymmetricKey.generate_key(key_len=16) assert "Custom-Header-Attr1" in msg.phdr assert "Custom-Header-Attr2" in msg.uhdr assert "Custom-Header-Attr3" in msg.recipients[0].uhdr msg = msg.encode() msg_decoded = CoseMessage.decode(msg) assert "Custom-Header-Attr1" in msg_decoded.phdr assert "Custom-Header-Attr2" in msg_decoded.uhdr assert "Custom-Header-Attr3" in msg_decoded.recipients[0].uhdr msg = SignMessage(phdr={"Custom-Header-Attr1": 7879}, uhdr={ KID: 8, IV: unhexlify(b'00000000000000000000000000'), "Custom-Header-Attr2": 878 }, signers=[ CoseSignature(phdr={ Algorithm: Es256, "Custom-Header-Attr3": 9999 }, key=EC2Key.generate_key(crv=P256)) ]) assert "Custom-Header-Attr1" in msg.phdr assert "Custom-Header-Attr2" in msg.uhdr msg = msg.encode() msg_decoded = CoseMessage.decode(msg) assert "Custom-Header-Attr1" in msg_decoded.phdr assert "Custom-Header-Attr2" in msg_decoded.uhdr assert "Custom-Header-Attr3" in msg_decoded.signers[0].phdr msg = Sign1Message(phdr={ Algorithm: Es256, "Custom-Header-Attr1": 7879 }, uhdr={ KID: 8, "Custom-Header-Attr2": 878 }) msg.key = EC2Key.generate_key(crv=P256) assert "Custom-Header-Attr1" in msg.phdr assert "Custom-Header-Attr2" in msg.uhdr msg = msg.encode() msg_decoded = CoseMessage.decode(msg) assert "Custom-Header-Attr1" in msg_decoded.phdr assert "Custom-Header-Attr2" in msg_decoded.uhdr
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(self): print('\nTest: ' + __name__ + '.' + type(self).__name__) # 1024-bit key private_key = RSAKey( n=binascii.unhexlify( b'b0b5fd85f52c91844007443c9f9371980025f76d51fc9c67681231da610cb291ba637ce813bffdb2e9c653258607389ec97dad3db295fded67744ed620707db36804e74e56a494030a73608fc8d92f2f0578d2d85cc201ef0ff22d7835d2d147d3b90a6884276235a01c2be99dfc597f79554362fc1eb03639cac5ccaddb2925' ), e=binascii.unhexlify(b'010001'), d=binascii.unhexlify( b'9b5d26ad6445ef1aab80b809e4f329684e9912d556c4166f041d1b1fb93c04b4037ffd0dbe6f8a8a86e70bab6e0f6344983a9ada27ed9ff7de816fdeeb5e7be48e607ce5fda4581ca6338a9e019fb3689b28934192b6a190cdda910abb5a86a2f7b6f9cd5011049d8de52ddfef73aa06df401c55623ec196720f54920deb4f01' ), p=binascii.unhexlify( b'db22d94e7784a27b568cbf985307ea8d6430ff6b88c18a7086fd4f57a326572f2250c39e48a6f8e2201661c2dfe12c7386835b649714d050aa36123ec3d00e75' ), q=binascii.unhexlify( b'ce7016adc5f326b7520397c5978ee2f50e69279983d54c5d76f05bcd61de0879d7056c923540dff9cbae95dcc0e5e86b52b3c902dc9669c8021c69557effb9f1' ), dp=binascii.unhexlify( b'6a6fcaccea106a3b2e16bf18e57b7ad9a2488a4758ed68a8af686a194f0d585b7477760c738d6665aee0302bcf4237ad0530d83b4b86b887f5a4bdc7eea427e1' ), dq=binascii.unhexlify( b'28a4cae245b1dcb285142e027a1768b9c4af915b59285a93a0422c60e05edd9e57663afd023d169bd0ad3bd62da8563d231840802ebbf271ad70b8905ba3af91' ), qinv=binascii.unhexlify( b'07b5a61733896270a6bd2bb1654194c54e2bc0e061b543a4ed9fa73c4bc79c87148aa92a451c4ab8262b6377a9c7b97f869160ca6f5d853ee4b65f4f92865ca3' ), optional_params={ keyparam.KpKid: b'ExampleRSA', 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.Ps256, }, 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 verify_attestation_doc(attestation_doc, root_cert_pem = None, bech32hrp = "crocnclconspub"): """ Verify the attestation document If invalid, raise an exception """ # Decode CBOR attestation document data = cbor2.loads(attestation_doc) # Load and decode document payload doc = data[2] doc_obj = cbor2.loads(doc) # https://github.com/aws/aws-nitro-enclaves-nsm-api/blob/main/docs/attestation_process.md # 3.2 if 'module_id' not in doc_obj or len(doc_obj["module_id"]) == 0: raise Exception("module_id error") if 'digest' not in doc_obj or doc_obj["digest"] != "SHA384": raise Exception("digest error") if 'timestamp' not in doc_obj or doc_obj["timestamp"] == 0: raise Exception("timestamp error") time = datetime.fromtimestamp(doc_obj["timestamp"] / 1000) print(f"timestamp: {time}") # PCRs are printed for verification below if 'pcrs' not in doc_obj or len(doc_obj["pcrs"]) > 32 or len(doc_obj["pcrs"]) == 0: raise Exception("pcrs error") if 'certificate' not in doc_obj: raise Exception("certificate error") if 'cabundle' not in doc_obj or len(doc_obj["cabundle"]) == 0: raise Exception("cabundle error") # we also expect user data if 'user_data' not in doc_obj: raise Exception("user_data error") user_data = json.loads(doc_obj['user_data']) if 'pubkey' not in user_data or 'key_id' not in user_data: raise Exception("user_data error") key_id = base64.b64decode(user_data["key_id"]) pubkey = base64.b64decode(user_data["pubkey"]) pubkeyb64 = user_data["pubkey"] pubkeyb32 = bech32_encode(bech32hrp, list(pubkey)) print("*** VERIFY user_data below (used AWS KMS key and generated pubkey) ***") print(f"AWS KMS key id: {key_id}") print(f"validator pubkey (base64): {pubkeyb64}") print(f"validator pubkey (bech32): {pubkeyb32}") # Get PCRs from attestation document document_pcrs_arr = doc_obj['pcrs'] ########################### # Part 1: Validating PCRs # ########################### print("*** VERIFY PCRs below are complete and correct ***") for index in document_pcrs_arr.keys(): # Get PCR hexcode doc_pcr = document_pcrs_arr[index].hex() print(f"PCR{index}: {doc_pcr}") ################################ # Part 2: Validating signature # ################################ # Get signing certificate from attestation document cert = crypto.load_certificate(crypto.FILETYPE_ASN1, doc_obj['certificate']) # Get the key parameters from the cert public key cert_public_numbers = cert.get_pubkey().to_cryptography_key().public_numbers() x = cert_public_numbers.x y = cert_public_numbers.y curve = cert_public_numbers.curve if curve != P384: Exception("Incorrect curve") x = long_to_bytes(x) y = long_to_bytes(y) # Create the EC2 key from public key parameters key = EC2(x = x, y = y, crv = P384) # Get the protected header from attestation document phdr = cbor2.loads(data[0]) # Construct the Sign1 message msg = Sign1Message(phdr = phdr, uhdr = data[1], payload = doc, key = key) msg._signature = data[3] # Verify the signature using the EC2 key if not msg.verify_signature(): raise Exception("Wrong signature") ############################################## # Part 3: Validating signing certificate PKI # ############################################## if root_cert_pem is not None: # Create an X509Store object for the CA bundles store = crypto.X509Store() # Create the CA cert object from PEM string, and store into X509Store _cert = crypto.load_certificate(crypto.FILETYPE_PEM, root_cert_pem) store.add_cert(_cert) # Get the CA bundle from attestation document and store into X509Store # Except the first certificate, which is the root certificate for _cert_binary in doc_obj['cabundle'][1:]: _cert = crypto.load_certificate(crypto.FILETYPE_ASN1, _cert_binary) store.add_cert(_cert) # Get the X509Store context store_ctx = crypto.X509StoreContext(store, cert) # Validate the certificate # If the cert is invalid, it will raise exception # assuming this checks all items specified in 3.2.3.1. Certificates validity store_ctx.verify_certificate() return