From 2ee22f968a509335d57d8ea243d14baf2ab04bbd Mon Sep 17 00:00:00 2001 From: "harshal.patil" Date: Fri, 6 Dec 2024 15:55:20 +0530 Subject: [PATCH 01/11] fix(nvs_flash): Allow matching with partition when label provided is NULL - When esp_parition_find_first() is called with the partition label provided as NULL by the bootloader, allowing matching to the first partition that matches partition_type and partition_subtype as done in the app variant. --- components/esp_partition/partition_bootloader.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/components/esp_partition/partition_bootloader.c b/components/esp_partition/partition_bootloader.c index 8cfc5ff48b..5f01ddd705 100644 --- a/components/esp_partition/partition_bootloader.c +++ b/components/esp_partition/partition_bootloader.c @@ -46,9 +46,9 @@ const esp_partition_t* esp_partition_find_first(esp_partition_type_t type, esp_p // if everything matches, populate the internal_partition if (partition->type == type - && partition->subtype == subtype - && strncmp((char*) partition->label, label, sizeof(partition->label) - 1) == 0) { - + && partition->subtype == subtype + && (!label || (label && (strncmp((char*) partition->label, label, sizeof(partition->label) - 1) == 0))) + ) { ESP_LOGV(TAG, "Matched", partition->type, partition->subtype, partition->label); internal_partition.flash_chip = NULL; //esp_flash_default_chip; internal_partition.type = partition->type; From eb05db30fc680d6da39e07bb49a263744ada696a Mon Sep 17 00:00:00 2001 From: "harshal.patil" Date: Tue, 7 Jan 2025 16:58:30 +0530 Subject: [PATCH 02/11] fix(esptool_py): Fix NVS partition being incorrectly marked as encrypted - The CMake function esptool_py_partition_needs_encryption() in the esptool_py component used to mark NVS partition as encrypted, instead it should have marked the NVS keys partition as encrypted. --- components/esptool_py/project_include.cmake | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/components/esptool_py/project_include.cmake b/components/esptool_py/project_include.cmake index 89547014ed..20cd576727 100644 --- a/components/esptool_py/project_include.cmake +++ b/components/esptool_py/project_include.cmake @@ -289,13 +289,13 @@ function(esptool_py_partition_needs_encryption retencrypted partition_name) # - DATA 0x01 # Subtypes: # - ota 0x00 - # - nvs 0x02 - # If the partition is an app, an OTA or an NVS partition, then it should + # - nvs_keys 0x04 + # If the partition is an app, an OTA or an NVS keys partition, then it should # be encrypted if( (${type} EQUAL 0) OR (${type} EQUAL 1 AND ${subtype} EQUAL 0) OR - (${type} EQUAL 1 AND ${subtype} EQUAL 2) + (${type} EQUAL 1 AND ${subtype} EQUAL 4) ) set(encrypted TRUE) endif() From 4c384993037f484842a154bcbda25668ceffeca0 Mon Sep 17 00:00:00 2001 From: "harshal.patil" Date: Thu, 9 Jan 2025 14:14:56 +0530 Subject: [PATCH 03/11] fix(esp_partition): Support reading unencrypted partitions by the bootloader - When flash encryption is enable to support reading a partition that is not marked as "encrypted", the `esp_partition_read()` API of bootloader build should be redirected to the `bootloader_flash_read()` API. --- .../esp_partition/partition_bootloader.c | 14 +++++- components/nvs_flash/src/nvs_bootloader.c | 43 +++++++++++++++++-- 2 files changed, 52 insertions(+), 5 deletions(-) diff --git a/components/esp_partition/partition_bootloader.c b/components/esp_partition/partition_bootloader.c index 5f01ddd705..a408037fb3 100644 --- a/components/esp_partition/partition_bootloader.c +++ b/components/esp_partition/partition_bootloader.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2015-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 * @@ -74,6 +74,18 @@ const esp_partition_t* esp_partition_find_first(esp_partition_type_t type, esp_p esp_err_t esp_partition_read(const esp_partition_t *partition, size_t src_offset, void *dst, size_t size) { + assert(partition != NULL); + if (src_offset > partition->size) { + return ESP_ERR_INVALID_ARG; + } + if (size > partition->size - src_offset) { + return ESP_ERR_INVALID_SIZE; + } + + if (!partition->encrypted) { + return bootloader_flash_read(partition->address + src_offset, dst, size, false); + } + const void *buf; // log call to mmap diff --git a/components/nvs_flash/src/nvs_bootloader.c b/components/nvs_flash/src/nvs_bootloader.c index 07126c966a..d6eca44a25 100644 --- a/components/nvs_flash/src/nvs_bootloader.c +++ b/components/nvs_flash/src/nvs_bootloader.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -215,7 +215,7 @@ esp_err_t nvs_bootloader_page_visitor_get_namespaces(nvs_bootloader_page_visitor // iterate over all entries with state written // if the entry is namespace entry, then iterate the read_list and populate the namespace index by matching the namespace name uint8_t start_index = 0; - nvs_bootloader_single_entry_t item = {0}; + WORD_ALIGNED_ATTR nvs_bootloader_single_entry_t item = {0}; // repeat finding single entry items on the page until all entries are processed or error occurs while (ret == ESP_OK) { @@ -275,7 +275,7 @@ esp_err_t nvs_bootloader_page_visitor_get_key_value_pairs(nvs_bootloader_page_vi // if the entry is not a namespace entry, then iterate the read_list and populate the value by matching the namespace index, key name and value type uint8_t next_index = 0; // index of the next entry to read, updated to the next entry by the read_next_single_entry_item uint8_t current_index = 0; // index of the actual entry being processed - nvs_bootloader_single_entry_t item = {0}; + WORD_ALIGNED_ATTR nvs_bootloader_single_entry_t item = {0}; // repeat finding single entry items on the page until all entries are processed or error occurs while (ret == ESP_OK) { @@ -546,7 +546,42 @@ esp_err_t nvs_bootloader_read_entries_block(const esp_partition_t *partition, size_t data_offset = page_index * NVS_CONST_PAGE_SIZE + NVS_CONST_PAGE_ENTRY_DATA_OFFSET + entry_index * NVS_CONST_ENTRY_SIZE ; - return esp_partition_read(partition, data_offset, block, block_len); + if (data_offset & 3 || block_len & 3 || (intptr_t) block & 3) { + /* For the bootloader build, the esp_partition_read() API internally is calls bootloader_flash_read() that + * requires the src_address, length and the destination address to be word aligned. + * src_address: NVS keys and values are always stored at a word aligned offset + * length: Reading bytes of length divisible by 4 at a time (BOOTLOADER_FLASH_READ_LEN) + * destination address: Using a word aligned buffer to read the flash contents (bootloader_flash_read_buffer) + */ + #define BOOTLOADER_FLASH_READ_LEN 32 // because it satisfies the above conditions + WORD_ALIGNED_ATTR uint8_t bootloader_flash_read_buffer[BOOTLOADER_FLASH_READ_LEN] = { 0 }; + + size_t block_data_len = block_len / BOOTLOADER_FLASH_READ_LEN * BOOTLOADER_FLASH_READ_LEN; + size_t remaining_data_len = block_len % BOOTLOADER_FLASH_READ_LEN; + + /* Process block data */ + if (block_data_len > 0) { + for (size_t data_processed = 0; data_processed < block_data_len; data_processed += BOOTLOADER_FLASH_READ_LEN) { + ret = esp_partition_read(partition, data_offset + data_processed, bootloader_flash_read_buffer, BOOTLOADER_FLASH_READ_LEN); + if (ret != ESP_OK) { + return ret; + } + memcpy(block + data_processed, bootloader_flash_read_buffer, BOOTLOADER_FLASH_READ_LEN); + } + } + + /* Process remaining data */ + if (remaining_data_len) { + ret = esp_partition_read(partition, data_offset + block_data_len, bootloader_flash_read_buffer, BOOTLOADER_FLASH_READ_LEN); + if (ret != ESP_OK) { + return ret; + } + memcpy(block + block_data_len, bootloader_flash_read_buffer, remaining_data_len); + } + } else { + ret = esp_partition_read(partition, data_offset, block, block_len); + } + return ret; } // validates item's header From 7c3832ff2aba04079e5c7c4031e7647be517bb4d Mon Sep 17 00:00:00 2001 From: "harshal.patil" Date: Fri, 6 Dec 2024 09:07:54 +0530 Subject: [PATCH 04/11] feat(nvs_flash): Add a minimal XTS-AES layer for the bootloader --- components/nvs_flash/CMakeLists.txt | 9 +- .../private_include/nvs_bootloader_aes.h | 23 ++ .../private_include/nvs_bootloader_xts_aes.h | 94 ++++++++ components/nvs_flash/src/nvs_bootloader_aes.c | 43 ++++ .../nvs_flash/src/nvs_bootloader_xts_aes.c | 200 ++++++++++++++++++ 5 files changed, 368 insertions(+), 1 deletion(-) create mode 100644 components/nvs_flash/private_include/nvs_bootloader_aes.h create mode 100644 components/nvs_flash/private_include/nvs_bootloader_xts_aes.h create mode 100644 components/nvs_flash/src/nvs_bootloader_aes.c create mode 100644 components/nvs_flash/src/nvs_bootloader_xts_aes.c diff --git a/components/nvs_flash/CMakeLists.txt b/components/nvs_flash/CMakeLists.txt index 27f66cc9be..6f43b478bb 100644 --- a/components/nvs_flash/CMakeLists.txt +++ b/components/nvs_flash/CMakeLists.txt @@ -1,6 +1,11 @@ if(BOOTLOADER_BUILD) # bootloader build simplified version set(srcs "src/nvs_bootloader.c") + + if(CONFIG_NVS_ENCRYPTION) + list(APPEND srcs "src/nvs_bootloader_aes.c" + "src/nvs_bootloader_xts_aes.c") + endif() set(requires "esp_partition") idf_component_register(SRCS "${srcs}" @@ -52,7 +57,9 @@ else() target_compile_options(${COMPONENT_LIB} PUBLIC --coverage) target_link_libraries(${COMPONENT_LIB} PUBLIC --coverage) else() - target_sources(${COMPONENT_LIB} PRIVATE "src/nvs_encrypted_partition.cpp") + target_sources(${COMPONENT_LIB} PRIVATE "src/nvs_encrypted_partition.cpp" + "src/nvs_bootloader_aes.c" + "src/nvs_bootloader_xts_aes.c") target_link_libraries(${COMPONENT_LIB} PRIVATE idf::mbedtls) endif() diff --git a/components/nvs_flash/private_include/nvs_bootloader_aes.h b/components/nvs_flash/private_include/nvs_bootloader_aes.h new file mode 100644 index 0000000000..be05e9d4eb --- /dev/null +++ b/components/nvs_flash/private_include/nvs_bootloader_aes.h @@ -0,0 +1,23 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#pragma once + +#include +#include "sdkconfig.h" +#include "rom/aes.h" + +#if CONFIG_IDF_TARGET_ESP32 +enum AES_TYPE { + AES_ENC, + AES_DEC, +}; +#endif /* CONFIG_IDF_TARGET_ESP32 */ + +int nvs_bootloader_aes_crypt_ecb(enum AES_TYPE mode, + const unsigned char *key, + enum AES_BITS key_bits, + const unsigned char input[16], + unsigned char output[16]); diff --git a/components/nvs_flash/private_include/nvs_bootloader_xts_aes.h b/components/nvs_flash/private_include/nvs_bootloader_xts_aes.h new file mode 100644 index 0000000000..d1862c370b --- /dev/null +++ b/components/nvs_flash/private_include/nvs_bootloader_xts_aes.h @@ -0,0 +1,94 @@ +/* + * SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#pragma once + +#include +#include "esp_err.h" +#include "nvs_bootloader_aes.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \brief The XTS-AES context-type definition. + */ +typedef struct +{ + uint8_t crypt_key[32]; /*!< The AES context to use for AES block + encryption or decryption. */ + uint8_t tweak_key[32]; /*!< The AES context used for tweak + computation. */ +} nvs_bootloader_xts_aes_context; + +/** Invalid key length. */ +#define NVS_BOOTLOADER_ERR_AES_INVALID_KEY_LENGTH -0x0020 +/** Invalid data input length. */ +#define NVS_BOOTLOADER_ERR_AES_INVALID_INPUT_LENGTH -0x0022 + +/** + * @brief Initializes the specified XTS-AES context. + * + * @param ctx The XTS-AES context to be initialized. This must not be \c NULL. + */ +void nvs_bootloader_xts_aes_init(nvs_bootloader_xts_aes_context *ctx); + +/** + * @brief Clears the specified XTS-AES context. + * + * @param ctx The XTS-AES context to clear. + * If this is \c NULL, this function does nothing. + * Otherwise, the context must have been at least initialized. + */ +void nvs_bootloader_xts_aes_free(nvs_bootloader_xts_aes_context *ctx); + +/** + * @brief Sets the XTS-AES encryption-decryption key + * + * @param ctx The XTS-AES context to which the key should be bound. + * It must be initialized. + * @param key The encryption key. + * This must be a readable buffer of size \p key_bytes bytes. + * @param key_bytes The size of data passed in bits. Valid options are: + *
  • 128 bits
  • + *
  • 192 bits
  • + *
  • 256 bits
+ * @return \c 0 indicating success. + */ +int nvs_bootloader_xts_aes_setkey(nvs_bootloader_xts_aes_context *ctx, + const unsigned char *key, + unsigned int key_bytes); + +/** + * @brief Performs an XTS-AES encryption or decryption operation for an entire XTS data unit. + * + * @param ctx The XTS-AES context to use for XTS-AES operation + * It must be initialized and bound to a key. + * @param mode The AES operation: AES_ENC or AES_DEC. + * @param length The length of a data unit in bytes. + * @param data_unit The address of the data unit encoded as an array of 16 + * bytes in little-endian format. For disk encryption, this + * is typically the index of the block device sector that + * contains the data. + * @param input The buffer holding the input data (which is an entire + * data unit). This function reads \p length Bytes from \p + * input. + * @param output The buffer holding the output data (which is an entire + * data unit). This function writes \p length Bytes to \p + * output. + * @return \c 0 on success. + * 1 on failure. + */ +int nvs_bootloader_aes_crypt_xts(nvs_bootloader_xts_aes_context *ctx, + enum AES_TYPE mode, + size_t length, + const unsigned char data_unit[16], + const unsigned char *input, + unsigned char *output); + +#ifdef __cplusplus +} +#endif diff --git a/components/nvs_flash/src/nvs_bootloader_aes.c b/components/nvs_flash/src/nvs_bootloader_aes.c new file mode 100644 index 0000000000..c6a2e20cf0 --- /dev/null +++ b/components/nvs_flash/src/nvs_bootloader_aes.c @@ -0,0 +1,43 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include +#include +#include "nvs_bootloader_aes.h" +#include "sdkconfig.h" + +int nvs_bootloader_aes_crypt_ecb(enum AES_TYPE mode, + const unsigned char *key, + enum AES_BITS key_bits, + const unsigned char input[16], + unsigned char output[16]) +{ + int ret = -1; + ets_aes_enable(); + +#if CONFIG_IDF_TARGET_ESP32 + if (mode == AES_ENC) { + ret = ets_aes_setkey_enc(key, key_bits); + } else { + ret = ets_aes_setkey_dec(key, key_bits); + } + + if (ret) { + ets_aes_crypt(input, output); + // In case of esp32, ets_aes_setkey_dec returns 1 on success, + // whereas for other targets ets_aes_setkey return 0 on success + ret = 0; + } +#else /* !CONFIG_IDF_TARGET_ESP32m*/ + ret = ets_aes_setkey(mode, key, key_bits); + + if (ret == 0) { + ets_aes_block(input, output); + } +#endif /* CONFIG_IDF_TARGET_ESP32 */ + + ets_aes_disable(); + return ret; +} diff --git a/components/nvs_flash/src/nvs_bootloader_xts_aes.c b/components/nvs_flash/src/nvs_bootloader_xts_aes.c new file mode 100644 index 0000000000..bd7500f537 --- /dev/null +++ b/components/nvs_flash/src/nvs_bootloader_xts_aes.c @@ -0,0 +1,200 @@ +/* + * SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include +#include +#include +#include "esp_err.h" + +#include "nvs_bootloader_aes.h" +#include "nvs_bootloader_xts_aes.h" + +/* + * NOTE: The implementation of the below APIs have been copied + * from the mbedtls (v3.6.2) implementation of the XTS-AES APIs. + */ +void nvs_bootloader_xts_aes_init(nvs_bootloader_xts_aes_context *ctx) +{ + bzero(&ctx->crypt_key, sizeof(ctx->crypt_key)); + bzero(&ctx->tweak_key, sizeof(ctx->tweak_key)); +} + +void nvs_bootloader_xts_aes_free(nvs_bootloader_xts_aes_context *ctx) +{ + if (ctx) { + bzero(&ctx->crypt_key, sizeof(ctx->crypt_key)); + bzero(&ctx->tweak_key, sizeof(ctx->tweak_key)); + } +} + +int nvs_bootloader_xts_aes_setkey(nvs_bootloader_xts_aes_context *ctx, + const unsigned char *key, + unsigned int key_bytes) +{ + size_t xts_key_bytes = key_bytes / 2; + + memcpy(&ctx->crypt_key, key, xts_key_bytes); + memcpy(&ctx->tweak_key, &key[xts_key_bytes], xts_key_bytes); + return 0; +} + +/* Endianness with 64 bits values */ +#ifndef GET_UINT64_LE +#define GET_UINT64_LE(n,b,i) \ +{ \ + (n) = ((uint64_t) (b)[(i) + 7] << 56) \ + | ((uint64_t) (b)[(i) + 6] << 48) \ + | ((uint64_t) (b)[(i) + 5] << 40) \ + | ((uint64_t) (b)[(i) + 4] << 32) \ + | ((uint64_t) (b)[(i) + 3] << 24) \ + | ((uint64_t) (b)[(i) + 2] << 16) \ + | ((uint64_t) (b)[(i) + 1] << 8) \ + | ((uint64_t) (b)[(i) ] ); \ +} +#endif + +#ifndef PUT_UINT64_LE +#define PUT_UINT64_LE(n,b,i) \ +{ \ + (b)[(i) + 7] = (unsigned char) ((n) >> 56); \ + (b)[(i) + 6] = (unsigned char) ((n) >> 48); \ + (b)[(i) + 5] = (unsigned char) ((n) >> 40); \ + (b)[(i) + 4] = (unsigned char) ((n) >> 32); \ + (b)[(i) + 3] = (unsigned char) ((n) >> 24); \ + (b)[(i) + 2] = (unsigned char) ((n) >> 16); \ + (b)[(i) + 1] = (unsigned char) ((n) >> 8); \ + (b)[(i) ] = (unsigned char) ((n) ); \ +} +#endif + +/* + * GF(2^128) multiplication function + * + * This function multiplies a field element by x in the polynomial field + * representation. It uses 64-bit word operations to gain speed but compensates + * for machine endianness and hence works correctly on both big and little + * endian machines. + */ +static void bootloader_gf128mul_x_ble(unsigned char r[16], + const unsigned char x[16]) +{ + uint64_t a, b, ra, rb; + + GET_UINT64_LE(a, x, 0); + GET_UINT64_LE(b, x, 8); + + ra = (a << 1) ^ 0x0087 >> (8 - ((b >> 63) << 3)); + rb = (a >> 63) | (b << 1); + + PUT_UINT64_LE(ra, r, 0); + PUT_UINT64_LE(rb, r, 8); +} + +/* + * XTS-AES buffer encryption/decryption + */ +int nvs_bootloader_aes_crypt_xts(nvs_bootloader_xts_aes_context *ctx, + enum AES_TYPE mode, + size_t length, + const unsigned char data_unit[16], + const unsigned char *input, + unsigned char *output) +{ + int ret; + size_t blocks = length / 16; + size_t leftover = length % 16; + unsigned char tweak[16] = {}; + unsigned char prev_tweak[16] = {}; + unsigned char tmp[16] = {}; + + /* Sectors must be at least 16 bytes. */ + if (length < 16) { + return NVS_BOOTLOADER_ERR_AES_INVALID_INPUT_LENGTH; + } + + /* NIST SP 80-38E disallows data units larger than 2**20 blocks. */ + if (length > ( 1 << 20 ) * 16) { + return NVS_BOOTLOADER_ERR_AES_INVALID_INPUT_LENGTH; + } + + /* Compute the tweak. */ + ret = nvs_bootloader_aes_crypt_ecb(AES_ENC, (const unsigned char *) &ctx->tweak_key, + AES256, data_unit, tweak); + if (ret != 0) { + return (ret); + } + + while (blocks--) { + size_t i; + + if (leftover && (mode == AES_DEC) && blocks == 0) { + /* We are on the last block in a decrypt operation that has + * leftover bytes, so we need to use the next tweak for this block, + * and this tweak for the lefover bytes. Save the current tweak for + * the leftovers and then update the current tweak for use on this, + * the last full block. */ + memcpy(prev_tweak, tweak, sizeof(tweak)); + bootloader_gf128mul_x_ble(tweak, tweak); + } + + for (i = 0; i < 16; i++) { + tmp[i] = input[i] ^ tweak[i]; + } + + ret = nvs_bootloader_aes_crypt_ecb(mode, (const unsigned char *) &ctx->crypt_key, AES256, tmp, tmp); + if (ret != 0) { + return (ret); + } + + for (i = 0; i < 16; i++) { + output[i] = tmp[i] ^ tweak[i]; + } + + /* Update the tweak for the next block. */ + bootloader_gf128mul_x_ble(tweak, tweak); + + output += 16; + input += 16; + } + + if (leftover) { + /* If we are on the leftover bytes in a decrypt operation, we need to + * use the previous tweak for these bytes (as saved in prev_tweak). */ + unsigned char *t = mode == AES_DEC ? prev_tweak : tweak; + + /* We are now on the final part of the data unit, which doesn't divide + * evenly by 16. It's time for ciphertext stealing. */ + size_t i; + unsigned char *prev_output = output - 16; + + /* Copy ciphertext bytes from the previous block to our output for each + * byte of ciphertext we won't steal. At the same time, copy the + * remainder of the input for this final round (since the loop bounds + * are the same). */ + for (i = 0; i < leftover; i++) { + output[i] = prev_output[i]; + tmp[i] = input[i] ^ t[i]; + } + + /* Copy ciphertext bytes from the previous block for input in this + * round. */ + for (; i < 16; i++) { + tmp[i] = prev_output[i] ^ t[i]; + } + + ret = nvs_bootloader_aes_crypt_ecb(mode, (const unsigned char *) &ctx->crypt_key, AES256, tmp, tmp); + if (ret != 0) { + return ret; + } + + /* Write the result back to the previous block, overriding the previous + * output we copied. */ + for (i = 0; i < 16; i++) { + prev_output[i] = tmp[i] ^ t[i]; + } + } + + return 0; +} From 37de702e973ff2d0777bd16292aa19228dc85967 Mon Sep 17 00:00:00 2001 From: "harshal.patil" Date: Wed, 11 Dec 2024 02:35:26 +0530 Subject: [PATCH 05/11] feat(nvs_flash): Enable the usage of mbedtls from ROM for bootloader NVS decryption Bootloader NVS decryption uses hardware ROM APIs to decrypt the NVS contents, but for targets that do not support AES hardware we could benefit by using the software mbedtls library that is present in the ROM directly. --- components/esp_rom/CMakeLists.txt | 7 ++ components/mbedtls/CMakeLists.txt | 13 ++- components/mbedtls/Kconfig | 11 +++ .../port/mbedtls_rom/mbedtls_rom_osi.h | 17 +++- .../mbedtls_rom/mbedtls_rom_osi_bootloader.c | 58 +++++++++++ components/nvs_flash/CMakeLists.txt | 1 + .../private_include/nvs_bootloader_aes.h | 19 +++- components/nvs_flash/src/nvs_bootloader_aes.c | 74 +++++++++++++- .../nvs_flash/src/nvs_bootloader_xts_aes.c | 99 +++++++++++++++++++ 9 files changed, 289 insertions(+), 10 deletions(-) create mode 100644 components/mbedtls/port/mbedtls_rom/mbedtls_rom_osi_bootloader.c diff --git a/components/esp_rom/CMakeLists.txt b/components/esp_rom/CMakeLists.txt index 580a5bfc48..0570c4de84 100644 --- a/components/esp_rom/CMakeLists.txt +++ b/components/esp_rom/CMakeLists.txt @@ -156,6 +156,13 @@ if(BOOTLOADER_BUILD) endif() endif() + if(CONFIG_MBEDTLS_USE_CRYPTO_ROM_IMPL_BOOTLOADER) + rom_linker_script("mbedtls") + # For ESP32C2(ECO4), mbedTLS in ROM has been updated to v3.6.0-LTS + if(CONFIG_ESP32C2_REV_MIN_FULL GREATER_EQUAL 200) + rom_linker_script("mbedtls.eco4") + endif() + endif() else() # Regular app build if(target STREQUAL "esp32") diff --git a/components/mbedtls/CMakeLists.txt b/components/mbedtls/CMakeLists.txt index 8bc72f6179..8662869c89 100644 --- a/components/mbedtls/CMakeLists.txt +++ b/components/mbedtls/CMakeLists.txt @@ -5,6 +5,18 @@ idf_build_get_property(esp_tee_build ESP_TEE_BUILD) if(esp_tee_build) include(${COMPONENT_DIR}/esp_tee/esp_tee_mbedtls.cmake) return() + +elseif(BOOTLOADER_BUILD) # TODO: IDF-11673 + if(CONFIG_MBEDTLS_USE_CRYPTO_ROM_IMPL_BOOTLOADER) + set(include_dirs "${COMPONENT_DIR}/mbedtls/include" + "port/mbedtls_rom") + set(srcs "port/mbedtls_rom/mbedtls_rom_osi_bootloader.c") + endif() + + idf_component_register(SRCS "${srcs}" + INCLUDE_DIRS "${include_dirs}" + PRIV_REQUIRES hal) + return() endif() if(NOT ${IDF_TARGET} STREQUAL "linux") @@ -88,7 +100,6 @@ if(CONFIG_MBEDTLS_CERTIFICATE_BUNDLE) "${crt_bundle}") endif() - # Only build mbedtls libraries set(ENABLE_TESTING CACHE BOOL OFF) set(ENABLE_PROGRAMS CACHE BOOL OFF) diff --git a/components/mbedtls/Kconfig b/components/mbedtls/Kconfig index 7fd3d87ab6..b1edb31fba 100644 --- a/components/mbedtls/Kconfig +++ b/components/mbedtls/Kconfig @@ -1212,6 +1212,17 @@ menu "mbedTLS" Disabling this config can save some code/rodata size as the error string conversion implementation is replaced with an empty stub. + config MBEDTLS_USE_CRYPTO_ROM_IMPL_BOOTLOADER + bool "Use ROM implementation of the crypto algorithm in the bootloader" + depends on ESP_ROM_HAS_MBEDTLS_CRYPTO_LIB + default "n" + select MBEDTLS_AES_C + help + Enable this flag to use mbedtls crypto algorithm from ROM instead of ESP-IDF + in case of a bootloader build. + Similar to the MBEDTLS_USE_CRYPTO_ROM_IMPL config but enables usage of the + mbedtls crypto algorithm from ROM for the bootloader build. + config MBEDTLS_USE_CRYPTO_ROM_IMPL bool "Use ROM implementation of the crypto algorithm" depends on ESP_ROM_HAS_MBEDTLS_CRYPTO_LIB diff --git a/components/mbedtls/port/mbedtls_rom/mbedtls_rom_osi.h b/components/mbedtls/port/mbedtls_rom/mbedtls_rom_osi.h index b612adfa55..52fe58de3e 100644 --- a/components/mbedtls/port/mbedtls_rom/mbedtls_rom_osi.h +++ b/components/mbedtls/port/mbedtls_rom/mbedtls_rom_osi.h @@ -1,11 +1,12 @@ /* - * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ #pragma once +#include #include "mbedtls/aes.h" #include "mbedtls/asn1.h" #include "mbedtls/asn1write.h" @@ -43,6 +44,7 @@ extern "C" { #endif +#ifndef BOOTLOADER_BUILD #if (!defined(CONFIG_MBEDTLS_THREADING_C)) #error CONFIG_MBEDTLS_THREADING_C #endif @@ -51,6 +53,9 @@ typedef void (*_rom_mbedtls_threading_set_alt_t)(void (*mutex_init)(mbedtls_thre void (*mutex_free)(mbedtls_threading_mutex_t *), int (*mutex_lock)(mbedtls_threading_mutex_t *), int (*mutex_unlock)(mbedtls_threading_mutex_t *)); +#else /* BOOTLOADER_BUILD */ +typedef void mbedtls_threading_mutex_t; +#endif /* BOOTLOADER_BUILD */ typedef struct mbedtls_rom_funcs { void (*_rom_mbedtls_aes_init)( mbedtls_aes_context *ctx ); @@ -659,8 +664,8 @@ typedef struct mbedtls_rom_eco4_funcs { #define STRUCT_OFFSET_CHECK(x, y, z) _Static_assert((offsetof(x,y)==(z)), "The variables type of "#x" before "#y" should be "#z) #define STRUCT_SIZE_CHECK(x, y) _Static_assert((sizeof(x)==(y)), "The sizeof "#x" should be "#y) -#if (!defined(CONFIG_MBEDTLS_USE_CRYPTO_ROM_IMPL)) -#error "CONFIG_MBEDTLS_USE_CRYPTO_ROM_IMPL" +#if (!defined(CONFIG_MBEDTLS_USE_CRYPTO_ROM_IMPL) && !defined(CONFIG_MBEDTLS_USE_CRYPTO_ROM_IMPL_BOOTLOADER)) +#error "Please enable CONFIG_MBEDTLS_USE_CRYPTO_ROM_IMPL or CONFIG_MBEDTLS_USE_CRYPTO_ROM_IMPL_BOOTLOADER" #endif /* platform_util.c */ @@ -668,6 +673,7 @@ typedef struct mbedtls_rom_eco4_funcs { #error "MBEDTLS_PLATFORM_ZEROIZE_ALT" #endif +#ifndef BOOTLOADER_BUILD /* sha1.c */ STRUCT_OFFSET_CHECK(mbedtls_sha1_context, total, 0); STRUCT_OFFSET_CHECK(mbedtls_sha1_context, state, 8); @@ -788,6 +794,11 @@ STRUCT_OFFSET_CHECK(mbedtls_md5_context, MBEDTLS_PRIVATE(state), 8); STRUCT_OFFSET_CHECK(mbedtls_md5_context, MBEDTLS_PRIVATE(buffer), 24); STRUCT_SIZE_CHECK(mbedtls_md5_context, 88); #endif +#endif /* BOOTLOADER_BUILD */ + +#if BOOTLOADER_BUILD +void mbedtls_rom_osi_functions_init_bootloader(void); +#endif /* BOOTLOADER_BUILD */ #ifdef __cplusplus } diff --git a/components/mbedtls/port/mbedtls_rom/mbedtls_rom_osi_bootloader.c b/components/mbedtls/port/mbedtls_rom/mbedtls_rom_osi_bootloader.c new file mode 100644 index 0000000000..2c730a9ff7 --- /dev/null +++ b/components/mbedtls/port/mbedtls_rom/mbedtls_rom_osi_bootloader.c @@ -0,0 +1,58 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "soc/chip_revision.h" +#include "hal/efuse_hal.h" +#include "mbedtls_rom_osi.h" + +/* This structure can be automatically generated by the script with rom.mbedtls.ld. */ +static const mbedtls_rom_funcs_t mbedtls_rom_funcs_table = { + /* Fill the ROM functions into mbedtls rom function table. */ + /* aes module */ + ._rom_mbedtls_aes_init = mbedtls_aes_init, + ._rom_mbedtls_aes_free = mbedtls_aes_free, + ._rom_mbedtls_aes_setkey_enc = mbedtls_aes_setkey_enc, + ._rom_mbedtls_aes_setkey_dec = mbedtls_aes_setkey_dec, + ._rom_mbedtls_aes_crypt_ecb = mbedtls_aes_crypt_ecb, + ._rom_mbedtls_aes_crypt_cbc = mbedtls_aes_crypt_cbc, + ._rom_mbedtls_internal_aes_encrypt = mbedtls_internal_aes_encrypt, + ._rom_mbedtls_internal_aes_decrypt = mbedtls_internal_aes_decrypt, +}; + +/* This structure can be automatically generated by the script with rom.mbedtls.ld. */ +static const mbedtls_rom_eco4_funcs_t mbedtls_rom_eco4_funcs_table = { + /* Fill the ROM functions into mbedtls rom function table. */ + /* aes module */ + ._rom_mbedtls_aes_init = mbedtls_aes_init, + ._rom_mbedtls_aes_free = mbedtls_aes_free, + ._rom_mbedtls_aes_setkey_enc = mbedtls_aes_setkey_enc, + ._rom_mbedtls_aes_setkey_dec = mbedtls_aes_setkey_dec, + ._rom_mbedtls_aes_crypt_ecb = mbedtls_aes_crypt_ecb, + ._rom_mbedtls_aes_crypt_cbc = mbedtls_aes_crypt_cbc, + ._rom_mbedtls_internal_aes_encrypt = mbedtls_internal_aes_encrypt, + ._rom_mbedtls_internal_aes_decrypt = mbedtls_internal_aes_decrypt, + + ._rom_mbedtls_aes_xts_init = mbedtls_aes_xts_init, + ._rom_mbedtls_aes_xts_free = mbedtls_aes_xts_free, + ._rom_mbedtls_aes_xts_setkey_enc = mbedtls_aes_xts_setkey_enc, + ._rom_mbedtls_aes_xts_setkey_dec = mbedtls_aes_xts_setkey_dec, + ._rom_mbedtls_aes_crypt_xts = mbedtls_aes_crypt_xts, +}; + +void mbedtls_rom_osi_functions_init_bootloader(void) +{ + // /* Export the rom mbedtls functions table pointer */ + extern void *mbedtls_rom_osi_funcs_ptr; + + unsigned chip_version = efuse_hal_chip_revision(); + if ( ESP_CHIP_REV_ABOVE(chip_version, 200) ) { + /* Initialize the pointer of rom eco4 mbedtls functions table. */ + mbedtls_rom_osi_funcs_ptr = (mbedtls_rom_eco4_funcs_t *)&mbedtls_rom_eco4_funcs_table; + } else { + /* Initialize the pointer of rom mbedtls functions table. */ + mbedtls_rom_osi_funcs_ptr = (mbedtls_rom_funcs_t *)&mbedtls_rom_funcs_table; + } +} diff --git a/components/nvs_flash/CMakeLists.txt b/components/nvs_flash/CMakeLists.txt index 6f43b478bb..281ff63d8d 100644 --- a/components/nvs_flash/CMakeLists.txt +++ b/components/nvs_flash/CMakeLists.txt @@ -10,6 +10,7 @@ if(BOOTLOADER_BUILD) idf_component_register(SRCS "${srcs}" REQUIRES "${requires}" + PRIV_REQUIRES "mbedtls" INCLUDE_DIRS "include" PRIV_INCLUDE_DIRS "private_include" ) diff --git a/components/nvs_flash/private_include/nvs_bootloader_aes.h b/components/nvs_flash/private_include/nvs_bootloader_aes.h index be05e9d4eb..97fc6230be 100644 --- a/components/nvs_flash/private_include/nvs_bootloader_aes.h +++ b/components/nvs_flash/private_include/nvs_bootloader_aes.h @@ -6,18 +6,31 @@ #pragma once #include +#include "soc/soc_caps.h" #include "sdkconfig.h" -#include "rom/aes.h" -#if CONFIG_IDF_TARGET_ESP32 +#if defined(CONFIG_IDF_TARGET_ESP32) || !defined(SOC_AES_SUPPORTED) enum AES_TYPE { AES_ENC, AES_DEC, }; -#endif /* CONFIG_IDF_TARGET_ESP32 */ + +#if !SOC_AES_SUPPORTED +enum AES_BITS { + AES128, + AES192, + AES256 +}; +#endif /* !SOC_AES_SUPPORTED */ +#endif /* CONFIG_IDF_TARGET_ESP32 || !SOC_AES_SUPPORTED */ + +#if SOC_AES_SUPPORTED +#include "rom/aes.h" int nvs_bootloader_aes_crypt_ecb(enum AES_TYPE mode, const unsigned char *key, enum AES_BITS key_bits, const unsigned char input[16], unsigned char output[16]); + +#endif /* SOC_AES_SUPPORTED */ diff --git a/components/nvs_flash/src/nvs_bootloader_aes.c b/components/nvs_flash/src/nvs_bootloader_aes.c index c6a2e20cf0..5cbbab5ad0 100644 --- a/components/nvs_flash/src/nvs_bootloader_aes.c +++ b/components/nvs_flash/src/nvs_bootloader_aes.c @@ -1,13 +1,16 @@ /* - * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ #include #include +#include "esp_log.h" +#include "soc/soc_caps.h" #include "nvs_bootloader_aes.h" #include "sdkconfig.h" +#if SOC_AES_SUPPORTED int nvs_bootloader_aes_crypt_ecb(enum AES_TYPE mode, const unsigned char *key, enum AES_BITS key_bits, @@ -30,14 +33,79 @@ int nvs_bootloader_aes_crypt_ecb(enum AES_TYPE mode, // whereas for other targets ets_aes_setkey return 0 on success ret = 0; } -#else /* !CONFIG_IDF_TARGET_ESP32m*/ +#else /* CONFIG_IDF_TARGET_ESP32 */ ret = ets_aes_setkey(mode, key, key_bits); if (ret == 0) { ets_aes_block(input, output); } -#endif /* CONFIG_IDF_TARGET_ESP32 */ +#endif /* !CONFIG_IDF_TARGET_ESP32 */ ets_aes_disable(); return ret; } + +#else /* SOC_AES_SUPPORTED */ +#if BOOTLOADER_BUILD && !CONFIG_MBEDTLS_USE_CRYPTO_ROM_IMPL_BOOTLOADER +#if CONFIG_ESP_ROM_HAS_MBEDTLS_CRYPTO_LIB +#error "Enable `CONFIG_MBEDTLS_USE_CRYPTO_ROM_IMPL_BOOTLOADER` for non SOC_AES_SUPPORTED targets for supporting NVS encryption in bootloader build" +#else /* !CONFIG_ESP_ROM_HAS_MBEDTLS_CRYPTO_LIB */ +// TODO: IDF-11673 +// Due to unavailability of an software AES layer for bootloader build, +// we cannot support the below NVS bootloader's AES operations +// Thus we are adding stub APIs to indicate that the following operation fail. + +static const char *TAG = "nvs_bootloader_aes"; +static const char *op_unsupported_error = "AES operation in bootloader unsupported for this target"; + +int nvs_bootloader_aes_crypt_ecb(enum AES_TYPE mode, + const unsigned char *key, + enum AES_BITS key_bits, + const unsigned char input[16], + unsigned char output[16]) +{ + ESP_EARLY_LOGE(TAG, "%s", op_unsupported_error); + abort(); + return -1; +} +#endif /* CONFIG_ESP_ROM_HAS_MBEDTLS_CRYPTO_LIB */ +#else /* BOOTLOADER_BUILD && !CONFIG_MBEDTLS_USE_CRYPTO_ROM_IMPL_BOOTLOADER */ +#include "mbedtls/aes.h" + +int nvs_bootloader_aes_crypt_ecb(enum AES_TYPE mode, + const unsigned char *key, + enum AES_BITS key_bits, + const unsigned char input[16], + unsigned char output[16]) +{ + int ret = -1; + + uint16_t keybits = key_bits == AES256 ? 256 : key_bits == AES192 ? 192 : 128; + int mbedtls_aes_mode = mode == AES_ENC ? MBEDTLS_AES_ENCRYPT : MBEDTLS_AES_DECRYPT; + + mbedtls_aes_context ctx; + mbedtls_aes_init(&ctx); + + if (mode == AES_ENC) { + ret = mbedtls_aes_setkey_enc(&ctx, key, keybits); + } else { + ret = mbedtls_aes_setkey_dec(&ctx, key, keybits); + } + + if (ret != 0) { + mbedtls_aes_free(&ctx); + return ret; + } + + ret = mbedtls_aes_crypt_ecb(&ctx, mbedtls_aes_mode, input, output); + + if (ret != 0) { + mbedtls_aes_free(&ctx); + return ret; + } + + mbedtls_aes_free(&ctx); + return ret; +} +#endif /* !(BOOTLOADER_BUILD && !CONFIG_MBEDTLS_USE_CRYPTO_ROM_IMPL_BOOTLOADER) */ +#endif /* !SOC_AES_SUPPORTED */ diff --git a/components/nvs_flash/src/nvs_bootloader_xts_aes.c b/components/nvs_flash/src/nvs_bootloader_xts_aes.c index bd7500f537..ce204f6a9d 100644 --- a/components/nvs_flash/src/nvs_bootloader_xts_aes.c +++ b/components/nvs_flash/src/nvs_bootloader_xts_aes.c @@ -7,10 +7,15 @@ #include #include #include "esp_err.h" +#include "esp_log.h" #include "nvs_bootloader_aes.h" #include "nvs_bootloader_xts_aes.h" +#include "sdkconfig.h" +#include "soc/soc_caps.h" + +#if SOC_AES_SUPPORTED /* * NOTE: The implementation of the below APIs have been copied * from the mbedtls (v3.6.2) implementation of the XTS-AES APIs. @@ -198,3 +203,97 @@ int nvs_bootloader_aes_crypt_xts(nvs_bootloader_xts_aes_context *ctx, return 0; } +#else /* SOC_AES_SUPPORTED */ +#if BOOTLOADER_BUILD && !CONFIG_MBEDTLS_USE_CRYPTO_ROM_IMPL_BOOTLOADER +#if CONFIG_ESP_ROM_HAS_MBEDTLS_CRYPTO_LIB +#error "Enable `CONFIG_MBEDTLS_USE_CRYPTO_ROM_IMPL_BOOTLOADER` for non SOC_AES_SUPPORTED targets for supporting NVS encryption in bootloader build" +#else /* !CONFIG_ESP_ROM_HAS_MBEDTLS_CRYPTO_LIB */ +// TODO: IDF-11673 +// Due to unavailability of an software AES layer for bootloader build, +// we cannot support the below NVS bootloader's AES operations +// Thus we are adding stub APIs to indicate that the following operation fail. + +static const char *TAG = "nvs_bootloader_xts_aes"; +static const char *op_unsupported_error = "XTS-AES operation in bootloader unsupported for this target"; + +void nvs_bootloader_xts_aes_init(nvs_bootloader_xts_aes_context *ctx) +{ + (void) ctx; + ESP_EARLY_LOGE(TAG, "%s", op_unsupported_error); + abort(); +} + +void nvs_bootloader_xts_aes_free(nvs_bootloader_xts_aes_context *ctx) +{ + (void) ctx; + ESP_EARLY_LOGE(TAG, "%s", op_unsupported_error); + abort(); +} + +int nvs_bootloader_xts_aes_setkey(nvs_bootloader_xts_aes_context *ctx, + const unsigned char *key, + unsigned int key_bytes) +{ + (void) ctx; + ESP_EARLY_LOGE(TAG, "%s", op_unsupported_error); + abort(); + return -1; +} +/* + * XTS-AES buffer encryption/decryption + */ +int nvs_bootloader_aes_crypt_xts(nvs_bootloader_xts_aes_context *ctx, + enum AES_TYPE mode, + size_t length, + const unsigned char data_unit[16], + const unsigned char *input, + unsigned char *output) +{ + (void) ctx; + ESP_EARLY_LOGE(TAG, "XTS-AES operation in bootloader unsupported"); + abort(); + return -1; +} + +#endif /* CONFIG_ESP_ROM_HAS_MBEDTLS_CRYPTO_LIB */ +#else /* BOOTLOADER_BUILD && !CONFIG_MBEDTLS_USE_CRYPTO_ROM_IMPL_BOOTLOADER */ +#include "mbedtls/aes.h" + +static mbedtls_aes_xts_context ctx_xts; + +void nvs_bootloader_xts_aes_init(nvs_bootloader_xts_aes_context *ctx) +{ + (void) ctx; + mbedtls_aes_xts_init(&ctx_xts); +} + +void nvs_bootloader_xts_aes_free(nvs_bootloader_xts_aes_context *ctx) +{ + (void) ctx; + mbedtls_aes_xts_free(&ctx_xts); +} + +int nvs_bootloader_xts_aes_setkey(nvs_bootloader_xts_aes_context *ctx, + const unsigned char *key, + unsigned int key_bytes) +{ + (void) ctx; + return mbedtls_aes_xts_setkey_dec(&ctx_xts, key, key_bytes * 8); +} +/* + * XTS-AES buffer encryption/decryption + */ +int nvs_bootloader_aes_crypt_xts(nvs_bootloader_xts_aes_context *ctx, + enum AES_TYPE mode, + size_t length, + const unsigned char data_unit[16], + const unsigned char *input, + unsigned char *output) +{ + (void) ctx; + + int mbedtls_aes_mode = mode == AES_ENC ? MBEDTLS_AES_ENCRYPT : MBEDTLS_AES_DECRYPT; + return mbedtls_aes_crypt_xts(&ctx_xts, mbedtls_aes_mode, length, data_unit, input, output); +} +#endif /* !(BOOTLOADER_BUILD && !CONFIG_MBEDTLS_USE_CRYPTO_ROM_IMPL_BOOTLOADER) */ +#endif /* !SOC_AES_SUPPORTED */ From cadd6b8bcc1418b793ca568999e05d045090e148 Mon Sep 17 00:00:00 2001 From: "harshal.patil" Date: Mon, 30 Dec 2024 19:01:22 +0530 Subject: [PATCH 06/11] feat(nvs_flash): Support reading encrypted NVS partitions in the bootloader --- components/nvs_flash/CMakeLists.txt | 8 +- components/nvs_flash/include/nvs_bootloader.h | 34 +- .../private_include/nvs_bootloader_private.h | 4 +- components/nvs_flash/src/nvs_bootloader.c | 317 ++++++++++++------ components/nvs_sec_provider/CMakeLists.txt | 8 +- .../private/nvs_sec_provider_private.h | 8 + .../nvs_bootloader_sec_provider.c | 212 ++++++++++++ .../nvs_sec_provider/nvs_sec_provider.c | 7 +- .../api-reference/storage/nvs_bootloader.rst | 19 ++ 9 files changed, 499 insertions(+), 118 deletions(-) create mode 100644 components/nvs_sec_provider/include/private/nvs_sec_provider_private.h create mode 100644 components/nvs_sec_provider/nvs_bootloader_sec_provider.c diff --git a/components/nvs_flash/CMakeLists.txt b/components/nvs_flash/CMakeLists.txt index 281ff63d8d..eb8057afe2 100644 --- a/components/nvs_flash/CMakeLists.txt +++ b/components/nvs_flash/CMakeLists.txt @@ -1,11 +1,9 @@ if(BOOTLOADER_BUILD) # bootloader build simplified version - set(srcs "src/nvs_bootloader.c") + set(srcs "src/nvs_bootloader.c" + "src/nvs_bootloader_aes.c" + "src/nvs_bootloader_xts_aes.c") - if(CONFIG_NVS_ENCRYPTION) - list(APPEND srcs "src/nvs_bootloader_aes.c" - "src/nvs_bootloader_xts_aes.c") - endif() set(requires "esp_partition") idf_component_register(SRCS "${srcs}" diff --git a/components/nvs_flash/include/nvs_bootloader.h b/components/nvs_flash/include/nvs_bootloader.h index cdb11fb3f2..162319cfd6 100644 --- a/components/nvs_flash/include/nvs_bootloader.h +++ b/components/nvs_flash/include/nvs_bootloader.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -8,6 +8,7 @@ #include #include "esp_err.h" // esp_err_t #include "nvs.h" // nvs entry data types +#include "nvs_flash.h" // nvs_sec_cfg_t #ifdef __cplusplus extern "C" { @@ -104,6 +105,37 @@ esp_err_t nvs_bootloader_read(const char* partition_name, const size_t read_list_count, nvs_bootloader_read_list_t read_list[]); +/** + * @brief Initialize internal NVS security context, thus, enabling the NVS bootloader read API to decrypt encrypted NVS partitions + * + * @note Once `nvs_bootloader_secure_init()` is performed, `nvs_bootloader_read()` can correctly read only those NVS partitions + * that are encrypted using the given `nvs_sec_cfg_t` security config, until `nvs_bootloader_secure_deinit()` clears the internal + * NVS security context. + * + * @param sec_cfg NVS security key that would be used for decrypting the NVS partition + * @return ESP_OK if security initialization is successful + */ +esp_err_t nvs_bootloader_secure_init(const nvs_sec_cfg_t *sec_cfg); + +/** + * @brief Clear the internal NVS security context + */ +void nvs_bootloader_secure_deinit(void); + +/** + * @brief Reads NVS bootloader security configuration set by the specified security scheme + * + * @param[in] scheme_cfg Security scheme specific configuration + * + * @param[out] cfg Security configuration (encryption keys) + * + * @return + * - ESP_OK, if cfg was read successfully; + * - ESP_ERR_INVALID_ARG, if scheme_cfg or cfg is NULL; + * - ESP_FAIL, if the key reading process fails + */ +esp_err_t nvs_bootloader_read_security_cfg(nvs_sec_scheme_t *scheme_cfg, nvs_sec_cfg_t* cfg); + #ifdef __cplusplus } #endif diff --git a/components/nvs_flash/private_include/nvs_bootloader_private.h b/components/nvs_flash/private_include/nvs_bootloader_private.h index 5b4929ffe0..5be089e622 100644 --- a/components/nvs_flash/private_include/nvs_bootloader_private.h +++ b/components/nvs_flash/private_include/nvs_bootloader_private.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -9,6 +9,8 @@ #include "nvs_constants.h" // NVS_CONST_ENTRY_SIZE and all size related constants shared with cpp implementation of NVS #include "nvs_bootloader.h" // nvs_bootloader_read_list_t and function prototypes #include "esp_partition.h" // esp_partition_t +#include "nvs_flash.h" // nvs_sec_cfg_t +#include "nvs_bootloader_xts_aes.h" // nvs_bootloader_xts_aes_context #ifdef __cplusplus extern "C" { diff --git a/components/nvs_flash/src/nvs_bootloader.c b/components/nvs_flash/src/nvs_bootloader.c index d6eca44a25..9347cfd3ba 100644 --- a/components/nvs_flash/src/nvs_bootloader.c +++ b/components/nvs_flash/src/nvs_bootloader.c @@ -9,95 +9,115 @@ #include "nvs_bootloader.h" #include "nvs_bootloader_private.h" #include "esp_assert.h" +#include "sdkconfig.h" #include "esp_partition.h" #include "nvs_constants.h" #include +#if CONFIG_MBEDTLS_USE_CRYPTO_ROM_IMPL_BOOTLOADER && BOOTLOADER_BUILD +#include "mbedtls_rom_osi.h" +#endif + static const char* TAG = "nvs_bootloader"; -const bool const_is_encrypted = false; // Static asserts ensuring that the size of the c structures match NVS physical footprint on the flash ESP_STATIC_ASSERT(sizeof(nvs_bootloader_page_header_t) == NVS_CONST_ENTRY_SIZE, "nvs_bootloader_page_header_t size is not 32 bytes"); ESP_STATIC_ASSERT(sizeof(nvs_bootloader_page_entry_states_t) == NVS_CONST_ENTRY_SIZE, "nvs_bootloader_page_entry_states_t size is not 32 bytes"); ESP_STATIC_ASSERT(sizeof(nvs_bootloader_single_entry_t) == NVS_CONST_ENTRY_SIZE, "nvs_bootloader_single_entry_t size is not 32 bytes"); -esp_err_t nvs_bootloader_read(const char* partition_name, - const size_t read_list_count, - nvs_bootloader_read_list_t read_list[]) +#define NVS_KEY_SIZE 32 // AES-256 + +/* Currently we support only single-threaded use-cases of reading encrypted NVS partitions */ +static nvs_bootloader_xts_aes_context dec_ctx; +static bool is_nvs_partition_encrypted = false; + +static esp_err_t decrpyt_data(uint32_t src_offset, void* dst, size_t size) { + // decrypt data + // sector num, could have been just uint64/32. + uint8_t data_unit[16]; + uint8_t *destination = (uint8_t *)dst; + uint32_t relAddr = src_offset; + + memset(data_unit, 0, sizeof(data_unit)); + memcpy(data_unit, &relAddr, sizeof(relAddr)); + + return nvs_bootloader_aes_crypt_xts(&dec_ctx, AES_DEC, size, data_unit, destination, destination); +} + +static esp_err_t nvs_bootloader_partition_read_string_value(const esp_partition_t *partition, size_t src_offset, void *block, size_t block_len) +{ + esp_err_t ret = ESP_FAIL; + + /* For the bootloader build, the esp_partition_read() API internally is calls bootloader_flash_read() that + * requires the src_address, length and the destination address to be word aligned. + * src_address: NVS keys and values are always stored at a word aligned offset + * length: Reading bytes of length divisible by 4 at a time (BOOTLOADER_FLASH_READ_LEN) + * destination address: Using a word aligned buffer to read the flash contents (bootloader_flash_read_buffer) + */ + #define BOOTLOADER_FLASH_READ_LEN 32 // because it matches the below discussed XTS-AES requirements as well /* - The flow: + * When reading an encrypted partition, we implement the splitting method, that is, we read and decrypt data size in the multiples of 16. + * This is necessary because of a bug present in the mbedtls_aes_crypt_xts() function wherein "inplace" encryption/decryption + * calculation fails if the length of buffers is not a multiple of 16. + * Reference: https://github.com/Mbed-TLS/mbedtls/issues/4302 + * + * Thus, in this case we first operate over the aligned down to 16 length of data and then over the remaining data, by copying + * it to a buffer of size 16. + * + * Also, until https://github.com/Mbed-TLS/mbedtls/issues/9827 is resolved, we need to operate over chunks of length 32. + */ + #define XTS_AES_PROCESS_BLOCK_LEN 32 - 1. validate parameters and if there are any, report errors. - 2. check if the partition exists - 3. read the partition and browse all NVS pages to figure out whether there is any page in the state of "FREEING" - 3.1. if there is a page in the state of "FREEING", then reading from the page marked as "ACTIVE" will be skipped - 4. read entries from pages marked as "FULL" or ("ACTIVE" xor "FREEING") to identify namespace indexes of the requested namespaces - 5. read the requested entries, same skipping of pages in the state of "ACTIVE" as in step 4 + if (src_offset & 3 || block_len & 3 || (intptr_t) block & 3 + || is_nvs_partition_encrypted + ) { + WORD_ALIGNED_ATTR uint8_t bootloader_flash_read_buffer[BOOTLOADER_FLASH_READ_LEN] = { 0 }; - */ - // load input parameters - ESP_LOGD(TAG, "nvs_bootloader_read called with partition_name: %s, read_list_count: %u", partition_name, (unsigned)read_list_count); + size_t block_data_len = block_len / BOOTLOADER_FLASH_READ_LEN * BOOTLOADER_FLASH_READ_LEN; + size_t remaining_data_len = block_len % BOOTLOADER_FLASH_READ_LEN; - // Placeholder return value, replace with actual error handling - esp_err_t ret = ESP_OK; + /* Process block data */ + if (block_data_len > 0) { + for (size_t data_processed = 0; data_processed < block_data_len; data_processed += BOOTLOADER_FLASH_READ_LEN) { + ret = esp_partition_read(partition, src_offset + data_processed, bootloader_flash_read_buffer, BOOTLOADER_FLASH_READ_LEN); + if (ret != ESP_OK) { + return ret; + } - // Check if the parameters are valid - ret = nvs_bootloader_check_parameters(partition_name, read_list_count, read_list); - if (ret != ESP_OK) { - ESP_LOGD(TAG, "nvs_bootloader_check_parameters failed"); - return ret; + if (is_nvs_partition_encrypted) { + ret = decrpyt_data(src_offset + data_processed, bootloader_flash_read_buffer, BOOTLOADER_FLASH_READ_LEN); + if (ret != ESP_OK) { + return ret; + } + } + + memcpy(block + data_processed, bootloader_flash_read_buffer, BOOTLOADER_FLASH_READ_LEN); + } + } + + /* Process remaining data */ + if (remaining_data_len) { + ret = esp_partition_read(partition, src_offset + block_data_len, bootloader_flash_read_buffer, BOOTLOADER_FLASH_READ_LEN); + if (ret != ESP_OK) { + return ret; + } + + if (is_nvs_partition_encrypted) { + ret = decrpyt_data(src_offset + block_data_len, bootloader_flash_read_buffer, BOOTLOADER_FLASH_READ_LEN); + if (ret != ESP_OK) { + return ret; + } + } + + memcpy(block + block_data_len, bootloader_flash_read_buffer, remaining_data_len); + } + } else { + ret = esp_partition_read(partition, src_offset, block, block_len); } - const esp_partition_t *partition = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_NVS, partition_name); - if (partition == NULL) { - ESP_LOGV(TAG, "esp_partition_find_first failed"); - return ESP_ERR_NVS_PART_NOT_FOUND; - } - - // log the partition details - ESP_LOGV(TAG, "Partition %s found, size is: %" PRIu32 "", partition->label, partition->size); - - // visit pages to get the number of pages in the state of "ACTIVE" and "FREEING" - nvs_bootloader_page_visitor_param_get_page_states_t page_states_count = {0}; - - ret = nvs_bootloader_visit_pages(partition, nvs_bootloader_page_visitor_get_page_states, (nvs_bootloader_page_visitor_param_t*)&page_states_count); - - if (ret != ESP_OK) { - ESP_LOGV(TAG, "Failed reading page states"); - return ret; - } - if (page_states_count.no_freeing_pages > 1) { - ESP_LOGV(TAG, "Multiple pages in the state of FREEING"); - return ESP_ERR_INVALID_STATE; - } - if (page_states_count.no_active_pages > 1) { - ESP_LOGV(TAG, "Multiple pages in the state of ACTIVE"); - return ESP_ERR_INVALID_STATE; - } - - // Here we have at most 1 active page and at most 1 freeing page. If there exists a freeing page, we will skip reading from the active page - // Visit pages to get the namespace indexes of the requested namespaces - nvs_bootloader_page_visitor_param_read_entries_t read_entries = { .skip_active_page = page_states_count.no_freeing_pages > 0, .read_list_count = read_list_count, .read_list = read_list }; - - // log the visitor parameters - ESP_LOGV(TAG, "nvs_bootloader_read - visiting pages to get namespace indexes"); - - ret = nvs_bootloader_visit_pages(partition, nvs_bootloader_page_visitor_get_namespaces, (nvs_bootloader_page_visitor_param_t*) &read_entries); - - if (ret != ESP_OK) { - ESP_LOGV(TAG, "nvs_bootloader_page_visitor_get_namespaces failed"); - return ret; - } - - // log the visitor parameters - ESP_LOGV(TAG, "nvs_bootloader_read - visiting pages to get key - value pairs"); - - // Visit pages to read the requested key - value pairs - ret = nvs_bootloader_visit_pages(partition, nvs_bootloader_page_visitor_get_key_value_pairs, (nvs_bootloader_page_visitor_param_t*) &read_entries); - return ret; } @@ -446,7 +466,6 @@ esp_err_t nvs_bootloader_read_next_single_entry_item(const esp_partition_t *part uint8_t *entry_index, nvs_bootloader_single_entry_t *item) { - // log parameters ESP_LOGV(TAG, "nvs_bootloader_read_next_single_entry_item called with page_index: %u, entry_index: %d", (unsigned)page_index, *entry_index); @@ -463,11 +482,17 @@ esp_err_t nvs_bootloader_read_next_single_entry_item(const esp_partition_t *part if (NVS_BOOTLOADER_GET_ENTRY_STATE(page_entry_states, *entry_index) == NVS_CONST_ENTRY_STATE_WRITTEN) { ret = esp_partition_read(partition, page_index * NVS_CONST_PAGE_SIZE + NVS_CONST_PAGE_ENTRY_DATA_OFFSET + (*entry_index) * NVS_CONST_ENTRY_SIZE, (void*)item, sizeof(nvs_bootloader_single_entry_t)); - if (ret != ESP_OK) { return ret; } + if (is_nvs_partition_encrypted) { + ret = decrpyt_data(page_index * NVS_CONST_PAGE_SIZE + NVS_CONST_PAGE_ENTRY_DATA_OFFSET + (*entry_index) * NVS_CONST_ENTRY_SIZE, (void*)item, sizeof(nvs_bootloader_single_entry_t)); + if (ret != ESP_OK) { + return ret; + } + } + // only look at item with consistent header if (nvs_bootloader_check_item_header_consistency(item, *entry_index)) { // advance the start index @@ -544,44 +569,9 @@ esp_err_t nvs_bootloader_read_entries_block(const esp_partition_t *partition, return ret; } - size_t data_offset = page_index * NVS_CONST_PAGE_SIZE + NVS_CONST_PAGE_ENTRY_DATA_OFFSET + entry_index * NVS_CONST_ENTRY_SIZE ; + size_t data_offset = page_index * NVS_CONST_PAGE_SIZE + NVS_CONST_PAGE_ENTRY_DATA_OFFSET + entry_index * NVS_CONST_ENTRY_SIZE; - if (data_offset & 3 || block_len & 3 || (intptr_t) block & 3) { - /* For the bootloader build, the esp_partition_read() API internally is calls bootloader_flash_read() that - * requires the src_address, length and the destination address to be word aligned. - * src_address: NVS keys and values are always stored at a word aligned offset - * length: Reading bytes of length divisible by 4 at a time (BOOTLOADER_FLASH_READ_LEN) - * destination address: Using a word aligned buffer to read the flash contents (bootloader_flash_read_buffer) - */ - #define BOOTLOADER_FLASH_READ_LEN 32 // because it satisfies the above conditions - WORD_ALIGNED_ATTR uint8_t bootloader_flash_read_buffer[BOOTLOADER_FLASH_READ_LEN] = { 0 }; - - size_t block_data_len = block_len / BOOTLOADER_FLASH_READ_LEN * BOOTLOADER_FLASH_READ_LEN; - size_t remaining_data_len = block_len % BOOTLOADER_FLASH_READ_LEN; - - /* Process block data */ - if (block_data_len > 0) { - for (size_t data_processed = 0; data_processed < block_data_len; data_processed += BOOTLOADER_FLASH_READ_LEN) { - ret = esp_partition_read(partition, data_offset + data_processed, bootloader_flash_read_buffer, BOOTLOADER_FLASH_READ_LEN); - if (ret != ESP_OK) { - return ret; - } - memcpy(block + data_processed, bootloader_flash_read_buffer, BOOTLOADER_FLASH_READ_LEN); - } - } - - /* Process remaining data */ - if (remaining_data_len) { - ret = esp_partition_read(partition, data_offset + block_data_len, bootloader_flash_read_buffer, BOOTLOADER_FLASH_READ_LEN); - if (ret != ESP_OK) { - return ret; - } - memcpy(block + block_data_len, bootloader_flash_read_buffer, remaining_data_len); - } - } else { - ret = esp_partition_read(partition, data_offset, block, block_len); - } - return ret; + return nvs_bootloader_partition_read_string_value(partition, data_offset, block, block_len); } // validates item's header @@ -637,3 +627,116 @@ bool nvs_bootloader_check_item_header_consistency(const nvs_bootloader_single_en return ret; } + +esp_err_t nvs_bootloader_read(const char* partition_name, + const size_t read_list_count, + nvs_bootloader_read_list_t read_list[]) +{ + /* + The flow: + + 1. validate parameters and if there are any, report errors. + 2. check if the partition exists + 3. read the partition and browse all NVS pages to figure out whether there is any page in the state of "FREEING" + 3.1. if there is a page in the state of "FREEING", then reading from the page marked as "ACTIVE" will be skipped + 4. read entries from pages marked as "FULL" or ("ACTIVE" xor "FREEING") to identify namespace indexes of the requested namespaces + 5. read the requested entries, same skipping of pages in the state of "ACTIVE" as in step 4 + + */ + // load input parameters + ESP_LOGD(TAG, "nvs_bootloader_read called with partition_name: %s, read_list_count: %u", partition_name, (unsigned)read_list_count); + + // Placeholder return value, replace with actual error handling + esp_err_t ret = ESP_OK; + + // Check if the parameters are valid + ret = nvs_bootloader_check_parameters(partition_name, read_list_count, read_list); + if (ret != ESP_OK) { + ESP_LOGD(TAG, "nvs_bootloader_check_parameters failed"); + return ret; + } + + const esp_partition_t *partition = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_NVS, partition_name); + if (partition == NULL) { + ESP_LOGV(TAG, "esp_partition_find_first failed"); + return ESP_ERR_NVS_PART_NOT_FOUND; + } + + // log the partition details + ESP_LOGV(TAG, "Partition %s found, size is: %" PRIu32 "", partition->label, partition->size); + + // visit pages to get the number of pages in the state of "ACTIVE" and "FREEING" + nvs_bootloader_page_visitor_param_get_page_states_t page_states_count = {0}; + + ret = nvs_bootloader_visit_pages(partition, nvs_bootloader_page_visitor_get_page_states, (nvs_bootloader_page_visitor_param_t*)&page_states_count); + + if (ret != ESP_OK) { + ESP_LOGV(TAG, "Failed reading page states"); + return ret; + } + if (page_states_count.no_freeing_pages > 1) { + ESP_LOGV(TAG, "Multiple pages in the state of FREEING"); + return ESP_ERR_INVALID_STATE; + } + if (page_states_count.no_active_pages > 1) { + ESP_LOGV(TAG, "Multiple pages in the state of ACTIVE"); + return ESP_ERR_INVALID_STATE; + } + + // Here we have at most 1 active page and at most 1 freeing page. If there exists a freeing page, we will skip reading from the active page + // Visit pages to get the namespace indexes of the requested namespaces + nvs_bootloader_page_visitor_param_read_entries_t read_entries = { .skip_active_page = page_states_count.no_freeing_pages > 0, .read_list_count = read_list_count, .read_list = read_list }; + + // log the visitor parameters + ESP_LOGV(TAG, "nvs_bootloader_read - visiting pages to get namespace indexes"); + + ret = nvs_bootloader_visit_pages(partition, nvs_bootloader_page_visitor_get_namespaces, (nvs_bootloader_page_visitor_param_t*) &read_entries); + + if (ret != ESP_OK) { + ESP_LOGV(TAG, "nvs_bootloader_page_visitor_get_namespaces failed"); + return ret; + } + + // log the visitor parameters + ESP_LOGV(TAG, "nvs_bootloader_read - visiting pages to get key - value pairs"); + + // Visit pages to read the requested key - value pairs + ret = nvs_bootloader_visit_pages(partition, nvs_bootloader_page_visitor_get_key_value_pairs, (nvs_bootloader_page_visitor_param_t*) &read_entries); + + return ret; +} + +esp_err_t nvs_bootloader_secure_init(const nvs_sec_cfg_t *sec_cfg) +{ +#if CONFIG_MBEDTLS_USE_CRYPTO_ROM_IMPL_BOOTLOADER && BOOTLOADER_BUILD + // To enable usage of mbedtls APIs form the ROM, we need to initialize the rom mbedtls functions table pointer. + // In case of application, a constructor present in the mbedtls component initializes the rom mbedtls functions table pointer during boot up. + mbedtls_rom_osi_functions_init_bootloader(); +#endif /* CONFIG_MBEDTLS_USE_CRYPTO_ROM_IMPL_BOOTLOADER && BOOTLOADER_BUILD */ + + nvs_bootloader_xts_aes_init(&dec_ctx); + + esp_err_t ret = nvs_bootloader_xts_aes_setkey(&dec_ctx, (const uint8_t*) sec_cfg, 2 * NVS_KEY_SIZE); + if (ret != ESP_OK) { + return ret; + } + + is_nvs_partition_encrypted = true; + return ret; +} + +void nvs_bootloader_secure_deinit(void) +{ + if (is_nvs_partition_encrypted) { + nvs_bootloader_xts_aes_free(&dec_ctx); + is_nvs_partition_encrypted = false; + } +} + +esp_err_t nvs_bootloader_read_security_cfg(nvs_sec_scheme_t *scheme_cfg, nvs_sec_cfg_t* cfg) +{ + if (scheme_cfg == NULL || cfg == NULL || scheme_cfg->nvs_flash_read_cfg == NULL) { + return ESP_ERR_INVALID_ARG; + } + return (scheme_cfg->nvs_flash_read_cfg)(scheme_cfg->scheme_data, cfg); +} diff --git a/components/nvs_sec_provider/CMakeLists.txt b/components/nvs_sec_provider/CMakeLists.txt index 7fdcc9e81f..9de023e3d3 100644 --- a/components/nvs_sec_provider/CMakeLists.txt +++ b/components/nvs_sec_provider/CMakeLists.txt @@ -4,7 +4,13 @@ if(${target} STREQUAL "linux") return() # This component is not supported by the POSIX/Linux simulator endif() -idf_component_register(SRCS "nvs_sec_provider.c" +if(BOOTLOADER_BUILD) + set(srcs "nvs_bootloader_sec_provider.c") +else() + set(srcs "nvs_sec_provider.c") +endif() + +idf_component_register(SRCS ${srcs} INCLUDE_DIRS include PRIV_REQUIRES bootloader_support efuse esp_partition nvs_flash) diff --git a/components/nvs_sec_provider/include/private/nvs_sec_provider_private.h b/components/nvs_sec_provider/include/private/nvs_sec_provider_private.h new file mode 100644 index 0000000000..20d2cfcf08 --- /dev/null +++ b/components/nvs_sec_provider/include/private/nvs_sec_provider_private.h @@ -0,0 +1,8 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define EKEY_SEED 0xAEBE5A5A +#define TKEY_SEED 0xCEDEA5A5 diff --git a/components/nvs_sec_provider/nvs_bootloader_sec_provider.c b/components/nvs_sec_provider/nvs_bootloader_sec_provider.c new file mode 100644 index 0000000000..052abda446 --- /dev/null +++ b/components/nvs_sec_provider/nvs_bootloader_sec_provider.c @@ -0,0 +1,212 @@ +/* + * SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include "esp_err.h" +#include "esp_log.h" +#include "soc/soc_caps.h" +#include "sdkconfig.h" +#include "esp_partition.h" +#include "nvs_flash.h" +#include "nvs_sec_provider.h" +#include "esp_rom_crc.h" +#include "private/nvs_sec_provider_private.h" + +#if SOC_HMAC_SUPPORTED +#include "rom/efuse.h" +#include "rom/hmac.h" +#endif // SOC_HMAC_SUPPORTED + +static __attribute__((unused)) const char *TAG = "nvs_bootloader_sec_provider"; + +static __attribute__((unused)) nvs_sec_scheme_t sec_scheme; + +#if CONFIG_NVS_SEC_KEY_PROTECT_USING_FLASH_ENC + +static nvs_sec_config_flash_enc_t nvs_sec_config_flash_enc_scheme_data; + +static esp_err_t nvs_bootloader_read_security_cfg(const esp_partition_t* partition, nvs_sec_cfg_t* cfg) +{ + if (cfg == NULL || partition == NULL) { + return ESP_ERR_INVALID_ARG; + } + + uint32_t crc_read, crc_calc; + + esp_err_t err = esp_partition_read(partition, 0, cfg->eky, NVS_KEY_SIZE); + if (err != ESP_OK) { + return err; + } + + err = esp_partition_read(partition, NVS_KEY_SIZE, cfg->tky, NVS_KEY_SIZE); + if (err != ESP_OK) { + return err; + } + + err = esp_partition_read(partition, 2 * NVS_KEY_SIZE, &crc_read, 4); + if (err != ESP_OK) { + return err; + } + + crc_calc = esp_rom_crc32_le(0xffffffff, cfg->eky, NVS_KEY_SIZE); + crc_calc = esp_rom_crc32_le(crc_calc, cfg->tky, NVS_KEY_SIZE); + + if (crc_calc != crc_read) { + return ESP_ERR_NVS_CORRUPT_KEY_PART; + } + + return ESP_OK; +} + +static esp_err_t read_security_cfg_flash_enc(const void* sec_scheme_cfg, nvs_sec_cfg_t* cfg) +{ + if (sec_scheme_cfg == NULL || cfg == NULL) { + return ESP_ERR_INVALID_ARG; + } + + nvs_sec_config_flash_enc_t *scheme_cfg_flash_enc = (nvs_sec_config_flash_enc_t *)sec_scheme_cfg; + return nvs_bootloader_read_security_cfg(scheme_cfg_flash_enc->nvs_keys_part, cfg); +} + +esp_err_t nvs_sec_provider_register_flash_enc(const nvs_sec_config_flash_enc_t *sec_scheme_cfg, nvs_sec_scheme_t **sec_scheme_handle_out) +{ + if (sec_scheme_cfg == NULL || sec_scheme_handle_out == NULL) { + return ESP_ERR_INVALID_ARG; + } + + bzero(&sec_scheme, sizeof(nvs_sec_scheme_t)); + + sec_scheme.scheme_id = NVS_SEC_SCHEME_FLASH_ENC; + sec_scheme.nvs_flash_read_cfg = &read_security_cfg_flash_enc; + + bzero(&nvs_sec_config_flash_enc_scheme_data, sizeof(nvs_sec_config_flash_enc_t)); + sec_scheme.scheme_data = &nvs_sec_config_flash_enc_scheme_data; + + memcpy(sec_scheme.scheme_data, (void *)sec_scheme_cfg, sizeof(nvs_sec_config_flash_enc_t)); + + *sec_scheme_handle_out = &sec_scheme; + return ESP_OK; +} +#endif + +#if SOC_HMAC_SUPPORTED + +static nvs_sec_config_hmac_t nvs_sec_config_hmac_scheme_data; + +#if CONFIG_NVS_SEC_HMAC_EFUSE_KEY_ID > 5 +#error "NVS Encryption (HMAC): Configured eFuse block (CONFIG_NVS_SEC_HMAC_EFUSE_KEY_ID) out of range!" +#endif + +static esp_err_t nvs_bootloader_hmac_calculate(ets_efuse_block_t hmac_key, + const void *message, + size_t message_len, + uint8_t *hmac) +{ + ets_hmac_enable(); + + int hmac_ret = ets_hmac_calculate_message(hmac_key, message, message_len, hmac); + + ets_hmac_disable(); + + if (hmac_ret != 0) { + return ESP_FAIL; + } else { + return ESP_OK; + } +} + +static esp_err_t compute_nvs_keys_with_hmac(ets_efuse_block_t hmac_key, nvs_sec_cfg_t* cfg) +{ + uint32_t ekey_seed[8] = {[0 ... 7] = EKEY_SEED}; + uint32_t tkey_seed[8] = {[0 ... 7] = TKEY_SEED}; + + esp_err_t err = nvs_bootloader_hmac_calculate(hmac_key, ekey_seed, sizeof(ekey_seed), (uint8_t *)cfg->eky); + if (err != ESP_OK) { + ESP_LOGD(TAG, "Failed to calculate seed HMAC: [0x%02X]", err); + return err; + } + + err = nvs_bootloader_hmac_calculate(hmac_key, tkey_seed, sizeof(tkey_seed), (uint8_t *)cfg->tky); + if (err != ESP_OK) { + ESP_LOGD(TAG, "Failed to calculate seed HMAC: [0x%02X]", err); + return err; + } + + /* NOTE: If the XTS E-key and T-key are the same, we have a hash collision */ + if (memcmp(cfg->eky, cfg->tky, NVS_KEY_SIZE) == 0) { + ESP_LOGD(TAG, "The XTS-AES E-key and T-key are the same, we have a hash collision"); + return ESP_FAIL; + } + + return ESP_OK; +} + +static bool is_hmac_key_burnt_in_efuse(ets_efuse_block_t hmac_key_blk) +{ + + ets_efuse_purpose_t hmac_efuse_blk_purpose = ets_efuse_get_key_purpose(hmac_key_blk); + + if (hmac_efuse_blk_purpose == ETS_EFUSE_KEY_PURPOSE_HMAC_UP) { + return true; + } + + return false; +} + + +static ets_efuse_block_t convert_key_type(hmac_key_id_t key_id) { + return (ets_efuse_block_t)(ETS_EFUSE_BLOCK_KEY0 + (ets_efuse_block_t) key_id); +} + +static esp_err_t read_security_cfg_hmac(const void* scheme_cfg, nvs_sec_cfg_t* cfg) +{ + if (cfg == NULL) { + return ESP_ERR_INVALID_ARG; + } + + nvs_sec_config_hmac_t *scheme_cfg_hmac = (nvs_sec_config_hmac_t *)scheme_cfg; + + if (scheme_cfg_hmac->hmac_key_id >= HMAC_KEY_MAX) { + ESP_LOGD(TAG, "Invalid HMAC key ID received!"); + return ESP_ERR_INVALID_ARG; + } + + ets_efuse_block_t hmac_key = convert_key_type(scheme_cfg_hmac->hmac_key_id); + + if (!is_hmac_key_burnt_in_efuse(hmac_key)) { + ESP_LOGD(TAG, "Could not find HMAC key in configured eFuse block!"); + return ESP_ERR_NVS_SEC_HMAC_KEY_NOT_FOUND; + } + + if (compute_nvs_keys_with_hmac(hmac_key, cfg) != ESP_OK) { + ESP_LOGD(TAG, "Failed to derive the encryption keys using HMAC!"); + return ESP_ERR_NVS_SEC_HMAC_XTS_KEYS_DERIV_FAILED; + } + + return ESP_OK; +} + +esp_err_t nvs_sec_provider_register_hmac(const nvs_sec_config_hmac_t *sec_scheme_cfg, nvs_sec_scheme_t **sec_scheme_handle_out) +{ + if (sec_scheme_cfg == NULL || sec_scheme_handle_out == NULL) { + return ESP_ERR_INVALID_ARG; + } + + bzero(&sec_scheme, sizeof(nvs_sec_scheme_t)); + + sec_scheme.scheme_id = NVS_SEC_SCHEME_HMAC; + sec_scheme.nvs_flash_read_cfg = &read_security_cfg_hmac; + + bzero(&nvs_sec_config_hmac_scheme_data, sizeof(nvs_sec_config_hmac_t)); + sec_scheme.scheme_data = &nvs_sec_config_hmac_scheme_data; + + memcpy(sec_scheme.scheme_data, (void *)sec_scheme_cfg, sizeof(nvs_sec_config_hmac_t)); + + *sec_scheme_handle_out = &sec_scheme; + return ESP_OK; +} + +#endif // SOC_HMAC_SUPPORTED diff --git a/components/nvs_sec_provider/nvs_sec_provider.c b/components/nvs_sec_provider/nvs_sec_provider.c index e45c84f4db..c7f592dd43 100644 --- a/components/nvs_sec_provider/nvs_sec_provider.c +++ b/components/nvs_sec_provider/nvs_sec_provider.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -12,6 +12,7 @@ #include "sdkconfig.h" #include "nvs_flash.h" #include "nvs_sec_provider.h" +#include "private/nvs_sec_provider_private.h" #include "esp_private/startup_internal.h" #if SOC_HMAC_SUPPORTED @@ -112,8 +113,8 @@ ESP_SYSTEM_INIT_FN(nvs_sec_provider_register_flash_enc_scheme, SECONDARY, BIT(0) static esp_err_t compute_nvs_keys_with_hmac(hmac_key_id_t hmac_key_id, nvs_sec_cfg_t* cfg) { - uint32_t ekey_seed[8] = {[0 ... 7] = 0xAEBE5A5A}; - uint32_t tkey_seed[8] = {[0 ... 7] = 0xCEDEA5A5}; + uint32_t ekey_seed[8] = {[0 ... 7] = EKEY_SEED}; + uint32_t tkey_seed[8] = {[0 ... 7] = TKEY_SEED}; esp_err_t err = esp_hmac_calculate(hmac_key_id, ekey_seed, sizeof(ekey_seed), (uint8_t *)cfg->eky); if (err != ESP_OK) { diff --git a/docs/en/api-reference/storage/nvs_bootloader.rst b/docs/en/api-reference/storage/nvs_bootloader.rst index fa0049b795..d643350691 100644 --- a/docs/en/api-reference/storage/nvs_bootloader.rst +++ b/docs/en/api-reference/storage/nvs_bootloader.rst @@ -14,6 +14,25 @@ The API supports reading all NVS datatypes except for blobs. One call to the API To read string entries, the API requires the caller to provide a buffer and its size, due to the heap memory allocation restriction in the bootloader. +Reading encrypted NVS partitions +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The API also supports decrypting NVS data, if the NVS partition is encrypted using one of the schemes as mentioned in the :doc:`nvs_encryption` guide. + +Applications are expected to follow the steps below in order to enable decryption of a NVS partition using the NVS bootloader read API: + + 1. Populate the NVS security configuration structure :cpp:type:`nvs_sec_cfg_t` according to the selected NVS encryption scheme (Please refer to :doc:`nvs_encryption` for more details). + 2. Read NVS security configuration set by the specified security scheme using the :cpp:func:`nvs_bootloader_read_security_cfg` API. + 3. Initialise the NVS flash partition with the above read security configuration using the :cpp:func:`nvs_bootloader_secure_init` API. + 4. Perform NVS read operations using the :cpp:func:`nvs_bootloader_read` API. + 5. Deinitialise and clear the security configuration of the NVS flash partition using the :cpp:func:`nvs_bootloader_secure_deinit` API. + +.. only:: SOC_HMAC_SUPPORTED + + .. note:: + While using the HMAC-based scheme, the above workflow can be used without enabling any of the config options for NVS encryption - :ref:`CONFIG_NVS_ENCRYPTION`, :ref:`CONFIG_NVS_SEC_KEY_PROTECTION_SCHEME` -> ``CONFIG_NVS_SEC_KEY_PROTECT_USING_HMAC`` and :ref:`CONFIG_NVS_SEC_HMAC_EFUSE_KEY_ID` to encrypt the default as well as custom NVS partitions with :cpp:func:`nvs_flash_secure_init` API. + + Application Example ------------------- From 3b7d71eca27f5e3130606fe2c1c01313684506c5 Mon Sep 17 00:00:00 2001 From: "harshal.patil" Date: Mon, 30 Dec 2024 18:59:08 +0530 Subject: [PATCH 07/11] feat(examples): Extend the bootloader_nvs example to support encrypted NVS reads --- examples/storage/.build-test-rules.yml | 9 ++ examples/storage/nvs_bootloader/README.md | 117 +++++++++++++++++- .../nvs_bootloader_example/CMakeLists.txt | 4 +- .../src/nvs_bootloader_example.c | 85 ++++++++++--- .../nvs_bootloader/main/CMakeLists.txt | 12 +- .../nvs_bootloader/main/encryption_keys.bin | 1 + .../nvs_bootloader/main/nvs_enc_hmac_key.bin | 2 + .../nvs_bootloader/main/nvs_encrypted.bin | Bin 0 -> 24576 bytes .../main/nvs_encrypted_hmac.bin | Bin 0 -> 24576 bytes examples/storage/nvs_bootloader/nvs_data.csv | 2 + .../nvs_bootloader/pytest_nvs_bootloader.py | 24 +++- .../nvs_bootloader/sdkconfig.ci.default | 0 .../sdkconfig.ci.nvs_enc_flash_enc | 13 ++ .../nvs_bootloader/sdkconfig.ci.nvs_enc_hmac | 12 ++ .../storage/nvs_bootloader/sdkconfig.defaults | 2 +- .../nvs_bootloader/sdkconfig.defaults.esp32c2 | 2 + 16 files changed, 261 insertions(+), 24 deletions(-) create mode 100644 examples/storage/nvs_bootloader/main/encryption_keys.bin create mode 100644 examples/storage/nvs_bootloader/main/nvs_enc_hmac_key.bin create mode 100644 examples/storage/nvs_bootloader/main/nvs_encrypted.bin create mode 100644 examples/storage/nvs_bootloader/main/nvs_encrypted_hmac.bin create mode 100644 examples/storage/nvs_bootloader/sdkconfig.ci.default create mode 100644 examples/storage/nvs_bootloader/sdkconfig.ci.nvs_enc_flash_enc create mode 100644 examples/storage/nvs_bootloader/sdkconfig.ci.nvs_enc_hmac create mode 100644 examples/storage/nvs_bootloader/sdkconfig.defaults.esp32c2 diff --git a/examples/storage/.build-test-rules.yml b/examples/storage/.build-test-rules.yml index dfbff5c073..9fe58ad263 100644 --- a/examples/storage/.build-test-rules.yml +++ b/examples/storage/.build-test-rules.yml @@ -16,6 +16,15 @@ examples/storage/emmc: - if: IDF_TARGET == "esp32s3" reason: only support on esp32s3 +examples/storage/nvs_bootloader: + depends_components: + - nvs_flash + - nvs_sec_provider + disable: + - if: CONFIG_NAME == "nvs_enc_flash_enc" and (SOC_AES_SUPPORTED != 1 and ESP_ROM_HAS_MBEDTLS_CRYPTO_LIB != 1) + - if: CONFIG_NAME == "nvs_enc_hmac" and (SOC_HMAC_SUPPORTED != 1 or (SOC_HMAC_SUPPORTED == 1 and (SOC_AES_SUPPORTED != 1 and ESP_ROM_HAS_MBEDTLS_CRYPTO_LIB != 1))) + reason: As of now in such cases, we do not have any way to perform AES operations in the bootloader build + examples/storage/nvs_rw_blob: depends_components: - nvs_flash diff --git a/examples/storage/nvs_bootloader/README.md b/examples/storage/nvs_bootloader/README.md index a5247f64a3..e0b22376e5 100644 --- a/examples/storage/nvs_bootloader/README.md +++ b/examples/storage/nvs_bootloader/README.md @@ -3,7 +3,9 @@ # NVS Bootloader -The purpose of this example is to show how to use the simplified, read-only API of NVS flash that can be used as a part of bootloader +The purpose of this example is to show how to use the simplified, read-only API of NVS flash that can be used as a part of bootloader. + +A very practical application of being able to access the NVS in the bootloader build would be faster device restoration, where-in the application stores the device's current state/configurations and post a reset it would read the NVS to restore the device's last state, without waiting for application to boot-up. ## Usage of this example: @@ -123,3 +125,116 @@ Below is a short explanation of files in the project folder. The example creates request/response array `read_list[]`, populates it with identifiers of the data to be read. Function `nvs_bootloader_read()` tries to find respective data in the partition (here `"nvs"`) and if the data is found, it populates the request/response array with data. For nvs entries either not found or not matching are indicated in response array as well. Function `log_nvs_bootloader_read_list()`is used before and after reading from nvs to show request/response data to the console. + +# Encrypted NVS Bootloader + +This example is extended to support reading encrypted NVS partition when NVS encryption is enabled. + +## Usage of this example: + +Enable NVS encryption using your preferred scheme. Please find more details regarding the `flash encryption based NVS encryption scheme` and the `HMAC based NVS encryption scheme` in the [NVS encryption documentation](https://docs.espressif.com/projects/esp-idf/en/latest/api-reference/storage/nvs_encryption.html). + +(Note: In case you select the `HMAC based NVS encryption scheme`, make sure that you burn the below mentioned [HMAC key](./main/nvs_enc_hmac_key.bin) in the efuses.) + +For generating the encrypted NVS partitions, we shall use [NVS partition generator](https://docs.espressif.com/projects/esp-idf/en/latest/api-reference/storage/nvs_partition_gen.html#nvs-partition-generator-utility). +We shall use the [nvs_partition_gen.py](../../../components/nvs_flash/nvs_partition_generator/nvs_partition_gen.py) script for the operations. + +Along with the above mentioned file structure, the project folder also contains pre-generated encrypted partitions and the partition corresponding to the selected NVS encryption scheme is flashed along with the build artefacts using the `main/CMakeLists.txt`. + +In case the data in `nvs_data.csv` is modified, these encrypted NVS partitions can be re-generated using the following commands: + +1. NVS Encryption using the flash encryption scheme + +``` +python nvs_partition_gen.py encrypt $IDF_PATH/examples/storage/nvs_bootloader/nvs_data.csv $IDF_PATH/examples/storage/nvs_bootloader/main/nvs_encrypted.bin 0x6000 --inputkey $IDF_PATH/examples/storage/nvs_bootloader/main/encryption_keys.bin +``` + +2. NVS Encryption using the HMAC scheme + +``` +python nvs_partition_gen.py encrypt $IDF_PATH/examples/storage/nvs_bootloader/nvs_data.csv $IDF_PATH/examples/storage/nvs_bootloader/main/nvs_encrypted_hmac.bin 0x6000 --keygen --key_protect_hmac --kp_hmac_inputkey $IDF_PATH/examples/storage/nvs_bootloader/main/nvs_enc_hmac_key.bin +``` + +Build the application using configurations corresponding to the NVS encryption scheme that you have selected: + +``` +idf.py set-target + +# For NVS encryption using flash encryption scheme +cat sdkconfig.ci.nvs_enc_flash_enc >> sdkconfig + +OR + +# For NVS encryption using the HMAC scheme +cat sdkconfig.ci.nvs_enc_hmac >> sdkconfig + +idf.py build +``` + +Then flash it and open the monitor with the following command: +``` +idf.py flash monitor +``` + +If everything went well, the console output should contain the same three blocks of log messages that are mentioned above. + +### Running the example using QEMU + +You could quickly try out this example using QEMU. Refer this [link](https://github.com/espressif/esp-toolchain-docs/blob/main/qemu/README.md#choose-your-target) to know which targets are currently supported in QEMU. + +#### Using the NVS encryption's flash encryption scheme + +1. Configure the application with the corresponding configurations + +``` +idf.py set-target + +cat sdkconfig.ci.nvs_enc_flash_enc >> sdkconfig + +# Disable the below config as it was enabled as a CI related configuration, thus enabling flash encryption during boot-up in QEMU +echo "CONFIG_SECURE_FLASH_REQUIRE_ALREADY_ENABLED=n" >> sdkconfig + +``` + +2. Building the app + +``` +idf.py build +``` + +3. Running the app + +``` +idf.py qemu monitor +``` + +#### Using the NVS encryption's HMAC scheme + +1. Build the application using the corresponding configurations + +``` +idf.py set-target + +cat sdkconfig.ci.nvs_enc_hmac >> sdkconfig + +# Disable the below config as it was enabled as a CI related configuration, thus enabling flash encryption during boot-up in QEMU +echo "CONFIG_SECURE_FLASH_REQUIRE_ALREADY_ENABLED=n" >> sdkconfig +``` + +2. Building the app + +``` +idf.py build +``` + +3. Burn the related HMAC key in the efuses + +``` +idf.py qemu efuse-burn-key BLOCK_KEY0 main/nvs_enc_hmac_key.bin HMAC_UP +``` + +4. Running the app + +``` +idf.py qemu monitor +``` diff --git a/examples/storage/nvs_bootloader/bootloader_components/nvs_bootloader_example/CMakeLists.txt b/examples/storage/nvs_bootloader/bootloader_components/nvs_bootloader_example/CMakeLists.txt index 91038a9e13..cbe40eafec 100644 --- a/examples/storage/nvs_bootloader/bootloader_components/nvs_bootloader_example/CMakeLists.txt +++ b/examples/storage/nvs_bootloader/bootloader_components/nvs_bootloader_example/CMakeLists.txt @@ -1,6 +1,6 @@ -idf_component_register( SRCS "src/nvs_bootloader_example.c" "src/nvs_bootloader_example_utils.c" +idf_component_register(SRCS "src/nvs_bootloader_example.c" "src/nvs_bootloader_example_utils.c" INCLUDE_DIRS "include" - REQUIRES "nvs_flash" ) + REQUIRES "nvs_flash" "nvs_sec_provider") # We need to force GCC to integrate this static library into the # bootloader link. Indeed, by default, as the hooks in the bootloader are weak, diff --git a/examples/storage/nvs_bootloader/bootloader_components/nvs_bootloader_example/src/nvs_bootloader_example.c b/examples/storage/nvs_bootloader/bootloader_components/nvs_bootloader_example/src/nvs_bootloader_example.c index cf83507aaf..4b1348390c 100644 --- a/examples/storage/nvs_bootloader/bootloader_components/nvs_bootloader_example/src/nvs_bootloader_example.c +++ b/examples/storage/nvs_bootloader/bootloader_components/nvs_bootloader_example/src/nvs_bootloader_example.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -8,6 +8,8 @@ #include "nvs_bootloader.h" #include "nvs_bootloader_example_utils.h" +#include "nvs_sec_provider.h" + static const char* TAG = "nvs_bootloader_example"; // Function used to tell the linker to include this file @@ -21,14 +23,11 @@ void bootloader_before_init(void) { } -void log_request_call_read_evaluate_output(const char* nvs_partition_label, nvs_bootloader_read_list_t read_list[], const size_t read_list_count) { - // log the request structure before the read to see the requested keys and namespaces - // with the ESP_ERR_NOT_FINISHED return code we are just telling the log function to show the request data and omit printing the result data - // it is useful for debugging the request structure - log_nvs_bootloader_read_list(ESP_ERR_NOT_FINISHED, read_list, read_list_count); - +void log_request_call_read_evaluate_output(const char* nvs_partition_label, nvs_bootloader_read_list_t read_list[], const size_t read_list_count, const nvs_sec_cfg_t* sec_cfg) +{ + esp_err_t ret = ESP_FAIL; // call the read function - esp_err_t ret = nvs_bootloader_read(nvs_partition_label, read_list_count, read_list); + ret = nvs_bootloader_read(nvs_partition_label, read_list_count, read_list); // Error code ESP_OK means that the read function was successful and individual, per record results are stored in the read_list if (ret == ESP_OK) { @@ -62,10 +61,48 @@ void bootloader_after_init(void) { // we are going to read from the default nvs partition labelled 'nvs' const char* nvs_partition_label = "nvs"; - #define STR_BUFF_LEN 10+1 // 10 characters + null terminator - char str_buff[STR_BUFF_LEN]; + #define STR_BUFF_LEN_10 10+1 // 10 characters + null terminator + #define STR_BUFF_LEN_66 66+1 // 66 characters + null terminator + char str_buff_10[STR_BUFF_LEN_10]; + char str_buff_66[STR_BUFF_LEN_66]; + + nvs_sec_cfg_t* sec_cfg = NULL; + +#if CONFIG_NVS_ENCRYPTION + nvs_sec_cfg_t cfg = {}; + nvs_sec_scheme_t *sec_scheme_handle = NULL; +#if CONFIG_NVS_SEC_KEY_PROTECT_USING_HMAC + nvs_sec_config_hmac_t sec_scheme_cfg = NVS_SEC_PROVIDER_CFG_HMAC_DEFAULT(); + if (nvs_sec_provider_register_hmac(&sec_scheme_cfg, &sec_scheme_handle) != ESP_OK) { + ESP_EARLY_LOGE(TAG, "Registering the HMAC scheme failed"); + return; + } +#elif CONFIG_NVS_SEC_KEY_PROTECT_USING_FLASH_ENC + nvs_sec_config_flash_enc_t sec_scheme_cfg = NVS_SEC_PROVIDER_CFG_FLASH_ENC_DEFAULT(); + if (sec_scheme_cfg.nvs_keys_part == NULL) { + ESP_EARLY_LOGE(TAG, "partition with subtype \"nvs_keys\" not found"); + } + + if (nvs_sec_provider_register_flash_enc(&sec_scheme_cfg, &sec_scheme_handle) != ESP_OK) { + ESP_EARLY_LOGE(TAG, "Registering the Flash Encryption scheme failed"); + return; + } +#endif /* CONFIG_NVS_SEC_KEY_PROTECT_USING_HMAC */ + + if (nvs_bootloader_read_security_cfg(sec_scheme_handle, &cfg) != ESP_OK) { + ESP_EARLY_LOGE(TAG, "Reading the NVS security configuration failed"); + return; + } + + if (nvs_bootloader_secure_init(&cfg) != ESP_OK) { + ESP_LOGE(TAG, "Secure initialization of NVS failed"); + return; + } +#endif /* CONFIG_NVS_ENCRYPTION*/ // --- This is the request structure for the read function showing validation errors - function will return ESP_ERR_INVALID_ARG --- + ESP_EARLY_LOGI(TAG, "Trying to read the NVS partition by passing invalid arguments"); + nvs_bootloader_read_list_t bad_read_list_indicate_problems[] = { { .namespace_name = "sunny_day", .key_name = "u8", .value_type = NVS_TYPE_U8 }, // ESP_ERR_NVS_NOT_FOUND // this is correct request, not found is expected default result code @@ -75,19 +112,21 @@ void bootloader_after_init(void) { // too long key name { .namespace_name = "clowny_day", .key_name = "blobeee", .value_type = NVS_TYPE_BLOB }, // ESP_ERR_INVALID_ARG // not supported data type - { .namespace_name = "sunny_day", .key_name = "string_10_chars", .value_type = NVS_TYPE_STR, .value.str_val = { .buff_ptr = str_buff, .buff_len = 0 } }, + { .namespace_name = "sunny_day", .key_name = "string_10_chars", .value_type = NVS_TYPE_STR, .value.str_val = { .buff_ptr = str_buff_10, .buff_len = 0 } }, // ESP_ERR_INVALID_SIZE // buffer size is 0 - { .namespace_name = "sunny_day", .key_name = "string_10_chars", .value_type = NVS_TYPE_STR, .value.str_val = { .buff_ptr = NULL, .buff_len = 10 } } + { .namespace_name = "sunny_day", .key_name = "string_66_chars", .value_type = NVS_TYPE_STR, .value.str_val = { .buff_ptr = NULL, .buff_len = 66 } } // ESP_ERR_INVALID_SIZE // buffer pointer is invalid }; size_t bad_read_list_indicate_problems_count = sizeof(bad_read_list_indicate_problems) / sizeof(bad_read_list_indicate_problems[0]); - log_request_call_read_evaluate_output(nvs_partition_label, bad_read_list_indicate_problems, bad_read_list_indicate_problems_count); + log_request_call_read_evaluate_output(nvs_partition_label, bad_read_list_indicate_problems, bad_read_list_indicate_problems_count, sec_cfg); // --- This is the request structure for the read function showing runtime errors - function will return ESP_OK --- // but some records will have result_code set to ESP_ERR_NVS_NOT_FOUND, ESP_ERR_NVS_TYPE_MISMATCH, ESP_ERR_INVALID_SIZE + ESP_EARLY_LOGI(TAG, "Trying to read the NVS partition by expecting incorrect data"); + nvs_bootloader_read_list_t good_read_list_bad_results[] = { { .namespace_name = "sunny_day", .key_name = "u8", .value_type = NVS_TYPE_I8 }, // ESP_ERR_NVS_TYPE_MISMATCH // data in the partition is of different type (NVS_TYPE_U8) @@ -95,17 +134,20 @@ void bootloader_after_init(void) { // data in the partition won't be found, because there is a typo in the key name { .namespace_name = "clowny_day", .key_name = "i8", .value_type = NVS_TYPE_I8 }, // ESP_ERR_NVS_NOT_FOUND // data in the partition won't be found, because there is typo in namespace name - { .namespace_name = "sunny_day", .key_name = "string_10_chars", .value_type = NVS_TYPE_STR, .value.str_val = { .buff_ptr = str_buff, .buff_len = 2 } }, + { .namespace_name = "sunny_day", .key_name = "string_10_chars", .value_type = NVS_TYPE_STR, .value.str_val = { .buff_ptr = str_buff_10, .buff_len = 2 } }, // ESP_ERR_INVALID_SIZE // buffer is too small { .namespace_name = "sunny_day", .key_name = "u32", .value_type = NVS_TYPE_U32 }, // ESP_OK // this value will be read correctly - { .namespace_name = "sunny_day", .key_name = "u32", .value_type = NVS_TYPE_U32 } // ESP_ERR_NVS_NOT_FOUND + { .namespace_name = "sunny_day", .key_name = "u32", .value_type = NVS_TYPE_U32 }, // ESP_ERR_NVS_NOT_FOUND // this value won't be read as function doesn't support duplicate readings + { .namespace_name = "sunny_day", .key_name = "string_66_chars", .value_type = NVS_TYPE_STR, .value.str_val = { .buff_ptr = str_buff_66, .buff_len = 65 } } + // ESP_ERR_INVALID_SIZE + // buffer is just small }; size_t good_read_list_bad_results_count = sizeof(good_read_list_bad_results) / sizeof(good_read_list_bad_results[0]); - log_request_call_read_evaluate_output(nvs_partition_label, good_read_list_bad_results, good_read_list_bad_results_count); + log_request_call_read_evaluate_output(nvs_partition_label, good_read_list_bad_results, good_read_list_bad_results_count, sec_cfg); // --- This is the request structure for the read function showing all records found--- @@ -116,16 +158,23 @@ void bootloader_after_init(void) { // For NVS_TYPE_STR the value field is a structure with a pointer to the buffer and the buffer length is povided // In this case, the buffer is a stack allocated array of 10 characters plus space for the null terminator + ESP_EARLY_LOGI(TAG, "Trying to read the NVS partition correctly"); + nvs_bootloader_read_list_t good_read_list[] = { { .namespace_name = "sunny_day", .key_name = "u8", .value_type = NVS_TYPE_U8 }, { .namespace_name = "sunny_day", .key_name = "i32", .value_type = NVS_TYPE_I32 }, { .namespace_name = "cloudy_day", .key_name = "i8", .value_type = NVS_TYPE_I8 }, // mixed in different namespace { .namespace_name = "sunny_day", .key_name = "u16", .value_type = NVS_TYPE_U16 }, - { .namespace_name = "sunny_day", .key_name = "string_10_chars", .value_type = NVS_TYPE_STR, .value.str_val = { .buff_ptr = str_buff, .buff_len = STR_BUFF_LEN } } + { .namespace_name = "sunny_day", .key_name = "string_10_chars", .value_type = NVS_TYPE_STR, .value.str_val = { .buff_ptr = str_buff_10, .buff_len = STR_BUFF_LEN_10 } }, + { .namespace_name = "sunny_day", .key_name = "string_66_chars", .value_type = NVS_TYPE_STR, .value.str_val = { .buff_ptr = str_buff_66, .buff_len = STR_BUFF_LEN_66 } } }; size_t good_read_list_count = sizeof(good_read_list) / sizeof(good_read_list[0]); - log_request_call_read_evaluate_output(nvs_partition_label, good_read_list, good_read_list_count); + log_request_call_read_evaluate_output(nvs_partition_label, good_read_list, good_read_list_count, sec_cfg); + +#if CONFIG_NVS_ENCRYPTION + nvs_bootloader_secure_deinit(); +#endif /* CONFIG_NVS_ENCRYPTION */ ESP_LOGI(TAG, "Finished bootloader part"); } diff --git a/examples/storage/nvs_bootloader/main/CMakeLists.txt b/examples/storage/nvs_bootloader/main/CMakeLists.txt index d8d0fb7a76..47e59caa22 100644 --- a/examples/storage/nvs_bootloader/main/CMakeLists.txt +++ b/examples/storage/nvs_bootloader/main/CMakeLists.txt @@ -1,3 +1,13 @@ idf_component_register(SRCS "bootloader_hooks_example_main.c" INCLUDE_DIRS ".") -nvs_create_partition_image(nvs ../nvs_data.csv FLASH_IN_PROJECT) + +if(NOT CONFIG_NVS_ENCRYPTION) + nvs_create_partition_image(nvs ../nvs_data.csv FLASH_IN_PROJECT) +else() + if(CONFIG_NVS_SEC_KEY_PROTECT_USING_FLASH_ENC) + esptool_py_flash_to_partition(flash "nvs_key" ${PROJECT_DIR}/main/encryption_keys.bin) + esptool_py_flash_to_partition(flash "nvs" ${PROJECT_DIR}/main/nvs_encrypted.bin) + else() # NVS Encryption using HMAC (CONFIG_NVS_SEC_KEY_PROTECT_USING_HMAC) + esptool_py_flash_to_partition(flash "nvs" ${PROJECT_DIR}/main/nvs_encrypted_hmac.bin) + endif() +endif() diff --git a/examples/storage/nvs_bootloader/main/encryption_keys.bin b/examples/storage/nvs_bootloader/main/encryption_keys.bin new file mode 100644 index 0000000000..9ef4439d8c --- /dev/null +++ b/examples/storage/nvs_bootloader/main/encryption_keys.bin @@ -0,0 +1 @@ +"""""""""""""""""""""""""""""""",< \ No newline at end of file diff --git a/examples/storage/nvs_bootloader/main/nvs_enc_hmac_key.bin b/examples/storage/nvs_bootloader/main/nvs_enc_hmac_key.bin new file mode 100644 index 0000000000..2ea3dec3e1 --- /dev/null +++ b/examples/storage/nvs_bootloader/main/nvs_enc_hmac_key.bin @@ -0,0 +1,2 @@ + +  \ No newline at end of file diff --git a/examples/storage/nvs_bootloader/main/nvs_encrypted.bin b/examples/storage/nvs_bootloader/main/nvs_encrypted.bin new file mode 100644 index 0000000000000000000000000000000000000000..890d720300b95e6b407d5536310af374121b0207 GIT binary patch literal 24576 zcmezK|Nnmm1_p+I|1m*}?yjAyR;_x4DT5GMv86_WH)on|hIXpWn=7B{ZhTxCeA!QM zkxbzCv;WpjOP#o^CRydSSlRNLkEeBv4I?=WrrdC6`l(vzZ_ym&yrwx;X*G|GbOEn( zqHQW`uSvkq&(pv0?rgMeUud6q-8fW3k)?MnmLB>xB2@eKKJw4wyT=Cm>>|Fgtnk%Do+rE>m zkEZ6y@BKEXjDvMm`KHbaKhKIh)lF~IZzu6Cwtl&(&MCBaLUj_?+{b@b^l$oc!{y(M zrW^B)nJ4R9$u0`^TOG4C*Uj2GYgYG&d*{k zpSEj4;l&RIPyHKy6|RV24cfh!dG49R2^r9`h}RvfoDCJ{kg}Aut*OqaiRF0;3@?8UmvsFd71*Aut*OqaiRF0;3@S z2?0?5p9x3*zx!ch0z}m)IT`|^Aut*OqaiRF0;3@?8UmvsFd71*Aut*OqaiRF0;3@? zltTd2|7XV0|6kLx$ literal 0 HcmV?d00001 diff --git a/examples/storage/nvs_bootloader/main/nvs_encrypted_hmac.bin b/examples/storage/nvs_bootloader/main/nvs_encrypted_hmac.bin new file mode 100644 index 0000000000000000000000000000000000000000..e413d5108e783e9c5c695a09fd9fe1ec4bdec859 GIT binary patch literal 24576 zcmezK|Nnmm1_p+I|1m*}?yjAyR;_x4DT5GU-2KS*NY2C8rMbHj=149UK6&)*GV|jC zyVisjij<_jlsK&VXUR;FvhWnO&O>>JpBtS_-1}$RAE9>Xp3}brb>1#&bcx<5{F(kz|EwXvWTr=sQ!PN~6W(};#s}Gl-vM{_fYjxGk0`>(# zUiGgXU!PV{+#P!S-LDlJT{Xmc7EVphd>ZMrzFKIVy6odRJM|rJ_JyQ9Wh-PBWb-*A zTUQiiz3;2E>FU?*-TX%JUz4RXF2xmg25!xmuGd<#^|pMO-nj|B9D5rxqa@=cr%t<; zwus@eO8D!F%$YwN>omN@=JAE8w|@0n9I}4i+3$I?R)0RDI{W&q+^_$Yovyx=RC8GQ z$5>|9^G!EbzR2g<%W||$HtW|eF5QJISRHq-(PDLya8f_|=a$ClH??I6d&E9$2=DEl z&YJZyr7U%$b^wFcEaMNA2PQO?OjeAzAYIFRefLbYP1c^jYi(p@7_^u-9yxOT@cwNQ zVzz>TXEiLEC5|^gU1#UBxbDp&pT1cYash81I9~43U4GPhuGn>5ou#j@$y{8=AhmFc z&9h5i*FBv7^lRjT`gQ!_M_;b{qj*VIdTr*>j!kCQ-`MV2ZFA=p(@fJRvmP( None: # Expect to read hooks messages and data from NVS partition dut.expect_exact('Before reading from NVS partition') + dut.expect_exact('Result data. Return code: ESP_OK') + dut.expect_exact('u8 U8 255') + dut.expect_exact('i32 I32 -2147483648') + dut.expect_exact('i8 I8 -13') + dut.expect_exact('u16 U16 65535') + dut.expect_exact('string_10_chars STR Text_67890') + dut.expect_exact('string_66_chars STR Text_67890_Text_67890_Text_67890_0_Text_67890_Text_67890_Text_6789') dut.expect_exact('Finished bootloader part') dut.expect_exact('User application is loaded and running.') + + +@pytest.mark.esp32c3 +@pytest.mark.nvs_encr_hmac +@pytest.mark.parametrize('config', ['nvs_enc_hmac'], indirect=True) +def test_nvs_bootloader_example_nvs_encr_hmac(dut: Dut) -> None: + test_nvs_bootloader_example(dut) + + +@pytest.mark.esp32 +@pytest.mark.esp32c3 +@pytest.mark.flash_encryption +@pytest.mark.parametrize('config', ['nvs_enc_flash_enc'], indirect=True) +def test_nvs_bootloader_example_flash_enc(dut: Dut) -> None: + test_nvs_bootloader_example(dut) diff --git a/examples/storage/nvs_bootloader/sdkconfig.ci.default b/examples/storage/nvs_bootloader/sdkconfig.ci.default new file mode 100644 index 0000000000..e69de29bb2 diff --git a/examples/storage/nvs_bootloader/sdkconfig.ci.nvs_enc_flash_enc b/examples/storage/nvs_bootloader/sdkconfig.ci.nvs_enc_flash_enc new file mode 100644 index 0000000000..34dc45f65a --- /dev/null +++ b/examples/storage/nvs_bootloader/sdkconfig.ci.nvs_enc_flash_enc @@ -0,0 +1,13 @@ +CONFIG_SECURE_FLASH_ENC_ENABLED=y +CONFIG_SECURE_FLASH_ENCRYPTION_MODE_DEVELOPMENT=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_SECURE_FLASH_REQUIRE_ALREADY_ENABLED=y + +CONFIG_NVS_ENCRYPTION=y +CONFIG_NVS_SEC_KEY_PROTECT_USING_FLASH_ENC=y + +CONFIG_PARTITION_TABLE_SINGLE_APP_ENCRYPTED_NVS=y diff --git a/examples/storage/nvs_bootloader/sdkconfig.ci.nvs_enc_hmac b/examples/storage/nvs_bootloader/sdkconfig.ci.nvs_enc_hmac new file mode 100644 index 0000000000..b0b02f4a85 --- /dev/null +++ b/examples/storage/nvs_bootloader/sdkconfig.ci.nvs_enc_hmac @@ -0,0 +1,12 @@ +CONFIG_SECURE_FLASH_ENC_ENABLED=y +CONFIG_SECURE_FLASH_ENCRYPTION_MODE_DEVELOPMENT=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_SECURE_FLASH_REQUIRE_ALREADY_ENABLED=y + +CONFIG_NVS_ENCRYPTION=y +CONFIG_NVS_SEC_KEY_PROTECT_USING_HMAC=y +CONFIG_NVS_SEC_HMAC_EFUSE_KEY_ID=0 diff --git a/examples/storage/nvs_bootloader/sdkconfig.defaults b/examples/storage/nvs_bootloader/sdkconfig.defaults index bb48f6522e..5b4203d232 100644 --- a/examples/storage/nvs_bootloader/sdkconfig.defaults +++ b/examples/storage/nvs_bootloader/sdkconfig.defaults @@ -1,4 +1,4 @@ # The size of bootloader has been increased, so the partition table offset needs adjustment -CONFIG_PARTITION_TABLE_OFFSET=0xA000 +CONFIG_PARTITION_TABLE_OFFSET=0xB000 CONFIG_BOOTLOADER_LOG_LEVEL_WARN=y CONFIG_BOOTLOADER_LOG_LEVEL=2 diff --git a/examples/storage/nvs_bootloader/sdkconfig.defaults.esp32c2 b/examples/storage/nvs_bootloader/sdkconfig.defaults.esp32c2 new file mode 100644 index 0000000000..5e3a3c88f4 --- /dev/null +++ b/examples/storage/nvs_bootloader/sdkconfig.defaults.esp32c2 @@ -0,0 +1,2 @@ +CONFIG_IDF_TARGET="esp32c2" +CONFIG_MBEDTLS_USE_CRYPTO_ROM_IMPL_BOOTLOADER=y From 5b17375acaf3915abfd6151b97a9537ff721a74c Mon Sep 17 00:00:00 2001 From: "harshal.patil" Date: Thu, 2 Jan 2025 14:54:42 +0530 Subject: [PATCH 08/11] test(nvs_flash): Extend the nvs_flash test app to support encrypted NVS reads --- components/nvs_flash/.build-test-rules.yml | 5 + .../test_apps_bootloader/main/CMakeLists.txt | 17 +- .../main/encryption_keys.bin | 1 + .../main/nvs_enc_hmac_key.bin | 2 + .../main/nvs_partition.bin | Bin 0 -> 24576 bytes .../main/partition_encrypted.bin | Bin 0 -> 24576 bytes .../main/partition_encrypted_hmac.bin | Bin 0 -> 24576 bytes .../main/test_encrypted_nvs_bootloader.c | 198 ++++++++++++++++++ .../pytest_nvs_bootloader_support.py | 32 ++- .../sdkconfig.ci.nvs_enc_flash_enc | 15 ++ .../sdkconfig.ci.nvs_enc_hmac | 15 ++ .../sdkconfig.ci.nvs_enc_hmac_no_cfg | 12 ++ .../sdkconfig.defaults.esp32c2 | 2 + 13 files changed, 293 insertions(+), 6 deletions(-) create mode 100644 components/nvs_flash/test_apps_bootloader/main/encryption_keys.bin create mode 100644 components/nvs_flash/test_apps_bootloader/main/nvs_enc_hmac_key.bin create mode 100644 components/nvs_flash/test_apps_bootloader/main/nvs_partition.bin create mode 100644 components/nvs_flash/test_apps_bootloader/main/partition_encrypted.bin create mode 100644 components/nvs_flash/test_apps_bootloader/main/partition_encrypted_hmac.bin create mode 100644 components/nvs_flash/test_apps_bootloader/main/test_encrypted_nvs_bootloader.c create mode 100644 components/nvs_flash/test_apps_bootloader/sdkconfig.ci.nvs_enc_flash_enc create mode 100644 components/nvs_flash/test_apps_bootloader/sdkconfig.ci.nvs_enc_hmac create mode 100644 components/nvs_flash/test_apps_bootloader/sdkconfig.ci.nvs_enc_hmac_no_cfg create mode 100644 components/nvs_flash/test_apps_bootloader/sdkconfig.defaults.esp32c2 diff --git a/components/nvs_flash/.build-test-rules.yml b/components/nvs_flash/.build-test-rules.yml index cdfd738050..e939aa9bbd 100644 --- a/components/nvs_flash/.build-test-rules.yml +++ b/components/nvs_flash/.build-test-rules.yml @@ -21,5 +21,10 @@ components/nvs_flash/test_apps_bootloader: - spi_flash - nvs_flash - esp_partition + disable: + - if: CONFIG_NAME == "nvs_enc_flash_enc" and (SOC_AES_SUPPORTED != 1 and ESP_ROM_HAS_MBEDTLS_CRYPTO_LIB != 1) + - if: (CONFIG_NAME == "nvs_enc_hmac" or CONFIG_NAME == "nvs_enc_hmac_no_cfg") and (SOC_HMAC_SUPPORTED != 1 or (SOC_HMAC_SUPPORTED == 1 and (SOC_AES_SUPPORTED != 1 and ESP_ROM_HAS_MBEDTLS_CRYPTO_LIB != 1))) + + reason: As of now in such cases, we do not have any way to perform AES operations in the bootloader build disable_test: - if: IDF_TARGET not in ["esp32", "esp32c3"] diff --git a/components/nvs_flash/test_apps_bootloader/main/CMakeLists.txt b/components/nvs_flash/test_apps_bootloader/main/CMakeLists.txt index 978ea7e30f..18179a483d 100644 --- a/components/nvs_flash/test_apps_bootloader/main/CMakeLists.txt +++ b/components/nvs_flash/test_apps_bootloader/main/CMakeLists.txt @@ -1,6 +1,19 @@ -idf_component_register(SRCS "test_app_main.c" "test_nvs_bootloader.c" +set(srcs "test_app_main.c" "test_nvs_bootloader.c") +set(embed_txtfiles "") + +if(CONFIG_NVS_ENCRYPTION OR SOC_HMAC_SUPPORTED) + list(APPEND srcs "test_encrypted_nvs_bootloader.c") + list(APPEND embed_txtfiles "nvs_partition.bin" "partition_encrypted.bin" "partition_encrypted_hmac.bin") +endif() + +if(CONFIG_NVS_SEC_KEY_PROTECT_USING_FLASH_ENC) + list(APPEND embed_txtfiles "encryption_keys.bin") +endif() + +idf_component_register(SRCS "${srcs}" INCLUDE_DIRS "." - REQUIRES unity nvs_flash + REQUIRES unity nvs_flash nvs_sec_provider bootloader_support + EMBED_TXTFILES "${embed_txtfiles}" WHOLE_ARCHIVE ) diff --git a/components/nvs_flash/test_apps_bootloader/main/encryption_keys.bin b/components/nvs_flash/test_apps_bootloader/main/encryption_keys.bin new file mode 100644 index 0000000000..9ef4439d8c --- /dev/null +++ b/components/nvs_flash/test_apps_bootloader/main/encryption_keys.bin @@ -0,0 +1 @@ +"""""""""""""""""""""""""""""""",< \ No newline at end of file diff --git a/components/nvs_flash/test_apps_bootloader/main/nvs_enc_hmac_key.bin b/components/nvs_flash/test_apps_bootloader/main/nvs_enc_hmac_key.bin new file mode 100644 index 0000000000..2ea3dec3e1 --- /dev/null +++ b/components/nvs_flash/test_apps_bootloader/main/nvs_enc_hmac_key.bin @@ -0,0 +1,2 @@ + +  \ No newline at end of file diff --git a/components/nvs_flash/test_apps_bootloader/main/nvs_partition.bin b/components/nvs_flash/test_apps_bootloader/main/nvs_partition.bin new file mode 100644 index 0000000000000000000000000000000000000000..b00ed0c96226ac607b3a6e3b1aa8b1d8378d36a8 GIT binary patch literal 24576 zcmezK|Nnmm1_p+I|1m*}?yjAyR;~JlDT5GUU}XHyxiWygxFo+QF+CMz6c{r?l`t|g z{`b8S=v-mI4z{>Hmy^jQ@Wqny$%2lW%}3U}R$aKcl8%QmLUCiUCmR|BNh* z|Fmnp~1vQd*>tn37qLS)81im#&bSlUbt2 z_52#kinXb6mk zz-S1JhQMeDjE2By2#kinXb6mkz-S1JhQMeDjE2By2#kinXb6mkz-S1JhQMeDjE2By T2#kinXb6mkz-R~z^$-96MzwKI literal 0 HcmV?d00001 diff --git a/components/nvs_flash/test_apps_bootloader/main/partition_encrypted.bin b/components/nvs_flash/test_apps_bootloader/main/partition_encrypted.bin new file mode 100644 index 0000000000000000000000000000000000000000..8d34e7031bbf1666e1353a505b5e425e159c5d7b GIT binary patch literal 24576 zcmezK|Nnmm1_p+I|1m*}?yjAyR;~JlDT5FZu35BF;)iL#ng^woZ~ii!V0}MTc8gkH ziLvRPiG3DP;`UpfNF?!l8=d;0CcWUeoOGa+AeRuHfLE zHU9eCYbGZB{5<^|@6JZs_J#I&*NsD!=A=m6dDrTfQ2KVxq6oer|RTrP8MEo;vR^|H2O$PFI4I8>Qx1pJd$mu>MZy5qF;Q>pGnh z`LiY;W;^S&kiGVT+r-{m`9IYcZVx%P zzbT}|FE>9IcRc~)Mkh11hIC7snTygz8M8fUNmo_y<8 zvZ1G3fXM@99;-W->k`e<6?s;C-l(O{w*M_t`esKy-$1uzPhW1AYSUtm&$PWceOCRf zry3QfJtXI^tMay4t|*b{ncZU)qQxk?jhrA*;kg^8uHCCv#j59B_{XB zjC))Y0u+`1vz+^>oUe0@okN%J(x;{Cqx4QD?)@|Ek5IdG&*|TRI&T-9%)K3}VE3k5 zx;o@S=ZlVwWo#J@GZF)t*QdQ_TV(T$`D?NAgxF0JFYUILO7U4H8)SHC*6OO61?&rg zyy{;&zCOJpT3!8jr0C)wH)lWcTFiC#{adrS>1o*q79p>|x8~*T!&z74!d6@=xiHWDrszY%C-?1VCwb10D)f2v+juwIn&wrTbP7*p z_g=Hw=ce#|x1y|E!;`A&@V!PCcekC{^lYw$`0gzSj#QsqeNVy9^5S=iH&to;_L~!D zSzJ5%xJC8gPSz>e_nFqTc5zN9UH&)dc!bFLV+VOnzxv7DpTxj%<%HTwmuu!{oG;z{ z&+?+JS@opNZOIz@bEi@Y)ZXPjEO~uxWxe~Qr@S{A{@L0bU;OyW)i}umv*h14b$)DE zrtmA2Z3@#8&Z?4kHz&7k47qUJcKxQQl6n(2=jHa>Ec;Y@l+EK<>1O}>jwQtog|@fE zFK!NeqTo@!Hr2+Hd0VFUm1mbY_0R90v_aJANYau6YL^f(Im#Lhfzc2c4S~@R7!85Z z5Eu=C(GVC7fzc2c4S~@R7|tO8>i;w1=>ISFm~Szh{XXiq(GVC7fzc2c4S~@R7!85Z z5Eu=C(GVC7fzc2c4S~@R7%3qD>i;w0=>K;=Y)lv_;V|mY(GVC7fzc2c4S~@R7!85Z z5Eu=C(GVC7fzc2c4S~@R7|tO8>i;w2=>M;2S@Li=`+d}JqaiRF0;3@?8UmvsFd71* zAut*OqaiRF0;3@?8UmvsFj7JQ)c +#include +#include +#include + +#include "esp_err.h" +#include "esp_flash_encrypt.h" +#include "esp_partition.h" +#include "nvs_sec_provider.h" +#include "unity.h" + +#include "nvs_bootloader.h" + +static esp_err_t configure_nvs_sec_cfg(nvs_sec_cfg_t *cfg, nvs_sec_scheme_t **sec_scheme_handle) +{ + const esp_partition_t* nvs_part = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_NVS, NULL); + TEST_ASSERT(nvs_part && "partition table must have an NVS partition"); + printf("\n nvs_part size:%" PRId32 "\n", nvs_part->size); + ESP_ERROR_CHECK(esp_partition_erase_range(nvs_part, 0, nvs_part->size)); + +#if CONFIG_NVS_SEC_KEY_PROTECT_USING_FLASH_ENC + if (!esp_flash_encryption_enabled()) { + TEST_IGNORE_MESSAGE("flash encryption disabled, skipping nvs_api tests with encryption enabled"); + } + + extern const char nvs_key_start[] asm("_binary_encryption_keys_bin_start"); + extern const char nvs_key_end[] asm("_binary_encryption_keys_bin_end"); + extern const char nvs_data_sch0_start[] asm("_binary_partition_encrypted_bin_start"); + extern const char nvs_data_sch0_end[] asm("_binary_partition_encrypted_bin_end"); + + const esp_partition_t* key_part = esp_partition_find_first( + ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_NVS_KEYS, NULL); + + assert(key_part && "partition table must have a KEY partition"); + TEST_ASSERT_TRUE((nvs_key_end - nvs_key_start - 1) == key_part->erase_size); + + ESP_ERROR_CHECK(esp_partition_erase_range(key_part, 0, key_part->size)); + + for (int i = 0; i < key_part->size; i+= key_part->erase_size) { + ESP_ERROR_CHECK( esp_partition_write(key_part, i, nvs_key_start + i, key_part->erase_size) ); + } + + const int content_size = nvs_data_sch0_end - nvs_data_sch0_start - 1; + TEST_ASSERT_TRUE((content_size % key_part->erase_size) == 0); + + const int size_to_write = MIN(content_size, nvs_part->size); + for (int i = 0; i < size_to_write; i+= nvs_part->erase_size) { + ESP_ERROR_CHECK( esp_partition_write(nvs_part, i, nvs_data_sch0_start + i, nvs_part->erase_size) ); + } + + nvs_sec_config_flash_enc_t sec_scheme_cfg = { + .nvs_keys_part = key_part + }; + + TEST_ESP_OK(nvs_sec_provider_register_flash_enc(&sec_scheme_cfg, sec_scheme_handle)); + return nvs_flash_read_security_cfg_v2(*sec_scheme_handle, cfg); + +#elif SOC_HMAC_SUPPORTED + extern const char nvs_data_sch1_start[] asm("_binary_partition_encrypted_hmac_bin_start"); + extern const char nvs_data_sch1_end[] asm("_binary_partition_encrypted_hmac_bin_end"); + + const int content_size = nvs_data_sch1_end - nvs_data_sch1_start - 1; + TEST_ASSERT_TRUE((content_size % nvs_part->erase_size) == 0); + + const int size_to_write = MIN(content_size, nvs_part->size); + for (int i = 0; i < size_to_write; i+= nvs_part->erase_size) { + ESP_ERROR_CHECK( esp_partition_write(nvs_part, i, nvs_data_sch1_start + i, nvs_part->erase_size) ); + } + +#ifndef CONFIG_NVS_ENCRYPTION + nvs_sec_config_hmac_t sec_scheme_cfg = { + .hmac_key_id = HMAC_KEY0, + }; +#else + nvs_sec_config_hmac_t sec_scheme_cfg = NVS_SEC_PROVIDER_CFG_HMAC_DEFAULT(); +#endif /* CONFIG_NVS_ENCRYPTION */ + + TEST_ESP_OK(nvs_sec_provider_register_hmac(&sec_scheme_cfg, sec_scheme_handle)); + return nvs_flash_read_security_cfg_v2(*sec_scheme_handle, cfg); +#endif + + return ESP_FAIL; +} + +static void restore_nvs_partition(void) +{ + const esp_partition_t* nvs_part = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_NVS, NULL); + TEST_ASSERT(nvs_part && "partition table must have an NVS partition"); + printf("\n nvs_part size:%" PRId32 "\n", nvs_part->size); + ESP_ERROR_CHECK(esp_partition_erase_range(nvs_part, 0, nvs_part->size)); + + extern const char nvs_data_start[] asm("_binary_nvs_partition_bin_start"); + extern const char nvs_data_end[] asm("_binary_nvs_partition_bin_end"); + + const int content_size = nvs_data_end - nvs_data_start - 1; + TEST_ASSERT_TRUE((content_size % nvs_part->erase_size) == 0); + + const int size_to_write = MIN(content_size, nvs_part->size); + for (int i = 0; i < size_to_write; i+= nvs_part->erase_size) { + ESP_ERROR_CHECK(esp_partition_write(nvs_part, i, nvs_data_start + i, nvs_part->erase_size)); + } +} + +TEST_CASE("Verify encrypted nvs bootloader read_list result_code and value if bootloader read is successful", "[nvs_encrypted_bootloader]") +{ + nvs_sec_cfg_t xts_cfg; + nvs_sec_scheme_t *sec_scheme_handle = NULL; + TEST_ESP_OK(configure_nvs_sec_cfg(&xts_cfg, &sec_scheme_handle)); + + nvs_bootloader_read_list_t read_list[] = { +// {namespace_name, key_name, value_type, result_code, value, namespace_index}} + { .namespace_name = "storage", .key_name = "u8_key", .value_type = NVS_TYPE_U8 }, //0 OK + { .namespace_name = "storage", .key_name = "u16_key", .value_type = NVS_TYPE_U16 }, //1 OK + { .namespace_name = "storage", .key_name = "u32_key", .value_type = NVS_TYPE_U32 }, //2 OK + { .namespace_name = "storage", .key_name = "i32_key", .value_type = NVS_TYPE_I32 }, //3 OK + { .namespace_name = "storage", .key_name = "i8_key", .value_type = NVS_TYPE_U8 }, //4 Type mismatch + { .namespace_name = "storage", .key_name = "i16_key", .value_type = NVS_TYPE_I16 }, //5 Not found + }; + uint8_t size = sizeof(read_list) / sizeof(read_list[0]); + + TEST_ESP_OK(nvs_bootloader_secure_init(&xts_cfg)); + TEST_ESP_OK(nvs_bootloader_read("nvs", size, read_list)); + nvs_bootloader_secure_deinit(); + + TEST_ASSERT_EQUAL(ESP_OK, read_list[0].result_code); + TEST_ASSERT_EQUAL(ESP_OK, read_list[1].result_code); + TEST_ASSERT_EQUAL(ESP_OK, read_list[2].result_code); + TEST_ASSERT_EQUAL(ESP_OK, read_list[3].result_code); + TEST_ASSERT_EQUAL(ESP_ERR_NVS_TYPE_MISMATCH, read_list[4].result_code); + TEST_ASSERT_EQUAL(ESP_ERR_NVS_NOT_FOUND, read_list[5].result_code); + + TEST_ASSERT_EQUAL(255, read_list[0].value.u8_val); + TEST_ASSERT_EQUAL(65535, read_list[1].value.u16_val); + TEST_ASSERT_EQUAL(4294967295, read_list[2].value.u32_val); + TEST_ASSERT_EQUAL(-2147483648, read_list[3].value.i32_val); + + TEST_ESP_OK(nvs_sec_provider_deregister(sec_scheme_handle)); + restore_nvs_partition(); +} + +TEST_CASE("Verify encrypted nvs bootloader read_list result_code if bootloader read fails", "[nvs_encrypted_bootloader]") +{ + nvs_sec_cfg_t xts_cfg; + nvs_sec_scheme_t *sec_scheme_handle = NULL; + TEST_ESP_OK(configure_nvs_sec_cfg(&xts_cfg, &sec_scheme_handle)); + + nvs_bootloader_read_list_t read_list[] = { +// {namespace_name, key_name, value_type, result_code, value, namespace_index}} + { .namespace_name = "too_long_namespace", .key_name = "i32_key", .value_type = NVS_TYPE_I32 }, //0 Invalid name + { .namespace_name = "nvs", .key_name = "too_long_key_name", .value_type = NVS_TYPE_I32 }, //1 Key too long + { .namespace_name = "nvs", .key_name = "str_key", .value_type = NVS_TYPE_BLOB }, //2 Invalid arg + { .namespace_name = "nvs", .key_name = "i32_key", .value_type = NVS_TYPE_I32 }, //3 Not found + }; + uint8_t size = sizeof(read_list) / sizeof(read_list[0]); + + TEST_ESP_OK(nvs_bootloader_secure_init(&xts_cfg)); + esp_err_t ret = nvs_bootloader_read("nvs", size, read_list); + + TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, ret); + TEST_ASSERT_EQUAL(ESP_ERR_NVS_INVALID_NAME, read_list[0].result_code); + TEST_ASSERT_EQUAL(ESP_ERR_NVS_KEY_TOO_LONG, read_list[1].result_code); + TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, read_list[2].result_code); + TEST_ASSERT_EQUAL(ESP_ERR_NVS_NOT_FOUND, read_list[3].result_code); + + nvs_bootloader_secure_deinit(); + + TEST_ESP_OK(nvs_sec_provider_deregister(sec_scheme_handle)); + restore_nvs_partition(); +} + +TEST_CASE("Verify nvs_bootloader_read_encrypted failure cases", "[nvs_encrypted_bootloader]") +{ + nvs_sec_cfg_t xts_cfg; + nvs_sec_scheme_t *sec_scheme_handle = NULL; + TEST_ESP_OK(configure_nvs_sec_cfg(&xts_cfg, &sec_scheme_handle)); + + nvs_bootloader_read_list_t read_list[] = { +// {namespace_name, key_name, value_type, result_code, value, namespace_index}} + { "nvs", "i32_key", NVS_TYPE_I32, ESP_OK, {0}, 0} + }; + uint8_t size = sizeof(read_list) / sizeof(read_list[0]); + + TEST_ESP_OK(nvs_bootloader_secure_init(&xts_cfg)); + esp_err_t ret = nvs_bootloader_read("nvs_partition_name_too_long", size, read_list); + TEST_ASSERT_EQUAL(ESP_ERR_NVS_INVALID_NAME, ret); + + ret = nvs_bootloader_read("nvs_part", size, read_list); + TEST_ASSERT_EQUAL(ESP_ERR_NVS_PART_NOT_FOUND, ret); + + nvs_bootloader_secure_deinit(); + TEST_ESP_OK(nvs_sec_provider_deregister(sec_scheme_handle)); + restore_nvs_partition(); +} diff --git a/components/nvs_flash/test_apps_bootloader/pytest_nvs_bootloader_support.py b/components/nvs_flash/test_apps_bootloader/pytest_nvs_bootloader_support.py index b7562bf5a0..b44656e19d 100644 --- a/components/nvs_flash/test_apps_bootloader/pytest_nvs_bootloader_support.py +++ b/components/nvs_flash/test_apps_bootloader/pytest_nvs_bootloader_support.py @@ -1,12 +1,36 @@ -# SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD -# SPDX-License-Identifier: CC0-1.0 +# SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD +# SPDX-License-Identifier: Apache-2.0 import pytest -from pytest_embedded import Dut +from pytest_embedded_idf.dut import IdfDut @pytest.mark.esp32 @pytest.mark.esp32c3 @pytest.mark.generic @pytest.mark.parametrize('config', ['default'], indirect=True) -def test_nvs_bootloader_support(dut: Dut) -> None: +def test_nvs_bootloader_support(dut: IdfDut) -> None: + dut.run_all_single_board_cases(group='!nvs_encrypted_bootloader', timeout=120) + + +@pytest.mark.esp32c3 +@pytest.mark.nvs_encr_hmac +@pytest.mark.parametrize('config', ['nvs_enc_hmac'], indirect=True) +def test_nvs_bootloader_support_encr_hmac(dut: IdfDut) -> None: + dut.run_all_single_board_cases() + + +@pytest.mark.esp32 +@pytest.mark.esp32c3 +@pytest.mark.flash_encryption +@pytest.mark.parametrize('config', ['nvs_enc_flash_enc'], indirect=True) +def test_nvs_bootloader_support_encr_flash_enc(dut: IdfDut) -> None: + # Erase the nvs_key partition + dut.serial.erase_partition('nvs_key') + dut.run_all_single_board_cases() + + +@pytest.mark.esp32c3 +@pytest.mark.nvs_encr_hmac +@pytest.mark.parametrize('config', ['nvs_enc_hmac_no_cfg'], indirect=True) +def test_nvs_bootloader_support_encr_hmac_no_cfg(dut: IdfDut) -> None: dut.run_all_single_board_cases() diff --git a/components/nvs_flash/test_apps_bootloader/sdkconfig.ci.nvs_enc_flash_enc b/components/nvs_flash/test_apps_bootloader/sdkconfig.ci.nvs_enc_flash_enc new file mode 100644 index 0000000000..13c6b00aaf --- /dev/null +++ b/components/nvs_flash/test_apps_bootloader/sdkconfig.ci.nvs_enc_flash_enc @@ -0,0 +1,15 @@ +# Enabling Flash Encryption +CONFIG_SECURE_FLASH_ENC_ENABLED=y +CONFIG_SECURE_FLASH_ENCRYPTION_MODE_DEVELOPMENT=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_SECURE_FLASH_REQUIRE_ALREADY_ENABLED=y + +# Enabling NVS Encryption (Flash Encryption-based scheme) +CONFIG_NVS_ENCRYPTION=y +CONFIG_NVS_SEC_KEY_PROTECT_USING_FLASH_ENC=y + +CONFIG_PARTITION_TABLE_SINGLE_APP_ENCRYPTED_NVS=y diff --git a/components/nvs_flash/test_apps_bootloader/sdkconfig.ci.nvs_enc_hmac b/components/nvs_flash/test_apps_bootloader/sdkconfig.ci.nvs_enc_hmac new file mode 100644 index 0000000000..3f0d0fc27c --- /dev/null +++ b/components/nvs_flash/test_apps_bootloader/sdkconfig.ci.nvs_enc_hmac @@ -0,0 +1,15 @@ +# NOTE: The runner for this test-app has flash-encryption enabled +# Enabling Flash Encryption +CONFIG_SECURE_FLASH_ENC_ENABLED=y +CONFIG_SECURE_FLASH_ENCRYPTION_MODE_DEVELOPMENT=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_SECURE_FLASH_REQUIRE_ALREADY_ENABLED=y + +# Enabling NVS Encryption (HMAC-based scheme) +CONFIG_NVS_ENCRYPTION=y +CONFIG_NVS_SEC_KEY_PROTECT_USING_HMAC=y +CONFIG_NVS_SEC_HMAC_EFUSE_KEY_ID=0 diff --git a/components/nvs_flash/test_apps_bootloader/sdkconfig.ci.nvs_enc_hmac_no_cfg b/components/nvs_flash/test_apps_bootloader/sdkconfig.ci.nvs_enc_hmac_no_cfg new file mode 100644 index 0000000000..7541167a38 --- /dev/null +++ b/components/nvs_flash/test_apps_bootloader/sdkconfig.ci.nvs_enc_hmac_no_cfg @@ -0,0 +1,12 @@ +# NOTE: The runner for this test-app has flash-encryption enabled +# Enabling Flash Encryption +CONFIG_SECURE_FLASH_ENC_ENABLED=y +CONFIG_SECURE_FLASH_ENCRYPTION_MODE_DEVELOPMENT=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_SECURE_FLASH_REQUIRE_ALREADY_ENABLED=y + +CONFIG_NVS_ENCRYPTION=n diff --git a/components/nvs_flash/test_apps_bootloader/sdkconfig.defaults.esp32c2 b/components/nvs_flash/test_apps_bootloader/sdkconfig.defaults.esp32c2 new file mode 100644 index 0000000000..5e3a3c88f4 --- /dev/null +++ b/components/nvs_flash/test_apps_bootloader/sdkconfig.defaults.esp32c2 @@ -0,0 +1,2 @@ +CONFIG_IDF_TARGET="esp32c2" +CONFIG_MBEDTLS_USE_CRYPTO_ROM_IMPL_BOOTLOADER=y From c7f86301e6fa41269a3942653cfe78b9132d5ff1 Mon Sep 17 00:00:00 2001 From: "harshal.patil" Date: Thu, 2 Jan 2025 12:45:23 +0530 Subject: [PATCH 09/11] change(bootloader): Increase the iram_seg size of bootloader to fit NVS encryption --- components/bootloader/subproject/main/ld/esp32c2/bootloader.ld | 2 +- components/bootloader/subproject/main/ld/esp32c3/bootloader.ld | 2 +- components/bootloader/subproject/main/ld/esp32c5/bootloader.ld | 2 +- components/bootloader/subproject/main/ld/esp32c6/bootloader.ld | 2 +- components/bootloader/subproject/main/ld/esp32c61/bootloader.ld | 2 +- components/bootloader/subproject/main/ld/esp32h2/bootloader.ld | 2 +- components/bootloader/subproject/main/ld/esp32h21/bootloader.ld | 2 +- components/bootloader/subproject/main/ld/esp32p4/bootloader.ld | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/components/bootloader/subproject/main/ld/esp32c2/bootloader.ld b/components/bootloader/subproject/main/ld/esp32c2/bootloader.ld index f80441cebb..394cc3690b 100644 --- a/components/bootloader/subproject/main/ld/esp32c2/bootloader.ld +++ b/components/bootloader/subproject/main/ld/esp32c2/bootloader.ld @@ -27,7 +27,7 @@ bootloader_usable_dram_end = 0x3fcdcb70; bootloader_stack_overhead = 0x2000; /* For safety margin between bootloader data section and startup stacks */ bootloader_dram_seg_len = 0x5000; bootloader_iram_loader_seg_len = 0x7000; -bootloader_iram_seg_len = 0x2000; +bootloader_iram_seg_len = 0x2800; /* Start of the lower region is determined by region size and the end of the higher region */ bootloader_dram_seg_end = bootloader_usable_dram_end - bootloader_stack_overhead; diff --git a/components/bootloader/subproject/main/ld/esp32c3/bootloader.ld b/components/bootloader/subproject/main/ld/esp32c3/bootloader.ld index 21f19c14fe..e455c2e128 100644 --- a/components/bootloader/subproject/main/ld/esp32c3/bootloader.ld +++ b/components/bootloader/subproject/main/ld/esp32c3/bootloader.ld @@ -27,7 +27,7 @@ bootloader_usable_dram_end = 0x3fcdc710; bootloader_stack_overhead = 0x2000; /* For safety margin between bootloader data section and startup stacks */ bootloader_dram_seg_len = 0x5000; bootloader_iram_loader_seg_len = 0x7000; -bootloader_iram_seg_len = 0x2000; +bootloader_iram_seg_len = 0x2800; /* Start of the lower region is determined by region size and the end of the higher region */ bootloader_dram_seg_end = bootloader_usable_dram_end - bootloader_stack_overhead; diff --git a/components/bootloader/subproject/main/ld/esp32c5/bootloader.ld b/components/bootloader/subproject/main/ld/esp32c5/bootloader.ld index e389464825..0e7a30baa0 100644 --- a/components/bootloader/subproject/main/ld/esp32c5/bootloader.ld +++ b/components/bootloader/subproject/main/ld/esp32c5/bootloader.ld @@ -24,7 +24,7 @@ bootloader_usable_dram_end = 0x4085c9a0; bootloader_stack_overhead = 0x2000; /* For safety margin between bootloader data section and startup stacks */ bootloader_dram_seg_len = 0x5000; bootloader_iram_loader_seg_len = 0x7000; -bootloader_iram_seg_len = 0x2200; +bootloader_iram_seg_len = 0x2A00; /* Start of the lower region is determined by region size and the end of the higher region */ bootloader_dram_seg_end = bootloader_usable_dram_end - bootloader_stack_overhead; diff --git a/components/bootloader/subproject/main/ld/esp32c6/bootloader.ld b/components/bootloader/subproject/main/ld/esp32c6/bootloader.ld index 9afa821408..8c47badd70 100644 --- a/components/bootloader/subproject/main/ld/esp32c6/bootloader.ld +++ b/components/bootloader/subproject/main/ld/esp32c6/bootloader.ld @@ -24,7 +24,7 @@ bootloader_usable_dram_end = 0x4087c610; bootloader_stack_overhead = 0x2000; /* For safety margin between bootloader data section and startup stacks */ bootloader_dram_seg_len = 0x5000; bootloader_iram_loader_seg_len = 0x7000; -bootloader_iram_seg_len = 0x2500; +bootloader_iram_seg_len = 0x2D00; /* Start of the lower region is determined by region size and the end of the higher region */ bootloader_dram_seg_end = bootloader_usable_dram_end - bootloader_stack_overhead; diff --git a/components/bootloader/subproject/main/ld/esp32c61/bootloader.ld b/components/bootloader/subproject/main/ld/esp32c61/bootloader.ld index 160619c0e1..a5134ccd9f 100644 --- a/components/bootloader/subproject/main/ld/esp32c61/bootloader.ld +++ b/components/bootloader/subproject/main/ld/esp32c61/bootloader.ld @@ -24,7 +24,7 @@ bootloader_usable_dram_end = 0x4084ca70; bootloader_stack_overhead = 0x2000; /* For safety margin between bootloader data section and startup stacks */ bootloader_dram_seg_len = 0x5000; bootloader_iram_loader_seg_len = 0x7000; -bootloader_iram_seg_len = 0x2500; +bootloader_iram_seg_len = 0x2D00; /* Start of the lower region is determined by region size and the end of the higher region */ bootloader_dram_seg_end = bootloader_usable_dram_end - bootloader_stack_overhead; diff --git a/components/bootloader/subproject/main/ld/esp32h2/bootloader.ld b/components/bootloader/subproject/main/ld/esp32h2/bootloader.ld index 9ae2a74d8c..1880bf6792 100644 --- a/components/bootloader/subproject/main/ld/esp32h2/bootloader.ld +++ b/components/bootloader/subproject/main/ld/esp32h2/bootloader.ld @@ -25,7 +25,7 @@ bootloader_usable_dram_end = 0x4084cfd0; bootloader_stack_overhead = 0x2000; /* For safety margin between bootloader data section and startup stacks */ bootloader_dram_seg_len = 0x5000; bootloader_iram_loader_seg_len = 0x7000; -bootloader_iram_seg_len = 0x2500; +bootloader_iram_seg_len = 0x2D00; /* Start of the lower region is determined by region size and the end of the higher region */ bootloader_dram_seg_end = bootloader_usable_dram_end - bootloader_stack_overhead; diff --git a/components/bootloader/subproject/main/ld/esp32h21/bootloader.ld b/components/bootloader/subproject/main/ld/esp32h21/bootloader.ld index c2e755175e..ea03761c95 100644 --- a/components/bootloader/subproject/main/ld/esp32h21/bootloader.ld +++ b/components/bootloader/subproject/main/ld/esp32h21/bootloader.ld @@ -25,7 +25,7 @@ bootloader_usable_dram_end = 0x40849a78; bootloader_stack_overhead = 0x2000; /* For safety margin between bootloader data section and startup stacks */ bootloader_dram_seg_len = 0x5000; bootloader_iram_loader_seg_len = 0x7000; -bootloader_iram_seg_len = 0x2500; +bootloader_iram_seg_len = 0x2D00; /* Start of the lower region is determined by region size and the end of the higher region */ bootloader_dram_seg_end = bootloader_usable_dram_end - bootloader_stack_overhead; diff --git a/components/bootloader/subproject/main/ld/esp32p4/bootloader.ld b/components/bootloader/subproject/main/ld/esp32p4/bootloader.ld index b73ae430f4..7f45fa8a63 100644 --- a/components/bootloader/subproject/main/ld/esp32p4/bootloader.ld +++ b/components/bootloader/subproject/main/ld/esp32p4/bootloader.ld @@ -26,7 +26,7 @@ bootloader_usable_dram_end = 0x4ff3abd0; bootloader_stack_overhead = 0x2000; /* For safety margin between bootloader data section and startup stacks */ bootloader_dram_seg_len = 0x5000; bootloader_iram_loader_seg_len = 0x7000; -bootloader_iram_seg_len = 0x2000; +bootloader_iram_seg_len = 0x2D00; /* Start of the lower region is determined by region size and the end of the higher region */ bootloader_dram_seg_end = bootloader_usable_dram_end - bootloader_stack_overhead; From f205476ffe9b25fa28bb4d76b2f4f6daf21063e5 Mon Sep 17 00:00:00 2001 From: "harshal.patil" Date: Tue, 7 Jan 2025 17:04:02 +0530 Subject: [PATCH 10/11] fix(nvs_flash): Add encrypted-flash as the dependency in nvs_create_partition_image() --- components/nvs_flash/project_include.cmake | 1 + 1 file changed, 1 insertion(+) diff --git a/components/nvs_flash/project_include.cmake b/components/nvs_flash/project_include.cmake index a427c1ed01..b465024a2b 100644 --- a/components/nvs_flash/project_include.cmake +++ b/components/nvs_flash/project_include.cmake @@ -53,6 +53,7 @@ function(nvs_create_partition_image partition csv) if(arg_FLASH_IN_PROJECT) esptool_py_flash_to_partition(flash "${partition}" "${image_file}") add_dependencies(flash nvs_${partition}_bin) + add_dependencies(encrypted-flash nvs_${partition}_bin) endif() else() set(message From 62df1017ad126ef6549d5442784cc292b8bdfab2 Mon Sep 17 00:00:00 2001 From: "harshal.patil" Date: Thu, 16 Jan 2025 18:05:58 +0530 Subject: [PATCH 11/11] change(nvs_sec_provider): Change NVS encryption's default efuse HMAC key ID to -1 --- components/nvs_sec_provider/Kconfig | 4 ++-- components/nvs_sec_provider/nvs_bootloader_sec_provider.c | 2 +- components/nvs_sec_provider/nvs_sec_provider.c | 2 +- docs/en/api-reference/storage/nvs_encryption.rst | 2 +- docs/zh_CN/api-reference/storage/nvs_encryption.rst | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/components/nvs_sec_provider/Kconfig b/components/nvs_sec_provider/Kconfig index e50839d3a8..4ed79c4e87 100644 --- a/components/nvs_sec_provider/Kconfig +++ b/components/nvs_sec_provider/Kconfig @@ -32,8 +32,8 @@ menu "NVS Security Provider" config NVS_SEC_HMAC_EFUSE_KEY_ID int "eFuse key ID storing the HMAC key" depends on NVS_SEC_KEY_PROTECT_USING_HMAC - range 0 6 - default 6 + range -1 5 + default -1 help eFuse block key ID storing the HMAC key for deriving the NVS encryption keys diff --git a/components/nvs_sec_provider/nvs_bootloader_sec_provider.c b/components/nvs_sec_provider/nvs_bootloader_sec_provider.c index 052abda446..d8ffbde1b8 100644 --- a/components/nvs_sec_provider/nvs_bootloader_sec_provider.c +++ b/components/nvs_sec_provider/nvs_bootloader_sec_provider.c @@ -96,7 +96,7 @@ esp_err_t nvs_sec_provider_register_flash_enc(const nvs_sec_config_flash_enc_t * static nvs_sec_config_hmac_t nvs_sec_config_hmac_scheme_data; -#if CONFIG_NVS_SEC_HMAC_EFUSE_KEY_ID > 5 +#if CONFIG_NVS_SEC_HMAC_EFUSE_KEY_ID < 0 #error "NVS Encryption (HMAC): Configured eFuse block (CONFIG_NVS_SEC_HMAC_EFUSE_KEY_ID) out of range!" #endif diff --git a/components/nvs_sec_provider/nvs_sec_provider.c b/components/nvs_sec_provider/nvs_sec_provider.c index c7f592dd43..ab9a3e1533 100644 --- a/components/nvs_sec_provider/nvs_sec_provider.c +++ b/components/nvs_sec_provider/nvs_sec_provider.c @@ -107,7 +107,7 @@ ESP_SYSTEM_INIT_FN(nvs_sec_provider_register_flash_enc_scheme, SECONDARY, BIT(0) #if SOC_HMAC_SUPPORTED -#if CONFIG_NVS_SEC_HMAC_EFUSE_KEY_ID > 5 +#if CONFIG_NVS_SEC_HMAC_EFUSE_KEY_ID < 0 #error "NVS Encryption (HMAC): Configured eFuse block (CONFIG_NVS_SEC_HMAC_EFUSE_KEY_ID) out of range!" #endif diff --git a/docs/en/api-reference/storage/nvs_encryption.rst b/docs/en/api-reference/storage/nvs_encryption.rst index 826bd5e8cf..cbae3878dd 100644 --- a/docs/en/api-reference/storage/nvs_encryption.rst +++ b/docs/en/api-reference/storage/nvs_encryption.rst @@ -121,7 +121,7 @@ It is possible for an application to use different keys for different NVS partit .. note:: - The valid range for the config :ref:`CONFIG_NVS_SEC_HMAC_EFUSE_KEY_ID` is from ``0`` (:cpp:enumerator:`hmac_key_id_t::HMAC_KEY0`) to ``5`` (:cpp:enumerator:`hmac_key_id_t::HMAC_KEY5`). By default, the config is set to ``6`` (:cpp:enumerator:`hmac_key_id_t::HMAC_KEY_MAX`), which have to be configured before building the user application. + The valid range for the config :ref:`CONFIG_NVS_SEC_HMAC_EFUSE_KEY_ID` is from ``0`` (:cpp:enumerator:`hmac_key_id_t::HMAC_KEY0`) to ``5`` (:cpp:enumerator:`hmac_key_id_t::HMAC_KEY5`). By default, the config is set to ``-1``, which have to be configured before building the user application. - If no key is found, a key is generated internally and stored at the eFuse block specified at :ref:`CONFIG_NVS_SEC_HMAC_EFUSE_KEY_ID`. - If a key is found with the purpose :cpp:enumerator:`esp_efuse_purpose_t::ESP_EFUSE_KEY_PURPOSE_HMAC_UP`, the same is used for the derivation of the XTS encryption keys. diff --git a/docs/zh_CN/api-reference/storage/nvs_encryption.rst b/docs/zh_CN/api-reference/storage/nvs_encryption.rst index 13c3bb59d3..49dd20d283 100644 --- a/docs/zh_CN/api-reference/storage/nvs_encryption.rst +++ b/docs/zh_CN/api-reference/storage/nvs_encryption.rst @@ -121,7 +121,7 @@ NVS 密钥分区 .. note:: - :ref:`CONFIG_NVS_SEC_HMAC_EFUSE_KEY_ID` 配置的有效范围为 ``0`` (:cpp:enumerator:`hmac_key_id_t::HMAC_KEY0`) 到 ``5`` (:cpp:enumerator:`hmac_key_id_t::HMAC_KEY5`)。默认情况下该配置为 ``6`` (:cpp:enumerator:`hmac_key_id_t::HMAC_KEY_MAX`),须在构建用户应用程序之前进行修改。 + :ref:`CONFIG_NVS_SEC_HMAC_EFUSE_KEY_ID` 配置的有效范围为 ``0`` (:cpp:enumerator:`hmac_key_id_t::HMAC_KEY0`) 到 ``5`` (:cpp:enumerator:`hmac_key_id_t::HMAC_KEY5`)。默认情况下该配置为 ``-1``,须在构建用户应用程序之前进行修改。 - 如果找不到密钥,会内部生成一个密钥,并储存在 :ref:`CONFIG_NVS_SEC_HMAC_EFUSE_KEY_ID` 指定的 eFuse 块中。 - 如果找到用于 :cpp:enumerator:`esp_efuse_purpose_t::ESP_EFUSE_KEY_PURPOSE_HMAC_UP` 的密钥,该密钥也会用于 XTS 加密密钥的生成。