Initial Attestation Verifier

This is a set of utility scripts for working with PSA Initial Attestation Token, the structure of which is described here:

The following utilities are provided:

check_iat

Verifies the structure, and optionally the signature, of a token.

compile_token

Creates a (optionally, signed) token from a YAML descriptions of the claims.

decompile_token

Generates a YAML descriptions of the claims contained within a token. (Note: this description can then be compiled back into a token using compile_token.)

Installation

You can install the script using pip:

# Inside the directory containg this README
pip3 install .

This should automatically install all the required dependencies. Please see setup.py for the list of said dependencies.

Usage

Note

You can use -h flag with any of the scripts to see their usage help.

check_iat

After installing, you should have check_iat script in your PATH. The script expects two parameters:

  • a path to the signed IAT in COSE format

  • the token type

You can find an example in the tests/data directory.

The script will extract the COSE payload and make sure that it is a valid IAT (i.e. all mandatory fields are present, and all known fields have correct size/type):

$ check_iat -t PSA-IoT-Profile1-token tests/data/iat.cbor
Token format OK

If you want the script to verify the signature, you need to specify the file containing the signing key in PEM format using -k option. The key used to sign tests/data/iat.cbor is inside tests/data/key.pem.

$ check_iat -t PSA-IoT-Profile1-token -k tests/data/key.pem tests/data/iat.cbor
Signature OK
Token format OK

You can add a -p flag to the invocation in order to have the script print out the decoded IAT in JSON format. It should look something like this:

{
    "INSTANCE_ID": "b'0107060504030201000F0E0D0C0B0A090817161514131211101F1E1D1C1B1A1918'",
    "IMPLEMENTATION_ID": "b'07060504030201000F0E0D0C0B0A090817161514131211101F1E1D1C1B1A1918'",
    "CHALLENGE": "b'07060504030201000F0E0D0C0B0A090817161514131211101F1E1D1C1B1A1918'",
    "CLIENT_ID": 2,
    "SECURITY_LIFECYCLE": "SL_SECURED",
    "PROFILE_ID": "http://example.com",
    "BOOT_SEED": "b'07060504030201000F0E0D0C0B0A090817161514131211101F1E1D1C1B1A1918'",
    "SW_COMPONENTS": [
        {
            "SW_COMPONENT_TYPE": "BL",
            "SIGNER_ID": "b'07060504030201000F0E0D0C0B0A090817161514131211101F1E1D1C1B1A1918'",
            "SW_COMPONENT_VERSION": "3.4.2",
            "MEASUREMENT_VALUE": "b'07060504030201000F0E0D0C0B0A090817161514131211101F1E1D1C1B1A1918'",
            "MEASUREMENT_DESCRIPTION": "TF-M_SHA256MemPreXIP"
        },
        {
            "SW_COMPONENT_TYPE": "M1",
            "SIGNER_ID": "b'07060504030201000F0E0D0C0B0A090817161514131211101F1E1D1C1B1A1918'",
            "SW_COMPONENT_VERSION": "1.2",
            "MEASUREMENT_VALUE": "b'07060504030201000F0E0D0C0B0A090817161514131211101F1E1D1C1B1A1918'"
        },
        {
            "SW_COMPONENT_TYPE": "M2",
            "SIGNER_ID": "b'07060504030201000F0E0D0C0B0A090817161514131211101F1E1D1C1B1A1918'",
            "SW_COMPONENT_VERSION": "1.2.3",
            "MEASUREMENT_VALUE": "b'07060504030201000F0E0D0C0B0A090817161514131211101F1E1D1C1B1A1918'"
        },
        {
            "SW_COMPONENT_TYPE": "M3",
            "SIGNER_ID": "b'07060504030201000F0E0D0C0B0A090817161514131211101F1E1D1C1B1A1918'",
            "SW_COMPONENT_VERSION": "1",
            "MEASUREMENT_VALUE": "b'07060504030201000F0E0D0C0B0A090817161514131211101F1E1D1C1B1A1918'"
        }
    ]
}

compile_token

You can use this script to compile a YAML claims description into a COSE-wrapped CBOR token:

$ compile_token -t PSA-IoT-Profile1-token -k tests/data/key.pem tests/data/iat.yaml > sample_token.cbor

No validation is performed as part of this, so there is no guarantee that a valid IAT will be produced.

You can omit the -k option, in which case, the resulting token will not be signed, however it will still be wrapped in COSE “envelope”. If you would like to produce a pure CBOR encoding of the claims without a COSE wrapper, you can use -r flag.

