NVS 加密

[English]

概述

本文档主要介绍 NVS 加密功能,这一功能有助于实现设备在 flash 中的安全存储。

存储在 NVS 分区中的数据可以用 XTS-AES 进行加密,与磁盘加密标准 IEEE P1619 中提到的加密方式类似。加密时,每个条目都被视作一个 sector,而条目的相对地址(相对于分区起始位置)作为 sector-number 输入加密算法。

根据要使用的具体方案,可以选择启用 CONFIG_NVS_ENCRYPTIONCONFIG_NVS_SEC_KEY_PROTECTION_SCHEME > CONFIG_NVS_SEC_KEY_PROTECT_USING_FLASH_ENCCONFIG_NVS_SEC_KEY_PROTECT_USING_HMAC 实现 NVS 加密。

NVS 加密:基于 flash 加密的方案

在这个方案中,NVS 加密所需的密钥存储在另一个分区中,该分区用 flash 加密 进行保护。因此,使用该方案时,必须先启用 flash 加密

启用 flash 加密 时需同时启用 NVS 加密,因为 Wi-Fi 驱动程序会将凭据(如 SSID 和密码)储存在默认的 NVS 分区中。如已启用平台级加密,那么则需要同时启用 NVS 加密。

要用这一方案进行 NVS 加密,分区表中必须包含 NVS 密钥分区。在分区表选项 ( menuconfig > Partition Table ) 中,有两个包含 NVS 密钥分区 的分区表,可通过项目配置菜单 ( idf.py menuconfig) 进行选择。要了解如何配置和使用 NVS 加密功能,请参考示例 security/flash_encryption

NVS 密钥分区

应用如果要使用 NVS 加密(使用基于 flash 加密的方案)编译时,须使用类型为 data 和子类型为 key 的密钥分区。该分区应被标记为 encrypted 且最小为 4 KB (最小分区大小)。参考 分区表 了解详情。在分区表选项 ( menuconfig > Partition Table) 中,有两个包含 NVS 密钥分区 的额外分区表,可以直接用于 NVS 加密。分区的结构如下所示:

+-----------+--------------+-------------+----+
|              XTS encryption key (32)        |
+---------------------------------------------+
|              XTS tweak key (32)             |
+---------------------------------------------+
|                  CRC32 (4)                  |
+---------------------------------------------+

可以通过以下两种方式之一生成 NVS 密钥分区 中的 XTS 加密密钥:

在 ESP32-C6 芯片上生成密钥

备注

请注意,在使用此方法启动应用程序前,必须完全擦除 nvs_keys 分区。否则,应用程序可能会生成 ESP_ERR_NVS_CORRUPT_KEY_PART 错误代码,该代码假设 nvs_keys 分区不为空并且包含格式错误的数据。可以使用以下命令来实现:

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

使用预生成的 NVS 密钥分区

如果 NVS 密钥分区 中的密钥不是由应用程序生成,则需要使用预先生成的密钥分区。可以使用 NVS 分区生成程序 生成包含 XTS 加密密钥的 NVS 密钥分区。然后使用以下两个命令将预生成的密钥分区存储到 flash 上:

1. 构建并烧写分区表

idf.py partition-table partition-table-flash

2. 使用 parttool.py (参见 分区表 中分区工具相关章节)将密钥存储在 flash 上的 NVS 密钥分区

parttool.py --port PORT --partition-table-offset PARTITION_TABLE_OFFSET write_partition --partition-name="name of nvs_key partition" --input NVS_KEY_PARTITION_FILE

备注

如果设备是在 flash 加密开发模式下加密的,那么要更新 NVS 密钥分区就需要使用 parttool.py 来加密 NVS 密钥分区,并提供一个指向你构建目录中未加密分区表的指针 (build/partition_table),因为设备上的分区表也是加密的。命令如下:

parttool.py --esptool-write-args encrypt --port PORT --partition-table-file=PARTITION_TABLE_FILE --partition-table-offset PARTITION_TABLE_OFFSET write_partition --partition-name="nvs_key 分区名称" --input NVS_KEY_PARTITION_FILE

由于密钥分区被标记为 encrypted,且 flash 加密 已启用,引导程序会在首次启动时使用 flash 加密密钥对此分区进行加密。

一个应用程序可以使用不同的密钥对不同的 NVS 分区进行加密,从而拥有多个密钥分区。应用程序应为加密或解密操作提供正确的密钥分区和密钥信息。

NVS 加密:基于 HMAC 外设的方案

此方案中,用于 NVS 加密的 XTS 密钥来自 eFuse 中编程的 HMAC 密钥,其目的是 esp_efuse_purpose_t::ESP_EFUSE_KEY_PURPOSE_HMAC_UP。由于加密密钥在运行时生成,不存储在 flash 中,因此这个功能不需要单独的 NVS 密钥分区

备注

通过这个方案, 无需启用 flash 加密 就能在 ESP32-C6 上实现安全存储。

重要

注意,此方案使用一个 eFuse 块来存储获取加密密钥所需的 HMAC 密钥。

备注

