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,