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())