From c1bed366ba733426825e98f36f090fbed142bf57 Mon Sep 17 00:00:00 2001 From: Laukik Hase Date: Wed, 5 Apr 2023 18:03:56 +0530 Subject: [PATCH 1/6] nvs_flash: Add support for HMAC-based NVS encryption keys protection scheme - This features allows the NVS encryption keys to be derived and protected using the HMAC peripheral. Since the encryption keys are derived at runtime, they are not stored anywhere in the flash and hence this feature does not require a separate `nvs_keys` partition. --- components/bootloader/Kconfig.projbuild | 1 + components/nvs_flash/CMakeLists.txt | 4 +- components/nvs_flash/Kconfig | 8 +- components/nvs_flash/include/nvs_flash.h | 108 ++++++++++++++---- components/nvs_flash/src/nvs_api.cpp | 68 +++++++---- .../nvs_flash/src/nvs_partition_lookup.cpp | 9 +- .../nvs_flash/src/nvs_partition_lookup.hpp | 2 - .../nvs_flash/src/nvs_partition_manager.cpp | 6 +- .../nvs_flash/src/nvs_partition_manager.hpp | 2 - tools/ci/check_copyright_ignore.txt | 1 - 10 files changed, 147 insertions(+), 62 deletions(-) diff --git a/components/bootloader/Kconfig.projbuild b/components/bootloader/Kconfig.projbuild index 918810fd22..339d636d78 100644 --- a/components/bootloader/Kconfig.projbuild +++ b/components/bootloader/Kconfig.projbuild @@ -776,6 +776,7 @@ menu "Security features" bool "Enable flash encryption on boot (READ DOCS FIRST)" default N select SPI_FLASH_ENABLE_ENCRYPTED_READ_WRITE + select NVS_ENCRYPTION help If this option is set, flash contents will be encrypted by the bootloader on first boot. diff --git a/components/nvs_flash/CMakeLists.txt b/components/nvs_flash/CMakeLists.txt index a26493ce3d..c0d9421f9f 100644 --- a/components/nvs_flash/CMakeLists.txt +++ b/components/nvs_flash/CMakeLists.txt @@ -36,9 +36,7 @@ if(${target} STREQUAL "linux") elseif(NOT CMAKE_HOST_SYSTEM_NAME STREQUAL "Darwin") message(WARNING "Missing LIBBSD library. Install libbsd-dev package and/or check linker directories.") endif() -endif() - -if(CONFIG_NVS_ENCRYPTION) +else() target_sources(${COMPONENT_LIB} PRIVATE "src/nvs_encrypted_partition.cpp") target_link_libraries(${COMPONENT_LIB} PRIVATE idf::mbedtls) endif() diff --git a/components/nvs_flash/Kconfig b/components/nvs_flash/Kconfig index 5cabecab27..38d0a311f3 100644 --- a/components/nvs_flash/Kconfig +++ b/components/nvs_flash/Kconfig @@ -2,13 +2,13 @@ menu "NVS" config NVS_ENCRYPTION bool "Enable NVS encryption" - default y - depends on SECURE_FLASH_ENC_ENABLED + depends on SECURE_FLASH_ENC_ENABLED || SOC_HMAC_SUPPORTED + default y if SECURE_FLASH_ENC_ENABLED help This option enables encryption for NVS. When enabled, XTS-AES is used to encrypt the complete NVS data, except the page headers. It requires XTS encryption keys - to be stored in an encrypted partition. This means enabling flash encryption is - a pre-requisite for this feature. + to be stored in an encrypted partition (enabling flash encryption is mandatory here) + or to be derived from an HMAC key burnt in eFuse. config NVS_COMPATIBLE_PRE_V4_3_ENCRYPTION_FLAG bool "NVS partition encrypted flag compatible with ESP-IDF before v4.3" diff --git a/components/nvs_flash/include/nvs_flash.h b/components/nvs_flash/include/nvs_flash.h index 7e3d6a7985..6503936f6b 100644 --- a/components/nvs_flash/include/nvs_flash.h +++ b/components/nvs_flash/include/nvs_flash.h @@ -1,16 +1,8 @@ -// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at - -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +/* + * SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ #ifndef nvs_flash_h #define nvs_flash_h @@ -32,6 +24,27 @@ typedef struct { uint8_t tky[NVS_KEY_SIZE]; /*!< XTS tweak key */ } nvs_sec_cfg_t; +/** + * @brief Callback function prototype for generating the NVS encryption keys + */ +typedef esp_err_t (*nvs_flash_generate_keys_t) (const void *scheme_data, nvs_sec_cfg_t* cfg); + +/** + * @brief Callback function prototype for reading the NVS encryption keys + */ +typedef esp_err_t (*nvs_flash_read_cfg_t) (const void *scheme_data, nvs_sec_cfg_t* cfg); + +/** + * @brief NVS encryption: Security scheme configuration structure + */ +typedef struct +{ + int scheme_id; /*!< Security Scheme ID (E.g. HMAC) */ + void *scheme_data; /*!< Scheme-specific data (E.g. eFuse block for HMAC-based key generation) */ + nvs_flash_generate_keys_t nvs_flash_key_gen; /*!< Callback for the nvs_flash_key_gen implementation */ + nvs_flash_read_cfg_t nvs_flash_read_cfg; /*!< Callback for the nvs_flash_read_keys implementation */ +} nvs_sec_scheme_t; + /** * @brief Initialize the default NVS partition. * @@ -220,9 +233,9 @@ esp_err_t nvs_flash_secure_init_partition(const char *partition_label, nvs_sec_c * * * @return - * -ESP_OK, if cfg was read successfully; - * -ESP_INVALID_ARG, if partition or cfg; - * -or error codes from esp_partition_write/erase APIs. + * - ESP_OK, if cfg was read successfully; + * - ESP_ERR_INVALID_ARG, if partition or cfg is NULL; + * - or error codes from esp_partition_write/erase APIs. */ esp_err_t nvs_flash_generate_keys(const esp_partition_t* partition, nvs_sec_cfg_t* cfg); @@ -240,15 +253,68 @@ esp_err_t nvs_flash_generate_keys(const esp_partition_t* partition, nvs_sec_cfg_ * @note Provided partition is assumed to be marked 'encrypted'. * * @return - * -ESP_OK, if cfg was read successfully; - * -ESP_INVALID_ARG, if partition or cfg; - * -ESP_ERR_NVS_KEYS_NOT_INITIALIZED, if the partition is not yet written with keys. - * -ESP_ERR_NVS_CORRUPT_KEY_PART, if the partition containing keys is found to be corrupt - * -or error codes from esp_partition_read API. + * - ESP_OK, if cfg was read successfully; + * - ESP_ERR_INVALID_ARG, if partition or cfg is NULL + * - ESP_ERR_NVS_KEYS_NOT_INITIALIZED, if the partition is not yet written with keys. + * - ESP_ERR_NVS_CORRUPT_KEY_PART, if the partition containing keys is found to be corrupt + * - or error codes from esp_partition_read API. */ esp_err_t nvs_flash_read_security_cfg(const esp_partition_t* partition, nvs_sec_cfg_t* cfg); +/** + * @brief Registers the given security scheme for NVS encryption + * The scheme registered with sec_scheme_id by this API be used as + * the default security scheme for the "nvs" partition. + * Users will have to call this API explicitly in their application. + * + * @param[in] scheme_cfg Pointer to the security scheme configuration structure + * that the user (or the nvs_key_provider) wants to register. + * + * @return + * - ESP_OK, if security scheme registration succeeds; + * - ESP_ERR_INVALID_ARG, if scheme_cfg is NULL; + * - ESP_FAIL, if security scheme registration fails + */ +esp_err_t nvs_flash_register_security_scheme(nvs_sec_scheme_t *scheme_cfg); + +/** + * @brief Fetch the configuration structure for the default active + * security scheme for NVS encryption + * + * @return Pointer to the default active security scheme configuration + * (NULL if no scheme is registered yet i.e. active) + */ +nvs_sec_scheme_t *nvs_flash_get_default_security_scheme(void); + +/** + * @brief Generate (and store) the NVS keys using the specified key-protection scheme + * + * @param[in] scheme_cfg Security scheme specific configuration + * + * @param[out] cfg Security configuration (encryption keys) + * + * @return + * - ESP_OK, if cfg was populated successfully with generated encryption keys; + * - ESP_ERR_INVALID_ARG, if scheme_cfg or cfg is NULL; + * - ESP_FAIL, if the key generation process fails + */ +esp_err_t nvs_flash_generate_keys_v2(nvs_sec_scheme_t *scheme_cfg, nvs_sec_cfg_t* cfg); + +/** + * @brief Read NVS 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_flash_read_security_cfg_v2(nvs_sec_scheme_t *scheme_cfg, nvs_sec_cfg_t* cfg); + #ifdef __cplusplus } #endif diff --git a/components/nvs_flash/src/nvs_api.cpp b/components/nvs_flash/src/nvs_api.cpp index 8d8d0b013d..587f531352 100644 --- a/components/nvs_flash/src/nvs_api.cpp +++ b/components/nvs_flash/src/nvs_api.cpp @@ -23,6 +23,12 @@ #include "esp_log.h" static const char* TAG = "nvs"; +/** + * @brief Configuration structure for the active default security scheme + * for NVS Encryption + */ +static nvs_sec_scheme_t nvs_sec_default_scheme_cfg; + class NVSHandleEntry : public intrusive_list_node, public ExceptionlessAllocatable { public: NVSHandleEntry(nvs::NVSHandleSimple *handle, const char* part_name) @@ -133,28 +139,20 @@ extern "C" esp_err_t nvs_flash_init(void) { #ifdef CONFIG_NVS_ENCRYPTION esp_err_t ret = ESP_FAIL; - const esp_partition_t *key_part = esp_partition_find_first( - ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_NVS_KEYS, NULL); - if (key_part == NULL) { - ESP_LOGE(TAG, "CONFIG_NVS_ENCRYPTION is enabled, but no partition with subtype nvs_keys found in the partition table."); - return ret; - } - nvs_sec_cfg_t cfg = {}; - ret = nvs_flash_read_security_cfg(key_part, &cfg); - if (ret == ESP_ERR_NVS_KEYS_NOT_INITIALIZED) { - ESP_LOGI(TAG, "NVS key partition empty, generating keys"); - ret = nvs_flash_generate_keys(key_part, &cfg); + + ret = nvs_flash_read_security_cfg_v2(&nvs_sec_default_scheme_cfg, &cfg); + if (ret != ESP_OK) { + ESP_LOGW(TAG, "Failed to read NVS security cfg: [0x%02X] (%s)", ret, esp_err_to_name(ret)); + ESP_LOGI(TAG, "Generating NVS encr-keys..."); + ret = nvs_flash_generate_keys_v2(&nvs_sec_default_scheme_cfg, &cfg); if (ret != ESP_OK) { - ESP_LOGE(TAG, "Failed to generate keys: [0x%02X] (%s)", ret, esp_err_to_name(ret)); + ESP_LOGE(TAG, "Failed to generate NVS encr-keys: [0x%02X] (%s)", ret, esp_err_to_name(ret)); return ret; } - } else if (ret != ESP_OK) { - ESP_LOGE(TAG, "Failed to read NVS security cfg: [0x%02X] (%s)", ret, esp_err_to_name(ret)); - return ret; } - ret = nvs_flash_secure_init_partition(NVS_DEFAULT_PART_NAME, &cfg); + ret = nvs_flash_secure_init(&cfg); if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) { ESP_LOGE(TAG, "Failed to initialize NVS partition: [0x%02X] (%s)", ret, esp_err_to_name(ret)); return ret; @@ -166,7 +164,6 @@ extern "C" esp_err_t nvs_flash_init(void) #endif } -#ifdef CONFIG_NVS_ENCRYPTION extern "C" esp_err_t nvs_flash_secure_init_partition(const char *part_name, nvs_sec_cfg_t* cfg) { esp_err_t lock_result = Lock::init(); @@ -182,7 +179,6 @@ extern "C" esp_err_t nvs_flash_secure_init(nvs_sec_cfg_t* cfg) { return nvs_flash_secure_init_partition(NVS_DEFAULT_PART_NAME, cfg); } -#endif extern "C" esp_err_t nvs_flash_erase_partition(const char *part_name) { @@ -567,7 +563,7 @@ extern "C" esp_err_t nvs_get_used_entry_count(nvs_handle_t c_handle, size_t* use return err; } -#if (defined CONFIG_NVS_ENCRYPTION) && (!defined LINUX_TARGET) +#ifndef LINUX_TARGET extern "C" esp_err_t nvs_flash_generate_keys(const esp_partition_t* partition, nvs_sec_cfg_t* cfg) { @@ -704,7 +700,39 @@ extern "C" esp_err_t nvs_flash_read_security_cfg(const esp_partition_t* partitio return ESP_OK; } -#endif +#endif // ! LINUX_TARGET + +extern "C" esp_err_t nvs_flash_register_security_scheme(nvs_sec_scheme_t *scheme_cfg) +{ + if (scheme_cfg == nullptr) { + return ESP_ERR_INVALID_ARG; + } + + memcpy(&nvs_sec_default_scheme_cfg, scheme_cfg, sizeof(nvs_sec_scheme_t)); + + return ESP_OK; +} + +extern "C" nvs_sec_scheme_t *nvs_flash_get_default_security_scheme(void) +{ + return &nvs_sec_default_scheme_cfg; +} + +extern "C" esp_err_t nvs_flash_generate_keys_v2(nvs_sec_scheme_t *scheme_cfg, nvs_sec_cfg_t* cfg) +{ + if (scheme_cfg == nullptr || cfg == nullptr || scheme_cfg->nvs_flash_key_gen == nullptr) { + return ESP_ERR_INVALID_ARG; + } + return (scheme_cfg->nvs_flash_key_gen)(scheme_cfg->scheme_data, cfg); +} + +extern "C" esp_err_t nvs_flash_read_security_cfg_v2(nvs_sec_scheme_t *scheme_cfg, nvs_sec_cfg_t* cfg) +{ + if (scheme_cfg == nullptr || cfg == nullptr || scheme_cfg->nvs_flash_read_cfg == nullptr) { + return ESP_ERR_INVALID_ARG; + } + return (scheme_cfg->nvs_flash_read_cfg)(scheme_cfg->scheme_data, cfg); +} static nvs_iterator_t create_iterator(nvs::Storage *storage, nvs_type_t type) { diff --git a/components/nvs_flash/src/nvs_partition_lookup.cpp b/components/nvs_flash/src/nvs_partition_lookup.cpp index e9d27256fa..604049133a 100644 --- a/components/nvs_flash/src/nvs_partition_lookup.cpp +++ b/components/nvs_flash/src/nvs_partition_lookup.cpp @@ -1,9 +1,9 @@ #include "esp_partition.h" #include "nvs_partition_lookup.hpp" -#ifdef CONFIG_NVS_ENCRYPTION +#ifndef LINUX_TARGET #include "nvs_encrypted_partition.hpp" -#endif // CONFIG_NVS_ENCRYPTION +#endif // ! LINUX_TARGET namespace nvs { @@ -32,7 +32,7 @@ esp_err_t lookup_nvs_partition(const char* label, NVSPartition **p) return ESP_OK; } -#ifdef CONFIG_NVS_ENCRYPTION +#ifndef LINUX_TARGET esp_err_t lookup_nvs_encrypted_partition(const char* label, nvs_sec_cfg_t* cfg, NVSPartition **p) { const esp_partition_t* esp_partition = esp_partition_find_first( @@ -61,8 +61,7 @@ esp_err_t lookup_nvs_encrypted_partition(const char* label, nvs_sec_cfg_t* cfg, return ESP_OK; } - -#endif // CONFIG_NVS_ENCRYPTION +#endif // ! LINUX_TARGET } // partition_lookup diff --git a/components/nvs_flash/src/nvs_partition_lookup.hpp b/components/nvs_flash/src/nvs_partition_lookup.hpp index 62a8e8b6fe..5dcd5d692c 100644 --- a/components/nvs_flash/src/nvs_partition_lookup.hpp +++ b/components/nvs_flash/src/nvs_partition_lookup.hpp @@ -11,9 +11,7 @@ namespace partition_lookup { esp_err_t lookup_nvs_partition(const char* label, NVSPartition **p); -#ifdef CONFIG_NVS_ENCRYPTION esp_err_t lookup_nvs_encrypted_partition(const char* label, nvs_sec_cfg_t* cfg, NVSPartition **p); -#endif // CONFIG_NVS_ENCRYPTION } // partition_lookup diff --git a/components/nvs_flash/src/nvs_partition_manager.cpp b/components/nvs_flash/src/nvs_partition_manager.cpp index e90137eb2f..f43ca130a1 100644 --- a/components/nvs_flash/src/nvs_partition_manager.cpp +++ b/components/nvs_flash/src/nvs_partition_manager.cpp @@ -8,9 +8,9 @@ #include "nvs_partition_lookup.hpp" #include "nvs_internal.h" -#ifdef CONFIG_NVS_ENCRYPTION +#ifndef LINUX_TARGET #include "nvs_encrypted_partition.hpp" -#endif // CONFIG_NVS_ENCRYPTION +#endif // ! LINUX_TARGET namespace nvs { @@ -100,7 +100,6 @@ esp_err_t NVSPartitionManager::init_custom(Partition *partition, uint32_t baseSe return err; } -#ifdef CONFIG_NVS_ENCRYPTION #ifdef ESP_PLATFORM esp_err_t NVSPartitionManager::secure_init_partition(const char *part_name, nvs_sec_cfg_t* cfg) { @@ -140,7 +139,6 @@ esp_err_t NVSPartitionManager::secure_init_partition(const char *part_name, nvs_ return ESP_OK; } #endif // ESP_PLATFORM -#endif // CONFIG_NVS_ENCRYPTION esp_err_t NVSPartitionManager::deinit_partition(const char *partition_label) { diff --git a/components/nvs_flash/src/nvs_partition_manager.hpp b/components/nvs_flash/src/nvs_partition_manager.hpp index ebed1214e3..4b437d81e2 100644 --- a/components/nvs_flash/src/nvs_partition_manager.hpp +++ b/components/nvs_flash/src/nvs_partition_manager.hpp @@ -24,9 +24,7 @@ public: esp_err_t init_custom(Partition *partition, uint32_t baseSector, uint32_t sectorCount); -#ifdef CONFIG_NVS_ENCRYPTION esp_err_t secure_init_partition(const char *part_name, nvs_sec_cfg_t* cfg); -#endif esp_err_t deinit_partition(const char *partition_label); diff --git a/tools/ci/check_copyright_ignore.txt b/tools/ci/check_copyright_ignore.txt index 3efe7da1cf..fb5dc0f312 100644 --- a/tools/ci/check_copyright_ignore.txt +++ b/tools/ci/check_copyright_ignore.txt @@ -716,7 +716,6 @@ components/mqtt/host_test/mocks/include/freertos/portmacro.h components/mqtt/host_test/mocks/include/machine/endian.h components/mqtt/host_test/mocks/include/sys/queue.h components/nvs_flash/host_test/nvs_page_test/main/nvs_page_test.cpp -components/nvs_flash/include/nvs_flash.h components/nvs_flash/include/nvs_handle.hpp components/nvs_flash/src/nvs_cxx_api.cpp components/nvs_flash/src/nvs_encrypted_partition.hpp From 8efe2f86e9b5fc7c7ae4c388a70d0248522068b6 Mon Sep 17 00:00:00 2001 From: Laukik Hase Date: Wed, 5 Apr 2023 18:04:18 +0530 Subject: [PATCH 2/6] nvs_flash: Added helper component `nvs_sec_provider` - For processing NVS encryption-related security configuration --- .gitlab/CODEOWNERS | 1 + components/esp_common/src/esp_err_to_name.c | 22 ++ components/nvs_sec_provider/CMakeLists.txt | 12 + components/nvs_sec_provider/Kconfig | 46 +++ .../include/nvs_sec_provider.h | 114 +++++++ .../nvs_sec_provider/nvs_sec_provider.c | 279 ++++++++++++++++++ components/partition_table/Kconfig.projbuild | 7 +- 7 files changed, 478 insertions(+), 3 deletions(-) create mode 100644 components/nvs_sec_provider/CMakeLists.txt create mode 100644 components/nvs_sec_provider/Kconfig create mode 100644 components/nvs_sec_provider/include/nvs_sec_provider.h create mode 100644 components/nvs_sec_provider/nvs_sec_provider.c diff --git a/.gitlab/CODEOWNERS b/.gitlab/CODEOWNERS index 0cbcd47e90..f76ba310af 100644 --- a/.gitlab/CODEOWNERS +++ b/.gitlab/CODEOWNERS @@ -125,6 +125,7 @@ /components/mqtt/ @esp-idf-codeowners/network /components/newlib/ @esp-idf-codeowners/system @esp-idf-codeowners/tools /components/nvs_flash/ @esp-idf-codeowners/storage +/components/nvs_sec_provider/ @esp-idf-codeowners/storage @esp-idf-codeowners/security /components/openthread/ @esp-idf-codeowners/ieee802154 /components/partition_table/ @esp-idf-codeowners/system /components/perfmon/ @esp-idf-codeowners/tools diff --git a/components/esp_common/src/esp_err_to_name.c b/components/esp_common/src/esp_err_to_name.c index 01017e2c53..0fbf57a327 100644 --- a/components/esp_common/src/esp_err_to_name.c +++ b/components/esp_common/src/esp_err_to_name.c @@ -72,6 +72,9 @@ #if __has_include("nvs.h") #include "nvs.h" #endif +#if __has_include("nvs_sec_provider.h") +#include "nvs_sec_provider.h" +#endif #if __has_include("spi_flash_mmap.h") #include "spi_flash_mmap.h" #endif @@ -829,6 +832,25 @@ static const esp_err_msg_t esp_err_msg_table[] = { # endif # ifdef ESP_ERR_TCP_TRANSPORT_NO_MEM ERR_TBL_IT(ESP_ERR_TCP_TRANSPORT_NO_MEM), /* 57348 0xe004 Memory allocation failed */ +# endif + // components/nvs_sec_provider/include/nvs_sec_provider.h +# ifdef ESP_ERR_NVS_SEC_BASE + ERR_TBL_IT(ESP_ERR_NVS_SEC_BASE), /* 61440 0xf000 Starting number of error codes */ +# endif +# ifdef ESP_ERR_NVS_SEC_HMAC_KEY_NOT_FOUND + ERR_TBL_IT(ESP_ERR_NVS_SEC_HMAC_KEY_NOT_FOUND), /* 61441 0xf001 HMAC Key required to generate the NVS + encryption keys not found */ +# endif +# ifdef ESP_ERR_NVS_SEC_HMAC_KEY_BLK_ALREADY_USED + ERR_TBL_IT(ESP_ERR_NVS_SEC_HMAC_KEY_BLK_ALREADY_USED), /* 61442 0xf002 Provided eFuse block for HMAC key + generation is already in use */ +# endif +# ifdef ESP_ERR_NVS_SEC_HMAC_KEY_GENERATION_FAILED + ERR_TBL_IT(ESP_ERR_NVS_SEC_HMAC_KEY_GENERATION_FAILED), /* 61443 0xf003 Failed to generate/write the HMAC key to eFuse */ +# endif +# ifdef ESP_ERR_NVS_SEC_HMAC_XTS_KEYS_DERIV_FAILED + ERR_TBL_IT(ESP_ERR_NVS_SEC_HMAC_XTS_KEYS_DERIV_FAILED), /* 61444 0xf004 Failed to derive the NVS encryption keys + based on the HMAC-based scheme */ # endif }; #endif //CONFIG_ESP_ERR_TO_NAME_LOOKUP diff --git a/components/nvs_sec_provider/CMakeLists.txt b/components/nvs_sec_provider/CMakeLists.txt new file mode 100644 index 0000000000..3275f832fe --- /dev/null +++ b/components/nvs_sec_provider/CMakeLists.txt @@ -0,0 +1,12 @@ +idf_component_register(SRCS "nvs_sec_provider.c" + INCLUDE_DIRS include + PRIV_REQUIRES bootloader_support efuse esp_partition nvs_flash) + +# NOTE: In a case where only the default NVS partition is to be encrypted +# and no custom NVS partitions exist, `nvs_flash_init` is the only API that +# needs to be called. No API from the `nvs_sec_provider` component is called +# as neither the `nvs_flash` component nor the user app depends on it. +# Thus, the symbols from this component are not placed in the .map file and +# hence the constructor, which initialises the encryption scheme for the default +# NVS partition, never executes. The following is a workaround for the same. +target_link_libraries(${COMPONENT_LIB} PRIVATE "-u nvs_sec_provider_include_impl") diff --git a/components/nvs_sec_provider/Kconfig b/components/nvs_sec_provider/Kconfig new file mode 100644 index 0000000000..e50839d3a8 --- /dev/null +++ b/components/nvs_sec_provider/Kconfig @@ -0,0 +1,46 @@ +menu "NVS Security Provider" + visible if NVS_ENCRYPTION + + choice NVS_SEC_KEY_PROTECTION_SCHEME + prompt "NVS Encryption: Key Protection Scheme" + depends on NVS_ENCRYPTION + default NVS_SEC_KEY_PROTECT_USING_FLASH_ENC + help + This choice defines the default NVS encryption keys protection scheme; + which will be used for the default NVS partition. + Users can use the corresponding scheme registration APIs to register other + schemes for the default as well as other NVS partitions. + + config NVS_SEC_KEY_PROTECT_USING_FLASH_ENC + bool "Using Flash Encryption" + depends on SECURE_FLASH_ENC_ENABLED + help + Protect the NVS Encryption Keys using Flash Encryption + Requires a separate 'nvs_keys' partition (which will be encrypted by flash encryption) + for storing the NVS encryption keys + + config NVS_SEC_KEY_PROTECT_USING_HMAC + bool "Using HMAC peripheral" + depends on SOC_HMAC_SUPPORTED + help + Derive and protect the NVS Encryption Keys using the HMAC peripheral + Requires the specified eFuse block (NVS_SEC_HMAC_EFUSE_KEY_ID or the v2 API argument) + to be empty or pre-written with a key with the purpose ESP_EFUSE_KEY_PURPOSE_HMAC_UP + + endchoice + + 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 + help + eFuse block key ID storing the HMAC key for deriving the NVS encryption keys + + Note: The eFuse block key ID required by the HMAC scheme + (CONFIG_NVS_SEC_KEY_PROTECT_USING_HMAC) is set using this config when the default + NVS partition is initialized with nvs_flash_init(). The eFuse block key ID can + also be set at runtime by passing the appropriate value to the NVS security scheme + registration APIs. + +endmenu diff --git a/components/nvs_sec_provider/include/nvs_sec_provider.h b/components/nvs_sec_provider/include/nvs_sec_provider.h new file mode 100644 index 0000000000..3624553ef5 --- /dev/null +++ b/components/nvs_sec_provider/include/nvs_sec_provider.h @@ -0,0 +1,114 @@ +/* + * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include +#include "esp_err.h" +#include "soc/soc_caps.h" + +#include "nvs_flash.h" +#include "esp_partition.h" + +#if SOC_HMAC_SUPPORTED +#include "esp_hmac.h" +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#define ESP_ERR_NVS_SEC_BASE 0xF000 /*!< Starting number of error codes */ + +#define ESP_ERR_NVS_SEC_HMAC_KEY_NOT_FOUND (ESP_ERR_NVS_SEC_BASE + 0x01) /*!< HMAC Key required to generate the NVS encryption keys not found */ +#define ESP_ERR_NVS_SEC_HMAC_KEY_BLK_ALREADY_USED (ESP_ERR_NVS_SEC_BASE + 0x02) /*!< Provided eFuse block for HMAC key generation is already in use */ +#define ESP_ERR_NVS_SEC_HMAC_KEY_GENERATION_FAILED (ESP_ERR_NVS_SEC_BASE + 0x03) /*!< Failed to generate/write the HMAC key to eFuse */ +#define ESP_ERR_NVS_SEC_HMAC_XTS_KEYS_DERIV_FAILED (ESP_ERR_NVS_SEC_BASE + 0x04) /*!< Failed to derive the NVS encryption keys based on the HMAC-based scheme */ + +/** + * @brief NVS Encryption Keys Protection Scheme + */ +typedef enum { + NVS_SEC_SCHEME_FLASH_ENC = 0, /*!< Protect NVS encryption keys using Flash Encryption */ + NVS_SEC_SCHEME_HMAC, /*!< Protect NVS encryption keys using HMAC peripheral */ + NVS_SEC_SCHEME_MAX +} nvs_sec_scheme_id_t; + +/** + * @brief Flash encryption-based scheme specific configuration data + */ +typedef struct { + const esp_partition_t *nvs_keys_part; /*!< Partition of subtype `nvs_keys` holding the NVS encryption keys */ +} nvs_sec_config_flash_enc_t; + +/** + * @brief Helper for populating the Flash encryption-based scheme specific configuration data + */ +#define NVS_SEC_PROVIDER_CFG_FLASH_ENC_DEFAULT() { \ + .nvs_keys_part = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, \ + ESP_PARTITION_SUBTYPE_DATA_NVS_KEYS, \ + NULL), \ +} + +#if SOC_HMAC_SUPPORTED +/** + * @brief HMAC-based scheme specific configuration data + */ +typedef struct { + hmac_key_id_t hmac_key_id; /*!< HMAC Key ID used for generating the NVS encryption keys */ +} nvs_sec_config_hmac_t; + +/** + * @brief Helper for populating the HMAC-based scheme specific configuration data + */ +#define NVS_SEC_PROVIDER_CFG_HMAC_DEFAULT() { \ + .hmac_key_id = (hmac_key_id_t)(CONFIG_NVS_SEC_HMAC_EFUSE_KEY_ID), \ +} +#endif /* SOC_HMAC_SUPPORTED */ + +/** + * @brief Register the Flash-Encryption based scheme for NVS Encryption + * + * @param[in] sec_scheme_cfg Security scheme specific configuration data + * @param[out] sec_scheme_handle_out Security scheme specific configuration handle + * + * @return + * - ESP_OK, if `sec_scheme_handle_out` was populated successfully with the scheme configuration; + * - ESP_ERR_INVALID_ARG, if `scheme_cfg_hmac` is NULL; + * - ESP_ERR_NO_MEM, No memory for the scheme-specific handle `sec_scheme_handle_out` + * - ESP_ERR_NOT_FOUND, if no `nvs_keys` partition is found + */ +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 SOC_HMAC_SUPPORTED +/** + * @brief Register the HMAC-based scheme for NVS Encryption + * + * @param[in] sec_scheme_cfg Security scheme specific configuration data + * @param[out] sec_scheme_handle_out Security scheme specific configuration handle + * + * @return + * - ESP_OK, if `sec_scheme_handle_out` was populated successfully with the scheme configuration; + * - ESP_ERR_INVALID_ARG, if `scheme_cfg_hmac` is NULL; + * - ESP_ERR_NO_MEM, No memory for the scheme-specific handle `sec_scheme_handle_out` + */ +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); +#endif /* SOC_HMAC_SUPPORTED */ + +/** + * @brief Deregister the NVS encryption scheme registered with the given handle + * + * @param[in] sec_scheme_handle Security scheme specific configuration handle + + * @return + * - ESP_OK, if the scheme registered with `sec_scheme_handle` was deregistered successfully + * - ESP_ERR_INVALID_ARG, if `sec_scheme_handle` is NULL; + */ +esp_err_t nvs_sec_provider_deregister(nvs_sec_scheme_t *sec_scheme_handle); + +#ifdef __cplusplus +} +#endif diff --git a/components/nvs_sec_provider/nvs_sec_provider.c b/components/nvs_sec_provider/nvs_sec_provider.c new file mode 100644 index 0000000000..e14fe2cdc0 --- /dev/null +++ b/components/nvs_sec_provider/nvs_sec_provider.c @@ -0,0 +1,279 @@ +/* + * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include "esp_err.h" +#include "esp_log.h" +#include "esp_fault.h" +#include "soc/soc_caps.h" +#include "sdkconfig.h" + +#include "nvs_flash.h" +#include "nvs_sec_provider.h" + +#if SOC_HMAC_SUPPORTED +#include "bootloader_random.h" +#include "esp_random.h" + +#include "esp_efuse.h" +#include "esp_efuse_chip.h" +#endif // SOC_HMAC_SUPPORTED + +static __attribute__((unused)) const char *TAG = "nvs_sec_provider"; + +#if CONFIG_NVS_SEC_KEY_PROTECT_USING_FLASH_ENC + +static esp_err_t generate_keys_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_flash_generate_keys(scheme_cfg_flash_enc->nvs_keys_part, cfg); +} + +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_flash_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; + } + + nvs_sec_scheme_t *sec_scheme = calloc(1, sizeof(nvs_sec_scheme_t)); + if (sec_scheme == NULL) { + return ESP_ERR_NO_MEM; + } + + sec_scheme->scheme_id = NVS_SEC_SCHEME_FLASH_ENC; + sec_scheme->nvs_flash_key_gen = &generate_keys_flash_enc; + sec_scheme->nvs_flash_read_cfg = &read_security_cfg_flash_enc; + + sec_scheme->scheme_data = calloc(1, sizeof(nvs_sec_config_flash_enc_t)); + if (sec_scheme->scheme_data == NULL) { + return ESP_ERR_NO_MEM; + } + memcpy(sec_scheme->scheme_data, (void *)sec_scheme_cfg, sizeof(nvs_sec_config_flash_enc_t)); + + esp_err_t err = nvs_flash_register_security_scheme(sec_scheme); + if (err != ESP_OK) { + return err; + } + + *sec_scheme_handle_out = sec_scheme; + return ESP_OK; +} + +static void __attribute__((constructor)) nvs_sec_provider_register_flash_enc_ctr(void) +{ + ESP_EARLY_LOGI(TAG, "NVS Encryption - Registering Flash encryption-based scheme..."); + + nvs_sec_config_flash_enc_t sec_scheme_cfg = NVS_SEC_PROVIDER_CFG_FLASH_ENC_DEFAULT(); + nvs_sec_scheme_t *sec_scheme_handle_out = NULL; + + nvs_sec_provider_register_flash_enc(&sec_scheme_cfg, &sec_scheme_handle_out); +} + +#endif + +#if SOC_HMAC_SUPPORTED + +#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 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}; + + esp_err_t err = esp_hmac_calculate(hmac_key_id, ekey_seed, sizeof(ekey_seed), (uint8_t *)cfg->eky); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Failed to calculate seed HMAC: [0x%02X] (%s)", err, esp_err_to_name(err)); + return err; + } + ESP_FAULT_ASSERT(err == ESP_OK); + + err = esp_hmac_calculate(hmac_key_id, tkey_seed, sizeof(tkey_seed), (uint8_t *)cfg->tky); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Failed to calculate seed HMAC: [0x%02X] (%s)", err, esp_err_to_name(err)); + return err; + } + ESP_FAULT_ASSERT(err == ESP_OK); + + /* NOTE: If the XTS E-key and T-key are the same, we have a hash collision */ + ESP_FAULT_ASSERT(memcmp(cfg->eky, cfg->tky, NVS_KEY_SIZE) != 0); + + return ESP_OK; +} + +static esp_efuse_block_t convert_key_type(hmac_key_id_t key_id) { + return (esp_efuse_block_t)(EFUSE_BLK_KEY0 + (esp_efuse_block_t) key_id); +} + +static bool is_hmac_key_burnt_in_efuse(hmac_key_id_t hmac_key_id) +{ + bool ret = false; + + esp_efuse_block_t hmac_key_blk = convert_key_type(hmac_key_id); + + esp_efuse_purpose_t hmac_efuse_blk_purpose = esp_efuse_get_key_purpose(hmac_key_blk); + if (hmac_efuse_blk_purpose == ESP_EFUSE_KEY_PURPOSE_HMAC_UP) { + ret = true; + } + + return ret; +} + +static esp_err_t generate_keys_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; + + hmac_key_id_t hmac_key = scheme_cfg_hmac->hmac_key_id; + if (hmac_key >= HMAC_KEY_MAX) { + ESP_LOGE(TAG, "Invalid HMAC key ID received!"); + return ESP_ERR_INVALID_ARG; + } + + esp_err_t err = ESP_FAIL; + + if (!is_hmac_key_burnt_in_efuse(hmac_key)) { + uint8_t hmac_key_buf[32] = {0}; + + bootloader_random_enable(); + esp_fill_random(hmac_key_buf, sizeof(hmac_key_buf)); + bootloader_random_disable(); + + esp_efuse_block_t hmac_key_blk = convert_key_type(hmac_key); + + err = esp_efuse_write_key(hmac_key_blk, ESP_EFUSE_KEY_PURPOSE_HMAC_UP, hmac_key_buf, sizeof(hmac_key_buf)); + if (err != ESP_OK) { + if (err == ESP_ERR_EFUSE_REPEATED_PROG) { + ESP_LOGE(TAG, "Configured eFuse block for HMAC key already-in-use!"); + return ESP_ERR_NVS_SEC_HMAC_KEY_BLK_ALREADY_USED; + } + ESP_LOGE(TAG, "Failed to write the HMAC key to eFuse: [0x%02X] (%s)", err, esp_err_to_name(err)); + return ESP_ERR_NVS_SEC_HMAC_KEY_GENERATION_FAILED; + } + memset(hmac_key_buf, 0x00, sizeof(hmac_key_buf)); + } else { + ESP_LOGW(TAG, "HMAC Key already present in configured eFuse block, using the same for NVS encryption!"); + } + + err = compute_nvs_keys_with_hmac(hmac_key, cfg); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Failed to derive the encryption keys using HMAC!"); + return ESP_ERR_NVS_SEC_HMAC_XTS_KEYS_DERIV_FAILED; + } + + return ESP_OK; +} + +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; + + hmac_key_id_t hmac_key = scheme_cfg_hmac->hmac_key_id; + if (hmac_key >= HMAC_KEY_MAX) { + ESP_LOGE(TAG, "Invalid HMAC key ID received!"); + return ESP_ERR_INVALID_ARG; + } + + if (!is_hmac_key_burnt_in_efuse(hmac_key)) { + ESP_LOGE(TAG, "Could not find HMAC key in configured eFuse block!"); + return ESP_ERR_NVS_SEC_HMAC_KEY_NOT_FOUND; + } + + esp_err_t err = compute_nvs_keys_with_hmac(hmac_key, cfg); + if (err != ESP_OK) { + ESP_LOGE(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; + } + + nvs_sec_scheme_t *sec_scheme = calloc(1, sizeof(nvs_sec_scheme_t)); + if (sec_scheme == NULL) { + return ESP_ERR_NO_MEM; + } + + sec_scheme->scheme_id = NVS_SEC_SCHEME_HMAC; + sec_scheme->nvs_flash_key_gen = &generate_keys_hmac; + sec_scheme->nvs_flash_read_cfg = &read_security_cfg_hmac; + + sec_scheme->scheme_data = calloc(1, sizeof(nvs_sec_config_hmac_t)); + if (sec_scheme->scheme_data == NULL) { + return ESP_ERR_NO_MEM; + } + + memcpy(sec_scheme->scheme_data, (void *)sec_scheme_cfg, sizeof(nvs_sec_config_hmac_t)); + + esp_err_t err = nvs_flash_register_security_scheme(sec_scheme); + if (err != ESP_OK) { + return err; + } + + *sec_scheme_handle_out = sec_scheme; + return ESP_OK; +} + +#if CONFIG_NVS_SEC_KEY_PROTECT_USING_HMAC +static void __attribute__((constructor)) nvs_sec_provider_register_hmac_ctr(void) +{ + ESP_EARLY_LOGI(TAG, "NVS Encryption - Registering HMAC-based scheme..."); + + nvs_sec_config_hmac_t sec_scheme_cfg = NVS_SEC_PROVIDER_CFG_HMAC_DEFAULT(); + nvs_sec_scheme_t *sec_scheme_handle_out = NULL; + + nvs_sec_provider_register_hmac(&sec_scheme_cfg, &sec_scheme_handle_out); +} +#endif // CONFIG_NVS_SEC_KEY_PROTECT_USING_HMAC + +#endif // SOC_HMAC_SUPPORTED + +esp_err_t nvs_sec_provider_deregister(nvs_sec_scheme_t *sec_scheme_handle) +{ + if (sec_scheme_handle == NULL) { + return ESP_ERR_INVALID_ARG; + } + + if (sec_scheme_handle->scheme_data != NULL) { + free(sec_scheme_handle->scheme_data); + } + + free(sec_scheme_handle); + + return ESP_OK; +} + +void nvs_sec_provider_include_impl(void) +{ + // Linker hook, exists for no other purpose +} diff --git a/components/partition_table/Kconfig.projbuild b/components/partition_table/Kconfig.projbuild index 546bafe141..efcca14f59 100644 --- a/components/partition_table/Kconfig.projbuild +++ b/components/partition_table/Kconfig.projbuild @@ -54,7 +54,8 @@ menu "Partition Table" for more information. config PARTITION_TABLE_SINGLE_APP_ENCRYPTED_NVS bool "Single factory app, no OTA, encrypted NVS" - depends on !ESP32_COREDUMP_ENABLE_TO_FLASH && NVS_ENCRYPTION + depends on !ESP32_COREDUMP_ENABLE_TO_FLASH && NVS_SEC_KEY_PROTECT_USING_FLASH_ENC + help This is a variation of the default "Single factory app, no OTA" partition table that supports encrypted NVS when using flash encryption. See the Flash Encryption section @@ -64,7 +65,7 @@ menu "Partition Table" components/partition_table/partitions_singleapp_encr_nvs.csv config PARTITION_TABLE_SINGLE_APP_LARGE_ENC_NVS bool "Single factory app (large), no OTA, encrypted NVS" - depends on !ESP32_COREDUMP_ENABLE_TO_FLASH && NVS_ENCRYPTION + depends on !ESP32_COREDUMP_ENABLE_TO_FLASH && NVS_SEC_KEY_PROTECT_USING_FLASH_ENC help This is a variation of the "Single factory app (large), no OTA" partition table that supports encrypted NVS when using flash encryption. See the Flash Encryption section @@ -74,7 +75,7 @@ menu "Partition Table" components/partition_table/partitions_singleapp_large_encr_nvs.csv config PARTITION_TABLE_TWO_OTA_ENCRYPTED_NVS bool "Factory app, two OTA definitions, encrypted NVS" - depends on !ESP_COREDUMP_ENABLE_TO_FLASH && NVS_ENCRYPTION + depends on !ESP_COREDUMP_ENABLE_TO_FLASH && NVS_SEC_KEY_PROTECT_USING_FLASH_ENC help This is a variation of the "Factory app, two OTA definitions" partition table that supports encrypted NVS when using flash encryption. See the Flash Encryption section From 9ac87fcc8b9418a10606b00a0405cf3eb7020c43 Mon Sep 17 00:00:00 2001 From: Laukik Hase Date: Thu, 20 Apr 2023 16:37:49 +0530 Subject: [PATCH 3/6] nvs_partition_gen/mass_mfg: Support for HMAC-based scheme for generating NVS encr-keys --- .../nvs_partition_gen.py | 173 +++++++++++++----- .../nvs_partition_generator/sample_val.csv | 13 ++ .../testdata/sample_encryption_keys_hmac.bin | 1 + .../testdata/sample_hmac_key.bin | 2 + tools/mass_mfg/mfg_gen.py | 29 ++- .../testdata/sample_encryption_keys_hmac.bin | 1 + tools/mass_mfg/testdata/sample_hmac_key.bin | 2 + 7 files changed, 174 insertions(+), 47 deletions(-) create mode 100644 components/nvs_flash/nvs_partition_generator/sample_val.csv create mode 100644 components/nvs_flash/nvs_partition_generator/testdata/sample_encryption_keys_hmac.bin create mode 100644 components/nvs_flash/nvs_partition_generator/testdata/sample_hmac_key.bin create mode 100644 tools/mass_mfg/testdata/sample_encryption_keys_hmac.bin create mode 100644 tools/mass_mfg/testdata/sample_hmac_key.bin diff --git a/components/nvs_flash/nvs_partition_generator/nvs_partition_gen.py b/components/nvs_flash/nvs_partition_generator/nvs_partition_gen.py index b84d2f3fb5..36739328b7 100755 --- a/components/nvs_flash/nvs_partition_generator/nvs_partition_gen.py +++ b/components/nvs_flash/nvs_partition_generator/nvs_partition_gen.py @@ -18,11 +18,13 @@ import os import random import struct import sys +import textwrap import zlib from io import open try: from cryptography.hazmat.backends import default_backend + from cryptography.hazmat.primitives import hashes, hmac from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes except ImportError: print('The cryptography package is not installed.' @@ -44,6 +46,13 @@ def reverse_hexbytes(addr_tmp): return reversed_bytes +def desc_format(*args): + desc = '' + for arg in args: + desc += textwrap.fill(replace_whitespace=False, text=arg) + '\n' + return desc + + """ Class for standard NVS page structure """ @@ -842,16 +851,63 @@ def generate_key(args): distutils.dir_util.mkpath(keys_outdir) keys_outdir, output_keyfile = set_target_filepath(keys_outdir, args.keyfile) - key = ''.join(random.choice('0123456789abcdef') for _ in range(128)).strip() - encr_key_bytes = codecs.decode(key, 'hex') - key_len = len(encr_key_bytes) - keys_buf = bytearray(b'\xff') * page_max_size - keys_buf[0:key_len] = encr_key_bytes - crc_data = keys_buf[0:key_len] - crc_data = bytes(crc_data) - crc = zlib.crc32(crc_data, 0xFFFFFFFF) - struct.pack_into(' Date: Tue, 18 Apr 2023 18:09:38 +0530 Subject: [PATCH 4/6] docs: Update `nvs_flash` docs for the HMAC-based NVS encr-keys protection scheme - Also updated the `nvs_partition_generator` and `mass_mfg` tools documentation --- .../nvs_partition_generator/README.rst | 318 ++++++++++-------- docs/doxygen/Doxyfile | 1 + docs/en/api-reference/storage/index.rst | 1 + .../api-reference/storage/nvs_encryption.rst | 199 +++++++++++ docs/en/api-reference/storage/nvs_flash.rst | 89 +---- docs/en/security/flash-encryption.rst | 6 +- docs/en/security/security.rst | 7 +- docs/zh_CN/api-reference/storage/index.rst | 1 + .../api-reference/storage/nvs_encryption.rst | 1 + tools/mass_mfg/docs/README.rst | 158 ++++++--- 10 files changed, 506 insertions(+), 275 deletions(-) create mode 100644 docs/en/api-reference/storage/nvs_encryption.rst create mode 100644 docs/zh_CN/api-reference/storage/nvs_encryption.rst diff --git a/components/nvs_flash/nvs_partition_generator/README.rst b/components/nvs_flash/nvs_partition_generator/README.rst index 51798d1c20..90c754e9fa 100644 --- a/components/nvs_flash/nvs_partition_generator/README.rst +++ b/components/nvs_flash/nvs_partition_generator/README.rst @@ -6,20 +6,22 @@ NVS Partition Generator Utility Introduction ------------ -The utility :component_file:`nvs_flash/nvs_partition_generator/nvs_partition_gen.py` creates a binary file based on key-value pairs provided in a CSV file. The binary file is compatible with NVS architecture defined in :doc:`Non-Volatile Storage `. This utility is ideally suited for generating a binary blob, containing data specific to ODM/OEM, which can be flashed externally at the time of device manufacturing. This allows manufacturers to generate many instances of the same application firmware with customized parameters for each device, such as a serial number. +The utility :component_file:`nvs_flash/nvs_partition_generator/nvs_partition_gen.py` creates a binary file, compatible with the NVS architecture defined in :doc:`Non-Volatile Storage `, based on the key-value pairs provided in a CSV file. + +This utility is ideally suited for generating a binary blob, containing data specific to ODM/OEM, which can be flashed externally at the time of device manufacturing. This allows manufacturers to generate many instances of the same application firmware with customized parameters for each device, such as a serial number. Prerequisites ------------- To use this utility in encryption mode, install the following packages: - - cryptography package + - ``cryptography`` -All the required packages are included in `requirements.txt` in the root of the esp-idf directory. +All the required packages are included in `requirements.txt` in the root of the ESP-IDF directory. CSV File Format ---------------- +~~~~~~~~~~~~~~~ -Each line of a CSV file should contain 4 parameters, separated by a comma. The table below provides the description for each of these parameters. +Each line of a CSV file should contain 4 parameters, separated by a comma. The table below describes each of these parameters. .. list-table:: :widths: 10 20 35 35 @@ -73,19 +75,15 @@ When a namespace entry is encountered in a CSV file, each following entry will b Multipage Blob Support ---------------------- -By default, binary blobs are allowed to span over multiple pages and are written in the format mentioned in Section :ref:`structure_of_entry`. If you intend to use an older format, the utility provides an option to disable this feature. +By default, binary blobs are allowed to span over multiple pages and are written in the format mentioned in Section :ref:`structure_of_entry`. If you intend to use the older format, the utility provides an option to disable this feature. -Encryption Support -------------------- +Encryption-Decryption Support +----------------------------- -The NVS Partition Generator utility also allows you to create an encrypted binary file. The utility uses the XTS-AES encryption. Please refer to :ref:`nvs_encryption` for more details. +The NVS Partition Generator utility also allows you to create an encrypted binary file and decrypt an encrypted one. The utility uses the XTS-AES encryption. Please refer to :doc:`NVS Encryption ` for more details. -Decryption Support -------------------- -This utility allows you to decrypt an encrypted NVS binary file. The utility uses an NVS binary file encrypted using XTS-AES encryption. Please refer to :ref:`nvs_encryption` for more details. - Running the Utility ------------------- @@ -95,141 +93,173 @@ Running the Utility **Optional Arguments**: -+-----+------------+----------------------------------------------------------------------+ -| No. | Parameter | Description | -+=====+============+======================================================================+ -| 1 | -h, --help | Show this help message and exit | -+-----+------------+----------------------------------------------------------------------+ ++-----+------------------------+---------------------------------------------------------------+ +| No. | Parameter | Description | ++=====+========================+===============================================================+ +| 1 | ``-h`` \ ``--help`` | Show the help message and exit | ++-----+------------------------+---------------------------------------------------------------+ **Commands**:: Run nvs_partition_gen.py {command} -h for additional help -+-----+--------------+--------------------------------------------------------------------+ -| No. | Parameter | Description | -+=====+==============+====================================================================+ -| 1 | generate | Generate NVS partition | -+-----+--------------+--------------------------------------------------------------------+ -| 2 | generate-key | Generate keys for encryption | -+-----+--------------+--------------------------------------------------------------------+ -| 3 | encrypt | Generate NVS encrypted partition | -+-----+--------------+--------------------------------------------------------------------+ -| 4 | decrypt | Decrypt NVS encrypted partition | -+-----+--------------+--------------------------------------------------------------------+ ++-----+---------------------+---------------------------------------------------------------+ +| No. | Parameter | Description | ++=====+=====================+===============================================================+ +| 1 | ``generate`` | Generate NVS partition | ++-----+---------------------+---------------------------------------------------------------+ +| 2 | ``generate-key`` | Generate keys for encryption | ++-----+---------------------+---------------------------------------------------------------+ +| 3 | ``encrypt`` | Generate NVS encrypted partition | ++-----+---------------------+---------------------------------------------------------------+ +| 4 | ``decrypt`` | Decrypt NVS encrypted partition | ++-----+---------------------+---------------------------------------------------------------+ -To Generate NVS Partition (Default): -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Generate NVS Partition (Default) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ **Usage**:: - python nvs_partition_gen.py generate [-h] [--version {1,2}] [--outdir OUTDIR] - input output size + python nvs_partition_gen.py generate [-h] [--version {1,2}] [--outdir OUTDIR] input output size **Positional Arguments**: -+--------------+----------------------------------------------------------------------+ -| Parameter | Description | -+==============+======================================================================+ -| input | Path to CSV file to parse | -+--------------+----------------------------------------------------------------------+ -| output | Path to output NVS binary file | -+--------------+----------------------------------------------------------------------+ -| size | Size of NVS partition in bytes (must be multiple of 4096) | -+--------------+----------------------------------------------------------------------+ ++--------------+---------------------------------------------------------------+ +| Parameter | Description | ++==============+===============================================================+ +| ``input`` | Path to CSV file to parse | ++--------------+---------------------------------------------------------------+ +| ``output`` | Path to output NVS binary file | ++--------------+---------------------------------------------------------------+ +| ``size`` | Size of NVS partition in bytes (must be multiple of 4096) | ++--------------+---------------------------------------------------------------+ **Optional Arguments**: -+-----------------+--------------------------------------------------------------------+ -| Parameter | Description | -+=================+====================================================================+ -| -h, --help | Show this help message and exit | -+-----------------+--------------------------------------------------------------------+ -| --version {1,2} | Set multipage blob version | -| | Version 1 - Multipage blob support disabled | -| | Version 2 - Multipage blob support enabled | -| | Default: Version 2 | -| | | -+-----------------+--------------------------------------------------------------------+ -| --outdir OUTDIR | Output directory to store files created | -| | (Default: current directory) | -+-----------------+--------------------------------------------------------------------+ ++------------------------+----------------------------------------------------------------------+ +| Parameter | Description | ++========================+======================================================================+ +| ``-h`` \ ``--help`` | Show the help message and exit | ++------------------------+----------------------------------------------------------------------+ +| ``--version {1,2}`` | Set multipage blob version (Default: Version 2) | +| | | +| | Version 1 - Multipage blob support disabled | +| | | +| | Version 2 - Multipage blob support enabled | ++------------------------+----------------------------------------------------------------------+ +| ``--outdir OUTDIR`` | Output directory to store file created (Default: current directory) | ++------------------------+----------------------------------------------------------------------+ You can run the utility to generate NVS partition using the command below. A sample CSV file is provided with the utility:: python nvs_partition_gen.py generate sample_singlepage_blob.csv sample.bin 0x3000 -To Generate Only Encryption Key Partition: -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Generate Encryption Keys Partition +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ **Usage**:: - python nvs_partition_gen.py generate-key [-h] [--keyfile KEYFILE] - [--outdir OUTDIR] + python nvs_partition_gen.py generate-key [-h] [--key_protect_hmac] [--kp_hmac_keygen] + [--kp_hmac_keyfile KP_HMAC_KEYFILE] [--kp_hmac_inputkey KP_HMAC_INPUTKEY] + [--keyfile KEYFILE] [--outdir OUTDIR] + **Optional Arguments**: -.. list-table:: - :widths: 30 70 - :header-rows: 1 - - * - Parameter - - Description - * - -h, --help - - Show this help message and exit - * - --keyfile KEYFILE - - Path to output encryption key partition file - * - --outdir OUTDIR - - Output directory to store file created (Default: current directory) ++---------------------------------------------+-----------------------------------------------------------------------------------+ +| Parameter | Description | ++=============================================+===================================================================================+ +| ``-h`` \ ``--help`` | Show the help message and exit | ++---------------------------------------------+-----------------------------------------------------------------------------------+ +| ``--key_protect_hmac`` | If set, the NVS encryption key protection scheme based on HMAC | +| | peripheral is used; else the default scheme based on Flash Encryption | +| | is used | ++---------------------------------------------+-----------------------------------------------------------------------------------+ +| ``--kp_hmac_keygen`` | Generate the HMAC key for HMAC-based encryption scheme | ++---------------------------------------------+-----------------------------------------------------------------------------------+ +| ``--kp_hmac_keyfile KP_HMAC_KEYFILE`` | Path to output HMAC key file | ++---------------------------------------------+-----------------------------------------------------------------------------------+ +| ``--kp_hmac_inputkey KP_HMAC_INPUTKEY`` | File having the HMAC key for generating the NVS encryption keys | ++---------------------------------------------+-----------------------------------------------------------------------------------+ +| ``--keyfile KEYFILE`` | Path to output encryption keys file | ++---------------------------------------------+-----------------------------------------------------------------------------------+ +| ``--outdir OUTDIR`` | Output directory to store files created. (Default: current directory) | ++---------------------------------------------+-----------------------------------------------------------------------------------+ You can run the utility to generate only the encryption key partition using the command below:: python nvs_partition_gen.py generate-key -To Generate Encrypted NVS Partition: -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +For generating encryption key for the HMAC-based scheme, the following commands can be used: + +- Generate the HMAC key and the NVS encryption keys:: + + python nvs_partition_gen.py generate-key --key_protect_hmac --kp_hmac_keygen + +.. note:: Encryption key of the format ``/keys/keys-.bin`` and HMAC key of the format ``/keys/hmac-keys-.bin`` are created. + +- Generate the NVS encryption keys, given the HMAC-key:: + + python nvs_partition_gen.py generate-key --key_protect_hmac --kp_hmac_inputkey testdata/sample_hmac_key.bin + +.. note:: You can provide the custom filename for the HMAC key as well as the encryption key as a parameter. + +Generate Encrypted NVS Partition +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ **Usage**:: python nvs_partition_gen.py encrypt [-h] [--version {1,2}] [--keygen] - [--keyfile KEYFILE] [--inputkey INPUTKEY] - [--outdir OUTDIR] + [--keyfile KEYFILE] [--inputkey INPUTKEY] [--outdir OUTDIR] + [--key_protect_hmac] [--kp_hmac_keygen] + [--kp_hmac_keyfile KP_HMAC_KEYFILE] [--kp_hmac_inputkey KP_HMAC_INPUTKEY] input output size + **Positional Arguments**: -+--------------+----------------------------------------------------------------------+ -| Parameter | Description | -+==============+======================================================================+ -| input | Path to CSV file to parse | -+--------------+----------------------------------------------------------------------+ -| output | Path to output NVS binary file | -+--------------+----------------------------------------------------------------------+ -| size | Size of NVS partition in bytes (must be multiple of 4096) | -+--------------+----------------------------------------------------------------------+ ++--------------+---------------------------------------------------------------+ +| Parameter | Description | ++==============+===============================================================+ +| ``input`` | Path to CSV file to parse | ++--------------+---------------------------------------------------------------+ +| ``output`` | Path to output NVS binary file | ++--------------+---------------------------------------------------------------+ +| ``size`` | Size of NVS partition in bytes (must be multiple of 4096) | ++--------------+---------------------------------------------------------------+ **Optional Arguments**: -+---------------------+--------------------------------------------------------------------+ -| Parameter | Description | -+=====================+====================================================================+ -| -h, --help | Show this help message and exit | -| | | -+---------------------+--------------------------------------------------------------------+ -| --version {1,2} | Set multipage blob version | -| | Version 1 - Multipage blob support disabled | -| | Version 2 - Multipage blob support enabled | -| | Default: Version 2 | -+---------------------+--------------------------------------------------------------------+ -| --keygen | Generates key for encrypting NVS partition | -+---------------------+--------------------------------------------------------------------+ -| --keyfile KEYFILE | Path to output encryption keys file | -+---------------------+--------------------------------------------------------------------+ -| --inputkey INPUTKEY | File having key for encrypting NVS partition | -+---------------------+--------------------------------------------------------------------+ -| --outdir OUTDIR | Output directory to store files created | -| | (Default: current directory) | -+---------------------+--------------------------------------------------------------------+ ++---------------------------------------------+-------------------------------------------------------------------------------+ +| Parameter | Description | ++=============================================+===============================================================================+ +| ``-h`` \ ``--help`` | Show the help message and exit | ++---------------------------------------------+-------------------------------------------------------------------------------+ +| ``--version {1,2}`` | Set multipage blob version (Default: Version 2) | +| | | +| | Version 1 - Multipage blob support disabled | +| | | +| | Version 2 - Multipage blob support enabled | ++---------------------------------------------+-------------------------------------------------------------------------------+ +| ``--keygen`` | Generates key for encrypting NVS partition | ++---------------------------------------------+-------------------------------------------------------------------------------+ +| ``--keyfile KEYFILE`` | Path to output encryption keys file | ++---------------------------------------------+-------------------------------------------------------------------------------+ +| ``--inputkey INPUTKEY`` | File having key for encrypting NVS partition | ++---------------------------------------------+-------------------------------------------------------------------------------+ +| ``--outdir OUTDIR`` | Output directory to store file created (Default: current directory) | ++---------------------------------------------+-------------------------------------------------------------------------------+ +| ``--key_protect_hmac`` | If set, the NVS encryption key protection scheme based on HMAC | +| | peripheral is used; else the default scheme based on Flash Encryption | +| | is used | ++---------------------------------------------+-------------------------------------------------------------------------------+ +| ``--kp_hmac_keygen`` | Generate the HMAC key for HMAC-based encryption scheme | ++---------------------------------------------+-------------------------------------------------------------------------------+ +| ``--kp_hmac_keyfile KP_HMAC_KEYFILE`` | Path to output HMAC key file | ++---------------------------------------------+-------------------------------------------------------------------------------+ +| ``--kp_hmac_inputkey KP_HMAC_INPUTKEY`` | File having the HMAC key for generating the NVS encryption keys | ++---------------------------------------------+-------------------------------------------------------------------------------+ You can run the utility to encrypt NVS partition using the command below. A sample CSV file is provided with the utility: @@ -237,21 +267,38 @@ You can run the utility to encrypt NVS partition using the command below. A samp python nvs_partition_gen.py encrypt sample_singlepage_blob.csv sample_encr.bin 0x3000 --keygen -.. note:: Encryption key of the following format ``/keys/keys-.bin`` is created. + .. note:: Encryption key of the format ``/keys/keys-.bin`` is created. + + - To generate an encrypted partition using the HMAC-based scheme, the above command can be used alongwith some additional parameters. + + - Encrypt by allowing the utility to generate encryption keys and the HMAC-key:: + + python nvs_partition_gen.py encrypt sample_singlepage_blob.csv sample_encr.bin 0x3000 --keygen --key_protect_hmac --kp_hmac_keygen + + .. note:: Encryption key of the format ``/keys/keys-.bin`` and HMAC key of the format ``/keys/hmac-keys-.bin`` are created. + + - Encrypt by allowing the utility to generate encryption keys with user-provided HMAC-key:: + + python nvs_partition_gen.py encrypt sample_singlepage_blob.csv sample_encr.bin 0x3000 --keygen --key_protect_hmac --kp_hmac_inputkey testdata/sample_hmac_key.bin + + .. note:: You can provide the custom filename for the HMAC key as well as the encryption key as a parameter. - Encrypt by allowing the utility to generate encryption keys and store it in provided custom filename:: python nvs_partition_gen.py encrypt sample_singlepage_blob.csv sample_encr.bin 0x3000 --keygen --keyfile sample_keys.bin -.. note:: Encryption key of the following format ``/keys/sample_keys.bin`` is created. -.. note:: This newly created file having encryption keys in ``keys/`` directory is compatible with NVS key-partition structure. Refer to :ref:`nvs_key_partition` for more details. +.. note:: + + - Encryption key of the format ``/keys/sample_keys.bin`` is created. + + - This newly created file having encryption keys in ``keys/`` directory is compatible with NVS key-partition structure. Refer to :ref:`nvs_encr_key_partition` for more details. - Encrypt by providing the encryption keys as input binary file:: python nvs_partition_gen.py encrypt sample_singlepage_blob.csv sample_encr.bin 0x3000 --inputkey sample_keys.bin -To Decrypt Encrypted NVS Partition: -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Decrypt Encrypted NVS Partition +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ **Usage**:: @@ -259,26 +306,25 @@ To Decrypt Encrypted NVS Partition: **Positional Arguments**: -+--------------+----------------------------------------------------------------------+ -| Parameter | Description | -+==============+======================================================================+ -| input | Path to encrypted NVS partition file to parse | -+--------------+----------------------------------------------------------------------+ -| key | Path to file having keys for decryption | -+--------------+----------------------------------------------------------------------+ -| output | Path to output decrypted binary file | -+--------------+----------------------------------------------------------------------+ ++--------------+---------------------------------------------------------------+ +| Parameter | Description | ++==============+===============================================================+ +| ``input`` | Path to encrypted NVS partition file to parse | ++--------------+---------------------------------------------------------------+ +| ``key`` | Path to file having keys for decryption | ++--------------+---------------------------------------------------------------+ +| ``output`` | Path to output decrypted binary file | ++--------------+---------------------------------------------------------------+ **Optional Arguments**: -+---------------------+--------------------------------------------------------------------+ -| Parameter | Description | -+=====================+====================================================================+ -| -h, --help | Show this help message and exit | -+---------------------+--------------------------------------------------------------------+ -| --outdir OUTDIR | Output directory to store files created | -| | (Default: current directory) | -+---------------------+--------------------------------------------------------------------+ ++------------------------+----------------------------------------------------------------------+ +| Parameter | Description | ++========================+======================================================================+ +| ``-h`` / ``--help`` | Show the help message and exit | ++------------------------+----------------------------------------------------------------------+ +| ``--outdir OUTDIR`` | Output directory to store file created (Default: current directory) | ++------------------------+----------------------------------------------------------------------+ You can run the utility to decrypt encrypted NVS partition using the command below:: @@ -289,24 +335,26 @@ You can also provide the format version number: - Multipage Blob Support Enabled (Version 2) -Multipage Blob Support Disabled (Version 1): -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Multipage Blob Support Disabled (Version 1) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -You can run the utility in this format by setting the version parameter to 1, as shown below. A sample CSV file is provided with the utility:: +You can run the utility in this format by setting the version parameter to 1, as shown below. A sample CSV file for the same is provided with the utility:: python nvs_partition_gen.py generate sample_singlepage_blob.csv sample.bin 0x3000 --version 1 -Multipage Blob Support Enabled (Version 2): -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Multipage Blob Support Enabled (Version 2) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -You can run the utility in this format by setting the version parameter to 2, as shown below. A sample CSV file is provided with the utility:: +You can run the utility in this format by setting the version parameter to 2, as shown below. A sample CSV file for the same is provided with the utility:: python nvs_partition_gen.py generate sample_multipage_blob.csv sample.bin 0x4000 --version 2 -.. note:: *Minimum NVS Partition Size needed is 0x3000 bytes.* +.. note:: -.. note:: *When flashing the binary onto the device, make sure it is consistent with the application's sdkconfig.* + - Minimum NVS Partition Size needed is 0x3000 bytes. + + - When flashing the binary onto the device, make sure it is consistent with the application's sdkconfig. Caveats diff --git a/docs/doxygen/Doxyfile b/docs/doxygen/Doxyfile index 33d0171c91..74d2667e20 100644 --- a/docs/doxygen/Doxyfile +++ b/docs/doxygen/Doxyfile @@ -235,6 +235,7 @@ INPUT = \ $(PROJECT_PATH)/components/mqtt/esp-mqtt/include/mqtt_client.h \ $(PROJECT_PATH)/components/nvs_flash/include/nvs_flash.h \ $(PROJECT_PATH)/components/nvs_flash/include/nvs.h \ + $(PROJECT_PATH)/components/nvs_sec_provider/include/nvs_sec_provider.h \ $(PROJECT_PATH)/components/openthread/include/esp_openthread_border_router.h \ $(PROJECT_PATH)/components/openthread/include/esp_openthread_lock.h \ $(PROJECT_PATH)/components/openthread/include/esp_openthread_netif_glue.h \ diff --git a/docs/en/api-reference/storage/index.rst b/docs/en/api-reference/storage/index.rst index 5a70bae009..e8985dc2d2 100644 --- a/docs/en/api-reference/storage/index.rst +++ b/docs/en/api-reference/storage/index.rst @@ -23,6 +23,7 @@ This section contains reference of the high-level storage APIs. They are based o fatfs mass_mfg.rst nvs_flash + nvs_encryption nvs_partition_gen.rst nvs_partition_parse.rst sdmmc diff --git a/docs/en/api-reference/storage/nvs_encryption.rst b/docs/en/api-reference/storage/nvs_encryption.rst new file mode 100644 index 0000000000..ebe384f1f2 --- /dev/null +++ b/docs/en/api-reference/storage/nvs_encryption.rst @@ -0,0 +1,199 @@ +NVS Encryption +============== + +Overview +-------- + +This guide provides an overview of the NVS Encryption feature. NVS encryption helps to achieve secure storage on the device flash memory. + +Data stored in NVS partitions can be encrypted using XTS-AES in the manner similar to the one mentioned in disk encryption standard IEEE P1619. For the purpose of encryption, each entry is treated as one `sector` and relative address of the entry (w.r.t. partition-start) is fed to the encryption algorithm as `sector-number`. + +.. only:: SOC_HMAC_SUPPORTED + + NVS Encryption can be facilitated by enabling :ref:`CONFIG_NVS_ENCRYPTION` and :ref:`CONFIG_NVS_SEC_KEY_PROTECTION_SCHEME` -> ``CONFIG_NVS_SEC_KEY_PROTECT_USING_FLASH_ENC`` or ``CONFIG_NVS_SEC_KEY_PROTECT_USING_HMAC`` depending on the scheme to be used. + + +NVS Encryption: Flash Encryption-based Scheme +--------------------------------------------- + +In this scheme, the keys required for NVS encryption are stored in yet another partition, which is protected using :doc:`Flash Encryption <../../security/flash-encryption>`. Therefore, enabling :doc:`Flash Encryption <../../security/flash-encryption>` becomes a prerequisite for NVS encryption here. + +The NVS Encryption is enabled by default when :doc:`Flash Encryption <../../security/flash-encryption>` is enabled. This is done because Wi-Fi driver stores credentials (like SSID and passphrase) in the default NVS partition. It is important to encrypt them as default choice if platform level encryption is already enabled. + +For using NVS encryption using this scheme, the partition table must contain the :ref:`nvs_encr_key_partition`. Two partition tables containing the :ref:`nvs_encr_key_partition` are provided for NVS encryption under the partition table option (``menuconfig`` > ``Partition Table``). They can be selected with the project configuration menu (``idf.py menuconfig``). Please refer to the example :example:`security/flash_encryption` for how to configure and use the NVS encryption feature. + +.. _nvs_encr_key_partition: + +NVS Key Partition +^^^^^^^^^^^^^^^^^ + +An application requiring NVS encryption support (using the Flash Encryption-based scheme) needs to be compiled with a key-partition of the type `data` and subtype `key`. This partition should be marked as `encrypted` and its size should be the minimum partition size (4KB). Refer to :doc:`Partition Tables <../../api-guides/partition-tables>` for more details. Two additional partition tables which contain the :ref:`nvs_encr_key_partition` are provided under the partition table option (``menuconfig`` > ``Partition Table``). They can be directly used for NVS encryption. The structure of these partitions is depicted below. + +.. highlight:: none + +:: + + +-----------+--------------+-------------+----+ + | XTS encryption key (32) | + +---------------------------------------------+ + | XTS tweak key (32) | + +---------------------------------------------+ + | CRC32 (4) | + +---------------------------------------------+ + +The XTS encryption keys in the :ref:`nvs_encr_key_partition` can be generated in one of the following two ways. + +**Generate the keys on {IDF_TARGET_NAME} chip itself** + + * When NVS encryption is enabled the :cpp:func:`nvs_flash_init` API function can be used to initialize the encrypted default NVS partition. The API function internally generates the XTS encryption keys on the ESP chip. The API function finds the first :ref:`nvs_encr_key_partition`. + + * Then the API function automatically generates and stores the NVS keys in that partition by making use of the :cpp:func:`nvs_flash_generate_keys` API function provided by :component_file:`nvs_flash/include/nvs_flash.h`. New keys are generated and stored only when the respective key partition is empty. The same key partition can then be used to read the security configurations for initializing a custom encrypted NVS partition with help of :cpp:func:`nvs_flash_secure_init_partition`. + + * The API functions :cpp:func:`nvs_flash_secure_init` and :cpp:func:`nvs_flash_secure_init_partition` do not generate the keys internally. When these API functions are used for initializing encrypted NVS partitions, the keys can be generated after startup using the :cpp:func:`nvs_flash_generate_keys` API function provided by ``nvs_flash.h``. The API function will then write those keys onto the key-partition in encrypted form. + + .. note:: Please note that ``nvs_keys`` partition must be completely erased before you start the application in this approach. Otherwise the application may generate :c:macro:`ESP_ERR_NVS_CORRUPT_KEY_PART` error code assuming that ``nvs_keys`` partition is not empty and contains malformatted data. You can use the following command for this: + :: + + parttool.py --port PORT --partition-table-file=PARTITION_TABLE_FILE --partition-table-offset PARTITION_TABLE_OFFSET erase_partition --partition-type=data --partition-subtype=nvs_keys + +**Use a pre-generated NVS key partition** + + This option will be required by the user when keys in the :ref:`nvs_encr_key_partition` are not generated by the application. The :ref:`nvs_encr_key_partition` containing the XTS encryption keys can be generated with the help of :doc:`NVS Partition Generator Utility`. Then the user can store the pre generated key partition on the flash with help of the following two commands: + + 1. Build and flash the partition table + :: + + idf.py partition-table partition-table-flash + + 2. Store the keys in the :ref:`nvs_encr_key_partition` (on the flash) with the help of :component_file:`parttool.py` (see Partition Tool section in :doc:`partition-tables ` for more details) + :: + + parttool.py --port PORT --partition-table-offset PARTITION_TABLE_OFFSET write_partition --partition-name="name of nvs_key partition" --input NVS_KEY_PARTITION_FILE + + .. note:: If the device is encrypted in flash encryption development mode and you want to renew the NVS key partition, you need to tell :component_file:`parttool.py ` to encrypt the NVS key partition and you also need to give it a pointer to the unencrypted partition table in your build directory (build/partition_table) since the partition table on the device is encrypted, too. You can use the following command: + :: + + parttool.py --esptool-write-args encrypt --port PORT --partition-table-file=PARTITION_TABLE_FILE --partition-table-offset PARTITION_TABLE_OFFSET write_partition --partition-name="name of nvs_key partition" --input NVS_KEY_PARTITION_FILE + +Since the key partition is marked as `encrypted` and :doc:`Flash Encryption <../../security/flash-encryption>` is enabled, the bootloader will encrypt this partition using flash encryption key on the first boot. + +It is possible for an application to use different keys for different NVS partitions and thereby have multiple key-partitions. However, it is a responsibility of the application to provide correct key-partition/keys for the purpose of encryption/decryption. + +.. only:: SOC_HMAC_SUPPORTED + + NVS Encryption: HMAC peripheral-based Scheme + -------------------------------------------- + + In this scheme, the XTS keys required for NVS encryption are derived from an HMAC key programmed in eFuse with the purpose :cpp:enumerator:`esp_efuse_purpose_t::ESP_EFUSE_KEY_PURPOSE_HMAC_UP`. Since the encryption keys are derived at runtime, they are not stored anywhere in the flash. Thus, this feature does not require a separate :ref:`nvs_encr_key_partition`. + + .. note:: + + This scheme enables us to achieve secure storage on {IDF_TARGET_NAME} **without enabling flash encryption**. + + .. important:: + + Please take note that this scheme will use one eFuse block for storing the HMAC key required for deriving the encryption keys. + + - When NVS encryption is enabled, the :cpp:func:`nvs_flash_init` API function can be used to initialize the encrypted default NVS partition. The API function first checks whether an HMAC key is present at :ref:`CONFIG_NVS_SEC_HMAC_EFUSE_KEY_ID`. + + .. 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 will have to 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. + - If the specified eFuse block is found to be occupied with a key with a purpose other than :cpp:enumerator:`esp_efuse_purpose_t::ESP_EFUSE_KEY_PURPOSE_HMAC_UP`, an error is thrown. + + - The API :cpp:func:`nvs_flash_init` then automatically generates the NVS keys on demand by using the :cpp:func:`nvs_flash_generate_keys_v2` API function provided by the :component_file:`nvs_flash/include/nvs_flash.h`. The same keys can also be used to read the security configurations (see :cpp:func:`nvs_flash_read_security_cfg_v2`) for initializing a custom encrypted NVS partition with help of :cpp:func:`nvs_flash_secure_init_partition`. + + - The API functions :cpp:func:`nvs_flash_secure_init` and :cpp:func:`nvs_flash_secure_init_partition` do not generate the keys internally. When these API functions are used for initializing encrypted NVS partitions, the keys can be generated after startup using the :cpp:func:`nvs_flash_generate_keys_v2` API function or take and populate the NVS security configuration structure :cpp:type:`nvs_sec_cfg_t` with :cpp:func:`nvs_flash_read_security_cfg_v2` and feed them into the above APIs. + + .. note:: Users can program their own HMAC key in eFuse block beforehand by using the following command - + :: + + espefuse.py -p PORT burn_key HMAC_UP + +Encrypted Read/Write +-------------------- + +The same NVS API functions ``nvs_get_*`` or ``nvs_set_*`` can be used for reading of, and writing to an encrypted nvs partition as well. + +**Encrypt the default NVS partition** + +- To enable encryption for the default NVS partition no additional steps are necessary. When :ref:`CONFIG_NVS_ENCRYPTION` is enabled, the :cpp:func:`nvs_flash_init` API function internally performs some additional steps to enable encryption for the default NVS partition depending on the scheme being used (set by :ref:`CONFIG_NVS_SEC_KEY_PROTECTION_SCHEME`). + +- For the flash encryption-based scheme, the first :ref:`nvs_encr_key_partition` found is used to generate the encryption keys while for the HMAC one, keys are generated using the HMAC key burnt in eFuse at :ref:`CONFIG_NVS_SEC_HMAC_EFUSE_KEY_ID` (refer to the API documentation for more details). + +Alternatively, :cpp:func:`nvs_flash_secure_init` API function can also be used to enable encryption for the default NVS partition. + +**Encrypt a custom NVS partition** + +- To enable encryption for a custom NVS partition, :cpp:func:`nvs_flash_secure_init_partition` API function is used instead of :cpp:func:`nvs_flash_init_partition`. + +- When :cpp:func:`nvs_flash_secure_init` and :cpp:func:`nvs_flash_secure_init_partition` API functions are used, the applications are expected to follow the steps below in order to perform NVS read/write operations with encryption enabled. + + 1. Populate the NVS security configuration structure :cpp:type:`nvs_sec_cfg_t` + + * For the Flash Encryption-based scheme - + + - Find key partition and NVS data partition using ``esp_partition_find*`` API functions. + - Populate the :cpp:type:`nvs_sec_cfg_t` struct using the :cpp:func:`nvs_flash_read_security_cfg` or :cpp:func:`nvs_flash_generate_keys` API functions. + + .. only:: SOC_HMAC_SUPPORTED + + * For the HMAC-based scheme - + + - Set the scheme (see :cpp:enum:`nvs_sec_scheme_id_t`) and the scheme-specific data (to :cpp:enum:`hmac_key_id_t`) with the :cpp:type:`nvs_sec_scheme_t` struct and register the HMAC-based scheme with the API :cpp:func:`nvs_sec_provider_register_hmac`. + - Populate the :cpp:type:`nvs_sec_cfg_t` struct using the :cpp:func:`nvs_flash_read_security_cfg_v2` or :cpp:func:`nvs_flash_generate_keys_v2` API functions. + + .. code-block:: c + + nvs_sec_cfg_t cfg = {}; + nvs_sec_scheme_t *sec_scheme_handle = NULL; + + nvs_sec_config_hmac_t sec_scheme_cfg = {}; + hmac_key_id_t hmac_key = HMAC_KEY0; + sec_scheme_cfg.hmac_key_id = hmac_key; + + ret = nvs_sec_provider_register_hmac(&sec_scheme_cfg, &sec_scheme_handle); + if (ret != ESP_OK) { + return ret; + } + + ret = nvs_flash_read_security_cfg_v2(sec_scheme_handle, &cfg); + if (ret != ESP_OK) { + if (ret == ESP_ERR_NVS_SEC_HMAC_KEY_NOT_FOUND) { + ret = nvs_flash_generate_keys_v2(&sec_scheme_handle, &cfg); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "Failed to generate NVS encr-keys!"); + return ret; + } + } + ESP_LOGE(TAG, "Failed to read NVS security cfg!"); + return ret; + } + + 2. Initialise NVS flash partition using the :cpp:func:`nvs_flash_secure_init` or :cpp:func:`nvs_flash_secure_init_partition` API functions. + 3. Open a namespace using the :cpp:func:`nvs_open` or :cpp:func:`nvs_open_from_partition` API functions. + 4. Perform NVS read/write operations using ``nvs_get_*`` or ``nvs_set_*``. + 5. Deinitialise an NVS partition using :cpp:func:`nvs_flash_deinit`. + + .. 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. + + +NVS Security Provider +--------------------- + +The component :component:`nvs_sec_provider` stores all the implementation-specific code for the NVS encryption schemes and would also accomodate any future schemes. This component acts as an interface to the :component:`nvs_flash` component for the handling of encryption keys. :component:`nvs_sec_provider` has a configuration menu of its own, based on which the selected security scheme and the corresponding settings are registered for the :component:`nvs_flash` component. + +.. only:: SOC_HMAC_SUPPORTED + + This component offers factory functions with which a particular security scheme can be registered without having to worry about the APIs to generate and read the encryption keys (e.g. :cpp:func:`nvs_sec_provider_register_hmac`). Refer to the :example:`security/nvs_encryption_hmac` example for API usage. + + +API Reference +------------- + +.. include-build-file:: inc/nvs_sec_provider.inc diff --git a/docs/en/api-reference/storage/nvs_flash.rst b/docs/en/api-reference/storage/nvs_flash.rst index 6d4dff58a2..7cc1f3cf0f 100644 --- a/docs/en/api-reference/storage/nvs_flash.rst +++ b/docs/en/api-reference/storage/nvs_flash.rst @@ -66,7 +66,13 @@ In general, all iterators obtained via :cpp:func:`nvs_entry_find` have to be rel Security, Tampering, and Robustness ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -NVS is not directly compatible with the {IDF_TARGET_NAME} flash encryption system. However, data can still be stored in encrypted form if NVS encryption is used together with {IDF_TARGET_NAME} flash encryption. Please refer to :ref:`nvs_encryption` for more details. +.. only:: not SOC_HMAC_SUPPORTED + + NVS is not directly compatible with the {IDF_TARGET_NAME} flash encryption system. However, data can still be stored in encrypted form if NVS encryption is used together with {IDF_TARGET_NAME} flash encryption. Please refer to :doc:`NVS Encryption ` for more details. + +.. only:: SOC_HMAC_SUPPORTED + + NVS is not directly compatible with the {IDF_TARGET_NAME} flash encryption system. However, data can still be stored in encrypted form if NVS encryption is used together with {IDF_TARGET_NAME} flash encryption or with the help of the HMAC peripheral. Please refer to :doc:`NVS Encryption ` for more details. If NVS encryption is not used, it is possible for anyone with physical access to the flash chip to alter, erase, or add key-value pairs. With NVS encryption enabled, it is not possible to alter or add a key-value pair and get recognized as a valid pair without knowing corresponding NVS encryption keys. However, there is no tamper-resistance against the erase operation. @@ -78,86 +84,7 @@ The library does try to recover from conditions when flash memory is in an incon NVS Encryption -------------- -Data stored in NVS partitions can be encrypted using XTS-AES in the manner similar to the one mentioned in disk encryption standard IEEE P1619. For the purpose of encryption, each entry is treated as one `sector` and relative address of the entry (w.r.t. partition-start) is fed to the encryption algorithm as `sector-number`. The NVS Encryption can be enabled by enabling :ref:`CONFIG_NVS_ENCRYPTION`. The keys required for NVS encryption are stored in yet another partition, which is protected using :doc:`Flash Encryption <../../security/flash-encryption>`. Therefore, enabling :doc:`Flash Encryption <../../security/flash-encryption>` is a prerequisite for NVS encryption. - -The NVS Encryption is enabled by default when :doc:`Flash Encryption <../../security/flash-encryption>` is enabled. This is done because Wi-Fi driver stores credentials (like SSID and passphrase) in the default NVS partition. It is important to encrypt them as default choice if platform level encryption is already enabled. - -For using NVS encryption, the partition table must contain the :ref:`nvs_key_partition`. Two partition tables containing the :ref:`nvs_key_partition` are provided for NVS encryption under the partition table option (``menuconfig`` > ``Partition Table``). They can be selected with the project configuration menu (``idf.py menuconfig``). Please refer to the example :example:`security/flash_encryption` for how to configure and use NVS encryption feature. - -.. _nvs_key_partition: - -NVS Key Partition -^^^^^^^^^^^^^^^^^ - -An application requiring NVS encryption support needs to be compiled with a key-partition of the type `data` and subtype `key`. This partition should be marked as `encrypted` and its size should be the minimum partition size (4KB). Refer to :doc:`Partition Tables <../../api-guides/partition-tables>` for more details. Two additional partition tables which contain the :ref:`nvs_key_partition` are provided under the partition table option (``menuconfig`` > ``Partition Table``). They can be directly used for :ref:`nvs_encryption`. The structure of these partitions is depicted below. - -.. highlight:: none - -:: - - +-----------+--------------+-------------+----+ - | XTS encryption key (32) | - +---------------------------------------------+ - | XTS tweak key (32) | - +---------------------------------------------+ - | CRC32 (4) | - +---------------------------------------------+ - -The XTS encryption keys in the :ref:`nvs_key_partition` can be generated in one of the following two ways. - -1. Generate the keys on the ESP chip: - - When NVS encryption is enabled the :cpp:func:`nvs_flash_init` API function can be used to initialize the encrypted default NVS partition. The API function internally generates the XTS encryption keys on the ESP chip. The API function finds the first :ref:`nvs_key_partition`. Then the API function automatically generates and stores the NVS keys in that partition by making use of the :cpp:func:`nvs_flash_generate_keys` API function provided by :component_file:`nvs_flash/include/nvs_flash.h`. New keys are generated and stored only when the respective key partition is empty. The same key partition can then be used to read the security configurations for initializing a custom encrypted NVS partition with help of :cpp:func:`nvs_flash_secure_init_partition`. - - The API functions :cpp:func:`nvs_flash_secure_init` and :cpp:func:`nvs_flash_secure_init_partition` do not generate the keys internally. When these API functions are used for initializing encrypted NVS partitions, the keys can be generated after startup using the :cpp:func:`nvs_flash_generate_keys` API function provided by ``nvs_flash.h``. The API function will then write those keys onto the key-partition in encrypted form. - - .. note:: Please note that `nvs_keys` partition must be completely erased before you start the application in this approach. Otherwise the application may generate :c:macro:`ESP_ERR_NVS_CORRUPT_KEY_PART` error code assuming that `nvs_keys` partition is not empty and contains malformatted data. You can use the following command for this: - :: - - parttool.py --port PORT --partition-table-file=PARTITION_TABLE_FILE --partition-table-offset PARTITION_TABLE_OFFSET erase_partition --partition-type=data --partition-subtype=nvs_keys - -2. Use pre-generated key partition: - - This option will be required by the user when keys in the :ref:`nvs_key_partition` are not generated by the application. The :ref:`nvs_key_partition` containing the XTS encryption keys can be generated with the help of :doc:`NVS Partition Generator Utility`. Then the user can store the pre generated key partition on the flash with help of the following two commands: - - i) Build and flash the partition table - :: - - idf.py partition-table partition-table-flash - - ii) Store the keys in the :ref:`nvs_key_partition` (on the flash) with the help of :component_file:`parttool.py` (see Partition Tool section in :doc:`partition-tables ` for more details) - :: - - parttool.py --port PORT --partition-table-offset PARTITION_TABLE_OFFSET write_partition --partition-name="name of nvs_key partition" --input NVS_KEY_PARTITION_FILE - - .. note:: If the device is encrypted in flash encryption development mode and you want to renew the NVS key partition, you need to tell :component_file:`parttool.py ` to encrypt the NVS key partition and you also need to give it a pointer to the unencrypted partition table in your build directory (build/partition_table) since the partition table on the device is encrypted, too. You can use the following command: - :: - - parttool.py --esptool-write-args encrypt --port PORT --partition-table-file=PARTITION_TABLE_FILE --partition-table-offset PARTITION_TABLE_OFFSET write_partition --partition-name="name of nvs_key partition" --input NVS_KEY_PARTITION_FILE - -Since the key partition is marked as `encrypted` and :doc:`Flash Encryption <../../security/flash-encryption>` is enabled, the bootloader will encrypt this partition using flash encryption key on the first boot. - -It is possible for an application to use different keys for different NVS partitions and thereby have multiple key-partitions. However, it is a responsibility of the application to provide correct key-partition/keys for the purpose of encryption/decryption. - -Encrypted Read/Write -^^^^^^^^^^^^^^^^^^^^ - -The same NVS API functions ``nvs_get_*`` or ``nvs_set_*`` can be used for reading of, and writing to an encrypted nvs partition as well. - -**Encrypt the default NVS partition:** -To enable encryption for the default NVS partition no additional steps are necessary. When :ref:`CONFIG_NVS_ENCRYPTION` is enabled, the :cpp:func:`nvs_flash_init` API function internally performs some additional steps using the first :ref:`nvs_key_partition` found to enable encryption for the default NVS partition (refer to the API documentation for more details). Alternatively, :cpp:func:`nvs_flash_secure_init` API function can also be used to enable encryption for the default NVS partition. - -**Encrypt a custom NVS partition:** -To enable encryption for a custom NVS partition, :cpp:func:`nvs_flash_secure_init_partition` API function is used instead of :cpp:func:`nvs_flash_init_partition`. - -When :cpp:func:`nvs_flash_secure_init` and :cpp:func:`nvs_flash_secure_init_partition` API functions are used, the applications are expected to follow the steps below in order to perform NVS read/write operations with encryption enabled. - - 1. Find key partition and NVS data partition using ``esp_partition_find*`` API functions. - 2. Populate the :cpp:type:`nvs_sec_cfg_t` struct using the :cpp:func:`nvs_flash_read_security_cfg` or :cpp:func:`nvs_flash_generate_keys` API functions. - 3. Initialise NVS flash partition using the :cpp:func:`nvs_flash_secure_init` or :cpp:func:`nvs_flash_secure_init_partition` API functions. - 4. Open a namespace using the :cpp:func:`nvs_open` or :cpp:func:`nvs_open_from_partition` API functions. - 5. Perform NVS read/write operations using ``nvs_get_*`` or ``nvs_set_*``. - 6. Deinitialise an NVS partition using :cpp:func:`nvs_flash_deinit`. +Please refer to the :doc:`NVS Encryption ` guide for more details. NVS Partition Generator Utility ------------------------------- diff --git a/docs/en/security/flash-encryption.rst b/docs/en/security/flash-encryption.rst index 073d12f291..2425523809 100644 --- a/docs/en/security/flash-encryption.rst +++ b/docs/en/security/flash-encryption.rst @@ -721,7 +721,7 @@ You can also use the following SPI flash API functions: - :cpp:func:`esp_flash_read` to read raw (encrypted) data which will not be decrypted - :cpp:func:`esp_flash_read_encrypted` to read and decrypt data -Data stored using the Non-Volatile Storage (NVS) API is always stored and read decrypted from the perspective of flash encryption. It is up to the library to provide encryption feature if required. Refer to :ref:`NVS Encryption ` for more details. +Data stored using the Non-Volatile Storage (NVS) API is always stored and read decrypted from the perspective of flash encryption. It is up to the library to provide encryption feature if required. Refer to :doc:`NVS Encryption <../api-reference/storage/nvs_encryption>` for more details. Writing to Encrypted Flash @@ -809,7 +809,7 @@ Key Points About Flash Encryption - Flash access is transparent via the flash cache mapping feature of {IDF_TARGET_NAME} - any flash regions which are mapped to the address space will be transparently decrypted when read. - Some data partitions might need to remain unencrypted for ease of access or might require the use of flash-friendly update algorithms which are ineffective if the data is encrypted. NVS partitions for non-volatile storage cannot be encrypted since the NVS library is not directly compatible with flash encryption. For details, refer to :ref:`NVS Encryption `. + Some data partitions might need to remain unencrypted for ease of access or might require the use of flash-friendly update algorithms which are ineffective if the data is encrypted. NVS partitions for non-volatile storage cannot be encrypted since the NVS library is not directly compatible with flash encryption. For details, refer to :doc:`NVS Encryption <../api-reference/storage/nvs_encryption>`. - If flash encryption might be used in future, the programmer must keep it in mind and take certain precautions when writing code that :ref:`uses encrypted flash `. @@ -834,7 +834,7 @@ Flash encryption protects firmware against unauthorised readout and modification - Flash encryption is only as strong as the key. For this reason, we recommend keys are generated on the device during first boot (default behaviour). If generating keys off-device, ensure proper procedure is followed and don't share the same key between all production devices. - Not all data is stored encrypted. If storing data on flash, check if the method you are using (library, API, etc.) supports flash encryption. - Flash encryption does not prevent an attacker from understanding the high-level layout of the flash. This is because the same AES key is used for every pair of adjacent 16 byte AES blocks. When these adjacent 16 byte blocks contain identical content (such as empty or padding areas), these blocks will encrypt to produce matching pairs of encrypted blocks. This may allow an attacker to make high-level comparisons between encrypted devices (i.e. to tell if two devices are probably running the same firmware version). - :esp32: - For the same reason, an attacker can always tell when a pair of adjacent 16 byte blocks (32 byte aligned) contain two identical 16 byte sequences. Keep this in mind if storing sensitive data on the flash, design your flash storage so this doesn't happen (using a counter byte or some other non-identical value every 16 bytes is sufficient). :ref:`NVS Encryption ` deals with this and is suitable for many uses. + :esp32: - For the same reason, an attacker can always tell when a pair of adjacent 16 byte blocks (32 byte aligned) contain two identical 16 byte sequences. Keep this in mind if storing sensitive data on the flash, design your flash storage so this doesn't happen (using a counter byte or some other non-identical value every 16 bytes is sufficient). :doc:`NVS Encryption <../api-reference/storage/nvs_encryption>` deals with this and is suitable for many uses. - Flash encryption alone may not prevent an attacker from modifying the firmware of the device. To prevent unauthorised firmware from running on the device, use flash encryption in combination with :doc:`Secure Boot `. .. _flash-encryption-and-secure-boot: diff --git a/docs/en/security/security.rst b/docs/en/security/security.rst index 2ac5019d30..5c1ffe538c 100644 --- a/docs/en/security/security.rst +++ b/docs/en/security/security.rst @@ -205,7 +205,12 @@ Secure storage refers to the application specific data that can be stored in a s ESP-IDF provides "NVS (Non-volatile Storage)" management component which allows encrypted data partitions. This feature is tied with the platform :ref:`flash_enc-guide` feature described earlier. -Please refer to the :ref:`NVS Encryption ` for detailed documentation on the working and instructions to enable this feature. +.. only:: SOC_HMAC_SUPPORTED + + This feature can also be used independent of the platform :ref:`flash_enc-guide` feature with the help of the {IDF_TARGET_NAME} HMAC peripheral. + + +Please refer to the :doc:`NVS Encryption <../api-reference/storage/nvs_encryption>` for detailed documentation on the working and instructions to enable this feature. .. important:: diff --git a/docs/zh_CN/api-reference/storage/index.rst b/docs/zh_CN/api-reference/storage/index.rst index eb65280959..9fdc9494e3 100644 --- a/docs/zh_CN/api-reference/storage/index.rst +++ b/docs/zh_CN/api-reference/storage/index.rst @@ -23,6 +23,7 @@ fatfs mass_mfg.rst nvs_flash + nvs_encryption nvs_partition_gen.rst nvs_partition_parse.rst sdmmc diff --git a/docs/zh_CN/api-reference/storage/nvs_encryption.rst b/docs/zh_CN/api-reference/storage/nvs_encryption.rst new file mode 100644 index 0000000000..947e793297 --- /dev/null +++ b/docs/zh_CN/api-reference/storage/nvs_encryption.rst @@ -0,0 +1 @@ +.. include:: ../../../en/api-reference/storage/nvs_encryption.rst diff --git a/tools/mass_mfg/docs/README.rst b/tools/mass_mfg/docs/README.rst index 612527d952..537d8008e3 100644 --- a/tools/mass_mfg/docs/README.rst +++ b/tools/mass_mfg/docs/README.rst @@ -11,14 +11,15 @@ This utility is designed to create instances of factory NVS partition images on Please note that this utility only creates manufacturing binary images which then need to be flashed onto your devices using: - `esptool.py`_ -- `Flash Download tool `_ (available on Windows only).Just download it, unzip, and follow the instructions inside the *doc* folder. +- `Flash Download tool `_ (available on Windows only) + - Download and unzip it, and follow the instructions inside the *doc* folder. - Direct flash programming using custom production tools. Prerequisites ------------- -**This utility is dependent on esp-idf's NVS partition utility.** +**This utility is dependent on ESP-IDF's NVS Partition Generator Utility.** * Operating System requirements: - Linux / MacOS / Windows (standard distributions) @@ -30,7 +31,7 @@ Prerequisites Before using this utility, please make sure that: - The path to Python is added to the PATH environment variable. - - You have installed the packages from `requirement.txt`, the file in the root of the esp-idf directory. + - You have installed the packages from `requirement.txt`, the file in the root of the ESP-IDF directory. Workflow @@ -66,7 +67,7 @@ The data in the configuration file has the following format (the `REPEAT` tag is Each line should have three parameters: ``key,type,encoding``, separated by a comma. If the ``REPEAT`` tag is present, the value corresponding to this key in the master value CSV file will be the same for all devices. -*Please refer to README of the NVS Partition Generator utility for detailed description of each parameter.* +*Please refer to README of the NVS Partition Generator Utility for detailed description of each parameter.* Below is a sample example of such a configuration file:: @@ -134,31 +135,32 @@ Running the utility **Optional Arguments**: - +-----+------------+----------------------------------------------------------------------+ - | No. | Parameter | Description | - +=====+============+======================================================================+ - | 1 | -h, --help | show this help message and exit | - +-----+------------+----------------------------------------------------------------------+ + +-----+------------------------+----------------------------------------------------------------------+ + | No. | Parameter | Description | + +=====+========================+======================================================================+ + | 1 | ``-h`` / ``--help`` | Show the help message and exit | + +-----+------------------------+----------------------------------------------------------------------+ **Commands**: Run mfg_gen.py {command} -h for additional help - +-----+--------------+--------------------------------------------------------------------+ - | No. | Parameter | Description | - +=====+==============+====================================================================+ - | 1 | generate | Generate NVS partition | - +-----+--------------+--------------------------------------------------------------------+ - | 2 | generate-key | Generate keys for encryption | - +-----+--------------+--------------------------------------------------------------------+ + +-----+------------------+--------------------------------------------------------------------+ + | No. | Parameter | Description | + +=====+==================+====================================================================+ + | 1 | ``generate`` | Generate NVS partition | + +-----+------------------+--------------------------------------------------------------------+ + | 2 | ``generate-key`` | Generate keys for encryption | + +-----+------------------+--------------------------------------------------------------------+ **To generate factory images for each device (Default):** **Usage**:: python mfg_gen.py generate [-h] [--fileid FILEID] [--version {1,2}] [--keygen] - [--keyfile KEYFILE] [--inputkey INPUTKEY] - [--outdir OUTDIR] + [--inputkey INPUTKEY] [--outdir OUTDIR] + [--key_protect_hmac] [--kp_hmac_keygen] + [--kp_hmac_keyfile KP_HMAC_KEYFILE] [--kp_hmac_inputkey KP_HMAC_INPUTKEY] conf values prefix size **Positional Arguments**: @@ -166,38 +168,47 @@ Running the utility +--------------+----------------------------------------------------------------------+ | Parameter | Description | +==============+======================================================================+ - | conf | Path to configuration csv file to parse | + | ``conf`` | Path to configuration csv file to parse | +--------------+----------------------------------------------------------------------+ - | values | Path to values csv file to parse | + | ``values`` | Path to values csv file to parse | +--------------+----------------------------------------------------------------------+ - | prefix | Unique name for each output filename prefix | - +-----+--------------+----------------------------------------------------------------+ - | size | Size of NVS partition in bytes | - | | (must be multiple of 4096) | + | ``prefix`` | Unique name for each output filename prefix | + +--------------+----------------------------------------------------------------------+ + | ``size`` | Size of NVS partition in bytes (must be multiple of 4096) | +--------------+----------------------------------------------------------------------+ **Optional Arguments**: - +---------------------+--------------------------------------------------------------------+ - | Parameter | Description | - +=====================+====================================================================+ - | -h, --help | show this help message and exit | - +---------------------+--------------------------------------------------------------------+ - | --fileid FILEID | Unique file identifier(any key in values file) | - | | for each filename suffix (Default: numeric value(1,2,3...) | - +---------------------+--------------------------------------------------------------------+ - | --version {1,2} | Set multipage blob version. | - | | Version 1 - Multipage blob support disabled. | - | | Version 2 - Multipage blob support enabled. | - | | Default: Version 2 | - +---------------------+--------------------------------------------------------------------+ - | --keygen | Generates key for encrypting NVS partition | - +---------------------+--------------------------------------------------------------------+ - | --inputkey INPUTKEY | File having key for encrypting NVS partition | - +---------------------+--------------------------------------------------------------------+ - | --outdir OUTDIR | Output directory to store files created | - | | (Default: current directory) | - +---------------------+--------------------------------------------------------------------+ + +---------------------------------------------+-------------------------------------------------------------------------------+ + | Parameter | Description | + +=============================================+===============================================================================+ + | ``-h`` / ``--help`` | Show the help message and exit | + +---------------------------------------------+-------------------------------------------------------------------------------+ + | ``--fileid FILEID`` | Unique file identifier (any key in values file) | + | | for each filename suffix (Default: numeric value(1,2,3...)) | + +---------------------------------------------+-------------------------------------------------------------------------------+ + | ``--version {1,2}`` | Set multipage blob version. (Default: Version 2) | + | | | + | | Version 1 - Multipage blob support disabled. | + | | | + | | Version 2 - Multipage blob support enabled. | + +---------------------------------------------+-------------------------------------------------------------------------------+ + | ``--keygen`` | Generates key for encrypting NVS partition | + +---------------------------------------------+-------------------------------------------------------------------------------+ + | ``--inputkey INPUTKEY`` | File having key for encrypting NVS partition | + +---------------------------------------------+-------------------------------------------------------------------------------+ + | ``--outdir OUTDIR`` | Output directory to store files created (Default: current directory) | + +---------------------------------------------+-------------------------------------------------------------------------------+ + | ``--key_protect_hmac`` | If set, the NVS encryption key protection scheme based on HMAC | + | | peripheral is used; else the default scheme based on Flash Encryption | + | | is used | + +---------------------------------------------+-------------------------------------------------------------------------------+ + | ``--kp_hmac_keygen`` | Generate the HMAC key for HMAC-based encryption scheme | + +---------------------------------------------+-------------------------------------------------------------------------------+ + | ``--kp_hmac_keyfile KP_HMAC_KEYFILE`` | Path to output HMAC key file | + +---------------------------------------------+-------------------------------------------------------------------------------+ + | ``--kp_hmac_inputkey KP_HMAC_INPUTKEY`` | File having the HMAC key for generating the NVS encryption keys | + +---------------------------------------------+-------------------------------------------------------------------------------+ You can run the utility to generate factory images for each device using the command below. A sample CSV file is provided with the utility:: @@ -213,7 +224,21 @@ You can run the utility to encrypt factory images for each device using the comm python mfg_gen.py generate samples/sample_config.csv samples/sample_values_singlepage_blob.csv Sample 0x3000 --keygen -.. note:: Encryption key of the following format ``/keys/keys--.bin`` is created. This newly created file having encryption keys in ``keys/`` directory is compatible with NVS key-partition structure. Refer to :ref:`nvs_key_partition` for more details. +.. note:: Encryption key of the following format ``/keys/keys--.bin`` is created. This newly created file having encryption keys in ``keys/`` directory is compatible with NVS key-partition structure. Refer to :ref:`nvs_encr_key_partition` for more details. + +- To generate an encrypted image using the HMAC-based scheme, the above command can be used alongwith some additional parameters. + + - Encrypt by allowing the utility to generate encryption keys and the HMAC-key:: + + python mfg_gen.py generate samples/sample_config.csv samples/sample_values_singlepage_blob.csv Sample 0x3000 --keygen --key_protect_hmac --kp_hmac_keygen + + .. note:: Encryption key of the format ``/keys/keys-.bin`` and HMAC key of the format ``/keys/hmac-keys-.bin`` are created. + +- Encrypt by allowing the utility to generate encryption keys with user-provided HMAC-key:: + + python mfg_gen.py generate samples/sample_config.csv samples/sample_values_singlepage_blob.csv Sample 0x3000 --keygen --key_protect_hmac --kp_hmac_inputkey testdata/sample_hmac_key.bin + +.. note:: You can provide the custom filename for the HMAC key as well as the encryption key as a parameter. - Encrypt by providing the encryption keys as input binary file:: @@ -225,16 +250,25 @@ You can run the utility to encrypt factory images for each device using the comm python mfg_gen.py generate-key [-h] [--keyfile KEYFILE] [--outdir OUTDIR] **Optional Arguments**: - +--------------------+----------------------------------------------------------------------+ - | Parameter | Description | - +====================+======================================================================+ - | -h, --help | show this help message and exit | - +--------------------+----------------------------------------------------------------------+ - | --keyfile KEYFILE | Path to output encryption keys file | - +--------------------+----------------------------------------------------------------------+ - | --outdir OUTDIR | Output directory to store files created. | - | | (Default: current directory) | - +--------------------+----------------------------------------------------------------------+ + +---------------------------------------------+-----------------------------------------------------------------------------------+ + | Parameter | Description | + +=============================================+===================================================================================+ + | ``-h`` / ``--help`` | Show the help message and exit | + +---------------------------------------------+-----------------------------------------------------------------------------------+ + | ``--keyfile KEYFILE`` | Path to output encryption keys file | + +---------------------------------------------+-----------------------------------------------------------------------------------+ + | ``--outdir OUTDIR`` | Output directory to store files created. (Default: current directory) | + +---------------------------------------------+-----------------------------------------------------------------------------------+ + | ``--key_protect_hmac`` | If set, the NVS encryption key protection scheme based on HMAC | + | | peripheral is used; else the default scheme based on Flash Encryption | + | | is used | + +---------------------------------------------+-----------------------------------------------------------------------------------+ + | ``--kp_hmac_keygen`` | Generate the HMAC key for HMAC-based encryption scheme | + +---------------------------------------------+-----------------------------------------------------------------------------------+ + | ``--kp_hmac_keyfile KP_HMAC_KEYFILE`` | Path to output HMAC key file | + +---------------------------------------------+-----------------------------------------------------------------------------------+ + | ``--kp_hmac_inputkey KP_HMAC_INPUTKEY`` | File having the HMAC key for generating the NVS encryption keys | + +---------------------------------------------+-----------------------------------------------------------------------------------+ You can run the utility to generate only encryption keys using the command below:: @@ -242,6 +276,20 @@ You can run the utility to generate only encryption keys using the command below .. note:: Encryption key of the following format ``/keys/keys-.bin`` is created. Timestamp format is: ``%m-%d_%H-%M``. To provide custom target filename use the --keyfile argument. +For generating encryption key for the HMAC-based scheme, the following commands can be used: + +- Generate the HMAC key and the NVS encryption keys:: + + python mfg_gen.py generate-key --key_protect_hmac --kp_hmac_keygen + +.. note:: Encryption key of the format ``/keys/keys-.bin`` and HMAC key of the format ``/keys/hmac-keys-.bin`` are created. + +- Generate the NVS encryption keys, given the HMAC-key:: + + python mfg_gen.py generate-key --key_protect_hmac --kp_hmac_inputkey testdata/sample_hmac_key.bin + +.. note:: You can provide the custom filename for the HMAC key as well as the encryption key as a parameter. + Generated encryption key binary file can further be used to encrypt factory images created on the per device basis. The default numeric value: 1,2,3... of the ``fileid`` argument corresponds to each line bearing device instance values in the master value CSV file. From 72f703ccd49a957782a4571559c94e707c601526 Mon Sep 17 00:00:00 2001 From: Laukik Hase Date: Sun, 16 Apr 2023 11:41:11 +0530 Subject: [PATCH 5/6] nvs_flash: Extended test-app and host tests for the HMAC-based NVS encr-keys protection scheme --- .gitlab/ci/dependencies/dependencies.yml | 1 + .gitlab/ci/rules.yml | 71 +++ .gitlab/ci/target-test.yml | 16 + .../nvs_flash/test_apps/main/CMakeLists.txt | 8 +- .../main/partition_encrypted_hmac.bin | Bin 0 -> 24576 bytes .../test_apps/main/partition_plaintext.bin | Bin 0 -> 8192 bytes .../nvs_flash/test_apps/main/test_nvs.c | 239 ++++++++-- ....csv => partitions_nvs_encr_flash_enc.csv} | 0 .../nvs_flash/test_apps/pytest_nvs_flash.py | 24 +- ... => sdkconfig.ci.nvs_encr_flash_enc_esp32} | 8 +- ...> sdkconfig.ci.nvs_encr_flash_enc_esp32c3} | 8 +- .../sdkconfig.ci.nvs_encr_hmac_esp32c3 | 24 + components/nvs_flash/test_nvs_host/Makefile | 6 +- .../nvs_flash/test_nvs_host/test_nvs.cpp | 423 ++++++++++++++++++ conftest.py | 1 + 15 files changed, 765 insertions(+), 64 deletions(-) create mode 100644 components/nvs_flash/test_apps/main/partition_encrypted_hmac.bin create mode 100644 components/nvs_flash/test_apps/main/partition_plaintext.bin rename components/nvs_flash/test_apps/{partitions_nvs_encr_keys_flash_enc.csv => partitions_nvs_encr_flash_enc.csv} (100%) rename components/nvs_flash/test_apps/{sdkconfig.ci.nvs_encr_keys_flash_enc_esp32 => sdkconfig.ci.nvs_encr_flash_enc_esp32} (65%) rename components/nvs_flash/test_apps/{sdkconfig.ci.nvs_encr_keys_flash_enc_esp32c3 => sdkconfig.ci.nvs_encr_flash_enc_esp32c3} (65%) create mode 100644 components/nvs_flash/test_apps/sdkconfig.ci.nvs_encr_hmac_esp32c3 diff --git a/.gitlab/ci/dependencies/dependencies.yml b/.gitlab/ci/dependencies/dependencies.yml index 8220351063..2cd4cd47c0 100644 --- a/.gitlab/ci/dependencies/dependencies.yml +++ b/.gitlab/ci/dependencies/dependencies.yml @@ -141,6 +141,7 @@ build:integration_test: - flash_multi - ecdsa - ccs811 # pytest*ccs811* + - nvs_encr_hmac patterns: - "{0}-{1}-{2}" - "{0}-{2}" diff --git a/.gitlab/ci/rules.yml b/.gitlab/ci/rules.yml index 61b91ac9cc..a04b20b9a9 100644 --- a/.gitlab/ci/rules.yml +++ b/.gitlab/ci/rules.yml @@ -320,6 +320,15 @@ - "components/efuse/**/*" - "components/mbedtls/port/ecdsa/*" +.patterns-component_ut-nvs_encr_hmac: &patterns-component_ut-nvs_encr_hmac + - "components/nvs_flash/**/*" + - "components/nvs_sec_provider/**/*" + +.patterns-example_test-nvs_encr_hmac: &patterns-example_test-nvs_encr_hmac + - "components/nvs_flash/**/*" + - "components/nvs_sec_provider/**/*" + - "examples/security/nvs_encryption_hmac/**/*" + ############## # if anchors # ############## @@ -627,6 +636,8 @@ changes: *patterns-component_ut - <<: *if-dev-push changes: *patterns-component_ut-flash_multi + - <<: *if-dev-push + changes: *patterns-component_ut-nvs_encr_hmac - <<: *if-dev-push changes: *patterns-component_ut-sdio - <<: *if-dev-push @@ -662,6 +673,8 @@ changes: *patterns-component_ut - <<: *if-dev-push changes: *patterns-component_ut-flash_multi + - <<: *if-dev-push + changes: *patterns-component_ut-nvs_encr_hmac - <<: *if-dev-push changes: *patterns-component_ut-sdio - <<: *if-dev-push @@ -696,6 +709,8 @@ changes: *patterns-component_ut - <<: *if-dev-push changes: *patterns-component_ut-flash_multi + - <<: *if-dev-push + changes: *patterns-component_ut-nvs_encr_hmac - <<: *if-dev-push changes: *patterns-component_ut-sdio - <<: *if-dev-push @@ -730,6 +745,8 @@ changes: *patterns-component_ut - <<: *if-dev-push changes: *patterns-component_ut-flash_multi + - <<: *if-dev-push + changes: *patterns-component_ut-nvs_encr_hmac - <<: *if-dev-push changes: *patterns-component_ut-sdio - <<: *if-dev-push @@ -764,6 +781,8 @@ changes: *patterns-component_ut - <<: *if-dev-push changes: *patterns-component_ut-flash_multi + - <<: *if-dev-push + changes: *patterns-component_ut-nvs_encr_hmac - <<: *if-dev-push changes: *patterns-component_ut-sdio - <<: *if-dev-push @@ -798,6 +817,8 @@ changes: *patterns-component_ut - <<: *if-dev-push changes: *patterns-component_ut-flash_multi + - <<: *if-dev-push + changes: *patterns-component_ut-nvs_encr_hmac - <<: *if-dev-push changes: *patterns-component_ut-sdio - <<: *if-dev-push @@ -832,6 +853,8 @@ changes: *patterns-component_ut - <<: *if-dev-push changes: *patterns-component_ut-flash_multi + - <<: *if-dev-push + changes: *patterns-component_ut-nvs_encr_hmac - <<: *if-dev-push changes: *patterns-component_ut-sdio - <<: *if-dev-push @@ -866,6 +889,8 @@ changes: *patterns-component_ut - <<: *if-dev-push changes: *patterns-component_ut-flash_multi + - <<: *if-dev-push + changes: *patterns-component_ut-nvs_encr_hmac - <<: *if-dev-push changes: *patterns-component_ut-sdio - <<: *if-dev-push @@ -1145,6 +1170,8 @@ changes: *patterns-example_test-ethernet - <<: *if-dev-push changes: *patterns-example_test-i154 + - <<: *if-dev-push + changes: *patterns-example_test-nvs_encr_hmac - <<: *if-dev-push changes: *patterns-example_test-sdio - <<: *if-dev-push @@ -1188,6 +1215,8 @@ changes: *patterns-example_test-ethernet - <<: *if-dev-push changes: *patterns-example_test-i154 + - <<: *if-dev-push + changes: *patterns-example_test-nvs_encr_hmac - <<: *if-dev-push changes: *patterns-example_test-sdio - <<: *if-dev-push @@ -1230,6 +1259,8 @@ changes: *patterns-example_test-ethernet - <<: *if-dev-push changes: *patterns-example_test-i154 + - <<: *if-dev-push + changes: *patterns-example_test-nvs_encr_hmac - <<: *if-dev-push changes: *patterns-example_test-sdio - <<: *if-dev-push @@ -1273,6 +1304,8 @@ changes: *patterns-example_test-ethernet - <<: *if-dev-push changes: *patterns-example_test-i154 + - <<: *if-dev-push + changes: *patterns-example_test-nvs_encr_hmac - <<: *if-dev-push changes: *patterns-example_test-sdio - <<: *if-dev-push @@ -1315,6 +1348,8 @@ changes: *patterns-example_test-ethernet - <<: *if-dev-push changes: *patterns-example_test-i154 + - <<: *if-dev-push + changes: *patterns-example_test-nvs_encr_hmac - <<: *if-dev-push changes: *patterns-example_test-sdio - <<: *if-dev-push @@ -1357,6 +1392,8 @@ changes: *patterns-example_test-ethernet - <<: *if-dev-push changes: *patterns-example_test-i154 + - <<: *if-dev-push + changes: *patterns-example_test-nvs_encr_hmac - <<: *if-dev-push changes: *patterns-example_test-sdio - <<: *if-dev-push @@ -1399,6 +1436,8 @@ changes: *patterns-example_test-ethernet - <<: *if-dev-push changes: *patterns-example_test-i154 + - <<: *if-dev-push + changes: *patterns-example_test-nvs_encr_hmac - <<: *if-dev-push changes: *patterns-example_test-sdio - <<: *if-dev-push @@ -1441,6 +1480,8 @@ changes: *patterns-example_test-ethernet - <<: *if-dev-push changes: *patterns-example_test-i154 + - <<: *if-dev-push + changes: *patterns-example_test-nvs_encr_hmac - <<: *if-dev-push changes: *patterns-example_test-sdio - <<: *if-dev-push @@ -1544,6 +1585,8 @@ changes: *patterns-component_ut - <<: *if-dev-push changes: *patterns-component_ut-flash_multi + - <<: *if-dev-push + changes: *patterns-component_ut-nvs_encr_hmac - <<: *if-dev-push changes: *patterns-component_ut-sdio - <<: *if-dev-push @@ -1562,6 +1605,8 @@ changes: *patterns-example_test-ethernet - <<: *if-dev-push changes: *patterns-example_test-i154 + - <<: *if-dev-push + changes: *patterns-example_test-nvs_encr_hmac - <<: *if-dev-push changes: *patterns-example_test-sdio - <<: *if-dev-push @@ -1997,6 +2042,19 @@ - <<: *if-dev-push changes: *patterns-component_ut-flash_multi +.rules:test:component_ut-esp32c3-nvs_encr_hmac: + rules: + - <<: *if-revert-branch + when: never + - <<: *if-protected + - <<: *if-label-build-only + when: never + - <<: *if-label-component_ut + - <<: *if-label-component_ut_esp32c3 + - <<: *if-label-target_test + - <<: *if-dev-push + changes: *patterns-component_ut-nvs_encr_hmac + .rules:test:component_ut-esp32c3-sdio: rules: - <<: *if-revert-branch @@ -2501,6 +2559,19 @@ when: never - <<: *if-example_test-ota-include_nightly_run-rule +.rules:test:example_test-esp32c3-nvs_encr_hmac: + rules: + - <<: *if-revert-branch + when: never + - <<: *if-protected + - <<: *if-label-build-only + when: never + - <<: *if-label-example_test + - <<: *if-label-example_test_esp32c3 + - <<: *if-label-target_test + - <<: *if-dev-push + changes: *patterns-example_test-nvs_encr_hmac + .rules:test:example_test-esp32c3-sdio: rules: - <<: *if-revert-branch diff --git a/.gitlab/ci/target-test.yml b/.gitlab/ci/target-test.yml index ca1cdb0c36..97203f7367 100644 --- a/.gitlab/ci/target-test.yml +++ b/.gitlab/ci/target-test.yml @@ -410,6 +410,14 @@ pytest_examples_esp32c3_flash_encryption: - build_pytest_examples_esp32c3 tags: [ esp32c3, flash_encryption ] +pytest_examples_esp32c3_nvs_encr_hmac: + extends: + - .pytest_examples_dir_template + - .rules:test:example_test-esp32c3-nvs_encr_hmac + needs: + - build_pytest_examples_esp32c3 + tags: [ esp32c3, nvs_encr_hmac ] + pytest_examples_esp32s2_usb_device: extends: - .pytest_examples_dir_template @@ -844,6 +852,14 @@ pytest_components_esp32c3_flash_encryption: - build_pytest_components_esp32c3 tags: [ esp32c3, flash_encryption ] +pytest_components_esp32c3_nvs_encr_hmac: + extends: + - .pytest_components_dir_template + - .rules:test:component_ut-esp32c3-nvs_encr_hmac + needs: + - build_pytest_components_esp32c3 + tags: [ esp32c3, nvs_encr_hmac ] + pytest_components_esp32c3_flash_multi: extends: - .pytest_components_dir_template diff --git a/components/nvs_flash/test_apps/main/CMakeLists.txt b/components/nvs_flash/test_apps/main/CMakeLists.txt index 1553014c85..093fc198ba 100644 --- a/components/nvs_flash/test_apps/main/CMakeLists.txt +++ b/components/nvs_flash/test_apps/main/CMakeLists.txt @@ -1,8 +1,10 @@ idf_component_register(SRC_DIRS "." - PRIV_REQUIRES cmock test_utils nvs_flash bootloader_support spi_flash - EMBED_TXTFILES encryption_keys.bin partition_encrypted.bin sample.bin + PRIV_REQUIRES cmock test_utils nvs_flash nvs_sec_provider + bootloader_support spi_flash + EMBED_TXTFILES encryption_keys.bin partition_encrypted.bin + partition_encrypted_hmac.bin sample.bin WHOLE_ARCHIVE) -if(CONFIG_NVS_ENCRYPTION) +if(CONFIG_NVS_ENCRYPTION OR CONFIG_SOC_HMAC_SUPPORTED) target_link_libraries(${COMPONENT_LIB} PUBLIC idf::mbedtls) endif() diff --git a/components/nvs_flash/test_apps/main/partition_encrypted_hmac.bin b/components/nvs_flash/test_apps/main/partition_encrypted_hmac.bin new file mode 100644 index 0000000000000000000000000000000000000000..6b9a14c86742ab631375686b1d8ae4f12996d98b GIT binary patch literal 24576 zcmezK|Nnmm1_p+I|1m*}?yjAy@Pl9K6Y5OEB%fEOx!2miT&MK&XZ8F&&pCF!wRTRM zlDIF}kaz06qau?IDDGMz9?$iV-}B_34^P@x{5iF!HIO~xtK6M0m5JfqI~(JtE&8-3 z(kAKkzlB=eVU~8aeCKbm>P6UTFSwrgG~;YkMsQ3p19829u~vG<*n28qi~Da`6vVBf9G?o`Kl*9S+YcR z5^JrTRLO%JrOQi3UDqDCPMY@DeqN_0 z!_moS?`d@3m}NDy`QgjIzHNU#XDda|=H?d<>WrJ(e5uxTTSS`F^G)1Luihz6T6W~g zr*rB(-#@5ygfL-3hB-s-CxSOSRdq`gy+KL$Sz`CCiUm&lS6_tF!d=HJOX+7^DjR?mY7O z&fy0sQ`LS1zPDs^c2Y=X%D583ww*gS;o8B;Us)!9{`oM0dDW3!)uN&e)jc6k;$`IL zKNyvPz4H|I9V9offL z7|JZNXeu>GGwCwq3ZEN&oMpoODH%qlO>-~Vyk8M0aYxts$K;h?-*mIZ96KX-)qZjR zE?EXg>l%@LY}XXl@Bbd**tCD?5-*=u3dN0JeLd(cgjcX2NOjf<_*cEsE-{)<0DH~d(oNqk8^)ssE-PA|ja!ud1YB*%E zCmCLR;29qET~}rM*2Rjv0^HZ`%rEmcd~kZYn8_Qa8G5hZg#9m^oxEky`Z}k?dp8bV zl(lg`x75Am&#$n*kuQ#|owKDbJX)D;M(nS-2d3R>Oj#m4F@B2gi*@C`+RiOI1OHnH zolkw#5S#wy_twB^hI4{hei?(y$HHYi(H{@R$cmhE zzb5fEqAI|q>aLCm!0qJWV;#hyjUo$D?lRl z`0G^tEq6YB|0A-T=THad9PLZSY^)ZY4G~e%b2t%UEW<`nYfz0u%F*Lc0Z0Dg{^lY?Js6Nc$e(dBJNQ( zt7F@(*#6){>$Wz$IluFFrE+e{(rHbeVkU>F+lk8oqU;dFUcy;)A#H442pIOA$D(3C({=>%K@myMd)0etWQl9Q_GCzg; zCO4nux;saFf5eV|3E`nX{yR!lU3>VUZOY`iiS6|s?-sxQ{^X%3_k(R^p9_<(%iiEz zq5q&Y-`_dt-n#==QqL=MGEACPaw_Up@rrPx+gDxEJiNVKba}UvuwK+LPXZimc5riou4HI~SE)O64MGl}Q@ZV$I|MuotcEg~OGjwkKdpr3W- zX3hJTvAa}sFG{f8oL9m6;ltN>i?}t(dz{|b=Uv#p!Jgfp>+RHgHfMQUUMk+5o?7H{ zUMtx3+g^r~yAGVXcFVZW>y_ozjS|7iV#< zofu~^^Y{k=qlZe)0`BSO_}=gH@?P~gY5QcM9-Gys6P|2jdHVm}v4$T4Ov6^*e*zFzsY@)$KYn;u+A*1c535`1^oO!-`Xha7Kd)0 zx8mN519LNutS#0#yZp$hi#H7UHwkOiJD$`~e3_!l%5pPm$-R?T*Y7W{`&{-$|8CrW zALky6Zx5d(him2E^3mJB;P9dn^K!eL{DxK^?z3d|6@(r;@=P>sHB<9vzFktQ?!8E7 zt!$0}SbxaP8s&%1k<*sh-~THN?;Uby3%tZ%>O8idx) zb^lp?h~>tYwvAg^wr}W9>AW>vTI-d%G)v|&|A{%bmT|2(_hiGVxwR$?Qfv(OdjH8y z-eW!O>+FC(;a0zRWxhu@8Ae^yQOFbHF(`kNXLINKv-*++2Me$FXk~4R_Fo(2a&<~o znL}v8uKp_vu6$C-=vyfx(!qA^Z(kJu#fkM+*}AHob-FeCum5nLsh#w+G*M0T>iLj( zf2WN5Iy=_i-*l-abnVKfZP6>=JGvg7Ht$Jg>GSjTyJpRtoHF?YtEh_Pg1JwA%-kjW zVfn$Wr&rI>>RCM@VtQfss^`1I+N~NgY+r5isMr;Ce@2t*uaqmQ>!N}KA6^f-aXPo@ z$C=*}2fX)h4sZWoAnbCiqL)+f|9>S8%Mx1)&wW?LpWf+x6}KQ|JKO9p!o4ca(f3l; zq%XPq^uAEafqbppsV+62MELx7{crzjYBE3iLw#7l%5Q5IA5mz%wL|M+(7wvdPkWgz zEQs@Pc(ra(KbM{F`wNZMQ4X!VW&7`KTstGGTlYk+sD)=^?cRhuy1BxSN={-8ansd!jdG#o;+duNF3V zu511xyI|KJ83&8yN)~FY=5Lp^UM{$hzT4`-M((o; z#T71g<^SHGW2`_9eZ6w@(l&4Os9*!9VWA10mc_v7YF zcqWy>(S2x2-1jM>vfMt`rMB2VoZTqfYW_{j(0GzmOwxRZ^LIMf-|*%-pZ$KY`uP`y zj~k3PlyogiFrP1+d7P(MskmBaO8rgMYnk;s8VZ(bq|_aVQm8ooGqQp4`Tupx{d*rT zu5mRHs#>5L&$sJ8Cs%Q6TZD~o3*b;G$e)}J+&nz@2UKhl~p8mPXF<_c?a>x3Ak?g@f zhnIdb@+o5Zbj0)-!y~B?-W&;F`w7#2F2qk3x-lSQTGqtY~^KkHrB zW!UP(eZ8A%Fyn*%ZhKbe1$(+){aO91Mtd1wb%#XsKS{@!pnVrEJMer@)m(7V>RDj* zQR8nGObW+#FFQQH{72juUf~1$(xqb7@3daFFzhz6@0@Zn=yc*^H6Q(m|Hn5pe$d~y z`+7on=U2Z1eWs2tp#DE2j{g5*kNFm;-Tzk@g9LZ4+j+TLdii7??)?$M;W&r!n?$AU zMW)u)YwIj`I(i*>c(Gyq?bAEI9lTzzoT@nQQGT$&?>N01_Ag9QF7wzjY_h4?yP|S$ z=e)zt|Kx8bWrQs*y~H%rRM5@r=Y?YpXHQ`QE)Mw)J_MeDpEv+N_}M$ERJ6QvTwZ z*=3;S?vUSu|ihG{^(M{6}C&K zeY5Jl(0GJtZtO9g)$@FF`cHjbvHhvIX@~S~l|BP|1r~MlEetx9?H&8s;gpH{4@LfKHJr)Yuyfiw2r;HLQ5e3?(Drsjlx2A{?2{3eeFs6D?%Bf zb5-(`uI!Nge0eRC0;ZV^V%rw_&o}a`SSr)W3;mf^jIoIF~xgzpg zI|cteD&8Jt5*`x3{cokt?I)Jst^^9)RP6AGTErD9zFweyo6_-$7ZSXcO+SJcg?vB7 zu6M$0g@^y7PFrW489&#n;M(Yd z$C`T!w#B>8yUF&ycdwqj!!ujsXUk6MESve>cCFCH;~W2#?r^(PAbRP-^y|H@#%0FS zcUgKccI~cz^QbE7TYkS{Ov^R4Pdh#R7adX%D1E>G-pamE^Ya~XYEkbF{Z3`QaBqUj z8j%UTd5hK@>R;DzX^X&#)t%D0@vJI$HWXgDJkjcV`?i=5n;hnfzJ4xI)p6x-*(DXZ znhvwSthJ|QM)SYDJaS{(BCGiq z?i~tREB@#`i;K;z`RN@YbCVT(eul2P-SWlcN8|61M}o`0S(zBgI;JdF`J^+!_N?Wx z=~F{*eS7_Ip+L_U?RfFo0^5rdw@*(^R6Tl!i<#4cPei%BZK(-gY{O;kgzlM3PYJ&{ z6ni$b&f;LN|2d^8CEHG)s+@iPaHUUxsLl622X5=I7aRW1vTN*Pci4CI{j8lUZ*Rw<};CUGB8ed~8~DqmjNq_`^1hM%r%$_=*3`EMNR8j^+f z_vzem+H`u>Yq_oeKRd7(GPV9Z(0C~_F7}3Y$`}8SfyYgkay9MvJZEZdqmv!m?1QEi zA>H;lN2PUo*L>TMqoR_(??lq28=GQ{TIv@w7S3NAb@H0ov4frmosQ&7G8Ig#<$txw zr>1p-^^u>UOZm^~C?$3l&wBR2JLGju;py*;QS-Zh1g}?(l53dhynOejrs{7UYj;P< zCZCSq!T$S6VT)ANER&?Xxb%qp#~dfxxOG!Lv52j0E%|V}Cg<{FA94LVWzCzlA~(jY zTWN4!YT`om-Cv^Y`Ne)cuDN(pG(h!Jr3Zh~!u+zlGuMl!t^Tvn`0oCk#hd|lGKur| zJT{k}^mCS(&=I%juG<##em?r}!+%AXkl}Ta2RHL3sr30b%xj(S%vp>jKX?lB;|)0) z7viV>c9!wk;%OVRQmv!BQDa+YXvq7$iYvMv_VvXmzNuNJy07>AjAu98E{lfx?eXmO zZKhowlsYF zu_f%F+$^UZ{jXcwLcA?Rcvd$~y!!aSg_C^ZpZ4Cn^QC0Q&iBT*!?N-jPq}_zOBdNc zo&Pr73krui}IAc|WsToVu;S zc!rJV|NQ5B4lN7+v&l*A?aHN3SKN6gpxw}QPNHPhl1(0NyrwdV1+zao=cGN|Bwz|doczr(fC}&R0qneck zn;Dr3g611)eps^bP`J#|;=|8ueHx#ev-TKpf7uXFb|#g#&@U)^2S$ZEDli%XqaiRF z0;3@?8UmvsFd70Bh5)Gl&xE7@-~F&LfkNX)wU36tXb6mkz-S1JhQMeDjE2By2#kin zXb6mkz-S1JhQMeD41y2<_5Yc1^#9kiEO|HxoH*))(GVC7fzc2c4S~@R7!85Z5Eu=C z(GVC7fzc2c4S~@R7!3gmLjctOXTj0`zrA_S1qzKH)jk>mqaiRF0;3@?8UmvsFd71* zAut*OqaiRF0;3@?8UmvsFd71*Aut*OqaiRF0;3@?8UmvsFd71*Aut*OqaiRF0;3@? M8UmvsK!*?j09q8~M*si- literal 0 HcmV?d00001 diff --git a/components/nvs_flash/test_apps/main/partition_plaintext.bin b/components/nvs_flash/test_apps/main/partition_plaintext.bin new file mode 100644 index 0000000000000000000000000000000000000000..76fe7122269f0d1f8c8e8a379f7801d4fb062690 GIT binary patch literal 8192 zcmezO|Nnmm1_p-zSim8%yLVRM2fr8?8ULTOTf&}FnwwkcmzbMcT#%TY%D}(~HII>z z@xQG^-(j$PsD*cGCCD}ghI*L1AmjhX^Q=6;@}3C!2ADh(hKd~iuoW?nkj z2qA|5|5sa!D;PLh88}%PI9nOGSQ)rl8MvW^6r&RpgPeZDBCvjs)Cwb~%sh|*Yz+VZ z?>CV;&d9{P`s{m{YcOd>561tqHGNiK>SthJWcdFdWd5lql6_!#r^Mn^GZT>U3?TFS z_zxd8G%`+1O2#x7CIT{l>)F?vG4&(N&q!WtpOISOmYI_ZHX9T}91Q>eTYQw-#Hj3= z-igNtAoI(uo_xa8k1+qJc9C!r*y#u>co_cwpTV7z#mL0W!pg?Z!HL5Tkoo?SdABh2 zL(NxY`Y&Psg{>GIbqMpN8UFuYuu8fqF)2AEH7z|OGb=kMH!r`Su&B7Cw5+_MvI-KB zj7}L0Nvks_CS~R!G&3+TJmC5N|JK#3M#UwGMI{DU0Yu8s$k@cx%-q5fViShSQTfsE z8BHIf`D3(v0GGt0L&DAVZY!x^|Q?8UmvsFd71*Aut*O MqaiRF0@Mfr0AP*B!Tsize)); TEST_ESP_ERR(ESP_ERR_NVS_KEYS_NOT_INITIALIZED, nvs_flash_read_security_cfg(key_part, &cfg)); TEST_ESP_OK(nvs_flash_generate_keys(key_part, &cfg)); TEST_ESP_OK(nvs_flash_read_security_cfg(key_part, &cfg2)); +#elif CONFIG_NVS_SEC_KEY_PROTECT_USING_HMAC + nvs_sec_scheme_t *scheme_cfg = nvs_flash_get_default_security_scheme(); + assert(scheme_cfg != NULL); + TEST_ESP_OK(nvs_flash_generate_keys_v2(scheme_cfg, &cfg)); + + TEST_ESP_OK(nvs_flash_read_security_cfg_v2(scheme_cfg, &cfg2)); +#endif TEST_ASSERT_TRUE(!memcmp(&cfg, &cfg2, sizeof(nvs_sec_cfg_t))); } - TEST_CASE("test nvs apis with encryption enabled", "[nvs]") { +#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"); } @@ -404,19 +470,23 @@ TEST_CASE("test nvs apis with encryption enabled", "[nvs]") assert(key_part && "partition table must have an NVS Key partition"); + ESP_ERROR_CHECK(esp_partition_erase_range(key_part, 0, key_part->size)); +#endif + const esp_partition_t* nvs_partition = esp_partition_find_first( ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_NVS, NULL); assert(nvs_partition && "partition table must have an NVS partition"); - ESP_ERROR_CHECK( esp_partition_erase_range(key_part, 0, key_part->size) ); - bool done = false; do { - ESP_ERROR_CHECK( esp_partition_erase_range(nvs_partition, 0, nvs_partition->size) ); - nvs_sec_cfg_t cfg; - esp_err_t err = nvs_flash_read_security_cfg(key_part, &cfg); + esp_err_t err = ESP_FAIL; + +#if CONFIG_NVS_SEC_KEY_PROTECT_USING_FLASH_ENC + ESP_ERROR_CHECK(esp_partition_erase_range(nvs_partition, 0, nvs_partition->size)); + + err = nvs_flash_read_security_cfg(key_part, &cfg); if(err == ESP_ERR_NVS_KEYS_NOT_INITIALIZED) { uint8_t value[4096] = {[0 ... 4095] = 0xff}; @@ -430,6 +500,23 @@ TEST_CASE("test nvs apis with encryption enabled", "[nvs]") ESP_ERROR_CHECK(err); done = true; } +#elif CONFIG_NVS_SEC_KEY_PROTECT_USING_HMAC + nvs_sec_scheme_t *scheme_cfg = nvs_flash_get_default_security_scheme(); + assert(scheme_cfg != NULL); + + err = nvs_flash_read_security_cfg_v2(scheme_cfg, &cfg); + if (err != ESP_OK) { + if (err == ESP_ERR_NVS_SEC_HMAC_KEY_NOT_FOUND) { + TEST_ESP_OK(nvs_flash_generate_keys_v2(scheme_cfg, &cfg)); + } else { + ESP_ERROR_CHECK(err); + } + } else { + ESP_ERROR_CHECK(err); + done = true; + } +#endif + TEST_ESP_OK(nvs_flash_secure_init(&cfg)); nvs_handle_t handle_1; @@ -484,45 +571,59 @@ TEST_CASE("test nvs apis with encryption enabled", "[nvs]") TEST_CASE("test nvs apis for nvs partition generator utility with encryption enabled", "[nvs_part_gen]") { - - if (!esp_flash_encryption_enabled()) { - TEST_IGNORE_MESSAGE("flash encryption disabled, skipping nvs_api tests with encryption enabled"); - } - nvs_handle_t handle; nvs_sec_cfg_t xts_cfg; - - 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_start[] asm("_binary_partition_encrypted_bin_start"); - - extern const char sample_bin_start[] asm("_binary_sample_bin_start"); - - const esp_partition_t* key_part = esp_partition_find_first( - ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_NVS_KEYS, NULL); + esp_err_t err = ESP_FAIL; const esp_partition_t* nvs_part = esp_partition_find_first( ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_NVS, NULL); - assert(key_part && "partition table must have a KEY partition"); - TEST_ASSERT_TRUE((nvs_key_end - nvs_key_start - 1) == SPI_FLASH_SEC_SIZE); - 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 sample_bin_start[] asm("_binary_sample_bin_start"); + +#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"); + + 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) == SPI_FLASH_SEC_SIZE); + ESP_ERROR_CHECK(esp_partition_erase_range(key_part, 0, key_part->size)); - ESP_ERROR_CHECK( esp_partition_erase_range(nvs_part, 0, nvs_part->size) ); for (int i = 0; i < key_part->size; i+= SPI_FLASH_SEC_SIZE) { ESP_ERROR_CHECK( esp_partition_write(key_part, i, nvs_key_start + i, SPI_FLASH_SEC_SIZE) ); } for (int i = 0; i < nvs_part->size; i+= SPI_FLASH_SEC_SIZE) { - ESP_ERROR_CHECK( esp_partition_write(nvs_part, i, nvs_data_start + i, SPI_FLASH_SEC_SIZE) ); + ESP_ERROR_CHECK( esp_partition_write(nvs_part, i, nvs_data_sch0_start + i, SPI_FLASH_SEC_SIZE) ); } - esp_err_t err = nvs_flash_read_security_cfg(key_part, &xts_cfg); + err = nvs_flash_read_security_cfg(key_part, &xts_cfg); +#elif CONFIG_NVS_SEC_KEY_PROTECT_USING_HMAC + extern const char nvs_data_sch1_start[] asm("_binary_partition_encrypted_hmac_bin_start"); + + for (int i = 0; i < nvs_part->size; i+= SPI_FLASH_SEC_SIZE) { + ESP_ERROR_CHECK( esp_partition_write(nvs_part, i, nvs_data_sch1_start + i, SPI_FLASH_SEC_SIZE) ); + } + + nvs_sec_scheme_t *scheme_cfg = nvs_flash_get_default_security_scheme(); + assert(scheme_cfg != NULL); + + err = nvs_flash_read_security_cfg_v2(scheme_cfg, &xts_cfg); +#endif + ESP_ERROR_CHECK(err); TEST_ESP_OK(nvs_flash_secure_init(&xts_cfg)); @@ -583,4 +684,46 @@ TEST_CASE("test nvs apis for nvs partition generator utility with encryption ena TEST_ESP_OK(nvs_flash_deinit()); } + +#if CONFIG_NVS_SEC_KEY_PROTECT_USING_FLASH_ENC +TEST_CASE("test nvs encryption with Flash Encryption-based scheme with v2 apis", "[nvs]") +{ + nvs_handle_t handle; + + nvs_sec_cfg_t cfg = {}; + nvs_sec_scheme_t *sec_scheme_handle = NULL; + nvs_sec_config_flash_enc_t sec_scheme_cfg = NVS_SEC_PROVIDER_CFG_FLASH_ENC_DEFAULT(); + + TEST_ESP_OK(nvs_sec_provider_register_flash_enc(&sec_scheme_cfg, &sec_scheme_handle)); + + esp_err_t err = nvs_flash_read_security_cfg_v2(sec_scheme_handle, &cfg); + if (err != ESP_OK) { + if (err == ESP_ERR_NVS_KEYS_NOT_INITIALIZED) { + TEST_ESP_OK(nvs_flash_generate_keys_v2(sec_scheme_handle, &cfg)); + } + TEST_ESP_OK(err); + } + + TEST_ESP_OK(nvs_flash_secure_init(&cfg)); + memset(&cfg, 0x00, sizeof(nvs_sec_cfg_t)); + + int32_t foo = 0; + + TEST_ESP_OK(nvs_open("uninit_ns", NVS_READWRITE, &handle)); + TEST_ESP_OK(nvs_set_i32(handle, "foo", 0x12345678)); + nvs_close(handle); + + TEST_ESP_OK(nvs_open("uninit_ns", NVS_READWRITE, &handle)); + TEST_ESP_OK(nvs_get_i32(handle, "foo", &foo)); + nvs_close(handle); + + TEST_ASSERT_EQUAL_INT32(foo, 0x12345678); + + TEST_ESP_OK(nvs_sec_provider_deregister(sec_scheme_handle)); + + TEST_ESP_OK(nvs_flash_deinit()); + TEST_ESP_OK(nvs_flash_erase()); +} +#endif + #endif diff --git a/components/nvs_flash/test_apps/partitions_nvs_encr_keys_flash_enc.csv b/components/nvs_flash/test_apps/partitions_nvs_encr_flash_enc.csv similarity index 100% rename from components/nvs_flash/test_apps/partitions_nvs_encr_keys_flash_enc.csv rename to components/nvs_flash/test_apps/partitions_nvs_encr_flash_enc.csv diff --git a/components/nvs_flash/test_apps/pytest_nvs_flash.py b/components/nvs_flash/test_apps/pytest_nvs_flash.py index ed07f4f729..1da98e2da0 100644 --- a/components/nvs_flash/test_apps/pytest_nvs_flash.py +++ b/components/nvs_flash/test_apps/pytest_nvs_flash.py @@ -4,23 +4,31 @@ import pytest from pytest_embedded_idf.dut import IdfDut +CONFIGS_NVS_ENCR_FLASH_ENC = [ + pytest.param('nvs_encr_flash_enc_esp32', marks=[pytest.mark.esp32]), + pytest.param('nvs_encr_flash_enc_esp32c3', marks=[pytest.mark.esp32c3]), +] + @pytest.mark.supported_targets @pytest.mark.generic @pytest.mark.parametrize('config', ['default'], indirect=True) def test_nvs_flash(dut: IdfDut) -> None: + dut.expect_exact('Press ENTER to see the list of tests') + dut.write('![nvs_encr_hmac]') + dut.expect_unity_test_output(timeout=120) + + +@pytest.mark.esp32c3 +@pytest.mark.nvs_encr_hmac +@pytest.mark.parametrize('config', ['nvs_encr_hmac_esp32c3'], indirect=True) +def test_nvs_flash_encr_hmac(dut: IdfDut) -> None: dut.run_all_single_board_cases() -CONFIGS_NVS_ENCR_KEYS_FLASH_ENC = [ - pytest.param('nvs_encr_keys_flash_enc_esp32', marks=[pytest.mark.esp32]), - pytest.param('nvs_encr_keys_flash_enc_esp32c3', marks=[pytest.mark.esp32c3]), -] - - -@pytest.mark.parametrize('config', CONFIGS_NVS_ENCR_KEYS_FLASH_ENC, indirect=True) @pytest.mark.flash_encryption -def test_nvs_flash_encr_keys_flash_enc(dut: IdfDut) -> None: +@pytest.mark.parametrize('config', CONFIGS_NVS_ENCR_FLASH_ENC, indirect=True) +def test_nvs_flash_encr_flash_enc(dut: IdfDut) -> None: # Erase the nvs_key partition dut.serial.erase_partition('nvs_key') dut.run_all_single_board_cases() diff --git a/components/nvs_flash/test_apps/sdkconfig.ci.nvs_encr_keys_flash_enc_esp32 b/components/nvs_flash/test_apps/sdkconfig.ci.nvs_encr_flash_enc_esp32 similarity index 65% rename from components/nvs_flash/test_apps/sdkconfig.ci.nvs_encr_keys_flash_enc_esp32 rename to components/nvs_flash/test_apps/sdkconfig.ci.nvs_encr_flash_enc_esp32 index 28b989f246..ac5486ed7f 100644 --- a/components/nvs_flash/test_apps/sdkconfig.ci.nvs_encr_keys_flash_enc_esp32 +++ b/components/nvs_flash/test_apps/sdkconfig.ci.nvs_encr_flash_enc_esp32 @@ -3,8 +3,8 @@ CONFIG_IDF_TARGET="esp32" # Partition Table CONFIG_PARTITION_TABLE_CUSTOM=y -CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions_nvs_encr_keys_flash_enc.csv" -CONFIG_PARTITION_TABLE_FILENAME="partitions_nvs_encr_keys_flash_enc.csv" +CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions_nvs_encr_flash_enc.csv" +CONFIG_PARTITION_TABLE_FILENAME="partitions_nvs_encr_flash_enc.csv" CONFIG_PARTITION_TABLE_OFFSET=0x9000 # Enabling Flash Encryption @@ -16,3 +16,7 @@ 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 diff --git a/components/nvs_flash/test_apps/sdkconfig.ci.nvs_encr_keys_flash_enc_esp32c3 b/components/nvs_flash/test_apps/sdkconfig.ci.nvs_encr_flash_enc_esp32c3 similarity index 65% rename from components/nvs_flash/test_apps/sdkconfig.ci.nvs_encr_keys_flash_enc_esp32c3 rename to components/nvs_flash/test_apps/sdkconfig.ci.nvs_encr_flash_enc_esp32c3 index 6a986fa54d..ae23bea21d 100644 --- a/components/nvs_flash/test_apps/sdkconfig.ci.nvs_encr_keys_flash_enc_esp32c3 +++ b/components/nvs_flash/test_apps/sdkconfig.ci.nvs_encr_flash_enc_esp32c3 @@ -3,8 +3,8 @@ CONFIG_IDF_TARGET="esp32c3" # Partition Table CONFIG_PARTITION_TABLE_CUSTOM=y -CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions_nvs_encr_keys_flash_enc.csv" -CONFIG_PARTITION_TABLE_FILENAME="partitions_nvs_encr_keys_flash_enc.csv" +CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions_nvs_encr_flash_enc.csv" +CONFIG_PARTITION_TABLE_FILENAME="partitions_nvs_encr_flash_enc.csv" CONFIG_PARTITION_TABLE_OFFSET=0x9000 # Enabling Flash Encryption @@ -16,3 +16,7 @@ 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 diff --git a/components/nvs_flash/test_apps/sdkconfig.ci.nvs_encr_hmac_esp32c3 b/components/nvs_flash/test_apps/sdkconfig.ci.nvs_encr_hmac_esp32c3 new file mode 100644 index 0000000000..f208fae5ec --- /dev/null +++ b/components/nvs_flash/test_apps/sdkconfig.ci.nvs_encr_hmac_esp32c3 @@ -0,0 +1,24 @@ +# Restricting to ESP32C3 +CONFIG_IDF_TARGET="esp32c3" + +# NOTE: The runner for this test-app has flash-encryption enabled +# Partition Table +CONFIG_PARTITION_TABLE_CUSTOM=y +CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions_nvs_encr_flash_enc.csv" +CONFIG_PARTITION_TABLE_FILENAME="partitions_nvs_encr_flash_enc.csv" +CONFIG_PARTITION_TABLE_OFFSET=0x9000 + +# 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_nvs_host/Makefile b/components/nvs_flash/test_nvs_host/Makefile index be4e5c46b3..0ac5df21fc 100644 --- a/components/nvs_flash/test_nvs_host/Makefile +++ b/components/nvs_flash/test_nvs_host/Makefile @@ -33,7 +33,7 @@ else COMPILER := gcc endif -CPPFLAGS += -I../private_include -I../include -I../src -I../../esp_rom/include -I../../esp_rom/include/linux -I../../log/include -I./ -I../../esp_common/include -I../../esp32/include -I ../../mbedtls/mbedtls/include -I ../../spi_flash/include -I ../../esp_partition/include -I ../../hal/include -I ../../xtensa/include -I ../../../tools/catch -fprofile-arcs -ftest-coverage -g2 -ggdb +CPPFLAGS += -I../private_include -I../include -I../src -I../../esp_rom/include -I../../esp_rom/include/linux -I../../log/include -I./ -I../../esp_common/include -I../../esp32/include -I ../../mbedtls/mbedtls/include -I ../../spi_flash/include -I ../../esp_partition/include -I ../../hal/include -I ../../xtensa/include -I ../../soc/linux/include -I ../../../tools/catch -fprofile-arcs -ftest-coverage -g2 -ggdb CFLAGS += -fprofile-arcs -ftest-coverage -DLINUX_TARGET -DLINUX_HOST_LEGACY_TEST CXXFLAGS += -std=c++11 -Wall -Werror -DLINUX_TARGET -DLINUX_HOST_LEGACY_TEST LDFLAGS += -lstdc++ -Wall -fprofile-arcs -ftest-coverage @@ -93,10 +93,14 @@ clean: clean-coverage rm -f ../nvs_partition_generator/partition_single_page.bin rm -f ../nvs_partition_generator/partition_multipage_blob.bin rm -f ../nvs_partition_generator/partition_encrypted.bin + rm -f ../nvs_partition_generator/partition_encrypted_hmac.bin rm -f ../nvs_partition_generator/partition_encrypted_using_keygen.bin rm -f ../nvs_partition_generator/partition_encrypted_using_keyfile.bin + rm -f ../nvs_partition_generator/partition_encrypted_using_keygen_hmac.bin rm -f ../nvs_partition_generator/partition_decrypted.bin + rm -f ../nvs_partition_generator/partition_decrypted_hmac.bin rm -f ../nvs_partition_generator/partition_encoded.bin + rm -f ../nvs_partition_generator/Test-1-partition-encrypted-hmac.bin rm -f ../nvs_partition_generator/Test-1-partition-encrypted.bin rm -f ../nvs_partition_generator/Test-1-partition.bin rm -f ../../../tools/mass_mfg/samples/sample_values_multipage_blob_created.csv diff --git a/components/nvs_flash/test_nvs_host/test_nvs.cpp b/components/nvs_flash/test_nvs_host/test_nvs.cpp index 6550927015..1fae2c15e3 100644 --- a/components/nvs_flash/test_nvs_host/test_nvs.cpp +++ b/components/nvs_flash/test_nvs_host/test_nvs.cpp @@ -11,6 +11,7 @@ #include "nvs_partition_manager.hpp" #include "nvs_partition.hpp" #include "mbedtls/aes.h" +#include "mbedtls/md.h" #include #include #include @@ -1567,12 +1568,24 @@ TEST_CASE("test decrypt functionality for encrypted data", "[nvs_part_gen]") status = system("python ../nvs_partition_generator/nvs_partition_gen.py encrypt ../nvs_partition_generator/sample_multipage_blob.csv partition_encrypted.bin 0x5000 --inputkey ../nvs_partition_generator/testdata/sample_encryption_keys.bin --outdir ../nvs_partition_generator"); CHECK(status == 0); + //encrypting data from sample_multipage_blob.csv (hmac-based scheme) + status = system("python ../nvs_partition_generator/nvs_partition_gen.py encrypt ../nvs_partition_generator/sample_multipage_blob.csv partition_encrypted_hmac.bin 0x5000 --keygen --key_protect_hmac --kp_hmac_inputkey ../nvs_partition_generator/testdata/sample_hmac_key.bin --outdir ../nvs_partition_generator"); + CHECK(status == 0); + //decrypting data from partition_encrypted.bin status = system("python ../nvs_partition_generator/nvs_partition_gen.py decrypt ../nvs_partition_generator/partition_encrypted.bin ../nvs_partition_generator/testdata/sample_encryption_keys.bin ../nvs_partition_generator/partition_decrypted.bin"); CHECK(status == 0); status = system("diff ../nvs_partition_generator/partition_decrypted.bin ../nvs_partition_generator/partition_encoded.bin"); CHECK(status == 0); + + //decrypting data from partition_encrypted_hmac.bin + status = system("python ../nvs_partition_generator/nvs_partition_gen.py decrypt ../nvs_partition_generator/partition_encrypted_hmac.bin ../nvs_partition_generator/testdata/sample_encryption_keys_hmac.bin ../nvs_partition_generator/partition_decrypted_hmac.bin"); + CHECK(status == 0); + + status = system("diff ../nvs_partition_generator/partition_decrypted_hmac.bin ../nvs_partition_generator/partition_encoded.bin"); + CHECK(status == 0); + CHECK(WEXITSTATUS(status) == 0); @@ -1757,6 +1770,201 @@ TEST_CASE("test nvs apis for nvs partition generator utility with encryption ena } +static void compute_nvs_keys_with_hmac(nvs_sec_cfg_t *cfg, void *hmac_key) +{ + unsigned char key_bytes[32] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, + 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, + 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, + 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20 }; + if (hmac_key != NULL){ + memcpy(key_bytes, hmac_key, 32); + } + + unsigned char ekey_seed[32], tkey_seed[32]; + + for (unsigned int i = 0; i < sizeof(ekey_seed); i+=4) { + ekey_seed[i] = 0x5A; + ekey_seed[i + 1] = 0x5A; + ekey_seed[i + 2] = 0xBE; + ekey_seed[i + 3] = 0xAE; + } + + for (unsigned int i = 0; i < sizeof(tkey_seed); i+=4) { + tkey_seed[i] = 0xA5; + tkey_seed[i + 1] = 0xA5; + tkey_seed[i + 2] = 0xDE; + tkey_seed[i + 3] = 0xCE; + } + + const mbedtls_md_type_t alg = MBEDTLS_MD_SHA256; + + mbedtls_md_context_t ctx; + mbedtls_md_init(&ctx); + + const mbedtls_md_info_t *info = mbedtls_md_info_from_type(alg); + mbedtls_md_setup(&ctx, info, 1); + mbedtls_md_hmac_starts(&ctx, key_bytes, sizeof(key_bytes)); + + mbedtls_md_hmac_update(&ctx, ekey_seed, sizeof(ekey_seed)); + mbedtls_md_hmac_finish(&ctx, cfg->eky); + + mbedtls_md_hmac_reset(&ctx); + mbedtls_md_hmac_update(&ctx, tkey_seed, sizeof(tkey_seed)); + mbedtls_md_hmac_finish(&ctx, cfg->tky); + + assert(memcmp(cfg->eky, cfg->tky, NVS_KEY_SIZE)); + + mbedtls_md_free(&ctx); +} + +TEST_CASE("test nvs apis for nvs partition generator utility with encryption enabled using keygen (user-provided HMAC-key)", "[nvs_part_gen]") +{ + int childpid = fork(); + int status; + + if (childpid == 0) { + exit(execlp("cp", " cp", + "-rf", + "../nvs_partition_generator/testdata", + ".", NULL)); + } else { + CHECK(childpid > 0); + waitpid(childpid, &status, 0); + CHECK(WEXITSTATUS(status) == 0); + + childpid = fork(); + + if (childpid == 0) { + exit(execlp("rm", " rm", + "-rf", + "../nvs_partition_generator/keys", NULL)); + } else { + CHECK(childpid > 0); + waitpid(childpid, &status, 0); + CHECK(WEXITSTATUS(status) == 0); + + childpid = fork(); + if (childpid == 0) { + exit(execlp("python", "python", + "../nvs_partition_generator/nvs_partition_gen.py", + "encrypt", + "../nvs_partition_generator/sample_multipage_blob.csv", + "partition_encrypted_using_keygen_hmac.bin", + "0x4000", + "--keygen", + "--key_protect_hmac", + "--kp_hmac_inputkey", + "../nvs_partition_generator/testdata/sample_hmac_key.bin", + "--outdir", + "../nvs_partition_generator", NULL)); + + } else { + CHECK(childpid > 0); + waitpid(childpid, &status, 0); + CHECK(WEXITSTATUS(status) == 0); + + } + } + } + + SpiFlashEmulator emu("../nvs_partition_generator/partition_encrypted_using_keygen_hmac.bin"); + + nvs_sec_cfg_t cfg; + compute_nvs_keys_with_hmac(&cfg, NULL); + + check_nvs_part_gen_args(&emu, NVS_DEFAULT_PART_NAME, 4, "../nvs_partition_generator/testdata/sample_multipage_blob.bin", true, &cfg); + +} + +TEST_CASE("test nvs apis for nvs partition generator utility with encryption enabled using keygen (dynamically generated HMAC-key)", "[nvs_part_gen]") +{ + int childpid = fork(); + int status; + + if (childpid == 0) { + exit(execlp("cp", " cp", + "-rf", + "../nvs_partition_generator/testdata", + ".", NULL)); + } else { + CHECK(childpid > 0); + waitpid(childpid, &status, 0); + CHECK(WEXITSTATUS(status) == 0); + + childpid = fork(); + + if (childpid == 0) { + exit(execlp("rm", " rm", + "-rf", + "../nvs_partition_generator/keys", NULL)); + } else { + CHECK(childpid > 0); + waitpid(childpid, &status, 0); + CHECK(WEXITSTATUS(status) == 0); + + childpid = fork(); + if (childpid == 0) { + exit(execlp("python", "python", + "../nvs_partition_generator/nvs_partition_gen.py", + "encrypt", + "../nvs_partition_generator/sample_multipage_blob.csv", + "partition_encrypted_using_keygen_hmac.bin", + "0x4000", + "--keygen", + "--key_protect_hmac", + "--kp_hmac_keygen", + "--outdir", + "../nvs_partition_generator", NULL)); + + } else { + CHECK(childpid > 0); + waitpid(childpid, &status, 0); + CHECK(WEXITSTATUS(status) == 0); + + } + } + } + + + DIR *dir; + struct dirent *file; + char *filename; + char *files; + char *file_ext; + char *hmac_key_file; + + dir = opendir("../nvs_partition_generator/keys"); + while ((file = readdir(dir)) != NULL) { + filename = file->d_name; + file_ext = NULL; + files = strrchr(filename, '.'); + if (files != NULL) { + file_ext = files + 1; + if (strncmp(file_ext, "bin", 3) != 0) { + continue; + } + } + if (strstr(filename, "hmac") != NULL) { + hmac_key_file = filename; + } + } + + std::string hmac_key_path = std::string("../nvs_partition_generator/keys/") + std::string(hmac_key_file); + SpiFlashEmulator emu("../nvs_partition_generator/partition_encrypted_using_keygen_hmac.bin"); + + char hmac_key_buf[32]; + FILE *fp; + fp = fopen(hmac_key_path.c_str(), "rb"); + fread(hmac_key_buf, sizeof(hmac_key_buf), 1, fp); + fclose(fp); + + nvs_sec_cfg_t cfg; + compute_nvs_keys_with_hmac(&cfg, hmac_key_buf); + + check_nvs_part_gen_args(&emu, NVS_DEFAULT_PART_NAME, 4, "../nvs_partition_generator/testdata/sample_multipage_blob.bin", true, &cfg); + +} + TEST_CASE("check and read data from partition generated via manufacturing utility with encryption enabled using sample inputkey", "[mfg_gen]") { int childpid = fork(); @@ -1969,6 +2177,221 @@ TEST_CASE("check and read data from partition generated via manufacturing utilit } } + +TEST_CASE("check and read data from partition generated via manufacturing utility with encryption enabled using new generated key (user-provided HMAC-key)", "[mfg_gen]") +{ + int childpid = fork(); + int status; + + if (childpid == 0) { + exit(execlp("bash", " bash", + "-c", + "rm -rf ../../../tools/mass_mfg/host_test | \ + cp -rf ../../../tools/mass_mfg/testdata mfg_testdata | \ + cp -rf ../nvs_partition_generator/testdata . | \ + mkdir -p ../../../tools/mass_mfg/host_test", NULL)); + } else { + CHECK(childpid > 0); + waitpid(childpid, &status, 0); + CHECK(WEXITSTATUS(status) == 0); + + childpid = fork(); + if (childpid == 0) { + exit(execlp("python", "python", + "../../../tools/mass_mfg/mfg_gen.py", + "generate", + "../../../tools/mass_mfg/samples/sample_config.csv", + "../../../tools/mass_mfg/samples/sample_values_multipage_blob.csv", + "Test", + "0x4000", + "--version", + "2", + "--keygen", + "--key_protect_hmac", + "--kp_hmac_inputkey", + "mfg_testdata/sample_hmac_key.bin", + "--outdir", + "../../../tools/mass_mfg/host_test",NULL)); + + } else { + CHECK(childpid > 0); + waitpid(childpid, &status, 0); + CHECK(WEXITSTATUS(status) == 0); + + childpid = fork(); + if (childpid == 0) { + exit(execlp("python", "python", + "../nvs_partition_generator/nvs_partition_gen.py", + "encrypt", + "../../../tools/mass_mfg/host_test/csv/Test-1.csv", + "../nvs_partition_generator/Test-1-partition-encrypted-hmac.bin", + "0x4000", + "--version", + "2", + "--keygen", + "--key_protect_hmac", + "--kp_hmac_inputkey", + "mfg_testdata/sample_hmac_key.bin", NULL)); + + } else { + CHECK(childpid > 0); + waitpid(childpid, &status, 0); + CHECK(WEXITSTATUS(status) == 0); + + } + + } + + } + + SpiFlashEmulator emu1("../../../tools/mass_mfg/host_test/bin/Test-1.bin"); + + nvs_sec_cfg_t cfg; + compute_nvs_keys_with_hmac(&cfg, NULL); + + check_nvs_part_gen_args_mfg(&emu1, NVS_DEFAULT_PART_NAME, 4, "mfg_testdata/sample_multipage_blob.bin", true, &cfg); + + SpiFlashEmulator emu2("../nvs_partition_generator/Test-1-partition-encrypted-hmac.bin"); + + check_nvs_part_gen_args_mfg(&emu2, NVS_DEFAULT_PART_NAME, 4, "testdata/sample_multipage_blob.bin", true, &cfg); + + + childpid = fork(); + if (childpid == 0) { + exit(execlp("bash", " bash", + "-c", + "rm -rf ../../../tools/mass_mfg/host_test | \ + rm -rf mfg_testdata | \ + rm -rf testdata", NULL)); + } else { + CHECK(childpid > 0); + waitpid(childpid, &status, 0); + CHECK(WEXITSTATUS(status) == 0); + + } + +} + +TEST_CASE("check and read data from partition generated via manufacturing utility with encryption enabled using new generated key (dynamically generated HMAC-key)", "[mfg_gen]") +{ + int childpid = fork(); + int status; + + if (childpid == 0) { + exit(execlp("bash", " bash", + "-c", + "rm -rf ../../../tools/mass_mfg/host_test | \ + cp -rf ../../../tools/mass_mfg/testdata mfg_testdata | \ + cp -rf ../nvs_partition_generator/testdata . | \ + mkdir -p ../../../tools/mass_mfg/host_test", NULL)); + } else { + CHECK(childpid > 0); + waitpid(childpid, &status, 0); + CHECK(WEXITSTATUS(status) == 0); + + childpid = fork(); + if (childpid == 0) { + exit(execlp("python", "python", + "../../../tools/mass_mfg/mfg_gen.py", + "generate-key", + "--outdir", + "../../../tools/mass_mfg/host_test", + "--key_protect_hmac", + "--kp_hmac_keygen", + "--kp_hmac_keyfile", + "hmac_key_host_test.bin", + "--keyfile", + "encr_keys_host_test.bin", NULL)); + + } else { + CHECK(childpid > 0); + waitpid(childpid, &status, 0); + CHECK(WEXITSTATUS(status) == 0); + + childpid = fork(); + if (childpid == 0) { + exit(execlp("python", "python", + "../../../tools/mass_mfg/mfg_gen.py", + "generate", + "../../../tools/mass_mfg/samples/sample_config.csv", + "../../../tools/mass_mfg/samples/sample_values_multipage_blob.csv", + "Test", + "0x4000", + "--outdir", + "../../../tools/mass_mfg/host_test", + "--version", + "2", + "--inputkey", + "../../../tools/mass_mfg/host_test/keys/encr_keys_host_test.bin", NULL)); + + } else { + CHECK(childpid > 0); + waitpid(childpid, &status, 0); + CHECK(WEXITSTATUS(status) == 0); + + childpid = fork(); + if (childpid == 0) { + exit(execlp("python", "python", + "../nvs_partition_generator/nvs_partition_gen.py", + "encrypt", + "../../../tools/mass_mfg/host_test/csv/Test-1.csv", + "../nvs_partition_generator/Test-1-partition-encrypted-hmac.bin", + "0x4000", + "--version", + "2", + "--inputkey", + "../../../tools/mass_mfg/host_test/keys/encr_keys_host_test.bin", NULL)); + + } else { + CHECK(childpid > 0); + waitpid(childpid, &status, 0); + CHECK(WEXITSTATUS(status) == 0); + + } + + } + + } + + } + + + SpiFlashEmulator emu1("../../../tools/mass_mfg/host_test/bin/Test-1.bin"); + + char hmac_key_buf[32]; + FILE *fp; + + fp = fopen("../../../tools/mass_mfg/host_test/keys/hmac_key_host_test.bin", "rb"); + fread(hmac_key_buf, sizeof(hmac_key_buf), 1, fp); + + fclose(fp); + + nvs_sec_cfg_t cfg; + compute_nvs_keys_with_hmac(&cfg, hmac_key_buf); + + check_nvs_part_gen_args_mfg(&emu1, NVS_DEFAULT_PART_NAME, 4, "mfg_testdata/sample_multipage_blob.bin", true, &cfg); + + SpiFlashEmulator emu2("../nvs_partition_generator/Test-1-partition-encrypted-hmac.bin"); + + check_nvs_part_gen_args_mfg(&emu2, NVS_DEFAULT_PART_NAME, 4, "testdata/sample_multipage_blob.bin", true, &cfg); + + childpid = fork(); + if (childpid == 0) { + exit(execlp("bash", " bash", + "-c", + "rm -rf keys | \ + rm -rf mfg_testdata | \ + rm -rf testdata | \ + rm -rf ../../../tools/mass_mfg/host_test", NULL)); + } else { + CHECK(childpid > 0); + waitpid(childpid, &status, 0); + CHECK(WEXITSTATUS(status) == 0); + + } + +} + #endif /* Add new tests above */ diff --git a/conftest.py b/conftest.py index da6f07ba46..385fe9fb26 100644 --- a/conftest.py +++ b/conftest.py @@ -124,6 +124,7 @@ ENV_MARKERS = { 'ecdsa_efuse': 'Runner with test ECDSA private keys programmed in efuse', 'ccs811': 'Runner with CCS811 connected', 'ethernet_w5500': 'SPI Ethernet module with two W5500', + 'nvs_encr_hmac': 'Runner with test HMAC key programmed in efuse', # multi-dut markers 'ieee802154': 'ieee802154 related tests should run on ieee802154 runners.', 'openthread_br': 'tests should be used for openthread border router.', From 3aa6f97c72242ec03e9e65ff4256f02bd529fd08 Mon Sep 17 00:00:00 2001 From: Laukik Hase Date: Thu, 9 Mar 2023 15:59:08 +0530 Subject: [PATCH 6/6] security: Added example for HMAC-based NVS encr-keys protection scheme --- examples/security/.build-test-rules.yml | 8 + .../nvs_encryption_hmac/CMakeLists.txt | 6 + .../security/nvs_encryption_hmac/README.md | 81 +++++++ .../nvs_encryption_hmac/main/CMakeLists.txt | 2 + .../security/nvs_encryption_hmac/main/main.c | 205 ++++++++++++++++++ .../partitions_example.csv | 4 + .../pytest_nvs_encr_hmac.py | 45 ++++ .../sdkconfig.ci.nvs_encr_hmac | 16 ++ .../nvs_encryption_hmac/sdkconfig.defaults | 7 + 9 files changed, 374 insertions(+) create mode 100644 examples/security/nvs_encryption_hmac/CMakeLists.txt create mode 100644 examples/security/nvs_encryption_hmac/README.md create mode 100644 examples/security/nvs_encryption_hmac/main/CMakeLists.txt create mode 100644 examples/security/nvs_encryption_hmac/main/main.c create mode 100644 examples/security/nvs_encryption_hmac/partitions_example.csv create mode 100644 examples/security/nvs_encryption_hmac/pytest_nvs_encr_hmac.py create mode 100644 examples/security/nvs_encryption_hmac/sdkconfig.ci.nvs_encr_hmac create mode 100644 examples/security/nvs_encryption_hmac/sdkconfig.defaults diff --git a/examples/security/.build-test-rules.yml b/examples/security/.build-test-rules.yml index 9c68c8780d..084469e45f 100644 --- a/examples/security/.build-test-rules.yml +++ b/examples/security/.build-test-rules.yml @@ -5,3 +5,11 @@ examples/security/flash_encryption: - if: IDF_TARGET in ["esp32s2", "esp32s3", "esp32c6", "esp32h2", "esp32c2"] temporary: true reason: lack of runners + +examples/security/nvs_encryption_hmac: + disable: + - if: SOC_HMAC_SUPPORTED != 1 + disable_test: + - if: IDF_TARGET not in ["esp32c3"] + temporary: true + reason: lack of runners diff --git a/examples/security/nvs_encryption_hmac/CMakeLists.txt b/examples/security/nvs_encryption_hmac/CMakeLists.txt new file mode 100644 index 0000000000..b06d1d05f9 --- /dev/null +++ b/examples/security/nvs_encryption_hmac/CMakeLists.txt @@ -0,0 +1,6 @@ +# The following lines of boilerplate have to be in your project's +# CMakeLists in this exact order for cmake to work correctly +cmake_minimum_required(VERSION 3.16) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +project(nvs_encryption_hmac) diff --git a/examples/security/nvs_encryption_hmac/README.md b/examples/security/nvs_encryption_hmac/README.md new file mode 100644 index 0000000000..a4eefc59fb --- /dev/null +++ b/examples/security/nvs_encryption_hmac/README.md @@ -0,0 +1,81 @@ +| Supported Targets | ESP32-C3 | ESP32-C6 | ESP32-H2 | ESP32-S2 | ESP32-S3 | +| ----------------- | -------- | -------- | -------- | -------- | -------- | + +# NVS Encryption with HMAC-based encryption key protection scheme + +## Overview + +This example demonstrates NVS encryption using the HMAC peripheral, wherein the encryption keys are derived from the HMAC key burnt in eFuse. Since the derivation of the encryption keys occurs at runtime, they are not stored in the flash. Thus, this feature does not require a separate `nvs_keys` partition and _also does not require flash encryption enabled_. + +## How to use the example + +### Hardware Required + +This example can be executed on any development board with a supported Espressif SOC chip - possessing a built-in HMAC peripheral (see `Supported Targets` table above). + +### Configure the project + +Before the project configuration and build, be sure to set the correct chip target using `idf.py set-target `. + +Open the project configuration menu (`idf.py menuconfig`). + +#### Configure the eFuse key ID storing the HMAC key + +- Set the eFuse key ID storing the HMAC key at `Component config → NVS Security Provider → eFuse key ID storing the HMAC key`. +The HMAC key stored at this key block will be used to generate the encryption keys for the default NVS partition (`nvs`), initialised with `nvs_flash_init()`. Note that the example will fail to build without setting this config option to the correct value (the default value is out of range). + +- Users can program their own HMAC key in the configured block before running the example - refer below snippet. The example checks if the configured block is empty or already programmed with an HMAC key - if empty, a new key is generated at runtime and stored in the block, or else the provided key is used. While burning the key prior to flashing the app, please make sure that the config value is set to the eFuse block holding the HMAC key. + +```shell +# Burning the HMAC-key in eFuse block 0 - key ID 0 +espefuse.py -p PORT burn_key BLOCK_KEY0 hmac_key_file.bin HMAC_UP +``` + +### Build and Flash + +Build the project and flash it to the board, then run the monitor tool to view the serial output: + +``` +idf.py -p PORT flash monitor +``` + +(To exit the serial monitor, type `Ctrl-]`.) + +See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects. + +### Example Output + +```log +I (300) nvs_sec_provider: NVS Encryption - Registering HMAC-based scheme... +I (308) app_start: Starting scheduler on CPU0 +I (313) main_task: Started on CPU0 +I (313) main_task: Calling app_main() +I (313) example: Initialising the default NVS partition +I (333) nvs: NVS partition "nvs" is encrypted. +I (603) example: Initialising the custom NVS partition +I (613) example: NVS partition "custom_nvs" is encrypted. +I (623) example: Key: u8_key | Val: 255 +I (623) example: Key: i8_key | Val: -128 +I (623) example: Key: u16_key | Val: 65535 +I (633) example: Key: u32_key | Val: 4294967295 +I (633) example: Key: i32_key | Val: -2147483648 +I (643) example: Key: str_key | Val: Lorem ipsum dolor sit amet, consectetur adipiscing elit. +Fusce quis risus justo. +Suspendisse egestas in nisi sit amet auctor. +Pellentesque rhoncus dictum sodales. +In justo erat, viverra at interdum eget, interdum vel dui. + +I (663) custom_nvs: 0x3ffc5f5c fe ff ff ff 00 00 00 00 fe ff ff ff ff ff ff ff |................| +I (673) custom_nvs: 0x3ffc5f6c ff ff ff ff ff ff ff ff ff ff ff ff 84 2d ba b9 |.............-..| +I (683) custom_nvs: 0x3ffc5f7c aa aa aa fa ff ff ff ff ff ff ff ff ff ff ff ff |................| +I (693) custom_nvs: 0x3ffc5f8c ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................| +I (703) custom_nvs: 0x3ffc5f9c ca 8b c3 bb 2d c2 33 d6 6b d4 a7 3d 31 0e 9c 36 |....-.3.k..=1..6| +I (713) custom_nvs: 0x3ffc5fac 39 7f bc d4 5c 6d f8 98 de 0a 90 50 21 23 ff 04 |9...\m.....P!#..| +I (723) custom_nvs: 0x3ffc5fbc ce f9 23 6f 2c d6 07 08 2d 0e d2 f2 a5 af 5a 2e |..#o,...-.....Z.| +I (733) custom_nvs: 0x3ffc5fcc c9 61 bd fc 96 fc 12 87 1b 8c cb fb 51 2c ed a2 |.a..........Q,..| +... +... +... +I (1133) custom_nvs: 0x3ffc624c ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................| +I (1143) main_task: Returned from app_main() +``` diff --git a/examples/security/nvs_encryption_hmac/main/CMakeLists.txt b/examples/security/nvs_encryption_hmac/main/CMakeLists.txt new file mode 100644 index 0000000000..8a9d914e13 --- /dev/null +++ b/examples/security/nvs_encryption_hmac/main/CMakeLists.txt @@ -0,0 +1,2 @@ +idf_component_register(SRCS "main.c" + INCLUDE_DIRS ".") diff --git a/examples/security/nvs_encryption_hmac/main/main.c b/examples/security/nvs_encryption_hmac/main/main.c new file mode 100644 index 0000000000..f13b6c281f --- /dev/null +++ b/examples/security/nvs_encryption_hmac/main/main.c @@ -0,0 +1,205 @@ +/* + * NVS Encryption with HMAC-based encryption key protection scheme example + * + * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Unlicense OR CC0-1.0 + */ + +#include +#include +#include +#include + +#include "esp_flash.h" +#include "esp_partition.h" +#include "esp_hmac.h" +#include "esp_log.h" + +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" + +#include "nvs_flash.h" +#include "nvs_sec_provider.h" + +#define CUSTOM_NVS_PART_LABEL "custom_nvs" +#define CUSTOM_NVS_PART_NAMESPACE "storage" +#define CUSTOM_NVS_PART_DUMP_SIZE (512 + 16) + +static const char* str_val = "Lorem ipsum dolor sit amet, consectetur adipiscing elit.\n" + "Fusce quis risus justo.\n" + "Suspendisse egestas in nisi sit amet auctor.\n" + "Pellentesque rhoncus dictum sodales.\n" + "In justo erat, viverra at interdum eget, interdum vel dui.\n"; + +static const char* TAG = "example"; + +static esp_err_t example_custom_nvs_part_init(const char *label) +{ + esp_err_t ret = ESP_FAIL; +#if defined(CONFIG_NVS_ENCRYPTION) && defined(CONFIG_NVS_SEC_KEY_PROTECT_USING_HMAC) + nvs_sec_cfg_t cfg = {}; + nvs_sec_scheme_t *sec_scheme_handle = NULL; + + nvs_sec_config_hmac_t sec_scheme_cfg = NVS_SEC_PROVIDER_CFG_HMAC_DEFAULT(); + + ret = nvs_sec_provider_register_hmac(&sec_scheme_cfg, &sec_scheme_handle); + if (ret != ESP_OK) { + return ret; + } + + ret = nvs_flash_read_security_cfg_v2(sec_scheme_handle, &cfg); + if (ret != ESP_OK) { + /* We shall not generate keys here as that must have been done in default NVS partition initialization case */ + ESP_LOGE(TAG, "Failed to read NVS security cfg: [0x%02X] (%s)", ret, esp_err_to_name(ret)); + return ret; + } + + ret = nvs_flash_secure_init_partition(label, &cfg); + if (ret == ESP_OK) { + ESP_LOGI(TAG, "NVS partition \"%s\" is encrypted.", label); + } + memset(&cfg, 0x00, sizeof(nvs_sec_cfg_t)); +#else + ret = nvs_flash_init_partition(label); +#endif + return ret; +} + +static esp_err_t example_custom_nvs_part_write(const char *label, const char *namespace) +{ + nvs_handle_t my_handle; + esp_err_t err = ESP_FAIL; + + // Open + err = nvs_open_from_partition(label, namespace, NVS_READWRITE, &my_handle); + if (err != ESP_OK) return err; + + // Write + err = nvs_set_u8(my_handle, "u8_key", UINT8_MAX); + if (err != ESP_OK) goto exit; + + err = nvs_set_i8(my_handle, "i8_key", INT8_MIN); + if (err != ESP_OK) goto exit; + + err = nvs_set_u16(my_handle, "u16_key", UINT16_MAX); + if (err != ESP_OK) goto exit; + + err = nvs_set_u32(my_handle, "u32_key", UINT32_MAX); + if (err != ESP_OK) goto exit; + + err = nvs_set_i32(my_handle, "i32_key", INT32_MIN); + if (err != ESP_OK) goto exit; + + err = nvs_set_str(my_handle, "str_key", str_val); + if (err != ESP_OK) goto exit; + + // Commit + err = nvs_commit(my_handle); + if (err != ESP_OK) goto exit; + +exit: + // Close + nvs_close(my_handle); + return err; +} + +static esp_err_t example_custom_nvs_part_read(const char *label, const char *namespace) +{ + nvs_handle_t my_handle; + esp_err_t err = ESP_FAIL; + + // Open + err = nvs_open_from_partition(label, namespace, NVS_READWRITE, &my_handle); + if (err != ESP_OK) return err; + + // Write + uint8_t u8_val = 0; + err = nvs_get_u8(my_handle, "u8_key", &u8_val); + if (err != ESP_OK) goto exit; + ESP_LOGI(TAG, "Key: u8_key | Val: %" PRIu8, u8_val); + + int8_t i8_val = 0; + err = nvs_get_i8(my_handle, "i8_key", &i8_val); + if (err != ESP_OK) goto exit; + ESP_LOGI(TAG, "Key: i8_key | Val: %" PRIi8, i8_val); + + uint16_t u16_val = 0; + err = nvs_get_u16(my_handle, "u16_key", &u16_val); + if (err != ESP_OK) goto exit; + ESP_LOGI(TAG, "Key: u16_key | Val: %" PRIu16, u16_val); + + uint32_t u32_val = 0; + err = nvs_get_u32(my_handle, "u32_key", &u32_val); + if (err != ESP_OK) goto exit; + ESP_LOGI(TAG, "Key: u32_key | Val: %" PRIu32, u32_val); + + int32_t i32_val = 0; + err = nvs_get_i32(my_handle, "i32_key", &i32_val); + if (err != ESP_OK) goto exit; + ESP_LOGI(TAG, "Key: i32_key | Val: %" PRIi32, i32_val); + + size_t str_val_len = 0; + err = nvs_get_str(my_handle, "str_key", NULL, &str_val_len); + if (err != ESP_OK) goto exit; + + char* str_key_val = malloc(str_val_len); + assert(str_val); + + err = nvs_get_str(my_handle, "str_key", str_key_val, &str_val_len); + if (err != ESP_OK) goto cleanup; + ESP_LOGI(TAG, "Key: str_key | Val: %s", str_key_val); + +cleanup: + free(str_key_val); +exit: + // Close + nvs_close(my_handle); + return err; +} + +void dump_custom_nvs_partition(const char *label, size_t len) +{ + const esp_partition_t* partition = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_NVS, label); + assert(partition); + + uint8_t *read_data = calloc(len, sizeof(uint8_t)); + assert(read_data != NULL); + + ESP_ERROR_CHECK(esp_partition_read(partition, 0, read_data, len)); + ESP_LOG_BUFFER_HEXDUMP(label, read_data, len, ESP_LOG_INFO); + + free(read_data); +} + +void app_main(void) +{ + ESP_LOGI(TAG, "Initialising the default NVS partition"); + + /* Initialising the default NVS partition */ + ESP_ERROR_CHECK(nvs_flash_init()); + + /* Erasing the custom NVS partition */ + ESP_ERROR_CHECK(nvs_flash_erase_partition(CUSTOM_NVS_PART_LABEL)); + + ESP_LOGI(TAG, "Initialising the custom NVS partition"); + /* Initialize the custom NVS partition with encryption enabled */ + esp_err_t ret = example_custom_nvs_part_init(CUSTOM_NVS_PART_LABEL); + if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) { + ESP_ERROR_CHECK(nvs_flash_erase_partition(CUSTOM_NVS_PART_LABEL)); + ret = example_custom_nvs_part_init(CUSTOM_NVS_PART_LABEL); + } + ESP_ERROR_CHECK(ret); + + ret = example_custom_nvs_part_write(CUSTOM_NVS_PART_LABEL, CUSTOM_NVS_PART_NAMESPACE); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "Failed to write NVS partition (%s | %s): %s", CUSTOM_NVS_PART_LABEL, CUSTOM_NVS_PART_NAMESPACE, esp_err_to_name(ret)); + }; + + ret = example_custom_nvs_part_read(CUSTOM_NVS_PART_LABEL, CUSTOM_NVS_PART_NAMESPACE); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "Failed to read NVS partition (%s | %s): %s", CUSTOM_NVS_PART_LABEL, CUSTOM_NVS_PART_NAMESPACE, esp_err_to_name(ret)); + }; + + dump_custom_nvs_partition(CUSTOM_NVS_PART_LABEL, CUSTOM_NVS_PART_DUMP_SIZE); +} diff --git a/examples/security/nvs_encryption_hmac/partitions_example.csv b/examples/security/nvs_encryption_hmac/partitions_example.csv new file mode 100644 index 0000000000..badae4c1f4 --- /dev/null +++ b/examples/security/nvs_encryption_hmac/partitions_example.csv @@ -0,0 +1,4 @@ +# Name, Type, SubType, Offset, Size, Flags +nvs, data, nvs, , 24K, +factory, app, factory, , 1M, +custom_nvs, data, nvs, , 24K, diff --git a/examples/security/nvs_encryption_hmac/pytest_nvs_encr_hmac.py b/examples/security/nvs_encryption_hmac/pytest_nvs_encr_hmac.py new file mode 100644 index 0000000000..06fe8cb73a --- /dev/null +++ b/examples/security/nvs_encryption_hmac/pytest_nvs_encr_hmac.py @@ -0,0 +1,45 @@ +# SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD +# SPDX-License-Identifier: Unlicense OR CC0-1.0 +import logging +import os + +import pytest +from pytest_embedded_idf.dut import IdfDut + +STR_KEY_VAL = ['Lorem ipsum dolor sit amet, consectetur adipiscing elit.', + 'Fusce quis risus justo.', + 'Suspendisse egestas in nisi sit amet auctor.', + 'Pellentesque rhoncus dictum sodales.', + 'In justo erat, viverra at interdum eget, interdum vel dui.'] + +ENCR_TEXT_ARR = ['fe ff ff ff 00 00 00 00 fe ff ff ff ff ff ff ff', + 'ca 8b c3 bb 2d c2 33 d6 6b d4 a7 3d 31 0e 9c 36', + 'bd c1 2a 10 87 44 5e 1c 4b 2c 7c 5d ac 97 48 63'] + + +@pytest.mark.esp32c3 +@pytest.mark.nvs_encr_hmac +@pytest.mark.parametrize('config', ['nvs_encr_hmac'], indirect=True) +def test_nvs_flash_encr_keys_hmac(dut: IdfDut) -> None: + # Logging example binary details + binary_file = os.path.join(dut.app.binary_path, 'nvs_encryption_hmac.bin') + bin_size = os.path.getsize(binary_file) + logging.info('nvs_encryption_hmac_bin_size : {}KB'.format(bin_size // 1024)) + + # Start test and verify serial output + dut.expect('NVS partition "nvs" is encrypted.', timeout=30) + dut.expect('NVS partition "custom_nvs" is encrypted', timeout=30) + + dut.expect('Key: u8_key | Val: 255', timeout=30) + dut.expect('Key: i8_key | Val: -128', timeout=30) + dut.expect('Key: u16_key | Val: 65535', timeout=30) + dut.expect('Key: u32_key | Val: 4294967295', timeout=30) + dut.expect('Key: i32_key | Val: -2147483648', timeout=30) + + for string in STR_KEY_VAL: + dut.expect(string, timeout=30) + + for encr_txt in ENCR_TEXT_ARR: + dut.expect(encr_txt, timeout=30) + + dut.expect('Returned from app_main()', timeout=30) diff --git a/examples/security/nvs_encryption_hmac/sdkconfig.ci.nvs_encr_hmac b/examples/security/nvs_encryption_hmac/sdkconfig.ci.nvs_encr_hmac new file mode 100644 index 0000000000..c2cab83da9 --- /dev/null +++ b/examples/security/nvs_encryption_hmac/sdkconfig.ci.nvs_encr_hmac @@ -0,0 +1,16 @@ +# Partition Table +CONFIG_PARTITION_TABLE_OFFSET=0x9000 + +# NOTE: The runner for this example has flash-encryption enabled +# 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 + +# NVS Encryption +CONFIG_NVS_SEC_HMAC_EFUSE_KEY_ID=0 diff --git a/examples/security/nvs_encryption_hmac/sdkconfig.defaults b/examples/security/nvs_encryption_hmac/sdkconfig.defaults new file mode 100644 index 0000000000..3db25efd40 --- /dev/null +++ b/examples/security/nvs_encryption_hmac/sdkconfig.defaults @@ -0,0 +1,7 @@ +# This example uses an extra partition to demonstrate encrypted/non-encrypted reads/writes. +CONFIG_PARTITION_TABLE_CUSTOM=y +CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions_example.csv" +CONFIG_PARTITION_TABLE_FILENAME="partitions_example.csv" + +CONFIG_NVS_ENCRYPTION=y +CONFIG_NVS_SEC_KEY_PROTECT_USING_HMAC=y