Refactor token and map parsing

The aim of this change is to make it possible to verify nested EATs.
This requires finer grade control over how the token structure is
parsed, as CBOR envelopes can now be present inside the tree.

So this change makes the parsing the token and the map a recursive
operation, calling the necessary methods of the objects at each level.

Change-Id: I4c1e29deae7b238f2d82a73bd95c533f89492d40
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 c391393..8193b84 100755
--- a/iat-verifier/scripts/check_iat
+++ b/iat-verifier/scripts/check_iat
@@ -5,18 +5,21 @@
 #
 # -----------------------------------------------------------------------------
 
+"""CLI script for verifying an IAT."""
+
 import argparse
 import json
 import logging
 import sys
 
-from iatverifier.util import extract_iat_from_cose, recursive_bytes_to_strings
+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
 
 logger = logging.getLogger('iat-verify')
 
 def main():
+    """Main function for verifying an IAT"""
 
     token_verifiers = {
         "PSA-IoT-Profile1-token": PSAIoTProfile1TokenVerifier,
@@ -67,25 +70,44 @@
     logging.basicConfig(level=logging.INFO)
 
     config = VerifierConfiguration(keep_going=args.keep_going, strict=args.strict)
-    verifier = token_verifiers[args.token_type].get_verifier(config)
     if args.method == 'mac':
-        verifier.method = AttestationTokenVerifier.SIGN_METHOD_MAC0
-        verifier.cose_alg = AttestationTokenVerifier.COSE_ALG_HS256
+        method = AttestationTokenVerifier.SIGN_METHOD_MAC0
+    else:
+        method = AttestationTokenVerifier.SIGN_METHOD_SIGN1
 
-    try:
-        raw_iat = extract_iat_from_cose(args.keyfile, args.tokenfile, verifier, args.check_protected_header)
-        if args.keyfile:
-            print('Signature OK')
-    except ValueError as e:
-        logger.error('Could not extract IAT from COSE:\n\t{}'.format(e))
+    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
+
+    verifier_class = token_verifiers[args.token_type]
+    if verifier_class == PSAIoTProfile1TokenVerifier:
+        verifier = PSAIoTProfile1TokenVerifier(
+            method=method,
+            cose_alg=cose_alg,
+            signing_key=key,
+            configuration=config)
+    else:
+        logger.error(f'Invalid token type:{verifier_class}\n\t')
         sys.exit(1)
 
     try:
-        token = verifier.decode_and_validate_iat(raw_iat)
-        if not verifier.seen_errors:
-            print('Token format OK')
-    except ValueError as e:
-        logger.error('Could not validate IAT:\n\t{}'.format(e))
+        with open(args.tokenfile, 'rb') as token_file:
+            token = verifier.parse_token(
+                token=token_file.read(),
+                verify=True,
+                check_p_header=args.check_protected_header,
+                lower_case_key=False)
+        if args.keyfile:
+            print('Signature OK')
+        print('Token format OK')
+    except ValueError as exc:
+        logger.error(f'Could not extract IAT from COSE:\n\t{exc}')
         sys.exit(1)
 
     if args.print_iat:
@@ -95,4 +117,4 @@
         print('')
 
 if __name__ == '__main__':
-    main()
\ No newline at end of file
+    main()
diff --git a/iat-verifier/scripts/compile_token b/iat-verifier/scripts/compile_token
index 8fac1fc..7fa9816 100755
--- a/iat-verifier/scripts/compile_token
+++ b/iat-verifier/scripts/compile_token
@@ -6,13 +6,15 @@
 #
 #-------------------------------------------------------------------------------
 
+"""CLI tool for compiling token from a yaml file"""
+
 import argparse
 import logging
 import os
 import sys
 
-from ecdsa import SigningKey
-from iatverifier.util import read_token_map, convert_map_to_token
+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
 
@@ -50,36 +52,51 @@
                         required=True)
 
     args = parser.parse_args()
-    signing_key = None
 
-    cose_alg = None
     if args.hmac:
-        method = AttestationTokenVerifier.SIGN_METHOD_MAC0
-        cose_alg = AttestationTokenVerifier.COSE_ALG_HS256
-
-        if args.keyfile:
-            with open(args.keyfile, 'rb') as fh:
-                signing_key = fh.read()
+        METHOD = AttestationTokenVerifier.SIGN_METHOD_MAC0
     elif args.raw:
         if args.keyfile:
             raise ValueError('A keyfile cannot be specified with --raw.')
