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.
This commit is contained in:
Laukik Hase 2023-04-05 18:03:56 +05:30
parent 1cf46bd0f0
commit c1bed366ba
No known key found for this signature in database
GPG Key ID: 11C571361F51A199
10 changed files with 147 additions and 62 deletions

View File

@ -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.

View File

@ -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()

View File

@ -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"

View File

@ -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

View File

@ -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<NVSHandleEntry>, 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)
{

View File

@ -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

View File

@ -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

View File

@ -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)
{

View File

@ -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);

View File

@ -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