diff --git a/components/mbedtls/CMakeLists.txt b/components/mbedtls/CMakeLists.txt index 0bfd1f4ebf9..a105206c809 100644 --- a/components/mbedtls/CMakeLists.txt +++ b/components/mbedtls/CMakeLists.txt @@ -39,7 +39,8 @@ if(NOT ${IDF_TARGET} STREQUAL "linux") endif() endif() -set(mbedtls_srcs "port/esp_mem.c") +set(mbedtls_srcs "port/esp_mem.c" + "port/psa_crypto_storage/esp_psa_key_file.c") set(mbedtls_include_dirs "port/include" "mbedtls/include" @@ -60,6 +61,7 @@ endif() list(APPEND mbedtls_include_dirs "${COMPONENT_DIR}/port/psa_driver/include") +list(APPEND mbedtls_include_dirs "${COMPONENT_DIR}/port/psa_crypto_storage/include") idf_component_register(SRCS "${mbedtls_srcs}" INCLUDE_DIRS "${mbedtls_include_dirs}" @@ -243,6 +245,7 @@ if(NOT ${IDF_TARGET} STREQUAL "linux") target_link_libraries(tfpsacrypto PRIVATE "$<$:idf::nvs_flash>") # Define compile definition to indicate ESP-IDF PSA ITS implementation is available target_compile_definitions(tfpsacrypto PUBLIC "$<$:ESP_PSA_ITS_AVAILABLE>") + target_include_directories(tfpsacrypto PRIVATE "${COMPONENT_DIR}/port/psa_crypto_storage/include") else() # For v1: check if component is in build before adding source and linking idf_build_get_property(build_components BUILD_COMPONENTS) @@ -251,6 +254,7 @@ if(NOT ${IDF_TARGET} STREQUAL "linux") idf_component_get_property(nvs_flash_lib nvs_flash COMPONENT_LIB) target_link_libraries(tfpsacrypto PRIVATE ${nvs_flash_lib}) target_compile_definitions(tfpsacrypto PUBLIC ESP_PSA_ITS_AVAILABLE) + target_include_directories(tfpsacrypto PRIVATE "${COMPONENT_DIR}/port/psa_crypto_storage/include") endif() endif() endif() diff --git a/components/mbedtls/Kconfig b/components/mbedtls/Kconfig index ea5f41dc2b5..fc13f5e37b6 100644 --- a/components/mbedtls/Kconfig +++ b/components/mbedtls/Kconfig @@ -30,6 +30,38 @@ menu "mbedTLS" which is added through vfs component for ESP32 based targets or by the host system when the target is Linux. + config MBEDTLS_PSA_ITS_CUSTOM_STORAGE_BACKEND + bool "Enable custom storage backend for PSA ITS" + default n + help + Enable support for registering a custom storage backend that + handles PSA ITS operations for keys in a reserved UID range. + When enabled, users can call esp_psa_its_register_custom_backend() + to route storage operations for UIDs in the configured range to + their own implementation. UIDs outside the range continue using + the default NVS backend. + + config MBEDTLS_PSA_ITS_CUSTOM_BACKEND_UID_MIN + hex "Minimum UID for custom backend range" + depends on MBEDTLS_PSA_ITS_CUSTOM_STORAGE_BACKEND + default 0x30000000 + range 0x00000001 0x3FFFFFFF + help + The minimum UID value (inclusive) that will be routed to the + custom storage backend. Must be within the PSA user key ID + range (0x00000001 - 0x3FFFFFFF). + + config MBEDTLS_PSA_ITS_CUSTOM_BACKEND_UID_MAX + hex "Maximum UID for custom backend range" + depends on MBEDTLS_PSA_ITS_CUSTOM_STORAGE_BACKEND + default 0x3FFFFFFF + range MBEDTLS_PSA_ITS_CUSTOM_BACKEND_UID_MIN 0x3FFFFFFF + help + The maximum UID value (inclusive) that will be routed to the + custom storage backend. Must be >= the minimum UID and within + the PSA user key ID range (0x00000001 - 0x3FFFFFFF); the lower + bound is enforced by Kconfig. + config MBEDTLS_THREADING_C bool "Enable the threading abstraction layer" default y diff --git a/components/mbedtls/port/psa_crypto_storage/esp_psa_its.c b/components/mbedtls/port/psa_crypto_storage/esp_psa_its.c index 89a5f693f88..e7e8b6de002 100644 --- a/components/mbedtls/port/psa_crypto_storage/esp_psa_its.c +++ b/components/mbedtls/port/psa_crypto_storage/esp_psa_its.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2025-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 * @@ -23,8 +23,28 @@ #include "nvs_flash.h" #include "esp_log.h" +#include "sdkconfig.h" + +#if CONFIG_MBEDTLS_PSA_ITS_CUSTOM_STORAGE_BACKEND +#include "esp_psa_its.h" +#endif + static const char *TAG = "esp_psa_its"; +#if CONFIG_MBEDTLS_PSA_ITS_CUSTOM_STORAGE_BACKEND +/* Single registered custom backend (NULL when none registered) */ +static const esp_psa_its_custom_ops_t *s_custom_ops = NULL; + +/** + * Check if a UID falls within the custom backend range. + */ +static inline bool uid_in_custom_range(psa_storage_uid_t uid) +{ + return (uid >= CONFIG_MBEDTLS_PSA_ITS_CUSTOM_BACKEND_UID_MIN && + uid <= CONFIG_MBEDTLS_PSA_ITS_CUSTOM_BACKEND_UID_MAX); +} +#endif /* CONFIG_MBEDTLS_PSA_ITS_CUSTOM_STORAGE_BACKEND */ + /* NVS namespace for PSA ITS */ #define PSA_ITS_NVS_NAMESPACE "psa_its" @@ -106,6 +126,15 @@ psa_status_t psa_its_get_info(psa_storage_uid_t uid, return PSA_ERROR_INVALID_ARGUMENT; } +#if CONFIG_MBEDTLS_PSA_ITS_CUSTOM_STORAGE_BACKEND + if (uid_in_custom_range(uid)) { + if (s_custom_ops == NULL || s_custom_ops->get_info == NULL) { + return PSA_ERROR_STORAGE_FAILURE; + } + return s_custom_ops->get_info(s_custom_ops->ctx, uid, p_info); + } +#endif + /* Convert UID to NVS key */ uid_to_nvs_key(uid, nvs_key); @@ -188,6 +217,16 @@ psa_status_t psa_its_get(psa_storage_uid_t uid, return PSA_ERROR_INVALID_ARGUMENT; } +#if CONFIG_MBEDTLS_PSA_ITS_CUSTOM_STORAGE_BACKEND + if (uid_in_custom_range(uid)) { + if (s_custom_ops == NULL || s_custom_ops->get == NULL) { + return PSA_ERROR_STORAGE_FAILURE; + } + return s_custom_ops->get(s_custom_ops->ctx, uid, data_offset, + data_length, p_data, p_data_length); + } +#endif + /* Convert UID to NVS key */ uid_to_nvs_key(uid, nvs_key); @@ -297,6 +336,16 @@ psa_status_t psa_its_set(psa_storage_uid_t uid, return PSA_ERROR_INVALID_ARGUMENT; } +#if CONFIG_MBEDTLS_PSA_ITS_CUSTOM_STORAGE_BACKEND + if (uid_in_custom_range(uid)) { + if (s_custom_ops == NULL || s_custom_ops->set == NULL) { + return PSA_ERROR_STORAGE_FAILURE; + } + return s_custom_ops->set(s_custom_ops->ctx, uid, data_length, + p_data, create_flags); + } +#endif + /* Convert UID to NVS key */ uid_to_nvs_key(uid, nvs_key); @@ -383,6 +432,15 @@ psa_status_t psa_its_remove(psa_storage_uid_t uid) psa_its_entry_t *existing_entry = NULL; psa_status_t status = PSA_ERROR_STORAGE_FAILURE; +#if CONFIG_MBEDTLS_PSA_ITS_CUSTOM_STORAGE_BACKEND + if (uid_in_custom_range(uid)) { + if (s_custom_ops == NULL || s_custom_ops->remove == NULL) { + return PSA_ERROR_STORAGE_FAILURE; + } + return s_custom_ops->remove(s_custom_ops->ctx, uid); + } +#endif + /* Convert UID to NVS key */ uid_to_nvs_key(uid, nvs_key); @@ -451,3 +509,30 @@ exit: nvs_close(handle); return status; } + +#if CONFIG_MBEDTLS_PSA_ITS_CUSTOM_STORAGE_BACKEND +psa_status_t esp_psa_its_register_custom_backend(const esp_psa_its_custom_ops_t *ops) +{ + if (ops == NULL || ops->set == NULL || ops->get == NULL || + ops->get_info == NULL || ops->remove == NULL) { + return PSA_ERROR_INVALID_ARGUMENT; + } + + if (s_custom_ops != NULL) { + return PSA_ERROR_NOT_PERMITTED; + } + + s_custom_ops = ops; + return PSA_SUCCESS; +} + +psa_status_t esp_psa_its_unregister_custom_backend(void) +{ + if (s_custom_ops == NULL) { + return PSA_ERROR_DOES_NOT_EXIST; + } + + s_custom_ops = NULL; + return PSA_SUCCESS; +} +#endif diff --git a/components/mbedtls/port/psa_crypto_storage/esp_psa_key_file.c b/components/mbedtls/port/psa_crypto_storage/esp_psa_key_file.c new file mode 100644 index 00000000000..68253ec4aaa --- /dev/null +++ b/components/mbedtls/port/psa_crypto_storage/esp_psa_key_file.c @@ -0,0 +1,70 @@ +/* + * SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "esp_psa_key_file.h" + +#include "psa/crypto.h" +#include "psa_crypto_storage.h" + +size_t esp_psa_key_file_size(const size_t material_len) +{ + return psa_persistent_key_storage_blob_size(material_len); +} + +psa_status_t esp_psa_key_file_pack(const psa_key_attributes_t *attrs, + const uint8_t *material, + const size_t material_len, + uint8_t *out_buf, + const size_t out_buf_size, + size_t *out_len) +{ + if (attrs == NULL || out_buf == NULL || out_len == NULL || + (material == NULL && material_len != 0)) { + return PSA_ERROR_INVALID_ARGUMENT; + } + + const size_t total = psa_persistent_key_storage_blob_size(material_len); + if (out_buf_size < total) { + return PSA_ERROR_BUFFER_TOO_SMALL; + } + + psa_format_key_data_for_storage(material, material_len, attrs, out_buf); + + *out_len = total; + return PSA_SUCCESS; +} + +psa_status_t esp_psa_key_file_unpack(const uint8_t *blob, + const size_t blob_len, + psa_key_attributes_t *attrs, + const uint8_t **material, + size_t *material_len) +{ + if (blob == NULL || attrs == NULL || material == NULL || material_len == NULL) { + return PSA_ERROR_INVALID_ARGUMENT; + } + + uint8_t *upstream_material = NULL; + size_t upstream_material_len = 0; + psa_status_t status = psa_parse_key_data_from_storage(blob, blob_len, + &upstream_material, + &upstream_material_len, + attrs); + if (status != PSA_SUCCESS) { + return status; + } + + /* Upstream allocates and copies the material into a fresh buffer. + * Discard the copy and return a pointer into the caller's blob — the + * material section starts immediately after the fixed-size header, so + * the bytes are identical. Preserves the zero-copy public contract. */ + *material = (upstream_material_len == 0) ? NULL + : blob + (blob_len - upstream_material_len); + *material_len = upstream_material_len; + + psa_free_persistent_key_data(upstream_material, upstream_material_len); + return PSA_SUCCESS; +} diff --git a/components/mbedtls/port/psa_crypto_storage/include/esp_psa_its.h b/components/mbedtls/port/psa_crypto_storage/include/esp_psa_its.h new file mode 100644 index 00000000000..d80206a1f6e --- /dev/null +++ b/components/mbedtls/port/psa_crypto_storage/include/esp_psa_its.h @@ -0,0 +1,122 @@ +/* + * SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + * + * PSA ITS custom storage backend API. + * + * When CONFIG_MBEDTLS_PSA_ITS_CUSTOM_STORAGE_BACKEND is enabled, users can register + * a custom storage backend for a reserved range of PSA key IDs. UIDs within + * the configured range are routed to the registered backend; all other UIDs + * continue using the default NVS backend. + */ + +#pragma once + +#include +#include +#include "psa/internal_trusted_storage.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Custom storage backend operations for PSA ITS. + * + * Implement this structure to provide a custom storage backend for PSA ITS + * keys in the configured UID range. Callback signatures mirror the PSA ITS + * API with an additional user-provided context pointer. + */ +typedef struct { + /** + * @brief Store data for the given UID. + * + * @param ctx User-provided context pointer + * @param uid Storage UID (key ID) + * @param data_length Length of the data to store + * @param p_data Pointer to the data buffer + * @param create_flags Storage flags (e.g., PSA_STORAGE_FLAG_WRITE_ONCE) + * @return PSA status code + */ + psa_status_t (*set)(void *ctx, + const psa_storage_uid_t uid, + const uint32_t data_length, + const void *p_data, + const psa_storage_create_flags_t create_flags); + + /** + * @brief Retrieve data for the given UID. + * + * @param ctx User-provided context pointer + * @param uid Storage UID (key ID) + * @param data_offset Byte offset within the stored data + * @param data_length Number of bytes to retrieve + * @param p_data Output buffer for the data + * @param p_data_length On success, set to the number of bytes written + * @return PSA status code + */ + psa_status_t (*get)(void *ctx, + const psa_storage_uid_t uid, + const uint32_t data_offset, + const uint32_t data_length, + void *p_data, + size_t *p_data_length); + + /** + * @brief Retrieve metadata for the given UID. + * + * @param ctx User-provided context pointer + * @param uid Storage UID (key ID) + * @param p_info Output structure for size and flags + * @return PSA status code + */ + psa_status_t (*get_info)(void *ctx, + const psa_storage_uid_t uid, + struct psa_storage_info_t *p_info); + + /** + * @brief Remove data for the given UID. + * + * @param ctx User-provided context pointer + * @param uid Storage UID (key ID) + * @return PSA status code + */ + psa_status_t (*remove)(void *ctx, + const psa_storage_uid_t uid); + + /** User-provided context pointer, passed as the first argument to all callbacks. */ + void *ctx; +} esp_psa_its_custom_ops_t; + +/** + * @brief Register a custom storage backend for the configured UID range. + * + * Only one custom backend may be registered at a time. The UID range is + * determined by CONFIG_MBEDTLS_PSA_ITS_CUSTOM_BACKEND_UID_MIN (inclusive) and + * CONFIG_MBEDTLS_PSA_ITS_CUSTOM_BACKEND_UID_MAX (inclusive). + * + * @param ops Pointer to the operations structure. Must remain valid for + * the lifetime of the registration. All four function pointers + * (set, get, get_info, remove) must be non-NULL. + * + * @return PSA_SUCCESS on success + * @return PSA_ERROR_INVALID_ARGUMENT if ops or any callback is NULL + * @return PSA_ERROR_NOT_PERMITTED if a backend is already registered + */ +psa_status_t esp_psa_its_register_custom_backend(const esp_psa_its_custom_ops_t *ops); + +/** + * @brief Unregister the currently registered custom storage backend. + * + * After this call, UIDs in the custom range will return + * PSA_ERROR_STORAGE_FAILURE until a new backend is registered. + * + * @return PSA_SUCCESS on success + * @return PSA_ERROR_DOES_NOT_EXIST if no backend is registered + */ +psa_status_t esp_psa_its_unregister_custom_backend(void); + +#ifdef __cplusplus +} +#endif diff --git a/components/mbedtls/port/psa_crypto_storage/include/esp_psa_key_file.h b/components/mbedtls/port/psa_crypto_storage/include/esp_psa_key_file.h new file mode 100644 index 00000000000..3e306d84bf6 --- /dev/null +++ b/components/mbedtls/port/psa_crypto_storage/include/esp_psa_key_file.h @@ -0,0 +1,87 @@ +/* + * SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + * + * Custom PSA ITS backends that synthesize blobs on read, or that strip the + * header on write to save space, use these helpers to convert between PSA + * attributes + raw key material and the documented blob format without + * depending on tf-psa-crypto internal functions. + */ + +#pragma once + +#include +#include + +#include "psa/crypto.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Total blob size produced by esp_psa_key_file_pack() for a given key + * material length (i.e. fixed header size + @p material_len). + */ +size_t esp_psa_key_file_size(const size_t material_len); + +/** + * @brief Pack key attributes and material into a key file blob. + * + * @param[in] attrs Source attributes (lifetime, type, bits, policy). + * @param[in] material Key material. For transparent keys, the + * psa_export_key() output. For opaque keys, the + * driver-specific opaque blob. + * @param[in] material_len Length of @p material in bytes. + * @param[out] out_buf Destination buffer. + * @param[in] out_buf_size Size of @p out_buf. Must be at least + * esp_psa_key_file_size(material_len). + * @param[out] out_len Set to the number of bytes written. + * + * @retval PSA_SUCCESS + * @retval PSA_ERROR_INVALID_ARGUMENT Null pointer. + * @retval PSA_ERROR_BUFFER_TOO_SMALL @p out_buf_size is too small. + */ +psa_status_t esp_psa_key_file_pack(const psa_key_attributes_t *attrs, + const uint8_t *material, + const size_t material_len, + uint8_t *out_buf, + const size_t out_buf_size, + size_t *out_len); + +/** + * @brief Parse a key file blob into key attributes and material. + * + * The returned @p material pointer points into @p blob and is valid only as + * long as @p blob itself is. No allocation occurs. + * + * Attribute fields described in the blob (lifetime, type, bits, usage, + * algorithm, enrollment algorithm) are set on @p attrs on success. Other + * fields (notably the key id) are left untouched, so the caller can set the + * id beforehand if needed. + * + * @param[in] blob Blob to parse. + * @param[in] blob_len Length of @p blob in bytes. + * @param[out] attrs Filled with parsed attributes on success. + * @param[out] material Set to point to the material section within @p blob, + * or NULL if the blob declares zero-length material. + * @param[out] material_len Length of the material section in bytes. + * + * @retval PSA_SUCCESS + * @retval PSA_ERROR_INVALID_ARGUMENT Null pointer, or @p blob_len smaller than + * ESP_PSA_KEY_FILE_HEADER_SIZE. + * @retval PSA_ERROR_DATA_INVALID Bad magic, unsupported version, or the + * declared material length does not match + * the blob size (the spec rejects trailing + * data on load). + */ +psa_status_t esp_psa_key_file_unpack(const uint8_t *blob, + const size_t blob_len, + psa_key_attributes_t *attrs, + const uint8_t **material, + size_t *material_len); + +#ifdef __cplusplus +} +#endif diff --git a/docs/en/api-reference/protocols/mbedtls.rst b/docs/en/api-reference/protocols/mbedtls.rst index a491e065a1c..44ebdff9ead 100644 --- a/docs/en/api-reference/protocols/mbedtls.rst +++ b/docs/en/api-reference/protocols/mbedtls.rst @@ -167,6 +167,114 @@ The new mbedTLS configuration system is organized into logical categories for ea X.509 certificate parsing, validation, and certificate bundle management. +PSA ITS Custom Storage Backend +------------------------------- + +ESP-IDF's PSA Internal Trusted Storage (ITS) implementation uses NVS as its default backend for storing persistent PSA Crypto keys. The custom storage backend feature allows routing a reserved range of PSA key IDs to a user-provided storage implementation, while all other keys continue using NVS. + +This is useful when: + +- Certain keys need to be stored on a different filesystem (FATFS, SPIFFS, littlefs) +- Keys require hardware-protected encryption (e.g., via TEE secure storage) +- Different storage partitions are needed for different key categories + +Enabling the Custom Backend +^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Enable the feature via ``menuconfig`` under ``Component Config`` > ``mbedTLS``: + +- :ref:`CONFIG_MBEDTLS_PSA_ITS_CUSTOM_STORAGE_BACKEND`: Enable the custom storage backend +- :ref:`CONFIG_MBEDTLS_PSA_ITS_CUSTOM_BACKEND_UID_MIN`: Start of the custom key ID range (default ``0x30000000``) +- :ref:`CONFIG_MBEDTLS_PSA_ITS_CUSTOM_BACKEND_UID_MAX`: End of the custom key ID range (default ``0x3FFFFFFF``) + +PSA key IDs within the configured range are routed to the registered backend. All other key IDs (and internal PSA data such as the random seed) continue using the default NVS backend. + +Implementing a Custom Backend +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Implement the ``esp_psa_its_custom_ops_t`` callback structure and register it before using PSA Crypto with keys in the custom range: + +.. code-block:: c + + #include "esp_psa_its.h" + + static psa_status_t my_set(void *ctx, const psa_storage_uid_t uid, + const uint32_t data_length, const void *p_data, + const psa_storage_create_flags_t create_flags) + { + /* Store the blob identified by uid */ + } + + static psa_status_t my_get(void *ctx, const psa_storage_uid_t uid, + const uint32_t data_offset, const uint32_t data_length, + void *p_data, size_t *p_data_length) + { + /* Retrieve the blob identified by uid */ + } + + static psa_status_t my_get_info(void *ctx, const psa_storage_uid_t uid, + struct psa_storage_info_t *p_info) + { + /* Return size and flags for the blob identified by uid */ + } + + static psa_status_t my_remove(void *ctx, const psa_storage_uid_t uid) + { + /* Delete the blob identified by uid */ + } + + static esp_psa_its_custom_ops_t my_ops = { + .set = my_set, + .get = my_get, + .get_info = my_get_info, + .remove = my_remove, + .ctx = NULL, /* optional user context */ + }; + + /* Register before using PSA keys in the custom range */ + esp_psa_its_register_custom_backend(&my_ops); + +The callback signatures mirror the PSA ITS API. Each callback receives the raw ``psa_storage_uid_t`` (not a string), allowing the implementation to make routing decisions based on the numeric key ID. The ``ctx`` pointer is passed as the first argument to every callback. + +.. note:: + + Only persistent keys flow through the ITS layer. PSA requires ``psa_set_key_id()`` for persistent keys, so the application always controls which key IDs it assigns and thus which range they fall into. + + The backend implementation is responsible for enforcing ``psa_storage_create_flags_t`` semantics if needed. + +Working with the PSA Key Blob Format +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The byte stream that flows through ``psa_its_set()`` / ``psa_its_get()`` is the PSA persistent key blob format documented in the Mbed TLS `storage specification `__. Backends that store the blob verbatim do not need to look inside it. Backends that strip the header on write (to save space) or synthesise the blob on read (for example, to expose a pre-provisioned hardware key) need to construct or parse it themselves. + +ESP-IDF provides two helpers in ``esp_psa_key_file.h`` for this: + +- :cpp:func:`esp_psa_key_file_pack` — assemble a key blob from a ``psa_key_attributes_t`` structure and raw key material bytes. +- :cpp:func:`esp_psa_key_file_unpack` — parse a key blob back into attributes and a pointer into the key material section. +- :cpp:func:`esp_psa_key_file_size` — return the total blob size for a given material length. + +These helpers implement the documented byte layout directly and do not depend on any internal Mbed TLS function. For example, a backend that stores only the inner key bytes can rebuild the blob on read: + +.. code-block:: c + + #include "esp_psa_key_file.h" + + psa_key_attributes_t attr = PSA_KEY_ATTRIBUTES_INIT; + psa_set_key_lifetime(&attr, PSA_KEY_LIFETIME_PERSISTENT); + psa_set_key_type(&attr, PSA_KEY_TYPE_AES); + psa_set_key_bits(&attr, key_data_len * 8); + psa_set_key_usage_flags(&attr, PSA_KEY_USAGE_ENCRYPT | PSA_KEY_USAGE_DECRYPT); + psa_set_key_algorithm(&attr, PSA_ALG_CBC_NO_PADDING); + + size_t blob_size = esp_psa_key_file_size(key_data_len); + uint8_t *blob = calloc(1, blob_size); + size_t written = 0; + esp_psa_key_file_pack(&attr, key_data, key_data_len, blob, blob_size, &written); + /* blob now holds the full PSA persistent key file; copy the requested + * window into p_data per psa_its_get()'s offset/length arguments. */ + +For a complete working example using a custom NVS namespace as the custom backend, refer to :example:`security/psa_its_custom_backend`. + Application Examples -------------------- diff --git a/docs/zh_CN/api-reference/protocols/mbedtls.rst b/docs/zh_CN/api-reference/protocols/mbedtls.rst index 271874f1cd7..500235fea39 100644 --- a/docs/zh_CN/api-reference/protocols/mbedtls.rst +++ b/docs/zh_CN/api-reference/protocols/mbedtls.rst @@ -167,6 +167,114 @@ ESP-IDF 为 Mbed TLS 提供了基于预设的配置系统,用于简化设置 X.509 证书解析、验证和证书包管理。 +PSA ITS 自定义存储后端 +----------------------- + +ESP-IDF 的 PSA 内部可信存储 (Internal Trusted Storage, ITS) 实现默认使用 NVS 作为持久化 PSA Crypto 密钥的存储后端。自定义存储后端功能允许将保留范围内的 PSA 密钥 ID 路由到用户提供的存储实现,而其他密钥继续使用 NVS。 + +适用场景包括: + +- 某些密钥需要存储在不同的文件系统中(如 FATFS、SPIFFS、littlefs) +- 密钥需要硬件保护的加密存储(例如通过 TEE 安全存储) +- 不同类别的密钥需要使用不同的存储分区 + +启用自定义后端 +^^^^^^^^^^^^^^^ + +通过 ``menuconfig`` 在 ``Component Config`` > ``mbedTLS`` 中启用该功能: + +- :ref:`CONFIG_MBEDTLS_PSA_ITS_CUSTOM_STORAGE_BACKEND`:启用自定义存储后端 +- :ref:`CONFIG_MBEDTLS_PSA_ITS_CUSTOM_BACKEND_UID_MIN`:自定义密钥 ID 范围的起始值(默认 ``0x30000000``) +- :ref:`CONFIG_MBEDTLS_PSA_ITS_CUSTOM_BACKEND_UID_MAX`:自定义密钥 ID 范围的结束值(默认 ``0x3FFFFFFF``) + +配置范围内的 PSA 密钥 ID 会被路由到已注册的后端。其他所有密钥 ID(以及随机种子等 PSA 内部数据)继续使用默认的 NVS 后端。 + +实现自定义后端 +^^^^^^^^^^^^^^^ + +实现 ``esp_psa_its_custom_ops_t`` 回调结构体,并在使用自定义范围内的 PSA Crypto 密钥之前进行注册: + +.. code-block:: c + + #include "esp_psa_its.h" + + static psa_status_t my_set(void *ctx, const psa_storage_uid_t uid, + const uint32_t data_length, const void *p_data, + const psa_storage_create_flags_t create_flags) + { + /* 存储 uid 对应的 blob */ + } + + static psa_status_t my_get(void *ctx, const psa_storage_uid_t uid, + const uint32_t data_offset, const uint32_t data_length, + void *p_data, size_t *p_data_length) + { + /* 读取 uid 对应的 blob */ + } + + static psa_status_t my_get_info(void *ctx, const psa_storage_uid_t uid, + struct psa_storage_info_t *p_info) + { + /* 返回 uid 对应 blob 的大小和标志位 */ + } + + static psa_status_t my_remove(void *ctx, const psa_storage_uid_t uid) + { + /* 删除 uid 对应的 blob */ + } + + static esp_psa_its_custom_ops_t my_ops = { + .set = my_set, + .get = my_get, + .get_info = my_get_info, + .remove = my_remove, + .ctx = NULL, /* 可选的用户上下文 */ + }; + + /* 在使用自定义范围内的 PSA 密钥之前注册 */ + esp_psa_its_register_custom_backend(&my_ops); + +回调函数的签名与 PSA ITS API 保持一致。每个回调接收原始的 ``psa_storage_uid_t``\ (而非字符串),允许实现根据数值型密钥 ID 进行路由决策。``ctx`` 指针会作为第一个参数传递给每个回调。 + +.. note:: + + 只有持久化密钥会经过 ITS 层。PSA 要求持久化密钥必须调用 ``psa_set_key_id()``,因此应用程序始终可以控制分配哪些密钥 ID,进而决定它们落在哪个范围内。 + + 如有需要,后端实现需自行处理 ``psa_storage_create_flags_t`` 的语义。 + +处理 PSA 密钥文件格式 +^^^^^^^^^^^^^^^^^^^^^^ + +通过 ``psa_its_set()`` / ``psa_its_get()`` 传输的字节流采用 Mbed TLS `存储规范 `__\ 中描述的 PSA 持久密钥文件格式。原样存储该 blob 的后端无需查看其内容;在写入时剥离头部以节省空间,或在读取时合成 blob 的后端(例如,将预配置的硬件密钥暴露为持久 PSA 密钥)则需要自行构造或解析。 + +为此,ESP-IDF 在 ``esp_psa_key_file.h`` 中提供以下辅助函数: + +- :cpp:func:`esp_psa_key_file_pack` —— 将 ``psa_key_attributes_t`` 结构和原始密钥字节封装为密钥 blob。 +- :cpp:func:`esp_psa_key_file_unpack` —— 将密钥 blob 解析回属性以及指向密钥字节段的指针。 +- :cpp:func:`esp_psa_key_file_size` —— 根据给定的密钥字节长度返回 blob 的总大小。 + +这些辅助函数直接实现规范文档中描述的字节布局,不依赖任何 Mbed TLS 内部函数。例如,仅存储内部密钥字节的后端可以在读取时重建 blob: + +.. code-block:: c + + #include "esp_psa_key_file.h" + + psa_key_attributes_t attr = PSA_KEY_ATTRIBUTES_INIT; + psa_set_key_lifetime(&attr, PSA_KEY_LIFETIME_PERSISTENT); + psa_set_key_type(&attr, PSA_KEY_TYPE_AES); + psa_set_key_bits(&attr, key_data_len * 8); + psa_set_key_usage_flags(&attr, PSA_KEY_USAGE_ENCRYPT | PSA_KEY_USAGE_DECRYPT); + psa_set_key_algorithm(&attr, PSA_ALG_CBC_NO_PADDING); + + size_t blob_size = esp_psa_key_file_size(key_data_len); + uint8_t *blob = calloc(1, blob_size); + size_t written = 0; + esp_psa_key_file_pack(&attr, key_data, key_data_len, blob, blob_size, &written); + /* blob 现在包含完整的 PSA 持久密钥文件;按 psa_its_get() 的 offset/length + * 参数将所请求的窗口复制到 p_data。 */ + +完整的可运行示例(使用自定义 NVS 命名空间作为自定义后端)请参考 :example:`security/psa_its_custom_backend`。 + 应用示例 --------