ESP32的flash加密过程及实现

Posted 小小河神纠结多

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ESP32的flash加密过程及实现相关的知识,希望对你有一定的参考价值。


前言

       一个项目完成之后,为了防止二次烧录或固件盗版,通常会进行程序加密,就好像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_DECRYPTDISABLE_DL_CACHE 的 eFuse 位,以便 UART 引导加载程序重新烧录加密的二进制文件。此外, FLASH_CRYPT_CNT 的 eFuse 位不受写入保护。

  • 对于 发布模式,固件引导加载程序设置 DISABLE_DL_ENCRYPTDISABLE_DL_DECRYPTDISABLE_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_ENCRYPTDISABLE_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
欢迎关注:安信可科技

以上是关于ESP32的flash加密过程及实现的主要内容,如果未能解决你的问题,请参考以下文章

ESP32的flash加密过程及实现

ESP32的flash加密过程及实现

esp32如何给外部flash下载程序

Arduino ESP32 flash数据存储结构

ESP8266学习笔记——内存分布及Flash读写接口

Arduino IDE增加ESP32flash分区配置选项