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

mbedtls: configurable PSA ITS provider #86184

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions modules/mbedtls/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -253,4 +253,8 @@ else()
# included the required directories for mbedtls in their projects.
endif()

if(CONFIG_MBEDTLS_PSA_ITS_BACKEND_MBEDTLS_FILE)
zephyr_include_directories(psa/include)
endif()

endif()
24 changes: 24 additions & 0 deletions modules/mbedtls/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -243,3 +243,27 @@ config APP_LINK_WITH_MBEDTLS
issues for 'app'.

endif # MBEDTLS

choice MBEDTLS_PSA_ITS_BACKEND
prompt "PSA Internal-Trusted-Storage backend implementation"

config MBEDTLS_PSA_ITS_BACKEND_TFM
bool "Trusted-Firmware-M"
depends on BUILD_WITH_TFM

config MBEDTLS_PSA_ITS_BACKEND_SECURE_STORAGE
bool "Zephyr Secure Storage"
depends on !BUILD_WITH_TFM
Comment on lines +247 to +256
Copy link
Collaborator

@tomi-font tomi-font Feb 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems those choice options don't really bring value. I think we could just have the option to enable the persistent keys in the Mbed TLS PSA Crypto core (by enabling its own internal ITS implementation).
Though that would mean introducing a (helper) Kconfig option that wouldn't have a corresponding build symbol in Mbed TLS.
The alternative would be to just expose the 3 Mbed TLS defines that are needed as Kconfig options and let whoever wants to use that enable them.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The thing the application cares about is support for the PSA key operations (psa_generate_key, etc).
To support PSA_KEY_LIFETIME_PERSISTENT, there needs to be a single implementation of the PSA ITS API.
That seems like the perfect use of a choice symbol to me, even if some options are only used implicitly (MBEDTLS_PSA_ITS_BACKEND_TFM), since Kconfig is now doing the work of ensuring the mutual exclusion.
Otherwise when implementation N of the ITS comes along, every other implementation needs to have a depends on !ITS_N added.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see the logic, but I don't think we'll see any other ITS implementation come along, and all we really need here is a single Kconfig option to handle the Mbed TLS ITS implementation you want to make available.
Also for example for the TF-M case it's more complex than that. TF-M could be enabled but without the ITS partition, which means there wouldn't be ITS even though there is TF-M.
As of now I don't see benefits in having a choice as the TF-M and secure storage options are already handled by preexisting configuration.

Copy link
Collaborator Author

