启用安全功能的工作流程
概述
启用 ESP32 SoC 的安全功能时,建议保证不间断的电源供应。在此过程中,如果发生电力故障,可能会引起难以调试的问题,甚至在某些情况下可能导致永久性启动故障。
这份指南介绍了一系列工作流程,从而在外部主机的协助下启用设备的安全功能。这些工作流程分为多个阶段,每个阶段都会在主机上生成签名/加密密钥,从而在发生电力或其他故障时,提高恢复几率。此外,在主机的协助下,这些流程将加快整体配置过程(例如,在主机上加密固件要比在设备上加密更快)。
目标
- 用逐步指令简化启用安全功能的传统工作流程。 
- 设计比基于固件的传统工作流更加灵活的工作流。 
- 将工作流划分为多个小操作,从而提高可靠性。 
- 消除对 二级引导加载程序 的依赖。 
准备工作
- esptool:确保已安装- esptool。可运行以下命令安装:
pip install esptool
目录
启用安全功能
外部启用 flash 加密和 Secure Boot v2
重要
建议在生产用例中同时启用 flash 加密和 Secure Boot v2。
外部启用 flash 加密和 Secure Boot v2 时,须遵循以下启用顺序:
- 按照 外部启用 flash 加密 中列出的步骤启用 flash 加密功能。 
- 按照 外部启用 Secure Boot v2 中列出的步骤启用 Secure Boot v2 功能。 
须严格遵循以上顺序,因为启用 Secure Boot (SB) v2 时,要确保 SB v2 密钥可读。通过启用 RD_DIS (ESP_EFUSE_WR_DIS_RD_DIS) 的写保护,确保了密钥的可读性。但是,这也为启用 flash 加密带来了困难,因为 flash 加密 (FE) 密钥须保持不可读状态。产生这种冲突的原因是 RD_DIS 已受到写保护,因此无法对 FE 密钥进行读保护。
外部启用 flash 加密
在这种情况下,所有与 flash 加密相关的 eFuse 都是借助 espefuse 工具写入的。关于 flash 加密过程的详细信息,请参阅 flash 加密。
- 确保有一块 ESP32-C2,其默认 flash 加密 eFuse 设置如 相关 eFuses 所示 - 参考 ESP32-C2 flash 加密状态,查看 flash 加密状态。 - 此时需要擦除芯片上的 flash,且 flash 加密必须尚未启用。请运行以下命令进行擦除: - esptool.py --port PORT erase_flash 
- 生成一个 flash 加密密钥 - 运行以下命令可以生成一个随机的 flash 加密密钥: - 如果 :ref:` 生成的 AES-XTS 密钥的大小 <CONFIG_SECURE_FLASH_ENCRYPTION_KEYSIZE>` 为 AES-128(256 位密钥): - espsecure.py generate_flash_encryption_key my_flash_encryption_key.bin - 如果 生成的 AES-XTS 密钥的大小 是从 128 位(SHA256(128 位))派生的 AES-128 密钥: - espsecure.py generate_flash_encryption_key --keylen 128 my_flash_encryption_key.bin 
- 将 flash 加密密钥烧录到 eFuse 中 - 警告 - 这个操作 无法回退。 - 运行以下命令进行烧录: - 对于 AES-128 (256 位密钥) - - XTS_AES_128_KEY(- XTS_KEY_LENGTH_256eFuse 会被烧录为 1):- espefuse.py --port PORT burn_key BLOCK_KEY0 flash_encryption_key256.bin XTS_AES_128_KEY - 对于从 SHA256(128 eFuse 位)派生的 AES-128 密钥 - - XTS_AES_128_KEY_DERIVED_FROM_128_EFUSE_BITS。FE 密钥会被写入 eFuse BLOCK_KEY0 的后半部分。前 128 位不会被使用,并保持可供软件读取状态。使用 espefuse 工具的特殊模式,可以用任何 espefuse 命令将数据写入其中,可参考下文- 同时烧录两个密钥。- espefuse.py --port PORT burn_key BLOCK_KEY0 flash_encryption_key128.bin XTS_AES_128_KEY_DERIVED_FROM_128_EFUSE_BITS - 同时烧录两个密钥(Secure Boot 和 flash 加密): - espefuse.py --port PORT --chip esp32c2 burn_key_digest secure_boot_signing_key.pem \ burn_key BLOCK_KEY0 flash_encryption_key128.bin XTS_AES_128_KEY_DERIVED_FROM_128_EFUSE_BITS 
- 烧录 - SPI_BOOT_CRYPT_CNTeFuse- 如果你只想在 开发 模式下启用 flash 加密,并在将来可能会禁用 flash 加密,可将下面命令中的 SPI_BOOT_CRYPT_CNT 值从 7 更新为 0x1。(不推荐在生产中使用) - espefuse.py --port PORT --chip esp32c2 burn_efuse SPI_BOOT_CRYPT_CNT 7 
- 烧录下列与 flash 加密相关的安全 eFuse - 烧录安全 eFuse 
 - 重要 - 对于生产用例,强烈建议烧录下列所有的 eFuse。 - DIS_DOWNLOAD_ICACHE:禁用 UART cache
