ESP-IDF NVS 分区生成器实战:用 CSV 定制 NVS 分区,支持加密与多页 Blob
【免费下载链接】esp-idfEspressif IoT Development Framework. Official development framework for Espressif SoCs.项目地址: https://gitcode.com/GitHub_Trending/es/esp-idf
本文基于 ESP-IDF 仓库中的 NVS Partition Generator 文档 展开,讲解nvs_partition_gen.py工具的完整用法:如何用 CSV 文件描述键值对并生成可烧录的 NVS 二进制分区,包括多页 Blob(multipage blob)格式版本控制、XTS-AES 加密分区的生成与解密、以及 HMAC 密钥保护方案。读完后你将能够完成 ODM/OEM 产线场景下"一份固件、多台设备各自定制参数(如序列号)"的 NVS 分区定制工作,并理解加密密钥分区与应用侧 sdkconfig 的一致性要求。
工具定位与适用场景
该工具的实际入口是 nvs_partition_gen.py,它负责根据 CSV 文件提供的键值对,生成符合 NVS 架构定义 的二进制文件。
它的典型应用场景是:为 ODM/OEM 厂商生成包含设备特定数据的二进制镜像,在设备量产时由外部设备烧录。这样厂家可以用同一份应用固件,为每台设备烧录定制化的 NVS 数据(例如每台设备唯一的序列号),而不需要为每台设备单独编译固件。
从源码结构看,nvs_partition_gen.py 本身只有一行核心逻辑——通过subprocess调用esp_idf_nvs_partition_genPython 模块执行真正的生成逻辑,即该工具以 Python 包形式随 ESP-IDF 安装环境一起分发,仓库中的脚本只是方便用户直接运行的薄封装。
前提条件
若要以加密模式使用本工具,需要安装cryptography包。文档指出,所有必需依赖均已包含在 ESP-IDF 根目录的requirements.txt中,即执行 ESP-IDF 标准的 Python 依赖安装(idf_tools.py install-python-env)后即可满足加密模式所需。
CSV 文件格式
CSV 文件每一行包含 4 个以逗号分隔的参数:
| 序号 | 参数 | 说明 | 备注 |
|---|---|---|---|
| 1 | Key | 数据键名,应用之后使用该键访问数据 | — |
| 2 | Type | 支持file、data、namespace三种类型 | — |
| 3 | Encoding | 支持u8、i8、u16、i16、u32、i32、u64、i64、string、hex2bin、base64、binary。指定实际数据在输出二进制文件中的编码方式。string与binary的区别在于:string数据以 NULL 字符结尾,binary数据不以 NULL 结尾 | 目前file类型仅支持hex2bin、base64、string、binary四种编码 |
| 4 | Value | 数据值 | namespace类型的Encoding和Value两列必须留空(其取值固定、不可配置,填写也会被忽略) |
两点格式硬性要求:
- 第一行必须是不可配置的列头:
key,type,encoding,value; - 空格规则:逗号前后不能有空格,每行行尾不能有空格。
示例 CSV(仓库中随工具提供的 sample_singlepage_blob.csv 即包含下列全部要素):
key,type,encoding,value <-- 列头 namespace_name,namespace,, <-- 第一条必须为 namespace 类型 key1,data,u8,1 key2,file,string,/path/to/file仓库还另外提供了一份 sample_val.csv,演示了在storage命名空间下写入 u8/i8/u16/u32/i32 整型和多行字符串的用法;字符串若包含逗号或换行,需要用双引号包裹(可跨行)。
NVS 条目与命名空间的关联规则
命名空间(namespace)的关联遵循"区间归属"规则:当解析器在 CSV 中遇到一个 namespace 条目时,其后所有条目都归属于该命名空间,直到遇到下一个 namespace 条目为止——之后的条目转而归属于新的命名空间。
因此第一条条目必须是namespace条目,否则后续的 data/file 条目将无处归属。这也是所有示例 CSV 都将namespace行放在第一位的原因。
多页 Blob 支持(格式版本 1 与 2)
默认情况下,二进制 Blob 允许跨多个页面存储,按 NVS 文档中的条目结构(structure of entry)格式写入,对应格式版本 2(Version 2,默认值)。如果目标设备运行的是旧版本固件,可以使用--version 1选项禁用多页 Blob 支持,生成版本 1格式:
| 版本 | 含义 |
|---|---|
--version 1 | 禁用多页 Blob 支持 |
--version 2 | 启用多页 Blob 支持(默认) |
版本 2 的完整示例命令(仓库提供 sample_multipage_blob.csv 作为输入,其包含testdata/sample_multipage_blob.bin这样的大 Blob 文件):
python nvs_partition_gen.py generate sample_multipage_blob.csv sample.bin 0x4000 --version 2两个注意点(原文档 Caveats 部分):
- 所需最小 NVS 分区大小为
0x3000字节; - 将生成的二进制烧录到设备时,必须确保它与应用侧的
sdkconfig配置一致(例如加密与否、格式版本等需与应用匹配)。
加密分区的生成与解密
工具支持创建加密二进制文件和解密已有加密文件,采用XTS-AES加密方案(详见 NVS 加密文档)。ESP-IDF 侧对应 Kconfig 中的NVS_ENCRYPTION选项:启用后整个 NVS 数据(页头除外)用 XTS-AES 加密,XTS 密钥要么存放在一个加密的密钥分区中(此时必须启用 Flash 加密),要么从烧入 eFuse 的 HMAC 密钥派生。工具侧的密钥生成/加密流程与应用侧这两套方案一一对应。
子命令总览
工具提供 4 个子命令,各子命令可通过python nvs_partition_gen.py {command} -h查看更详细帮助:
| 命令 | 说明 |
|---|---|
generate | 生成 NVS 分区 |
generate-key | 生成加密密钥 |
encrypt | 生成加密的 NVS 分区 |
decrypt | 解密加密的 NVS 分区 |
generate:生成 NVS 分区(默认命令)
python nvs_partition_gen.py generate [-h] [--version {1,2}] [--outdir OUTDIR] input output size位置参数:
| 参数 | 说明 |
|---|---|
input | 待解析 CSV 文件的路径 |
output | 输出 NVS 二进制文件的路径 |
size | NVS 分区大小(字节,必须是 4096 的整数倍) |
可选参数:
| 参数 | 说明 |
|---|---|
-h/--help | 显示帮助信息 |
--version {1,2} | 设置多页 Blob 格式版本(默认 Version 2;1 表示禁用多页 Blob 支持,2 表示启用) |
--outdir OUTDIR | 生成文件的输出目录(默认当前目录) |
运行示例:
python nvs_partition_gen.py generate sample_singlepage_blob.csv sample.bin 0x3000generate-key:仅生成加密密钥分区
# 默认(Flash 加密)方案 python nvs_partition_gen.py generate-key [-h] [--keyfile KEYFILE] [--outdir OUTDIR]| 参数 | 说明 |
|---|---|
--keyfile KEYFILE | 输出密钥文件的路径 |
--outdir OUTDIR | 输出目录(默认当前目录) |
最小运行命令:
python nvs_partition_gen.py generate-keyHMAC 方案专用参数(仅当目标芯片支持 HMAC 外设,即 Kconfig 中SOC_HMAC_SUPPORTED成立时可用,例如 ESP32-C3 等 RISC-V 芯片):
| 参数 | 说明 |
|---|---|
--key_protect_hmac | 设置后使用基于 HMAC 外设的 NVS 加密密钥保护方案;否则使用默认的基于 Flash 加密的方案 |
--kp_hmac_keygen | 为 HMAC 方案生成 HMAC 密钥 |
--kp_hmac_keyfile KP_HMAC_KEYFILE | HMAC 密钥文件输出路径 |
--kp_hmac_inputkey KP_HMAC_INPUTKEY | 包含用于派生 NVS 加密密钥的 HMAC 密钥的文件 |
HMAC 方案的两种典型命令:
# 同时生成 HMAC 密钥与 NVS 加密密钥 python nvs_partition_gen.py generate-key --key_protect_hmac --kp_hmac_keygen执行后会在输出目录创建加密密钥文件<outdir>/keys/keys-<timestamp>.bin和 HMAC 密钥文件<outdir>/keys/hmac-keys-<timestamp>.bin(两个文件名均可通过参数自定义)。
# 已有 HMAC 密钥时,仅生成 NVS 加密密钥 python nvs_partition_gen.py generate-key --key_protect_hmac --kp_hmac_inputkey testdata/sample_hmac_key.bin仓库 testdata/ 目录下提供了sample_hmac_key.bin、sample_encryption_keys.bin、sample_encryption_keys_hmac.bin等样例密钥文件,可直接用于演练上述流程。
encrypt:生成加密的 NVS 分区
python nvs_partition_gen.py encrypt [-h] [--version {1,2}] [--keygen] [--keyfile KEYFILE] [--inputkey INPUTKEY] [--outdir OUTDIR] input output size位置参数与generate相同(inputCSV 路径、output输出 NVS 二进制路径、size分区大小且为 4096 的整数倍)。
可选参数:
| 参数 | 说明 |
|---|---|
--version {1,2} | 多页 Blob 格式版本(默认 2) |
--keygen | 由工具自动生成 NVS 分区加密密钥 |
--keyfile KEYFILE | 输出密钥文件的路径 |
--inputkey INPUTKEY | 包含 NVS 分区加密密钥的输入文件 |
--outdir OUTDIR | 输出目录(默认当前目录) |
HMAC 方案支持的额外参数与generate-key相同(--key_protect_hmac、--kp_hmac_keygen、--kp_hmac_keyfile、--kp_hmac_inputkey)。
按"密钥来源"分为四种典型用法:
# 1. 让工具自动生成密钥 python nvs_partition_gen.py encrypt sample_singlepage_blob.csv sample_encr.bin 0x3000 --keygen # 生成 <outdir>/keys/keys-<timestamp>.bin # 2. 自定义密钥文件名 python nvs_partition_gen.py encrypt sample_singlepage_blob.csv sample_encr.bin 0x3000 --keygen --keyfile sample_keys.bin # 生成 <outdir>/keys/sample_keys.bin # 3. 使用已有的密钥文件加密 python nvs_partition_gen.py encrypt sample_singlepage_blob.csv sample_encr.bin 0x3000 --inputkey sample_keys.binHMAC 方案的两种变体:
# 工具同时生成 NVS 加密密钥与 HMAC 密钥 python nvs_partition_gen.py encrypt sample_singlepage_blob.csv sample_encr.bin 0x3000 --keygen --key_protect_hmac --kp_hmac_keygen # 生成 keys/keys-<timestamp>.bin 与 keys/hmac-keys-<timestamp>.bin # 用户自行提供 HMAC 密钥 python nvs_partition_gen.py encrypt sample_singlepage_blob.csv sample_encr.bin 0x3000 --keygen --key_protect_hmac --kp_hmac_inputkey testdata/sample_hmac_key.bin关键兼容性说明:--keyfile生成并保存在keys/目录下的密钥文件,与 NVS 密钥分区(key partition)结构兼容。结合 NVS 加密文档:应用侧要求分区表中存在一个类型data、子类型nvs_keys、标记为encrypted、大小至少 4 KB 的密钥分区,菜单配置(idf.py menuconfig的 Partition Table 选项)中也提供了两份带该密钥分区的现成分区表。也就是说,工具生成的密钥文件可以直接烧录为密钥分区,与应用侧NVS_ENCRYPTION机制无缝对接。
decrypt:解密加密的 NVS 分区
python nvs_partition_gen.py decrypt [-h] [--outdir OUTDIR] input key output| 参数 | 说明 |
|---|---|
input | 待解密的加密 NVS 分区文件路径 |
key | 包含解密密钥的文件路径 |
output | 输出解密后的二进制文件路径 |
--outdir OUTDIR | 输出目录(默认当前目录) |
运行示例:
python nvs_partition_gen.py decrypt sample_encr.bin sample_keys.bin sample_decr.bin解密时同样可以指定格式版本号:版本 1 对应禁用多页 Blob 支持,版本 2 对应启用多页 Blob 支持。
多页 Blob 两档格式的运行示例
版本 1(禁用多页 Blob):
python nvs_partition_gen.py generate sample_singlepage_blob.csv sample.bin 0x3000 --version 1版本 2(启用多页 Blob):
python nvs_partition_gen.py generate sample_multipage_blob.csv sample.bin 0x4000 --version 2两个示例分别对应仓库中提供的 sample_singlepage_blob.csv 和 sample_multipage_blob.csv,区别仅在最后引用的 Blob 文件:单页版引用testdata/sample_singlepage_blob.bin(单页内可容纳的 Blob),多页版引用testdata/sample_multipage_blob.bin(跨页的大 Blob),分区大小也相应从0x3000提升到0x4000。
使用限制(Caveats)
原文档明确列出以下限制,生产使用前务必注意:
- 不检查重复键:工具不会检测重复的 key,两个同名键的数据都会被写入。键的唯一性需要使用者自己保证;
- 新页不回填旧页剩余空间:一旦开始写新页,之前页面剩余的空间就不会再被利用。CSV 中的条目顺序应当有意识地安排以优化存储空间(例如把大的 Blob 排在合适位置,避免"小条目碎页"浪费);
- 64 位数据类型尚不支持:CSV 中可写
u64/i64编码值,但工具实际尚未支持 64 位数据写入,应避开这两类编码。
配套工具与交叉参考
- 生成完成后,可以用同目录下的 nvs 分区解析工具 检查分区内容,其文档 描述了
nvs_tool.py的用法; - NVS 运行时行为、API 与分区结构见 nvs_flash 参考文档;
- 密钥分区结构、XTS-AES 与 HMAC 方案的完整说明见 NVS 加密文档;
- 端到端的加密 NVS 测试用例可参考 nvs_flash 测试应用,其中包含由该工具生成的
partition_encrypted.bin、partition_encrypted_hmac.bin等真实产物及其sdkconfig.ci.*配置,可用于验证"工具生成的密钥分区 + 应用侧NVS_ENCRYPTION配置"的组合在不同芯片上是否工作正常。
【免费下载链接】esp-idfEspressif IoT Development Framework. Official development framework for Espressif SoCs.项目地址: https://gitcode.com/GitHub_Trending/es/esp-idf
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考