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(