CONFIG_NVS_SEC_HMAC_EFUSE_KEY_ID 配置的有效范围为 0 (hmac_key_id_t::HMAC_KEY0) 到 5 (hmac_key_id_t::HMAC_KEY5)。默认情况下该配置为 6 (hmac_key_id_t::HMAC_KEY_MAX),须在构建用户应用程序之前进行修改。

备注

可以使用以下命令预先在 eFuse 中设置自己的 HMAC 密钥:

espefuse.py -p PORT burn_key <BLOCK_KEYN> <hmac_key_file.bin> HMAC_UP

加密读/写

NVS API 函数 nvs_get_*nvs_set_* 也可用于读取和写入加密的 NVS 分区。

加密默认的 NVS 分区

另外,还可使用 API 函数 nvs_flash_secure_init() 为默认 NVS 分区启用加密。

加密自定义 NVS 分区

备注

在采用基于 HMAC 的方案时,可以在不启用任何 NVS 加密的配置选项的情况下开始上述工作流:CONFIG_NVS_ENCRYPTIONCONFIG_NVS_SEC_KEY_PROTECTION_SCHEME -> CONFIG_NVS_SEC_KEY_PROTECT_USING_HMACCONFIG_NVS_SEC_HMAC_EFUSE_KEY_ID,以使用 nvs_flash_secure_init() API 加密默认分区及自定义的 NVS 分区。

NVS Security Provider

组件 nvs_sec_provider 存储了 NVS 加密方案的所有特定实现代码,并且适用于未来的方案。此组件充当 nvs_flash 组件处理加密密钥的接口。组件 nvs_sec_provider 有自己的配置菜单,选定的安全方案和相应设置基于这一菜单注册到 nvs_flash 组件。

该组件通过工厂函数注册了特殊的安全框架,可以实现出厂即用的安全方案。在该方案中,无需使用 API 来生成、读取加密密钥(如 nvs_sec_provider_register_hmac())。要了解 API 的使用,参考示例 security/nvs_encryption_hmac

API 参考

Header File

  • components/nvs_sec_provider/include/nvs_sec_provider.h

  • This header file can be included with:

    #include "nvs_sec_provider.h"
    
  • This header file is a part of the API provided by the nvs_sec_provider component. To declare that your component depends on nvs_sec_provider, add the following to your CMakeLists.txt:

    REQUIRES nvs_sec_provider
    

    or

    PRIV_REQUIRES nvs_sec_provider
    

Functions

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)

Register the Flash-Encryption based scheme for NVS Encryption.

参数
  • sec_scheme_cfg -- [in] Security scheme specific configuration data

  • sec_scheme_handle_out -- [out] Security scheme specific configuration handle

返回

  • 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_hmac(const nvs_sec_config_hmac_t *sec_scheme_cfg, nvs_sec_scheme_t **sec_scheme_handle_out)

Register the HMAC-based scheme for NVS Encryption.

参数
  • sec_scheme_cfg -- [in] Security scheme specific configuration data

  • sec_scheme_handle_out -- [out] Security scheme specific configuration handle

返回

  • 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_deregister(nvs_sec_scheme_t *sec_scheme_handle)

Deregister the NVS encryption scheme registered with the given handle.

参数

sec_scheme_handle -- [in] Security scheme specific configuration handle

返回

  • ESP_OK, if the scheme registered with sec_scheme_handle was deregistered successfully

  • ESP_ERR_INVALID_ARG, if sec_scheme_handle is NULL;

Structures

struct nvs_sec_config_flash_enc_t

Flash encryption-based scheme specific configuration data.

Public Members

const esp_partition_t *nvs_keys_part

Partition of subtype nvs_keys holding the NVS encryption keys

struct nvs_sec_config_hmac_t

HMAC-based scheme specific configuration data.

Public Members

hmac_key_id_t hmac_key_id

HMAC Key ID used for generating the NVS encryption keys

Macros

ESP_ERR_NVS_SEC_BASE

Starting number of error codes

ESP_ERR_NVS_SEC_HMAC_KEY_NOT_FOUND

HMAC Key required to generate the NVS encryption keys not found

ESP_ERR_NVS_SEC_HMAC_KEY_BLK_ALREADY_USED

Provided eFuse block for HMAC key generation is already in use

ESP_ERR_NVS_SEC_HMAC_KEY_GENERATION_FAILED

Failed to generate/write the HMAC key to eFuse

ESP_ERR_NVS_SEC_HMAC_XTS_KEYS_DERIV_FAILED

Failed to derive the NVS encryption keys based on the HMAC-based scheme

NVS_SEC_PROVIDER_CFG_FLASH_ENC_DEFAULT()

Helper for populating the Flash encryption-based scheme specific configuration data.

NVS_SEC_PROVIDER_CFG_HMAC_DEFAULT()

Helper for populating the HMAC-based scheme specific configuration data.

Enumerations

enum nvs_sec_scheme_id_t

NVS Encryption Keys Protection Scheme.

Values:

enumerator NVS_SEC_SCHEME_FLASH_ENC

Protect NVS encryption keys using Flash Encryption

enumerator NVS_SEC_SCHEME_HMAC

Protect NVS encryption keys using HMAC peripheral

enumerator NVS_SEC_SCHEME_MAX