发现 STM32 防火墙的安全配置

judy的头像
judy 发布于:周二, 09/26/2017 - 17:17 ,关键词:

STM32 防火墙(Firewall)能够构建一个与其它代码隔离的带有数据存储的可信任代码区域,结合 RDP、WRP 以及 PCROP,可用来保护安全敏感的算法。在 STM32 Cube 固件库参考代码里提供了几个不同的防火墙配置。那么问题来了,什么是STM32 防火墙的应该使用的安全配置呢?本文以 STM32 参考手册为基础,以最大化安全为目标,来探索发现 STM32 防火墙的推荐配置。

STM32 防火墙介绍

STM32 防火墙保护特定代码/数据不被保护区域之外的执行所访问。代码和数据位于 Flash 存储器中,也可以位于 SRAM1 中。

可选择配置的防火墙的三个保护段如下:

 代码段(Flash)
 数据段(Flash)
 易失数据段(SRAM1), 可被配置为可执行

防火墙配置激活后,对受保护代码的访问必须唯一的通过调用门(Call gate)进行。防火墙外设监听 AMBA 总线,任何不通过调用门的访问,将导致系统重启。

发现 STM32 防火墙的安全配置
Figure 1 防火墙的连接

不同于 STM32 上的 ARM MPU 技术,防火墙激活后,将一直保持激活状态,直至下次系统复位。

防火墙的调用门

防火墙的调用门由三个字组成,位于 Flash 中的代码段以及配置成非共享且可执行的 SRAM1 数据段,开始地址的前三个 32位。

 第 1 字: 虚设。总是处于关闭状态。用于保护指令预取造成的对调用门的访问。
 第 2 和第 3 字:总是处于打开状态。

为了打开防火墙,代码必须跳到调用门的第 2 字执行,且第 2 字和第 3 字的执行不能被中断,否则会导致系统重启。

防火墙的状态图

防火强的配置激活后,处于关闭(Close)状态。在关闭状态下,对受保护区域的访问将被禁止。跳到防火墙的调用门处执行,则防火墙打开(open)。在防火墙打开状态下,若防火墙控制寄存器(FW_CR)的 Prearm(FPA)位依然为 0,跳转至非保护代码将导致系统重启。将防火墙控制寄存器的 Prearm 位设置成 1,这时任何对非保护代码的访问将导致防火墙进入关闭(close)但激活状态。

发现 STM32 防火墙的安全配置
Figure 2 防火墙的状态转换

STM32 防火墙例程

一个 STM32 防火墙的实例可在 CubeMX 固件包里找到。例如:

STM32Cube\Repository\STM32Cube_FW_L4_V1.6.0\Projects\STM32L476RG-Nucleo\Examples\FIREWALL

工程文件支持 IAR 和 Keil 开发环境,含有两个目录:

FIREWALL_VolatileData_Executable
FIREWALL_VolatileData_Shared

读者可使用 IAR 或者 Keil 打开这两个例子进行编译以及运行。

例程防火墙配置引出的疑问

例程在激活防火墙时使用了不同的配置,演示了防火墙外设的灵活性与不同的安全性,具有很好的学习用途,但是在实际应用中,为最大化安全考虑,我们究竟应该去应用哪一种配置 呢?

 配置一: 仅配置保护 SRAM1 中的数据,且将 SRAM1 的数据配置成可执行且非共享。注意没有对 Flash 的任何地方配置保护。

 /* No protected code segment (length set to 0) */
 fw_init.CodeSegmentStartAddress = 0x0;
 fw_init.CodeSegmentLength = 0;

 /* No protected non-volatile data segment (length set to 0) */
 fw_init.NonVDataSegmentStartAddress = 0x0;
 fw_init.NonVDataSegmentLength = 0;

 /* Protected volatile data segment (in SRAM1 memory) start address and length */
 fw_init.VDataSegmentStartAddress = 0x2000F100;
 fw_init.VDataSegmentLength = 3840; /* 0xF00 bytes */

 /* The protected volatile data segment can be executed */
 fw_init.VolatileDataExecution = FIREWALL_VOLATILEDATA_EXECUTABLE;

 /* The protected volatile data segment is not shared with non-protected
 application code */
 fw_init.VolatileDataShared = FIREWALL_VOLATILEDATA_NOT_SHARED;

 配置二: 保护 Flash 里的代码和数据,以及 SRAM1 中的数据。SRAM1 中的数据被配置为不可执行,但可共享

 /* Protected code segment start address and length */
 fw_init.CodeSegmentStartAddress = 0x08010000;
 fw_init.CodeSegmentLength = 512; /* 0x200 bytes */

 /* Protected non-volatile data segment (in FLASH memory) start address and length */
 fw_init.NonVDataSegmentStartAddress = 0x080FF000;
 fw_init.NonVDataSegmentLength = 256; /* 0x100 bytes */

 /* Protected volatile data segment (in SRAM1 memory) start address and length */
 fw_init.VDataSegmentStartAddress = 0x20000000;
 fw_init.VDataSegmentLength = 576; /* 0x240 bytes */

 /* The protected volatile data segment can't be executed */
 fw_init.VolatileDataExecution = FIREWALL_VOLATILEDATA_NOT_EXECUTABLE;

 /* The protected volatile data segment is shared with non-protected
 application code */
 fw_init.VolatileDataShared = FIREWALL_VOLATILEDATA_SHARED;