-        method = AttestationTokenVerifier.SIGN_METHOD_RAW
+        METHOD = AttestationTokenVerifier.SIGN_METHOD_RAW
     else:
-        method = AttestationTokenVerifier.SIGN_METHOD_SIGN1
-        if args.keyfile:
-            with open(args.keyfile) as fh:
-                signing_key = SigningKey.from_pem(fh.read())
+        METHOD = AttestationTokenVerifier.SIGN_METHOD_SIGN1
 
-    verifier = token_verifiers[args.token_type].get_verifier()
-    if verifier.method != method:
-        verifier.method = method
-    if cose_alg is not None and verifier.cose_alg != cose_alg:
-        verifier.cose_alg = cose_alg
+    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)
+
+    verifier_class = token_verifiers[args.token_type]
+    if verifier_class == PSAIoTProfile1TokenVerifier:
+        verifier = PSAIoTProfile1TokenVerifier(
+            method=METHOD,
+            cose_alg=COSE_ALG,
+            signing_key=key,
+            configuration=None)
+    else:
+        logging.error(f'Invalid token type:{verifier_class}\n\t')
+        sys.exit(1)
     token_map = read_token_map(args.source)
 
     if args.outfile:
         with open(args.outfile, 'wb') as wfh:
-            convert_map_to_token(token_map, signing_key, verifier, wfh, args.add_protected_header)
+            convert_map_to_token(
+                token_map,
+                verifier,
+                wfh,
+                add_p_header=args.add_protected_header,
+                name_as_key=True,
+                parse_raw_value=True)
     else:
         with os.fdopen(sys.stdout.fileno(), 'wb') as wfh:
-            convert_map_to_token(token_map, signing_key, verifier, wfh, args.add_protected_header)
+            convert_map_to_token(
+                token_map,
+                verifier,
+                wfh,
+                add_p_header=args.add_protected_header,
+                name_as_key=True,
+                parse_raw_value=True)
diff --git a/iat-verifier/scripts/decompile_token b/iat-verifier/scripts/decompile_token
index d61247f..b64fa59 100755
--- a/iat-verifier/scripts/decompile_token
+++ b/iat-verifier/scripts/decompile_token
@@ -6,15 +6,19 @@
 #
 #-------------------------------------------------------------------------------
 
+"""CLI script for decompiling a cbor formatted IAT file"""
+
 import argparse
+import logging
 import sys
 
 import yaml
-from iatverifier.util import convert_token_to_map
 from iatverifier.psa_iot_profile1_token_verifier import PSAIoTProfile1TokenVerifier
+from iatverifier.attest_token_verifier import AttestationTokenVerifier
 
 
 if __name__ == '__main__':
+    logging.basicConfig(level=logging.INFO)
 
     token_verifiers = {
         "PSA-IoT-Profile1-token": PSAIoTProfile1TokenVerifier,
@@ -31,14 +35,24 @@
                         required=True)
     args = parser.parse_args()
 
-    verifier = token_verifiers[args.token_type].get_verifier()
+    verifier_class = token_verifiers[args.token_type]
+    if verifier_class == PSAIoTProfile1TokenVerifier:
+        verifier = PSAIoTProfile1TokenVerifier(
+            method=AttestationTokenVerifier.SIGN_METHOD_SIGN1,
+            cose_alg=AttestationTokenVerifier.COSE_ALG_ES256,
+            signing_key=None, configuration=None)
+    else:
+        logging.error(f'Invalid token type:{verifier_class}\n\t')
+        sys.exit(1)
     with open(args.source, 'rb') as fh:
-        token_map = convert_token_to_map(fh.read(), verifier)
+        token_map = verifier.parse_token(
+            token=fh.read(),
+            verify=False,
+            check_p_header=False,
+            lower_case_key=True)
 
     if args.outfile:
-        with open(args.outfile, 'w') as wfh:
+        with open(args.outfile, 'w', encoding="UTF-8") as wfh:
             yaml.dump(token_map, wfh)
     else:
         yaml.dump(token_map, sys.stdout)
-
-