Refactor token parsing to keep data in a tree
Refactor token parsing to keep data in a tree of TokenItem objects.
For each data item the Claim type is stored.
Change-Id: I8bd33b51b7fb8ff86bfdce8ffe3c0c7f9bac5895
Signed-off-by: Mate Toth-Pal <mate.toth-pal@arm.com>
diff --git a/iat-verifier/README.rst b/iat-verifier/README.rst
index a7c00ca..9c26b78 100644
--- a/iat-verifier/README.rst
+++ b/iat-verifier/README.rst
@@ -244,24 +244,34 @@
* For each claim a new class must be created that inherits from
``AttestationClaim`` or from one of its descendants
- * ``NonVerifiedClaim`` and ``CompositeAttestClaim`` are descendants of
- ``AttestationClaim``, for details on how to use them see the documentation
- in the class definition.
+ * ``CompositeAttestClaim`` is descendants of ``AttestationClaim``, for
+ details on how to use it see the documentation in the class definition.
- * For each claim non-composite claim, the methods
- ``get_claim_key(self=None)``, ``get_claim_name(self=None)`` and
- ``verify(self, value)`` methods must be implemented. for composite claims
- (that inherit from ``CompositeAttestClaim``), ``verify(self, value)`` is
- implemented by the base class.
+ * For each claim, the methods ``get_claim_key(self=None)``,
+ ``get_claim_name(self=None)`` must be implemented.
+
* Other methods of ``AttestationClaim`` are optional to override.
+ * Any claim that inherits from ``AttestationClaim`` might have a ``verify``
+ method (``def verify(self, token_item):``). This method is called when the
+ ``verify()`` method of a ``TokenItem`` object is called. ``TokenItem``'s
+ ``verify()`` method walks up the inheritance tree of the ``claim_type``
+ object's class in that ``TokenItem``. If a class in the walk path has a
+ ``verify()`` method, calls it. For further details see ``TokenItem``'s
+ ``_call_verify_with_parents()`` method.
+
+ Any verify method needs to call ``AttestationClaim``'s ``error()`` or
+ ``warning()`` in case of a problem. If the actual class inherits from
+ ``AttestationTokenVerifier`` this can be done like
+ ``self.error('Meaningful error message.')``. In other cases
+ ``self.verifier.error('Meaningful error message.')``
+
#. Create a file for the new token in `tf-m-tools/iat-verifier/iatverifier`.
* Create a new class for the token type. It must inherit from the class
``AttestationTokenVerifier``.
* Implement ``get_claim_key(self=None)`` and ``get_claim_name(self=None)``
- (The return value of ``get_claim_key(self=None)`` is not used)
* Implement the ``__init__(self, ...)`` function. This function must create a
list with the claims that are accepted by this token. (Note that the
@@ -280,7 +290,7 @@
The list of claims must be passed to the init function of the base class.
- * Implement ``check_cross_claim_requirements`` for the token if necessary
+ For example see *iat-verifier/iatverifier/cca_token_verifier.py*.
#. Add handling of the new token type to the ``check_iat``, ``decompile_token``,
and ``compile_token`` scripts.
diff --git a/iat-verifier/iatverifier/attest_token_verifier.py b/iat-verifier/iatverifier/attest_token_verifier.py
index c0f8e5f..3e288b1 100644
--- a/iat-verifier/iatverifier/attest_token_verifier.py
+++ b/iat-verifier/iatverifier/attest_token_verifier.py
@@ -29,6 +29,36 @@
_CBOR_MAJOR_TYPE_MAP = 5
_CBOR_MAJOR_TYPE_SEMANTIC_TAG = 6
+class TokenItem:
+ """This class represents an item in the token map
+
+ The Field `claim_type` contains an AttestationClaim object, that determines how to interpret the
+ `value` field.
+ The field `value` contains either another TokenItem object or a representation of a claim value
+ (list, dictionary, bytestring...) depending on the value of `claim_type`
+
+ A TokenItem object might have extra fields beyond these as it might be necessary to store
+ properties during parsing, that can aid verifying.
+ """
+ def __init__(self, *, value, claim_type):
+ self.value = value # The value of the claim
+ self.claim_type = claim_type # an AttestationClaim instance
+
+ @classmethod
+ def _call_verify_with_parents(cls, claim_type_class, claim_type, token_item, indent):
+ for parent_class in claim_type_class.__bases__:
+ cls._call_verify_with_parents(parent_class, claim_type, token_item, indent + 2)
+ if "verify" in claim_type_class.__dict__:
+ claim_type_class.verify(claim_type, token_item)
+
+ def verify(self):
+ """Calls claim_type's and its parents' verify method"""
+ claim_type = self.claim_type
+ self.__class__._call_verify_with_parents(claim_type.__class__, claim_type, self, 0)
+
+ def get_token_map(self):
+ return self.claim_type.get_token_map(self)
+
class AttestationClaim(ABC):
"""
This class represents a claim.
@@ -38,12 +68,6 @@
This class contains methods that are not abstract. These are here as a
default behavior, that a derived class might either keep, or override.
-
- A token is built up as a hierarchy of claim classes. Although it is
- important, that claim objects don't have a 'value' field. The actual parsed
- token is stored in a map structure. It is possible to execute operations
- on a token map, and the operations are defined by claim classes/objects.
- Such operations are for example verifying a token.
"""
MANDATORY = 0
@@ -54,21 +78,12 @@
self.config = verifier.config
self.verifier = verifier
self.necessity = necessity
- self.verify_count = 0
- self.cross_claim_requirement_checker = None
#
# Abstract methods
#
@abstractmethod
- def verify(self, value):
- """Verify this claim
-
- Throw an exception if the claim is not valid"""
- raise NotImplementedError
-
- @abstractmethod
def get_claim_key(self=None):
"""Get the key of this claim
@@ -92,7 +107,7 @@
"""
Decode the value of the claim if the value is an UTF-8 string
"""
- if type(self).is_utf_8():
+ if self.__class__.is_utf_8():
try:
return value.decode()
except UnicodeDecodeError as exc:
@@ -102,10 +117,6 @@
else: # not a UTF-8 value, i.e. a bytestring
return value
- def claim_found(self):
- """Return true if verify was called on tis claim instance"""
- return self.verify_count>0
-
@classmethod
def is_utf_8(cls):
"""Returns whether the value of this claim should be UTF-8"""
@@ -121,32 +132,19 @@
# pylint: disable=unused-argument
value = token_map
if parse_raw_value:
- value = type(self).parse_raw(value)
+ value = self.__class__.parse_raw(value)
return token_encoder.encode(value)
- def parse_token(self, *, token, verify, check_p_header, lower_case_key):
+ def parse_token(self, *, token, check_p_header, lower_case_key):
"""Parse a token into a map
This function is recursive for composite claims and for token verifiers.
- A big difference is that the parameter token should be a map for claim
- objects, and a 'bytes' object for verifiers. The entry point to this
- function is calling the parse_token function of a verifier.
- From some aspects it would be cleaner to have different functions for
- this in verifiers and claims, but that would require to do a type check
- in every recursive step to see which method to call. So instead the
- method name is the same, and the 'token' parameter is interpreted
- differently."""
- # pylint: disable=unused-argument
- if verify:
- self.verify(token)
-
- formatted = type(self).get_formatted_value(token)
-
- # If the formatted value is still a bytestring then try to decode
- if isinstance(formatted, bytes):
- formatted = self.decode(formatted)
- return formatted
+ The `token` parameter can be interpreted differently in derived classes:
+ - as a raw token that is decoded by the CBOR parsing library
+ - as CBOR encoded token in case of (nested) tokens.
+ """
+ return TokenItem(value=token, claim_type=self)
@classmethod
def parse_raw(cls, raw_value):
@@ -213,22 +211,23 @@
msg = 'Invalid {} length: must be at least {} bytes, found {} bytes'
self.verifier.error(msg.format(name, minimal_length, value_len))
+ def get_token_map(self, token_item):
+ formatted = self.__class__.get_formatted_value(token_item.value)
-class NonVerifiedClaim(AttestationClaim):
- """An abstract claim type for which verify() always passes.
+ # If the formatted value is still a bytestring then try to decode
+ if isinstance(formatted, bytes):
+ formatted = self.decode(formatted)
- Can be used for claims for which no verification is implemented."""
- def verify(self, value):
- self.verify_count += 1
+ return formatted
class CompositeAttestClaim(AttestationClaim):
"""
This class represents composite claim.
This class is still abstract, but can contain other claims. This means that
- a value representing this claim is a dictionary. This claim contains further
- claims which represent the possible key-value pairs in the value for this
- claim.
+ a value representing this claim is a dictionary, or a list of dictionaries.
+ This claim contains further claims which represent the possible key-value
+ pairs in the value for this claim.
It is possible that there are requirement that the claims in this claim must
satisfy, but this can't be checked in the `verify` function of a claim.
@@ -236,15 +235,14 @@
For example the composite claim can contain a claim type `A`, and a claim
type `B`, exactly one of the two can be present.
- In this case a method must be passed in the `cross_claim_requirement_checker`
- parameter of the `__init__` function, that does this check.
+ In this case the class inheriting from this class can have its own verify()
+ method.
"""
def __init__(self,
*, verifier,
claims,
is_list,
- cross_claim_requirement_checker,
necessity=AttestationClaim.MANDATORY):
""" Initialise a composite claim.
@@ -257,107 +255,128 @@
super().__init__(verifier=verifier, necessity=necessity)
self.is_list = is_list
self.claims = claims
- self.cross_claim_requirement_checker = cross_claim_requirement_checker
def _get_contained_claims(self):
- claims = []
for claim, args in self.claims:
try:
- claims.append(claim(**args))
+ yield claim(**args)
except TypeError as exc:
raise TypeError(f"Failed to instantiate '{claim}' with args '{args}' in token " +
f"{type(self.verifier)}\nSee error in exception above.") from exc
- return claims
- def verify(self, value):
- # No actual verification is done here. The `verify` function of the contained claims
- # is called during traversing of the token tree.
- self.verify_count += 1
+ def _verify_dict(self, claim_type, entry_number, dictionary):
+ if not isinstance(dictionary, dict):
+ if self.config.strict:
+ msg = 'The values in token {} must be a dict.'
+ self.verifier.error(msg.format(claim_type.get_claim_name()))
+ else:
+ msg = 'The values in token {} must be a dict, skipping'
+ self.verifier.warning(msg.format(claim_type.get_claim_name()))
+ return
- def _parse_token_dict(self, *, entry_number, token, verify, check_p_header, lower_case_key):
- ret = {}
-
- if verify:
- self.verify(token)
- if not self._check_type(self.get_claim_name(), token, dict):
- return None
- else:
- if not isinstance(token, dict):
- return token
-
- claims = {val.get_claim_key(): val for val in self._get_contained_claims()}
- for key, val in token.items():
- if key not in claims.keys():
- if verify and self.config.strict:
+ claim_names = [val.get_claim_name() for val in claim_type._get_contained_claims()]
+ for claim_name, _ in dictionary.items():
+ if claim_name not in claim_names:
+ if self.config.strict:
msg = 'Unexpected {} claim: {}'
- self.verifier.error(msg.format(self.get_claim_name(), key))
+ self.verifier.error(msg.format(claim_type.get_claim_name(), claim_name))
else:
msg = 'Unexpected {} claim: {}, skipping.'
- self.verifier.warning(msg.format(self.get_claim_name(), key))
+ self.verifier.warning(msg.format(claim_type.get_claim_name(), claim_name))
continue
- try:
- claim = claims[key]
- name = claim.get_claim_name()
- if lower_case_key:
- name = name.lower()
- ret[name] = claim.parse_token(
- token=val,
- verify=verify,
- check_p_header=check_p_header,
- lower_case_key=lower_case_key)
- except Exception:
- if not self.config.keep_going:
- raise
- if verify:
- self._check_claims_necessity(entry_number, claims)
- if self.cross_claim_requirement_checker is not None:
- self.cross_claim_requirement_checker(self.verifier, claims)
+ claims = {val.get_claim_key(): val for val in claim_type._get_contained_claims()}
+ self._check_claims_necessity(entry_number, claims, dictionary)
+ for token_item in dictionary.values():
+ if isinstance(token_item, TokenItem):
+ token_item.verify()
+ else:
+ # the parse of this token item failed. So it cannot be verified.
+ # Warning had been reported during parsing.
+ pass
- return ret
+ def verify(self, token_item):
+ if self.is_list:
+ if not isinstance(token_item.value, list):
+ if self.config.strict:
+ msg = 'The value of this token {} must be a list.'
+ self.verifier.error(msg.format(self.get_claim_name()))
+ else:
+ msg = 'The value of this token {} must be a list, skipping'
+ self.verifier.warning(msg.format(self.get_claim_name()))
+ return
+ for entry_number, list_item in enumerate(token_item.value):
+ self._verify_dict(token_item.claim_type, entry_number, list_item)
+ else:
+ self._verify_dict(token_item.claim_type, None, token_item.value)
- def _check_claims_necessity(self, entry_number, claims):
- for claim in claims.values():
- if not claim.claim_found():
- if claim.necessity==AttestationClaim.MANDATORY:
- msg = (f'Invalid IAT: missing MANDATORY claim "{claim.get_claim_name()}" '
- f'from {self.get_claim_name()}')
- if entry_number is not None:
- msg += f' at index {entry_number}'
- self.verifier.error(msg)
- elif claim.necessity==AttestationClaim.RECOMMENDED:
- msg = (f'Missing RECOMMENDED claim "{claim.get_claim_name()}" '
- f'from {self.get_claim_name()}')
- if entry_number is not None:
- msg += f' at index {entry_number}'
- self.verifier.warning(msg)
+ def _parse_token_dict(self, *, entry_number, token, check_p_header, lower_case_key):
+ claim_value = {}
- def parse_token(self, *, token, verify, check_p_header, lower_case_key):
+ if not isinstance(token, dict):
+ claim_value = token
+ else:
+ claims = {val.get_claim_key(): val for val in self._get_contained_claims()}
+ for key, val in token.items():
+ try:
+ claim = claims[key]
+ name = claim.get_claim_name()
+ if lower_case_key:
+ name = name.lower()
+ claim_value[name] = claim.parse_token(
+ token=val,
+ check_p_header=check_p_header,
+ lower_case_key=lower_case_key)
+ except KeyError:
+ claim_value[key] = val
+ except Exception:
+ if not self.config.keep_going:
+ raise
+ return claim_value
+
+ def _check_claims_necessity(self, entry_number, claims, dictionary):
+ mandatory_claim_names = [claim.get_claim_name() for claim in claims.values() if claim.necessity == AttestationClaim.MANDATORY]
+ recommended_claim_names = [claim.get_claim_name() for claim in claims.values() if claim.necessity == AttestationClaim.RECOMMENDED]
+ dictionary_claim_names = dictionary.keys()
+
+ for mandatory_claim_name in mandatory_claim_names:
+ if mandatory_claim_name not in dictionary_claim_names:
+ msg = (f'Invalid IAT: missing MANDATORY claim "{mandatory_claim_name}" '
+ f'from {self.get_claim_name()}')
+ if entry_number is not None:
+ msg += f' at index {entry_number}'
+ self.verifier.error(msg)
+
+ for recommended_claim_name in recommended_claim_names:
+ if recommended_claim_name not in dictionary_claim_names:
+ msg = (f'Missing RECOMMENDED claim "{recommended_claim_name}" '
+ f'from {self.get_claim_name()}')
+ if entry_number is not None:
+ msg += f' at index {entry_number}'
+ self.verifier.warning(msg)
+
+ def parse_token(self, *, token, check_p_header, lower_case_key):
"""This expects a raw token map as 'token'"""
if self.is_list:
- ret = []
- if verify:
- if not self._check_type(self.get_claim_name(), token, list):
- return None
+ claim_value = []
+ if not isinstance(token, list):
+ claim_value = token
else:
- if not isinstance(token, list):
- return token
- for entry_number, entry in enumerate(token):
- ret.append(self._parse_token_dict(
- entry_number=entry_number,
- check_p_header=check_p_header,
- token=entry,
- verify=verify,
- lower_case_key=lower_case_key))
- return ret
- return self._parse_token_dict(
- entry_number=None,
- check_p_header=check_p_header,
- token=token,
- verify=verify,
- lower_case_key=lower_case_key)
+ for entry_number, entry in enumerate(token):
+ claim_value.append(self._parse_token_dict(
+ entry_number=entry_number,
+ check_p_header=check_p_header,
+ token=entry,
+ lower_case_key=lower_case_key))
+ else:
+ claim_value = self._parse_token_dict(
+ entry_number=None,
+ check_p_header=check_p_header,
+ token=token,
+ lower_case_key=lower_case_key)
+ return TokenItem(value=claim_value, claim_type=self)
def _encode_dict(self, token_encoder, token_map, *, add_p_header, name_as_key, parse_raw_value):
@@ -410,6 +429,20 @@
name_as_key=name_as_key,
parse_raw_value=parse_raw_value)
+ def get_token_map(self, token_item):
+ if self.is_list:
+ ret = []
+ for token_item_dict in token_item.value:
+ token_dict = {}
+ for key, claim_token_item in token_item_dict.items():
+ token_dict[key] = claim_token_item.get_token_map()
+ ret.append(token_dict)
+ return ret
+ else:
+ token_dict = {}
+ for key, claim_token_item in token_item.value.items():
+ token_dict[key] = claim_token_item.get_token_map()
+ return token_dict
@dataclass
class VerifierConfiguration:
@@ -430,13 +463,9 @@
return None
def get_claim_name(self=None):
- return "TOKEN_CLAIM"
+ return "TOKEN_ROOT_CLAIMS"
-# This class inherits from NonVerifiedClaim. The actual claims in the token are
-# checked by the AttestTokenRootClaims object owned by this verifier. The
-# verify() function of the AttestTokenRootClaims object is called during
-# traversing the claim tree.
-class AttestationTokenVerifier(NonVerifiedClaim):
+class AttestationTokenVerifier(AttestationClaim):
"""Abstract base class for attestation token verifiers"""
SIGN_METHOD_SIGN1 = "sign"
@@ -472,11 +501,6 @@
def _parse_p_header(self, msg):
"""Throw exception in case of error"""
- @staticmethod
- @abstractmethod
- def check_cross_claim_requirements(verifier, claims):
- """Throw exception in case of error"""
-
def _get_cose_alg(self):
return self.cose_alg
@@ -503,7 +527,6 @@
verifier=self,
claims=claims,
is_list=False,
- cross_claim_requirement_checker=type(self).check_cross_claim_requirements,
necessity=necessity)
super().__init__(verifier=self, necessity=necessity)
@@ -594,7 +617,6 @@
err_msg = f'Unexpected method "{self._get_method()}"; must be one of: sign, mac'
raise ValueError(err_msg)
-
def convert_map_to_token(
self,
token_encoder,
@@ -635,7 +657,7 @@
else:
token_encoder.encode_bytestring(signed_token)
- def parse_token(self, *, token, verify, check_p_header, lower_case_key):
+ def parse_token(self, *, token, check_p_header, lower_case_key):
if self._get_method() == AttestationTokenVerifier.SIGN_METHOD_RAW:
payload = token
else:
@@ -643,7 +665,8 @@
payload = self._get_cose_payload(
token,
check_p_header=check_p_header,
- verify_signature=(verify and self._get_signing_key() is not None))
+ # signature verification is done in the verify function
+ verify_signature=False)
except Exception as exc:
msg = f'Bad COSE: {exc}'
self.error(msg)
@@ -654,30 +677,52 @@
msg = f'Invalid CBOR: {exc}'
self.error(msg)
- wrapping_tag = self._get_wrapping_tag()
-
if isinstance(raw_map, _cbor2.CBORTag):
+ raw_map_tag = raw_map.tag
+ raw_map = raw_map.value
+ else:
+ raw_map_tag = None
+
+ token_items = self.claims.parse_token(
+ token=raw_map,
+ check_p_header=check_p_header,
+ lower_case_key=lower_case_key)
+
+ ret = TokenItem(value=token_items, claim_type=self)
+ ret.wrapping_tag = raw_map_tag
+ ret.token = token
+ ret.check_p_header = check_p_header
+ return ret
+
+ def verify(self, token_item):
+ if self._get_method() != AttestationTokenVerifier.SIGN_METHOD_RAW:
+ try:
+ self._get_cose_payload(
+ token_item.token,
+ check_p_header=token_item.check_p_header,
+ verify_signature=(self._get_signing_key() is not None))
+ except Exception as exc:
+ msg = f'Bad COSE: {exc}'
+ raise ValueError(msg) from exc
+
+ wrapping_tag = self._get_wrapping_tag()
+ if token_item.wrapping_tag is not None:
if wrapping_tag is None:
- msg = f'Invalid token: Unexpected tag (0x{raw_map.tag:x}) in token {self.get_claim_name()}'
+ msg = f'Invalid token: Unexpected tag (0x{token_item.wrapping_tag:x}) in token {self.get_claim_name()}'
self.error(msg)
else:
- if wrapping_tag != raw_map.tag:
- msg = f'Invalid token: token {self.get_claim_name()} is wrapped in tag 0x{raw_map.tag:x} instead of 0x{wrapping_tag:x}'
+ if wrapping_tag != token_item.wrapping_tag:
+ msg = f'Invalid token: token {self.get_claim_name()} is wrapped in tag 0x{token_item.wrapping_tag:x} instead of 0x{wrapping_tag:x}'
self.error(msg)
- raw_map = raw_map.value
else:
if wrapping_tag is not None:
msg = f'Invalid token: token {self.get_claim_name()} should be wrapped in tag 0x{wrapping_tag:x}'
self.error(msg)
- if verify:
- self.verify(token)
+ token_item.value.verify()
- return self.claims.parse_token(
- token=raw_map,
- check_p_header=check_p_header,
- verify=verify,
- lower_case_key=lower_case_key)
+ def get_token_map(self, token_item):
+ return self.claims.get_token_map(token_item.value)
def error(self, message, *, exception=None):
"""Act on an error depending on the configuration of this verifier"""
diff --git a/iat-verifier/iatverifier/cca_claims.py b/iat-verifier/iatverifier/cca_claims.py
index 684ece0..1b1ccbe 100644
--- a/iat-verifier/iatverifier/cca_claims.py
+++ b/iat-verifier/iatverifier/cca_claims.py
@@ -8,7 +8,7 @@
from collections.abc import Iterable
import logging
-from iatverifier.attest_token_verifier import AttestationClaim, NonVerifiedClaim, CompositeAttestClaim
+from iatverifier.attest_token_verifier import AttestationClaim, CompositeAttestClaim
logger = logging.getLogger('iat-verifiers')
@@ -23,16 +23,15 @@
def get_claim_name(self=None):
return 'CCA_REALM_CHALLENGE'
- def verify(self, value):
- self._validate_bytestring_length_equals(value, self.get_claim_name(), 64)
+ def verify(self, token_item):
+ self._validate_bytestring_length_equals(token_item.value, self.get_claim_name(), 64)
if self.expected_challenge_byte is not None:
- for i, b in enumerate(value):
+ for i, b in enumerate(token_item.value):
if b != self.expected_challenge_byte:
- print (f'Challenge = {value}')
+ print (f'Challenge = {token_item.value}')
msg = 'Invalid CHALLENGE byte at {:d}: 0x{:02x} instead of 0x{:02x}'
self.verifier.error(msg.format(i, b, self.expected_challenge_byte))
break
- self.verify_count += 1
class CCARealmPersonalizationValue(AttestationClaim):
def get_claim_key(self=None):
@@ -41,9 +40,8 @@
def get_claim_name(self=None):
return 'CCA_REALM_PERSONALIZATION_VALUE'
- def verify(self, value):
- self._validate_bytestring_length_equals(value, self.get_claim_name(), 64)
- self.verify_count += 1
+ def verify(self, token_item):
+ self._validate_bytestring_length_equals(token_item.value, self.get_claim_name(), 64)
class CCARealmInitialMeasurementClaim(AttestationClaim):
def get_claim_key(self=None):
@@ -52,9 +50,8 @@
def get_claim_name(self=None):
return 'CCA_REALM_INITIAL_MEASUREMENT'
- def verify(self, value):
- self._check_type(self.get_claim_name(), value, bytes)
- self.verify_count += 1
+ def verify(self, token_item):
+ self._check_type(self.get_claim_name(), token_item.value, bytes)
class CCARealmExtensibleMeasurementsClaim(AttestationClaim):
def get_claim_key(self=None):
@@ -63,21 +60,20 @@
def get_claim_name(self=None):
return 'CCA_REALM_EXTENSIBLE_MEASUREMENTS'
- def verify(self, value):
+ def verify(self, token_item):
min_measurement_count = 4
max_measurement_count = 4
- if not isinstance(value, Iterable):
+ if not isinstance(token_item.value, Iterable):
msg = 'Invalid {:s}: Value must be a list.'
self.verifier.error(msg.format(self.get_claim_name()))
- if len(value) < min_measurement_count or len(value) > max_measurement_count:
+ if len(token_item.value) < min_measurement_count or len(token_item.value) > max_measurement_count:
msg = 'Invalid {:s}: Value must be a list of min {:d} elements and max {:d} elements.'
self.verifier.error(msg.format(self.get_claim_name(), min_measurement_count,
max_measurement_count))
- for v in value:
+ for v in token_item.value:
self._validate_bytestring_length_one_of(v, self.get_claim_name()+f'[{str()}]', [32, 64])
- self.verify_count += 1
-class CCARealmHashAlgorithmIdClaim(NonVerifiedClaim):
+class CCARealmHashAlgorithmIdClaim(AttestationClaim):
def get_claim_key(self=None):
return 44236
@@ -88,7 +84,7 @@
def is_utf_8(cls):
return True
-class CCARealmPubKeyHashAlgorithmIdClaim(NonVerifiedClaim):
+class CCARealmPubKeyHashAlgorithmIdClaim(AttestationClaim):
def get_claim_key(self=None):
return 44240
@@ -106,9 +102,8 @@
def get_claim_name(self=None):
return 'CCA_REALM_PUB_KEY'
- def verify(self, value):
- self._validate_bytestring_length_equals(value, self.get_claim_name(), 97)
- self.verify_count += 1
+ def verify(self, token_item):
+ self._validate_bytestring_length_equals(token_item.value, self.get_claim_name(), 97)
class CCAAttestationProfileClaim(AttestationClaim):
def get_claim_key(self=None):
@@ -121,13 +116,12 @@
def is_utf_8(cls):
return True
- def verify(self, value):
+ def verify(self, token_item):
expected_value = "http://arm.com/CCA-SSD/1.0.0"
- self._check_type(self.get_claim_name(), value, str)
- if value != expected_value:
+ self._check_type(self.get_claim_name(), token_item.value, str)
+ if token_item.value != expected_value:
msg = 'Invalid Attest profile "{}": must be "{}"'
- self.verifier.error(msg.format(value, expected_value))
- self.verify_count += 1
+ self.verifier.error(msg.format(token_item.value, expected_value))
class CCAPlatformChallengeClaim(AttestationClaim):
def get_claim_key(self=None):
@@ -136,9 +130,8 @@
def get_claim_name(self=None):
return 'CCA_PLATFORM_CHALLENGE'
- def verify(self, value):
- self._validate_bytestring_length_one_of(value, self.get_claim_name(), [32, 48, 64])
- self.verify_count += 1
+ def verify(self, token_item):
+ self._validate_bytestring_length_one_of(token_item.value, self.get_claim_name(), [32, 48, 64])
class CCAPlatformImplementationIdClaim(AttestationClaim):
def get_claim_key(self=None):
@@ -147,9 +140,8 @@
def get_claim_name(self=None):
return 'CCA_PLATFORM_IMPLEMENTATION_ID'
- def verify(self, value):
- self._validate_bytestring_length_equals(value, self.get_claim_name(), 32)
- self.verify_count += 1
+ def verify(self, token_item):
+ self._validate_bytestring_length_equals(token_item.value, self.get_claim_name(), 32)
class CCAPlatformInstanceIdClaim(AttestationClaim):
def get_claim_key(self=None):
@@ -158,12 +150,11 @@
def get_claim_name(self=None):
return 'CCA_PLATFORM_INSTANCE_ID'
- def verify(self, value):
- self._validate_bytestring_length_between(value, self.get_claim_name(), 7, 33)
- if value[0] != 0x01:
+ def verify(self, token_item):
+ self._validate_bytestring_length_between(token_item.value, self.get_claim_name(), 7, 33)
+ if token_item.value[0] != 0x01:
msg = 'Invalid Instance ID first byte "0x{:02x}": must be "0x01"'
- self.verifier.error(msg.format(value[0]))
- self.verify_count += 1
+ self.verifier.error(msg.format(token_item.value[0]))
class CCAPlatformConfigClaim(AttestationClaim):
def get_claim_key(self=None):
@@ -172,9 +163,8 @@
def get_claim_name(self=None):
return 'CCA_PLATFORM_CONFIG'
- def verify(self, value):
- self._check_type(self.get_claim_name(), value, bytes)
- self.verify_count += 1
+ def verify(self, token_item):
+ self._check_type(self.get_claim_name(), token_item.value, bytes)
class CCAPlatformLifecycleClaim(AttestationClaim):
@@ -222,19 +212,18 @@
return f"{text}_{value:04x}".lower()
return f"INVALID_{value:04x}"
- def verify(self, value):
- self._check_type(self.get_claim_name, value, int)
+ def verify(self, token_item):
+ self._check_type(self.get_claim_name, token_item.value, int)
value_valid = False
for _, min, max in CCAPlatformLifecycleClaim.SL_VALUES:
- if min <= value <= max:
+ if min <= token_item.value <= max:
value_valid = True
break
if not value_valid:
msg = 'Invalid Platform Lifecycle claim "0x{:02x}"'
- self.verifier.error(msg.format(value))
- self.verify_count += 1
+ self.verifier.error(msg.format(token_item.value))
-class CCASwCompHashAlgIdClaim(NonVerifiedClaim):
+class CCASwCompHashAlgIdClaim(AttestationClaim):
def get_claim_key(self=None):
return 6
@@ -253,7 +242,7 @@
def get_claim_name(self=None):
return 'CCA_PLATFORM_SW_COMPONENTS'
-class CCAPlatformVerificationServiceClaim(NonVerifiedClaim):
+class CCAPlatformVerificationServiceClaim(AttestationClaim):
def get_claim_key(self=None):
return 2400
@@ -264,7 +253,7 @@
def is_utf_8(cls):
return True
-class CCAPlatformHashAlgorithmIdClaim(NonVerifiedClaim):
+class CCAPlatformHashAlgorithmIdClaim(AttestationClaim):
def get_claim_key(self=None):
return 2402
diff --git a/iat-verifier/iatverifier/cca_token_verifier.py b/iat-verifier/iatverifier/cca_token_verifier.py
index dad5680..79fee2c 100644
--- a/iat-verifier/iatverifier/cca_token_verifier.py
+++ b/iat-verifier/iatverifier/cca_token_verifier.py
@@ -66,11 +66,6 @@
cose_alg=Verifier.COSE_ALG_ES256,
signing_key=None)
- @staticmethod
- def check_cross_claim_requirements(verifier, claims):
- pass
-
-
class CCARealmTokenVerifier(Verifier):
def get_claim_key(self=None):
return 44241
@@ -113,11 +108,6 @@
cose_alg=cose_alg,
signing_key=signing_key)
- @staticmethod
- def check_cross_claim_requirements(verifier, claims):
- pass
-
-
class CCAPlatformTokenVerifier(Verifier):
def get_claim_key(self=None):
return 44234 #0xACCA
@@ -159,7 +149,7 @@
(CCAPlatformInstanceIdClaim, {'verifier':self, 'necessity': Claim.MANDATORY}),
(CCAPlatformConfigClaim, {'verifier':self, 'necessity': Claim.MANDATORY}),
(CCAPlatformLifecycleClaim, {'verifier':self, 'necessity': Claim.MANDATORY}),
- (CCAPlatformSwComponentsClaim, {'verifier':self, 'claims': sw_component_claims, 'is_list': True, 'cross_claim_requirement_checker':None, 'necessity': Claim.MANDATORY}),
+ (CCAPlatformSwComponentsClaim, {'verifier':self, 'claims': sw_component_claims, 'is_list': True, 'necessity': Claim.MANDATORY}),
(CCAPlatformVerificationServiceClaim, {'verifier':self, 'necessity': Claim.OPTIONAL}),
(CCAPlatformHashAlgorithmIdClaim, {'verifier':self, 'necessity': Claim.MANDATORY}),
]
@@ -172,8 +162,3 @@
method=method,
cose_alg=cose_alg,
signing_key=signing_key)
-
- @staticmethod
- def check_cross_claim_requirements(verifier, claims):
- pass
-
diff --git a/iat-verifier/iatverifier/psa_2_0_0_token_claims.py b/iat-verifier/iatverifier/psa_2_0_0_token_claims.py
index 63d49ee..06f0e78 100644
--- a/iat-verifier/iatverifier/psa_2_0_0_token_claims.py
+++ b/iat-verifier/iatverifier/psa_2_0_0_token_claims.py
@@ -7,7 +7,7 @@
import string
-from iatverifier.attest_token_verifier import AttestationClaim, NonVerifiedClaim
+from iatverifier.attest_token_verifier import AttestationClaim
from iatverifier.attest_token_verifier import CompositeAttestClaim
# IAT custom claims
@@ -28,12 +28,11 @@
def get_claim_name(self=None):
return 'INSTANCE_ID'
- def verify(self, value):
- self._validate_bytestring_length_equals(value, 'INSTANCE_ID', self.expected_len)
- if value[0] != 0x01:
+ def verify(self, token_item):
+ self._validate_bytestring_length_equals(token_item.value, 'INSTANCE_ID', self.expected_len)
+ if token_item.value[0] != 0x01:
msg = 'Invalid INSTANCE_ID: first byte must be 0x01, found: 0x{}'
- self.verifier.error(msg.format(value[0]))
- self.verify_count += 1
+ self.verifier.error(msg.format(token_item.value[0]))
class ChallengeClaim(AttestationClaim):
@@ -46,17 +45,16 @@
def get_claim_name(self=None):
return 'CHALLENGE'
- def verify(self, value):
- self._check_type('CHALLENGE', value, bytes)
+ def verify(self, token_item):
+ self._check_type('CHALLENGE', token_item.value, bytes)
- value_len = len(value)
+ value_len = len(token_item.value)
if value_len not in ChallengeClaim.HASH_SIZES:
msg = 'Invalid CHALLENGE length; must one of {}, found {} bytes'
self.verifier.error(msg.format(ChallengeClaim.HASH_SIZES, value_len))
- self.verify_count += 1
-class ImplementationIdClaim(NonVerifiedClaim):
+class ImplementationIdClaim(AttestationClaim):
"""Class representing a PSA Attestation Token Implementation ID claim"""
def get_claim_key(self=None):
return ARM_RANGE + 3
@@ -67,20 +65,19 @@
class CertificationReference(AttestationClaim):
"""Class representing a PSA Attestation Token Certification Reference claim"""
- def verify(self, value):
- self._check_type('CERTIFICATION_REFERENCE', value, str)
+ def verify(self, token_item):
+ self._check_type('CERTIFICATION_REFERENCE', token_item.value, str)
- value_len = len(value)
+ value_len = len(token_item.value)
expected_len = 19 # 'EAN13-Version' 13 + '-' + 5. e.g.:0604565272829-10010
- if len(value) != expected_len:
+ if len(token_item.value) != expected_len:
msg = 'Invalid CERTIFICATION_REFERENCE length; must be {} characters, found {} characters'
self.verifier.error(msg.format(expected_len, value_len))
- for idx, character in enumerate(value):
+ for idx, character in enumerate(token_item.value):
if character not in string.digits and character not in '-':
msg = 'Invalid character {} at position {}'
self.verifier.error(msg.format(character, idx+1))
- self.verify_count += 1
def get_claim_key(self=None):
return ARM_RANGE + 5
@@ -101,7 +98,7 @@
def get_claim_name(self=None):
return 'SW_COMPONENTS'
-class SWComponentTypeClaim(NonVerifiedClaim):
+class SWComponentTypeClaim(AttestationClaim):
"""Class representing a PSA Attestation Token Software Component Measurement Type claim"""
def get_claim_key(self=None):
return SW_COMPONENT_RANGE + 1
@@ -121,9 +118,8 @@
def get_claim_name(self=None):
return 'CLIENT_ID'
- def verify(self, value):
- self._check_type('CLIENT_ID', value, int)
- self.verify_count += 1
+ def verify(self, token_item):
+ self._check_type('CLIENT_ID', token_item.value, int)
class SecurityLifecycleClaim(AttestationClaim):
"""Class representing a PSA Attestation Token Security Lifecycle claim"""
@@ -152,9 +148,8 @@
def get_claim_name(self=None):
return 'SECURITY_LIFECYCLE'
- def verify(self, value):
- self._check_type('SECURITY_LIFECYCLE', value, int)
- self.verify_count += 1
+ def verify(self, token_item):
+ self._check_type('SECURITY_LIFECYCLE', token_item.value, int)
@staticmethod
def parse_raw(raw_value):
@@ -174,13 +169,12 @@
def get_claim_name(self=None):
return 'PROFILE_ID'
- def verify(self, value):
+ def verify(self, token_item):
expected_value = "http://arm.com/psa/2.0.0"
- self._check_type(self.get_claim_name(), value, str)
- if value != expected_value:
+ self._check_type(self.get_claim_name(), token_item.value, str)
+ if token_item.value != expected_value:
msg = 'Invalid Attest profile "{}": must be "{}"'
- self.verifier.error(msg.format(value, expected_value))
- self.verify_count += 1
+ self.verifier.error(msg.format(token_item.value, expected_value))
@classmethod
def is_utf_8(cls):
@@ -195,12 +189,11 @@
def get_claim_name(self=None):
return 'BOOT_SEED'
- def verify(self, value):
- self._validate_bytestring_length_is_at_least(value, 'BOOT_SEED', 32)
- self.verify_count += 1
+ def verify(self, token_item):
+ self._validate_bytestring_length_is_at_least(token_item.value, 'BOOT_SEED', 32)
-class VerificationServiceClaim(NonVerifiedClaim):
+class VerificationServiceClaim(AttestationClaim):
"""Class representing a PSA Attestation Token Verification Service Indicator claim"""
def get_claim_key(self=None):
return ARM_RANGE + 7 # originator
@@ -221,12 +214,11 @@
def get_claim_name(self=None):
return 'SIGNER_ID'
- def verify(self, value):
- self._validate_bytestring_length_is_at_least(value, 'SIGNER_ID', 32)
- self.verify_count += 1
+ def verify(self, token_item):
+ self._validate_bytestring_length_is_at_least(token_item.value, 'SIGNER_ID', 32)
-class SwComponentVersionClaim(NonVerifiedClaim):
+class SwComponentVersionClaim(AttestationClaim):
"""Class representing a PSA Attestation Token Software Component Version claim"""
def get_claim_key(self=None):
return SW_COMPONENT_RANGE + 4
@@ -247,12 +239,11 @@
def get_claim_name(self=None):
return 'MEASUREMENT_VALUE'
- def verify(self, value):
- self._validate_bytestring_length_is_at_least(value, 'MEASUREMENT', 32)
- self.verify_count += 1
+ def verify(self, token_item):
+ self._validate_bytestring_length_is_at_least(token_item.value, 'MEASUREMENT', 32)
-class MeasurementDescriptionClaim(NonVerifiedClaim):
+class MeasurementDescriptionClaim(AttestationClaim):
"""Class representing a PSA Attestation Token Software Component Measurement description claim"""
def get_claim_key(self=None):
return SW_COMPONENT_RANGE + 6
diff --git a/iat-verifier/iatverifier/psa_2_0_0_token_verifier.py b/iat-verifier/iatverifier/psa_2_0_0_token_verifier.py
index b3ff4f1..e8ae28a 100644
--- a/iat-verifier/iatverifier/psa_2_0_0_token_verifier.py
+++ b/iat-verifier/iatverifier/psa_2_0_0_token_verifier.py
@@ -56,7 +56,7 @@
(ImplementationIdClaim, {'verifier': self, 'necessity': Claim.MANDATORY}),
(BootSeedClaim, {'verifier': self, 'necessity': Claim.MANDATORY}),
(CertificationReference, {'verifier': self, 'necessity': Claim.OPTIONAL}),
- (SWComponentsClaim, {'verifier': self, 'claims': sw_component_claims, 'is_list': True, 'cross_claim_requirement_checker':None, 'necessity': Claim.MANDATORY}),
+ (SWComponentsClaim, {'verifier': self, 'claims': sw_component_claims, 'is_list': True, 'necessity': Claim.MANDATORY}),
(ChallengeClaim, {'verifier': self, 'necessity': Claim.MANDATORY}),
(InstanceIdClaim, {'verifier': self, 'expected_len': 33, 'necessity': Claim.MANDATORY}),
(VerificationServiceClaim, {'verifier': self, 'necessity': Claim.OPTIONAL}),
@@ -74,7 +74,3 @@
# Set the properties of this token:
self.signing_key_set = False # None is a valid value for signing_key
self.signing_key = signing_key
-
- @staticmethod
- def check_cross_claim_requirements(verifier, claims):
- pass
diff --git a/iat-verifier/iatverifier/psa_iot_profile1_token_claims.py b/iat-verifier/iatverifier/psa_iot_profile1_token_claims.py
index 44ed4db..a51b544 100644
--- a/iat-verifier/iatverifier/psa_iot_profile1_token_claims.py
+++ b/iat-verifier/iatverifier/psa_iot_profile1_token_claims.py
@@ -11,7 +11,7 @@
import string
-from iatverifier.attest_token_verifier import AttestationClaim, NonVerifiedClaim
+from iatverifier.attest_token_verifier import AttestationClaim
from iatverifier.attest_token_verifier import CompositeAttestClaim
# IAT custom claims
@@ -32,12 +32,11 @@
def get_claim_name(self=None):
return 'INSTANCE_ID'
- def verify(self, value):
- self._validate_bytestring_length_equals(value, 'INSTANCE_ID', self.expected_len)
- if value[0] != 0x01:
+ def verify(self, token_item):
+ self._validate_bytestring_length_equals(token_item.value, 'INSTANCE_ID', self.expected_len)
+ if token_item.value[0] != 0x01:
msg = 'Invalid INSTANCE_ID: first byte must be 0x01, found: 0x{}'
- self.verifier.error(msg.format(value[0]))
- self.verify_count += 1
+ self.verifier.error(msg.format(token_item.value[0]))
class ChallengeClaim(AttestationClaim):
@@ -50,17 +49,16 @@
def get_claim_name(self=None):
return 'CHALLENGE'
- def verify(self, value):
- self._check_type('CHALLENGE', value, bytes)
+ def verify(self, token_item):
+ self._check_type('CHALLENGE', token_item.value, bytes)
- value_len = len(value)
+ value_len = len(token_item.value)
if value_len not in ChallengeClaim.HASH_SIZES:
msg = 'Invalid CHALLENGE length; must one of {}, found {} bytes'
self.verifier.error(msg.format(ChallengeClaim.HASH_SIZES, value_len))
- self.verify_count += 1
-class ImplementationIdClaim(NonVerifiedClaim):
+class ImplementationIdClaim(AttestationClaim):
"""Class representing a PSA Attestation Token Implementation ID claim"""
def get_claim_key(self=None):
return ARM_RANGE - 3
@@ -71,21 +69,20 @@
class HardwareVersionClaim(AttestationClaim):
"""Class representing a PSA Attestation Token Hardware version claim"""
- def verify(self, value):
- self._check_type('HARDWARE_VERSION', value, str)
+ def verify(self, token_item):
+ self._check_type('HARDWARE_VERSION', token_item.value, str)
- value_len = len(value)
+ value_len = len(token_item.value)
expected_len = 19 # 'EAN13-Version' 13 + 1 + 5. e.g.:0604565272829-10010
- if len(value) != expected_len:
+ if len(token_item.value) != expected_len:
msg = 'Invalid HARDWARE_VERSION length; must be {} characters, found {} characters'
self.verifier.error(msg.format(expected_len, value_len))
- for idx, character in enumerate(value):
+ for idx, character in enumerate(token_item.value):
if character not in string.digits:
if idx != 13 or character not in '-':
msg = 'Invalid character {} at position {}'
self.verifier.error(msg.format(character, idx+1))
- self.verify_count += 1
def get_claim_key(self=None):
return ARM_RANGE - 5
@@ -106,7 +103,7 @@
def get_claim_name(self=None):
return 'SW_COMPONENTS'
-class SWComponentTypeClaim(NonVerifiedClaim):
+class SWComponentTypeClaim(AttestationClaim):
"""Class representing a PSA Attestation Token Software Component Measurement Type claim"""
def get_claim_key(self=None):
return SW_COMPONENT_RANGE + 1
@@ -119,7 +116,7 @@
return True
-class NoMeasurementsClaim(NonVerifiedClaim):
+class NoMeasurementsClaim(AttestationClaim):
"""Class representing a PSA Attestation Token No Software Measurements claim"""
def get_claim_key(self=None):
return ARM_RANGE - 7
@@ -136,9 +133,8 @@
def get_claim_name(self=None):
return 'CLIENT_ID'
- def verify(self, value):
- self._check_type('CLIENT_ID', value, int)
- self.verify_count += 1
+ def verify(self, token_item):
+ self._check_type('CLIENT_ID', token_item.value, int)
class SecurityLifecycleClaim(AttestationClaim):
"""Class representing a PSA Attestation Token Security Lifecycle claim"""
@@ -167,9 +163,8 @@
def get_claim_name(self=None):
return 'SECURITY_LIFECYCLE'
- def verify(self, value):
- self._check_type('SECURITY_LIFECYCLE', value, int)
- self.verify_count += 1
+ def verify(self, token_item):
+ self._check_type('SECURITY_LIFECYCLE', token_item.value, int)
@classmethod
def parse_raw(cls, raw_value):
@@ -189,9 +184,8 @@
def get_claim_name(self=None):
return 'PROFILE_ID'
- def verify(self, value):
- self._check_type('PROFILE_ID', value, str)
- self.verify_count += 1
+ def verify(self, token_item):
+ self._check_type('PROFILE_ID', token_item.value, str)
@classmethod
def is_utf_8(cls):
@@ -206,12 +200,11 @@
def get_claim_name(self=None):
return 'BOOT_SEED'
- def verify(self, value):
- self._validate_bytestring_length_is_at_least(value, 'BOOT_SEED', 32)
- self.verify_count += 1
+ def verify(self, token_item):
+ self._validate_bytestring_length_is_at_least(token_item.value, 'BOOT_SEED', 32)
-class VerificationServiceClaim(NonVerifiedClaim):
+class VerificationServiceClaim(AttestationClaim):
"""Class representing a PSA Attestation Token Verification Service Indicator claim"""
def get_claim_key(self=None):
return ARM_RANGE - 10 # originator
@@ -232,12 +225,11 @@
def get_claim_name(self=None):
return 'SIGNER_ID'
- def verify(self, value):
- self._validate_bytestring_length_is_at_least(value, 'SIGNER_ID', 32)
- self.verify_count += 1
+ def verify(self, token_item):
+ self._validate_bytestring_length_is_at_least(token_item.value, 'SIGNER_ID', 32)
-class SwComponentVersionClaim(NonVerifiedClaim):
+class SwComponentVersionClaim(AttestationClaim):
"""Class representing a PSA Attestation Token Software Component Version claim"""
def get_claim_key(self=None):
return SW_COMPONENT_RANGE + 4
@@ -258,12 +250,11 @@
def get_claim_name(self=None):
return 'MEASUREMENT_VALUE'
- def verify(self, value):
- self._validate_bytestring_length_is_at_least(value, 'MEASUREMENT', 32)
- self.verify_count += 1
+ def verify(self, token_item):
+ self._validate_bytestring_length_is_at_least(token_item.value, 'MEASUREMENT', 32)
-class MeasurementDescriptionClaim(NonVerifiedClaim):
+class MeasurementDescriptionClaim(AttestationClaim):
"""Class representing PSA Attestation Token Software Component Measurement description claim"""
def get_claim_key(self=None):
return SW_COMPONENT_RANGE + 6
diff --git a/iat-verifier/iatverifier/psa_iot_profile1_token_verifier.py b/iat-verifier/iatverifier/psa_iot_profile1_token_verifier.py
index ae1b4f4..96f721e 100644
--- a/iat-verifier/iatverifier/psa_iot_profile1_token_verifier.py
+++ b/iat-verifier/iatverifier/psa_iot_profile1_token_verifier.py
@@ -65,7 +65,6 @@
'verifier': self,
'claims': sw_component_claims,
'is_list': True,
- 'cross_claim_requirement_checker': None,
'necessity': Claim.OPTIONAL}),
(NoMeasurementsClaim, {'verifier': self, 'necessity': Claim.OPTIONAL}),
(ChallengeClaim, {'verifier': self, 'necessity': Claim.MANDATORY}),
@@ -82,18 +81,21 @@
cose_alg=cose_alg,
signing_key=signing_key)
- @staticmethod
- def check_cross_claim_requirements(verifier, claims):
- if SWComponentsClaim.get_claim_key() in claims:
- sw_component_present = claims[SWComponentsClaim.get_claim_key()].verify_count > 0
- else:
- sw_component_present = False
+ def verify(self, token_item):
- if NoMeasurementsClaim.get_claim_key() in claims:
- no_measurement_present = claims[NoMeasurementsClaim.get_claim_key()].verify_count > 0
- else:
- no_measurement_present = False
+ root_claims_token_item = token_item.value
+ root_claims_dict = root_claims_token_item.value
+
+ assert(isinstance(root_claims_dict, dict))
+
+ sw_component_present = False
+ no_measurement_present = False
+ for claim_token_item in root_claims_dict.values():
+ if (isinstance(claim_token_item.claim_type, SWComponentsClaim)):
+ sw_component_present = True
+ if (isinstance(claim_token_item.claim_type, NoMeasurementsClaim)):
+ no_measurement_present = True
if not sw_component_present and not no_measurement_present:
- verifier.error('Invalid IAT: no software measurements defined and '
- 'NO_MEASUREMENTS claim is not present.')
+ self.error('Invalid IAT: no software measurements defined and '
+ 'NO_MEASUREMENTS claim is not present.')
\ No newline at end of file
diff --git a/iat-verifier/scripts/check_iat b/iat-verifier/scripts/check_iat
index 80a1dfc..f8b1279 100755
--- a/iat-verifier/scripts/check_iat
+++ b/iat-verifier/scripts/check_iat
@@ -153,9 +153,9 @@
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)
+ token.verify()
if key_checked:
print('Signature OK')
print('Token format OK')
@@ -165,7 +165,8 @@
if args.print_iat:
print('Token:')
- json.dump(recursive_bytes_to_strings(token, in_place=True),
+ token_map = token.get_token_map()
+ json.dump(recursive_bytes_to_strings(token_map, in_place=True),
sys.stdout, indent=4)
print('')
diff --git a/iat-verifier/scripts/decompile_token b/iat-verifier/scripts/decompile_token
index c9b7f37..491263b 100755
--- a/iat-verifier/scripts/decompile_token
+++ b/iat-verifier/scripts/decompile_token
@@ -80,9 +80,8 @@
with open(args.source, 'rb') as fh:
token_map = verifier.parse_token(
token=fh.read(),
- verify=False,
check_p_header=False,
- lower_case_key=True)
+ lower_case_key=True).get_token_map()
if args.outfile:
with open(args.outfile, 'w', encoding="UTF-8") as wfh:
diff --git a/iat-verifier/tests/synthetic_token_claims.py b/iat-verifier/tests/synthetic_token_claims.py
index 992c9ba..73e3ffe 100644
--- a/iat-verifier/tests/synthetic_token_claims.py
+++ b/iat-verifier/tests/synthetic_token_claims.py
@@ -23,17 +23,15 @@
def get_claim_name(self=None):
return 'SYN_CLAIM_INT'
- def verify(self, value):
- self._check_type(self.get_claim_name(), value, int)
- self.verify_count += 1
+ def verify(self, token_item):
+ self._check_type(self.get_claim_name(), token_item.value, int)
class SynBoxesClaim(CompositeAttestClaim):
- """A composite claim with a cross claim checker."""
+ """A claim that contains other claims with a verifier"""
def __init__(self, verifier, *, claims, is_list, necessity=AttestationClaim.MANDATORY):
super().__init__(
verifier=verifier,
claims=claims,
- cross_claim_requirement_checker=type(self).check_cross_claim_requirements,
is_list=is_list,
necessity=necessity)
@@ -43,19 +41,19 @@
def get_claim_name(self=None):
return 'SYN_BOXES'
- @staticmethod
- def check_cross_claim_requirements(verifier, claims):
+ def verify(self, token_item):
"""Checking the claims for a box.
A box must have either all its dimensions defined, or none of them
"""
- box_size_prop_count = 0
- for c in [BoxWidthClaim, BoxHeightClaim, BoxDepthClaim]:
- if claims[c.get_claim_key()].claim_found():
- box_size_prop_count += 1
+ for box in token_item.value:
+ box_size_prop_count = 0
+ for c in [BoxWidthClaim, BoxHeightClaim, BoxDepthClaim]:
+ if c.get_claim_name() in box.keys():
+ box_size_prop_count += 1
- if box_size_prop_count not in (0, 3):
- verifier.error('Invalid IAT: Box size must have all 3 dimensions')
+ if box_size_prop_count not in (0, 3):
+ self.verifier.error('Invalid IAT: Box size must have all 3 dimensions')
class BoxWidthClaim(AttestationClaim):
"""A simple claim that has an int value."""
@@ -65,9 +63,8 @@
def get_claim_name(self=None):
return 'BOX_WIDTH'
- def verify(self, value):
- self._check_type(self.get_claim_name(), value, int)
- self.verify_count += 1
+ def verify(self, token_item):
+ self._check_type(self.get_claim_name(), token_item.value, int)
class BoxHeightClaim(AttestationClaim):
"""A simple claim that has an int value."""
@@ -77,9 +74,8 @@
def get_claim_name(self=None):
return 'BOX_HEIGHT'
- def verify(self, value):
- self._check_type(self.get_claim_name(), value, int)
- self.verify_count += 1
+ def verify(self, token_item):
+ self._check_type(self.get_claim_name(), token_item.value, int)
class BoxDepthClaim(AttestationClaim):
"""A simple claim that has an int value."""
@@ -89,9 +85,8 @@
def get_claim_name(self=None):
return 'BOX_DEPTH'
- def verify(self, value):
- self._check_type(self.get_claim_name(), value, int)
- self.verify_count += 1
+ def verify(self, token_item):
+ self._check_type(self.get_claim_name(), token_item.value, int)
class BoxColorClaim(AttestationClaim):
"""A simple claim that has a string value."""
@@ -101,9 +96,8 @@
def get_claim_name(self=None):
return 'BOX_COLOR'
- def verify(self, value):
- self._check_type(self.get_claim_name(), value, str)
- self.verify_count += 1
+ def verify(self, token_item):
+ self._check_type(self.get_claim_name(), token_item.value, str)
@classmethod
def is_utf_8(cls):
diff --git a/iat-verifier/tests/synthetic_token_verifier.py b/iat-verifier/tests/synthetic_token_verifier.py
index a8ac500..96d9a8c 100644
--- a/iat-verifier/tests/synthetic_token_verifier.py
+++ b/iat-verifier/tests/synthetic_token_verifier.py
@@ -85,10 +85,6 @@
configuration=configuration,
necessity=Claim.MANDATORY)
- @staticmethod
- def check_cross_claim_requirements(verifier, claims):
- pass
-
class SyntheticTokenVerifier2(Verifier):
"""Another test token that may contain other tokens"""
def get_claim_key(self=None):
@@ -165,10 +161,6 @@
configuration=configuration,
necessity=Claim.MANDATORY)
- @staticmethod
- def check_cross_claim_requirements(verifier, claims):
- pass
-
class SyntheticInternalTokenVerifier(Verifier):
"""A Test token that is intended to use inside another token"""
@@ -211,11 +203,6 @@
configuration=configuration,
necessity=necessity)
- @staticmethod
- def check_cross_claim_requirements(verifier, claims):
- pass
-
-
class SyntheticInternalTokenVerifier2(Verifier):
"""Another Test token that is intended to use inside another token"""
@@ -250,7 +237,3 @@
claims=claims,
configuration=configuration,
necessity=necessity)
-
- @staticmethod
- def check_cross_claim_requirements(verifier, claims):
- pass
diff --git a/iat-verifier/tests/test_synthetic.py b/iat-verifier/tests/test_synthetic.py
index 37775f3..413703d 100644
--- a/iat-verifier/tests/test_synthetic.py
+++ b/iat-verifier/tests/test_synthetic.py
@@ -213,9 +213,9 @@
'unknown_claims.cbor',
test_verifier)
self.assertEquals(4, len(test_ctx.output))
- self.assertIn('Unexpected TOKEN_CLAIM claim: 9901, skipping', test_ctx.output[0])
+ self.assertIn('Unexpected TOKEN_ROOT_CLAIMS claim: 9901, skipping', test_ctx.output[0])
self.assertIn('Unexpected SYN_BOXES claim: 9902, skipping', test_ctx.output[1])
- self.assertIn('Unexpected TOKEN_CLAIM claim: 9903, skipping', test_ctx.output[2])
+ self.assertIn('Unexpected TOKEN_ROOT_CLAIMS claim: 9903, skipping', test_ctx.output[2])
self.assertIn('Unexpected SYN_BOXES claim: 9904, skipping', test_ctx.output[3])
config = VerifierConfiguration(keep_going=True, strict=True)
@@ -232,9 +232,9 @@
'unknown_claims.cbor',
test_verifier)
self.assertEquals(4, len(test_ctx.output))
- self.assertIn('ERROR:iat-verifiers:Unexpected TOKEN_CLAIM claim: 9901', test_ctx.output[0])
+ self.assertIn('ERROR:iat-verifiers:Unexpected TOKEN_ROOT_CLAIMS claim: 9901', test_ctx.output[0])
self.assertIn('ERROR:iat-verifiers:Unexpected SYN_BOXES claim: 9902', test_ctx.output[1])
- self.assertIn('ERROR:iat-verifiers:Unexpected TOKEN_CLAIM claim: 9903', test_ctx.output[2])
+ self.assertIn('ERROR:iat-verifiers:Unexpected TOKEN_ROOT_CLAIMS claim: 9903', test_ctx.output[2])
self.assertIn('ERROR:iat-verifiers:Unexpected SYN_BOXES claim: 9904', test_ctx.output[3])
config = VerifierConfiguration(keep_going=False, strict=False)
@@ -250,9 +250,9 @@
DATA_DIR,
'unknown_claims.cbor',
test_verifier)
- self.assertIn('Unexpected TOKEN_CLAIM claim: 9901, skipping', test_ctx.output[0])
+ self.assertIn('Unexpected TOKEN_ROOT_CLAIMS claim: 9901, skipping', test_ctx.output[0])
self.assertIn('Unexpected SYN_BOXES claim: 9902, skipping', test_ctx.output[1])
- self.assertIn('Unexpected TOKEN_CLAIM claim: 9903, skipping', test_ctx.output[2])
+ self.assertIn('Unexpected TOKEN_ROOT_CLAIMS claim: 9903, skipping', test_ctx.output[2])
self.assertIn('Unexpected SYN_BOXES claim: 9904, skipping', test_ctx.output[3])
config = VerifierConfiguration(keep_going=False, strict=True)
@@ -269,4 +269,4 @@
'unknown_claims.cbor',
test_verifier)
self.assertIn(
- 'Unexpected TOKEN_CLAIM claim: 9901', test_ctx.exception.args[0])
+ 'Unexpected TOKEN_ROOT_CLAIMS claim: 9901', test_ctx.exception.args[0])
diff --git a/iat-verifier/tests/test_utils.py b/iat-verifier/tests/test_utils.py
index 7c54ef6..e05c953 100644
--- a/iat-verifier/tests/test_utils.py
+++ b/iat-verifier/tests/test_utils.py
@@ -71,11 +71,12 @@
"""Read a cbor file and returns the parsed dictionary"""
filepath = os.path.join(data_dir, filename)
with open(filepath, 'rb') as file:
- return verifier.parse_token(
+ token_item = verifier.parse_token(
token=file.read(),
- verify=True,
check_p_header=check_p_header,
lower_case_key=False)
+ token_item.verify()
+ return token_item
def create_and_read_iat(data_dir, source_name, verifier):
"""Read a yaml file, compile it into a cbor token, and read it back"""
diff --git a/iat-verifier/tests/test_verifier.py b/iat-verifier/tests/test_verifier.py
index ed3a725..bb03513 100644
--- a/iat-verifier/tests/test_verifier.py
+++ b/iat-verifier/tests/test_verifier.py
@@ -58,20 +58,19 @@
#dump_file_binary(good_sig)
with open(good_sig, 'rb') as wfh:
- verifier_good_sig.parse_token(
- token=wfh.read(),
- verify=True,
- check_p_header=False,
- lower_case_key=False)
-
+ token_item = verifier_good_sig.parse_token(
+ token=wfh.read(),
+ check_p_header=False,
+ lower_case_key=False)
+ token_item.verify()
with self.assertRaises(ValueError) as test_ctx:
with open(bad_sig, 'rb') as wfh:
- verifier_good_sig.parse_token(
+ token_item = verifier_good_sig.parse_token(
token=wfh.read(),
- verify=True,
check_p_header=False,
lower_case_key=False)
+ token_item.verify()
self.assertIn('Bad signature', test_ctx.exception.args[0])
@@ -223,7 +222,7 @@
PSAIoTProfile1TokenVerifier(method=method,
cose_alg=cose_alg,
signing_key=signing_key,
- configuration=self.config))
+ configuration=self.config)).get_token_map()
self.assertEqual(iat['SECURITY_LIFECYCLE'], 'SL_SECURED')
def test_security_lifecycle_decoding(self):
@@ -237,5 +236,5 @@
PSAIoTProfile1TokenVerifier(method=method,
cose_alg=cose_alg,
signing_key=signing_key,
- configuration=self.config))
+ configuration=self.config)).get_token_map()
self.assertEqual(iat['SECURITY_LIFECYCLE'], 'SL_SECURED')