diff --git a/components/esp_tee/test_apps/tee_test_fw/main/CMakeLists.txt b/components/esp_tee/test_apps/tee_test_fw/main/CMakeLists.txt index d212a2fd82c..fcfe416a29c 100644 --- a/components/esp_tee/test_apps/tee_test_fw/main/CMakeLists.txt +++ b/components/esp_tee/test_apps/tee_test_fw/main/CMakeLists.txt @@ -19,7 +19,7 @@ if(CONFIG_SECURE_TEE_ATTESTATION) list(APPEND srcs "test_esp_tee_att.c") endif() -set(mbedtls_test_srcs_dir "${idf_path}/components/mbedtls/test_apps/main") +set(mbedtls_test_srcs_dir "${idf_path}/components/mbedtls/test_apps/mbedtls_ut/main") #AES if(CONFIG_SOC_AES_SUPPORTED) diff --git a/components/mbedtls/mbedtls b/components/mbedtls/mbedtls index 8bfdb425305..6cc42afad30 160000 --- a/components/mbedtls/mbedtls +++ b/components/mbedtls/mbedtls @@ -1 +1 @@ -Subproject commit 8bfdb42530588ab6027ab6a5e7bd34385cb749d2 +Subproject commit 6cc42afad309e861f4c07e6f106e2ab14a9cb8e5 diff --git a/components/mbedtls/port/psa_driver/esp_rsa_ds/psa_crypto_driver_esp_rsa_ds.c b/components/mbedtls/port/psa_driver/esp_rsa_ds/psa_crypto_driver_esp_rsa_ds.c index 476cd5241c6..44d8c8c97dc 100644 --- a/components/mbedtls/port/psa_driver/esp_rsa_ds/psa_crypto_driver_esp_rsa_ds.c +++ b/components/mbedtls/port/psa_driver/esp_rsa_ds/psa_crypto_driver_esp_rsa_ds.c @@ -11,10 +11,12 @@ #include "psa_crypto_driver_esp_rsa_ds.h" #include "psa_crypto_driver_esp_rsa_ds_contexts.h" +#include "psa_crypto_driver_esp_opaque_common.h" #include "include/psa_crypto_driver_esp_rsa_ds_utilities.h" #include "esp_log.h" #include "esp_efuse.h" +#include "esp_assert.h" #include "soc/soc_caps.h" #if SOC_KEY_MANAGER_SUPPORTED @@ -28,6 +30,281 @@ static const char *TAG = "PSA_RSA_DS"; static SemaphoreHandle_t s_ds_lock = NULL; static int s_timeout_ms = 0; +/* + * Per-source storage structs — internal to the driver. + * These are what actually get persisted to NVS, not the user-facing esp_rsa_ds_opaque_key_t. + * + * All storage structs share a common prefix: [version][key_source] + * so that the driver can identify the key source at operation time. + */ +typedef enum { + ESP_RSA_DS_KEY_STORAGE_VERSION_INVALID = 0, + ESP_RSA_DS_KEY_STORAGE_VERSION_V1 = 1, + ESP_RSA_DS_KEY_STORAGE_VERSION_MAX = 2, +} esp_rsa_ds_key_storage_version_t; + +typedef enum { + ESP_RSA_DS_KEY_SOURCE_EFUSE = 0, + ESP_RSA_DS_KEY_SOURCE_KEY_MGR = 1, +} esp_rsa_ds_key_source_t; + +/* Storage structs use uint8_t for key_source instead of enum + * to ensure stable serialized size across compilers. */ + +typedef struct __attribute__((packed)) { + uint8_t version; + uint8_t key_source; /* esp_rsa_ds_key_source_t */ +} esp_rsa_ds_common_key_storage_metadata_t; + +ESP_STATIC_ASSERT(sizeof(esp_rsa_ds_common_key_storage_metadata_t) == 2 * sizeof(uint8_t), + "esp_rsa_ds_common_key_storage_metadata_t must be exactly 2 bytes"); + +/* esp_ds_data_t is serialised verbatim into NVS as part of the persistent storage structs, + * and its first field is an enum (esp_digital_signature_length_t). esp_ds.h documents that + * "in IDF, the enum type length is the same as of type unsigned" — assert it here so a + * future toolchain change that shrinks enums fails the build instead of silently making + * existing stored DS keys unreadable. */ +ESP_STATIC_ASSERT(sizeof(esp_digital_signature_length_t) == sizeof(unsigned), + "esp_digital_signature_length_t must be sized as unsigned for stable NVS layout of esp_ds_data_t"); + +/** + * Pointer-based storage for volatile keys. + * The caller must keep all referenced data valid until psa_destroy_key(). + * This avoids deep-copying esp_ds_data_t (~1600 bytes), preserving the + * heap savings of mmap'd flash data from esp_secure_cert_mgr. + */ +typedef struct { + esp_rsa_ds_common_key_storage_metadata_t metadata; + esp_ds_data_ctx_t *ds_data_ctx; +#if SOC_KEY_MANAGER_SUPPORTED + esp_key_mgr_key_recovery_info_t *key_recovery_info; +#endif /* SOC_KEY_MANAGER_SUPPORTED */ +} esp_rsa_ds_volatile_key_storage_t; + +/** + * Inline storage for persistent eFuse DS keys. + * All data is deep-copied — no external references. + */ +typedef struct { + esp_rsa_ds_common_key_storage_metadata_t metadata; + uint8_t efuse_key_id; + uint8_t reserved; /* explicit padding */ + uint16_t rsa_length_bits; + uint8_t reserved2[2]; /* explicit padding for ds_data 4-byte alignment */ + esp_ds_data_t ds_data; +} esp_rsa_ds_efuse_key_storage_t; + +ESP_STATIC_ASSERT(offsetof(esp_rsa_ds_efuse_key_storage_t, ds_data) % 4 == 0, + "ds_data must be 4-byte aligned in esp_rsa_ds_efuse_key_storage_t"); + +#if SOC_KEY_MANAGER_SUPPORTED +/** + * Inline storage for persistent Key Manager DS keys. + * All data is deep-copied — no external references. + */ +typedef struct { + esp_rsa_ds_common_key_storage_metadata_t metadata; + uint16_t rsa_length_bits; + esp_ds_data_t ds_data; + esp_key_mgr_key_recovery_info_t key_recovery_info; +} esp_rsa_ds_km_key_storage_t; + +ESP_STATIC_ASSERT(offsetof(esp_rsa_ds_km_key_storage_t, ds_data) % 4 == 0, + "ds_data must be 4-byte aligned in esp_rsa_ds_km_key_storage_t"); +ESP_STATIC_ASSERT(offsetof(esp_rsa_ds_km_key_storage_t, key_recovery_info) % 4 == 0, + "key_recovery_info must be 4-byte aligned in esp_rsa_ds_km_key_storage_t"); +#endif /* SOC_KEY_MANAGER_SUPPORTED */ + +/** + * @brief Read the key_source tag from any storage struct. + * + * All storage structs share the layout: [version(1)][key_source(1)][...] + */ +static inline esp_rsa_ds_key_source_t rsa_ds_storage_get_key_source(const uint8_t *key_buffer) +{ + return (esp_rsa_ds_key_source_t)key_buffer[1]; +} + +/** + * @brief Calculate the storage buffer size for an import operation. + * + * For volatile keys: returns pointer-based storage size (small). + * For persistent keys: inspects the user struct to determine key source + * and returns the corresponding inline storage struct size (large). + */ +static size_t esp_rsa_ds_get_storage_size(const esp_rsa_ds_opaque_key_t *key, bool persistent) +{ + if (!persistent) { + return sizeof(esp_rsa_ds_volatile_key_storage_t); + } + +#if SOC_KEY_MANAGER_SUPPORTED + if (key->key_recovery_info) { + return sizeof(esp_rsa_ds_km_key_storage_t); + } +#endif /* SOC_KEY_MANAGER_SUPPORTED */ + + (void)key; + return sizeof(esp_rsa_ds_efuse_key_storage_t); +} + +/** + * @brief Calculate the expected storage size from an already-serialized storage buffer. + * + * @param key_buffer The storage buffer. + * @param key_buffer_size Size of @p key_buffer in bytes. + * @param persistent true if the key is persistent (inline data), false if volatile. + * @param[out] expected_storage_size The expected minimum buffer size. + */ +static psa_status_t esp_rsa_ds_get_expected_storage_size(const uint8_t *key_buffer, + size_t key_buffer_size, + bool persistent, + size_t *expected_storage_size) +{ + if (key_buffer_size < sizeof(esp_rsa_ds_common_key_storage_metadata_t)) { + return PSA_ERROR_INVALID_ARGUMENT; + } + + if (key_buffer[0] == ESP_RSA_DS_KEY_STORAGE_VERSION_INVALID || key_buffer[0] >= ESP_RSA_DS_KEY_STORAGE_VERSION_MAX) { + return PSA_ERROR_DATA_INVALID; + } + + *expected_storage_size = 0; + + if (!persistent) { + *expected_storage_size = sizeof(esp_rsa_ds_volatile_key_storage_t); + return PSA_SUCCESS; + } + + esp_rsa_ds_key_source_t key_source = rsa_ds_storage_get_key_source(key_buffer); + + switch (key_source) { +#if SOC_KEY_MANAGER_SUPPORTED + case ESP_RSA_DS_KEY_SOURCE_KEY_MGR: + *expected_storage_size = sizeof(esp_rsa_ds_km_key_storage_t); + return PSA_SUCCESS; +#endif /* SOC_KEY_MANAGER_SUPPORTED */ + case ESP_RSA_DS_KEY_SOURCE_EFUSE: + *expected_storage_size = sizeof(esp_rsa_ds_efuse_key_storage_t); + return PSA_SUCCESS; + default: + return PSA_ERROR_DATA_INVALID; + } +} + +/** + * @brief Validate a storage buffer and extract the per-source DS parameters. + * + * Centralises the metadata + size validation and the per-source pointer/value extraction + * shared by the sign-hash and asymmetric-decrypt entry points. On success, all out-params + * are filled. On failure, out-params are left untouched. + * + * @param key_buffer The PSA storage buffer. + * @param key_buffer_size Size of @p key_buffer in bytes. + * @param is_persistent Persistence flag derived from the PSA key attributes. + * @param[out] key_source Storage key source tag (eFuse / Key Manager). + * @param[out] rsa_length_bits RSA key length in bits, taken from the storage struct. + * @param[out] ds_data Pointer to the in-storage @c esp_ds_data_t (volatile: caller's + * mmap'd flash buffer; persistent: the inline copy). + * @param[out] hmac_key_id HMAC key id for the DS peripheral. + * @param[out] km_ri Pointer to the @c esp_key_mgr_key_recovery_info_t to pass to + * @c esp_key_mgr_activate_key, or NULL when the key source is + * not Key Manager. Only present on KM-capable SoCs. + * + * @return PSA_SUCCESS, or a PSA error if validation fails. + */ +static psa_status_t esp_rsa_ds_extract_storage( + const uint8_t *key_buffer, + size_t key_buffer_size, + bool is_persistent, + esp_rsa_ds_key_source_t *key_source, + uint16_t *rsa_length_bits, + const esp_ds_data_t **ds_data, + hmac_key_id_t *hmac_key_id +#if SOC_KEY_MANAGER_SUPPORTED + , esp_key_mgr_key_recovery_info_t **km_ri +#endif /* SOC_KEY_MANAGER_SUPPORTED */ +) +{ + size_t expected_storage_size = 0; + psa_status_t status = esp_rsa_ds_get_expected_storage_size(key_buffer, key_buffer_size, + is_persistent, &expected_storage_size); + if (status != PSA_SUCCESS) { + return PSA_ERROR_INVALID_ARGUMENT; + } + + if (key_buffer_size < expected_storage_size) { + return PSA_ERROR_INVALID_ARGUMENT; + } + + esp_rsa_ds_key_source_t src = rsa_ds_storage_get_key_source(key_buffer); + uint16_t bits = 0; + const esp_ds_data_t *data = NULL; + hmac_key_id_t hmac_id = 0; +#if SOC_KEY_MANAGER_SUPPORTED + esp_key_mgr_key_recovery_info_t *ri = NULL; +#endif /* SOC_KEY_MANAGER_SUPPORTED */ + + if (!is_persistent) { + const esp_rsa_ds_volatile_key_storage_t *ptr_st = + (const esp_rsa_ds_volatile_key_storage_t *)key_buffer; + bits = ptr_st->ds_data_ctx->rsa_length_bits; + data = ptr_st->ds_data_ctx->esp_ds_data; + hmac_id = ptr_st->ds_data_ctx->efuse_key_id; +#if SOC_KEY_MANAGER_SUPPORTED + if (src == ESP_RSA_DS_KEY_SOURCE_KEY_MGR) { + hmac_id = HMAC_KEY_KM; + ri = ptr_st->key_recovery_info; + } +#endif /* SOC_KEY_MANAGER_SUPPORTED */ + } else { + switch (src) { + case ESP_RSA_DS_KEY_SOURCE_EFUSE: { + const esp_rsa_ds_efuse_key_storage_t *efuse_st = + (const esp_rsa_ds_efuse_key_storage_t *)key_buffer; + bits = efuse_st->rsa_length_bits; + data = &efuse_st->ds_data; + hmac_id = efuse_st->efuse_key_id; + break; + } +#if SOC_KEY_MANAGER_SUPPORTED + case ESP_RSA_DS_KEY_SOURCE_KEY_MGR: { + const esp_rsa_ds_km_key_storage_t *km_st = + (const esp_rsa_ds_km_key_storage_t *)key_buffer; + bits = km_st->rsa_length_bits; + data = &km_st->ds_data; + hmac_id = HMAC_KEY_KM; + /* esp_key_mgr_activate_key() takes a non-const pointer for API compatibility + * but does not modify the recovery info. The cast away const is therefore safe; + * if that contract ever changes, copy the recovery info to a local first. */ + ri = (esp_key_mgr_key_recovery_info_t *)&km_st->key_recovery_info; + break; + } +#endif /* SOC_KEY_MANAGER_SUPPORTED */ + default: + return PSA_ERROR_INVALID_ARGUMENT; + } + } + + if (bits % 32 != 0 || bits < 1024 || bits > SOC_DS_SIGNATURE_MAX_BIT_LEN) { + return PSA_ERROR_INVALID_ARGUMENT; + } + + if (data->rsa_length != (bits / 32) - 1) { + return PSA_ERROR_DATA_INVALID; + } + + *key_source = src; + *rsa_length_bits = bits; + *ds_data = data; + *hmac_key_id = hmac_id; +#if SOC_KEY_MANAGER_SUPPORTED + *km_ri = ri; +#endif /* SOC_KEY_MANAGER_SUPPORTED */ + + return PSA_SUCCESS; +} + void esp_rsa_ds_release_ds_lock(void); static int esp_rsa_ds_pad(esp_rsa_ds_padding_t padding, psa_algorithm_t hash_alg, unsigned int hashlen, @@ -129,37 +406,48 @@ psa_status_t esp_rsa_ds_opaque_sign_hash_start( return PSA_ERROR_INVALID_ARGUMENT; } - if (key_buffer_size < sizeof(esp_rsa_ds_opaque_key_t)) { - return PSA_ERROR_INVALID_ARGUMENT; - } + bool is_persistent = esp_opaque_key_is_persistent(attributes); if (!PSA_ALG_IS_RSA_PKCS1V15_SIGN(alg) && !PSA_ALG_IS_RSA_PSS(alg)) { return PSA_ERROR_NOT_SUPPORTED; } - operation->alg = alg; + esp_rsa_ds_key_source_t key_source = ESP_RSA_DS_KEY_SOURCE_EFUSE; + uint16_t rsa_length_bits = 0; + const esp_ds_data_t *ds_data = NULL; + hmac_key_id_t hmac_key_id = 0; +#if SOC_KEY_MANAGER_SUPPORTED + esp_key_mgr_key_recovery_info_t *km_ri = NULL; +#endif /* SOC_KEY_MANAGER_SUPPORTED */ - const esp_rsa_ds_opaque_key_t *opaque_key = (const esp_rsa_ds_opaque_key_t *)key_buffer; - operation->esp_rsa_ds_opaque_key = opaque_key; - - if (esp_rsa_ds_validate_opaque_key(opaque_key) != PSA_SUCCESS) { - return PSA_ERROR_INVALID_ARGUMENT; + psa_status_t status = esp_rsa_ds_extract_storage( + key_buffer, key_buffer_size, is_persistent, + &key_source, &rsa_length_bits, &ds_data, &hmac_key_id +#if SOC_KEY_MANAGER_SUPPORTED + , &km_ri +#endif /* SOC_KEY_MANAGER_SUPPORTED */ + ); + if (status != PSA_SUCCESS) { + return status; } + operation->alg = alg; + operation->key_buffer = key_buffer; + if ((xSemaphoreTake(s_ds_lock, s_timeout_ms / portTICK_PERIOD_MS) != pdTRUE)) { return PSA_ERROR_GENERIC_ERROR; } esp_rsa_ds_padding_t padding = ESP_RSA_DS_PADDING_INVALID; - if (PSA_ALG_IS_RSA_PSS(operation->alg)) { + if (PSA_ALG_IS_RSA_PSS(alg)) { padding = ESP_RSA_DS_PADDING_PSS; - } else if (PSA_ALG_IS_RSA_PKCS1V15_SIGN(operation->alg)) { + } else if (PSA_ALG_IS_RSA_PKCS1V15_SIGN(alg)) { padding = ESP_RSA_DS_PADDING_PKCS_V15; } - psa_algorithm_t hash_alg = PSA_ALG_SIGN_GET_HASH(operation->alg); + psa_algorithm_t hash_alg = PSA_ALG_SIGN_GET_HASH(alg); - const size_t words_len = (opaque_key->ds_data_ctx->rsa_length_bits / 32); + const size_t words_len = rsa_length_bits / 32; const size_t rsa_len_bytes = words_len * 4; operation->sig_buffer_size = rsa_len_bytes; operation->sig_buffer = NULL; @@ -170,7 +458,7 @@ psa_status_t esp_rsa_ds_opaque_sign_hash_start( return PSA_ERROR_INSUFFICIENT_MEMORY; } - psa_status_t status = esp_rsa_ds_pad( + status = esp_rsa_ds_pad( padding, hash_alg, hash_length, hash, -1, em, rsa_len_bytes); if (status != PSA_SUCCESS) { goto error; @@ -188,24 +476,21 @@ psa_status_t esp_rsa_ds_opaque_sign_hash_start( sig_words[i] = SWAP_INT32(em_words[words_len - (i + 1)]); } - hmac_key_id_t hmac_key_id = opaque_key->ds_data_ctx->efuse_key_id; - #if SOC_KEY_MANAGER_SUPPORTED - esp_key_mgr_key_recovery_info_t *km_key_recovery_info = operation->esp_rsa_ds_opaque_key->key_recovery_info; - if (km_key_recovery_info) { - err = esp_key_mgr_activate_key(km_key_recovery_info); + if (key_source == ESP_RSA_DS_KEY_SOURCE_KEY_MGR) { + err = esp_key_mgr_activate_key(km_ri); if (err != ESP_OK) { ESP_LOGE(TAG, "Failed to activate key: 0x%x", err); status = PSA_ERROR_INVALID_HANDLE; goto error; } - hmac_key_id = HMAC_KEY_KM; operation->is_km_key_active = true; + operation->km_ri = km_ri; } #endif /* SOC_KEY_MANAGER_SUPPORTED */ err = esp_ds_start_sign((const void *)operation->sig_buffer, - opaque_key->ds_data_ctx->esp_ds_data, + ds_data, hmac_key_id, &operation->esp_rsa_ds_ctx); if (err != ESP_OK) { @@ -239,7 +524,7 @@ psa_status_t esp_rsa_ds_opaque_sign_hash_complete( return PSA_ERROR_BAD_STATE; } - int expected_signature_size = operation->esp_rsa_ds_opaque_key->ds_data_ctx->rsa_length_bits / 8; + size_t expected_signature_size = operation->sig_buffer_size; if (signature_size < expected_signature_size) { return PSA_ERROR_BUFFER_TOO_SMALL; } @@ -277,15 +562,15 @@ psa_status_t esp_rsa_ds_opaque_sign_hash_abort( return PSA_ERROR_INVALID_ARGUMENT; } - if (operation->esp_rsa_ds_opaque_key) { #if SOC_KEY_MANAGER_SUPPORTED - esp_key_mgr_key_recovery_info_t *km_key_recovery_info = operation->esp_rsa_ds_opaque_key->key_recovery_info; - if (km_key_recovery_info && operation->is_km_key_active) { - esp_key_mgr_deactivate_key(km_key_recovery_info->key_type); - operation->is_km_key_active = false; - } + if (operation->is_km_key_active) { + esp_key_mgr_deactivate_key(operation->km_ri->key_type); + operation->is_km_key_active = false; + } #endif /* SOC_KEY_MANAGER_SUPPORTED */ - operation->esp_rsa_ds_opaque_key = NULL; + + if (operation->key_buffer) { + operation->key_buffer = NULL; } if (operation->esp_rsa_ds_ctx) { @@ -362,33 +647,100 @@ psa_status_t esp_rsa_ds_opaque_import_key( return PSA_ERROR_INVALID_ARGUMENT; } - if (key_buffer_size < sizeof(esp_rsa_ds_opaque_key_t)) { - return PSA_ERROR_BUFFER_TOO_SMALL; - } - const esp_rsa_ds_opaque_key_t *opaque_key = (const esp_rsa_ds_opaque_key_t *)data; int ret = esp_rsa_ds_validate_opaque_key(opaque_key); if (ret != PSA_SUCCESS) { return ret; } - /* Shallow copy: key buffer holds the context; esp_ds_data points to the caller's data. - * The key material (esp_rsa_ds_opaque_key_t and the esp_ds_data_t it points to) must remain - * valid until psa_destroy_key() is called on this key. */ - memcpy(key_buffer, opaque_key, sizeof(esp_rsa_ds_opaque_key_t)); - *key_buffer_length = sizeof(esp_rsa_ds_opaque_key_t); + bool is_persistent = esp_opaque_key_is_persistent(attributes); + + esp_rsa_ds_key_source_t key_source = ESP_RSA_DS_KEY_SOURCE_EFUSE; +#if SOC_KEY_MANAGER_SUPPORTED + if (opaque_key->key_recovery_info) { + key_source = ESP_RSA_DS_KEY_SOURCE_KEY_MGR; + } +#endif /* SOC_KEY_MANAGER_SUPPORTED */ + + if (!is_persistent) { + /* Volatile: store pointers only — caller keeps data alive. + * This preserves the heap savings of mmap'd flash data from esp_secure_cert_mgr. */ + if (key_buffer_size < sizeof(esp_rsa_ds_volatile_key_storage_t)) { + return PSA_ERROR_BUFFER_TOO_SMALL; + } + + esp_rsa_ds_volatile_key_storage_t *storage = (esp_rsa_ds_volatile_key_storage_t *)key_buffer; + storage->metadata.version = ESP_RSA_DS_KEY_STORAGE_VERSION_V1; + storage->metadata.key_source = key_source; + storage->ds_data_ctx = opaque_key->ds_data_ctx; +#if SOC_KEY_MANAGER_SUPPORTED + storage->key_recovery_info = opaque_key->key_recovery_info; +#endif /* SOC_KEY_MANAGER_SUPPORTED */ + *key_buffer_length = sizeof(esp_rsa_ds_volatile_key_storage_t); + } else { + /* Persistent: deep-copy all data into self-contained storage struct */ +#if SOC_KEY_MANAGER_SUPPORTED + if (key_source == ESP_RSA_DS_KEY_SOURCE_KEY_MGR) { + if (key_buffer_size < sizeof(esp_rsa_ds_km_key_storage_t)) { + return PSA_ERROR_BUFFER_TOO_SMALL; + } + + esp_rsa_ds_km_key_storage_t *storage = (esp_rsa_ds_km_key_storage_t *)key_buffer; + storage->metadata.version = ESP_RSA_DS_KEY_STORAGE_VERSION_V1; + storage->metadata.key_source = ESP_RSA_DS_KEY_SOURCE_KEY_MGR; + storage->rsa_length_bits = opaque_key->ds_data_ctx->rsa_length_bits; + memcpy(&storage->ds_data, opaque_key->ds_data_ctx->esp_ds_data, sizeof(esp_ds_data_t)); + memcpy(&storage->key_recovery_info, opaque_key->key_recovery_info, + sizeof(esp_key_mgr_key_recovery_info_t)); + *key_buffer_length = sizeof(esp_rsa_ds_km_key_storage_t); + } else +#endif /* SOC_KEY_MANAGER_SUPPORTED */ + { + if (key_buffer_size < sizeof(esp_rsa_ds_efuse_key_storage_t)) { + return PSA_ERROR_BUFFER_TOO_SMALL; + } + + esp_rsa_ds_efuse_key_storage_t *storage = (esp_rsa_ds_efuse_key_storage_t *)key_buffer; + storage->metadata.version = ESP_RSA_DS_KEY_STORAGE_VERSION_V1; + storage->metadata.key_source = ESP_RSA_DS_KEY_SOURCE_EFUSE; + storage->efuse_key_id = opaque_key->ds_data_ctx->efuse_key_id; + storage->reserved = 0; + storage->rsa_length_bits = opaque_key->ds_data_ctx->rsa_length_bits; + memset(storage->reserved2, 0, sizeof(storage->reserved2)); + memcpy(&storage->ds_data, opaque_key->ds_data_ctx->esp_ds_data, sizeof(esp_ds_data_t)); + *key_buffer_length = sizeof(esp_rsa_ds_efuse_key_storage_t); + } + } + *bits = opaque_key->ds_data_ctx->rsa_length_bits; return PSA_SUCCESS; } size_t esp_rsa_ds_opaque_size_function( + const psa_key_attributes_t *attributes, psa_key_type_t key_type, - size_t key_bits) + const uint8_t *data, + size_t data_length) { (void)key_type; - (void)key_bits; - return sizeof(esp_rsa_ds_opaque_key_t); + bool is_persistent = esp_opaque_key_is_persistent(attributes); + + if (!data || data_length < sizeof(esp_rsa_ds_opaque_key_t)) { + /* Data too short to inspect the user struct — return the largest possible + * size for the persistence flavor so import has enough room to write whichever + * variant ends up being needed. import_key() does the real validation. */ + if (!is_persistent) { + return sizeof(esp_rsa_ds_volatile_key_storage_t); + } +#if SOC_KEY_MANAGER_SUPPORTED + return sizeof(esp_rsa_ds_km_key_storage_t); +#else + return sizeof(esp_rsa_ds_efuse_key_storage_t); +#endif /* SOC_KEY_MANAGER_SUPPORTED */ + } + + return esp_rsa_ds_get_storage_size((const esp_rsa_ds_opaque_key_t *)data, is_persistent); } void esp_rsa_ds_opaque_set_session_timeout(int timeout_ms) @@ -416,8 +768,7 @@ psa_status_t esp_rsa_ds_opaque_asymmetric_decrypt( esp_err_t err = ESP_FAIL; - if (!attributes || !key || key_length < sizeof(esp_rsa_ds_opaque_key_t) || - !input || input_length < 1 || !output || !output_length) { + if (!attributes || !key || !input || input_length < 1 || !output || !output_length) { return PSA_ERROR_INVALID_ARGUMENT; } @@ -425,14 +776,28 @@ psa_status_t esp_rsa_ds_opaque_asymmetric_decrypt( return PSA_ERROR_NOT_SUPPORTED; } - const esp_rsa_ds_opaque_key_t *opaque_key = (const esp_rsa_ds_opaque_key_t *)key; + bool is_persistent = esp_opaque_key_is_persistent(attributes); - if (esp_rsa_ds_validate_opaque_key(opaque_key) != PSA_SUCCESS) { - return PSA_ERROR_INVALID_ARGUMENT; + esp_rsa_ds_key_source_t key_source = ESP_RSA_DS_KEY_SOURCE_EFUSE; + uint16_t rsa_length_bits = 0; + const esp_ds_data_t *ds_data = NULL; + hmac_key_id_t hmac_key_id = 0; +#if SOC_KEY_MANAGER_SUPPORTED + esp_key_mgr_key_recovery_info_t *km_ri = NULL; +#endif /* SOC_KEY_MANAGER_SUPPORTED */ + + psa_status_t status = esp_rsa_ds_extract_storage( + key, key_length, is_persistent, + &key_source, &rsa_length_bits, &ds_data, &hmac_key_id +#if SOC_KEY_MANAGER_SUPPORTED + , &km_ri +#endif /* SOC_KEY_MANAGER_SUPPORTED */ + ); + if (status != PSA_SUCCESS) { + return status; } - size_t key_bits = opaque_key->ds_data_ctx->rsa_length_bits; - if (input_length != (key_bits / 8)) { + if (input_length != (rsa_length_bits / 8)) { return PSA_ERROR_INVALID_ARGUMENT; } @@ -447,7 +812,7 @@ psa_status_t esp_rsa_ds_opaque_asymmetric_decrypt( return PSA_ERROR_GENERIC_ERROR; } - size_t ilen = key_bits / 8; + size_t ilen = rsa_length_bits / 8; size_t data_len = ilen / 4; uint32_t *em_words = heap_caps_malloc_prefer(sizeof(uint32_t) * data_len, 1, MALLOC_CAP_32BIT | MALLOC_CAP_INTERNAL, MALLOC_CAP_DEFAULT | MALLOC_CAP_INTERNAL); if (em_words == NULL) { @@ -459,49 +824,41 @@ psa_status_t esp_rsa_ds_opaque_asymmetric_decrypt( em_words[i] = SWAP_INT32(((uint32_t *)input)[(data_len) - (i + 1)]); } - esp_rsa_ds_opaque_sign_hash_operation_t operation = {0}; - operation.alg = alg; - operation.esp_rsa_ds_opaque_key = opaque_key; - operation.sig_buffer = em_words; - - hmac_key_id_t hmac_key_id = opaque_key->ds_data_ctx->efuse_key_id; #if SOC_KEY_MANAGER_SUPPORTED - esp_key_mgr_key_recovery_info_t *km_key_recovery_info = opaque_key->key_recovery_info; - if (km_key_recovery_info) { - err = esp_key_mgr_activate_key(km_key_recovery_info); + bool is_km_key_active = false; + if (key_source == ESP_RSA_DS_KEY_SOURCE_KEY_MGR) { + err = esp_key_mgr_activate_key(km_ri); if (err != ESP_OK) { ESP_LOGE(TAG, "Failed to activate key: 0x%x", err); heap_caps_free(em_words); esp_rsa_ds_release_ds_lock(); return PSA_ERROR_INVALID_HANDLE; } - hmac_key_id = HMAC_KEY_KM; - operation.is_km_key_active = true; + is_km_key_active = true; } #endif /* SOC_KEY_MANAGER_SUPPORTED */ + esp_ds_context_t *ds_ctx = NULL; err = esp_ds_start_sign((const void *)em_words, - opaque_key->ds_data_ctx->esp_ds_data, + ds_data, hmac_key_id, - &operation.esp_rsa_ds_ctx); + &ds_ctx); if (err != ESP_OK) { heap_caps_free(em_words); #if SOC_KEY_MANAGER_SUPPORTED - if (km_key_recovery_info && operation.is_km_key_active) { - esp_key_mgr_deactivate_key(km_key_recovery_info->key_type); - operation.is_km_key_active = false; - } + if (is_km_key_active) { + esp_key_mgr_deactivate_key(km_ri->key_type); + } #endif /* SOC_KEY_MANAGER_SUPPORTED */ esp_rsa_ds_release_ds_lock(); return PSA_ERROR_GENERIC_ERROR; } - err = esp_ds_finish_sign((void *)em_words, operation.esp_rsa_ds_ctx); + err = esp_ds_finish_sign((void *)em_words, ds_ctx); #if SOC_KEY_MANAGER_SUPPORTED - if (km_key_recovery_info && operation.is_km_key_active) { - esp_key_mgr_deactivate_key(km_key_recovery_info->key_type); - operation.is_km_key_active = false; + if (is_km_key_active) { + esp_key_mgr_deactivate_key(km_ri->key_type); } #endif /* SOC_KEY_MANAGER_SUPPORTED */ diff --git a/components/mbedtls/port/psa_driver/include/psa_crypto_driver_esp_rsa_ds.h b/components/mbedtls/port/psa_driver/include/psa_crypto_driver_esp_rsa_ds.h index c5a88205b43..d762a20f2cc 100644 --- a/components/mbedtls/port/psa_driver/include/psa_crypto_driver_esp_rsa_ds.h +++ b/components/mbedtls/port/psa_driver/include/psa_crypto_driver_esp_rsa_ds.h @@ -22,14 +22,13 @@ extern "C" { #define PSA_KEY_LOCATION_ESP_RSA_DS ((psa_key_location_t) 0x800003) -/* IDF-15427: ESP-PSA driver does not support persistent RSA DS keys as of now */ -#if 0 -/* @brief Construct a lifetime for ESP RSA DS keys with default persistence */ +/** + * @brief Construct a persistent lifetime for ESP RSA DS keys + */ #define PSA_KEY_LIFETIME_ESP_RSA_DS \ PSA_KEY_LIFETIME_FROM_PERSISTENCE_AND_LOCATION( \ PSA_KEY_PERSISTENCE_DEFAULT, \ PSA_KEY_LOCATION_ESP_RSA_DS) -#endif /** * @brief Construct a volatile lifetime for ESP RSA DS keys @@ -145,15 +144,23 @@ psa_status_t esp_rsa_ds_opaque_import_key( size_t *bits); /** - * @brief Return the size of the RSA DS opaque key in bytes + * @brief Return the storage buffer size required for an RSA DS opaque key * - * @param key_type Key type - * @param key_bits Key bits - * @return Size of the RSA DS opaque key in bytes + * For volatile keys, returns a small pointer-based storage size. + * For persistent keys, inspects the user-facing import data to determine + * the key source and returns the corresponding inline storage struct size. + * + * @param attributes Key attributes (used to check persistence) + * @param key_type Key type + * @param data Import data (user-facing esp_rsa_ds_opaque_key_t) + * @param data_length Length of import data + * @return Size of the storage buffer in bytes, or 0 on error */ size_t esp_rsa_ds_opaque_size_function( + const psa_key_attributes_t *attributes, psa_key_type_t key_type, - size_t key_bits); + const uint8_t *data, + size_t data_length); /** * @brief Set the timeout for the RSA DS session diff --git a/components/mbedtls/port/psa_driver/include/psa_crypto_driver_esp_rsa_ds_contexts.h b/components/mbedtls/port/psa_driver/include/psa_crypto_driver_esp_rsa_ds_contexts.h index 713b96fcf65..c907e711f4d 100644 --- a/components/mbedtls/port/psa_driver/include/psa_crypto_driver_esp_rsa_ds_contexts.h +++ b/components/mbedtls/port/psa_driver/include/psa_crypto_driver_esp_rsa_ds_contexts.h @@ -35,9 +35,14 @@ typedef enum { * @brief ESP DS data context * This context is used to store the ESP DS data. * - * When passed to psa_import_key() for PSA_KEY_LIFETIME_ESP_RSA_DS_VOLATILE, the key material - * (this struct and the esp_ds_data_t pointed to by esp_ds_data) must remain valid - * until psa_destroy_key() is called on the imported key. + * For persistent keys (PSA_KEY_LIFETIME_ESP_RSA_DS), the driver deep-copies + * all referenced data at import time. The caller's data does not need to + * remain valid after psa_import_key() returns. + * + * For volatile keys (PSA_KEY_LIFETIME_ESP_RSA_DS_VOLATILE), the driver stores + * pointers to the caller's data. This struct and the esp_ds_data_t pointed to + * by esp_ds_data must remain valid until psa_destroy_key() is called. + * This preserves the heap savings of mmap'd flash data from esp_secure_cert_mgr. */ typedef struct { esp_ds_data_t *esp_ds_data; /**< Pointer to the esp ds data */ @@ -55,9 +60,10 @@ typedef struct { #if !(__DOXYGEN__) // No need to document these structures, these are internal to the driver /* The buffers are stored in the little-endian format */ typedef struct { - const esp_rsa_ds_opaque_key_t *esp_rsa_ds_opaque_key; /**< Pointer to the esp ds opaque key */ + const uint8_t *key_buffer; /**< Pointer to per-source storage struct in key slot */ #if SOC_KEY_MANAGER_SUPPORTED bool is_km_key_active; /**< Flag indicating if the Key Manager key is active for this operation */ + esp_key_mgr_key_recovery_info_t *km_ri; /**< Pointer to the key recovery info for DS key */ #endif /* SOC_KEY_MANAGER_SUPPORTED */ psa_algorithm_t alg; /**< Algorithm used in the sign operation */ uint32_t *sig_buffer; /**< Buffer to hold the signature */ diff --git a/components/mbedtls/test_apps/.build-test-rules.yml b/components/mbedtls/test_apps/.build-test-rules.yml index b3036495194..053441ec4a3 100644 --- a/components/mbedtls/test_apps/.build-test-rules.yml +++ b/components/mbedtls/test_apps/.build-test-rules.yml @@ -1,6 +1,6 @@ # Documentation: .gitlab/ci/README.md#manifest-file-to-control-the-buildtest-apps -components/mbedtls/test_apps: +components/mbedtls/test_apps/mbedtls_ut: disable: - if: CONFIG_NAME == "aes_no_hw" and SOC_AES_SUPPORTED != 1 - if: CONFIG_NAME == "psram" and SOC_SPIRAM_SUPPORTED != 1 @@ -22,3 +22,18 @@ components/mbedtls/test_apps: - esp_driver_dma - esp_hal_dma - esp_mm + - esp_hw_support + +components/mbedtls/test_apps/persistent_storage_format: + disable: + - if: CONFIG_NAME not in ["hmac", "ecdsa"] + reason: this app has no default config; only the hmac and ecdsa overlays are exercised + - if: CONFIG_NAME == "hmac" and IDF_TARGET != "esp32c3" + reason: nvs_encr_hmac runner (HMAC + RSA-DS persistent format consume) is esp32c3 only + - if: CONFIG_NAME == "ecdsa" and IDF_TARGET != "esp32h2" + reason: ECDSA persistent format consume runner is esp32h2 only + depends_components: + - mbedtls + - esp_security + - esp_hal_security + - nvs_flash diff --git a/components/mbedtls/test_apps/CMakeLists.txt b/components/mbedtls/test_apps/mbedtls_ut/CMakeLists.txt similarity index 100% rename from components/mbedtls/test_apps/CMakeLists.txt rename to components/mbedtls/test_apps/mbedtls_ut/CMakeLists.txt diff --git a/components/mbedtls/test_apps/README.md b/components/mbedtls/test_apps/mbedtls_ut/README.md similarity index 100% rename from components/mbedtls/test_apps/README.md rename to components/mbedtls/test_apps/mbedtls_ut/README.md diff --git a/components/mbedtls/test_apps/ecdsa_key_p192.pem b/components/mbedtls/test_apps/mbedtls_ut/ecdsa_key_p192.pem similarity index 100% rename from components/mbedtls/test_apps/ecdsa_key_p192.pem rename to components/mbedtls/test_apps/mbedtls_ut/ecdsa_key_p192.pem diff --git a/components/mbedtls/test_apps/ecdsa_key_p256.pem b/components/mbedtls/test_apps/mbedtls_ut/ecdsa_key_p256.pem similarity index 100% rename from components/mbedtls/test_apps/ecdsa_key_p256.pem rename to components/mbedtls/test_apps/mbedtls_ut/ecdsa_key_p256.pem diff --git a/components/mbedtls/test_apps/ecdsa_key_p384.pem b/components/mbedtls/test_apps/mbedtls_ut/ecdsa_key_p384.pem similarity index 100% rename from components/mbedtls/test_apps/ecdsa_key_p384.pem rename to components/mbedtls/test_apps/mbedtls_ut/ecdsa_key_p384.pem diff --git a/components/mbedtls/test_apps/main/CMakeLists.txt b/components/mbedtls/test_apps/mbedtls_ut/main/CMakeLists.txt similarity index 100% rename from components/mbedtls/test_apps/main/CMakeLists.txt rename to components/mbedtls/test_apps/mbedtls_ut/main/CMakeLists.txt diff --git a/components/mbedtls/test_apps/main/Kconfig.projbuild b/components/mbedtls/test_apps/mbedtls_ut/main/Kconfig.projbuild similarity index 100% rename from components/mbedtls/test_apps/main/Kconfig.projbuild rename to components/mbedtls/test_apps/mbedtls_ut/main/Kconfig.projbuild diff --git a/components/mbedtls/test_apps/main/app_main.c b/components/mbedtls/test_apps/mbedtls_ut/main/app_main.c similarity index 100% rename from components/mbedtls/test_apps/main/app_main.c rename to components/mbedtls/test_apps/mbedtls_ut/main/app_main.c diff --git a/components/mbedtls/test_apps/main/crts/bad_md_crt.pem b/components/mbedtls/test_apps/mbedtls_ut/main/crts/bad_md_crt.pem similarity index 100% rename from components/mbedtls/test_apps/main/crts/bad_md_crt.pem rename to components/mbedtls/test_apps/mbedtls_ut/main/crts/bad_md_crt.pem diff --git a/components/mbedtls/test_apps/main/crts/correct_sig_crt_esp32_com.pem b/components/mbedtls/test_apps/mbedtls_ut/main/crts/correct_sig_crt_esp32_com.pem similarity index 100% rename from components/mbedtls/test_apps/main/crts/correct_sig_crt_esp32_com.pem rename to components/mbedtls/test_apps/mbedtls_ut/main/crts/correct_sig_crt_esp32_com.pem diff --git a/components/mbedtls/test_apps/main/crts/ecdsa_cert_bundle b/components/mbedtls/test_apps/mbedtls_ut/main/crts/ecdsa_cert_bundle similarity index 100% rename from components/mbedtls/test_apps/main/crts/ecdsa_cert_bundle rename to components/mbedtls/test_apps/mbedtls_ut/main/crts/ecdsa_cert_bundle diff --git a/components/mbedtls/test_apps/main/crts/ecdsa_correct_sig_crt.pem b/components/mbedtls/test_apps/mbedtls_ut/main/crts/ecdsa_correct_sig_crt.pem similarity index 100% rename from components/mbedtls/test_apps/main/crts/ecdsa_correct_sig_crt.pem rename to components/mbedtls/test_apps/mbedtls_ut/main/crts/ecdsa_correct_sig_crt.pem diff --git a/components/mbedtls/test_apps/main/crts/ecdsa_wrong_sig_crt.pem b/components/mbedtls/test_apps/mbedtls_ut/main/crts/ecdsa_wrong_sig_crt.pem similarity index 100% rename from components/mbedtls/test_apps/main/crts/ecdsa_wrong_sig_crt.pem rename to components/mbedtls/test_apps/mbedtls_ut/main/crts/ecdsa_wrong_sig_crt.pem diff --git a/components/mbedtls/test_apps/main/crts/prvtkey.pem b/components/mbedtls/test_apps/mbedtls_ut/main/crts/prvtkey.pem similarity index 100% rename from components/mbedtls/test_apps/main/crts/prvtkey.pem rename to components/mbedtls/test_apps/mbedtls_ut/main/crts/prvtkey.pem diff --git a/components/mbedtls/test_apps/main/crts/server_cert_bundle b/components/mbedtls/test_apps/mbedtls_ut/main/crts/server_cert_bundle similarity index 100% rename from components/mbedtls/test_apps/main/crts/server_cert_bundle rename to components/mbedtls/test_apps/mbedtls_ut/main/crts/server_cert_bundle diff --git a/components/mbedtls/test_apps/main/crts/server_cert_chain.pem b/components/mbedtls/test_apps/mbedtls_ut/main/crts/server_cert_chain.pem similarity index 100% rename from components/mbedtls/test_apps/main/crts/server_cert_chain.pem rename to components/mbedtls/test_apps/mbedtls_ut/main/crts/server_cert_chain.pem diff --git a/components/mbedtls/test_apps/main/crts/server_root.pem b/components/mbedtls/test_apps/mbedtls_ut/main/crts/server_root.pem similarity index 100% rename from components/mbedtls/test_apps/main/crts/server_root.pem rename to components/mbedtls/test_apps/mbedtls_ut/main/crts/server_root.pem diff --git a/components/mbedtls/test_apps/main/crts/wrong_sig_crt_esp32_com.pem b/components/mbedtls/test_apps/mbedtls_ut/main/crts/wrong_sig_crt_esp32_com.pem similarity index 100% rename from components/mbedtls/test_apps/main/crts/wrong_sig_crt_esp32_com.pem rename to components/mbedtls/test_apps/mbedtls_ut/main/crts/wrong_sig_crt_esp32_com.pem diff --git a/components/mbedtls/test_apps/main/idf_component.yml b/components/mbedtls/test_apps/mbedtls_ut/main/idf_component.yml similarity index 100% rename from components/mbedtls/test_apps/main/idf_component.yml rename to components/mbedtls/test_apps/mbedtls_ut/main/idf_component.yml diff --git a/components/mbedtls/test_apps/main/test_apb_dport_access.c b/components/mbedtls/test_apps/mbedtls_ut/main/test_apb_dport_access.c similarity index 100% rename from components/mbedtls/test_apps/main/test_apb_dport_access.c rename to components/mbedtls/test_apps/mbedtls_ut/main/test_apb_dport_access.c diff --git a/components/mbedtls/test_apps/main/test_apb_dport_access.h b/components/mbedtls/test_apps/mbedtls_ut/main/test_apb_dport_access.h similarity index 100% rename from components/mbedtls/test_apps/main/test_apb_dport_access.h rename to components/mbedtls/test_apps/mbedtls_ut/main/test_apb_dport_access.h diff --git a/components/mbedtls/test_apps/main/test_ds_sign_and_decrypt.c b/components/mbedtls/test_apps/mbedtls_ut/main/test_ds_sign_and_decrypt.c similarity index 100% rename from components/mbedtls/test_apps/main/test_ds_sign_and_decrypt.c rename to components/mbedtls/test_apps/mbedtls_ut/main/test_ds_sign_and_decrypt.c diff --git a/components/mbedtls/test_apps/main/test_ecp.c b/components/mbedtls/test_apps/mbedtls_ut/main/test_ecp.c similarity index 100% rename from components/mbedtls/test_apps/main/test_ecp.c rename to components/mbedtls/test_apps/mbedtls_ut/main/test_ecp.c diff --git a/components/mbedtls/test_apps/main/test_esp_crt_bundle.c b/components/mbedtls/test_apps/mbedtls_ut/main/test_esp_crt_bundle.c similarity index 100% rename from components/mbedtls/test_apps/main/test_esp_crt_bundle.c rename to components/mbedtls/test_apps/mbedtls_ut/main/test_esp_crt_bundle.c diff --git a/components/mbedtls/test_apps/main/test_mbedtls.c b/components/mbedtls/test_apps/mbedtls_ut/main/test_mbedtls.c similarity index 100% rename from components/mbedtls/test_apps/main/test_mbedtls.c rename to components/mbedtls/test_apps/mbedtls_ut/main/test_mbedtls.c diff --git a/components/mbedtls/test_apps/main/test_mbedtls_mpi.c b/components/mbedtls/test_apps/mbedtls_ut/main/test_mbedtls_mpi.c similarity index 100% rename from components/mbedtls/test_apps/main/test_mbedtls_mpi.c rename to components/mbedtls/test_apps/mbedtls_ut/main/test_mbedtls_mpi.c diff --git a/components/mbedtls/test_apps/main/test_mbedtls_utils.c b/components/mbedtls/test_apps/mbedtls_ut/main/test_mbedtls_utils.c similarity index 100% rename from components/mbedtls/test_apps/main/test_mbedtls_utils.c rename to components/mbedtls/test_apps/mbedtls_ut/main/test_mbedtls_utils.c diff --git a/components/mbedtls/test_apps/main/test_mbedtls_utils.h b/components/mbedtls/test_apps/mbedtls_ut/main/test_mbedtls_utils.h similarity index 100% rename from components/mbedtls/test_apps/main/test_mbedtls_utils.h rename to components/mbedtls/test_apps/mbedtls_ut/main/test_mbedtls_utils.h diff --git a/components/mbedtls/test_apps/main/test_md5.c b/components/mbedtls/test_apps/mbedtls_ut/main/test_md5.c similarity index 100% rename from components/mbedtls/test_apps/main/test_md5.c rename to components/mbedtls/test_apps/mbedtls_ut/main/test_md5.c diff --git a/components/mbedtls/test_apps/main/test_psa_aes.c b/components/mbedtls/test_apps/mbedtls_ut/main/test_psa_aes.c similarity index 100% rename from components/mbedtls/test_apps/main/test_psa_aes.c rename to components/mbedtls/test_apps/mbedtls_ut/main/test_psa_aes.c diff --git a/components/mbedtls/test_apps/main/test_psa_aes_gcm.c b/components/mbedtls/test_apps/mbedtls_ut/main/test_psa_aes_gcm.c similarity index 100% rename from components/mbedtls/test_apps/main/test_psa_aes_gcm.c rename to components/mbedtls/test_apps/mbedtls_ut/main/test_psa_aes_gcm.c diff --git a/components/mbedtls/test_apps/main/test_psa_aes_perf.c b/components/mbedtls/test_apps/mbedtls_ut/main/test_psa_aes_perf.c similarity index 100% rename from components/mbedtls/test_apps/main/test_psa_aes_perf.c rename to components/mbedtls/test_apps/mbedtls_ut/main/test_psa_aes_perf.c diff --git a/components/mbedtls/test_apps/main/test_psa_aes_sha_parallel.c b/components/mbedtls/test_apps/mbedtls_ut/main/test_psa_aes_sha_parallel.c similarity index 100% rename from components/mbedtls/test_apps/main/test_psa_aes_sha_parallel.c rename to components/mbedtls/test_apps/mbedtls_ut/main/test_psa_aes_sha_parallel.c diff --git a/components/mbedtls/test_apps/main/test_psa_aes_sha_rsa.c b/components/mbedtls/test_apps/mbedtls_ut/main/test_psa_aes_sha_rsa.c similarity index 100% rename from components/mbedtls/test_apps/main/test_psa_aes_sha_rsa.c rename to components/mbedtls/test_apps/mbedtls_ut/main/test_psa_aes_sha_rsa.c diff --git a/components/mbedtls/test_apps/main/test_psa_cipher.c b/components/mbedtls/test_apps/mbedtls_ut/main/test_psa_cipher.c similarity index 100% rename from components/mbedtls/test_apps/main/test_psa_cipher.c rename to components/mbedtls/test_apps/mbedtls_ut/main/test_psa_cipher.c diff --git a/components/mbedtls/test_apps/main/test_psa_cmac.c b/components/mbedtls/test_apps/mbedtls_ut/main/test_psa_cmac.c similarity index 100% rename from components/mbedtls/test_apps/main/test_psa_cmac.c rename to components/mbedtls/test_apps/mbedtls_ut/main/test_psa_cmac.c diff --git a/components/mbedtls/test_apps/main/test_psa_ecdsa.c b/components/mbedtls/test_apps/mbedtls_ut/main/test_psa_ecdsa.c similarity index 100% rename from components/mbedtls/test_apps/main/test_psa_ecdsa.c rename to components/mbedtls/test_apps/mbedtls_ut/main/test_psa_ecdsa.c diff --git a/components/mbedtls/test_apps/main/test_psa_gcm.c b/components/mbedtls/test_apps/mbedtls_ut/main/test_psa_gcm.c similarity index 100% rename from components/mbedtls/test_apps/main/test_psa_gcm.c rename to components/mbedtls/test_apps/mbedtls_ut/main/test_psa_gcm.c diff --git a/components/mbedtls/test_apps/main/test_psa_hmac.c b/components/mbedtls/test_apps/mbedtls_ut/main/test_psa_hmac.c similarity index 100% rename from components/mbedtls/test_apps/main/test_psa_hmac.c rename to components/mbedtls/test_apps/mbedtls_ut/main/test_psa_hmac.c diff --git a/components/mbedtls/test_apps/main/test_psa_rsa.c b/components/mbedtls/test_apps/mbedtls_ut/main/test_psa_rsa.c similarity index 100% rename from components/mbedtls/test_apps/main/test_psa_rsa.c rename to components/mbedtls/test_apps/mbedtls_ut/main/test_psa_rsa.c diff --git a/components/mbedtls/test_apps/main/test_psa_sha.c b/components/mbedtls/test_apps/mbedtls_ut/main/test_psa_sha.c similarity index 100% rename from components/mbedtls/test_apps/main/test_psa_sha.c rename to components/mbedtls/test_apps/mbedtls_ut/main/test_psa_sha.c diff --git a/components/mbedtls/test_apps/main/test_sha.c b/components/mbedtls/test_apps/mbedtls_ut/main/test_sha.c similarity index 100% rename from components/mbedtls/test_apps/main/test_sha.c rename to components/mbedtls/test_apps/mbedtls_ut/main/test_sha.c diff --git a/components/mbedtls/test_apps/main/test_sha_perf.c b/components/mbedtls/test_apps/mbedtls_ut/main/test_sha_perf.c similarity index 100% rename from components/mbedtls/test_apps/main/test_sha_perf.c rename to components/mbedtls/test_apps/mbedtls_ut/main/test_sha_perf.c diff --git a/components/mbedtls/test_apps/partitions.csv b/components/mbedtls/test_apps/mbedtls_ut/partitions.csv similarity index 100% rename from components/mbedtls/test_apps/partitions.csv rename to components/mbedtls/test_apps/mbedtls_ut/partitions.csv diff --git a/components/mbedtls/test_apps/pytest_mbedtls_ut.py b/components/mbedtls/test_apps/mbedtls_ut/pytest_mbedtls_ut.py similarity index 100% rename from components/mbedtls/test_apps/pytest_mbedtls_ut.py rename to components/mbedtls/test_apps/mbedtls_ut/pytest_mbedtls_ut.py diff --git a/components/mbedtls/test_apps/sdkconfig.ci.aes_no_hw b/components/mbedtls/test_apps/mbedtls_ut/sdkconfig.ci.aes_no_hw similarity index 100% rename from components/mbedtls/test_apps/sdkconfig.ci.aes_no_hw rename to components/mbedtls/test_apps/mbedtls_ut/sdkconfig.ci.aes_no_hw diff --git a/components/mbedtls/test_apps/sdkconfig.ci.aria b/components/mbedtls/test_apps/mbedtls_ut/sdkconfig.ci.aria similarity index 100% rename from components/mbedtls/test_apps/sdkconfig.ci.aria rename to components/mbedtls/test_apps/mbedtls_ut/sdkconfig.ci.aria diff --git a/components/mbedtls/test_apps/sdkconfig.ci.default b/components/mbedtls/test_apps/mbedtls_ut/sdkconfig.ci.default similarity index 100% rename from components/mbedtls/test_apps/sdkconfig.ci.default rename to components/mbedtls/test_apps/mbedtls_ut/sdkconfig.ci.default diff --git a/components/mbedtls/test_apps/sdkconfig.ci.ds_rsa b/components/mbedtls/test_apps/mbedtls_ut/sdkconfig.ci.ds_rsa similarity index 100% rename from components/mbedtls/test_apps/sdkconfig.ci.ds_rsa rename to components/mbedtls/test_apps/mbedtls_ut/sdkconfig.ci.ds_rsa diff --git a/components/mbedtls/test_apps/sdkconfig.ci.ecdsa_sign b/components/mbedtls/test_apps/mbedtls_ut/sdkconfig.ci.ecdsa_sign similarity index 100% rename from components/mbedtls/test_apps/sdkconfig.ci.ecdsa_sign rename to components/mbedtls/test_apps/mbedtls_ut/sdkconfig.ci.ecdsa_sign diff --git a/components/mbedtls/test_apps/sdkconfig.ci.hmac_opaque b/components/mbedtls/test_apps/mbedtls_ut/sdkconfig.ci.hmac_opaque similarity index 100% rename from components/mbedtls/test_apps/sdkconfig.ci.hmac_opaque rename to components/mbedtls/test_apps/mbedtls_ut/sdkconfig.ci.hmac_opaque diff --git a/components/mbedtls/test_apps/sdkconfig.ci.perf_esp32 b/components/mbedtls/test_apps/mbedtls_ut/sdkconfig.ci.perf_esp32 similarity index 100% rename from components/mbedtls/test_apps/sdkconfig.ci.perf_esp32 rename to components/mbedtls/test_apps/mbedtls_ut/sdkconfig.ci.perf_esp32 diff --git a/components/mbedtls/test_apps/sdkconfig.ci.psram b/components/mbedtls/test_apps/mbedtls_ut/sdkconfig.ci.psram similarity index 100% rename from components/mbedtls/test_apps/sdkconfig.ci.psram rename to components/mbedtls/test_apps/mbedtls_ut/sdkconfig.ci.psram diff --git a/components/mbedtls/test_apps/sdkconfig.ci.psram_all_ext b/components/mbedtls/test_apps/mbedtls_ut/sdkconfig.ci.psram_all_ext similarity index 100% rename from components/mbedtls/test_apps/sdkconfig.ci.psram_all_ext rename to components/mbedtls/test_apps/mbedtls_ut/sdkconfig.ci.psram_all_ext diff --git a/components/mbedtls/test_apps/sdkconfig.ci.psram_all_ext_flash_enc b/components/mbedtls/test_apps/mbedtls_ut/sdkconfig.ci.psram_all_ext_flash_enc similarity index 100% rename from components/mbedtls/test_apps/sdkconfig.ci.psram_all_ext_flash_enc rename to components/mbedtls/test_apps/mbedtls_ut/sdkconfig.ci.psram_all_ext_flash_enc diff --git a/components/mbedtls/test_apps/sdkconfig.ci.psram_all_ext_flash_enc_f4r8 b/components/mbedtls/test_apps/mbedtls_ut/sdkconfig.ci.psram_all_ext_flash_enc_f4r8 similarity index 100% rename from components/mbedtls/test_apps/sdkconfig.ci.psram_all_ext_flash_enc_f4r8 rename to components/mbedtls/test_apps/mbedtls_ut/sdkconfig.ci.psram_all_ext_flash_enc_f4r8 diff --git a/components/mbedtls/test_apps/sdkconfig.ci.rom_impl b/components/mbedtls/test_apps/mbedtls_ut/sdkconfig.ci.rom_impl similarity index 100% rename from components/mbedtls/test_apps/sdkconfig.ci.rom_impl rename to components/mbedtls/test_apps/mbedtls_ut/sdkconfig.ci.rom_impl diff --git a/components/mbedtls/test_apps/sdkconfig.defaults b/components/mbedtls/test_apps/mbedtls_ut/sdkconfig.defaults similarity index 100% rename from components/mbedtls/test_apps/sdkconfig.defaults rename to components/mbedtls/test_apps/mbedtls_ut/sdkconfig.defaults diff --git a/components/mbedtls/test_apps/sdkconfig.defaults.esp32 b/components/mbedtls/test_apps/mbedtls_ut/sdkconfig.defaults.esp32 similarity index 100% rename from components/mbedtls/test_apps/sdkconfig.defaults.esp32 rename to components/mbedtls/test_apps/mbedtls_ut/sdkconfig.defaults.esp32 diff --git a/components/mbedtls/test_apps/sdkconfig.defaults.esp32c2 b/components/mbedtls/test_apps/mbedtls_ut/sdkconfig.defaults.esp32c2 similarity index 100% rename from components/mbedtls/test_apps/sdkconfig.defaults.esp32c2 rename to components/mbedtls/test_apps/mbedtls_ut/sdkconfig.defaults.esp32c2 diff --git a/components/mbedtls/test_apps/sdkconfig.defaults.esp32c3 b/components/mbedtls/test_apps/mbedtls_ut/sdkconfig.defaults.esp32c3 similarity index 100% rename from components/mbedtls/test_apps/sdkconfig.defaults.esp32c3 rename to components/mbedtls/test_apps/mbedtls_ut/sdkconfig.defaults.esp32c3 diff --git a/components/mbedtls/test_apps/sdkconfig.defaults.esp32s2 b/components/mbedtls/test_apps/mbedtls_ut/sdkconfig.defaults.esp32s2 similarity index 100% rename from components/mbedtls/test_apps/sdkconfig.defaults.esp32s2 rename to components/mbedtls/test_apps/mbedtls_ut/sdkconfig.defaults.esp32s2 diff --git a/components/mbedtls/test_apps/sdkconfig.defaults.esp32s3 b/components/mbedtls/test_apps/mbedtls_ut/sdkconfig.defaults.esp32s3 similarity index 100% rename from components/mbedtls/test_apps/sdkconfig.defaults.esp32s3 rename to components/mbedtls/test_apps/mbedtls_ut/sdkconfig.defaults.esp32s3 diff --git a/components/mbedtls/test_apps/persistent_storage_format/CMakeLists.txt b/components/mbedtls/test_apps/persistent_storage_format/CMakeLists.txt new file mode 100644 index 00000000000..a0a6d29017b --- /dev/null +++ b/components/mbedtls/test_apps/persistent_storage_format/CMakeLists.txt @@ -0,0 +1,35 @@ +# Persistent storage format stability tests for the ESP PSA opaque drivers. +# +# This is a separate test project from `mbedtls_ut` because it depends on a +# pre-flashed NVS fixture image, has its own partition layout (NVS is reserved +# at a known offset), and is run by a dedicated pytest entry point. +cmake_minimum_required(VERSION 3.22) + +set(EXTRA_COMPONENT_DIRS "$ENV{IDF_PATH}/tools/test_apps/components") +set(COMPONENTS main) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +project(persistent_storage_format_test) + +# Hook the shared multi-driver NVS fixture into `idf.py flash` so every +# CI runner gets a pre-populated NVS partition without manual steps. +# The same .bin works across runners — each only references its driver's +# key id from the fixture. +set(NVS_FIXTURE + "${CMAKE_CURRENT_SOURCE_DIR}/fixtures/nvs_efuse_v1.bin") +if(EXISTS ${NVS_FIXTURE}) + partition_table_get_partition_info(nvs_offset "--partition-name nvs" "offset") + partition_table_get_partition_info(nvs_size "--partition-name nvs" "size") + if(NOT nvs_offset OR NOT nvs_size) + message(FATAL_ERROR "persistent_storage_format: nvs partition not found " + "in partition table — fixture cannot be flashed.") + endif() + # NVS partition is plaintext-on-flash (IDF excludes it from auto flash + # encryption). Force plaintext writes even when SECURE_FLASH_ENC is on, + # otherwise the bytes get encrypted-on-write and NVS reads them as + # garbage at runtime. + esptool_py_flash_target_image(flash "nvs" ${nvs_offset} ${NVS_FIXTURE} + ALWAYS_PLAINTEXT) + message(STATUS "persistent_storage_format: will flash ${NVS_FIXTURE} " + "to nvs @ ${nvs_offset} (size ${nvs_size}).") +endif() diff --git a/components/mbedtls/test_apps/persistent_storage_format/README.md b/components/mbedtls/test_apps/persistent_storage_format/README.md new file mode 100644 index 00000000000..9216dc57a96 --- /dev/null +++ b/components/mbedtls/test_apps/persistent_storage_format/README.md @@ -0,0 +1,2 @@ +| Supported Targets | ESP32-C3 | ESP32-H2 | +| ----------------- | -------- | -------- | diff --git a/components/mbedtls/test_apps/persistent_storage_format/fixtures/README.md b/components/mbedtls/test_apps/persistent_storage_format/fixtures/README.md new file mode 100644 index 00000000000..dbaac7c4e1e --- /dev/null +++ b/components/mbedtls/test_apps/persistent_storage_format/fixtures/README.md @@ -0,0 +1,55 @@ +# Persistent storage format fixtures + +A single committed NVS partition image, pre-populated with three persistent eFuse PSA keys — one per driver. Every CI runner flashes the same `.bin`; each runner's consume test only references its driver's key id (RSA-DS / HMAC / ECDSA) and ignores the other two. + +Cross-platform format drift is caught by every runner — three independent signals on the same regression. + +## File + +| File | Storage version | Contains | +|------|-----------------|----------| +| `nvs_efuse_v1.bin` | v1 | 3 persistent eFuse keys: RSA-DS (id `0x1ADA1`), HMAC (id `0x1ADA2`), ECDSA SECP256R1 (id `0x1ADA3`) | + +The key ids and the eFuse block/key-id assignments are declared in `../main/test_persistent_format.h`. They align with the existing volatile tests in `mbedtls_ut`, so each runner's already-burned eFuse key serves the persistent path too. + +KM-source fixtures are intentionally absent — the per-key `esp_key_mgr_key_recovery_info_t` blob is HUK-wrapped by the deploying chip and meaningless on any other device. KM persistence is verified by the runtime deploy-then-import tests in `mbedtls_ut`. + +## Regenerating + +You need any chip with all three drivers in the build (typically ESP32-C5 or ESP32-P4 with DS + HMAC + ECDSA enabled). + +The capture build needs all three drivers compiled in, which neither `sdkconfig.ci.hmac` nor `sdkconfig.ci.ecdsa` provides on its own. Create your own local overlay file (any path; the example below uses `sdkconfig.capture`) with the following contents, then point `SDKCONFIG_DEFAULTS` at it. Do NOT name it `sdkconfig.ci.*` — that prefix is auto-discovered by the CI app manifest and would add a build target the runners can't use. + +``` +CONFIG_MBEDTLS_HARDWARE_RSA_DS_PERIPHERAL=y +CONFIG_MBEDTLS_HARDWARE_ECDSA_SIGN=y +CONFIG_MBEDTLS_HARDWARE_ECDSA_VERIFY=y +``` + +``` +cd components/mbedtls/test_apps/persistent_storage_format + +# 1. Erase NVS so the capture starts deterministic +esptool --chip --port erase_region --force + +# 2. Build, flash, and run ONLY the capture test +idf.py set-target +idf.py -DSDKCONFIG_DEFAULTS="sdkconfig.defaults;sdkconfig.capture" reconfigure +idf.py -p flash +idf.py -p monitor # send `[fixture_capture]` to Unity, wait for PASS + +# 3. Read the partition out +esptool --chip --port read_flash fixtures/nvs_efuse_v1.bin + +# 4. Commit. If you bumped any storage version, rename to nvs_efuse_v2.bin +# and keep nvs_efuse_v1.bin around — the v1 consume tests then prove +# v2 firmware can still read v1 NVS blobs (backward compat). +``` + +NVS offset/size live in `partitions.csv` — currently `0xA000` / `0x6000` (24 KB). + +## When to regenerate + +- After bumping any driver's persistent storage struct version. Add a new fixture file (`v2`, `v3`, …) AND keep the older fixtures around with their consume tests, so older-on-disk -> newer-firmware compatibility is proven. +- After an mbedtls upgrade that changes PSA ITS framing. Rare; needs a release note. +- Never just "to refresh." The file is meant to stay frozen so it detects regressions. diff --git a/components/mbedtls/test_apps/persistent_storage_format/fixtures/nvs_efuse_v1.bin b/components/mbedtls/test_apps/persistent_storage_format/fixtures/nvs_efuse_v1.bin new file mode 100644 index 00000000000..da3f88204e6 Binary files /dev/null and b/components/mbedtls/test_apps/persistent_storage_format/fixtures/nvs_efuse_v1.bin differ diff --git a/components/mbedtls/test_apps/persistent_storage_format/main/CMakeLists.txt b/components/mbedtls/test_apps/persistent_storage_format/main/CMakeLists.txt new file mode 100644 index 00000000000..20ae1e57262 --- /dev/null +++ b/components/mbedtls/test_apps/persistent_storage_format/main/CMakeLists.txt @@ -0,0 +1,14 @@ +idf_component_register( + SRC_DIRS "." + PRIV_INCLUDE_DIRS "." + PRIV_REQUIRES efuse cmock test_utils mbedtls esp_timer + unity spi_flash esp_security nvs_flash + WHOLE_ARCHIVE) + +# The RSA-DS opaque driver is the unit under test for these format +# stability checks; the linker wraps below let the sign path complete +# without a real eFuse HMAC key on the runner. Mirrors mbedtls_ut. +target_link_options( + ${COMPONENT_LIB} INTERFACE + "-Wl,--wrap=esp_ds_finish_sign,--wrap=esp_ds_start_sign,--wrap=esp_efuse_get_key_purpose" + ) diff --git a/components/mbedtls/test_apps/persistent_storage_format/main/app_main.c b/components/mbedtls/test_apps/persistent_storage_format/main/app_main.c new file mode 100644 index 00000000000..42463fd9382 --- /dev/null +++ b/components/mbedtls/test_apps/persistent_storage_format/main/app_main.c @@ -0,0 +1,52 @@ +/* + * SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "esp_err.h" +#include "esp_newlib.h" +#include "memory_checks.h" +#include "nvs_flash.h" +#include "unity.h" + +void setUp(void) +{ + test_utils_record_free_mem(); + test_utils_set_leak_level(CONFIG_UNITY_CRITICAL_LEAK_LEVEL_GENERAL, + ESP_LEAK_TYPE_CRITICAL, ESP_COMP_LEAK_GENERAL); + test_utils_set_leak_level(CONFIG_UNITY_WARN_LEAK_LEVEL_GENERAL, + ESP_LEAK_TYPE_WARNING, ESP_COMP_LEAK_GENERAL); +} + +void tearDown(void) +{ + vTaskDelay(5); + esp_reent_cleanup(); + TEST_ASSERT_MESSAGE(heap_caps_check_integrity(MALLOC_CAP_INVALID, true), + "The test has corrupted the heap"); + test_utils_finish_and_evaluate_leaks( + test_utils_get_leak_level(ESP_LEAK_TYPE_WARNING, ESP_COMP_LEAK_ALL), + test_utils_get_leak_level(ESP_LEAK_TYPE_CRITICAL, ESP_COMP_LEAK_ALL)); +} + +static void test_task(void *pvParameters) +{ + vTaskDelay(2); + unity_run_menu(); +} + +void app_main(void) +{ + esp_err_t err = nvs_flash_init(); + if (err == ESP_ERR_NVS_NO_FREE_PAGES || err == ESP_ERR_NVS_NEW_VERSION_FOUND) { + ESP_ERROR_CHECK(nvs_flash_erase()); + ESP_ERROR_CHECK(nvs_flash_init()); + } + xTaskCreatePinnedToCore(test_task, "testTask", + CONFIG_UNITY_FREERTOS_STACK_SIZE, NULL, + CONFIG_UNITY_FREERTOS_PRIORITY, NULL, + CONFIG_UNITY_FREERTOS_CPU); +} diff --git a/components/mbedtls/test_apps/persistent_storage_format/main/test_persistent_format.c b/components/mbedtls/test_apps/persistent_storage_format/main/test_persistent_format.c new file mode 100644 index 00000000000..7bacb731156 --- /dev/null +++ b/components/mbedtls/test_apps/persistent_storage_format/main/test_persistent_format.c @@ -0,0 +1,341 @@ +/* + * SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + * + * Shared scaffolding for persistent-storage format-stability tests. + * + * - One capture test that imports a persistent eFuse key for every PSA + * opaque driver compiled into the build (RSA-DS, HMAC, ECDSA), with + * fixed key ids. Run once on a chip that has all three drivers in the + * build; the resulting NVS partition is captured with esptool and + * committed as fixtures/nvs_efuse_v1.bin. + * + * - The committed fixture is then flashed by every CI runner; each runner + * only references its own driver's key id (see the per-driver consume + * files), so a single shared NVS image works across heterogeneous + * runners. + */ + +#include +#include "unity.h" +#include "sdkconfig.h" +#include "soc/soc_caps.h" +#include "esp_efuse.h" +#include "psa/crypto.h" + +#ifdef CONFIG_MBEDTLS_HARDWARE_RSA_DS_PERIPHERAL +#include "esp_ds.h" +#include "hal/hmac_types.h" +#include "psa_crypto_driver_esp_rsa_ds.h" +#endif + +#ifdef ESP_HMAC_OPAQUE_DRIVER_ENABLED +#include "psa_crypto_driver_esp_hmac_opaque.h" +#include "psa_crypto_driver_esp_hmac_opaque_contexts.h" +#endif + +#if CONFIG_MBEDTLS_HARDWARE_ECDSA_SIGN +#include "psa_crypto_driver_esp_ecdsa.h" +#include "psa_crypto_driver_esp_ecdsa_contexts.h" +#endif + +#include "test_persistent_format.h" + +/* --- Dynamic-purpose wrap shared across all drivers' import validation. */ +static volatile esp_efuse_purpose_t s_purpose_override = ESP_EFUSE_KEY_PURPOSE_USER; + +extern esp_efuse_purpose_t __real_esp_efuse_get_key_purpose(esp_efuse_block_t block); + +esp_efuse_purpose_t __wrap_esp_efuse_get_key_purpose(esp_efuse_block_t block) +{ + if (s_purpose_override != ESP_EFUSE_KEY_PURPOSE_USER) { + return s_purpose_override; + } + return __real_esp_efuse_get_key_purpose(block); +} + +/* --- DS hardware wraps. Without these the DS sign path on the consume + * side would touch the DS peripheral and fail for lack of a real + * eFuse HMAC key. Compiled only when DS is in the build. */ +#ifdef CONFIG_MBEDTLS_HARDWARE_RSA_DS_PERIPHERAL +int __wrap_esp_ds_start_sign(const void *message, const esp_ds_data_t *data, + hmac_key_id_t key_id, esp_ds_context_t **esp_ds_ctx) +{ + if (message == NULL || data == NULL || esp_ds_ctx == NULL) { + return ESP_ERR_INVALID_ARG; + } + *esp_ds_ctx = malloc(sizeof(esp_ds_context_t)); + if (*esp_ds_ctx == NULL) { + return ESP_ERR_NO_MEM; + } + return ESP_OK; +} + +int __wrap_esp_ds_finish_sign(void *sig, esp_ds_context_t *ctx) +{ + free(ctx); + return 0; +} + +/* RSA-DS storage embeds the encrypted key blob; the capture test below + * needs a syntactically-valid mock since the driver cross-validates + * rsa_length_bits against ds_data.rsa_length. */ +static esp_ds_data_ctx_t *mock_ds_data_ctx(void) +{ + esp_ds_data_ctx_t *ds = calloc(1, sizeof(esp_ds_data_ctx_t)); + if (!ds) { + return NULL; + } + ds->esp_ds_data = calloc(1, sizeof(esp_ds_data_t)); + if (!ds->esp_ds_data) { + free(ds); + return NULL; + } + ds->rsa_length_bits = 2048; + ds->efuse_key_id = ESP_PERSISTENT_FIXTURE_DS_EFUSE_KEY_ID; + ds->esp_ds_data->rsa_length = (ds->rsa_length_bits / 32) - 1; + return ds; +} + +static void free_mock_ds_data_ctx(esp_ds_data_ctx_t *ds) +{ + if (ds) { + free(ds->esp_ds_data); + free(ds); + } +} +#endif /* CONFIG_MBEDTLS_HARDWARE_RSA_DS_PERIPHERAL */ + +/* --- Capture test. Imports one persistent eFuse key per compiled driver, + * using fixed ids. Run once; capture NVS via esptool; commit. */ +TEST_CASE("efuse persistent fixture capture v1 (one-shot, all drivers)", + "[fixture_capture]") +{ + /* Clean any leftover from prior runs so we capture a deterministic NVS. */ + psa_destroy_key(ESP_PERSISTENT_FIXTURE_DS_KEY_ID); + psa_destroy_key(ESP_PERSISTENT_FIXTURE_HMAC_KEY_ID); + psa_destroy_key(ESP_PERSISTENT_FIXTURE_ECDSA_KEY_ID); + +#ifdef CONFIG_MBEDTLS_HARDWARE_RSA_DS_PERIPHERAL + { + esp_rsa_ds_opaque_key_t key = {0}; + key.ds_data_ctx = mock_ds_data_ctx(); + TEST_ASSERT_NOT_NULL(key.ds_data_ctx); + + psa_key_attributes_t attr = PSA_KEY_ATTRIBUTES_INIT; + psa_set_key_type(&attr, PSA_KEY_TYPE_RSA_KEY_PAIR); + psa_set_key_bits(&attr, key.ds_data_ctx->rsa_length_bits); + psa_set_key_usage_flags(&attr, PSA_KEY_USAGE_SIGN_HASH); + psa_set_key_algorithm(&attr, PSA_ALG_RSA_PKCS1V15_SIGN(PSA_ALG_SHA_256)); + psa_set_key_lifetime(&attr, PSA_KEY_LIFETIME_ESP_RSA_DS); + psa_set_key_id(&attr, ESP_PERSISTENT_FIXTURE_DS_KEY_ID); + + s_purpose_override = ESP_EFUSE_KEY_PURPOSE_HMAC_DOWN_DIGITAL_SIGNATURE; + psa_key_id_t kid; + TEST_ASSERT_EQUAL(PSA_SUCCESS, + psa_import_key(&attr, (const uint8_t *)&key, sizeof(key), &kid)); + s_purpose_override = ESP_EFUSE_KEY_PURPOSE_USER; + TEST_ASSERT_EQUAL(ESP_PERSISTENT_FIXTURE_DS_KEY_ID, kid); + TEST_ASSERT_EQUAL(PSA_SUCCESS, psa_purge_key(kid)); + + free_mock_ds_data_ctx(key.ds_data_ctx); + psa_reset_key_attributes(&attr); + } +#endif + +#ifdef ESP_HMAC_OPAQUE_DRIVER_ENABLED + { + esp_hmac_opaque_key_t key = { + .efuse_key_id = ESP_PERSISTENT_FIXTURE_HMAC_EFUSE_KEY_ID, + }; + + psa_key_attributes_t attr = PSA_KEY_ATTRIBUTES_INIT; + psa_set_key_type(&attr, PSA_KEY_TYPE_HMAC); + psa_set_key_bits(&attr, 256); + psa_set_key_usage_flags(&attr, PSA_KEY_USAGE_SIGN_MESSAGE | PSA_KEY_USAGE_VERIFY_MESSAGE); + psa_set_key_algorithm(&attr, PSA_ALG_HMAC(PSA_ALG_SHA_256)); + psa_set_key_lifetime(&attr, PSA_KEY_LIFETIME_ESP_HMAC); + psa_set_key_id(&attr, ESP_PERSISTENT_FIXTURE_HMAC_KEY_ID); + + s_purpose_override = ESP_EFUSE_KEY_PURPOSE_HMAC_UP; + psa_key_id_t kid; + TEST_ASSERT_EQUAL(PSA_SUCCESS, + psa_import_key(&attr, (const uint8_t *)&key, sizeof(key), &kid)); + s_purpose_override = ESP_EFUSE_KEY_PURPOSE_USER; + TEST_ASSERT_EQUAL(ESP_PERSISTENT_FIXTURE_HMAC_KEY_ID, kid); + TEST_ASSERT_EQUAL(PSA_SUCCESS, psa_purge_key(kid)); + + psa_reset_key_attributes(&attr); + } +#endif + +#if CONFIG_MBEDTLS_HARDWARE_ECDSA_SIGN + { + esp_ecdsa_opaque_key_t key = { + .curve = ESP_ECDSA_CURVE_SECP256R1, + .efuse_block = ESP_PERSISTENT_FIXTURE_ECDSA_EFUSE_BLOCK, + }; + + psa_key_attributes_t attr = PSA_KEY_ATTRIBUTES_INIT; + psa_set_key_type(&attr, PSA_KEY_TYPE_ECC_KEY_PAIR(PSA_ECC_FAMILY_SECP_R1)); + psa_set_key_bits(&attr, 256); + psa_set_key_usage_flags(&attr, PSA_KEY_USAGE_SIGN_HASH); + psa_set_key_algorithm(&attr, PSA_ALG_ECDSA(PSA_ALG_SHA_256)); + psa_set_key_lifetime(&attr, PSA_KEY_LIFETIME_ESP_ECDSA); + psa_set_key_id(&attr, ESP_PERSISTENT_FIXTURE_ECDSA_KEY_ID); + + s_purpose_override = ESP_EFUSE_KEY_PURPOSE_ECDSA_KEY; + psa_key_id_t kid; + TEST_ASSERT_EQUAL(PSA_SUCCESS, + psa_import_key(&attr, (const uint8_t *)&key, sizeof(key), &kid)); + s_purpose_override = ESP_EFUSE_KEY_PURPOSE_USER; + TEST_ASSERT_EQUAL(ESP_PERSISTENT_FIXTURE_ECDSA_KEY_ID, kid); + TEST_ASSERT_EQUAL(PSA_SUCCESS, psa_purge_key(kid)); + + psa_reset_key_attributes(&attr); + } +#endif + + printf("\n*** Fixture written; capture NVS partition with esptool now. ***\n\n"); +} + +/* ====================================================================== * + * Per-driver consume tests. + * + * The CI runner flashes fixtures/nvs_efuse_v1.bin to the NVS partition. + * The persistent keys are already there with their fixed ids; each + * driver's consume test only references its own id and ignores the rest, + * so a single shared NVS image works across heterogeneous runners. + * + * If anything in the on-NVS format has drifted (storage struct, PSA + * framing, NVS encoding) the corresponding op call returns + * INVALID_ARGUMENT/DATA_INVALID and the test fails. Each driver gates + * its test on the same CONFIG flag that pulls its driver into the build. + * ====================================================================== */ + +#if CONFIG_MBEDTLS_HARDWARE_RSA_DS_PERIPHERAL +TEST_CASE("rsa-ds persistent NVS fixture v1 sign", + "[persistent_format][rsa_ds]") +{ + uint8_t hash[32] = {0}; + size_t hash_length = 0; + uint8_t input[7] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06}; + TEST_ASSERT_EQUAL(PSA_SUCCESS, + psa_hash_compute(PSA_ALG_SHA_256, input, sizeof(input), + hash, sizeof(hash), &hash_length)); + + uint8_t signature[256] = {0}; + size_t signature_length = 0; + TEST_ASSERT_EQUAL_HEX32(PSA_SUCCESS, + psa_sign_hash(ESP_PERSISTENT_FIXTURE_DS_KEY_ID, + PSA_ALG_RSA_PKCS1V15_SIGN(PSA_ALG_SHA_256), + hash, hash_length, + signature, sizeof(signature), &signature_length)); + TEST_ASSERT_EQUAL(256, signature_length); + + /* v15 padding-shape sanity, same as the volatile sign test. */ + TEST_ASSERT_EQUAL(0, memcmp(hash, signature + (256 - hash_length), hash_length)); + TEST_ASSERT_EQUAL(hash_length, signature[256 - hash_length - 1]); + TEST_ASSERT_EQUAL(0x04, signature[256 - hash_length - 2]); + TEST_ASSERT_EQUAL(0x00, signature[0]); + + TEST_ASSERT_EQUAL(PSA_SUCCESS, psa_purge_key(ESP_PERSISTENT_FIXTURE_DS_KEY_ID)); +} +#endif /* CONFIG_MBEDTLS_HARDWARE_RSA_DS_PERIPHERAL */ + +#ifdef ESP_HMAC_OPAQUE_DRIVER_ENABLED +/* The runner has an eFuse HMAC key burned in + * ESP_PERSISTENT_FIXTURE_HMAC_EFUSE_KEY_ID with HMAC_UP purpose; we do a + * real mac_compute + mac_verify roundtrip. */ +static const uint8_t hmac_test_data[] = "Pretty long input message"; + +TEST_CASE("hmac efuse persistent NVS fixture v1 mac", + "[persistent_format][hmac_efuse_key]") +{ + uint8_t mac[32] = {0}; + size_t mac_length = 0; + TEST_ASSERT_EQUAL_HEX32(PSA_SUCCESS, + psa_mac_compute(ESP_PERSISTENT_FIXTURE_HMAC_KEY_ID, + PSA_ALG_HMAC(PSA_ALG_SHA_256), + hmac_test_data, sizeof(hmac_test_data) - 1, + mac, sizeof(mac), &mac_length)); + TEST_ASSERT_EQUAL(32, mac_length); + + TEST_ASSERT_EQUAL(PSA_SUCCESS, + psa_mac_verify(ESP_PERSISTENT_FIXTURE_HMAC_KEY_ID, + PSA_ALG_HMAC(PSA_ALG_SHA_256), + hmac_test_data, sizeof(hmac_test_data) - 1, + mac, mac_length)); + + TEST_ASSERT_EQUAL(PSA_SUCCESS, psa_purge_key(ESP_PERSISTENT_FIXTURE_HMAC_KEY_ID)); +} +#endif /* ESP_HMAC_OPAQUE_DRIVER_ENABLED */ + +#if CONFIG_MBEDTLS_HARDWARE_ECDSA_SIGN +/* SECP256R1 public key matching the SECP256R1 ECDSA key burned in + * ESP_PERSISTENT_FIXTURE_ECDSA_EFUSE_BLOCK on the runner. Same constants + * as mbedtls_ut/test_psa_ecdsa.c uses for its eFuse-key volatile tests. */ +static const uint8_t ecdsa256_pub_x[] = { + 0xa2, 0x8f, 0x52, 0x60, 0x20, 0x9b, 0x54, 0x3c, + 0x13, 0x2f, 0x51, 0xb1, 0x89, 0xbf, 0xc7, 0xfa, + 0x84, 0x5c, 0x56, 0x96, 0x2a, 0x00, 0x67, 0xdd, + 0x7c, 0x8c, 0x0f, 0x63, 0x8b, 0x76, 0x7f, 0xb9, +}; +static const uint8_t ecdsa256_pub_y[] = { + 0xf6, 0x4c, 0x87, 0x5b, 0x5a, 0x9b, 0x59, 0x0a, + 0xc4, 0x53, 0x04, 0x72, 0x0d, 0x7c, 0xde, 0xac, + 0x7e, 0xad, 0x49, 0x8c, 0xf7, 0x5c, 0xc3, 0x1c, + 0x1e, 0x81, 0xf2, 0x47, 0x01, 0x74, 0x05, 0xd5, +}; + +/* The runner has a SECP256R1 ECDSA key burned in + * ESP_PERSISTENT_FIXTURE_ECDSA_EFUSE_BLOCK with ECDSA_KEY purpose. We + * sign with the persistent key, then verify the resulting signature + * with a freshly-imported transparent public key matching the burned + * eFuse private key. The verify pass is the mathematical "compare the + * signature" — it proves the persistent extract recovered the SAME + * private key as the runner has burned. We don't rely on HW pubkey + * export here, since some ECDSA-capable chips (e.g. esp32h2) lack it + * for opaque keys. */ +TEST_CASE("ecdsa efuse persistent NVS fixture v1 sign and verify", + "[persistent_format][ecdsa_efuse_key]") +{ + psa_algorithm_t alg = PSA_ALG_ECDSA(PSA_ALG_SHA_256); + uint8_t hash[32]; + memset(hash, 0xA5, sizeof(hash)); + + /* Sign on the persistent eFuse key. */ + uint8_t signature[64]; /* SECP256R1: r || s, 32 bytes each */ + size_t signature_length = 0; + TEST_ASSERT_EQUAL_HEX32(PSA_SUCCESS, + psa_sign_hash(ESP_PERSISTENT_FIXTURE_ECDSA_KEY_ID, + alg, hash, sizeof(hash), + signature, sizeof(signature), &signature_length)); + TEST_ASSERT_EQUAL(64, signature_length); + + /* Import the matching transparent public key and verify. */ + uint8_t pub[65]; + pub[0] = 0x04; /* uncompressed point format */ + memcpy(pub + 1, ecdsa256_pub_x, sizeof(ecdsa256_pub_x)); + memcpy(pub + 1 + sizeof(ecdsa256_pub_x), ecdsa256_pub_y, sizeof(ecdsa256_pub_y)); + + psa_key_attributes_t pub_attr = PSA_KEY_ATTRIBUTES_INIT; + psa_set_key_type(&pub_attr, PSA_KEY_TYPE_ECC_PUBLIC_KEY(PSA_ECC_FAMILY_SECP_R1)); + psa_set_key_usage_flags(&pub_attr, PSA_KEY_USAGE_VERIFY_HASH); + psa_set_key_algorithm(&pub_attr, PSA_ALG_ECDSA(PSA_ALG_SHA_256)); + psa_set_key_bits(&pub_attr, 256); + + psa_key_id_t pub_kid = 0; + TEST_ASSERT_EQUAL(PSA_SUCCESS, + psa_import_key(&pub_attr, pub, sizeof(pub), &pub_kid)); + + TEST_ASSERT_EQUAL_HEX32(PSA_SUCCESS, + psa_verify_hash(pub_kid, PSA_ALG_ECDSA(PSA_ALG_SHA_256), + hash, sizeof(hash), signature, signature_length)); + + TEST_ASSERT_EQUAL(PSA_SUCCESS, psa_destroy_key(pub_kid)); + psa_reset_key_attributes(&pub_attr); + TEST_ASSERT_EQUAL(PSA_SUCCESS, psa_purge_key(ESP_PERSISTENT_FIXTURE_ECDSA_KEY_ID)); +} +#endif /* CONFIG_MBEDTLS_HARDWARE_ECDSA_SIGN */ diff --git a/components/mbedtls/test_apps/persistent_storage_format/main/test_persistent_format.h b/components/mbedtls/test_apps/persistent_storage_format/main/test_persistent_format.h new file mode 100644 index 00000000000..bdc53fc5795 --- /dev/null +++ b/components/mbedtls/test_apps/persistent_storage_format/main/test_persistent_format.h @@ -0,0 +1,32 @@ +/* + * SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + * + * Fixed key ids and eFuse block/id assignments baked into the committed + * NVS fixture (fixtures/nvs_efuse_v1.bin). Bumping ANY of these values + * means regenerating the fixture and bumping its filename's version. + * + * The eFuse block/id values are chosen to align with the existing + * volatile tests on each driver's runner so the same physical eFuse + * key the runner already has burned for the volatile test path serves + * the persistent path too. + */ +#pragma once + +/* Persistent PSA key ids (chosen arbitrarily, must be non-zero). */ +#define ESP_PERSISTENT_FIXTURE_DS_KEY_ID 0x1ADA1U +#define ESP_PERSISTENT_FIXTURE_HMAC_KEY_ID 0x1ADA2U +#define ESP_PERSISTENT_FIXTURE_ECDSA_KEY_ID 0x1ADA3U + +/* eFuse block / key-id assignments. The capture chip's import path uses + * the dynamic-purpose wrap to satisfy validation; on the consume runners + * these must match the eFuse blocks that runner has burned for the + * relevant peripheral. */ +/* RSA-DS uses a distinct block from HMAC so the two persistent keys + * don't visually share an eFuse id. The DS HW is wrapped at op time, so + * the runner doesn't actually need anything burned at this block — any + * non-conflicting value works. */ +#define ESP_PERSISTENT_FIXTURE_DS_EFUSE_KEY_ID 1 /* HMAC_KEY1; DS HW wrapped at op time */ +#define ESP_PERSISTENT_FIXTURE_HMAC_EFUSE_KEY_ID 0 /* HMAC_KEY0 — matches the nvs_encr_hmac runner's real burned key */ +#define ESP_PERSISTENT_FIXTURE_ECDSA_EFUSE_BLOCK 5 /* EFUSE_BLK_KEY1, matches SECP256R1_EFUSE_BLOCK in test_psa_ecdsa.c */ diff --git a/components/mbedtls/test_apps/persistent_storage_format/partitions.csv b/components/mbedtls/test_apps/persistent_storage_format/partitions.csv new file mode 100644 index 00000000000..38f99eeb442 --- /dev/null +++ b/components/mbedtls/test_apps/persistent_storage_format/partitions.csv @@ -0,0 +1,9 @@ +# Persistent-storage-format test partition layout. +# CONFIG_PARTITION_TABLE_CUSTOM=y is needed for this file to be picked up. +# The NVS partition is the target for the pre-flashed fixture images +# under fixtures/. +# +# Name, Type, SubType, Offset, Size, Flags +nvs, data, nvs, 0xA000, 0x6000, +esp_secure_cert, 0x3F, , 0x10000, 0x2000, +factory, app, factory, 0x20000, 1M, diff --git a/components/mbedtls/test_apps/persistent_storage_format/pytest_persistent_storage_format.py b/components/mbedtls/test_apps/persistent_storage_format/pytest_persistent_storage_format.py new file mode 100644 index 00000000000..0cd1f70c596 --- /dev/null +++ b/components/mbedtls/test_apps/persistent_storage_format/pytest_persistent_storage_format.py @@ -0,0 +1,36 @@ +# SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD +# SPDX-License-Identifier: CC0-1.0 +# +# Per-runner pytest entry points for the persistent-storage format +# stability tests. Each runner flashes the SAME shared NVS fixture +# (fixtures/nvs_efuse_v1.bin) — pre-populated with three persistent +# eFuse keys, one per driver — and runs the consume tests for whichever +# drivers it has hardware for. The capture-side test is tagged +# [fixture_capture] (NOT [persistent_format]) so it is never picked up +# by the normal CI groups. +import pytest +from pytest_embedded import Dut +from pytest_embedded_idf.utils import idf_parametrize + + +# nvs_encr_hmac runner (esp32c3) covers BOTH HMAC and RSA-DS: +# - real eFuse HMAC key burned in block 0 with HMAC_UP purpose → +# genuine psa_mac_compute roundtrip +# - DS peripheral on c3, DS HW wrapped → RSA-DS consume runs without +# needing a real HMAC-DOWN-DIGITAL-SIGNATURE eFuse key +# A single runner exercises both drivers' persistent extract paths. +@pytest.mark.nvs_encr_hmac +@pytest.mark.parametrize('config', ['hmac'], indirect=True) +@idf_parametrize('target', ['esp32c3'], indirect=['target']) +def test_persistent_storage_format_hmac_and_rsa_ds(dut: Dut) -> None: + dut.run_all_single_board_cases(group='persistent_format') + + +# ECDSA runner (esp32h2) — has a SECP256R1 ECDSA key burned in +# EFUSE_BLK_KEY1 with ECDSA_KEY purpose (matches mbedtls_ut's existing +# ecdsa_sign volatile tests). +@pytest.mark.ecdsa_efuse +@pytest.mark.parametrize('config', ['ecdsa'], indirect=True) +@idf_parametrize('target', ['esp32h2'], indirect=['target']) +def test_persistent_storage_format_ecdsa(dut: Dut) -> None: + dut.run_all_single_board_cases(group='ecdsa_efuse_key') diff --git a/components/mbedtls/test_apps/persistent_storage_format/sdkconfig.ci.ecdsa b/components/mbedtls/test_apps/persistent_storage_format/sdkconfig.ci.ecdsa new file mode 100644 index 00000000000..95f68d57d64 --- /dev/null +++ b/components/mbedtls/test_apps/persistent_storage_format/sdkconfig.ci.ecdsa @@ -0,0 +1,5 @@ +# ECDSA opaque persistent-format runner overlay (esp32h2). +# The runner has a SECP256R1 ECDSA key burned in EFUSE_BLK_KEY1 with +# ECDSA_KEY purpose. DS isn't on h2; HMAC opaque test runs on c3, not here. +CONFIG_MBEDTLS_HARDWARE_ECDSA_SIGN=y +CONFIG_MBEDTLS_HARDWARE_ECDSA_VERIFY=y diff --git a/components/mbedtls/test_apps/persistent_storage_format/sdkconfig.ci.hmac b/components/mbedtls/test_apps/persistent_storage_format/sdkconfig.ci.hmac new file mode 100644 index 00000000000..de2ed42e26e --- /dev/null +++ b/components/mbedtls/test_apps/persistent_storage_format/sdkconfig.ci.hmac @@ -0,0 +1,21 @@ +# HMAC + RSA-DS persistent-format runner overlay (nvs_encr_hmac runner, +# esp32c3). The runner has: +# - an eFuse HMAC key in block 0 with HMAC_UP purpose (used by the HMAC +# consume test for a real psa_mac_compute roundtrip) +# in test_persistent_format.c, so the RSA-DS consume test runs without +# needing a real HMAC-DOWN-DIGITAL-SIGNATURE eFuse key on this runner. +# +# A single runner therefore exercises both the HMAC and RSA-DS persistent +# extract paths against the same shared NVS fixture. + +CONFIG_SECURE_FLASH_ENC_ENABLED=y +CONFIG_SECURE_FLASH_ENCRYPTION_MODE_DEVELOPMENT=y +CONFIG_SECURE_FLASH_REQUIRE_ALREADY_ENABLED=y +CONFIG_SECURE_BOOT_ALLOW_ROM_BASIC=y +CONFIG_SECURE_BOOT_ALLOW_JTAG=y +CONFIG_SECURE_FLASH_UART_BOOTLOADER_ALLOW_ENC=y +CONFIG_SECURE_FLASH_UART_BOOTLOADER_ALLOW_DEC=y +CONFIG_SECURE_FLASH_UART_BOOTLOADER_ALLOW_CACHE=y +CONFIG_PARTITION_TABLE_OFFSET=0x9000 + +CONFIG_MBEDTLS_HARDWARE_RSA_DS_PERIPHERAL=y diff --git a/components/mbedtls/test_apps/persistent_storage_format/sdkconfig.defaults b/components/mbedtls/test_apps/persistent_storage_format/sdkconfig.defaults new file mode 100644 index 00000000000..6af4dd7be9b --- /dev/null +++ b/components/mbedtls/test_apps/persistent_storage_format/sdkconfig.defaults @@ -0,0 +1,19 @@ +CONFIG_PARTITION_TABLE_CUSTOM=y +CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv" +CONFIG_PARTITION_TABLE_OFFSET=0x9000 + +# NVS encryption disabled — fixture images are committed as plaintext NVS +# bytes so they're portable across devices. With CONFIG_NVS_ENCRYPTION=y the +# bootloader auto-routes through nvs_sec_provider which requires an eFuse +# HMAC key; that's outside the scope of these format-stability tests. +CONFIG_NVS_ENCRYPTION=n + +# Common build sanity options (mirrored from mbedtls_ut) +CONFIG_HEAP_POISONING_COMPREHENSIVE=y +CONFIG_COMPILER_WARN_WRITE_STRINGS=y +CONFIG_BOOTLOADER_LOG_LEVEL_WARN=y +CONFIG_FREERTOS_WATCHPOINT_END_OF_STACK=y +CONFIG_COMPILER_STACK_CHECK_MODE_STRONG=y +CONFIG_COMPILER_STACK_CHECK=y +CONFIG_ESP_TASK_WDT_EN=y +CONFIG_ESP_TASK_WDT_INIT=n diff --git a/docs/en/api-reference/peripherals/ds.rst b/docs/en/api-reference/peripherals/ds.rst index 6cac5199176..c2ad72223bc 100644 --- a/docs/en/api-reference/peripherals/ds.rst +++ b/docs/en/api-reference/peripherals/ds.rst @@ -113,8 +113,17 @@ To use the DS peripheral for signing or decryption in application code (outside psa_destroy_key(key_id); -Example for SSL Mutual Authentication Using DS ----------------------------------------------- +Persistent vs. Volatile RSA_DS PSA Keys +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The driver supports two PSA key lifetimes for RSA_DS keys: + +- ``PSA_KEY_LIFETIME_ESP_RSA_DS_VOLATILE`` (used in the example above) stores only pointers to the caller-supplied ``esp_ds_data_ctx_t`` and any Key Manager recovery info in the PSA key slot. The referenced buffers must remain valid until :cpp:func:`psa_destroy_key` is called. This avoids deep-copying large blobs such as :cpp:type:`esp_ds_data_t` (≈1200-1600 bytes, chip-dependent) when they already live in mmap'd flash via ``esp_secure_cert_mgr``. + +- ``PSA_KEY_LIFETIME_ESP_RSA_DS`` (persistent) deep-copies the encrypted key material into the PSA key slot at :cpp:func:`psa_import_key` time and PSA persists it to NVS together with the rest of the key attributes. The caller is free to release the import-time buffers once :cpp:func:`psa_import_key` returns; subsequent :cpp:func:`psa_sign_hash` / :cpp:func:`psa_asymmetric_decrypt` calls retrieve the bytes back from NVS automatically. Use this lifetime when the application wants the key to survive reboots without having to reload the ``esp_ds_data_ctx_t`` from external storage on every boot. + +Example for SSL Mutual Authentication Using RSA_DS +--------------------------------------------------- The SSL mutual authentication example that previously lived under ``examples/protocols/mqtt/ssl_ds`` is now shipped with the standalone `espressif/mqtt `__ component. Follow the component documentation to fetch the SSL DS example and build it together with ESP-MQTT. The example continues to use ``mqtt_client`` (implemented by ESP-MQTT) to connect to ``test.mosquitto.org`` over mutual-authenticated TLS, with the TLS portion handled by ESP-TLS. diff --git a/docs/en/security/security.rst b/docs/en/security/security.rst index 8070c4b7804..c7fe012b9b2 100644 --- a/docs/en/security/security.rst +++ b/docs/en/security/security.rst @@ -175,7 +175,7 @@ Flash Encryption Best Practices * - High - 72.4 % - .. [#] The above performance numbers have been calculated using the AES performance test of the mbedtls test application :component_file:`test_psa_aes_perf.c `. + .. [#] The above performance numbers have been calculated using the AES performance test of the mbedtls test application :component_file:`test_psa_aes_perf.c `. Considering the above performance impact, ESP-IDF by-default does not enable the pseudo-round function to avoid any performance-related degrade. But it is recommended to enable the pseudo-round function for better security. diff --git a/docs/zh_CN/api-reference/peripherals/ds.rst b/docs/zh_CN/api-reference/peripherals/ds.rst index 22aa2d288d8..dc3a97bf725 100644 --- a/docs/zh_CN/api-reference/peripherals/ds.rst +++ b/docs/zh_CN/api-reference/peripherals/ds.rst @@ -113,8 +113,17 @@ TLS 连接所需的 DS 外设配置 psa_destroy_key(key_id); -使用 DS 外设进行 SSL 双向认证 ------------------------------ +持久化与易失性 RSA_DS PSA 密钥 +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +驱动支持两种 PSA 密钥生命周期: + +- ``PSA_KEY_LIFETIME_ESP_RSA_DS_VOLATILE`` (上述示例中使用)只在 PSA 密钥 槽中保存指向调用方提供的 ``esp_ds_data_ctx_t`` 以及密钥管理器恢复信息的 指针。被引用的缓冲区必须保持有效,直到调用 :cpp:func:`psa_destroy_key` 为止。这样可避免对大块数据(例如 :cpp:type:`esp_ds_data_t`,约 1200–1600 字节,因芯片而异)进行深拷贝;当这些数据已经通过 ``esp_secure_cert_mgr`` 从 flash 中以 mmap 形式可用时,这一点尤其有用。 + +- ``PSA_KEY_LIFETIME_ESP_RSA_DS`` (持久化)在调用 :cpp:func:`psa_import_key` 时将加密的密钥数据深拷贝到 PSA 密钥槽中,并 由 PSA 与其他密钥属性一同持久化到 NVS。导入返回后,调用方即可释放原始 缓冲区;后续的 :cpp:func:`psa_sign_hash` 和 :cpp:func:`psa_asymmetric_decrypt` 调用会自动从 NVS 取回所需数据。 当应用希望密钥在重启后依然可用,且无需在每次启动时重新从外部存储 载入 ``esp_ds_data_ctx_t`` 时,应使用此生命周期。 + +使用 RSA_DS 外设进行 SSL 双向认证 +------------------------------------ 此前位于 ``examples/protocols/mqtt/ssl_ds`` 目录下的 SSL 双向认证示例现已随独立的 `espressif/mqtt `__ 组件一同提供。请参照该组件文档获取 SSL DS 示例,并与 ESP-MQTT 一同构建。该示例仍使用 ``mqtt_client`` (由 ESP-MQTT 实现),通过双向认证 TLS 连接至 ``test.mosquitto.org``,其中 TLS 通信层仍由 ESP-TLS 实现。 diff --git a/docs/zh_CN/security/security.rst b/docs/zh_CN/security/security.rst index eb5b9cf6e51..b983659f863 100644 --- a/docs/zh_CN/security/security.rst +++ b/docs/zh_CN/security/security.rst @@ -175,7 +175,7 @@ flash 加密最佳实践 * - 高 - 72.4 % - .. [#] 上述性能数据通过 mbedtls 测试应用中的 AES 性能测试 :component_file:`test_psa_aes_perf.c ` 计算得出。 + .. [#] 上述性能数据通过 mbedtls 测试应用中的 AES 性能测试 :component_file:`test_psa_aes_perf.c ` 计算得出。 考虑到上述性能影响,ESP-IDF 默认关闭伪轮次功能,避免对相关性能造成影响。但如果需要更高的安全性,仍然建议启用。