ESP32-C3 手动启用 Secure Boot V2 与 Flash 加密流程

ESP-IDF 中 flash 加密可以在 bootloader 阶段自动启用,但是这需要设备自加密后重启一次,为了节省这次重启的步骤,你可以选择通过一些脚本工具在外部启用 flash 加密。

本篇文档用于介绍 ESP32-C3 手动启用 Secure Boot V2Flash 加密的操作流程。其中,Flash 加密使用发布模式(Release Mode),使用主机生成的密钥对数据进行加密。Secure Boot 方案所需的公钥的 SHA-256 哈希摘要和和 Flash 加密方案需要的 Flash 加密秘钥均为外部生成(bootloader 阶段自动启用的方式为设备内自动计算并写入相关的 efuse)。其他相关的 efuse 也通过脚本工具进行烧录,更多信息请参考乐鑫官方文档 基于主机的安全工作流程。本篇文档可以看做官方文档的一次实践,仅做参考。

1 测试环境

2 通过 menuconfig 使能

2.1 调整分区表偏移量

使能 Flash 加密以及 Secure Boot V2 后,生成的 bootloader bin 会变大,可以通过调整分区表偏移量增大 bootloader 分区大小,本例中将分区表偏移量从默认的 0x8000 调整到 0xF000,这样 bootloader 分区的大小为 0xF000 - 0x1000 = 0xE000 (0x0 为 bootloader bin 烧录地址),可以参考引导加载程序大小
在这里插入图片描述

需要注意的是,开启 Flash 加密会默认使能 NVS 加密,需要在分区表中定义 NVS Key 分区,否则运行会出现错误。参考 NVS Key Partition。如果不需要 NVS 加密,可以通过 menuconfig 禁用该功能,配置路径: (Top) -> Component config -> NVS -> [ ] Enable NVS encryption,这样无需在分区表中定义 NVS Key 分区。

2.2 安全特征配置

在这里插入图片描述
Flash 加密 release 模式默认的 UART ROM download modePermanently switch to Secure mode (recommended),在开发过程中,为了避出现意外无法使设备恢复正常,暂且使能 UART ROM 下载模式,后面可以通过烧录 ENABLE_SECURITY_DOWNLOAD 使用安全下载模式。

3 测试流程

3.1 生成 Secure Boot V2 签名秘钥并烧录到对应 efuse

  • 可使用如下指令生成签名秘钥:
python $IDF_PATH/components/esptool_py/esptool/espsecure.py generate_signing_key --version 2 secure_boot_signing_key.pem
  • 生成 secure_boot_digest.bin,该文件为 secure boot 公钥的 SHA-256 摘要:
python $IDF_PATH/components/esptool_py/esptool/espsecure.py digest_sbv2_public_key --keyfile secure_boot_signing_key.pem --output secure_boot_digest.bin
  • 烧录 secure_boot_digest.bin
python $IDF_PATH/components/esptool_py/esptool/espefuse.py burn_key BLOCK_KEY0 secure_boot_digest.bin SECURE_BOOT_DIGEST0
  • 烧录 SECURE_BOOT_EN
python $IDF_PATH/components/esptool_py/esptool/espefuse.py burn_efuse SECURE_BOOT_EN 1

3.2 Flash 加密密钥生成与烧录

python $IDF_PATH/components/esptool_py/esptool/espsecure.py generate_flash_encryption_key my_flash_encryption_key.bin
python $IDF_PATH/components/esptool_py/esptool/espefuse.py burn_key BLOCK_KEY1 my_flash_encryption_key.bin XTS_AES_128_KEY
  • Flash 加密相关安全 eFuses:
    • DIS_DOWNLOAD_ICACHE: Disable UART cache
    • DIS_DIRECT_BOOT: Disable direct boot (legacy SPI boot mode)
    • DIS_USB_JTAG: Disable USB switch to JTAG
    • DIS_PAD_JTAG: Disable JTAG permanently
    • DIS_DOWNLOAD_MANUAL_ENCRYPT: Disable UART bootloader encryption access
      此处先不烧录 DIS_DOWNLOAD_MANUAL_ENCRYPT,如果不禁用这个 efuse,那么仍可通过 idf.py encrypted-app-flash/idf.py encrypted-flash (参考 Re-flashing Updated Partitions)或者 esptool.py write_flash --encrypt 这两种方式边写边加密明文固件。