- DIS_DIRECT_BOOT:禁用直接引导(旧版 SPI 引导模式)
- DIS_PAD_JTAG:永久禁用 JTAG
- DIS_DOWNLOAD_MANUAL_ENCRYPT:禁用 UART 引导加载程序加密访问
 - 可运行以下命令烧录相应的 eFuse: - espefuse.py burn_efuse --port PORT EFUSE_NAME 0x1 - 备注 - 请将 - EFUSE_NAME更新为需要烧录的 eFuse。可以在上述命令中添加多个 efuse 同时进行烧录(例如:- EFUSE_NAME VAL EFUSE_NAME2 VAL2)。有关 espefuse.py 的更多信息,请参阅 此文档。
- 配置项目 - 项目的引导加载程序和应用程序二进制文件必须使用默认配置的 flash 加密量产模式进行构建。 - 如下所示,可以在 menuconfig 中设置 flash 加密量产模式: 
- 选择量产模式 (注意,若选择量产模式,则将烧录 - EFUSE_DIS_DOWNLOAD_MANUAL_ENCRYPTeFuse 位,ROM 下载模式下 flash 加密硬件将被禁用)。
- 选择 UART ROM 下载模式(永久切换到安全模式(推荐))。这是推荐的默认选项,如果不需要,也可将其更改为永久禁用 UART ROM 下载模式。 
- 保存配置并退出。 
 
- 构建、加密并烧录二进制文件 - 可以在主机上运行下列命令来加密二进制文件: - espsecure.py encrypt_flash_data --aes_xts --keyfile my_flash_encryption_key.bin --address 0x0 --output bootloader-enc.bin build/bootloader/bootloader.bin espsecure.py encrypt_flash_data --aes_xts --keyfile my_flash_encryption_key.bin --address 0x8000 --output partition-table-enc.bin build/partition_table/partition-table.bin espsecure.py encrypt_flash_data --aes_xts --keyfile my_flash_encryption_key.bin --address 0x10000 --output my-app-enc.bin build/my-app.bin - 上述命令中的偏移量仅适用于示例固件,请通过检查分区表条目或运行 idf.py partition-table 来获取你固件的实际偏移量。请注意,不需要加密所有二进制文件,只需加密在分区表定义文件中带有 - encrypted标记的文件,其他二进制文件只作为构建过程的普通输出进行烧录。- 使用 - esptool.py可以将上述文件烧写到各自的偏移地址。要查看所有推荐的- esptool.py命令行选项,请查阅- idf.py build构建成功后打印的输出。- 若应用程序包含分区 - otadata和- nvs_encryption_keys,则该分区也需加密。详情请参阅 加密分区。- 备注 - 如果 ESP32-C2 启动时无法识别烧录的密文,请检查密钥是否匹配、命令行参数是否精确匹配及偏移量的正确性。偏移量必须正确,因为当偏移量改变时,密文也会改变。 - 使用 - espsecure.py decrypt_flash_data命令时,可以用相同的选项(和不同的输入或输出文件)来解密密文 flash 或之前加密的文件。
- 确保 ROM 下载模式安全 - 警告 - 请在最后烧录以下位。烧录后,espefuse 工具将无法再用于烧录其他 eFuse。 - 启用安全下载模式: - ENABLE_SECURITY_DOWNLOAD:启用安全 ROM 下载模式
 - 运行以下指令,烧录 eFuse: - espefuse.py --port PORT burn_efuse ENABLE_SECURITY_DOWNLOAD 
