4、STM32安全硬件特性:不同系列(如STM32L5、STM32H7、STM32U5)的TrustZone、OTP、RDP(读保护)、PCROP、HDP等安全特性详解

在嵌入式安全领域,STM32不同系列芯片的安全能力差异显著。STM32L5、STM32H7、STM32U5分别代表了Arm Cortex-M33内核的入门安全级、Cortex-M7的高性能安全级以及新一代超低功耗安全级。理解这些系列在TrustZone、OTP、RDP、PCROP、HDP等硬件安全特性上的具体实现与差异,是构建可靠Secure Boot启动链的基石。本章将逐一拆解这些安全特性的原理、配置方法、实战场景与常见误区,帮助你在选型和开发中做出精准决策。

STM32L5系列 Cortex-M33 @ 110MHz STM32H7系列 Cortex-M7 @ 480MHz STM32U5系列 Cortex-M33 @ 160MHz TrustZone (TZ) 单核TZ,SAU/IDAU划分 OTP (512B) 一次性编程,存密钥/UID RDP (Level 0/1/2) 读保护,Level 2不可逆 PCROP 专有代码读保护 HDP 隐藏保护区,防调试 TrustZone (TZ) 仅H7A/H7B支持,双核TZ OTP (512B) 含SFI/密钥配置区 RDP (Level 0/1/2) Level 1可配合调试 PCROP 按扇区/银行配置 HDP HDP_CTRL寄存器控制 TrustZone (TZ) 增强型TZ,带HUK派生 OTP (1KB) 含SFI、密钥、配置区 RDP (Level 0/1/2) Level 2支持密码回退 PCROP 支持动态PCROP HDP HDP + HDP_EXT扩展 Secure Boot 启动链硬件基础

上图展示了三个主流系列在安全特性上的整体布局。可以看到,虽然它们都具备TrustZone、OTP、RDP、PCROP、HDP五大核心能力,但在具体实现、容量、配置灵活性和安全强度上存在显著差异。下面我们逐一深入分析。

4.1 TrustZone:从单核隔离到双核协同

核心原理

TrustZone是Arm针对Cortex-M33/M23等v8-M架构引入的硬件强制隔离技术。它将处理器、内存、外设划分为安全世界(Secure World)和非安全世界(Non-Secure World)。安全代码(如Secure Boot、密钥管理、加密算法)运行在安全状态,而非安全代码(如RTOS、应用逻辑)运行在非安全状态。两者通过SAU(安全属性单元)和IDAU(实现定义属性单元)共同决定内存区域的归属。对于STM32L5和U5,TrustZone是单核实现,即同一个Cortex-M33内核通过状态切换来执行安全与非安全代码;而STM32H7A/H7B则支持双核TrustZone,Cortex-M7和Cortex-M4可以分别被配置为安全核和非安全核,实现物理级别的隔离。

判断方法与配置流程

在STM32CubeMX中,启用TrustZone需要以下步骤:

  1. 在“Security”选项卡中勾选“TrustZone enabled”。
  2. 配置SAU区域:将Flash和SRAM划分为安全/非安全区域。通常将启动代码(0x0C0000起始)设为安全区域,应用代码设为非安全区域。
  3. 配置外设安全属性:每个外设(如UART、GPIO、DMA)都有独立的SEC寄存器位,决定其归属。
  4. 生成代码后,安全工程和非安全工程会分别生成,通过CMSE(Cortex-M Security Extension)库进行安全函数调用。
经验提醒:在L5/U5上,IDAU是硬件固定的,Flash和SRAM的低地址区域默认被标记为安全,高地址区域为非安全。你无法通过软件修改IDAU的默认值,只能通过SAU进一步缩小安全区域。因此,规划内存布局时,务必先查阅参考手册的IDAU映射表。

案例拆解:基于L5的Secure Boot分区设计

假设我们使用STM32L552ZE设计一款工业网关,需要保护固件不被逆向。我们规划了如下内存布局:

区域地址范围大小安全属性内容
Secure Boot0x0C0000 - 0x0C3FFF16KB安全启动代码、密钥校验
Secure Firmware0x0C4000 - 0x0C7FFF16KB安全安全服务、加密库
Non-Secure App0x0C8000 - 0x0FFFFF224KB非安全应用逻辑、协议栈
SRAM Secure0x20000000 - 0x20003FFF16KB安全安全栈、密钥缓存
SRAM Non-Secure0x20004000 - 0x2001FFFF112KB非安全应用堆栈、DMA缓冲

