blob: e3e8d0eeb9478dd9d31aa4b436b98d3fbb315ef3 [file] [log] [blame]
############################
Initial Attestation Verifier
############################
This is a set of utility scripts for working with PSA Initial Attestation
Token, the structure of which is described here:
https://tools.ietf.org/html/draft-tschofenig-rats-psa-token-05
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:
.. code:: bash
# 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 ``sample`` 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):
.. code:: bash
$ check_iat -t PSA-IoT-Profile1-token sample/cbor/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 sample/iat.cbor is inside sample/key.pem.
::
$ check_iat -t PSA-IoT-Profile1-token -k sample/key.pem sample/cbor/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:
.. code:: json
{
"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:
.. code:: bash
$ compile_token -t PSA-IoT-Profile1-token -k sample/key.pem sample/yaml/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.
.. code:: bash
$ decompile_token -t PSA-IoT-Profile1-token sample/cbor/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 sample/hmac.key sample/iat-hmac.cbor
Signature OK
Token format OK
*******
Testing
*******
Tests can be run using ``nose2``:
.. code:: bash
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.
.. code:: bash
./dev_scripts/generate-key.py OUTFILE
Generate an ECDSA (NIST256p curve) signing key and write it in PEM
format to the specified file.
.. code:: bash
./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
*********************
#. 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.')``
#. 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*.
#. 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.*