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