feat: support draft-ffm-rats-cca-token-00 encoding
Add support for the new CCA token profile(s) defined in
draft-ffm-rats-cca-token-00.
In particular, claim 44237 (`cca_realm_pub_key`') now encodes the RAK as
a CBOR-serialised COSE_Key rather than a raw public key.
This non-backwards compatible encoding is signalled by the new (in-band)
profile identifier `"tag:arm.com,2023:realm#1.0.0"`.
The legacy profile `"http://arm.com/CCA-SSD/1.0.0"` with the raw RAK
encoding is still fully processed, but its use triggers a deprecation
warning.
Change-Id: I381fb0a885cba99191622ba2f38688a357eaf736
Signed-off-by: Thomas Fossati <thomas.fossati@linaro.org>
diff --git a/iat-verifier/dev_scripts/generate-key.py b/iat-verifier/dev_scripts/generate-key.py
index 6c1eee7..ceb5cc6 100755
--- a/iat-verifier/dev_scripts/generate-key.py
+++ b/iat-verifier/dev_scripts/generate-key.py
@@ -6,14 +6,31 @@
#
# -----------------------------------------------------------------------------
-import sys
+import argparse
-from ecdsa import SigningKey, NIST256p
-
+from ecdsa import SigningKey, NIST256p, NIST384p
+from pycose.keys import EC2Key, CoseKey
+from ecdsa.curves import curve_by_name
if __name__ == '__main__':
- outfile = sys.argv[1]
+ parser = argparse.ArgumentParser(description='generate an ECDSA key')
- sk = SigningKey.generate(curve=NIST256p)
- with open(outfile, 'wb') as wfh:
- wfh.write(sk.to_pem())
+ parser.add_argument('outfile', type=str, help='output file')
+ parser.add_argument('--crv', type=str, help='ECDSA curve',
+ choices=[NIST256p.name, NIST384p.name], default='NIST256p')
+ parser.add_argument('--fmt', type=str, help='key format',
+ choices=['PEM', 'COSE'], default='PEM')
+
+ args = parser.parse_args()
+
+ sk = SigningKey.generate(curve_by_name(args.crv))
+
+ pem_key = sk.to_pem().decode('utf-8')
+
+ if args.fmt == 'PEM':
+ o = pem_key
+ elif args.fmt == 'COSE':
+ o = CoseKey.from_pem_private_key(pem_key)
+
+ with open(args.outfile, 'wb') as wfh:
+ wfh.write(o.encode())
diff --git a/iat-verifier/dev_scripts/pem2cose.py b/iat-verifier/dev_scripts/pem2cose.py
new file mode 100755
index 0000000..f6c62c7
--- /dev/null
+++ b/iat-verifier/dev_scripts/pem2cose.py
@@ -0,0 +1,58 @@
+#!/usr/bin/env python3
+# -----------------------------------------------------------------------------
+# Copyright (c) 2024, Linaro Limited. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+# -----------------------------------------------------------------------------
+
+"""
+Convert a PEM key into an equivalent COSE_Key, and optionally compute the CCA hash-lock claims
+
+Examples:
+ ./pem2cose.py -h
+ ./pem2cose.py ../tests/data/cca_realm.pem cca_realm.cbor
+ ./pem2cose.py --hash-alg sha-256 ../tests/data/cca_realm.pem - > hashlock-claims.yaml
+
+"""
+import argparse
+
+from iatverifier.util import read_keyfile
+from iatverifier.attest_token_verifier import AttestationTokenVerifier
+from hashlib import sha256, sha384, sha512
+from base64 import b64encode
+
+hash_algorithms = {
+ 'sha-256': sha256,
+ 'sha-384': sha384,
+ 'sha-512': sha512,
+}
+
+if __name__ == '__main__':
+ parser = argparse.ArgumentParser(
+ description='convert a PEM key into an equivalent COSE_Key; optionally compute the CCA hash-lock claims')
+
+ parser.add_argument('pemfile', type=str, help='input PEM file')
+ parser.add_argument(
+ 'cosefile', type=str, help='output COSE_Key file (pass "-" to write to stdout)')
+ parser.add_argument('--hash-alg', type=str, help='compute the hash lock using the specified algorithm',
+ choices=hash_algorithms.keys())
+
+ args = parser.parse_args()
+
+ cose_key = read_keyfile(
+ args.pemfile, AttestationTokenVerifier.SIGN_METHOD_SIGN1).encode()
+
+ if args.cosefile == '-':
+ b64_cose_key = b64encode(cose_key).decode()
+ print(f'cca_realm_pub_key: !!binary {b64_cose_key}')
+ else:
+ with open(args.cosefile, 'wb') as f:
+ f.write(cose_key)
+
+ if args.hash_alg is not None:
+ h = hash_algorithms[args.hash_alg]()
+ h.update(cose_key)
+ b64_hash_lock = b64encode(h.digest()).decode()
+ print(f'cca_platform_challenge: !!binary {b64_hash_lock}')
+ print(f'cca_realm_pub_key_hash_algo_id: {args.hash_alg}')
diff --git a/iat-verifier/iatverifier/cca_claims.py b/iat-verifier/iatverifier/cca_claims.py
index 7948425..e5de8e6 100644
--- a/iat-verifier/iatverifier/cca_claims.py
+++ b/iat-verifier/iatverifier/cca_claims.py
@@ -13,6 +13,29 @@
logger = logging.getLogger('iat-verifiers')
+CCA_PLATFORM_PROFILE = "tag:arm.com,2023:cca_platform#1.0.0"
+CCA_REALM_PROFILE = "tag:arm.com,2023:realm#1.0.0"
+CCA_PLATFORM_PROFILE_LEGACY = "http://arm.com/CCA-SSD/1.0.0"
+CCA_REALM_PROFILE_LEGACY = None
+
+class CCARealmProfileClaim(AttestationClaim):
+ def get_claim_key(self=None):
+ return 265
+
+ def get_claim_name(self=None):
+ return 'CCA_REALM_PROFILE'
+
+ @classmethod
+ def is_utf_8(cls):
+ return True
+
+ def verify(self, token_item):
+ expected_value = CCA_REALM_PROFILE
+ self._check_type(self.get_claim_name(), token_item.value, str)
+ if token_item.value != expected_value:
+ msg = 'Invalid Realm profile "{}": must be "{}"'
+ self.verifier.error(msg.format(token_item.value, expected_value))
+
class CCARealmChallengeClaim(AttestationClaim):
def __init__(self, verifier, expected_challenge_byte, necessity=AttestationClaim.MANDATORY):
super().__init__(verifier=verifier, necessity=necessity)
@@ -34,6 +57,7 @@
self.verifier.error(msg.format(i, b, self.expected_challenge_byte))
break
+
class CCARealmPersonalizationValue(AttestationClaim):
def get_claim_key(self=None):
return 44235
@@ -119,11 +143,16 @@
return True
def verify(self, token_item):
- expected_value = "http://arm.com/CCA-SSD/1.0.0"
- self._check_type(self.get_claim_name(), token_item.value, str)
- if token_item.value != expected_value:
- msg = 'Invalid Attest profile "{}": must be "{}"'
- self.verifier.error(msg.format(token_item.value, expected_value))
+ # allow legacy too when decoding
+ allowed_profiles = [ CCA_PLATFORM_PROFILE, CCA_PLATFORM_PROFILE_LEGACY ]
+
+ for allowed_profile in allowed_profiles:
+ self._check_type(self.get_claim_name(), token_item.value, str)
+ if token_item.value == allowed_profile:
+ return
+
+ msg = 'Invalid Platform profile "{}": must be one of "{}"'
+ self.verifier.error(msg.format(token_item.value, ','.join(allowed_profiles)))
class CCAPlatformChallengeClaim(AttestationClaim):
def get_claim_key(self=None):
diff --git a/iat-verifier/iatverifier/cca_token_verifier.py b/iat-verifier/iatverifier/cca_token_verifier.py
index fc05d9a..8531896 100644
--- a/iat-verifier/iatverifier/cca_token_verifier.py
+++ b/iat-verifier/iatverifier/cca_token_verifier.py
@@ -5,17 +5,17 @@
#
# -----------------------------------------------------------------------------
-from collections import namedtuple
from cryptography.hazmat.primitives import hashes
-from ecdsa.keys import VerifyingKey
-from ecdsa.curves import NIST256p, NIST384p, NIST521p
-from hashlib import sha1
from pycose.headers import Algorithm
from pycose.algorithms import Es256, Es384, Es512
+from pycose.keys import CoseKey
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 CCARealmProfileClaim
+from iatverifier.cca_claims import CCA_REALM_PROFILE, CCA_REALM_PROFILE_LEGACY
+from iatverifier.cca_claims import CCA_PLATFORM_PROFILE, CCA_PLATFORM_PROFILE_LEGACY
from iatverifier.cca_claims import CCARealmHashAlgorithmIdClaim, CCARealmPubKeyClaim
from iatverifier.cca_claims import CCARealmExtensibleMeasurementsClaim, CCARealmInitialMeasurementClaim
from iatverifier.cca_claims import CCARealmPubKeyHashAlgorithmIdClaim, CCAPlatformHashAlgorithmIdClaim
@@ -26,13 +26,12 @@
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
-from iatverifier.util import es384_cose_key_from_raw_ecdsa
+from iatverifier.util import ec2_cose_key_from_raw_ecdsa
-_Algorithm = namedtuple("Algorithm", "ecdsa_curve pub_key_len")
_algorithms = {
- Es256: _Algorithm(NIST256p, 65),
- Es384: _Algorithm(NIST384p, 97),
- Es512: _Algorithm(NIST521p, 133),
+ Es256: 65,
+ Es384: 97,
+ Es512: 133,
}
class CCATokenVerifier(Verifier):
@@ -88,10 +87,32 @@
cose_alg=Es256,
signing_key=None)
+ def _check_profiles_coherence(self, platform_profile, realm_profile):
+ if platform_profile == CCA_PLATFORM_PROFILE_LEGACY:
+ self.warning(f'legacy profile {platform_profile} is deprecated')
+ if realm_profile != CCA_REALM_PROFILE_LEGACY:
+ self.error(f'unsupported profile combination: {platform_profile}, {realm_profile}')
+ elif platform_profile == CCA_PLATFORM_PROFILE:
+ if realm_profile != CCA_REALM_PROFILE:
+ self.error(f'unsupported profile combination: {platform_profile}, {realm_profile}')
+ else:
+ self.error(f'unknown profile(s): {platform_profile}, {realm_profile}')
+
def verify(self, token_item):
- # Extract the realm public key
cca_token_root_claims_item = token_item.value
+ cca_platform_token_root_claims_item = cca_token_root_claims_item.value[CCAPlatformTokenVerifier.get_claim_name()].value
cca_realm_delegated_token_root_claims_item = cca_token_root_claims_item.value[CCARealmTokenVerifier.get_claim_name()].value
+
+ # Extract the platform profile
+ cca_platform_profile_item = cca_platform_token_root_claims_item.value[CCAAttestationProfileClaim.get_claim_name()]
+ cca_platform_profile = cca_platform_profile_item.value
+
+ # Extract the realm profile
+ cca_realm_profile = CCARealmTokenVerifier.get_profile(cca_realm_delegated_token_root_claims_item)
+
+ self._check_profiles_coherence(cca_platform_profile, cca_realm_profile)
+
+ # Extract the realm public key
cca_realm_public_key_item = cca_realm_delegated_token_root_claims_item.value[CCARealmPubKeyClaim.get_claim_name()]
cca_realm_public_key = cca_realm_public_key_item.value
@@ -101,7 +122,6 @@
cca_realm_public_key_hash = digest.finalize()
# Get the challenge value from the platform token
- cca_platform_token_root_claims_item = cca_token_root_claims_item.value[CCAPlatformTokenVerifier.get_claim_name()].value
cca_platform_challenge_item = cca_platform_token_root_claims_item.value[CCAPlatformChallengeClaim.get_claim_name()]
cca_platform_challenge = cca_platform_challenge_item.value
@@ -139,6 +159,7 @@
(CCARealmHashAlgorithmIdClaim, {'verifier':self, 'necessity': Claim.MANDATORY}),
(CCARealmPubKeyHashAlgorithmIdClaim, {'verifier':self, 'necessity': Claim.MANDATORY}),
(CCARealmPubKeyClaim, {'verifier':self, 'necessity': Claim.MANDATORY}),
+ (CCARealmProfileClaim, {'verifier':self, 'necessity': Claim.OPTIONAL}),
]
# initialise the base part of the token
@@ -170,23 +191,19 @@
alg = token_item.protected_header[Algorithm]
if alg not in _algorithms:
- self.error(f"Unknown alg '{alg}' in realm token's protected header.")
+ self.error(f"Unknown alg '{alg.fullname}' in realm token's protected header.")
return
- alg_info = _algorithms[alg]
- if len(cca_realm_public_key) != alg_info.pub_key_len:
- self.error(f"Invalid realm public key length (alg: '{alg}'): "
- f"{len(cca_realm_public_key)} instead of {alg_info.pub_key_len}")
- return
+ # encoding of the RAK depends on the profile
+ profile_value = self.get_profile(cca_realm_delegated_token_root_claims_item)
- # Set the signing key in the parsed CCARealmTokenVerifier object
- token_item.claim_type.signing_key = es384_cose_key_from_raw_ecdsa(
- VerifyingKey.from_string(
- cca_realm_public_key,
- curve=alg_info.ecdsa_curve,
- hashfunc=sha1
+ try:
+ token_item.claim_type.signing_key = self._get_rak_as_cose_key(
+ cca_realm_public_key, alg, profile_value
)
- )
+ except Exception as e:
+ self.error(f"invalid RAK value: {e}")
+ return
# call the '_get_cose_payload' of AttestationTokenVerifier to verify the
# signature
@@ -197,6 +214,28 @@
except ValueError:
self.error("Realm signature doesn't match Realm Public Key claim in Realm token.")
+ @staticmethod
+ def get_profile(realm_token):
+ try:
+ return realm_token.value[CCARealmProfileClaim.get_claim_name()].value
+ except KeyError:
+ # if profile is not found in the realm token, assume the legacy format
+ return CCA_REALM_PROFILE_LEGACY
+
+ def _get_rak_as_cose_key(self, rak, alg, profile):
+ if profile == CCA_REALM_PROFILE:
+ return CoseKey.decode(rak)
+
+ if profile == CCA_REALM_PROFILE_LEGACY:
+ expected_rak_len = _algorithms[alg]
+ if len(rak) != expected_rak_len:
+ e = f"Invalid realm public key length (alg: '{alg.fullname}'): " \
+ f"want {expected_rak_len} got {len(rak)} bytes"
+ raise ValueError(e)
+ return ec2_cose_key_from_raw_ecdsa(rak, alg)
+
+ raise ValueError(f"unknown profile {profile}")
+
class CCAPlatformTokenVerifier(Verifier):
def get_claim_key(self=None):
return 44234 #0xACCA
diff --git a/iat-verifier/iatverifier/util.py b/iat-verifier/iatverifier/util.py
index ea152c7..3a26da3 100644
--- a/iat-verifier/iatverifier/util.py
+++ b/iat-verifier/iatverifier/util.py
@@ -16,9 +16,11 @@
import yaml
import yaml_include
from pycose.keys import CoseKey
+from ecdsa.curves import NIST256p, NIST384p, NIST521p
from pycose.keys.curves import P256, P384, P521
from pycose.keys.keytype import KtyEC2
from pycose.algorithms import Es256, Es384, Es512, HMAC256
+from ecdsa.keys import VerifyingKey
from iatverifier.attest_token_verifier import AttestationTokenVerifier
from cbor2 import CBOREncoder
from pycose.keys import CoseKey
@@ -32,18 +34,29 @@
P521: Es512,
}
+_ecdsa_to_cose_curve = {
+ 'NIST256p': 'P_256',
+ 'NIST384p': 'P_384',
+ 'NIST521p': 'P_512',
+}
-def es384_cose_key_from_raw_ecdsa(raw_key):
+def ec2_cose_key_from_raw_ecdsa(k, alg):
+ crv = alg.get_curve()
+ hash_func = alg.get_hash_func()
+
+ vk = VerifyingKey.from_string(k, curve=crv, hashfunc=hash_func)
+
d = {
'KTY': 'EC2',
- 'CURVE': 'P_384',
- 'ALG': 'ES384',
- 'X': number_to_string(raw_key.pubkey.point.x(), raw_key.curve.generator.order()),
- 'Y': number_to_string(raw_key.pubkey.point.y(), raw_key.curve.generator.order()),
+ 'CURVE': _ecdsa_to_cose_curve[crv.name],
+ 'ALG': alg.fullname,
+ 'X': number_to_string(vk.pubkey.point.x(), vk.curve.generator.order()),
+ 'Y': number_to_string(vk.pubkey.point.y(), vk.curve.generator.order()),
}
return CoseKey.from_dict(d)
+
def convert_map_to_token(token_map, verifier, wfh, *, name_as_key, parse_raw_value):
"""
Convert a map to token and write the result to a file.
diff --git a/iat-verifier/tests/data/cca_example_platform_token.cbor b/iat-verifier/tests/data/cca_example_platform_token.cbor
index 6813be9..b22d3f9 100644
--- a/iat-verifier/tests/data/cca_example_platform_token.cbor
+++ b/iat-verifier/tests/data/cca_example_platform_token.cbor
Binary files differ
diff --git a/iat-verifier/tests/data/cca_example_platform_token.yaml b/iat-verifier/tests/data/cca_example_platform_token.yaml
index 17db413..cf67b60 100644
--- a/iat-verifier/tests/data/cca_example_platform_token.yaml
+++ b/iat-verifier/tests/data/cca_example_platform_token.yaml
@@ -6,11 +6,11 @@
#-------------------------------------------------------------------------------
# The EAT profile to which the CCA platform token conforms.
-cca_attestation_profile: http://arm.com/CCA-SSD/1.0.0
+cca_attestation_profile: tag:arm.com,2023:cca_platform#1.0.0
# Hash of the public key used to sign the Realm token. Its length depends on
# the public key hash algorithm identifier claim in the Realm token.
-cca_platform_challenge: !!binary tZc8touqn8VVWHhrfsZ/aeQN9bpaqSHNDCf0BYegEeo=
+cca_platform_challenge: !!binary uIHLIBvuo5OR1W8V2mww7I30v4LDFzZVjVbaFoU1p9Y=
# Unique identifier of the implementation of the CCA platform.
# The semantics of the CCA platform Implementation ID value are defined by the
diff --git a/iat-verifier/tests/data/cca_example_platform_token_legacy.cbor b/iat-verifier/tests/data/cca_example_platform_token_legacy.cbor
new file mode 100644
index 0000000..6813be9
--- /dev/null
+++ b/iat-verifier/tests/data/cca_example_platform_token_legacy.cbor
Binary files differ
diff --git a/iat-verifier/tests/data/cca_example_platform_token_legacy.yaml b/iat-verifier/tests/data/cca_example_platform_token_legacy.yaml
new file mode 100644
index 0000000..17db413
--- /dev/null
+++ b/iat-verifier/tests/data/cca_example_platform_token_legacy.yaml
@@ -0,0 +1,187 @@
+#-------------------------------------------------------------------------------
+# Copyright (c) 2024, Linaro Limited. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+#-------------------------------------------------------------------------------
+
+# The EAT profile to which the CCA platform token conforms.
+cca_attestation_profile: http://arm.com/CCA-SSD/1.0.0
+
+# Hash of the public key used to sign the Realm token. Its length depends on
+# the public key hash algorithm identifier claim in the Realm token.
+cca_platform_challenge: !!binary tZc8touqn8VVWHhrfsZ/aeQN9bpaqSHNDCf0BYegEeo=
+
+# Unique identifier of the implementation of the CCA platform.
+# The semantics of the CCA platform Implementation ID value are defined by the
+# manufacturer or a particular certification scheme. For example, the ID could
+# take the form of a product serial number, database ID, or other appropriate
+# identifier.
+# Equivalent to a class identifier.
+cca_platform_implementation_id: !!binary f0VMRgIBAQAAAAAAAAAAAAMAPgABAAAAUFgAAAAAAAA=
+
+# Unique identifier of the Initial Attestation Key (IAK) for the CCA platform
+# encoded as a EAT Unique Entity IDentifier (UEID), see Section 4.2.1 of
+# https://datatracker.ietf.org/doc/draft-ietf-rats-eat/
+# The UEID MUST be of type RAND (type byte: 0x01), which makes the fingerprint of
+# the IAK a natural choice for this.
+cca_platform_instance_id: !!binary AQcGBQQDAgEADw4NDAsKCQgXFhUUExIREB8eHRwbGhkY
+
+# The CCA platform config claim describes the set of chosen implementation
+# options of the CCA platform. As an example, these may include a description
+# of the level of physical memory protection which is provided. The CCA
+# platform config claim is expected to contain the System Properties field
+# which is present in the Root Non-volatile Storage (RNVS) public parameters.
+cca_platform_config: !!binary z8/Pzw==
+
+# The CCA platform lifecycle claim identifies the lifecycle state of the CCA
+# platform.
+# Normally, a CCA platform will be in psa-lifecycle-secured state.
+# Other security states are not compatible with an attestable plaform.
+cca_platform_lifecycle: secured_0x3003
+
+cca_platform_hash_algo_id: sha-256
+
+# The CCA platform verification service claim is a hint which can be used by a
+# relying party to locate a verifier for the token.
+cca_platform_verification_service: https://veraison.example/.well-known/veraison/verification
+
+# The following SW components arrangement assumes the Arm reference design where
+# the Runtime Security Engine (RSE) is an independent core next to the
+# Application Processor (AP) and the System Control Processor (SCP) on the same
+# die. The RSE provides fundamental security guarantees and runtime services for
+# the rest of the system: trusted boot, measured boot, platform attestation, key
+# management and key derivation.
+#
+# It also assumes RSE BL1 is split into two distinct boot stages, BL1_1 which is
+# stored in ROM (and is not measured) and BL1_2 which is stored in other
+# non-volatile storage. At power up RSE boots first from its private ROM code.
+# It validates and loads its own images and the initial images of SCP and AP.
+#
+# A further assumption, reflected in the signer_id partitioning, is that the
+# Chain of Trust (CoT) is the one recommended by the Arm CCA security model,
+# which requires independent supply chains for the Arm CCA firmware, the secure
+# world firmware and the platform owner firmware. Hence, this CoT has 3
+# signer_id's, one for each supply chain:
+# * Arm CCA firmware (i.e., Monitor, RMM and HES)
+# -> U3h5YwdTXfPsjYsVouLcVkFBnD0wYM/jIjjA+pc/eqM=
+# * Platform owner firmware (SCP, etc.)
+# -> 8UtJh5BLy1gU5EWaBX7U0g9YpjMVIoinYSFNzSh4C1Y=
+# * A signer for the secure world firmware is omitted since SW is not part of
+# the CCA TCB.
+cca_platform_sw_components:
+ #
+ # Platform measurements
+ #
+ # 1. Runtime Security Engine (RSE) components
+ - sw_component_type: RSE_BL1_2
+ signer_id: !!binary U3h5YwdTXfPsjYsVouLcVkFBnD0wYM/jIjjA+pc/eqM=
+ measurement_value: !!binary micfKpFrC27mzsskJvCzIG7wdFeL5V2byU9vP+Orhqo=
+ cca_sw_component_hash_id: sha-256
+
+ - sw_component_type: RSE_BL2
+ signer_id: !!binary U3h5YwdTXfPsjYsVouLcVkFBnD0wYM/jIjjA+pc/eqM=
+ measurement_value: !!binary U8I05ehHK2rFHBrhyrP+BvrQU7646/2Jd7AQZVv908M=
+ cca_sw_component_hash_id: sha-256
+
+ # RSE secure runtime
+ - sw_component_type: RSE_S
+ signer_id: !!binary U3h5YwdTXfPsjYsVouLcVkFBnD0wYM/jIjjA+pc/eqM=
+ measurement_value: !!binary ESHPzNWRPwpj/sQKb/1E6mT53BNcZmNLoAHRC89DAqI=
+ cca_sw_component_hash_id: sha-256
+
+ #
+ # Firmware measurements
+ #
+ # 0. AP Initial Boot
+ #
+ # This is the first code to execute on the AP. In the CCA context, when RSE
+ # is the root of trust, it can be updated. Its primary purpose is to perform
+ # the minimum initialization necessary to load and authenticate an updateable
+ # AP firmware image into an executable RAM location, then hand-off control to
+ # that image.
+ - sw_component_type: AP_BL1
+ signer_id: !!binary U3h5YwdTXfPsjYsVouLcVkFBnD0wYM/jIjjA+pc/eqM=
+ measurement_value: !!binary FXG17Hi9aFEr94MLtqKkSyBHx99XvOeeuKHA5b6gpQE=
+ cca_sw_component_hash_id: sha-256
+
+ # 1. AP RAM Firmware
+ #
+ # This is the 2nd stage AP firmware. It is currently also known as the
+ # "Trusted Boot Firmware". Its primary purpose is to perform any additional
+ # initialization required to load and authenticate all 3rd level firmware
+ # images into their executable RAM locations, then hand-off control to the
+ # EL3 Runtime Firmware.
+ - sw_component_type: AP_BL2
+ signer_id: !!binary U3h5YwdTXfPsjYsVouLcVkFBnD0wYM/jIjjA+pc/eqM=
+ measurement_value: !!binary EBWbryYrQ6ktldtZ2uH3LGRRJzAWYeCjzk44spWpfFg=
+ cca_sw_component_hash_id: sha-256
+
+ # 2. SCP Initial Boot
+ #
+ # This is the first code to execute on the SCP. Its primary purpose is to
+ # perform the minimum initialization necessary to load and authenticate an
+ # updateable SCP firmware image into an executable RAM location, then hand-off
+ # control to that image.
+ - sw_component_type: SCP_BL1
+ signer_id: !!binary U3h5YwdTXfPsjYsVouLcVkFBnD0wYM/jIjjA+pc/eqM=
+ measurement_value: !!binary EBIuhWs/zUnwY2NjF0dhSctzChqhz6rYGFUrcvVtb2g=
+ cca_sw_component_hash_id: sha-256
+
+ # 2. SCP RAM Firmware
+ #
+ # This is the 2nd stage SCP firmware. It is currently also known as the
+ # "SCP runtime firmware" but it could potentially be an intermediate
+ # firmware if the SCP needs to load/authenticate multiple 3rd level images
+ # in future.
+ - sw_component_type: SCP_BL2
+ signer_id: !!binary 8UtJh5BLy1gU5EWaBX7U0g9YpjMVIoinYSFNzSh4C1Y=
+ measurement_value: !!binary qmehabC7oheqCqiKZTRpIMhMQkR8NrpffqZfQiwf5dg=
+ cca_sw_component_hash_id: sha-256
+
+ # 3. EL3 Runtime Firmware
+ #
+ # Also known as "SoC AP firmware" or "EL3 monitor firmware". Its primary
+ # purpose is to handle transitions between worlds (normal, secure and realm)
+ - sw_component_type: AP_BL31
+ signer_id: !!binary U3h5YwdTXfPsjYsVouLcVkFBnD0wYM/jIjjA+pc/eqM=
+ measurement_value: !!binary Lm0xpZg6kSUb+uWu+hwKGdi6PPYB0OinBrTPqWYaa4o=
+ cca_sw_component_hash_id: sha-256
+
+ #
+ # Realm Monitor Management Firmware measurement
+ #
+ # This is required if Realm Management Extension (RME) feature is enabled.
+ - sw_component_type: RMM
+ signer_id: !!binary U3h5YwdTXfPsjYsVouLcVkFBnD0wYM/jIjjA+pc/eqM=
+ measurement_value: !!binary oftQ5shvrhZ57zNRKW/WcTQRoIz43ReQpP0F+uhogWQ=
+ cca_sw_component_hash_id: sha-256
+
+ #
+ # Configuration payloads
+ #
+ # BL2 configuration
+ #
+ # Properties related to hardware configuration of the SoC such as topology,
+ # GIC controller, PSCI hooks, CPU ID, etc.
+ - sw_component_type: HW_CONFIG
+ signer_id: !!binary U3h5YwdTXfPsjYsVouLcVkFBnD0wYM/jIjjA+pc/eqM=
+ measurement_value: !!binary GiUkApcvYFf6U8wXK1K5/8ppjhgxH6zQ87Buyq73nhc=
+ cca_sw_component_hash_id: sha-256
+ # Properties related to base address, maximum size and image id of other
+ # DTBs etc.
+ - sw_component_type: FW_CONFIG
+ signer_id: !!binary U3h5YwdTXfPsjYsVouLcVkFBnD0wYM/jIjjA+pc/eqM=
+ measurement_value: !!binary mpKtvAzuOO9ljHHOGxv4xlZo8Wa/shNkTIlcyxrQeiU=
+ cca_sw_component_hash_id: sha-256
+ # Properties related to trusted firmware such as IO policies, mbedtls heap
+ # info etc.
+ - sw_component_type: TB_FW_CONFIG
+ signer_id: !!binary U3h5YwdTXfPsjYsVouLcVkFBnD0wYM/jIjjA+pc/eqM=
+ measurement_value: !!binary I4kDGAzBBOwsXYs/IMW8YbOJ7AqWffjMIIzcfNRUF08=
+ cca_sw_component_hash_id: sha-256
+ # BL31 configuration (SoC firmware)
+ - sw_component_type: SOC_FW_CONFIG
+ signer_id: !!binary U3h5YwdTXfPsjYsVouLcVkFBnD0wYM/jIjjA+pc/eqM=
+ measurement_value: !!binary 5sIejSYP5xiC3r2zOdJAKiynZIUpvCMD9IZJvOA4ABc=
+ cca_sw_component_hash_id: sha-256
\ No newline at end of file
diff --git a/iat-verifier/tests/data/cca_example_token.cbor b/iat-verifier/tests/data/cca_example_token.cbor
index 5a1c466..b3dbcdc 100644
--- a/iat-verifier/tests/data/cca_example_token.cbor
+++ b/iat-verifier/tests/data/cca_example_token.cbor
Binary files differ
diff --git a/iat-verifier/tests/data/cca_example_token.yaml b/iat-verifier/tests/data/cca_example_token.yaml
index b1f23c3..80acdc3 100644
--- a/iat-verifier/tests/data/cca_example_token.yaml
+++ b/iat-verifier/tests/data/cca_example_token.yaml
@@ -9,6 +9,8 @@
cca_platform_token: !inc cca_example_platform_token.yaml
cca_realm_delegated_token:
+ # The Realm token profile defined in draft-ffm-rats-cca-token-00
+ cca_realm_profile: tag:arm.com,2023:realm#1.0.0
# The Realm challenge claim is used to carry the challenge provided by the
# caller to demonstrate freshness of the generated token. The length of the
# Realm challenge is 64 bytes.
@@ -27,10 +29,10 @@
cca_realm_personalization_value: !!binary |
VGhlIHF1aWNrIGJyb3duIGZveCBqdW1wcyBvdmVyIDEzIGxhenkgZG9ncy5UaGUgcXVpY2sgYnJvd24gZm94IA==
# The Realm public key claim carries the public portion of the RAK which is
- # used to sign the Realm token. The RAK value is encoded according to SEC
- # 1: Elliptic Curve Cryptography, version 2.0.
+ # used to sign the Realm token. The RAK value is encoded as a COSE_Key (see
+ # Section 7 of RFC9052)
cca_realm_pub_key: !!binary |
- BHb5iAkb5YXtQYAa7Pq4WFSMYwV+FrDmdhILvQ0vnCngVsXUGgEw65whUXiZ3CMUayjhsGK9PqSzFf0hnxy7Uoy250ykm+Fnc3NPYaHKYQMbK789kY8vlP/EIo5QkZVErg==
+ pQECIAIhWDB2+YgJG+WF7UGAGuz6uFhUjGMFfhaw5nYSC70NL5wp4FbF1BoBMOucIVF4mdwjFGsiWDAo4bBivT6ksxX9IZ8cu1KMtudMpJvhZ3NzT2GhymEDGyu/PZGPL5T/xCKOUJGVRK4jWDAgEcfwPO5DJRduUk8DPAzh4hp25sGk8Lg5qh32Hg6KXIoFdA+bae+n6xpBhb0Rf2g=
# The Realm Initial Measurement claim contains the value of the RIM (see
# Section A7.1.1 of DEN0137 for the details).
cca_realm_initial_measurement: !!binary |
diff --git a/iat-verifier/tests/data/cca_example_token_legacy.cbor b/iat-verifier/tests/data/cca_example_token_legacy.cbor
new file mode 100644
index 0000000..5a1c466
--- /dev/null
+++ b/iat-verifier/tests/data/cca_example_token_legacy.cbor
Binary files differ
diff --git a/iat-verifier/tests/data/cca_example_token_legacy.yaml b/iat-verifier/tests/data/cca_example_token_legacy.yaml
new file mode 100644
index 0000000..c7fb34e
--- /dev/null
+++ b/iat-verifier/tests/data/cca_example_token_legacy.yaml
@@ -0,0 +1,48 @@
+#-------------------------------------------------------------------------------
+# Copyright (c) 2022-2024, Arm Limited. All rights reserved.
+# Copyright (c) 2024, Linaro Limited. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+#-------------------------------------------------------------------------------
+
+cca_platform_token: !inc cca_example_platform_token_legacy.yaml
+
+cca_realm_delegated_token:
+ # The Realm challenge claim is used to carry the challenge provided by the
+ # caller to demonstrate freshness of the generated token. The length of the
+ # Realm challenge is 64 bytes.
+ cca_realm_challenge: !!binary |
+ bobW2XzHE7xt1D285JGmtAMRwCeov4WjnaY+nORMEyqKEZ0pb65qaZnpvz5EcbDOASRdiJQkwx6JeTs7HWsVBA==
+ # The Realm hash algorithm ID claim identifies the algorithm used to
+ # calculate all hash values which are present in the Realm token.
+ # See also https://www.iana.org/assignments/named-information/named-information.xhtml
+ cca_realm_hash_algm_id: sha-256
+ # The Realm public key hash algorithm identifier claim identifies the
+ # algorithm used to calculate the hash of the public portion of the Realm
+ # Attestation Key (RAK).
+ cca_realm_pub_key_hash_algo_id: sha-256
+ # The Realm Personalization Value claim contains a 64 bytes value which was
+ # provided at Realm creation.
+ cca_realm_personalization_value: !!binary |
+ VGhlIHF1aWNrIGJyb3duIGZveCBqdW1wcyBvdmVyIDEzIGxhenkgZG9ncy5UaGUgcXVpY2sgYnJvd24gZm94IA==
+ # The Realm public key claim carries the public portion of the RAK which is
+ # used to sign the Realm token. The RAK value is encoded according to SEC
+ # 1: Elliptic Curve Cryptography, version 2.0.
+ cca_realm_pub_key: !!binary |
+ BHb5iAkb5YXtQYAa7Pq4WFSMYwV+FrDmdhILvQ0vnCngVsXUGgEw65whUXiZ3CMUayjhsGK9PqSzFf0hnxy7Uoy250ykm+Fnc3NPYaHKYQMbK789kY8vlP/EIo5QkZVErg==
+ # The Realm Initial Measurement claim contains the value of the RIM (see
+ # Section A7.1.1 of DEN0137 for the details).
+ cca_realm_initial_measurement: !!binary |
+ MRMUq3NiA1DPdYg0rlxl2ejC3H/r5ufZZUu+hk4wDUk=
+ # The Realm Extensible Measurements claim contains the values of the Realm
+ # Extensible Measurements (see Section A7.1.2 of DEN0137 for the details).
+ cca_realm_extensible_measurements:
+ - !!binary |
+ JNWwopbMBcvYBoxQZ8W9Rzt3Ddpq4IL+O6MKvj+aarE=
+ - !!binary |
+ eI/AkL/GuO2QMVK6hBTnPa9bjHux55rVAqsGmbZZ7RY=
+ - !!binary |
+ 2sRqWEFdw6ANenQYUgCOnK5k9S0DufdtdvSzZE/vxBY=
+ - !!binary |
+ MsavxiflVYXAMVU1nzMaDiJfaEDblH3Zbvq4G+JnGTk=
diff --git a/iat-verifier/tests/data/mk_tokens.sh b/iat-verifier/tests/data/mk_tokens.sh
index cd4d668..740589a 100755
--- a/iat-verifier/tests/data/mk_tokens.sh
+++ b/iat-verifier/tests/data/mk_tokens.sh
@@ -8,29 +8,39 @@
set -eux
set -o pipefail
-compile_token \
- --token-type CCA-token \
- --platform-key cca_platform.pem \
- --realm-key cca_realm.pem \
- --method sign \
- --outfile cca_example_token.cbor \
- cca_example_token.yaml
+k=cca_platform.pem
-check_iat \
- -t CCA-token \
- -k cca_platform.pem \
- -m sign \
- cca_example_token.cbor
+for t in cca_example_platform_token cca_example_platform_token_legacy; do
-compile_token \
- --token-type CCA-plat-token \
- --platform-key cca_platform.pem \
- --method sign \
- --outfile cca_example_platform_token.cbor \
- cca_example_platform_token.yaml
+ compile_token \
+ --token-type CCA-plat-token \
+ --platform-key $k \
+ --method sign \
+ --outfile $t.cbor \
+ $t.yaml
-check_iat \
- -t CCA-plat-token \
- -k cca_platform.pem \
- -m sign \
- cca_example_platform_token.cbor
\ No newline at end of file
+ check_iat \
+ -t CCA-plat-token \
+ -k $k \
+ -m sign \
+ $t.cbor
+
+done
+
+for t in cca_example_token cca_example_token_legacy; do
+
+ compile_token \
+ --token-type CCA-token \
+ --platform-key cca_platform.pem \
+ --realm-key cca_realm.pem \
+ --method sign \
+ --outfile $t.cbor \
+ $t.yaml
+
+ check_iat \
+ -t CCA-token \
+ -k $k \
+ -m sign \
+ $t.cbor
+
+done
diff --git a/iat-verifier/tests/test_verifier.py b/iat-verifier/tests/test_verifier.py
index 48604b1..17658f1 100644
--- a/iat-verifier/tests/test_verifier.py
+++ b/iat-verifier/tests/test_verifier.py
@@ -269,3 +269,47 @@
signing_key=signing_key,
configuration=self.config)).get_token_map()
self.assertEqual(iat['SECURITY_LIFECYCLE'], 'sl_secured_3000')
+
+ def test_profiles(self):
+ """
+ Test that both legacy and new profiles are handled correctly.
+ In particular, ensure that the different RAK encodings are accommodated,
+ and that use of legacy profiles triggers a warning.
+ """
+ method=AttestationTokenVerifier.SIGN_METHOD_SIGN1
+ realm_token_key = read_keyfile(KEYFILE_CCA_REALM, method)
+ platform_token_key = read_keyfile(KEYFILE_CCA_PLAT, method)
+
+ # change directory here to make !inc work
+ os.chdir(DATA_DIR)
+
+ create_and_read_iat(
+ '.',
+ 'cca_example_token.yaml',
+ CCATokenVerifier(
+ realm_token_method=method,
+ realm_token_cose_alg=Es384,
+ realm_token_key=realm_token_key,
+ platform_token_method=method,
+ platform_token_cose_alg=Es384,
+ platform_token_key=platform_token_key,
+ configuration=self.config
+ )
+ )
+
+ with self.assertLogs() as test_ctx:
+ create_and_read_iat(
+ '.',
+ 'cca_example_token_legacy.yaml',
+ CCATokenVerifier(
+ realm_token_method=method,
+ realm_token_cose_alg=Es384,
+ realm_token_key=realm_token_key,
+ platform_token_method=method,
+ platform_token_cose_alg=Es384,
+ platform_token_key=platform_token_key,
+ configuration=self.config
+ )
+ )
+ self.assertIn('legacy profile http://arm.com/CCA-SSD/1.0.0 is deprecated',
+ test_ctx.records[0].getMessage())