重要
- 从主机上删除 flash 加密密钥 - 一旦为设备启用了 flash 加密,密钥 必须立即删除。这能确保主机以后不为同一设备生成加密二进制文件,从而减少 flash 加密密钥漏洞。 
flash 加密指南
- 建议为每个设备生成唯一的 flash 加密密钥用于生产用例。 
- 确保主机用于生成 flash 加密密钥的 RNG 具有良好的熵。 
- 更多详细信息请参阅 flash 加密的局限性。 
外部启用 Secure Boot v2
在此工作流中,我们会使用 espsecure 工具生成签名密钥,并使用 espefuse 工具烧录相关 eFuse。关于 Secure Boot v2 流程的详细信息,请参阅 安全启动 (Secure Boot) v2。
- 生成 Secure Boot v2 签名私钥 - 运行以下命令可以生成 ECDSA 方案的 Secure Boot v2 签名密钥: - bashespsecure.py generate_signing_key --version 2 --scheme ecdsa256 secure_boot_signing_key.pem - 将上述命令中的方案更改为 - ecdsa192,可生成 ecdsa192 私钥。
- 生成公钥摘要 - 运行以下命令可以为上一步生成的私钥生成公钥摘要: - espsecure.py digest_sbv2_public_key --keyfile secure_boot_signing_key.pem --output digest.bin 
- 在 eFuse 中烧录密钥摘要 - 运行以下命令可以在 eFuse 中烧录公钥摘要: - espefuse.py --port PORT --chip esp32c2 burn_key KEY_BLOCK0 digest.bin SECURE_BOOT_DIGEST 
- 启用 Secure Boot v2 - 运行以下命令启用 Secure Boot v2 eFuse: - espefuse.py --port PORT --chip esp32c2 burn_efuse SECURE_BOOT_EN 
- 烧录相关 eFuse - 烧录安全 eFuse 
 - 重要 - 对于生产用例,强烈建议烧录下列所有 eFuse。 - DIS_DIRECT_BOOT: 禁用直接引导(旧版 SPI 引导模式)。
- DIS_PAD_JTAG:永久禁用 JTAG。
 - 运行以下命令烧录相应的 eFuse: - espefuse.py burn_efuse --port PORT EFUSE_NAME 0x1 - 备注 - 请将 EFUSE_NAME 更新为需烧录的 eFuse。在上述命令中添加多个 eFuse 可以同时烧录(例如:EFUSE_NAME VAL EFUSE_NAME2 VAL2)。有关 espefuse.py 的更多信息,请参阅 此文档。 - 与 Secure Boot v2 相关的 eFuse 
 - 禁用读保护选项: 
 - 在 eFuse 中烧录的 Secure Boot 摘要必须保持可读,否则会导致安全启动失败。烧录以下 eFuse 可防止意外启用此密钥块的读保护: - espefuse.py -p $ESPPORT write_protect_efuse RD_DIS - 重要 - 烧录此 eFuse 后,不能为任何密钥启用读保护。例如,如果此时需要对密钥进行读保护的 flash 加密尚未启用,则之后也无法启用。请确保在此之后没有其他 efuse 密钥需要读保护。 
- 构建二进制文件 - 默认情况下,一级 (ROM) 引导加载程序只会验证 二级引导加载程序。只有在启用 CONFIG_SECURE_BOOT 选项(并将 CONFIG_SECURE_BOOT_VERSION 设置为 - SECURE_BOOT_V2_ENABLED)时,二级引导加载程序才会在构建引导加载程序时验证应用程序分区。- 打开 Editing the Configuration,在 - Security features中设置- Enable hardware Secure Boot in bootloader启用 Secure Boot。
 - 选中 - Secure Boot v2选项,- App Signing Scheme将被默认设置为 ECDSA (V2)。- 在 Editing the Configuration 中为项目禁用 CONFIG_SECURE_BOOT_BUILD_SIGNED_BINARIES 选项,以确保所有生成的二进制文件都受到安全保护且未签名,避免生成签名的二进制文件,因为需要使用 - espsecure工具手动签名二进制文件。
 