防火墙配置分析

SRAM1 的共享 Vs. 非共享

用户手册 RM0351 中提到若一段 SRAM1 被防火墙设置为共享(VDS=1),则该区域总是可被访问,而不用去管防火墙是打开,关闭还是没有激活。 同时,SRAM1 中的数据可被执行, 而不需要激活防火墙更不需要打开防火墙。换言之,共享区域其实是不在防火墙的保护之下。因此,为保护 SRAM1 中的敏感数据,我们应该避免使用共享配置,也就是 VDS 应当是 0。

发现 STM32 防火墙的安全配置
Figure 3 RM0351 中关于防火强的易失数据段配置成可共享

SRAM1 中的执行 Vs. 非执行

用户手册 RM0351 中提到,若 SRAM1 被配置成非共享(VDS=0)且可执行(VDE=1), 防火墙的调用门序列需要先被执行。这里有几层含义,一是这段代码已经处于防火墙的保护之下;二是这段代码可被调用门中的代码调用;三是这段代码可以是调用门(参考调用门的描述)。前面我们提到防火墙打开后,保护区域之内的执行可以访问保护区域之内的代码以及数据。换言之, 这里的 SRAM1 执行的代码是可以访问 Flash 里受防火墙保护的代码和数据。SRAM1 中的代码是可变 的,为避免不确定性,一般情况下我们应当避免 SRAM1 运行的代码访问 受保护的 Flash 代码和数据,也就是建议将 SRAM1 中配置成非执行。

发现 STM32 防火墙的安全配置
Figure 4 RM0351 中关于防火强的易失数据段配置成可执行

是否应该配置 Flash 的数据段(Non-volatile data)

前面的例程配置一仅配置了 SRAM1。然而用户手册 RM0351 中提到,Flash 里的数据段未被定义,则 FW_CR 寄存器可在任意时刻进行修改,而不用管 防火墙是打开还是关闭。

发现 STM32 防火墙的安全配置
Figure 5 RM0351 中关于防火强控制寄存器的动态配置方式的描述

也就是说,防火墙相比较 MPU 的优势“配置有效直到系统复位”,在这种配置下不再存在了。例如在例程“配置一”里,SRAM1 的代码配置成可执行非共享,受防火墙保护。然而在防火墙设置好且处于关闭状态后,我们依然可以通过保护区域之外这样的代码将该保护去掉,例如将它配置后再次改成可执行且共享。

__HAL_FIREWALL_VOLATILEDATA_SHARED_ENABLE();

读者可轻易将该代码加入到 FIREWALL_VolatileData_Executable 进行试验。这显然不是我们想要的安全配置, 我们应该避免这种用法。 所以,我们总是配置防火墙保护 Flash 中的某一数据段,使得 NVDSL 寄存器不为 0。

推荐的防火墙配置

综合前面的例子和分析,为最大化安全考虑,我们推荐使用防火墙 时使用以下典型配置。它配置了三个保护段,Flash 中的代码和数据,SRAM1 中数据;SRAM1 中数据配置成不可共享且不可执行。同时要注意修改编译器链接文件将三块受保护的代码和数据分别放置到相应的下列定义的区域(Region)里。链接文件, 对 IAR 是.icf 文件,对 MDK 是.sct 文件。

 /* Protected code segment start address and length */
 fw_init.CodeSegmentStartAddress = FW_CODE_START_ADDRESS;
 fw_init.CodeSegmentLength = FW_CODE_LENGTH;

 /* Protected non-volatile data segment (in FLASH memory) start address and length */
 fw_init.NonVDataSegmentStartAddress = FW_DATA_START_ADDRESS;
 fw_init.NonVDataSegmentLength = FW_DATA_LENGTH;
 /* Protected volatile data segment (in SRAM1 memory) start address and length */
 fw_init.VDataSegmentStartAddress = FW_VDATA_START_ADDRESS;
 fw_init.VDataSegmentLength = FW_VDATA_LENGTH;
 /* The protected volatile data segment can't be executed */
 fw_init.VolatileDataExecution = FIREWALL_VOLATILEDATA_NOT_EXECUTABLE;

 /* The protected volatile data segment is not shared with non-protected
 application code */
 fw_init.VolatileDataShared = FIREWALL_VOLATILEDATA_NOT_SHARED; 

因为控制寄存器 FW_CR,包括 Prearm 位、共享执行、设置,只能在防火墙打开时才能修改,因此在推荐配置下,典型的调用门代码执行序列应如下:

 清除 Prearm 位 (FPA)
 执行安全算法
 清除所有中间数据
 清除 CPU 寄存器信息
 设置 Prearm 位 (FPA)
 退出保护区域

结论

本文根据 STM32 参考手册, 提出了一个 STM32 防火墙的安全配置,可在实际 STM32 防火墙的案例中直接应用。也可以利用本文加深对 STM32 防火墙功能的理解。

来源: 21ic.com

围观 469