decompile_token

Decompile an IAT (or any COSE-wrapped CBOR object – no validation is performed as part of this) into a YAML description of its claims.

$ decompile_token -t PSA-IoT-Profile1-token tests/data/iat.cbor
boot_seed: !!binary |
  BwYFBAMCAQAPDg0MCwoJCBcWFRQTEhEQHx4dHBsaGRg=
challenge: !!binary |
  BwYFBAMCAQAPDg0MCwoJCBcWFRQTEhEQHx4dHBsaGRg=
client_id: 2
implementation_id: !!binary |
  BwYFBAMCAQAPDg0MCwoJCBcWFRQTEhEQHx4dHBsaGRg=
instance_id: !!binary |
  AQcGBQQDAgEADw4NDAsKCQgXFhUUExIREB8eHRwbGhkY
profile_id: http://example.com
security_lifecycle: SL_SECURED
sw_components:
- measurement_description: TF-M_SHA256MemPreXIP
  measurement_value: !!binary |
    BwYFBAMCAQAPDg0MCwoJCBcWFRQTEhEQHx4dHBsaGRg=
  signer_id: !!binary |
    BwYFBAMCAQAPDg0MCwoJCBcWFRQTEhEQHx4dHBsaGRg=
  sw_component_type: BL
  sw_component_version: 3.4.2
- measurement_value: !!binary |
    BwYFBAMCAQAPDg0MCwoJCBcWFRQTEhEQHx4dHBsaGRg=
  signer_id: !!binary |
    BwYFBAMCAQAPDg0MCwoJCBcWFRQTEhEQHx4dHBsaGRg=
  sw_component_type: M1
  sw_component_version: '1.2'
- measurement_value: !!binary |
    BwYFBAMCAQAPDg0MCwoJCBcWFRQTEhEQHx4dHBsaGRg=
  signer_id: !!binary |
    BwYFBAMCAQAPDg0MCwoJCBcWFRQTEhEQHx4dHBsaGRg=
  sw_component_type: M2
  sw_component_version: 1.2.3
- measurement_value: !!binary |
    BwYFBAMCAQAPDg0MCwoJCBcWFRQTEhEQHx4dHBsaGRg=
  signer_id: !!binary |
    BwYFBAMCAQAPDg0MCwoJCBcWFRQTEhEQHx4dHBsaGRg=
  sw_component_type: M3
  sw_component_version: '1'

This description can then be compiled back into CBOR using compile_token.

Mac0Message

By default, the expectation is that the message will be wrapped using Sign1Message COSE structure, however, the alternative Mac0Message structure that uses HMAC with SHA256 algorithm rather than a signature is supported via the -m mac flag:

$ check_iat -t PSA-IoT-Profile1-token -m mac -k tests/data/hmac.key tests/data/iat-hmac.cbor
Signature OK
Token format OK

Testing

Tests can be run using nose2:

pip install nose2

Then run by executing nose2 in the root directory.

Development Scripts

The following utility scripts are contained within dev_scripts subdirectory and were utilized in development of this tool. They are not need to use the iat-verifier script, and can generally be ignored.

./dev_scripts/generate-key.py OUTFILE

Generate an ECDSA (NIST256p curve) signing key and write it in PEM format to the specified file.

./dev_scripts/generate-sample-iat.py KEYFILE OUTFILE

Generate a sample token, signing it with the specified key, and writing the output to the specified file.

Note

This script is deprecated – use compile_token (see above) instead.

Adding new token type

  1. Create a file with the claims for the new token type in tf-m-tools/iat-verifier/iatverifier.

    • For each claim a new class must be created that inherits from AttestationClaim or from one of its descendants

    • CompositeAttestClaim is descendants of AttestationClaim, for details on how to use it see the documentation in the class definition.

    • 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.')

  2. 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)

    • Implement the __init__(self, ...) function. This function must create a list with the claims that are accepted by this token. (Note that the AttestationTokenVerifier class inherits from AttestationClaim. this makes it possible to create nested token). Each item is the list is a tuple:

      • first element is the class of the claim

      • Second is a dictionary containing the __init__ function parameters for the claim

        • the key is the name of the parameter

        • the value is the value of the parameter

      The list of claims must be passed to the init function of the base class.

      For example see iat-verifier/iatverifier/cca_token_verifier.py.

  3. Add handling of the new token type to the check_iat, decompile_token, and compile_token scripts.


Copyright (c) 2019-2022, Arm Limited. All rights reserved.