在配置过程中,我们遇到一个关键问题:DMA1和DMA2默认是非安全外设,但Secure Boot需要DMA来搬运固件。解决方案是将DMA1的通道0-3配置为安全属性,仅允许安全代码访问。同时,在安全代码中设置MPU(内存保护单元)来限制非安全代码对安全SRAM的访问。最终,Secure Boot成功在安全世界完成固件校验,然后通过SGATE指令切换到非安全世界执行应用。

风险提示:TrustZone不是万能的。如果非安全代码可以调用安全函数(通过SGATE),那么安全函数的入口参数和返回值必须严格校验。否则,攻击者可能通过构造恶意参数来利用安全世界中的缓冲区溢出漏洞。建议所有安全函数入口都使用CMSE的“secure gateway”属性,并启用栈保护。

4.2 OTP:一次性编程存储的密钥与配置

核心原理

OTP(One-Time Programmable)存储器是芯片内部的一块特殊Flash区域,只能编程一次,不可擦除。它用于存储设备唯一标识、根密钥、安全配置字等关键数据。STM32L5和H7提供512字节OTP,而U5提供1KB。OTP分为多个区域,包括:

  • 用户OTP区:可自由写入用户数据,但只能写一次。
  • 配置OTP区:用于存储RDP级别、PCROP配置、HDP配置等安全选项字节。
  • SFI区(仅H7/U5):用于安全固件安装(Secure Firmware Install),存储SFI密钥。

操作流程与注意事项

在STM32CubeProgrammer中,可以通过“OTP”选项卡读取和写入OTP。写入时需注意:

  1. OTP写入是位级别的,只能将1写为0,不能将0写为1。因此,写入前必须确认数据正确。
  2. OTP的编程电压和时序与普通Flash不同,必须使用芯片支持的编程算法。
  3. 一旦写入,无法回退。建议在量产前使用工程样片进行充分验证。
实战场景:在U5上,我们使用OTP存储AES-256根密钥。该密钥用于加密固件镜像。在Secure Boot启动时,从OTP读取密钥,解密固件并校验哈希。由于OTP不可擦除,即使攻击者通过JTAG读取了Flash内容,也无法获取密钥明文。但需要注意,OTP读取接口在RDP Level 1时仍然开放,因此必须将RDP提升到Level 2才能彻底保护OTP内容。

4.3 RDP(读保护):三级保护机制

核心原理

RDP(Read Protection)是STM32最基础的Flash保护机制,分为三个级别:

级别名称调试访问Flash读取回退可能性
Level 0无保护完全允许完全允许-
Level 1读保护禁止调试禁止外部读取可回退到Level 0(会擦除Flash)
Level 2永久保护永久禁止永久禁止不可回退

在Level 1下,芯片内部的代码仍然可以正常访问Flash,但外部调试器(如ST-Link)无法读取。Level 2则彻底关闭所有调试接口,且不可逆。对于Secure Boot场景,建议至少使用Level 1,并在量产时升级到Level 2。

案例拆解:H7的RDP Level 1与调试共存

在H7开发中,我们遇到一个棘手问题:启用RDP Level 1后,无法使用ST-Link进行在线调试。但开发阶段需要调试。解决方案是:

  1. 在开发阶段保持RDP Level 0,使用调试器进行单步调试。
  2. 在发布前,通过CubeProgrammer将RDP升级到Level 1。
  3. 如果后续需要调试,先将RDP回退到Level 0(会触发全片擦除),重新烧录固件。

但H7有一个特殊之处:在RDP Level 1下,如果设置了“调试认证”(Debug Authentication),可以通过特定的证书和密钥重新开放调试端口,而无需擦除Flash。这需要芯片支持“DA”功能,并且需要提前在OTP中烧录DA证书。

风险说明:RDP Level 2是永久性的。一旦设置,芯片将无法再被外部访问,包括固件升级。如果产品需要现场固件升级,必须通过内置的Bootloader(如UART、USB)进行,且Bootloader本身必须支持安全认证。否则,建议使用Level 1配合自定义的固件签名校验。

4.4 PCROP:专有代码保护

核心原理

