Add CCA attestation token verifier
This commit adds classes to verify CCA attestation token. A CCA
attestation token is defined by the document DEN0137 Realm Management
Monitor specification found here:
https://developer.arm.com/documentation/den0137/a/?lang=en
The commit
* Adds claim classes for CCA attestation token claims.
* Adds verifier classes CCA attestation token
* Adds CCA tokens to CLI scripts and change parameters to be possible to
specify multiple signing keys
* Adds sample cbor and yaml and key files to demonstrate CCA attestation
token
Change-Id: Ia88a5ce4af334143452e87d29975826165502409
Signed-off-by: Mate Toth-Pal <mate.toth-pal@arm.com>
diff --git a/iat-verifier/README.rst b/iat-verifier/README.rst
index cedaeca..a7c00ca 100644
--- a/iat-verifier/README.rst
+++ b/iat-verifier/README.rst
@@ -52,7 +52,7 @@
You can find an example in the ``sample`` directory.
The script will extract the COSE payload and make sure that it is a
-valid IAT (i.e. all mandatory fields are present, and all known
+valid IAT (i.e. all mandatory fields are present, and all known
fields have correct size/type):
.. code:: bash
@@ -66,7 +66,7 @@
::
- $ check_iat -t PSA-IoT-Profile1-token -k sample/key.pem sample/cbor/iat.cbor
+ $ check_iat -t PSA-IoT-Profile1-token --psa-iot-profile1-keyfile sample/key.pem sample/cbor/iat.cbor
Signature OK
Token format OK
@@ -121,7 +121,7 @@
.. code:: bash
- $ compile_token -t PSA-IoT-Profile1-token -k sample/key.pem sample/yaml/iat.yaml > sample_token.cbor
+ $ compile_token -t PSA-IoT-Profile1-token --psa-iot-profile1-keyfile sample/key.pem sample/yaml/iat.yaml > sample_token.cbor
*No validation* is performed as part of this, so there is no guarantee that a
valid IAT will be produced.
@@ -194,7 +194,7 @@
::
- $ check_iat -t PSA-IoT-Profile1-token -m mac -k sample/hmac.key sample/iat-hmac.cbor
+ $ check_iat -t PSA-IoT-Profile1-token -m mac --psa-iot-profile1-keyfile sample/hmac.key sample/iat-hmac.cbor
Signature OK
Token format OK
diff --git a/iat-verifier/iatverifier/attest_token_verifier.py b/iat-verifier/iatverifier/attest_token_verifier.py
index 3bd82ac..92b303a 100644
--- a/iat-verifier/iatverifier/attest_token_verifier.py
+++ b/iat-verifier/iatverifier/attest_token_verifier.py
@@ -292,6 +292,8 @@
msg = 'Unexpected {} claim: {}'
self.verifier.error(msg.format(self.get_claim_name(), key))
else:
+ msg = 'Unexpected {} claim: {}, skipping.'
+ self.verifier.warning(msg.format(self.get_claim_name(), key))
continue
try:
claim = claims[key]
@@ -427,7 +429,7 @@
return None
def get_claim_name(self=None):
- return None
+ return "TOKEN_CLAIM"
# This class inherits from NonVerifiedClaim. The actual claims in the token are
# checked by the AttestTokenRootClaims object owned by this verifier. The
diff --git a/iat-verifier/iatverifier/cca_claims.py b/iat-verifier/iatverifier/cca_claims.py
new file mode 100644
index 0000000..684ece0
--- /dev/null
+++ b/iat-verifier/iatverifier/cca_claims.py
@@ -0,0 +1,276 @@
+# -----------------------------------------------------------------------------
+# Copyright (c) 2022, Arm Limited. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+# -----------------------------------------------------------------------------
+
+from collections.abc import Iterable
+import logging
+
+from iatverifier.attest_token_verifier import AttestationClaim, NonVerifiedClaim, CompositeAttestClaim
+
+logger = logging.getLogger('iat-verifiers')
+
+class CCARealmChallengeClaim(AttestationClaim):
+ def __init__(self, verifier, expected_challenge_byte, necessity=AttestationClaim.MANDATORY):
+ super().__init__(verifier=verifier, necessity=necessity)
+ self.expected_challenge_byte = expected_challenge_byte
+
+ def get_claim_key(self=None):
+ return 10
+
+ def get_claim_name(self=None):
+ return 'CCA_REALM_CHALLENGE'
+
+ def verify(self, value):
+ self._validate_bytestring_length_equals(value, self.get_claim_name(), 64)
+ if self.expected_challenge_byte is not None:
+ for i, b in enumerate(value):
+ if b != self.expected_challenge_byte:
+ print (f'Challenge = {value}')
+ msg = 'Invalid CHALLENGE byte at {:d}: 0x{:02x} instead of 0x{:02x}'
+ self.verifier.error(msg.format(i, b, self.expected_challenge_byte))
+ break
+ self.verify_count += 1
+
+class CCARealmPersonalizationValue(AttestationClaim):
+ def get_claim_key(self=None):
+ return 44235
+
+ def get_claim_name(self=None):
+ return 'CCA_REALM_PERSONALIZATION_VALUE'
+
+ def verify(self, value):
+ self._validate_bytestring_length_equals(value, self.get_claim_name(), 64)
+ self.verify_count += 1
+
+class CCARealmInitialMeasurementClaim(AttestationClaim):
+ def get_claim_key(self=None):
+ return 44238
+
+ def get_claim_name(self=None):
+ return 'CCA_REALM_INITIAL_MEASUREMENT'
+
+ def verify(self, value):
+ self._check_type(self.get_claim_name(), value, bytes)
+ self.verify_count += 1
+
+class CCARealmExtensibleMeasurementsClaim(AttestationClaim):
+ def get_claim_key(self=None):
+ return 44239
+
+ def get_claim_name(self=None):
+ return 'CCA_REALM_EXTENSIBLE_MEASUREMENTS'
+
+ def verify(self, value):
+ min_measurement_count = 4
+ max_measurement_count = 4
+ if not isinstance(value, Iterable):
+ msg = 'Invalid {:s}: Value must be a list.'
+ self.verifier.error(msg.format(self.get_claim_name()))
+ if len(value) < min_measurement_count or len(value) > max_measurement_count:
+ msg = 'Invalid {:s}: Value must be a list of min {:d} elements and max {:d} elements.'
+ self.verifier.error(msg.format(self.get_claim_name(), min_measurement_count,
+ max_measurement_count))
+ for v in value:
+ self._validate_bytestring_length_one_of(v, self.get_claim_name()+f'[{str()}]', [32, 64])
+ self.verify_count += 1
+
+class CCARealmHashAlgorithmIdClaim(NonVerifiedClaim):
+ def get_claim_key(self=None):
+ return 44236
+
+ def get_claim_name(self=None):
+ return 'CCA_REALM_HASH_ALGM_ID'
+
+ @classmethod
+ def is_utf_8(cls):
+ return True
+
+class CCARealmPubKeyHashAlgorithmIdClaim(NonVerifiedClaim):
+ def get_claim_key(self=None):
+ return 44240
+
+ def get_claim_name(self=None):
+ return 'CCA_REALM_PUB_KEY_HASH_ALGO_ID'
+
+ @classmethod
+ def is_utf_8(cls):
+ return True
+
+class CCARealmPubKeyClaim(AttestationClaim):
+ def get_claim_key(self=None):
+ return 44237
+
+ def get_claim_name(self=None):
+ return 'CCA_REALM_PUB_KEY'
+
+ def verify(self, value):
+ self._validate_bytestring_length_equals(value, self.get_claim_name(), 97)
+ self.verify_count += 1
+
+class CCAAttestationProfileClaim(AttestationClaim):
+ def get_claim_key(self=None):
+ return 265
+
+ def get_claim_name(self=None):
+ return 'CCA_ATTESTATION_PROFILE'
+
+ @classmethod
+ def is_utf_8(cls):
+ return True
+
+ def verify(self, value):
+ expected_value = "http://arm.com/CCA-SSD/1.0.0"
+ self._check_type(self.get_claim_name(), value, str)
+ if value != expected_value:
+ msg = 'Invalid Attest profile "{}": must be "{}"'
+ self.verifier.error(msg.format(value, expected_value))
+ self.verify_count += 1
+
+class CCAPlatformChallengeClaim(AttestationClaim):
+ def get_claim_key(self=None):
+ return 10
+
+ def get_claim_name(self=None):
+ return 'CCA_PLATFORM_CHALLENGE'
+
+ def verify(self, value):
+ self._validate_bytestring_length_one_of(value, self.get_claim_name(), [32, 48, 64])
+ self.verify_count += 1
+
+class CCAPlatformImplementationIdClaim(AttestationClaim):
+ def get_claim_key(self=None):
+ return 2396
+
+ def get_claim_name(self=None):
+ return 'CCA_PLATFORM_IMPLEMENTATION_ID'
+
+ def verify(self, value):
+ self._validate_bytestring_length_equals(value, self.get_claim_name(), 32)
+ self.verify_count += 1
+
+class CCAPlatformInstanceIdClaim(AttestationClaim):
+ def get_claim_key(self=None):
+ return 256
+
+ def get_claim_name(self=None):
+ return 'CCA_PLATFORM_INSTANCE_ID'
+
+ def verify(self, value):
+ self._validate_bytestring_length_between(value, self.get_claim_name(), 7, 33)
+ if value[0] != 0x01:
+ msg = 'Invalid Instance ID first byte "0x{:02x}": must be "0x01"'
+ self.verifier.error(msg.format(value[0]))
+ self.verify_count += 1
+
+class CCAPlatformConfigClaim(AttestationClaim):
+ def get_claim_key(self=None):
+ return 2401
+
+ def get_claim_name(self=None):
+ return 'CCA_PLATFORM_CONFIG'
+
+ def verify(self, value):
+ self._check_type(self.get_claim_name(), value, bytes)
+ self.verify_count += 1
+
+class CCAPlatformLifecycleClaim(AttestationClaim):
+
+ SL_VALUES= [
+ ("UNKNOWN", 0x0000, 0x00ff),
+ ("ASSEMBLY_AND_TEST", 0x1000, 0x10ff),
+ ("CCA_PLATFORM_ROT_PROVISIONING", 0x2000, 0x20ff),
+ ("SECURED", 0x3000, 0x30ff),
+ ("NON_CCA_PLATFORM_ROT_DEBUG", 0x4000, 0x40ff),
+ ("RECOVERABLE_CCA_PLATFORM_ROT_DEBUG", 0x5000, 0x50ff),
+ ("DECOMMISSIONED", 0x6000, 0x60ff),
+ ]
+
+ def get_claim_key(self=None):
+ return 2395
+
+ def get_claim_name(self=None):
+ return 'CCA_PLATFORM_LIFECYCLE'
+
+ @classmethod
+ def parse_raw(cls, raw_value):
+ try:
+ int_value = int(raw_value, 16)
+ except ValueError:
+ # It is not a hex number. Try to decode known text values
+ pass
+ for text, min, max in cls.SL_VALUES:
+ if raw_value.startswith(text.lower()):
+ raw_value = raw_value[len(text):]
+ else:
+ continue
+ if len(raw_value) == 0:
+ return min
+ assert raw_value.startswith("_0x")
+ raw_value = raw_value[3:]
+ int_value = int(raw_value, 16)
+ assert(min <= int_value <= max)
+ return int_value
+ assert False
+
+ @classmethod
+ def get_formatted_value(cls, value):
+ for text, min, max in cls.SL_VALUES:
+ if min <= value <= max:
+ return f"{text}_{value:04x}".lower()
+ return f"INVALID_{value:04x}"
+
+ def verify(self, value):
+ self._check_type(self.get_claim_name, value, int)
+ value_valid = False
+ for _, min, max in CCAPlatformLifecycleClaim.SL_VALUES:
+ if min <= value <= max:
+ value_valid = True
+ break
+ if not value_valid:
+ msg = 'Invalid Platform Lifecycle claim "0x{:02x}"'
+ self.verifier.error(msg.format(value))
+ self.verify_count += 1
+
+class CCASwCompHashAlgIdClaim(NonVerifiedClaim):
+ def get_claim_key(self=None):
+ return 6
+
+ def get_claim_name(self=None):
+ return 'CCA_SW_COMPONENT_HASH_ID'
+
+ @classmethod
+ def is_utf_8(cls):
+ return True
+
+
+class CCAPlatformSwComponentsClaim(CompositeAttestClaim):
+ def get_claim_key(self=None):
+ return 2399
+
+ def get_claim_name(self=None):
+ return 'CCA_PLATFORM_SW_COMPONENTS'
+
+class CCAPlatformVerificationServiceClaim(NonVerifiedClaim):
+ def get_claim_key(self=None):
+ return 2400
+
+ def get_claim_name(self=None):
+ return 'CCA_PLATFORM_VERIFICATION_SERVICE'
+
+ @classmethod
+ def is_utf_8(cls):
+ return True
+
+class CCAPlatformHashAlgorithmIdClaim(NonVerifiedClaim):
+ def get_claim_key(self=None):
+ return 2402
+
+ def get_claim_name(self=None):
+ return 'CCA_PLATFORM_HASH_ALGO_ID'
+
+ @classmethod
+ def is_utf_8(cls):
+ return True
diff --git a/iat-verifier/iatverifier/cca_token_verifier.py b/iat-verifier/iatverifier/cca_token_verifier.py
new file mode 100644
index 0000000..cb61dcc
--- /dev/null
+++ b/iat-verifier/iatverifier/cca_token_verifier.py
@@ -0,0 +1,180 @@
+# -----------------------------------------------------------------------------
+# Copyright (c) 2022, Arm Limited. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+# -----------------------------------------------------------------------------
+
+from iatverifier.attest_token_verifier import AttestationTokenVerifier as Verifier
+from iatverifier.attest_token_verifier import AttestationClaim as Claim
+from iatverifier.cca_claims import CCARealmChallengeClaim, CCARealmPersonalizationValue
+from iatverifier.cca_claims import CCARealmHashAlgorithmIdClaim, CCARealmPubKeyClaim
+from iatverifier.cca_claims import CCARealmExtensibleMeasurementsClaim, CCARealmInitialMeasurementClaim
+from iatverifier.cca_claims import CCARealmPubKeyHashAlgorithmIdClaim, CCAPlatformHashAlgorithmIdClaim
+from iatverifier.cca_claims import CCAAttestationProfileClaim, CCAPlatformChallengeClaim
+from iatverifier.cca_claims import CCAPlatformImplementationIdClaim, CCAPlatformInstanceIdClaim
+from iatverifier.cca_claims import CCAPlatformConfigClaim, CCAPlatformLifecycleClaim
+from iatverifier.cca_claims import CCAPlatformSwComponentsClaim, CCAPlatformVerificationServiceClaim
+from iatverifier.cca_claims import CCASwCompHashAlgIdClaim, CCASwCompHashAlgIdClaim
+from iatverifier.psa_iot_profile1_token_claims import SWComponentTypeClaim, SwComponentVersionClaim
+from iatverifier.psa_iot_profile1_token_claims import MeasurementValueClaim, SignerIdClaim
+
+class CCATokenVerifier(Verifier):
+
+ def get_claim_key(self=None):
+ return 0xb5a101bf #TODO: some made up claim. Change claim indexing to use name
+ # and this should return None
+
+ def get_claim_name(self=None):
+ return 'CCA_TOKEN'
+
+ def _get_p_header(self):
+ None
+
+ def _get_wrapping_tag(self):
+ return 399
+
+ def _parse_p_header(self, msg):
+ pass
+
+ def __init__(self, *,
+ realm_token_method,
+ realm_token_cose_alg,
+ realm_token_key,
+ platform_token_method,
+ platform_token_cose_alg,
+ platform_token_key,
+ configuration):
+ verifier_claims = [
+ (CCARealmTokenVerifier, {'method': realm_token_method,
+ 'cose_alg': realm_token_cose_alg,
+ 'signing_key': realm_token_key,
+ 'configuration': configuration,
+ 'necessity': Claim.MANDATORY}),
+ (CCAPlatformTokenVerifier, {'method': platform_token_method,
+ 'cose_alg': platform_token_cose_alg,
+ 'signing_key': platform_token_key,
+ 'configuration': configuration,
+ 'necessity': Claim.MANDATORY}),
+ ]
+
+ # initialise the base part of the token
+ super().__init__(
+ claims=verifier_claims,
+ configuration=configuration,
+ necessity=Claim.MANDATORY,
+ method=Verifier.SIGN_METHOD_RAW,
+ cose_alg=Verifier.COSE_ALG_ES256,
+ signing_key=None)
+
+ @staticmethod
+ def check_cross_claim_requirements(verifier, claims):
+ pass
+
+
+class CCARealmTokenVerifier(Verifier):
+ def get_claim_key(self=None):
+ return 44241
+
+ def get_claim_name(self=None):
+ return 'CCA_REALM_DELEGATED_TOKEN'
+
+ def _get_p_header(self):
+ return {'alg': self._get_cose_alg()}
+
+ def _get_wrapping_tag(self):
+ return None
+
+ def _parse_p_header(self, msg):
+ alg = self._get_cose_alg()
+ try:
+ msg_alg = msg.protected_header['alg']
+ except KeyError:
+ raise ValueError('Missing alg from protected header (expected {})'.format(alg))
+ if alg != msg_alg:
+ raise ValueError('Unexpected alg in protected header (expected {} instead of {})'.format(alg, msg_alg))
+
+ def __init__(self, *, method, cose_alg, signing_key, configuration, necessity):
+ verifier_claims= [
+ (CCARealmChallengeClaim, {'verifier':self, 'expected_challenge_byte': None, 'necessity': Claim.MANDATORY}),
+ (CCARealmPersonalizationValue, {'verifier':self, 'necessity': Claim.MANDATORY}),
+ (CCARealmInitialMeasurementClaim, {'verifier':self, 'necessity': Claim.MANDATORY}),
+ (CCARealmExtensibleMeasurementsClaim, {'verifier':self, 'necessity': Claim.MANDATORY}),
+ (CCARealmHashAlgorithmIdClaim, {'verifier':self, 'necessity': Claim.MANDATORY}),
+ (CCARealmPubKeyHashAlgorithmIdClaim, {'verifier':self, 'necessity': Claim.MANDATORY}),
+ (CCARealmPubKeyClaim, {'verifier':self, 'necessity': Claim.MANDATORY}),
+ ]
+
+ # initialise the base part of the token
+ super().__init__(
+ claims=verifier_claims,
+ configuration=configuration,
+ necessity=necessity,
+ method=method,
+ cose_alg=cose_alg,
+ signing_key=signing_key)
+
+ @staticmethod
+ def check_cross_claim_requirements(verifier, claims):
+ pass
+
+
+class CCAPlatformTokenVerifier(Verifier):
+ def get_claim_key(self=None):
+ return 44234 #0xACCA
+
+ def get_claim_name(self=None):
+ return 'CCA_PLATFORM_TOKEN'
+
+ def _get_p_header(self):
+ return {'alg': self._get_cose_alg()}
+
+ def _get_wrapping_tag(self):
+ return None
+
+ def _parse_p_header(self, msg):
+ alg = self._get_cose_alg()
+ try:
+ msg_alg = msg.protected_header['alg']
+ except KeyError:
+ raise ValueError('Missing alg from protected header (expected {})'.format(alg))
+ if alg != msg_alg:
+ raise ValueError('Unexpected alg in protected header (expected {} instead of {})'.format(alg, msg_alg))
+
+ def __init__(self, *, method, cose_alg, signing_key, configuration, necessity):
+
+ # First prepare the claim hierarchy for this token
+
+ sw_component_claims = [
+ (SWComponentTypeClaim, {'verifier':self, 'necessity': Claim.OPTIONAL}),
+ (MeasurementValueClaim, {'verifier':self, 'necessity': Claim.MANDATORY}),
+ (SwComponentVersionClaim, {'verifier':self, 'necessity': Claim.OPTIONAL}),
+ (SignerIdClaim, {'verifier':self, 'necessity': Claim.MANDATORY}),
+ (CCASwCompHashAlgIdClaim, {'verifier':self, 'necessity': Claim.OPTIONAL}),
+ ]
+
+ verifier_claims = [
+ (CCAAttestationProfileClaim, {'verifier':self, 'necessity': Claim.MANDATORY}),
+ (CCAPlatformChallengeClaim, {'verifier':self, 'necessity': Claim.MANDATORY}),
+ (CCAPlatformImplementationIdClaim, {'verifier':self, 'necessity': Claim.MANDATORY}),
+ (CCAPlatformInstanceIdClaim, {'verifier':self, 'necessity': Claim.MANDATORY}),
+ (CCAPlatformConfigClaim, {'verifier':self, 'necessity': Claim.MANDATORY}),
+ (CCAPlatformLifecycleClaim, {'verifier':self, 'necessity': Claim.MANDATORY}),
+ (CCAPlatformSwComponentsClaim, {'verifier':self, 'claims': sw_component_claims, 'is_list': True, 'cross_claim_requirement_checker':None, 'necessity': Claim.MANDATORY}),
+ (CCAPlatformVerificationServiceClaim, {'verifier':self, 'necessity': Claim.OPTIONAL}),
+ (CCAPlatformHashAlgorithmIdClaim, {'verifier':self, 'necessity': Claim.MANDATORY}),
+ ]
+
+ # initialise the base part of the token
+ super().__init__(
+ claims=verifier_claims,
+ configuration=configuration,
+ necessity=necessity,
+ method=method,
+ cose_alg=cose_alg,
+ signing_key=signing_key)
+
+ @staticmethod
+ def check_cross_claim_requirements(verifier, claims):
+ pass
+
diff --git a/iat-verifier/iatverifier/util.py b/iat-verifier/iatverifier/util.py
index 12f16b8..bc78aad 100644
--- a/iat-verifier/iatverifier/util.py
+++ b/iat-verifier/iatverifier/util.py
@@ -90,11 +90,14 @@
return None
-def get_cose_alg_from_key(key):
+def get_cose_alg_from_key(key, default):
"""Extract the algorithm from the key if possible
Returns the signature algorithm ID defined by COSE
+ If key is None, default is returned.
"""
+ if key is None:
+ return default
if not hasattr(key, "curve"):
raise ValueError("Key has no curve specified in it.")
return _known_curves[key.curve.name]
diff --git a/iat-verifier/sample/cbor/cca_platform_token.cbor b/iat-verifier/sample/cbor/cca_platform_token.cbor
new file mode 100644
index 0000000..8d97a0c
--- /dev/null
+++ b/iat-verifier/sample/cbor/cca_platform_token.cbor
Binary files differ
diff --git a/iat-verifier/sample/cbor/cca_token.cbor b/iat-verifier/sample/cbor/cca_token.cbor
new file mode 100644
index 0000000..7f204a6
--- /dev/null
+++ b/iat-verifier/sample/cbor/cca_token.cbor
Binary files differ
diff --git a/iat-verifier/sample/cca_platform.pem b/iat-verifier/sample/cca_platform.pem
new file mode 100644
index 0000000..3645669
--- /dev/null
+++ b/iat-verifier/sample/cca_platform.pem
@@ -0,0 +1,6 @@
+-----BEGIN EC PRIVATE KEY-----
+MIGkAgEBBDCKwJDJlYafYawTWPArAhomq26zhiA6xzXXzphVU4uR90xEsNWAJD77
+eZopPcuqCJmgBwYFK4EEACKhZANiAAQhKGfFLiuVCLCkIKkFYPOU0t+qIb3XUU/x
+qQGv5+H3i7EdTmb4qKOK+navajHE3oyEzi2vyZZCWLU/rXGHdPRWINERsXboMY4R
+h9sCNaMY03ull/7oDg5MdioSvLPqbtQ=
+-----END EC PRIVATE KEY-----
diff --git a/iat-verifier/sample/cca_realm.pem b/iat-verifier/sample/cca_realm.pem
new file mode 100644
index 0000000..1a8d3a5
--- /dev/null
+++ b/iat-verifier/sample/cca_realm.pem
@@ -0,0 +1,6 @@
+-----BEGIN EC PRIVATE KEY-----
+MIGkAgEBBDAgEcfwPO5DJRduUk8DPAzh4hp25sGk8Lg5qh32Hg6KXIoFdA+bae+n
+6xpBhb0Rf2igBwYFK4EEACKhZANiAAR2+YgJG+WF7UGAGuz6uFhUjGMFfhaw5nYS
+C70NL5wp4FbF1BoBMOucIVF4mdwjFGso4bBivT6ksxX9IZ8cu1KMtudMpJvhZ3Nz
+T2GhymEDGyu/PZGPL5T/xCKOUJGVRK4=
+-----END EC PRIVATE KEY-----
\ No newline at end of file
diff --git a/iat-verifier/sample/yaml/cca_platform_token.yaml b/iat-verifier/sample/yaml/cca_platform_token.yaml
new file mode 100644
index 0000000..cdf3819
--- /dev/null
+++ b/iat-verifier/sample/yaml/cca_platform_token.yaml
@@ -0,0 +1,33 @@
+#-------------------------------------------------------------------------------
+# Copyright (c) 2022, Arm Limited. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+#-------------------------------------------------------------------------------
+
+cca_attestation_profile: http://arm.com/CCA-SSD/1.0.0
+cca_platform_challenge: !!binary BwYFBAMCAQAPDg0MCwoJCBcWFRQTEhEQHx4dHBsaGRg=
+cca_platform_implementation_id: !!binary f0VMRgIBAQAAAAAAAAAAAAMAPgABAAAAUFgAAAAAAAA=
+cca_platform_instance_id: !!binary AQcGBQQDAgEADw4NDAsKCQgXFhUUExIREB8eHRwbGhkY
+cca_platform_config: !!binary AQcGBQQDAgEADw4NDAsKCQgXFhUUExIREB8eHRwbGhkY
+cca_platform_lifecycle: secured_0x3003
+cca_platform_hash_algo_id: sha-256
+cca_platform_sw_components:
+ - sw_component_type: BL
+ signer_id: !!binary BwYFBAMCAQAPDg0MCwoJCBcWFRQTEhEQHx4dHBsaGRg=
+ sw_component_version: "3.4.2"
+ measurement_value: !!binary BwYFBAMCAQAPDg0MCwoJCBcWFRQTEhEQHx4dHBsaGRg=
+ cca_sw_component_hash_id: TF-M_SHA256MemPreXIP
+ - sw_component_type: M1
+ signer_id: !!binary BwYFBAMCAQAPDg0MCwoJCBcWFRQTEhEQHx4dHBsaGRg=
+ sw_component_version: "1.2"
+ measurement_value: !!binary BwYFBAMCAQAPDg0MCwoJCBcWFRQTEhEQHx4dHBsaGRg=
+ - sw_component_type: M2
+ signer_id: !!binary BwYFBAMCAQAPDg0MCwoJCBcWFRQTEhEQHx4dHBsaGRg=
+ sw_component_version: "1.2.3"
+ measurement_value: !!binary BwYFBAMCAQAPDg0MCwoJCBcWFRQTEhEQHx4dHBsaGRg=
+ - sw_component_type: M3
+ signer_id: !!binary BwYFBAMCAQAPDg0MCwoJCBcWFRQTEhEQHx4dHBsaGRg=
+ sw_component_version: "1"
+ measurement_value: !!binary BwYFBAMCAQAPDg0MCwoJCBcWFRQTEhEQHx4dHBsaGRg=
+cca_platform_verification_service: whatever.com
\ No newline at end of file
diff --git a/iat-verifier/sample/yaml/valid-cca-token.yaml b/iat-verifier/sample/yaml/valid-cca-token.yaml
new file mode 100644
index 0000000..9d1f354
--- /dev/null
+++ b/iat-verifier/sample/yaml/valid-cca-token.yaml
@@ -0,0 +1,55 @@
+#-------------------------------------------------------------------------------
+# Copyright (c) 2022, Arm Limited. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+#-------------------------------------------------------------------------------
+
+cca_platform_token:
+ cca_attestation_profile: http://arm.com/CCA-SSD/1.0.0
+ cca_platform_challenge: !!binary tZc8touqn8VVWHhrfsZ/aeQN9bpaqSHNDCf0BYegEeo=
+ cca_platform_implementation_id: !!binary f0VMRgIBAQAAAAAAAAAAAAMAPgABAAAAUFgAAAAAAAA=
+ cca_platform_instance_id: !!binary AQcGBQQDAgEADw4NDAsKCQgXFhUUExIREB8eHRwbGhkY
+ cca_platform_config: !!binary AQcGBQQDAgEADw4NDAsKCQgXFhUUExIREB8eHRwbGhkY
+ cca_platform_lifecycle: secured_0x3003
+ cca_platform_hash_algo_id: sha-256
+ cca_platform_sw_components:
+ - sw_component_type: BL
+ signer_id: !!binary BwYFBAMCAQAPDg0MCwoJCBcWFRQTEhEQHx4dHBsaGRg=
+ sw_component_version: "3.4.2"
+ measurement_value: !!binary BwYFBAMCAQAPDg0MCwoJCBcWFRQTEhEQHx4dHBsaGRg=
+ cca_sw_component_hash_id: sha-256
+ - sw_component_type: M1
+ signer_id: !!binary BwYFBAMCAQAPDg0MCwoJCBcWFRQTEhEQHx4dHBsaGRg=
+ sw_component_version: "1.2"
+ measurement_value: !!binary BwYFBAMCAQAPDg0MCwoJCBcWFRQTEhEQHx4dHBsaGRg=
+ - sw_component_type: M2
+ signer_id: !!binary BwYFBAMCAQAPDg0MCwoJCBcWFRQTEhEQHx4dHBsaGRg=
+ sw_component_version: "1.2.3"
+ measurement_value: !!binary BwYFBAMCAQAPDg0MCwoJCBcWFRQTEhEQHx4dHBsaGRg=
+ - sw_component_type: M3
+ signer_id: !!binary BwYFBAMCAQAPDg0MCwoJCBcWFRQTEhEQHx4dHBsaGRg=
+ sw_component_version: "1"
+ measurement_value: !!binary BwYFBAMCAQAPDg0MCwoJCBcWFRQTEhEQHx4dHBsaGRg=
+ cca_platform_verification_service: whatever.com
+cca_realm_delegated_token:
+ cca_realm_challenge: !!binary |
+ q6urq6urq6urq6urq6urq6urq6urq6urq6urq6urq6urq6urq6urq6urq6urq6urq6urq6urq6ur
+ q6urq6urqw==
+ cca_realm_hash_algm_id: sha-256
+ cca_realm_pub_key_hash_algo_id: sha-256
+ cca_realm_personalization_value: !!binary |
+ VGhlIHF1aWNrIGJyb3duIGZveCBqdW1wcyBvdmVyIDEzIGxhenkgZG9ncy5UaGUgcXVpY2sgYnJvd24gZm94IA==
+ cca_realm_pub_key: !!binary |
+ BHb5iAkb5YXtQYAa7Pq4WFSMYwV+FrDmdhILvQ0vnCngVsXUGgEw65whUXiZ3CMUayjhsGK9PqSzFf0hnxy7Uoy250ykm+Fnc3NPYaHKYQMbK789kY8vlP/EIo5QkZVErg==
+ cca_realm_initial_measurement: !!binary |
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
+ cca_realm_extensible_measurements:
+ - !!binary |
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
+ - !!binary |
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
+ - !!binary |
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
+ - !!binary |
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
diff --git a/iat-verifier/scripts/check_iat b/iat-verifier/scripts/check_iat
index 4778b39..e9adf48 100755
--- a/iat-verifier/scripts/check_iat
+++ b/iat-verifier/scripts/check_iat
@@ -13,9 +13,11 @@
import logging
import sys
+from iatverifier.attest_token_verifier import AttestationClaim as Claim
from iatverifier.util import recursive_bytes_to_strings, read_keyfile, get_cose_alg_from_key
from iatverifier.psa_iot_profile1_token_verifier import PSAIoTProfile1TokenVerifier
from iatverifier.attest_token_verifier import VerifierConfiguration, AttestationTokenVerifier
+from iatverifier.cca_token_verifier import CCATokenVerifier, CCAPlatformTokenVerifier
logger = logging.getLogger('iat-verify')
@@ -24,6 +26,8 @@
token_verifiers = {
"PSA-IoT-Profile1-token": PSAIoTProfile1TokenVerifier,
+ "CCA-token": CCATokenVerifier,
+ "CCA-plat-token": CCAPlatformTokenVerifier,
}
parser = argparse.ArgumentParser(
@@ -32,10 +36,18 @@
that the signature is valid, the token contian the required
fields, and those fields are in a valid format.
''')
- parser.add_argument('-k', '--keyfile',
- help='''
- Path to a file containing signing key in PEM format.
- ''')
+ parser.add_argument('--psa-iot-profile1-keyfile',
+ help='''Path to the key in PEM format that should be used to
+ verify the token. If this is not specified, the token signature
+ will not be checked.''')
+ parser.add_argument('--cca-platform-token-keyfile',
+ help='''Path to the key in PEM format that should be used to
+ verify the CCA platform token. If this is not specified, the
+ token signature will not be checked.''')
+ parser.add_argument('--cca-realm-token-keyfile',
+ help='''Path to the key in PEM format that should be used to
+ verify the CCA Realm token. If this is not specified, the
+ token signature will not be checked.''')
parser.add_argument('tokenfile',
help='''
path to a file containing a signed IAT.
@@ -76,23 +88,54 @@
else:
method = AttestationTokenVerifier.SIGN_METHOD_SIGN1
- key = read_keyfile(keyfile=args.keyfile, method=method)
-
- if args.method == 'mac':
- cose_alg = AttestationTokenVerifier.COSE_ALG_HS256
- else:
- if key is not None:
- cose_alg = get_cose_alg_from_key(key)
- else:
- cose_alg = AttestationTokenVerifier.COSE_ALG_ES256
+ key_checked = False
verifier_class = token_verifiers[args.token_type]
if verifier_class == PSAIoTProfile1TokenVerifier:
+ key_checked = args.psa_iot_profile1_keyfile
+ key = read_keyfile(keyfile=args.psa_iot_profile1_keyfile, method=method)
+ if method == AttestationTokenVerifier.SIGN_METHOD_SIGN1:
+ cose_alg = get_cose_alg_from_key(key, AttestationTokenVerifier.COSE_ALG_ES256)
+ else:
+ cose_alg = AttestationTokenVerifier.COSE_ALG_HS256
verifier = PSAIoTProfile1TokenVerifier(
method=method,
cose_alg=cose_alg,
signing_key=key,
configuration=config)
+ elif verifier_class == CCATokenVerifier:
+ if method != AttestationTokenVerifier.SIGN_METHOD_SIGN1:
+ logger.error('Only sign1 method is supported by this token type.\n\t'.format(verifier_class))
+ sys.exit(1)
+ key_checked = args.cca_platform_token_keyfile and args.cca_realm_token_keyfile
+ platform_token_key = read_keyfile(args.cca_platform_token_keyfile, method)
+ realm_token_key = read_keyfile(args.cca_realm_token_keyfile, method)
+ realm_token_method = AttestationTokenVerifier.SIGN_METHOD_SIGN1
+ platform_token_method = AttestationTokenVerifier.SIGN_METHOD_SIGN1
+ realm_token_cose_alg = get_cose_alg_from_key(
+ realm_token_key,
+ AttestationTokenVerifier.COSE_ALG_ES384)
+ platform_token_cose_alg = get_cose_alg_from_key(
+ platform_token_key,
+ AttestationTokenVerifier.COSE_ALG_ES384)
+ verifier = CCATokenVerifier(
+ realm_token_method=realm_token_method,
+ realm_token_cose_alg=realm_token_cose_alg,
+ realm_token_key=realm_token_key,
+ platform_token_method=platform_token_method,
+ platform_token_cose_alg=platform_token_cose_alg,
+ platform_token_key=platform_token_key,
+ configuration=config)
+ elif verifier_class == CCAPlatformTokenVerifier:
+ key_checked = args.cca_platform_token_keyfile
+ key = read_keyfile(args.cca_platform_token_keyfile, method)
+ cose_alg = get_cose_alg_from_key(key, AttestationTokenVerifier.COSE_ALG_ES384)
+ verifier = CCAPlatformTokenVerifier(
+ method=AttestationTokenVerifier.SIGN_METHOD_SIGN1,
+ cose_alg=cose_alg,
+ signing_key=key,
+ configuration=config,
+ necessity=None)
else:
logger.error(f'Invalid token type:{verifier_class}\n\t')
sys.exit(1)
@@ -104,11 +147,11 @@
verify=True,
check_p_header=args.check_protected_header,
lower_case_key=False)
- if args.keyfile:
+ if key_checked:
print('Signature OK')
print('Token format OK')
except ValueError as exc:
- logger.error(f'Could not extract IAT from COSE:\n\t{exc}')
+ logger.error(f'Token verification failed:\n\t{exc}')
sys.exit(1)
if args.print_iat:
diff --git a/iat-verifier/scripts/compile_token b/iat-verifier/scripts/compile_token
index 7fa9816..2783cf6 100755
--- a/iat-verifier/scripts/compile_token
+++ b/iat-verifier/scripts/compile_token
@@ -16,14 +16,16 @@
from iatverifier.util import read_token_map, convert_map_to_token, read_keyfile
from iatverifier.util import get_cose_alg_from_key
from iatverifier.psa_iot_profile1_token_verifier import PSAIoTProfile1TokenVerifier
-from iatverifier.attest_token_verifier import AttestationTokenVerifier
-
+from iatverifier.attest_token_verifier import AttestationTokenVerifier, VerifierConfiguration
+from iatverifier.cca_token_verifier import CCATokenVerifier, CCAPlatformTokenVerifier
if __name__ == '__main__':
logging.basicConfig(level=logging.INFO)
token_verifiers = {
"PSA-IoT-Profile1-token": PSAIoTProfile1TokenVerifier,
+ "CCA-token": CCATokenVerifier,
+ "CCA-plat-token": CCAPlatformTokenVerifier,
}
parser = argparse.ArgumentParser()
@@ -31,10 +33,18 @@
parser.add_argument('-o', '--outfile',
help='''Output file for the compiled token. If this is not
specified, the token will be written to standard output.''')
- parser.add_argument('-k', '--keyfile',
+ parser.add_argument('--psa-iot-profile1-keyfile',
help='''Path to the key in PEM format that should be used to
sign the token. If this is not specified, the token will be
unsigned.''')
+ parser.add_argument('--cca-platform-token-keyfile',
+ help='''Path to the key in PEM format that should be used to
+ sign the CCA platform token. If this is not specified,
+ the token will be unsigned.''')
+ parser.add_argument('--cca-realm-token-keyfile',
+ help='''Path to the key in PEM format that should be used to
+ sign the CCA Realm token. If this is not specified, the
+ token will be unsigned.''')
group = parser.add_mutually_exclusive_group()
parser.add_argument('-a', '--add-protected-header', action='store_true',
help='''
@@ -56,27 +66,60 @@
if args.hmac:
METHOD = AttestationTokenVerifier.SIGN_METHOD_MAC0
elif args.raw:
- if args.keyfile:
+ if args.psa_iot_profile1_keyfile:
raise ValueError('A keyfile cannot be specified with --raw.')
METHOD = AttestationTokenVerifier.SIGN_METHOD_RAW
else:
METHOD = AttestationTokenVerifier.SIGN_METHOD_SIGN1
- key = read_keyfile(args.keyfile, METHOD)
-
- COSE_ALG = None
- if args.hmac:
- COSE_ALG = AttestationTokenVerifier.COSE_ALG_HS256
- elif not args.raw:
- COSE_ALG = get_cose_alg_from_key(key)
+ configuration = VerifierConfiguration(strict=True, keep_going=False)
verifier_class = token_verifiers[args.token_type]
if verifier_class == PSAIoTProfile1TokenVerifier:
+ key = read_keyfile(args.psa_iot_profile1_keyfile, METHOD)
+ if METHOD == AttestationTokenVerifier.SIGN_METHOD_SIGN1:
+ cose_alg = get_cose_alg_from_key(
+ key,
+ AttestationTokenVerifier.COSE_ALG_ES256)
+ else:
+ cose_alg = AttestationTokenVerifier.COSE_ALG_HS256
verifier = PSAIoTProfile1TokenVerifier(
method=METHOD,
- cose_alg=COSE_ALG,
+ cose_alg=cose_alg,
signing_key=key,
- configuration=None)
+ configuration=configuration)
+ elif verifier_class == CCATokenVerifier:
+ if METHOD != AttestationTokenVerifier.SIGN_METHOD_SIGN1:
+ logging.error('Only sign1 method is supported by this token type.\n\t')
+ sys.exit(1)
+ platform_token_key = read_keyfile(args.cca_platform_token_keyfile, METHOD)
+ realm_token_key = read_keyfile(args.cca_realm_token_keyfile, METHOD)
+ realm_token_method = AttestationTokenVerifier.SIGN_METHOD_SIGN1
+ platform_token_method = AttestationTokenVerifier.SIGN_METHOD_SIGN1
+ realm_token_cose_alg = get_cose_alg_from_key(
+ realm_token_key,
+ AttestationTokenVerifier.COSE_ALG_ES384)
+ platform_token_cose_alg = get_cose_alg_from_key(
+ platform_token_key,
+ AttestationTokenVerifier.COSE_ALG_ES384)
+ verifier = CCATokenVerifier(
+ realm_token_method=realm_token_method,
+ realm_token_cose_alg=realm_token_cose_alg,
+ realm_token_key=realm_token_key,
+ platform_token_method=platform_token_method,
+ platform_token_cose_alg=platform_token_cose_alg,
+ platform_token_key=platform_token_key,
+ configuration=configuration)
+ elif verifier_class == CCAPlatformTokenVerifier:
+ key_checked = args.cca_platform_token_keyfile
+ key = read_keyfile(args.cca_platform_token_keyfile, METHOD)
+ cose_alg = get_cose_alg_from_key(key, AttestationTokenVerifier.COSE_ALG_ES384)
+ verifier = CCAPlatformTokenVerifier(
+ method=AttestationTokenVerifier.SIGN_METHOD_SIGN1,
+ cose_alg=cose_alg,
+ signing_key=key,
+ configuration=configuration,
+ necessity=None)
else:
logging.error(f'Invalid token type:{verifier_class}\n\t')
sys.exit(1)
diff --git a/iat-verifier/scripts/decompile_token b/iat-verifier/scripts/decompile_token
index b64fa59..58bc9cf 100755
--- a/iat-verifier/scripts/decompile_token
+++ b/iat-verifier/scripts/decompile_token
@@ -15,6 +15,7 @@
import yaml
from iatverifier.psa_iot_profile1_token_verifier import PSAIoTProfile1TokenVerifier
from iatverifier.attest_token_verifier import AttestationTokenVerifier
+from iatverifier.cca_token_verifier import CCATokenVerifier, CCAPlatformTokenVerifier
if __name__ == '__main__':
@@ -22,6 +23,8 @@
token_verifiers = {
"PSA-IoT-Profile1-token": PSAIoTProfile1TokenVerifier,
+ "CCA-token": CCATokenVerifier,
+ "CCA-plat-token": CCAPlatformTokenVerifier,
}
parser = argparse.ArgumentParser()
@@ -40,7 +43,29 @@
verifier = PSAIoTProfile1TokenVerifier(
method=AttestationTokenVerifier.SIGN_METHOD_SIGN1,
cose_alg=AttestationTokenVerifier.COSE_ALG_ES256,
- signing_key=None, configuration=None)
+ signing_key=None,
+ configuration=None)
+ elif verifier_class == CCATokenVerifier:
+ realm_token_method = AttestationTokenVerifier.SIGN_METHOD_SIGN1
+ platform_token_method = AttestationTokenVerifier.SIGN_METHOD_SIGN1
+ realm_token_cose_alg = AttestationTokenVerifier.COSE_ALG_ES384
+ platform_token_cose_alg = AttestationTokenVerifier.COSE_ALG_ES384
+ verifier = CCATokenVerifier(
+ realm_token_method=realm_token_method,
+ realm_token_cose_alg=realm_token_cose_alg,
+ realm_token_key=None,
+ platform_token_method=platform_token_method,
+ platform_token_cose_alg=platform_token_cose_alg,
+ platform_token_key=None,
+ configuration=None)
+ elif verifier_class == CCAPlatformTokenVerifier:
+ cose_alg = AttestationTokenVerifier.COSE_ALG_ES384
+ verifier = CCAPlatformTokenVerifier(
+ method=AttestationTokenVerifier.SIGN_METHOD_SIGN1,
+ cose_alg=cose_alg,
+ signing_key=None,
+ configuration=None,
+ necessity=None)
else:
logging.error(f'Invalid token type:{verifier_class}\n\t')
sys.exit(1)
diff --git a/iat-verifier/tests/data/cca_platform.pem b/iat-verifier/tests/data/cca_platform.pem
new file mode 100644
index 0000000..3645669
--- /dev/null
+++ b/iat-verifier/tests/data/cca_platform.pem
@@ -0,0 +1,6 @@
+-----BEGIN EC PRIVATE KEY-----
+MIGkAgEBBDCKwJDJlYafYawTWPArAhomq26zhiA6xzXXzphVU4uR90xEsNWAJD77
+eZopPcuqCJmgBwYFK4EEACKhZANiAAQhKGfFLiuVCLCkIKkFYPOU0t+qIb3XUU/x
+qQGv5+H3i7EdTmb4qKOK+navajHE3oyEzi2vyZZCWLU/rXGHdPRWINERsXboMY4R
+h9sCNaMY03ull/7oDg5MdioSvLPqbtQ=
+-----END EC PRIVATE KEY-----
diff --git a/iat-verifier/tests/data/cca_platform_token.yaml b/iat-verifier/tests/data/cca_platform_token.yaml
new file mode 100644
index 0000000..cdf3819
--- /dev/null
+++ b/iat-verifier/tests/data/cca_platform_token.yaml
@@ -0,0 +1,33 @@
+#-------------------------------------------------------------------------------
+# Copyright (c) 2022, Arm Limited. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+#-------------------------------------------------------------------------------
+
+cca_attestation_profile: http://arm.com/CCA-SSD/1.0.0
+cca_platform_challenge: !!binary BwYFBAMCAQAPDg0MCwoJCBcWFRQTEhEQHx4dHBsaGRg=
+cca_platform_implementation_id: !!binary f0VMRgIBAQAAAAAAAAAAAAMAPgABAAAAUFgAAAAAAAA=
+cca_platform_instance_id: !!binary AQcGBQQDAgEADw4NDAsKCQgXFhUUExIREB8eHRwbGhkY
+cca_platform_config: !!binary AQcGBQQDAgEADw4NDAsKCQgXFhUUExIREB8eHRwbGhkY
+cca_platform_lifecycle: secured_0x3003
+cca_platform_hash_algo_id: sha-256
+cca_platform_sw_components:
+ - sw_component_type: BL
+ signer_id: !!binary BwYFBAMCAQAPDg0MCwoJCBcWFRQTEhEQHx4dHBsaGRg=
+ sw_component_version: "3.4.2"
+ measurement_value: !!binary BwYFBAMCAQAPDg0MCwoJCBcWFRQTEhEQHx4dHBsaGRg=
+ cca_sw_component_hash_id: TF-M_SHA256MemPreXIP
+ - sw_component_type: M1
+ signer_id: !!binary BwYFBAMCAQAPDg0MCwoJCBcWFRQTEhEQHx4dHBsaGRg=
+ sw_component_version: "1.2"
+ measurement_value: !!binary BwYFBAMCAQAPDg0MCwoJCBcWFRQTEhEQHx4dHBsaGRg=
+ - sw_component_type: M2
+ signer_id: !!binary BwYFBAMCAQAPDg0MCwoJCBcWFRQTEhEQHx4dHBsaGRg=
+ sw_component_version: "1.2.3"
+ measurement_value: !!binary BwYFBAMCAQAPDg0MCwoJCBcWFRQTEhEQHx4dHBsaGRg=
+ - sw_component_type: M3
+ signer_id: !!binary BwYFBAMCAQAPDg0MCwoJCBcWFRQTEhEQHx4dHBsaGRg=
+ sw_component_version: "1"
+ measurement_value: !!binary BwYFBAMCAQAPDg0MCwoJCBcWFRQTEhEQHx4dHBsaGRg=
+cca_platform_verification_service: whatever.com
\ No newline at end of file
diff --git a/iat-verifier/tests/data/cca_realm.pem b/iat-verifier/tests/data/cca_realm.pem
new file mode 100644
index 0000000..1a8d3a5
--- /dev/null
+++ b/iat-verifier/tests/data/cca_realm.pem
@@ -0,0 +1,6 @@
+-----BEGIN EC PRIVATE KEY-----
+MIGkAgEBBDAgEcfwPO5DJRduUk8DPAzh4hp25sGk8Lg5qh32Hg6KXIoFdA+bae+n
+6xpBhb0Rf2igBwYFK4EEACKhZANiAAR2+YgJG+WF7UGAGuz6uFhUjGMFfhaw5nYS
+C70NL5wp4FbF1BoBMOucIVF4mdwjFGso4bBivT6ksxX9IZ8cu1KMtudMpJvhZ3Nz
+T2GhymEDGyu/PZGPL5T/xCKOUJGVRK4=
+-----END EC PRIVATE KEY-----
\ No newline at end of file
diff --git a/iat-verifier/tests/data/valid-cca-token.yaml b/iat-verifier/tests/data/valid-cca-token.yaml
new file mode 100644
index 0000000..9d1f354
--- /dev/null
+++ b/iat-verifier/tests/data/valid-cca-token.yaml
@@ -0,0 +1,55 @@
+#-------------------------------------------------------------------------------
+# Copyright (c) 2022, Arm Limited. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+#-------------------------------------------------------------------------------
+
+cca_platform_token:
+ cca_attestation_profile: http://arm.com/CCA-SSD/1.0.0
+ cca_platform_challenge: !!binary tZc8touqn8VVWHhrfsZ/aeQN9bpaqSHNDCf0BYegEeo=
+ cca_platform_implementation_id: !!binary f0VMRgIBAQAAAAAAAAAAAAMAPgABAAAAUFgAAAAAAAA=
+ cca_platform_instance_id: !!binary AQcGBQQDAgEADw4NDAsKCQgXFhUUExIREB8eHRwbGhkY
+ cca_platform_config: !!binary AQcGBQQDAgEADw4NDAsKCQgXFhUUExIREB8eHRwbGhkY
+ cca_platform_lifecycle: secured_0x3003
+ cca_platform_hash_algo_id: sha-256
+ cca_platform_sw_components:
+ - sw_component_type: BL
+ signer_id: !!binary BwYFBAMCAQAPDg0MCwoJCBcWFRQTEhEQHx4dHBsaGRg=
+ sw_component_version: "3.4.2"
+ measurement_value: !!binary BwYFBAMCAQAPDg0MCwoJCBcWFRQTEhEQHx4dHBsaGRg=
+ cca_sw_component_hash_id: sha-256
+ - sw_component_type: M1
+ signer_id: !!binary BwYFBAMCAQAPDg0MCwoJCBcWFRQTEhEQHx4dHBsaGRg=
+ sw_component_version: "1.2"
+ measurement_value: !!binary BwYFBAMCAQAPDg0MCwoJCBcWFRQTEhEQHx4dHBsaGRg=
+ - sw_component_type: M2
+ signer_id: !!binary BwYFBAMCAQAPDg0MCwoJCBcWFRQTEhEQHx4dHBsaGRg=
+ sw_component_version: "1.2.3"
+ measurement_value: !!binary BwYFBAMCAQAPDg0MCwoJCBcWFRQTEhEQHx4dHBsaGRg=
+ - sw_component_type: M3
+ signer_id: !!binary BwYFBAMCAQAPDg0MCwoJCBcWFRQTEhEQHx4dHBsaGRg=
+ sw_component_version: "1"
+ measurement_value: !!binary BwYFBAMCAQAPDg0MCwoJCBcWFRQTEhEQHx4dHBsaGRg=
+ cca_platform_verification_service: whatever.com
+cca_realm_delegated_token:
+ cca_realm_challenge: !!binary |
+ q6urq6urq6urq6urq6urq6urq6urq6urq6urq6urq6urq6urq6urq6urq6urq6urq6urq6urq6ur
+ q6urq6urqw==
+ cca_realm_hash_algm_id: sha-256
+ cca_realm_pub_key_hash_algo_id: sha-256
+ cca_realm_personalization_value: !!binary |
+ VGhlIHF1aWNrIGJyb3duIGZveCBqdW1wcyBvdmVyIDEzIGxhenkgZG9ncy5UaGUgcXVpY2sgYnJvd24gZm94IA==
+ cca_realm_pub_key: !!binary |
+ BHb5iAkb5YXtQYAa7Pq4WFSMYwV+FrDmdhILvQ0vnCngVsXUGgEw65whUXiZ3CMUayjhsGK9PqSzFf0hnxy7Uoy250ykm+Fnc3NPYaHKYQMbK789kY8vlP/EIo5QkZVErg==
+ cca_realm_initial_measurement: !!binary |
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
+ cca_realm_extensible_measurements:
+ - !!binary |
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
+ - !!binary |
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
+ - !!binary |
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
+ - !!binary |
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
diff --git a/iat-verifier/tests/test_verifier.py b/iat-verifier/tests/test_verifier.py
index 5f8380e..ed3a725 100644
--- a/iat-verifier/tests/test_verifier.py
+++ b/iat-verifier/tests/test_verifier.py
@@ -11,8 +11,10 @@
import unittest
from iatverifier.psa_iot_profile1_token_verifier import PSAIoTProfile1TokenVerifier
+from iatverifier.cca_token_verifier import CCATokenVerifier, CCAPlatformTokenVerifier
from iatverifier.util import read_keyfile
-from iatverifier.attest_token_verifier import VerifierConfiguration, AttestationTokenVerifier
+from iatverifier.attest_token_verifier import AttestationClaim, VerifierConfiguration
+from iatverifier.attest_token_verifier import AttestationTokenVerifier
from test_utils import create_and_read_iat, read_iat, create_token_tmp_file
@@ -20,6 +22,8 @@
DATA_DIR = os.path.join(THIS_DIR, 'data')
KEYFILE = os.path.join(DATA_DIR, 'key.pem')
+KEYFILE_CCA_PLAT = os.path.join(DATA_DIR, 'cca_platform.pem')
+KEYFILE_CCA_REALM = os.path.join(DATA_DIR, 'cca_realm.pem')
KEYFILE_ALT = os.path.join(DATA_DIR, 'key-alt.pem')
class TestIatVerifier(unittest.TestCase):
@@ -77,14 +81,38 @@
method=AttestationTokenVerifier.SIGN_METHOD_SIGN1
cose_alg=AttestationTokenVerifier.COSE_ALG_ES256
signing_key = read_keyfile(KEYFILE, method)
+ realm_token_key = read_keyfile(KEYFILE_CCA_REALM, method)
+ platform_token_key = read_keyfile(KEYFILE_CCA_PLAT, method)
create_and_read_iat(
DATA_DIR,
'valid-iat.yaml',
- PSAIoTProfile1TokenVerifier(method=method,
- cose_alg=cose_alg,
- signing_key=signing_key,
- configuration=self.config))
+ PSAIoTProfile1TokenVerifier(
+ method=method,
+ cose_alg=cose_alg,
+ signing_key=signing_key,
+ configuration=self.config))
+ create_and_read_iat(
+ DATA_DIR,
+ 'valid-cca-token.yaml',
+ CCATokenVerifier(
+ realm_token_method=method,
+ realm_token_cose_alg=AttestationTokenVerifier.COSE_ALG_ES384,
+ realm_token_key=realm_token_key,
+ platform_token_method=method,
+ platform_token_cose_alg=AttestationTokenVerifier.COSE_ALG_ES384,
+ platform_token_key=platform_token_key,
+ configuration=self.config))
+
+ create_and_read_iat(
+ DATA_DIR,
+ 'cca_platform_token.yaml',
+ CCAPlatformTokenVerifier(
+ method=method,
+ cose_alg=AttestationTokenVerifier.COSE_ALG_ES384,
+ signing_key=platform_token_key,
+ configuration=self.config,
+ necessity=AttestationClaim.MANDATORY))
with self.assertRaises(ValueError) as test_ctx:
create_and_read_iat(