python $IDF_PATH/components/esptool_py/esptool/espefuse.py burn_efuse DIS_DOWNLOAD_ICACHE 1 DIS_PAD_JTAG 1 DIS_USB_JTAG 1 DIS_DIRECT_BOOT 1 
  • Flash 加密模式下 SPI_BOOT_CRYPT_CNT 需要设置为 7,此时 Flash 加密无法关闭。
python $IDF_PATH/components/esptool_py/esptool/espefuse.py burn_efuse SPI_BOOT_CRYPT_CNT 7

3.3 编译 bootloader

idf.py bootloader

3.4 编译 app/partition bin

idf.py build
Project build complete. To flash, run this command:
/home/mali/.espressif/python_env/idf5.0_py3.8_env/bin/python ../../../../components/esptool_py/esptool/esptool.py -p (PORT) -b 460800 --before default_reset --after no_reset --chip esp32c3 --no-stub write_flash --flash_mode dio --flash_size keep --flash_freq 80m 0xf000 build/partition_table/partition-table.bin 0x14000 build/ota_data_initial.bin 0x20000 build/simple_ota.bin
or run 'idf.py -p (PORT) flash'

由于使能了 Sign binaries during build, 会在构建过程中对 bootloaderapp bin 进行签名,此时,生成的 bootloader.binapp.bin 为签过名的明文固件。

  • 获取分区表信息:
$ idf.py partition-table
Executing action: partition-table
Running ninja in directory /home/mali/esp/esp32/rel_50/examples/system/ota/simple_ota_example/build
Executing "ninja partition-table"...
[1/2] cd /home/mali/esp/esp32/rel_50/examples/system/ota/simple_ota_example/build/esp-idf/partition_table && /home/mali/.e...sif/tools/cmake/3.24.0/bin/cmake -E echo "*******************************************************************************"Partition table binary generated. Contents:
*******************************************************************************
# ESP-IDF Partition Table
# Name, Type, SubType, Offset, Size, Flags
nvs,data,nvs,0x10000,16K,
otadata,data,ota,0x14000,8K,
phy_init,data,phy,0x16000,4K,
ota_0,app,ota_0,0x20000,1536K,
ota_1,app,ota_1,0x1a0000,1536K,
nvs_key,data,nvs_keys,0x320000,4K,encrypted
*******************************************************************************
[2/2] Running utility command for partition-tablePartition table built:

Partition Table build complete. To flash, run this command:
/home/mali/.espressif/python_env/idf5.0_py3.8_env/bin/python ../../../../components/esptool_py/esptool/esptool.py -p (PORT) -b 460800 --before default_reset --after no_reset --chip esp32c3 --no-stub write_flash 0xf000 build/partition_table/partition-table.bin
or run 'idf.py -p (PORT) partition-table-flash'

3.5 使用 Flash 加密密钥对签名后的 bin 文件进行加密

使用 Flash 加密密钥对 bin 文件加密时,需要指定 bin 文件的地址,这个地址可从 idf.py build 结束后输出的烧录指令中获取,在本例中,共有四个 bin 文件需要被加密。

0x0 build/bootloader/bootloader.bin (ESP32-C3 的 bootloader bin 的烧录地址是一个确定值,为 0x0)
0xf000 build/partition_table/partition-table.bin
0x14000 build/ota_data_initial.bin 
0x20000 build/simple_ota.bin
python $IDF_PATH/components/esptool_py/esptool/espsecure.py encrypt_flash_data --aes_xts --keyfile my_flash_encryption_key.bin --address 0x20000 --output simple_ota_enc.bin build/simple_ota.bin
python $IDF_PATH/components/esptool_py/esptool/espsecure.py encrypt_flash_data --aes_xts --keyfile my_flash_encryption_key.bin --address 0xf000 --output partition-table_enc.bin build/partition_table/partition-table.bin
python $IDF_PATH/components/esptool_py/esptool/espsecure.py encrypt_flash_data --aes_xts --keyfile my_flash_encryption_key.bin --address 0x0 --output bootloader_enc.bin build/bootloader/bootloader.bin
python $IDF_PATH/components/esptool_py/esptool/espsecure.py encrypt_flash_data --aes_xts --keyfile my_flash_encryption_key.bin --address 0x14000 --output ota_data_initial_enc.bin build/ota_data_initial.bin

