mirror of
https://github.com/espressif/esp-idf.git
synced 2024-10-05 20:47:46 -04:00
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:
parent
1cf46bd0f0
commit
c1bed366ba
@ -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.
|
||||
|
||||
|
@ -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()
|
||||
|
@ -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"
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user