From c1bed366ba733426825e98f36f090fbed142bf57 Mon Sep 17 00:00:00 2001 From: Laukik Hase Date: Wed, 5 Apr 2023 18:03:56 +0530 Subject: [PATCH] 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