Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

API function to register an existing key #242

Open
gilles-peskine-arm opened this issue Feb 28, 2025 · 6 comments
Open

API function to register an existing key #242

gilles-peskine-arm opened this issue Feb 28, 2025 · 6 comments
Labels
API design Related the design of the API Crypto API Issue or PR related to the Cryptography API enhancement New feature or request

Comments

@gilles-peskine-arm
Copy link
Contributor

As the developer of an application (or application stack) using the PSA crypto API on a device with a secure element,
I want to access keys that are provisioned onto the device by a process outside of my control,
so that my application can use pre-provisioned keys while sticking to the PSA API.

The idea is that many devices that have a secure element have a factory process for injecting credentials. That process exists and can't be retooled easily, so we can't insist that this process becomes aware of the PSA API. The device does have a software stack that implements the PSA crypto API. Through that API, we can create new keys and use them. But there's no standard way to use the credentials that are provisioned during the device manufacturing.

More generally, there's interest in using a key through PSA even when it's created through proprietary means.

Mbed TLS implemented a function to do that alongside its original secure element interface: mbedtls_psa_register_se_key(). That API was tied to the interface, and had some limitations (notably, it can't be used with volatile keys), so in our next major release (TF-PSA-Crypyo 1.0), we are evolving the API. We have heard interest in making this feature official in the PSA API.

The starting proposal is as follows:

psa_register_opaque_key(
    const psa_key_attributes_t *attributes,
    const uint8_t *label, size_t label_length,
    psa_key_id_t *key_id);

This is a key creation function, taking attributes as input and key_id as output like the others. The way to obtain the key material is by associating the PSA key identifier with some existing “slot”. In PSA terms, the meaning of the label parameter is implementation-specified, and likely to be different for each supported key location.

With Mbed TLS opaque drivers, there are two plausible meanings for label:

  • It could be whatever the driver expects as key data — what you'd get as output from asking an opaque driver to import/generate/derive a key.
  • Or it could be an input passed to a driver callback.

In practice, the primary intent is to pass a slot number, or slot label, or key name, in the secure element.

Unresolved question: does psa_destroy_key only destroy the PSA key stub, or does it also destroy the underlying key?

Unresolved question: what can this function mean for transparent keys (keys in the location PSA_KEY_LOCATION_LOCAL_STORAGE)? Possibly this might duplicate the functionality that Mbed TLS calls “built-in keys”, which are a way to access keys that were not created through PSA, such as a HUK or HUK-derived key. These are independent features in Mbed TLS partly for historical reasons, but also because they are intended for different scenarios, and it's unclear whether a unified API would handle all the desired scenarios. Typically, the set of available built-in keys is decided by the device integrator and will not change over time, whereas registrable keys can be created dynamically if the proprietary provisioning scheme extends after the factory phase of the device lifecycle. Also they interact very differently with driver dispatch.

Timeline: we would ideally like to release the official psa_register_opaque_key in TF-PSA-Crypto 1.0, which means the official API should be stable (not necessarily released) by late June 2025.

@gilles-peskine-arm gilles-peskine-arm added enhancement New feature or request API design Related the design of the API Crypto API Issue or PR related to the Cryptography API labels Feb 28, 2025
@MarcusJGStreets
Copy link
Contributor

It strikes me that this is closer to Import than it is to Generate.

It is essentially Import, but instead of a buffer with the bits, that you need to move to a protected location, you already have the bits in a protected location and simply need to add the metadata.

If the key is persistent, the metadata and the key material would end up being stored in different places.

So, that leads to the question, does register need to support persistent keys.
Would it be acceptable for the application to have to call register each time and to get a volatile handle.

This does of course mean that the discussion on what happens if you try to destroy the key is moot.

@gilles-peskine-arm
Copy link
Contributor Author

So, that leads to the question, does register need to support persistent keys.
Would it be acceptable for the application to have to call register each time and to get a volatile handle.

Hmmm, I hadn't really considered that. We did have a request for the volatile case to work. But I think the primary use case is a persistent metadata object:

  1. The key material is injected during device manufacturing.
  2. The key metadata is created during application provisioning.
  3. After that, the application uses the key without knowing where it comes from. Quite possibly the application works with a key that can be pre-provisioned or not depending on which device model it happens to be running on.

I'd expect that in such scenarios, steps 2 and 3 tend to be done by different pieces of code, and the code that uses the key wouldn't know whether to call register and with what label.

@MarcusJGStreets
Copy link
Contributor

Good point

valeriosetti added a commit to valeriosetti/TF-PSA-Crypto that referenced this issue Mar 7, 2025
Add the possibility to register a PSA key which is baked by some driver.
The key material in this case will simply be a "label" that has to be
known/understood from that driver.

It implements requirements from:
- ARM-software/psa-api#242
- Mbed-TLS/mbedtls#9255

Signed-off-by: Valerio Setti <vsetti@baylibre.com>
valeriosetti added a commit to valeriosetti/TF-PSA-Crypto that referenced this issue Mar 10, 2025
Add the possibility to register a PSA key which is baked by some driver.
The key material in this case will simply be a "label" that has to be
known/understood from that driver.

It implements requirements from:
- ARM-software/psa-api#242
- Mbed-TLS/mbedtls#9255

Signed-off-by: Valerio Setti <vsetti@baylibre.com>
valeriosetti added a commit to valeriosetti/TF-PSA-Crypto that referenced this issue Mar 10, 2025
Add the possibility to register a PSA key which is baked by some driver.
The key material in this case will simply be a "label" that has to be
known/understood from that driver.

It implements requirements from:
- ARM-software/psa-api#242
- Mbed-TLS/mbedtls#9255

Signed-off-by: Valerio Setti <vsetti@baylibre.com>
@gilles-peskine-arm
Copy link
Contributor Author

Is it ok to register a persistent key with a key ID outside the application range? This may be tempting in when the registered key is vendor-specific and not handled by normal application code. However, it would be difficult to specify and implement, since such keys should not interfere with volatile keys and any other mechanism by which an implementation may define keys in the implementation range. So I think the answer is no, the same key IDs are acceptable for register as for import/generate/derive.

@athoelke
Copy link
Contributor

So this is intended for binding a PSA Crypto key identifier, and key attributes, to some existing key material that is already stored within the implementation under some other naming scheme (the label).

API name

Presumably, as the material is already internal, it will be stored in a specific key location, and the key lifetime in the provided key attributes is essential for the discovery and registration of the key id with the key material. Given that support for unbound key material, and the naming scheme, will be implementation and storage-location specific - I would like to propose that the API name dropped the opaque infix, as this term is not part of the existing specification language. I.e. the API is just psa_register_key().

As an alternative name, I would also suggest psa_bind_key() - as the API binds a key id and attributes to existing key material. However, is there any risk that using the term 'binding' might confuse this with 'cryptographic binding'?

Key attribute handling

Is there any expectation of which key attributes would already be associated with the key material? For example:

  • key lifetime/location is known
  • key size is known
  • key type would PROBABLY be known
  • key policy MIGHT be known
    Would we need flexibility in integrating values in the supplied key attributes with those of the existing key; or can we be strict and require that any provided key attributes must match the key material?

On key destruction

On the question of what happens with psa_destroy(). I think it is difficult to handle all envisaged use cases cleanly if this API sometimes destroys the key material, as well as the meta-data binding; and sometimes just undoes the registration process. Would this depend on the key persistence attribute (part of lifetime) - and is this application determined, or implementation determined?

A suggestion that enables both the intention of the application, and the behavior permitted by the implementation, to be clear, would be to add a separate unregister function that explicitly undoes the actions of psa_register_key(). An application that expects to destroy the key material would use psa_destroy_key() as before - but an implementation is permitted to deny such a request for any or all of this type of key.

We might want to consider if would be permitted, and what it would mean, to create a key, and then unregister it?

@frkv
Copy link

frkv commented Mar 17, 2025

With the statements on @athoelke, I'm happy to see "opaque" being dropped from the name in the initial proposal.

Main criticisms:

My main criticism of the initial proposal in this issue (as I've raised in the Mbed TLS biweekly):

  • This API is describing something specific to driver level and that the initial proposal is not very generic.
  • If we are to add APIs for key-relevant registration it should be generic and natural additions to the existing key management APIs
  • Alternatively an addition could be considered for the vendor-specific scope (e.g. tied to psa_key_attributes_t) to hide this from front-end APIs for key management.

When discussing the means of additional context-information that is retrieved from or passed to to any specialized (opaque) driver, I feel it is natural to also discuss a "standard case", where the driver-specific information is already provided, as-is, meaning that the "registration" is inherit in the system and is not something you are required to do with extra API calls. What I mean by this is the case when no additional context-information is required to be passed to any opaque driver to be able to use the driver and its keys

Inherent-registration versus runtime-registration

Inherent registration can be described as follows: Compile-time established link between between a key_id and lifetime to a "driver slot" or any type of address-based locality expressed by the secure element, secure enclave or any type of opaque driver which constitutes an "external storage of keys", Including additional metadata that is relevant for key or driver usage

Runtime-registration can be described as follows: Establishing the aforementioned link between key_id and lifetime to a "driver slot" including setting up additional metadata that is relevant for key usage in runtime

Points for consideration:

  • Is the type of per-key registration so specialized that it merits usage of psa_import_key or similar APIs instead, since the key is not valid for use by the driver without additional content information?
  • Why is there a need for an additional front-end API for something that potentially could be solved in compile time or on driver-specific level?
  • Is the addition in the front-end APIs a bit too decentralized from the key usage scope?

Registering per-key or per-driver

The issue lists a proposal is focused on per-key registration which is the widest granularity possible for adding metadata to a key in a special driver. This is the main reason psa_import_key is mentioned as an alternative. One can wonder if there is potentially a need for a wider registration, something that could be handled per-driver instead of per-key.

Regardless, if the registration is with the following API:

psa_status_t psa_register_key(psa_key_attributes * attributes,
                              const uint8_t * metadata, size_t metadata_length,
                              psa_key_id_t * key_id)

.. then there is a potential of a double registration of key_id coupled with attributes if the usage-patterns ever follow a strategy similar to the builtin keysupport (see later).

And there is an option that the information is vaguely passed from per-key registrations when it is indeed intended per-driver

One option that is likely a bit clearer, which would allow for both types of registration (per-key and per-driver) is to extend the functionality of the psa_key_attributes_t structure type to hold vendor/implementation specific contributions for any type of context-based metadata. Meaning extending the APIs to manage key attributes to add and retrieve custom data:

I propose the following extension:

psa_set_key_metadata(psa_key_attributes_t * attributes,
                     const uint8_t * metadata, size_t metadata_length);

psa_get_key_metadata(psa_key_attributes_t * attributes,
                     uint8_t * metadata, size_t metadata_size, size_t * metadata_length);

When using these APIs we would achieve the following:

  • Registration is avoided if it is unneeded (if keys can be resolved on driver-level)
  • Relevant information for key usage is centralized in a relevant structure for keys
  • The granularity of registration can be as wide as per-key (if needed) but is general enough that it is possible to pass per-driver
  • Implementations can choose to provide macros for specialized initialization of the psa_key_attributes_t structure
    • Without filling in any additional information using psa_set_ prefixed APIs.
    • Avoiding specialized call to psa_import_key as mentioned before.
    • Possibly providing per-driver information if the information is reused on multiple keys/algorithms
  • If the driver key type is inherently registered, there is no need to run additional APIs (same as today).

Implementation details: It would be best for API to keep this additional feature in psa_key_attributes_t structure as an optional construct so that current implementation isn't imposed an ABI change

Implementation details: It is likely best for the API to store the additional metadata as a pointer and size so that ABIs breaks can be avoided

Background: Builtin key support and SE drivers (driver)

The additions in the raised issue is done to help transition away from the now deprecated methodology of SE drivers in Mbed TLS distribution (now placed in TF-PSA-Crypto)

The existing functionality of SE drivers lives alongside the so-called builtin key support which has multiple points of documentation in the Proposed scope of documentation in TF-PSA-Crypto. The builtin keysupport in itself is a type of opaque-like driver support, but it has the ability to reuse existing transparent drivers if the keys are passed out in-the-clear.

Using the existing builtin key support as a starting point one can see that one of the mandatory APIs to implement is the get_builtin_key driver API, which would populate the key attributes structure and optionally fill in a key_buffer which is used in actual calls.

Note: It is possible to provide any driver-specific metadata via the key_buffer parameter or via the psa_key_attributes_t structure and in case of a "inherit registration" (where everything is known at compile-time or can be resolved through runtime). The builtin key support that is provided today is sufficient to give support for opaque keys as long as the registration is inherent

For any register-like calls where additional information is passed to support opaque drivers (via opaque keys) I hope the information is transferred all the way towards the driver (similar to the existing builtin key support), meaning that PSA crypto implementation above the driver level is only used to route and forward information directly to driver scope and that it is the drivers responsibility to hold and indeed consume this context information.

The implementation example provided here will assume a driver named acme and expect that the acme term is used as a prefix for all APIs

APIs for registering per driver

Given the initial proposal for psa_register_key the implementation would likely be:

psa_status_t acme_register_key(psa_key_attributes_t * attributes,
                               const uint8_t * metadata, size_t metadata_length,
                               psa_key_id_t * key_id);

I deem this a double registration because the population of the attributes-type tied to key key_id comes in addition to anything that is inherently registered (by lookup) and provided back as a populated psa_key_attributes_t structure e.g. by using the get_builtin_key API

APIs for registering via psa_key_attributes_t

Alternatively by extending the psa_key_attributes_tstructure the registration is avoided and instead the driver can consume additional metadata by implementing the following APIs:

psa_status_t acme_set_key_metadata(psa_key_attributes_t * attributes,
                                   const uint8_t * metadata, size_t metadata_length);

psa_status_t acme_get_key_metadata(psa_key_attributes_t * attributes,
                                   uint8_t * metadata, size_t metadata_length, size_t * metadata_size);

In this case the following usage-pattern would be possible:

/* Given some_driver_key_id */
psa_status_t status;
psa_key_attributes_t attributes;

/* Populate key attributes from driver, constituting a registration */
status = psa_get_key_attributes(some_driver_key_id, &attributes);
if (status != PSA_SUCCESS) {
    return status;
}

/* Given some custom metadata */
psa_set_key_metadata(attributes, &attributes, metadata, metadata_length);

/* Run PSA crypto operations as-is */

Note: This would avoid additional registration beyond lookup-rules for key_id + lifetime processed by a driver (via psa_get_key_attributes).

Final note: I purposefully made use of psa_get_key_attributes to populate information about the key as expressed via any driver. This type of usage-pattern seems useful to populate any relevant information towards an existing or a newly established key. Technically an implementation of a driver specific API acme_get_key_attributes could be used to unify any type of transparent-like drivers (like for a builtin key that is providing full key buffer) and any type of opaque key representation (without providing any key buffer on driver wrapper level)

The main benefit of using psa_get_key_attributes to fully resolve keys is that this is an API that can be called by any and all code without any knowledge about implementation-specific details like whether or not a driver is opaque, transparent, locally stored etc, leading to the best possible reuse

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
API design Related the design of the API Crypto API Issue or PR related to the Cryptography API enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

4 participants