Check Realm signature against Realm pub key claim

This commit removes the `--cca-realm-token-keyfile` parameter from the
`check_iat` script as the key is read from the token claim.

Change-Id: I04c5b59e7669239c57b14cfc95ab90f794aa8d16
Signed-off-by: Mate Toth-Pal <mate.toth-pal@arm.com>
diff --git a/iat-verifier/iatverifier/attest_token_verifier.py b/iat-verifier/iatverifier/attest_token_verifier.py
index 3e288b1..1480a9a 100644
--- a/iat-verifier/iatverifier/attest_token_verifier.py
+++ b/iat-verifier/iatverifier/attest_token_verifier.py
@@ -59,6 +59,9 @@
     def get_token_map(self):
         return self.claim_type.get_token_map(self)
 
+    def __repr__(self):
+        return f"TokenItem({self.claim_type.__class__.__name__}, {self.value})"
+
 class AttestationClaim(ABC):
     """
     This class represents a claim.
@@ -582,7 +585,7 @@
                 msg.verify_signature(alg=self._get_cose_alg())
             except Exception as exc:
                 raise ValueError(f'Bad signature ({exc})') from exc
-        return msg.payload
+        return msg.payload, msg.protected_header
 
 
     def _get_cose_mac0_payload(self, cose, *, check_p_header, verify_signature):
@@ -599,7 +602,7 @@
                 msg.verify_auth_tag(alg=self._get_cose_alg())
             except Exception as exc:
                 raise ValueError(f'Bad signature ({exc})') from exc
-        return msg.payload
+        return msg.payload, msg.protected_header
 
 
     def _get_cose_payload(self, cose, *, check_p_header, verify_signature):
@@ -660,9 +663,10 @@
     def parse_token(self, *, token, check_p_header, lower_case_key):
         if self._get_method() == AttestationTokenVerifier.SIGN_METHOD_RAW:
             payload = token
+            protected_header = None
         else:
             try:
-                payload = self._get_cose_payload(
+                payload, protected_header = self._get_cose_payload(
                     token,
                     check_p_header=check_p_header,
                     # signature verification is done in the verify function
@@ -692,6 +696,7 @@
         ret.wrapping_tag = raw_map_tag
         ret.token = token
         ret.check_p_header = check_p_header
+        ret.protected_header = protected_header
         return ret
 
     def verify(self, token_item):
diff --git a/iat-verifier/iatverifier/cca_token_verifier.py b/iat-verifier/iatverifier/cca_token_verifier.py
index 79fee2c..afb95a9 100644
--- a/iat-verifier/iatverifier/cca_token_verifier.py
+++ b/iat-verifier/iatverifier/cca_token_verifier.py
@@ -5,6 +5,10 @@
 #
 # -----------------------------------------------------------------------------
 
+from ecdsa.keys import VerifyingKey
+from ecdsa.curves import NIST384p
+from hashlib import sha1
+
 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
@@ -19,6 +23,10 @@
 from iatverifier.psa_iot_profile1_token_claims import SWComponentTypeClaim, SwComponentVersionClaim
 from iatverifier.psa_iot_profile1_token_claims import MeasurementValueClaim, SignerIdClaim
 
+_algorithms = {
+    Verifier.COSE_ALG_ES384: NIST384p
+}
+
 class CCATokenVerifier(Verifier):
 
     def get_claim_key(self=None):
@@ -39,7 +47,7 @@
     def __init__(self, *,
             realm_token_method,
             realm_token_cose_alg,
-            realm_token_key,
+            realm_token_key = None, # Signing key is only necessary for token compile operation
             platform_token_method,
             platform_token_cose_alg,
             platform_token_key,
@@ -88,7 +96,7 @@
         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):
+    def __init__(self, *, method, cose_alg, signing_key=None, configuration, necessity):
         verifier_claims= [
             (CCARealmChallengeClaim, {'verifier':self, 'expected_challenge_byte': None, 'necessity': Claim.MANDATORY}),
             (CCARealmPersonalizationValue, {'verifier':self, 'necessity': Claim.MANDATORY}),
@@ -108,6 +116,45 @@
             cose_alg=cose_alg,
             signing_key=signing_key)
 
+    def verify(self, token_item):
+        # Realm token was not checked against the realm public key as it needs
+        # to be extracted from the realm token itself.
+
+        # Extract the public key
+        cca_realm_delegated_token_root_claims_item = token_item.value
+        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
+
+        # The 'parse_token' method of the AttestationTokenVerifier adds a 'token'
+        # field to the TokenItem.
+        assert hasattr(token_item, "token")
+
+        if not token_item.protected_header:
+            # parsing protected header failed, there is no way to deduce the curve used.
+            self.error("No protected header in realm token, failed to parse signature curve.")
+            return
+
+        alg = token_item.protected_header['alg']
+        if alg not in _algorithms:
+            self.error(f"Unknown alg '{alg}' in realm token's protected header.")
+            return
+
+        # Set the signing key in the parsed CCARealmTokenVerifier object
+        token_item.claim_type.signing_key = VerifyingKey.from_string(
+            cca_realm_public_key,
+            curve=_algorithms[alg],
+            hashfunc=sha1)
+
+        # call the '_get_cose_payload' of AttestationTokenVerifier to verify the
+        # signature
+        try:
+            token_item.claim_type._get_cose_payload(
+                        token_item.token,
+                        check_p_header=False, # already done in the parent's verify
+                        verify_signature=True)
+        except ValueError:
+            self.error("Realm signature doesn't match Realm Public Key claim in Realm token.")
+
 class CCAPlatformTokenVerifier(Verifier):
     def get_claim_key(self=None):
         return 44234 #0xACCA
diff --git a/iat-verifier/sample/cbor/cca_token.cbor b/iat-verifier/sample/cbor/cca_token.cbor
index 7f204a6..e40f8b7 100644
--- a/iat-verifier/sample/cbor/cca_token.cbor
+++ b/iat-verifier/sample/cbor/cca_token.cbor
Binary files differ
diff --git a/iat-verifier/scripts/check_iat b/iat-verifier/scripts/check_iat
index f8b1279..f3c41d0 100755
--- a/iat-verifier/scripts/check_iat
+++ b/iat-verifier/scripts/check_iat
@@ -45,10 +45,6 @@
                         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.
@@ -108,13 +104,12 @@
         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
+        key_checked = args.cca_platform_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,
+            None,
             AttestationTokenVerifier.COSE_ALG_ES384)
         platform_token_cose_alg = get_cose_alg_from_key(
             platform_token_key,
@@ -122,7 +117,6 @@
         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,
diff --git a/iat-verifier/scripts/decompile_token b/iat-verifier/scripts/decompile_token
index 491263b..c81f330 100755
--- a/iat-verifier/scripts/decompile_token
+++ b/iat-verifier/scripts/decompile_token
@@ -55,7 +55,6 @@
         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,
diff --git a/iat-verifier/tests/data/cca_realm2.pem b/iat-verifier/tests/data/cca_realm2.pem
new file mode 100644
index 0000000..86a96fd
--- /dev/null
+++ b/iat-verifier/tests/data/cca_realm2.pem
@@ -0,0 +1,6 @@
+-----BEGIN EC PRIVATE KEY-----
+MIGkAgEBBDAUpTBCqGYMwJ/qywlXHrHwZec2dLYE9xNYjtR6BEjLEC1NYJMDlUfk
+gCQORURs0zigBwYFK4EEACKhZANiAASBGViAoiB/uVYDKjy5f12lr3Jv/LcV7hZH
+hKf7FsBglr3ZRioyZQspEqhVFXDW6h87LR99qKJ1+gAzDwB4YYvD4UlUnIFw0y7F
+WJCn+ex4nx8YrpLrFdIir5cdlxyWWvE=
+-----END EC PRIVATE KEY-----
diff --git a/iat-verifier/tests/test_utils.py b/iat-verifier/tests/test_utils.py
index e05c953..8059139 100644
--- a/iat-verifier/tests/test_utils.py
+++ b/iat-verifier/tests/test_utils.py
@@ -45,7 +45,7 @@
     token_map = read_token_map(source_path)
     return convert_map_to_token_bytes(token_map, verifier, add_p_header)
 
-def create_token_file(data_dir, source_name, verifier, dest_path, *, add_p_header=False):
+def create_token_file(data_dir, source_name, verifier, dest_path, *, add_p_header=True):
     """Create a cbor token from a yaml file and write it to a file
     """
     token = create_token(
diff --git a/iat-verifier/tests/test_verifier.py b/iat-verifier/tests/test_verifier.py
index bb03513..2f45d66 100644
--- a/iat-verifier/tests/test_verifier.py
+++ b/iat-verifier/tests/test_verifier.py
@@ -24,6 +24,7 @@
 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_CCA_REALM2= os.path.join(DATA_DIR, 'cca_realm2.pem')
 KEYFILE_ALT = os.path.join(DATA_DIR, 'key-alt.pem')
 
 class TestIatVerifier(unittest.TestCase):
@@ -81,6 +82,7 @@
         cose_alg=AttestationTokenVerifier.COSE_ALG_ES256
         signing_key = read_keyfile(KEYFILE, method)
         realm_token_key = read_keyfile(KEYFILE_CCA_REALM, method)
+        realm_token_key2 = read_keyfile(KEYFILE_CCA_REALM2, method)
         platform_token_key = read_keyfile(KEYFILE_CCA_PLAT, method)
 
         create_and_read_iat(
@@ -91,6 +93,7 @@
                 cose_alg=cose_alg,
                 signing_key=signing_key,
                 configuration=self.config))
+
         create_and_read_iat(
             DATA_DIR,
             'valid-cca-token.yaml',
@@ -115,6 +118,20 @@
 
         with self.assertRaises(ValueError) as test_ctx:
             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_key2,
+                    platform_token_method=method,
+                    platform_token_cose_alg=AttestationTokenVerifier.COSE_ALG_ES384,
+                    platform_token_key=platform_token_key,
+                    configuration=self.config))
+        self.assertIn("Realm signature doesn't match Realm Public Key claim in Realm token", test_ctx.exception.args[0])
+
+        with self.assertRaises(ValueError) as test_ctx:
+            create_and_read_iat(
             DATA_DIR,
                 'invalid-profile-id.yaml',
                 PSAIoTProfile1TokenVerifier(method=method,