feat(iatverifier): add type indicator for CCA tokens
Change-Id: I0cf97e5c2e9eb639f0d7a5c9163f3d9765c47112
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 beed405..16c94a5 100644
--- a/iat-verifier/iatverifier/attest_token_verifier.py
+++ b/iat-verifier/iatverifier/attest_token_verifier.py
@@ -1,5 +1,5 @@
# -----------------------------------------------------------------------------
-# Copyright (c) 2019-2022, Arm Limited. All rights reserved.
+# Copyright (c) 2019-2025, Arm Limited. All rights reserved.
#
# SPDX-License-Identifier: BSD-3-Clause
#
@@ -554,6 +554,13 @@
hmac_msg = Mac0Message(payload=token, key=key, phdr=p_header)
return hmac_msg.encode()
+ def check_type_indicator(self, *, token):
+ # By default no type indication is expected
+ return token
+
+ def encode_type_indicator(self, *, encoder):
+ # By default no type indication is added
+ pass
def _get_cose_sign1_payload(self, cose, *, verify_signature):
msg = Sign1Message.decode(cose)
@@ -628,13 +635,25 @@
# Sign and pack in a COSE envelope if necessary
signed_token = self._sign_token(token)
- # Pack as a bstr if necessary
- if root:
- token_encoder.write(signed_token)
- else:
+ self.encode_type_indicator(encoder=token_encoder)
+
+ if 'encode_type_indicator' in self.__dict__:
+ # If the current verifier has its own encode_type_indicator
+ # implemented, then the signed token needs to be encoded as a
+ # bytestring, and passed to the type indicator encoding like
+ # that:
token_encoder.encode_bytestring(signed_token)
+ else:
+ # Otherwise encoding depends on the 'root' parameter
+ if root:
+ token_encoder.write(signed_token)
+ else:
+ token_encoder.encode_bytestring(signed_token)
def parse_token(self, *, token, lower_case_key):
+
+ token = self.check_type_indicator(token=token)
+
if self._get_method() == AttestationTokenVerifier.SIGN_METHOD_RAW:
payload = token
protected_header = None
diff --git a/iat-verifier/iatverifier/cca_token_verifier.py b/iat-verifier/iatverifier/cca_token_verifier.py
index 8531896..940af76 100644
--- a/iat-verifier/iatverifier/cca_token_verifier.py
+++ b/iat-verifier/iatverifier/cca_token_verifier.py
@@ -1,10 +1,12 @@
# -----------------------------------------------------------------------------
-# Copyright (c) 2022, Arm Limited. All rights reserved.
+# Copyright (c) 2022-2025, Arm Limited. All rights reserved.
#
# SPDX-License-Identifier: BSD-3-Clause
#
# -----------------------------------------------------------------------------
+import cbor2
+
from cryptography.hazmat.primitives import hashes
from pycose.headers import Algorithm
from pycose.algorithms import Es256, Es384, Es512
@@ -27,6 +29,7 @@
from iatverifier.psa_iot_profile1_token_claims import SWComponentTypeClaim, SwComponentVersionClaim
from iatverifier.psa_iot_profile1_token_claims import MeasurementValueClaim, SignerIdClaim
from iatverifier.util import ec2_cose_key_from_raw_ecdsa
+from iatverifier.attest_token_verifier import _CBOR_MAJOR_TYPE_ARRAY
_algorithms = {
Es256: 65,
@@ -34,6 +37,29 @@
Es512: 133,
}
+COAP_CONTENT_INDICATOR = 263
+
+def cca_check_type_indicator(verifier, token):
+ if (isinstance(token, bytes)):
+ # It can happen that the token is not a map structure, but an encoded cbor.
+ # try to parse it:
+ token = cbor2.loads(token)
+ if not isinstance(token, list):
+ msg = f'Expecting a type indicator (token must be a list)'
+ verifier.error(msg)
+ type_indicator_value = token[0]
+ if not isinstance(type_indicator_value, int):
+ msg = f'Expecting a type indicator value is not an int'
+ verifier.error(msg)
+ if type_indicator_value != COAP_CONTENT_INDICATOR:
+ msg = f'Invalid type indicator: {type_indicator_value}'
+ verifier.error(msg)
+ return token[1]
+
+def cca_encode_type_indicator(verifier, encoder):
+ encoder.encode_length(_CBOR_MAJOR_TYPE_ARRAY, 2)
+ encoder.encode_int(COAP_CONTENT_INDICATOR)
+
class CCATokenVerifier(Verifier):
def get_claim_key(self=None):
@@ -46,7 +72,7 @@
return {Algorithm: self._get_cose_alg()}
def _get_wrapping_tag(self):
- return 399
+ return 907
def _parse_p_header(self, msg):
alg = self._get_cose_alg()
@@ -150,7 +176,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=None, configuration, necessity):
+ def __init__(self, *, method, cose_alg, signing_key=None, configuration, necessity, has_type_indicator=True):
verifier_claims= [
(CCARealmChallengeClaim, {'verifier':self, 'expected_challenge_byte': None, 'necessity': Claim.MANDATORY}),
(CCARealmPersonalizationValue, {'verifier':self, 'necessity': Claim.MANDATORY}),
@@ -171,6 +197,10 @@
cose_alg=cose_alg,
signing_key=signing_key)
+ if has_type_indicator:
+ self.check_type_indicator = lambda token: cca_check_type_indicator(self, token)
+ self.encode_type_indicator = lambda encoder: cca_encode_type_indicator(self, encoder)
+
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.
@@ -258,7 +288,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, configuration, necessity, has_type_indicator=True):
# First prepare the claim hierarchy for this token
@@ -290,3 +320,8 @@
method=method,
cose_alg=cose_alg,
signing_key=signing_key)
+
+ if has_type_indicator:
+ self.check_type_indicator = lambda token: cca_check_type_indicator(self, token)
+ self.encode_type_indicator = lambda encoder: cca_encode_type_indicator(self, encoder)
+
diff --git a/iat-verifier/scripts/check_iat b/iat-verifier/scripts/check_iat
index 5cd1e81..6ae45cc 100755
--- a/iat-verifier/scripts/check_iat
+++ b/iat-verifier/scripts/check_iat
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
# -----------------------------------------------------------------------------
-# Copyright (c) 2019-2022, Arm Limited. All rights reserved.
+# Copyright (c) 2019-2025, Arm Limited. All rights reserved.
#
# SPDX-License-Identifier: BSD-3-Clause
#
@@ -69,6 +69,9 @@
help='''The type of the Token.''',
choices=token_verifiers.keys(),
required=True)
+ parser.add_argument('--expect-token-indicator',
+ help='''Expect token indicator in the cbor.''',
+ action='store_true')
args = parser.parse_args()
@@ -127,7 +130,8 @@
cose_alg=cose_alg,
signing_key=key,
configuration=config,
- necessity=None)
+ necessity=None,
+ has_type_indicator=args.expect_token_indicator)
elif verifier_class == PSA_2_0_0_TokenVerifier:
key_checked = args.key
key = read_keyfile(keyfile=args.key, method=method)
diff --git a/iat-verifier/scripts/compile_token b/iat-verifier/scripts/compile_token
index 34ec781..9195712 100755
--- a/iat-verifier/scripts/compile_token
+++ b/iat-verifier/scripts/compile_token
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#-------------------------------------------------------------------------------
-# Copyright (c) 2019-2022, Arm Limited. All rights reserved.
+# Copyright (c) 2019-2025, Arm Limited. All rights reserved.
#
# SPDX-License-Identifier: BSD-3-Clause
#
@@ -59,6 +59,9 @@
help='''The type of the Token.''',
choices=token_verifiers.keys(),
required=True)
+ parser.add_argument('--gen-token-indicator',
+ help='''Expect token indicator in the cbor.''',
+ action='store_true')
args = parser.parse_args()
@@ -114,7 +117,8 @@
cose_alg=cose_alg,
signing_key=key,
configuration=configuration,
- necessity=None)
+ necessity=None,
+ has_type_indicator=args.gen_token_indicator)
elif verifier_class == PSA_2_0_0_TokenVerifier:
key_checked = args.key
key = read_keyfile(keyfile=args.key, method=METHOD)
diff --git a/iat-verifier/scripts/decompile_token b/iat-verifier/scripts/decompile_token
index 6bcb26f..59c88b1 100755
--- a/iat-verifier/scripts/decompile_token
+++ b/iat-verifier/scripts/decompile_token
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#-------------------------------------------------------------------------------
-# Copyright (c) 2019-2022, Arm Limited. All rights reserved.
+# Copyright (c) 2019-2025, Arm Limited. All rights reserved.
#
# SPDX-License-Identifier: BSD-3-Clause
#
@@ -39,6 +39,9 @@
help='''The type of the Token.''',
choices=token_verifiers.keys(),
required=True)
+ parser.add_argument('--expect-token-indicator',
+ help='''Expect token indicator in the cbor.''',
+ action='store_true')
args = parser.parse_args()
verifier_class = token_verifiers[args.token_type]
@@ -67,7 +70,8 @@
cose_alg=cose_alg,
signing_key=None,
configuration=None,
- necessity=None)
+ necessity=None,
+ has_type_indicator=args.expect_token_indicator)
elif verifier_class == PSA_2_0_0_TokenVerifier:
verifier = PSA_2_0_0_TokenVerifier(
method=AttestationTokenVerifier.SIGN_METHOD_SIGN1,
diff --git a/iat-verifier/tests/data/cca_token.cbor b/iat-verifier/tests/data/cca_token.cbor
index e40f8b7..346c17e 100644
--- a/iat-verifier/tests/data/cca_token.cbor
+++ b/iat-verifier/tests/data/cca_token.cbor
Binary files differ
diff --git a/iat-verifier/tests/test_verifier.py b/iat-verifier/tests/test_verifier.py
index 17658f1..7ded05d 100644
--- a/iat-verifier/tests/test_verifier.py
+++ b/iat-verifier/tests/test_verifier.py
@@ -1,5 +1,5 @@
# -----------------------------------------------------------------------------
-# Copyright (c) 2019-2024, Arm Limited. All rights reserved.
+# Copyright (c) 2019-2025, Arm Limited. All rights reserved.
#
# SPDX-License-Identifier: BSD-3-Clause
#
@@ -114,7 +114,19 @@
cose_alg=Es384,
signing_key=platform_token_key,
configuration=self.config,
- necessity=AttestationClaim.MANDATORY))
+ necessity=AttestationClaim.MANDATORY,
+ has_type_indicator=False))
+
+ create_and_read_iat(
+ DATA_DIR,
+ 'cca_platform_token.yaml',
+ CCAPlatformTokenVerifier(
+ method=method,
+ cose_alg=Es384,
+ signing_key=platform_token_key,
+ configuration=self.config,
+ necessity=AttestationClaim.MANDATORY,
+ has_type_indicator=True))
with self.assertRaises(ValueError) as test_ctx:
create_and_read_iat(