前言
一个项目完成之后,为了防止二次烧录或固件盗版,通常会进行程序加密,就好像STM32的flash写都保护、唯一ID程序加密类似。
乐鑫给我们提供了flash加密的方案,flash 加密功能用于加密与 ESP32 搭载使用的片外 flash 中的内容。启用 flash 加密功能后,固件会以明文形式烧录,然后在首次启动时将数据进行加密。因此,物理读取 flash 将无法恢复大部分 flash 内容。
一、注意
请熟读这篇文章再尝试去做加密实验,因为加密是一次性的,可能会导致因为加密使模组变 “软砖”,加密执行之后,将无法使用 flash_download_tool 工具进行固件烧录,你会得到这样的提示:
二、flash加密过程
假设 eFuse 值处于默认状态,且固件的引导加载程序编译为支持 flash 加密,则 flash 加密的具体过程如下:
-
第一次开机复位时,flash 中的所有数据都是未加密的(明文)。ROM 引导加载程序加载固件引导加载程序。
-
固件的引导加载程序将读取 FLASH_CRYPT_CNT eFuse 值(0b0000000)。因为该值为 0(偶数位),固件的引导加载程序将配置并启用 flash 加密块,同时将 FLASH_CRYPT_CONFIG eFuse 的值编程为 0xF。
-
固件的引导加载程序使用 RNG(随机数生成)模块生成 AES-256 位密钥,然后将其写入 flash_encryption eFuse 中。由于 flash_encryption eFuse 已设置编写和读取保护位,将无法通过软件访问密钥。Flash 加密操作完全在硬件中完成,无法通过软件访问密钥。
-
Flash 加密块将加密 flash 的内容(固件的引导加载程序、应用程序、以及标有“加密”标志的分区)。就地加密可能会耗些时间(对于大分区最多需要一分钟)。
-
固件引导加载程序将在 FLASH_CRYPT_CNT (0b0000001) 中设置第一个可用位来对已加密的 flash 内容进行标记。设置奇数个比特位。
-
对于 开发模式,固件引导加载程序仅设置 DISABLE_DL_DECRYPT 和 DISABLE_DL_CACHE 的 eFuse 位,以便 UART 引导加载程序重新烧录加密的二进制文件。此外, FLASH_CRYPT_CNT 的 eFuse 位不受写入保护。
-
对于 发布模式,固件引导加载程序设置 DISABLE_DL_ENCRYPT、DISABLE_DL_DECRYPT 和 DISABLE_DL_CACHE 的 eFuse 位为 1,以防止 UART 引导加载程序解密 flash 内容。它还写保护 FLASH_CRYPT_CNT eFuse 位。要修改此行为,请参阅 启用 UART 引导加载程序加密/解密。
-
重新启动设备以开始执行加密镜像。固件引导加载程序调用 flash 解密块来解密 flash 内容,然后将解密的内容加载到 IRAM 中。
简而言之就是:加密的程序启动之后,首先会由程序引导进行加密。加密需要时间,加密成功之后,会把FLASH_CRYPT_CNT位从0x0变为1或0xf,最后才开始执行用户程序。
三、加密模式
Flash加密分两种模式,一个是开发模式、另一个则是生产模式。
3.1 开发模式加密
既然是加密,那肯定需要密钥,乐鑫也给我们提供了两种密钥的加密或生成方法:
3.1.1 使用ESP32生成的密钥加密
使用这个加密方法的话,密钥是唯一的,而且不可见。密钥不会被保存到系统文件当中,只会随机生成并烧录到efuse分区。
注意:
在做加密实验之前,且确保模组没有做任何的加密,加密状态的查询指令为,(PORT为串口的端口号):
espefuse.py -p PORT summary
本文将以hello_world例程作为例子进行加密,在hello_world目录下运行:
idf.py menuconfig
执行以下操作:
- 启动时使能 flash 加密
- 选择发布模式 (注意一旦选择了发布模式,DISABLE_DL_ENCRYPT 和 DISABLE_DL_DECRYPT eFuse 位将被编程为在 ROM 下载模式下禁用 flash 加密硬件)
- 选择 UART ROM 下载模式(推荐永久性禁用) (注意该选项仅在 CONFIG_ESP32_REV_MIN 级别设置为 3 时 (ESP32 V3) 可用。)默认选项是保持启用 UART ROM 下载模式,然而建议永久禁用该模式,以减少攻击者可用的选项。
- 选择适当详细程度的引导加载程序日志
- 保存配置并退出
然后直接运行:
idf.py flash monitor
加密成功截图:
然后我们可以看一下:FLASH_CRYPT_CNT
可以看到,FLASH_CRYPT_CNT的值被置为 1;就表示已经成功加密。
3.1.2 使用自主生成的密钥进行加密
使用自主生成的密钥其实就是多了烧录密钥这一步,密钥的生成指令:
espsecure.py generate_flash_encryption_key my_flash_encryption_key.bin
我建议把密钥保存到单独的文件当中,防止误删。生成密钥之后,使用以下指令烧录密钥:
espefuse.py --port PORT burn_key flash_encryption my_flash_encryption_key.bin
下一步,就可以根据3.1.1中的配置烧录
3.2 生产模式加密
使用开发模式,依旧是可以使用idf.py脚本进行烧录,只不过有次数限制,但是可以关闭加密:
espefuse.py burn_efuse FLASH_CRYPT_CNT
但是生产模式的加密烧录是没有重复烧录机会的,通常在量产时才会用,因为这是一次性的,烧录之后只能通过OTA来进行程序升级。它的配置如下:
直接使用:
idf.py flash monitor
为此,博主的模组就成了“软砖”:
更多信息请转跳:
https://docs.espressif.com/projects/esp-idf/zh_CN/latest/esp32/security/flash-encryption.html#flash-encryption-status
欢迎关注:安信可科技