@JordanYates JordanYates Feb 25, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe not upstream, but there are enough problems currently with the Secure Storage API (see #86177) that in the short term at least my downstream is more likely to implement the ITS API than the Secure Storage one.


config MBEDTLS_PSA_ITS_BACKEND_MBEDTLS_FILE
bool "MbedTLS builtin file"
depends on !BUILD_WITH_TFM
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this should also depend on !SECURE_STORAGE.

depends on ARCH_POSIX
select MBEDTLS
select MBEDTLS_PSA_CRYPTO_C
Comment on lines +262 to +263
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In your other PR you were removing imply/select statements but here you are introducing some?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MBEDTLS is select'd in a whole range of code where it really shouldn't be, but again the more immediate problem is the inconsistency between depends on and select.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In some places we also have depends on MBEDTLS. Why not instead just have depends on for everything here? I don't really get your rationale here.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because your current secure storage backends end up enabling FLASH_AREA_CHECK_INTEGRITY, where one of the choices does select MBEDTLS, attempting to use depends on MBEDTLS results in Kconfig dependency loops.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or there was some link over a long chain of options that ended up back there (probably through config HAWKBIT). I forget the exact chain

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because your current secure storage backends end up enabling FLASH_AREA_CHECK_INTEGRITY, where one of the choices does select MBEDTLS, attempting to use depends on MBEDTLS results in Kconfig dependency loops.

Ah okay. Yeah that's annoying. Something likely needs to be reworked but that's out of scope.

help
Native filesystem based implementation of PSA ITS. Note that multiple
instances of the sample application running at the same time will all
share the same backed.

endchoice
6 changes: 6 additions & 0 deletions modules/mbedtls/configs/config-tls-generic.h
Original file line number Diff line number Diff line change
Expand Up @@ -486,6 +486,12 @@
#define MBEDTLS_PSA_CRYPTO_STORAGE_C
#endif

#if defined(CONFIG_MBEDTLS_PSA_ITS_BACKEND_MBEDTLS_FILE)
#define MBEDTLS_PSA_CRYPTO_STORAGE_C
#define MBEDTLS_PSA_ITS_FILE_C
#define MBEDTLS_FS_IO
Comment on lines +490 to +492
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it makes sense to turn those into Kconfig options. Especially PSA_CRYPTO_STORAGE_C (which I am adding in this PR of mine, feel free to take the changes into your PR) and MBEDTLS_PSA_ITS_FILE_C (which could then be used as a condition for SECURE_STORAGE).

#endif

#endif /* CONFIG_MBEDTLS_PSA_CRYPTO_C */

#if defined(CONFIG_MBEDTLS_PSA_STATIC_KEY_SLOTS)
Expand Down
7 changes: 7 additions & 0 deletions modules/mbedtls/psa/include/psa/error.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
/*
* Copyright (c) 2025 Embeint Inc.
*
* SPDX-License-Identifier: Apache-2.0
*/

#include <psa/crypto_values.h>
153 changes: 153 additions & 0 deletions modules/mbedtls/psa/include/psa/internal_trusted_storage.h
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am really not a fan of copying header files, especially considering we already have a psa/internal_trusted_storage.h in Zephyr. (And it was not copied from Mbed TLS, it was made from the PSA Secure Storage spec.)

Mbed TLS has made it private probably for a good reason. And they have no plans whatsoever to make it public.
Allowing to use the Mbed TLS ITS implementation directly doesn't seem quite right if it hasn't been made available by the library itself (through an exposed header file).
Also, as per your comment here all you really care about is persistent keys in the PSA Crypto API. For that we don't need to be able to call the psa_its_*() functions directly, we just need to tell Mbed TLS to use its own internal implementation.

Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
/*
* Copyright The Mbed TLS Contributors
* SPDX-License-Identifier: Apache-2.0
*
* Copy of `mbedtls/library/psa_crypto_its.h` until it is available at the same
* include path as TF-M (`psa/internal_trusted_storage.h`).
*/

#ifndef PSA_CRYPTO_ITS_H
#define PSA_CRYPTO_ITS_H

#include <stddef.h>
#include <stdint.h>

#include <psa/crypto_types.h>
#include <psa/crypto_values.h>

#ifdef __cplusplus
extern "C" {
#endif

/** \brief Flags used when creating a data entry
*/
typedef uint32_t psa_storage_create_flags_t;

/** \brief A type for UIDs used for identifying data
*/
typedef uint64_t psa_storage_uid_t;

/** No flags to pass */
#define PSA_STORAGE_FLAG_NONE 0
/**
* The data associated with the uid will not be able to be modified or deleted.
* Intended to be used to set bits in `psa_storage_create_flags_t`
*/
#define PSA_STORAGE_FLAG_WRITE_ONCE (1 << 0)

/**
* \brief A container for metadata associated with a specific uid
*/
struct psa_storage_info_t {
uint32_t size; /**< The size of the data associated with a uid **/
psa_storage_create_flags_t flags; /**< The flags set when the uid was created **/
};

/** Flag indicating that \ref psa_storage_create and \ref psa_storage_set_extended are supported */
#define PSA_STORAGE_SUPPORT_SET_EXTENDED (1 << 0)

#define PSA_ITS_API_VERSION_MAJOR 1
#define PSA_ITS_API_VERSION_MINOR 1

/**
* \brief create a new or modify an existing uid/value pair
*
* \param[in] uid the identifier for the data
* \param[in] data_length The size in bytes of the data in `p_data`
* \param[in] p_data A buffer containing the data
* \param[in] create_flags The flags that the data will be stored with
*
* \return A status indicating the success/failure of the operation
*
* \retval #PSA_SUCCESS The operation completed successfully
* \retval #PSA_ERROR_NOT_PERMITTED The operation failed because the provided `uid` value
* was already created with PSA_STORAGE_FLAG_WRITE_ONCE
* \retval #PSA_ERROR_NOT_SUPPORTED The operation failed because one or more of the flags
* provided in `create_flags` is not supported or is not
* valid
* \retval #PSA_ERROR_INSUFFICIENT_STORAGE The operation failed because there was insufficient
* space on the storage medium
* \retval #PSA_ERROR_STORAGE_FAILURE The operation failed because the physical storage has
* failed (Fatal error)
* \retval #PSA_ERROR_INVALID_ARGUMENT The operation failed because one of the provided
* pointers(`p_data`) is invalid, for example is `NULL`
* or references memory the caller cannot access
*/
psa_status_t psa_its_set(psa_storage_uid_t uid,
uint32_t data_length,
const void *p_data,
psa_storage_create_flags_t create_flags);

/**
* \brief Retrieve the value associated with a provided uid
*
* \param[in] uid The uid value
* \param[in] data_offset The starting offset of the data requested
* \param[in] data_length the amount of data requested (and the minimum allocated size of the
* `p_data` buffer)
* \param[out] p_data The buffer where the data will be placed upon successful completion
* \param[out] p_data_length The amount of data returned in the p_data buffer
*
*
* \return A status indicating the success/failure of the operation
*
* \retval #PSA_SUCCESS The operation completed successfully
* \retval #PSA_ERROR_DOES_NOT_EXIST The operation failed because the provided `uid` value was
* not found in the storage
* \retval #PSA_ERROR_STORAGE_FAILURE The operation failed because the physical storage has
* failed (Fatal error)
* \retval #PSA_ERROR_DATA_CORRUPT The operation failed because stored data has been corrupted
* \retval #PSA_ERROR_INVALID_ARGUMENT The operation failed because one of the provided
* pointers(`p_data`, `p_data_length`) is invalid. For example
* is `NULL` or references memory the caller cannot access. In
* addition, this can also happen if an invalid offset was
* provided.
*/
psa_status_t psa_its_get(psa_storage_uid_t uid,
uint32_t data_offset,
uint32_t data_length,
void *p_data,
size_t *p_data_length);

/**
* \brief Retrieve the metadata about the provided uid
*
* \param[in] uid The uid value
* \param[out] p_info A pointer to the `psa_storage_info_t` struct that will be populated
* with the metadata
*
* \return A status indicating the success/failure of the operation
*
* \retval #PSA_SUCCESS The operation completed successfully
* \retval #PSA_ERROR_DOES_NOT_EXIST The operation failed because the provided uid value was
* not found in the storage
* \retval #PSA_ERROR_DATA_CORRUPT The operation failed because stored data has been corrupted
* \retval #PSA_ERROR_INVALID_ARGUMENT The operation failed because one of the provided
* pointers(`p_info`) is invalid, for example is `NULL` or
* references memory the caller cannot access
*/
psa_status_t psa_its_get_info(psa_storage_uid_t uid,
struct psa_storage_info_t *p_info);

/**
* \brief Remove the provided key and its associated data from the storage
*
* \param[in] uid The uid value
*
* \return A status indicating the success/failure of the operation
*
* \retval #PSA_SUCCESS The operation completed successfully
* \retval #PSA_ERROR_DOES_NOT_EXIST The operation failed because the provided key value was
* not found in the storage
* \retval #PSA_ERROR_NOT_PERMITTED The operation failed because the provided key value was
* created with PSA_STORAGE_FLAG_WRITE_ONCE
* \retval #PSA_ERROR_STORAGE_FAILURE The operation failed because the physical storage has
* failed (Fatal error)
*/
psa_status_t psa_its_remove(psa_storage_uid_t uid);

#ifdef __cplusplus
}
#endif

#endif /* PSA_CRYPTO_ITS_H */
2 changes: 1 addition & 1 deletion subsys/secure_storage/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

menuconfig SECURE_STORAGE
bool "Secure storage subsystem"
depends on !BUILD_WITH_TFM
depends on MBEDTLS_PSA_ITS_BACKEND_SECURE_STORAGE
select EXPERIMENTAL
help
The secure storage subsystem provides an implementation of the PSA Secure Storage API
Expand Down
25 changes: 22 additions & 3 deletions tests/subsys/secure_storage/psa/its/src/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,11 @@
/* The flash must be erased after this test suite is run for the write-once entry test to pass. */
ZTEST_SUITE(secure_storage_psa_its, NULL, NULL, NULL, NULL, NULL);

#ifdef CONFIG_SECURE_STORAGE
#if defined(CONFIG_SECURE_STORAGE)
#define MAX_DATA_SIZE CONFIG_SECURE_STORAGE_ITS_MAX_DATA_SIZE
#elif defined(CONFIG_MBEDTLS_PSA_ITS_BACKEND_MBEDTLS_FILE)
/* The backend supports arbitrarily large files, limit the value here for array sizes */
#define MAX_DATA_SIZE 256
#else
#define MAX_DATA_SIZE CONFIG_TFM_ITS_MAX_ASSET_SIZE
#endif
Expand All @@ -32,7 +35,7 @@ ZTEST(secure_storage_psa_its, test_all_sizes)

fill_data_buffer(written_data);

for (unsigned int i = 0; i <= sizeof(written_data); ++i) {
for (unsigned int i = 1; i <= sizeof(written_data); ++i) {

ret = psa_its_set(UID, i, written_data, PSA_STORAGE_FLAG_NONE);
zassert_equal(ret, PSA_SUCCESS);
Expand All @@ -41,9 +44,11 @@ ZTEST(secure_storage_psa_its, test_all_sizes)
zassert_equal(ret, PSA_SUCCESS);
zassert_equal(info.flags, PSA_STORAGE_FLAG_NONE);
zassert_equal(info.size, i);
#ifndef CONFIG_MBEDTLS_PSA_ITS_BACKEND_MBEDTLS_FILE
zassert_equal(info.capacity, i);
#endif

ret = psa_its_get(UID, 0, sizeof(read_data), read_data, &data_length);
ret = psa_its_get(UID, 0, i, read_data, &data_length);
zassert_equal(ret, PSA_SUCCESS);
zassert_equal(data_length, i);
zassert_mem_equal(read_data, written_data, data_length);
Expand Down Expand Up @@ -78,6 +83,14 @@ ZTEST(secure_storage_psa_its, test_all_offsets)

ZTEST(secure_storage_psa_its, test_max_num_entries)
{
if (IS_ENABLED(CONFIG_MBEDTLS_PSA_ITS_BACKEND_MBEDTLS_FILE)) {
/* The mbedtls file backend will happily fill your hard drive
* with as many entries as you have space. Skip this test.
*/
ztest_test_skip();
return;
}

psa_status_t ret = PSA_SUCCESS;
unsigned int i;
struct psa_storage_info_t info;
Expand Down Expand Up @@ -118,6 +131,12 @@ ZTEST(secure_storage_psa_its, test_write_once_flag)
const uint8_t data[MAX_DATA_SIZE] = {};
struct psa_storage_info_t info;

if (IS_ENABLED(CONFIG_MBEDTLS_PSA_ITS_BACKEND_MBEDTLS_FILE)) {
/* The mbedtls file backend does not support this option */
ztest_test_skip();
return;
}

ret = psa_its_set(uid, sizeof(data), data, PSA_STORAGE_FLAG_WRITE_ONCE);
zassert_equal(ret, PSA_SUCCESS, "%s%d", (ret == PSA_ERROR_NOT_PERMITTED) ?
"Has the flash been erased since this test ran? " : "", ret);
Expand Down
6 changes: 6 additions & 0 deletions tests/subsys/secure_storage/psa/its/testcase.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -52,3 +52,9 @@ tests:
integration_platforms:
- nrf9151dk/nrf9151/ns
extra_args: EXTRA_CONF_FILE=overlay-tfm.conf

secure_storage.psa.its.mbedtls_file:
platform_allow:
- native_sim
extra_configs:
- CONFIG_MBEDTLS_PSA_ITS_BACKEND_MBEDTLS_FILE=y
Loading