PCROP(Proprietary Code Read-Out Protection)允许将Flash的特定区域标记为“仅执行”模式。CPU可以从中取指执行,但任何读取操作(包括DMA、调试器、甚至CPU自身的数据访问)都会被禁止。这非常适合存放密钥、解密算法、许可证校验代码等敏感内容。

PCROP的配置方式因系列而异:

  • L5:通过FLASH_PCROP_VERR寄存器配置,支持按页(2KB)粒度设置。
  • H7:通过FLASH_OPTCR寄存器配置,支持按扇区(128KB)粒度设置,且可以按银行独立配置。
  • U5:支持动态PCROP,可以在运行时通过安全代码启用或禁用PCROP区域,灵活性更高。

操作流程与案例

以L5为例,启用PCROP的步骤:

  1. 在CubeMX中,选择“Flash”配置,勾选“PCROP”并设置起始页和结束页。
  2. 生成代码后,在系统初始化时调用HAL_FLASHEx_OBProgram()写入选项字节。
  3. 注意:PCROP区域不能包含中断向量表,因为向量表需要被读取。
实战案例:在U5上,我们将AES密钥存储在PCROP区域。Secure Boot启动时,安全代码直接调用PCROP区域内的解密函数,该函数从PCROP区域读取密钥并执行解密。由于PCROP区域不可读,即使攻击者通过侧信道攻击获取了部分内存数据,也无法还原密钥。但需要注意,PCROP区域内的代码不能包含任何调试断点,否则会导致HardFault。

4.5 HDP:隐藏保护区域

核心原理

HDP(Hide Protection)用于隐藏Flash中的特定区域,使其在系统启动后不可见。与PCROP不同,HDP区域在复位后的一段时间内是可读的(用于启动代码),但一旦设置了HDP_CTRL寄存器,该区域就会被隐藏,任何访问都会返回错误。这非常适合存放启动密钥或临时解密缓冲区。

HDP的关键特性:

  • L5:支持最多4个HDP区域,每个区域大小可配置(最小4KB)。
  • H7:HDP区域由HDP_CTRL寄存器控制,支持按银行隐藏。
  • U5:支持HDP和HDP_EXT扩展,可以隐藏更大的区域,并支持在运行时动态调整。

配置方法与注意事项

在代码中启用HDP的典型流程:

// 以L5为例
HAL_FLASH_Unlock();
// 配置HDP区域1,起始地址0x0C0000,大小8KB
FLASH_HDPConfigTypeDef hdpConfig;
hdpConfig.HDPStartAddr = 0x0C0000;
hdpConfig.HDPSize = FLASH_HDP_SIZE_8KB;
hdpConfig.HDPNumber = FLASH_HDP_NUM_1;
HAL_FLASHEx_HDPConfig(&hdpConfig);
// 启用HDP
HAL_FLASHEx_HDPEnable(FLASH_HDP_ENABLE);
HAL_FLASH_Lock();
关键提醒:HDP一旦启用,隐藏区域将无法再被读取。因此,在启用HDP之前,必须确保启动代码已经完成了对该区域的读取和校验。如果HDP区域存放了密钥,建议在启用HDP后立即清除SRAM中的密钥副本,防止侧信道攻击。

4.6 综合对比与选型建议

特性STM32L5STM32H7 (A/B)STM32U5
TrustZone单核M33双核(M7+M4)单核M33增强型
OTP容量512B512B1KB
RDP级别0/1/20/1/20/1/2(支持密码回退)
PCROP粒度2KB页128KB扇区2KB页 + 动态PCROP
HDP区域数424 + HDP_EXT
典型应用中低端IoT、传感器高性能网关、HMI低功耗可穿戴、医疗

选型建议:如果追求极致功耗和安全性,U5是最佳选择;如果需要高性能计算和双核隔离,H7A/H7B是首选;如果成本敏感且安全需求适中,L5提供了足够的保护能力。

总结复盘:在构建Secure Boot启动链时,不要孤立地看待每个安全特性。TrustZone提供了隔离环境,OTP存储根密钥,RDP防止外部读取,PCROP保护敏感代码,HDP隐藏临时密钥。它们需要协同工作:例如,RDP Level 1保护整个Flash,PCROP保护密钥存储区,HDP保护启动过程中的临时解密缓冲区,而TrustZone确保只有安全代码能访问这些区域。建议在项目初期就制定完整的安全策略,并在硬件设计阶段预留足够的OTP空间和PCROP区域。