3.6 烧录加密后的 bin 文件

  • 烧录前建议擦除 Flash 一次。
python $IDF_PATH/components/esptool_py/esptool/esptool.py -b 921600 --before default_reset --after no_reset --chip esp32c3 --no-stub write_flash --force --flash_mode dio --flash_size keep --flash_freq 80m 0x0 bootloader_enc.bin 0xf000 partition-table_enc.bin 0x14000 ota_data_initial_enc.bin 0x20000 simple_ota_enc.bin
  • 烧录完成后,检查设备是否可以正常启动。如果可以正常启动,从 log 中应该可以看到类似下面的日志:
E (1171) flash_encrypt: Flash encryption settings error: app is configured for RELEASE but efuses are set for DEVELOPMENT
E (1181) flash_encrypt: Mismatch found in security options in bootloader menuconfig and efuse settings. Device is not secure.

flash_encrypt 报错的的原因是当前烧录的 efuse 和 Flash 加密 release 模式不匹配,这是因为前面没有烧录 DIS_DOWNLOAD_MANUAL_ENCRYPT,release 模式下需要保证这个 efuse 烧录,此时,无法再可通过 idf.py encrypted-app-flash/idf.py encrypted-flash (参考 Re-flashing Updated Partitions)或者 esptool.py write_flash --encrypt 这两种方式边写边加密明文固件,需要先使用 Flash 加密秘钥对明文固件进行加密,然后直接烧录密文固件。

espefuse.py burn_efuse DIS_DOWNLOAD_MANUAL_ENCRYPT
  • 前面在 menuconfig 中使能 UART ROM 下载模式,Flash release 模式建议使用安全下载模式,该模式下,仍可烧录固件但是无法读取固件。
espefuse.py burn_efuse ENABLE_SECURITY_DOWNLOAD

4 补充内容

UART ROM 三种下载模式的比较

  1. 下面是对于 UART ROM 三种下载模式的大致比较,更多信息请见 UART ROM download mode
  • SECURE_DISABLE_ROM_DL_MODE:禁用下载模式
  • SECURE_ENABLE_SECURE_ROM_DL_MODE:安全下载模式
  • SECURE_INSECURE_ALLOW_DL_MODE:使能下载模式
禁用下载模式安全下载模式使能下载模式
是否可以下载固件
是否可以读取固件是(esptool.py --no-stub read_flash
是否可以获取 efuse 信息是(部分esptool.py --no-stub get_security_info是(全部espefuse.py summary
通过烧录 efuse 使能espefuse.py burn_efuse DIS_DOWNLOAD_MODEespefuse.py burn_efuse ENABLE_SECURITY_DOWNLOAD无需烧录 efuse
  1. 如果要对已经使能 Flash 加密和 secure boot v2 的设备进行 OTA 升级,那么 ota bin 文件需要先进行签名再放到服务器上,对于通用的 ota 例程,放在服务器上的 ota bin 应为签名后的明文固件,在写入到 ota 分区时会自动进行加密。
espsecure.py sign_data --version 2 --keyfile PRIVATE_SIGNING_KEY --output SIGNED_BINARY_FILE BINARY_FILE
  • 此处的 PRIVATE_SIGNING_KEY 为前面生成的 Secure Boot 签名密钥 secure_boot_signing_key.pem,更多信息可见 Signing using espsecure.py

  • 如果担心服务器上的明文 ota.bin 被他人窃取(例如: non-TLS).,可以对 ota.bin 进行预加密,参考例程 pre_encrypted_ota

5 参考链接

  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值