blob: 19a7d948852b4ab54619e15b528951456b1a92c1 [file] [log] [blame]
#################
TF-M builtin keys
#################
:Author: Raef Coles
:Organization: Arm Limited
:Contact: raef.coles@arm.com
.. _tfm-builtin-keys-label:
************
Introduction
************
When TF-M is integrated on a platform, the platform itself can provide several
keys for usage which are bound to the device instead of being owned by a
specific secure partition. When those keys are readable by the secure
processing environment, the platform must provide a function to load these keys
in the HAL layer, either loading them from OTP or another platform specific
location or subsystem. These keys are henceforth referred to as "builtin keys",
and might include (but are not limited to) the following:
1. The Hardware Unique Key (HUK)
2. The Initial Attestation Key (IAK)
The ``tfm_builtin_key_loader`` component implements a mechanism to discover
those keys and make them available to the PSA Crypto APIs for usage. Note that
if a key is stored in a subsystem which can't be read by the secure
processing environment, a full opaque driver must be used to be able to use
those keys through the PSA Crypto APIs. This document focuses only on the case
where the keys can be read by the SPE and processed by the driver described in
this document.
In TF-M's legacy solution, the IAK is loaded by the attestation service as a
volatile key, which requires some key-loading logic to be implemented by that
partition. The HUK is not loaded in the crypto service, and is instead used by
an implementation of a TF-M specific KDF algorithm which then loads the key and
invokes Mbed TLS directly. Both solutions are far from ideal as they require an
effort to load (and duplicate) the keys per each user, or require a dedicated
code in TF-M that directly interacts with the HAL layer and invokes functions
from the crypto library bypassing the PSA Crypto interface. The aim of the
``tfm_builtin_key_loader`` driver is to provide a uniform interface to use the
builtin keys available in a platform.
Implementing a driver to deal with builtin keys allows to expand the legacy
solution for dealing with this type of keys in several ways. For example, it
avoid the need for partitions to implement dedicated mechanisms for probing the
HAL layer for builtin keys and load them as volatile. It removes the need to
have implementation specific call flows for deriving keys from the HUK (as that
requirement now is fulfilled by the driver itself transparently). It allows
uniform access to the keys also from the NS world (in any case, subject to the
policy dictated by the platform). Correctly abstracts away details of the key
handling mechanism from TF-M partitions into the PSA Crypto core key management
subsystem.
****************
PSA builtin keys
****************
The PSA Cryptographic API provides a mechanism for accessing keys that are
stored in platform-specific locations (often hardware accelerators or OTP). A
builtin key is assigned a specific key_id, i.e. a handle, which is hardcoded at
build time and must be selected in the range [MBEDTLS_PSA_KEY_ID_BUILTIN_MIN,
MBEDLTS_PSA_KEY_ID_BUILTIN_MAX]. A user of the PSA Crypto API can reference
those keys directly by using these handles. It is up to the platform to specify
policy restrictions for specific users of the keys, e.g. an NS entity, or a
secure partition. The PSA Crypto core will then check those policies to grant
or deny access to the builtin key for that user.
******************************
PSA cryptoprocessor driver API
******************************
The PSA specification allows the PSA Crypto APIs to defer their operation to an
accelerator driver, through a mechanism described in the PSA cryptoprocessor
driver interface specification [1]_. This specification defines the concept of
PSA Crypto core, i.e. "An implementation of the PSA Cryptography API is
composed of a core and zero or more drivers". The PSA specification also has
the concept of storage locations for keys, through the type
``psa_key_location_t``, which is used to access keys that don't have local
storage [2]_. This is leveraged mainly by opaque drivers mainly that use keys
for which the key material is not readable by the PSA crypto core layer.
TF-M defines a software driver called ``tfm_builtin_key_loader`` that provides
no acceleration but just defines a dedicated key location defined through the
``TFM_BUILTIN_KEY_LOADER_KEY_LOCATION`` define. By resorting to the entry points
provided by this driver, the PSA Crypto core slot management subsystem can
access keys stored in the underlying platform, validate key usage policies and
allow PSA Crypto APIs to uniformly access builtin keys using the same call flows
used with traditional local-storage keys.
This is implemented by hooking the entry points defined by the driver into the
``library/psa_crypto_driver_wrappers.c`` file provided by the PSA Crypto core.
This is currently done manually but eventually could be just autogenerated by
parsing a description of the driver entry points in the JSON format. These entry
points are:
1. ``tfm_builtin_key_loader_init``
2. ``tfm_builtin_key_loader_get_key_buffer_size``
3. ``tfm_builtin_key_loader_get_builtin_key``
The call flow for the entry points from the driver wrapper layer is as follows:
1. During the driver initialisation phase started by the PSA Crypto Core init,
the ``tfm_builtin_key_loader_init`` function is called to probe the
platform HAL and retrieve the builtin keys. Those keys are described by
the types defined in the HAL layer interface header
``tfm_plat_crypto_keys.h``. In particular global tables describing key
properties and user policies must be implemented by each platform and are
retrieved by the two accessor functions
``tfm_plat_builtin_key_get_desc_table_ptr()`` and
``tfm_plat_builtin_key_get_policy_table_ptr()``. The keys are loaded from
the platform in secure RAM in the TF-M Crypto partition with associated
metadata. It's worth to note that the keys are loaded through callback
functions which the platform lists in the key descriptor table.
2. Once the TF-M Crypto service is initialised, at runtime it might receive a
request through an API call to use one of the builtin key IDs. Those IDs
are described in the ``tfm_builtin_key_ids.h`` header. Platforms can
override the default values providing their own header.
3. When the PSA Crypto core in the ``psa_get_and_lock_key_slot()`` function
checks that the ``key_id`` being requested is in the builtin range region,
it probes the platform through the function
``mbedtls_psa_platform_get_builtin_key`` which must be implemented by the
TF-M Crypto service library abstraction layer. This function just checks
the key descriptor table from the HAL to understand if such ``key_id`` is
available in the platform and what are the corresponding ``slot`` and
``lifetime`` values associated to it. The lifetime contains the location of
the key, which determines which driver is responsible for dealing with it
(for the ``tfm_builtin_key_loader`` driver, that is
``TFM_BUILTIN_KEY_LOADER_KEY_LOCATION``), while the ``slot`` value maps the
key to the slot in the driver internal storage.
4. At this point, the PSA Crypto core knows that the key exists and is bound
to the location associated to the driver. By calling into the driver
wrappers layer is then able to retrieve the key attributes stored in the
platform for that key ID, and the size required to allocate in its
internal slot management system in order to load the key material in the
core. This is done by calling ``tfm_builtin_key_loader_get_builtin_key``
just with a valid key attributes pointer (and nothing else), to retrieve
the attributes. Once the attributes are available, the required size is
retrieved through the driver wrapper by calling
``tfm_builtin_key_loader_get_key_buffer_size``.
5. At this stage, the slot management subsystem calls again into the driver
wrapper layer through ``tfm_builtin_key_loader_get_builtin_key`` with a
valid buffer to hold the key material returned by the
``tfm_builtin_key_loader`` driver. When loading the key, the user
requiring that key_id is validated by the driver code against the policies
defined by the platform. If the policies match, the builtin key material
and metadata is loaded and is used like a transparent key available to the
PSA Crypto core slot management subsystem.
*****************
Technical details
*****************
------------------------------
Builtin key IDs and overriding
------------------------------
TF-M builtin key IDs are defined in
``interface/include/crypto_keys/tfm_builtin_key_ids.h`` through the enum
``tfm_key_id_builtin_t``. They are allocated inside the range that PSA
specifies for the builtin keys, i.e. between ``MBEDTLS_PSA_KEY_ID_BUILTIN_MIN``
and ``MBEDLTS_PSA_KEY_ID_BUILTIN_MAX``. A platform can specify extra builtin key
IDs by setting the ``PLATFORM_DEFAULT_CRYPTO_KEYS`` variable to ``OFF``,
creating the header ``tfm_builtin_key_ids.h``, and specifying new keys and IDs.
--------------------------
Builtin key access control
--------------------------
A builtin key is accessible by all callers since the ``key_id`` associated to it
is public information. Access to the keys must be mediated, which is done by
matching the user requesting the ``key_id`` against the policies available for
that user on that particular key in the policy table. If no policies are
specified for a specific combination of user and ``key_id``, the usage flags in
the key attributes will be all set to zeros, meaning the key will be unusable
for any operation for that particular user.
------------------------------
Multi-partition key derivation
------------------------------
The HUK is used for key derivation by any secure partition or NS caller that
requires keys that are bound to a particular context. For example, Protected
Storage derives keys uniquely for each user of the service which are used to
encrypt user files. In order to provide HUK derivation to every secure
partition / NS caller, it must be ensured that no service that utilises HUK
derivation can derive the same key as another service (simply by using the same
inputs for the KDF APIs, i.e. accessing the same base key for derivation).
This is accomplished by deriving a further "platform key" for each builtin key
that has ``PSA_KEY_USAGE_DERIVE`` set in its attributes. These platform keys
are derived from the builtin key, using the partition ID as a KDF input, and
can then be used for safely for further derivations by the user, without risks
to derive the same keys as other users. This is enforced directly by the
``tfm_builtin_key_loader`` driver.
.. Note::
If the NS client ID feature is disabled, all NS callers share a partition ID
of ``-1``, and therefore will share a platform key and be therefore be able
to derive the same keys as other NS callers.
For keys that are not exposed outside the device, this is transparent to the
service that is using the key derivation, as they have no access to the builtin
key material and cannot distinguish between keys derived directly from it and
keys derived from the platform key. For some builtin keys, deriving platform
keys is not acceptable, as the key is used outside the device (i.e. the IAK
public key is used to verify attestation tokens) so the actual builtin key is
used.
The decision has been taken to derive platform keys for any key that can be used
for key derivation (``PSA_KEY_USAGE_DERIVE``), and not derive platform keys
otherwise. For builtin keys that do not derive platform keys but are directly
used, care must be taken with access control where multiple partitions have
access to the same raw key material.
---------------------------------
Mbed TLS transparent builtin keys
---------------------------------
Mbed TLS does not natively support transparent builtin keys (transparent keys
are keys where the key material is directly accessible by the PSA Crypto core),
so some modifications had to be made. Opaque keyslots have the same basic
structure as standard transparent key slots, and can be passed to the functions
usually reserved for transparent keys, though this is a private implementation
detail of the mbed TLS library and is not specified in the driver interface.
Therefore, the only modification required currently is to allow keys that have
the location ``TFM_BUILTIN_KEY_LOADER_KEY_LOCATION`` to be passed to the
functions that usually accept transparent keys only, i.e. with the location
``PSA_KEY_LOCATION_LOCAL_STORAGE``. This is due to the fact that the standard
assumption of the PSA Crypto core is that, if a driver that provides an
additional location, will also provide dedicated cryptographic mechanisms to act
on those keys, but this is not the case of the ``tfm_builtin_key_loader``, as it
just provides a mechanism to load keys (which act as a transparent key with
local storage, once loaded), but mbed TLS does not support such "transparent
builtin key" concept.
References
----------
.. [1] PSA cryptoprocessor driver interface\ https://github.com/Mbed-TLS/mbedtls/blob/development/docs/proposed/psa-driver-interface.md
.. [2] Definition of psa_key_location_t type in the PSA spec: \ https://armmbed.github.io/mbed-crypto/html/api/keys/lifetimes.html#c.psa_key_location_t
--------------
*Copyright (c) 2022-2023, Arm Limited. All rights reserved.*