ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

ESP-IDF 中 Flash 加密的完整实践:eFuse 烧录、Kconfig 配置与加密读写测试

2026/9/14 18:53:16 拓冰建站 浏览量
ESP-IDF 中 Flash 加密的完整实践:eFuse 烧录、Kconfig 配置与加密读写测试 ESP-IDF 中 Flash 加密的完整实践eFuse 烧录、Kconfig 配置与加密读写测试【免费下载链接】esp-idfEspressif IoT Development Framework. Official development framework for Espressif SoCs.项目地址: https://gitcode.com/GitHub_Trending/es/esp-idf本文基于 ESP-IDF 仓库中的 Flash 加密测试应用components/spi_flash/test_apps/flash_encryption展开讲解 Flash 加密功能的启用前提、eFuse 密钥烧录流程、关键 Kconfig 配置项以及esp_flash_write_encrypted/esp_flash_read_encrypted的 16 字节对齐约束等实现细节。读完之后你可以独立完成一台开发板的 Flash 加密使能含 eFuse 烧录并复现仓库中完整的加密读写验证用例。支持的目标芯片Flash 加密测试应用覆盖 ESP-IDF 中所有具备硬件 Flash 加密能力的 Xtensa/RISC-V 目标芯片Supported TargetsESP32ESP32-C2ESP32-C3ESP32-C5ESP32-C6ESP32-C61ESP32-H2ESP32-H21ESP32-H4ESP32-P4ESP32-S2ESP32-S3ESP32-S31Flash 加密由 SoC 的加密引擎完成加密密钥存放在不可擦除的 eFuse 中因此该功能是一次性的——这正是本文后续Prepare runner步骤需要格外谨慎的原因。测试应用的组成测试项目位于 flash_encryption是一个裁剪后的最小工程其 CMakeLists.txt 通过set(COMPONENTS main esp_psram esptool_py)只引入main、esp_psram和esptool_py三个组件项目名为test_flash_encryption并额外挂载了tools/test_apps/components和components/spi_flash/test_apps/components两个公共测试组件目录提供test_utils、ccomp_timer等工具。partitions.csv 定义了测试分区布局其中flash_testdata/fat 类型、528K是加密读写测试的数据区起始地址由测试代码运行时通过esp_partition_find_first动态获取# Name, Type, SubType, Offset, Size, Flags nvs, data, nvs, , 0x6000, factory, 0, 0, , 1M flash_test, data, fat, , 528K测试入口 test_app_main.c 用 Unity 框架跑用例并在每个用例前后记录 8BIT/32BIT 堆的剩余量做内存泄漏检查——代码注释解释了阈值的来源Flash 加密路径中存在一些延迟分配lazy allocated的资源因此阈值TEST_MEMORY_LEAK_THRESHOLD放宽为 -400 字节。配置sdkconfig 关键项sdkconfig.defaults 给出了使能 Flash 加密的完整配置组合是理解该功能最直接的样本配置项作用CONFIG_SECURE_FLASH_ENC_ENABLEDy使能 Flash 加密总开关CONFIG_SECURE_FLASH_ENCRYPTION_MODE_DEVELOPMENTy开发模式。密钥来自 eFuse 中烧录的 BLOCK_KEY2适合调试CONFIG_SECURE_FLASH_REQUIRE_ALREADY_ENABLEDy要求芯片上 eFuse 已实际烧录加密位否则启动时直接报错退出防止在未加密但以为已加密的板子上静默运行CONFIG_SECURE_FLASH_UART_BOOTLOADER_ALLOW_ENCy/..._ALLOW_DECy/..._ALLOW_CACHEy允许 UART 下载esptool在加密状态下写入/读出密文并允许在 eFuse 未烧录加密位时用 UART bootloader 模拟加密/解密流程CONFIG_SECURE_BOOT_ALLOW_ROM_BASICy/CONFIG_SECURE_BOOT_ALLOW_JTAGy放开对 ROM basic 启动和 JTAG 的限制CONFIG_SPI_FLASH_DANGEROUS_WRITE_FAILSy默认禁止擦除/写入 bootloader、分区表等危险区域见后文测试用例从 test_flash_encryption.c 头部的注释表还能看到官方对各芯片配置组合的归纳ESP32 只需CONFIG_SECURE_FLASH_ENC_ENABLED而 ESP32-S2/C3 还需要EFUSE_VIRTUAL不启用、并设置CONFIG_SECURE_FLASH_REQUIRE_ALREADY_ENABLED。整个测试文件被#ifdef CONFIG_SECURE_FLASH_ENC_ENABLED包裹即只有开启加密配置时这些用例才会编译。Prepare runner烧录 eFuse 使能加密README 中给出的准备步骤是运行 encrypt_flash.sh注意该操作会烧写 eFuse不可逆。脚本内容非常短值得逐行理解#!/bin/bash set -e if [ -z $ESPPORT ]; then echo ESPPORT must be set exit 1 fi dd if/dev/zero ofkey.bin bs1 count32 # Change the first byte as espsecure uses modules that wont # allow symmetric keys echo -ne \\xFF | dd convnotrunc bs1 count1 ofkey.bin espefuse --do-not-confirm -p $ESPPORT burn-efuse SPI_BOOT_CRYPT_CNT 0x1 espefuse --do-not-confirm -p $ESPPORT burn-key BLOCK_KEY2 key.bin XTS_AES_128_KEY其执行逻辑为要求设置ESPPORT环境变量串口设备路径否则退出生成 32 字节密钥文件key.bin先用dd生成全零再把首字节改为0xFF——注释说明了原因espsecure使用的密钥模块不允许纯对称密钥的零键之类弱键首字节置0xFF是为了绕开该限制此处为开发用随机弱键量产应使用真正的随机密钥烧录SPI_BOOT_CRYPT_CNT0x1置位该 eFuse 字段后芯片启动即要求 Flash 加密硬件进入必须解密读 Flash的状态烧录BLOCK_KEY2以XTS_AES_128_KEY类型把 32 字节密钥写入 BLOCK_KEY2这就是 Flash 加密实际使用的 XTS-AES-128 块密钥。从源码结构看这与 Kconfig 中的两条路径是配套的CONFIG_SECURE_FLASH_ENCRYPTION_MODE_DEVELOPMENT开发模式密钥即上述手动烧录到 BLOCK_KEY2 的值密钥可见于开发者便于调试量产模式则使用espsecure从 Flash 加密密钥FEK派生块密钥且要求安全启动公钥已烧录——测试应用走的是开发模式因此只需espefuse手动烧录。由于CONFIG_SECURE_FLASH_REQUIRE_ALREADY_ENABLEDy在 eFuse 尚未烧录的裸板上应用启动时就会检测到配置要求加密但硬件未加密而拒绝运行这正好倒逼开发者先完成本节的 Prepare runner 步骤。加密读写 API 的行为约束test_flash_encryption.c 是一组[flash_encryption]标签的 Unity 用例集中体现了 Flash 加密 API 的语义可以当作API 行为规格书阅读16 字节对齐是硬性约束XTS 加密以 16 字节块为单位加密写要求偏移和长度都对齐 16 字节。用例 test 16 byte encrypted writes 显式验证了两类非法调用/* 偏移未对齐 16 字节 → ESP_ERR_INVALID_ARG */ esp_flash_write_encrypted(NULL, start 1, fortyeight_bytes, 32); /* 长度非 16 字节倍数 → ESP_ERR_INVALID_SIZE */ esp_flash_write_encrypted(NULL, start, fortyeight_bytes, 15);而读取接口esp_flash_read_encrypted允许非对齐的起始/长度用例中在start0x10读 16 字节做交叉验证。部分块写入不会溢出加密test 16 byte encrypted writes 还验证了部分块partial block的边界行为向start0x30写 16 字节后其前后相邻的 16 字节区域仍是擦除态0xFFverify_erased_flash逐字节断言同理48 字节写入长度不是 32 的整数倍只影响其覆盖的 16 字节块。这说明实现对不足一个块的尾部不会越界写入相邻数据。随机数据的整扇区读写回环test read write random encrypted data 在一个 4K 扇区内循环执行以随机 16 字节步进、随机 16192 字节长度写入rand()数据最后再随机分块整扇区读回并逐字节比对覆盖了非对齐缓冲区 非对齐读的组合场景。大缓冲区与性能另有两个用例使用 16432 字节n*3216形态的large_const_buffer一个放在 Flash 映射区const一个用DRAM_ATTR固定到 RAM用于验证两种源地址下的加密写入并通过ccomp_timer打印us/KB的写入速度IDF_LOG_PERFORMANCE。对 Flash 物理大小大于 16MB 的芯片用例还会追加一轮对0x1030000偏移的测试覆盖大容量 Flash 的加密区。危险区域保护与越界检查在CONFIG_SPI_FLASH_DANGEROUS_WRITE_FAILSy下用例验证加密写/擦除到 bootloaderCONFIG_BOOTLOADER_OFFSET_IN_FLASH和分区表CONFIG_PARTITION_TABLE_OFFSET区域都会被拒绝返回ESP_ERR_INVALID_ARG在CONFIG_SPI_FLASH_DANGEROUS_WRITE_ALLOWEDy下则切换为另一组越界边界用例验证擦除/写入超过 Flash 末尾含UINT32_MAX溢出构造一律报参数错误——CI 的rom_impl与verify配置特意打开了DANGEROUS_WRITE_ALLOWED配置文件中注明Unrelated to rom/verify, but to test if the boundary checking works well说明边界检查代码路径在 ROM 实现和验证模式下同样受检。CI 配置矩阵与运行方式pytest_flash_encrypted.py 定义了 CI 参数矩阵配合 4 个 CI 配置文件运行pytest 函数目标芯片配置config对应文件test_flash_encryptionesp32 / esp32c3release、verifysdkconfig.ci.release、sdkconfig.ci.verifytest_flash_encryption_rom_implesp32c3rom_implsdkconfig.ci.rom_impltest_flash_encryption_f4r8esp32s3release_f4r8、rom_impl、verifysdkconfig.ci.release_f4r8test_flash_encryption_f8r8esp32s3release_f8r8sdkconfig.ci.release_f8r8各配置文件的差异点release/verify/f4r8/f8r8在 defaults 基础上启用FREERTOS_USE_TICKLESS_IDLE、COMPILER_OPTIMIZATION_SIZE验证 tickless 空闲与体积优化编译下加密路径仍正常f4r8额外打开CONFIG_SPIRAM_MODE_OCTSPIRAM_TYPE_AUTOOctal 80/80 PSRAMf8r8进一步要求CONFIG_ESPTOOLPY_OCT_FLASH、FLASHMODE_OPI、32MB Flash用于覆盖 S3 的 80/80/80 高速配置verify额外开启CONFIG_SPI_FLASH_VERIFY_WRITEy、CONFIG_SPI_FLASH_LOG_FAILED_WRITEy、CONFIG_SPI_FLASH_WARN_SETTING_ZERO_TO_ONEy把 Flash 驱动自带的写后回读校验verify write与加密写叠加rom_impl只设置CONFIG_SPI_FLASH_ROM_IMPLyFlash 操作使用 ROM 中的实现与SPI_FLASH_DANGEROUS_WRITE_ALLOWEDy并关闭自定义分区表——验证 ROM 实现路径下的边界检查。所有用例均为dut.run_all_single_board_cases()即按标准 pytest-embedded-idf 流程单板运行由于测试依赖 eFuse 烧录本地执行前必须先完成上一节的ESPPORT... ./encrypt_flash.sh准备步骤。小结与注意事项Flash 加密测试应用是配置 eFuse 烧录 用例三件套sdkconfig.defaults 给出 Kconfig 组合encrypt_flash.sh 完成 BLOCK_KEY2 与 SPI_BOOT_CRYPT_CNT 的不可逆烧录test_flash_encryption.c 验证 API 语义eFuse 烧录不可撤销且SPI_BOOT_CRYPT_CNT一旦置位Flash 中已有的明文数据含 bootloader 之外的旧分区将无法被正确解读务必在全新板子或允许报废的板子上操作加密写的 16 字节对齐要求ESP_ERR_INVALID_ARG/ESP_ERR_INVALID_SIZE是硬件块粒度决定的应用层封装加密数据区时应自行保证对齐若需理解各芯片配置差异如 S2/C3 需要REQUIRE_ALREADY_ENABLED参考 test_flash_encryption.c 头部注释。【免费下载链接】esp-idfEspressif IoT Development Framework. Official development framework for Espressif SoCs.项目地址: https://gitcode.com/GitHub_Trending/es/esp-idf创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考