警告

This document is not updated for ESP32C5 yet, so some of the content may not be correct.

This warning was automatically inserted due to the source file being in the add_warnings_pages list.

NVS 加密

[English]

概述

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

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

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 和子类型为 nvs_keys 的密钥分区。该分区应被标记为 encrypted 且最小为 4 KB (最小分区大小)。参考 分区表 了解详情。在分区表选项 ( menuconfig > Partition Table) 中,有两个包含 NVS 密钥分区 的额外分区表,可以直接用于 NVS 加密。分区的结构如下所示:

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

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

在 ESP32-C5 芯片上生成密钥

备注

请注意,在使用此方法启动应用程序前,必须完全擦除 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 API 函数 nvs_get_*nvs_set_* 也可用于读取和写入加密的 NVS 分区。

加密默认的 NVS 分区

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

加密自定义 NVS 分区

NVS Security Provider

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

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

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.

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