- 构建、签名并烧录二进制文件 - 完成上述配置后,可以用 - idf.py build命令构建引导加载程序和应用程序二进制文件。- Secure Boot v2 工作流程只验证 - bootloader和- application二进制文件,因此只需要对这些二进制文件进行签名。其他二进制文件(例如- partition-table.bin)可以在构建后直接进行烧录。- 运行以下命令对 - bootloader.bin和- app.bin二进制文件进行签名:- espsecure.py sign_data --version 2 --keyfile secure_boot_signing_key.pem --output bootloader-signed.bin build/bootloader/bootloader.bin espsecure.py sign_data --version 2 --keyfile secure_boot_signing_key.pem --output my-app-signed.bin build/my-app.bin - 运行以下命令来检查附加到二进制文件的签名: - espsecure.py signature_info_v2 bootloader-signed.bin - 然后使用 - esptool.py将上述文件和其他二进制文件(如分区表)烧录到各自的偏移地址。要查看所有推荐的- esptool.py命令行选项,请参阅- idf.py build的输出结果。要获得固件的 flash 偏移地址,可查找分区表条目或运行- idf.py partition-table查看。
- 确保 ROM 下载模式安全 - 警告 - 请在最后烧录以下位。烧录后,espefuse 工具将无法再用于烧录其他 eFuse。 - 启用安全下载模式: - ENABLE_SECURITY_DOWNLOAD:启用安全 ROM 下载模式
 - 运行以下指令,烧录 eFuse: - espefuse.py --port PORT burn_efuse ENABLE_SECURITY_DOWNLOAD 
Secure Boot v2 指南
- 建议将 Secure Boot 密钥存储在高度安全的地方,如可以使用物理或云 HSM 来存储 Secure Boot 私钥。请参阅 远程镜像签名 获取更多详细信息。 
启用外部 NVS 加密
有关 NVS 加密及相关方案的详细信息,请参阅 NVS 加密。
基于 flash 加密启用 NVS 加密
在这种情况下,主机上生成 NVS 加密密钥,并将其烧录到芯片上,借助 flash 加密 功能进行保护。
- 生成 NVS 加密密钥 - 使用 NVS 分区生成工具,可以生成相应的密钥。在主机上生成加密密钥,并将该密钥以加密状态存储在 ESP32-C2 的 flash 中。 - 使用以下命令,通过 nvs_flash/nvs_partition_generator/nvs_partition_gen.py 脚本生成密钥: - python3 nvs_partition_gen.py generate-key --keyfile nvs_encr_key.bin - keys文件夹中将生成相应的密钥。
- 生成加密的 NVS 分区 - 在主机上生成实际的加密 NVS 分区,详情请参阅 生成 NVS 加密分区。为此,CSV 文件应包含 NVS 文件数据,详情请参阅 CSV 文件格式。 - 使用以下命令,可以生成加密的 NVS 分区: - python3 nvs_partition_gen.py encrypt sample_singlepage_blob.csv nvs_encr_partition.bin 0x3000 --inputkey keys/nvs_encr_key.bin - 下文解释了上述命令中的一些参数: - CSV 文件名 - 上述命名中的 sample_singlepage_blob.csv 是指包含 NVS 数据的 CSV 文件,请将其替换为所选文件。 
- NVS 分区偏移量 - 这是 NVS 分区在 ESP32-C2 的 flash 中存储时的偏移地址。在项目目录中执行 - idf.py partition-table命令,可以找到 NVS 分区的偏移量。请将上述命令中的示例值- 0x3000替换为正确的偏移量。
 
- 配置项目 - 通过启用 CONFIG_NVS_ENCRYPTION 来启用 NVS 加密。 
- 通过将 CONFIG_NVS_SEC_KEY_PROTECTION_SCHEME 设置为 - CONFIG_NVS_SEC_KEY_PROTECT_USING_FLASH_ENC,配置 NVS 使用基于 flash 加密的方案。
 
- 烧录 NVS 分区和 NVS 加密密钥 - 使用 - esptool.py命令,将 NVS 分区 (- nvs_encr_partition.bin) 和 NVS 加密密钥 (- nvs_encr_key.bin) 烧录到各自的偏移地址。通过- idf.py build成功后打印的输出,可查看所有推荐的- esptool.py命令行选项。- 若芯片启用了 flash 加密,请在烧录之前先加密分区。详情请参阅 flash 加密工作流程 中与烧录相关的步骤。