1. 项目概述
在物联网设备爆发式增长的今天,嵌入式系统面临前所未有的安全挑战。从智能门锁到工业网关,从医疗设备到车载终端,固件被窃取、被篡改、被逆向的风险无处不在。传统的MCU开发往往将安全视为"锦上添花",但在当今的威胁环境下,安全已经成为产品设计的"地基"。
STM32L5系列是ST首款基于Arm Cortex-M33内核并集成TrustZone技术的超低功耗MCU。它不仅继承了STM32L4系列优秀的低功耗特性(停机模式仅3μA),更从硬件层面构建了完整的安全体系。从信任根(Root of Trust)到安全固件安装(SFI),从安全启动到安全调试,形成了一条完整的信任链。
更值得关注的是,STM32L5是首个全系列出厂即支持SFI服务的STM32产品线。每颗芯片在出厂前就由ST预置了Secure Bootloader、芯片私钥及对应的数字证书,这意味着开发者可以直接利用这些安全基础设施,在不可信的第三方产线上安全地完成固件烧录。
2. 环境准备
2.1 硬件要求
| 硬件 | 型号 / 规格 | 用途 |
|---|---|---|
| 开发板 | NUCLEO-L552ZE-Q | 主开发平台,板载ST-LINK V3 |
| 调试器 | ST-LINK V3(板载) | 烧录与调试 |
| HSM智能卡 | ST提供的HSM硬件安全模块 | SFI产线烧录时使用 |
| USB线缆 | Micro-USB / USB-C | 连接开发板与PC |
| 串口终端 | 任意USB-TTL模块(可选) | 调试输出 |
注意:HSM智能卡仅在实际产线SFI烧录场景中需要。开发阶段可以使用STM32CubeProgrammer直接烧录。
2.2 软件依赖
| 软件 | 版本要求 | 下载地址 |
|---|---|---|
| STM32CubeIDE | v1.12.0+ | ST官网 |
| STM32CubeProgrammer | v2.14.0+ | ST官网 |
| STM32CubeMX | v6.8.0+ | ST官网 |
| STM32CubeL5固件包 | v1.5.0+ | 通过STM32CubeIDE在线安装 |
| Trusted Package Creator | 随STM32CubeProgrammer安装 | 安装时勾选TPC组件 |
| Tera Term / PuTTY | 最新版 | 串口终端工具 |
2.3 安装与配置
安装STM32CubeProgrammer(含Trusted Package Creator):
bash复制# 下载安装包后执行(以Linux为例)
chmod +x SetupSTM32CubeProgrammer-2.14.0.linux
./SetupSTM32CubeProgrammer-2.14.0.linux
安装过程中务必勾选**Trusted Package Creator (TPC)**组件,这是后续固件签名和加密的核心工具。
验证安装:
bash复制# 验证STM32CubeProgrammer CLI
STM32_Programmer_CLI --version
# 预期输出: STM32CubeProgrammer v2.14.0
# 验证Trusted Package Creator CLI
STM32TrustedPackageCreator_CLI --version
安装STM32CubeL5固件包:
在STM32CubeIDE中,通过Help → Manage Embedded Software Packages安装STM32CubeL5固件包。安装完成后,在以下路径可以找到TF-M参考实现:
bash复制STM32Cube_FW_L5/Projects/NUCLEO-L552ZE-Q/Applications/TFM/
3. 核心概念:从TrustZone到信任根
3.1 Arm TrustZone在Cortex-M33上的实现
TrustZone是Arm在Cortex-M33内核中引入的硬件安全扩展,它将处理器的运行状态划分为两个隔离的"世界":
- 安全世界(Secure World):运行安全启动代码、密钥管理、加密服务等核心安全逻辑
- 非安全世界(Non-Secure World):运行用户业务应用代码
这种隔离是硬件级别的,非安全世界的代码无法直接访问安全世界的内存、外设和寄存器。两个世界之间的通信通过一组严格定义的**NSC(Non-Secure Callable)**入口函数完成。
| 非安全世界 (Non-Secure World) | 安全世界 (Secure World) |
|---|---|
| NSC调用 | 安全启动代码 |
| 用户业务应用 | 密钥管理 |
| RTOS / 裸机外设驱动 | 加密服务 |
| TF-M安全服务 |
TrustZone的硬件隔离机制包含以下核心组件:
SAU(Security Attribution Unit):安全属性单元,由软件配置,定义内存区域的安全属性。类似于MPU对内存访问权限的管理,SAU管理的是内存区域的安全/非安全属性。
IDAU(Implementation Defined Attribution Unit):由芯片厂商(ST)在硬件设计时固化的安全属性定义,不可被软件修改。IDAU和SAU共同决定一个地址的最终安全属性取两者中更严格的那个。
GTZC(Global TrustZone Controller):STM32L5特有的全局TrustZone控制器,负责管理外设和SRAM的安全属性。它包含三个子模块:
- TZSC(TrustZone Security Controller):配置外设的安全属性
- TZIC(TrustZone Illegal Access Controller):检测并报告非法的跨域访问
- MPCBB(Memory Protection Controller Block Based):以256字节为粒度配置SRAM的安全属性
3.2 STM32L5安全存储架构
STM32L5的Flash存储器(最大512KB,双Bank)支持精细的安全分区:
code复制┌─────────────────────────────────────────────────┐
│ Flash Bank 1 │
│ ┌───────────────────────────────────────────┐ │
│ │ 安全区域 (Secure) │ │
│ │ ┌─────────────────────────────────┐ │ │
│ │ │ HDP区域 (隐藏保护) │ │ │
│ │ │ 安全启动代码 + 密钥存储 │ │ │
│ │ │ (启动后对用户应用不可见) │ │ │
│ │ └─────────────────────────────────┘ │ │
│ │ 安全应用代码 (TF-M Services) │ │
│ └───────────────────────────────────────────┘ │
│ ┌───────────────────────────────────────────┐ │
│ │ NSC区域 (Veneer Table) │ │
│ │ 安全世界的入口函数表 │ │
│ └───────────────────────────────────────────┘ │
│ ┌───────────────────────────────────────────┐ │
│ │ 非安全区域 (Non-Secure) │ │
│ │ 用户业务应用代码 │ │
│ └───────────────────────────────────────────┘ │
├─────────────────────────────────────────────────┤
│ Flash Bank 2 │
│ (可用于安全固件更新的备份区域) │
└─────────────────────────────────────────────────┘
关键安全特性:
HDP(Hide Protection):可以将Flash的一部分区域"隐藏"起来。通常用于存放安全启动代码,一旦安全启动完成并跳转到用户应用,HDP区域的内容对后续所有代码(包括安全世界的应用代码)都不可见。这确保了安全启动代码本身不会被篡改或逆向。
BOOT_LOCK:锁定启动入口,确保每次复位后CPU必须从安全启动代码开始执行,不能被绕过。
RDP(Readout Protection):读出保护,分为Level 0(无保护)、Level 0.5(仅限安全调试)、Level 1(禁止调试读取Flash)和Level 2(永久锁定,不可逆)。
WRP(Write Protection):写保护,防止关键Flash区域被意外或恶意擦写。
3.3 信任根(Root of Trust)的构建
信任根是整个安全体系的锚点。它是一段不可更改的启动代码及数据,每次上电复位时都会运行且不能被绕过。信任根的核心职责包括:
- 验证系统配置:检查Option Bytes、安全水印(Secure Watermark)、RDP级别等是否被篡改
- 验证固件完整性:使用密码学算法(ECDSA + SHA-256)验证安全应用和非安全应用的数字签名
- 建立安全环境:配置SAU、GTZC、MPU等安全边界,为后续代码运行创建隔离环境
- 启动信任链:验证通过后,将控制权交给安全应用(TF-M),再由安全应用启动非安全应用
在STM32L5上,信任根的参考实现是STM32Cube TF-M(Trusted Firmware-M)。TF-M已通过PSA Level 2认证,提供了以下安全服务:
- Secure Boot:安全启动,验证固件完整性
- Secure Storage:安全存储,保护敏感数据
- Crypto Service:加密服务,提供AES、SHA、ECDSA等算法接口
- Attestation:设备身份证明
- Secure Firmware Update:安全固件更新
4. 实战一:使能TrustZone并配置安全分区
4.1 使能TrustZone(TZEN位)
TrustZone的使能通过Flash Option Bytes中的TZEN位控制。一旦使能,芯片将进入安全/非安全双世界模式。
**警告:**使能TZEN后,如果没有正确配置安全启动代码,芯片可能无法正常启动。请确保在使能前已准备好安全固件。
使用STM32CubeProgrammer使能TZEN:
bash复制# 连接开发板
STM32_Programmer_CLI -c port=SWD mode=UR
# 读取当前Option Bytes
STM32_Programmer_CLI -c port=SWD mode=UR -ob displ
# 使能TrustZone
STM32_Programmer_CLI -c port=SWD mode=UR -ob TZEN=1
# 验证TZEN已使能
STM32_Programmer_CLI -c port=SWD mode=UR -ob displ | grep TZEN
# 预期输出: TZEN=0x1
4.2 使用STM32CubeMX配置安全分区
在STM32CubeMX中创建TrustZone项目时,需要同时生成两个工程:Secure工程和Non-Secure工程。
步骤1:创建项目
打开STM32CubeMX,选择STM32L552ZETxQ,在弹出的对话框中选择with TrustZone activated。
步骤2:配置Flash安全水印
在Security → Flash Watermark Configuration中配置安全区域边界:
code复制Secure Area 1 (Bank 1):
SECWM1_PSTRT = 0x00 # 安全区域起始页
SECWM1_PEND = 0x39 # 安全区域结束页 (前232 KB)
Secure Area 2 (Bank 2):
SECWM2_PSTRT = 0x7F # 不使用Bank 2安全区域
SECWM2_PEND = 0x00
步骤3:配置GTZC外设安全属性
在Security → GTZC中,将需要安全访问的外设标记为Secure:
code复制AES → Secure # 硬件加密加速器
RNG → Secure # 真随机数发生器
PKA → Secure # 公钥加速器
HASH → Secure # 哈希加速器
TAMP → Secure # 防篡改检测
步骤4:配置SAU区域
code复制SAU Region 0: 0x0C03E000 - 0x0C03FFFF (NSC, Veneer Table)
SAU Region 1: 0x08040000 - 0x0807FFFF (NS, Non-Secure Flash)
SAU Region 2: 0x20018000 - 0x2003FFFF (NS, Non-Secure SRAM)
SAU Region 3: 0x40000000 - 0x4FFFFFFF (NS, Non-Secure Peripherals)
4.3 安全世界初始化代码
以下是安全启动代码中SAU和GTZC的初始化实现:
c复制/**
* @file secure_boot.c
* @brief STM32L5安全世界初始化 - SAU与GTZC配置
* @note 此代码运行在安全世界,负责建立安全边界
*/
#include "stm32l5xx_hal.h"
#include "partition_stm32l552xx.h"
/* ============================================================
* SAU (Security Attribution Unit)配置
* 定义内存区域的安全属性
* ============================================================ */
static void SAU_Config(void)
{
/* 禁用SAU以进行配置 */
SAU->CTRL = 0U;
/* Region 0: Non-Secure Callable (NSC) - Veneer Table
* 这是安全世界暴露给非安全世界的入口函数表
* 非安全代码只能通过这些入口调用安全服务 */
SAU->RNR = 0U; /* 选择Region 0 */
SAU->RBAR = (0x0C03E000U & SAU_RBAR_BADDR_Msk); /* 起始地址 */
SAU->RLAR = ((0x0C03FFFFU & SAU_RLAR_LADDR_Msk)
| SAU_RLAR_NSC_Msk /* 标记为NSC区域 */
| SAU_RLAR_ENABLE_Msk); /* 使能此Region */
/* Region 1: Non-Secure Flash
* 用户业务应用代码存放区域 */
SAU->RNR = 1U;
SAU->RBAR = (0x08040000U & SAU_RBAR_BADDR_Msk);
SAU->RLAR = ((0x0807FFFFU & SAU_RLAR_LADDR_Msk)
| SAU_RLAR_ENABLE_Msk);
/* Region 2: Non-Secure SRAM
* 非安全应用的RAM空间 */
SAU->RNR = 2U;
SAU->RBAR = (0x20018000U & SAU_RBAR_BADDR_Msk);
SAU->RLAR = ((0x2003FFFFU & SAU_RLAR_LADDR_Msk)
| SAU_RLAR_ENABLE_Msk);
/* Region 3: Non-Secure Peripherals
* 非安全外设地址空间 */
SAU->RNR = 3U;
SAU->RBAR = (0x40000000U & SAU_RBAR_BADDR_Msk);
SAU->RLAR = ((0x4FFFFFFFU & SAU_RLAR_LADDR_Msk)
| SAU_RLAR_ENABLE_Msk);
/* 使能SAU,未配置的区域默认为Secure */
SAU->CTRL = SAU_CTRL_ENABLE_Msk;
}
/* ============================================================
* GTZC (Global TrustZone Controller)配置
* 管理外设和SRAM的安全属性
* ============================================================ */
static void GTZC_Config(void)
{
/* 使能GTZC时钟 */
__HAL_RCC_GTZC_CLK_ENABLE();
/* 配置安全外设 - 加密相关模块必须在安全世界 */
GTZC_TZSC_TypeDef *tzsc = GTZC_TZSC;
/* AES硬件加密加速器 → Secure */
HAL_GTZC_TZSC_ConfigPeriphAttributes(GTZC_PERIPH_AES,
GTZC_TZSC_PERIPH_SEC);
/* RNG真随机数发生器 → Secure */
HAL_GTZC_TZSC_ConfigPeriphAttributes(GTZC_PERIPH_RNG,
GTZC_TZSC_PERIPH_SEC);
/* PKA公钥加速器 → Secure */
HAL_GTZC_TZSC_ConfigPeriphAttributes(GTZC_PERIPH_PKA,
GTZC_TZSC_PERIPH_SEC);
/* HASH哈希加速器 → Secure */
HAL_GTZC_TZSC_ConfigPeriphAttributes(GTZC_PERIPH_HASH,
GTZC_TZSC_PERIPH_SEC);
/* 配置SRAM安全属性(MPCBB)
* 前96KB (0x20000000 - 0x20017FFF) → Secure
* 后160KB (0x20018000 - 0x2003FFFF) → Non-Secure */
MPCBB_ConfigTypeDef mpcbb_config = {0};
mpcbb_config.SecureRWIllegalMode = GTZC_MPCBB_SRWILADIS_ENABLE;
mpcbb_config.InvertSecureState = GTZC_MPCBB_INVSECSTATE_NOT_INVERTED;
/* SRAM1: 前192个block(每block256字节)为Secure */
for (uint32_t i = 0; i < 6; i++) { /* 6个super-block, 每个32 block */
mpcbb_config.AttributeConfig.MPCBB_SecConfig_array[i] = 0xFFFFFFFF;
}
/* 后续block为Non-Secure */
for (uint32_t i = 6; i < 24; i++) {
mpcbb_config.AttributeConfig.MPCBB_SecConfig_array[i] = 0x00000000;
}
HAL_GTZC_MPCBB_ConfigMem(SRAM1_BASE, &mpcbb_config);
/* 使能TZIC非法访问中断 */
HAL_GTZC_TZIC_EnableIT(GTZC_PERIPH_ALL);
}
/* ============================================================
* 安全启动主函数
* ============================================================ */
void Secure_Boot_Init(void)
{
/* 1. 配置SAU - 定义安全/非安全内存边界 */
SAU_Config();
/* 2. 配置GTZC - 定义外设和SRAM安全属性 */
GTZC_Config();
/* 3. 使能SecureFault、配置优先级 */
SCB->SHCSR |= SCB_SHCSR_SECUREFAULTENA_Msk;
NVIC_SetPriority(SecureFault_IRQn, 0);
/* 4. 配置FPU为安全/非安全共享 */
SCB->NSACR |= (SCB_NSACR_CP10_Msk | SCB_NSACR_CP11_Msk);
FPU->FPCCR |= FPU_FPCCR_TS_Msk; /* 安全上下文自动保存 */
FPU->FPCCR |= FPU_FPCCR_CLRONRETS_Msk; /* 返回时清除FP寄存器 */
}
4.4 非安全世界的入口跳转
安全启动完成后,需要跳转到非安全世界执行用户应用。这个跳转过程必须通过特定的方式完成:
c复制/**
* @brief 从安全世界跳转到非安全世界
* @param ns_vector_table 非安全应用的向量表地址
* @note 跳转前必须确保安全边界已正确配置
*/
void Jump_To_NonSecure(uint32_t ns_vector_table)
{
/* 获取非安全应用的MSP(Main Stack Pointer) */
uint32_t ns_msp = *((uint32_t *)ns_vector_table);
/* 获取非安全应用的Reset Handler地址 */
uint32_t ns_reset_handler = *((uint32_t *)(ns_vector_table + 4U));
/* 设置非安全世界的向量表偏移 */
SCB_NS->VTOR = ns_vector_table;
/* 设置非安全世界的MSP */
__TZ_set_MSP_NS(ns_msp);
/* 定义非安全函数指针类型 */
typedef void (*NonSecure_ResetHandler_t)(void) __attribute__((cmse_nonsecure_call));
/* 将Reset Handler地址转换为非安全函数指针
* 注意:地址的bit[0]必须清零,表示这是非安全入口 */
NonSecure_ResetHandler_t ns_entry =
(NonSecure_ResetHandler_t)(ns_reset_handler & ~0x1U);
/* 跳转到非安全世界 此调用不会返回 */
ns_entry();
}
/* 在main()中调用 */
int main(void)
{
/* 安全世界初始化 */
HAL_Init();
SystemClock_Config();
Secure_Boot_Init();
/* 初始化TF-M安全服务 */
TFM_Services_Init();
/* 跳转到非安全应用
* 非安全应用的向量表位于0x08040000 */
Jump_To_NonSecure(0x08040000U);
/* 不应到达此处 */
while (1) {
__NOP();
}
}
4.5 NSC入口函数(Veneer Table)
非安全世界调用安全服务时,必须通过NSC区域的入口函数。这些函数使用cmse_nonsecure_entry属性标记:
c复制/**
* @file secure_nsc.c
* @brief Non-Secure Callable入口函数
* @note 这些函数是安全世界暴露给非安全世界的API
* 编译后会被放置在NSC区域(Veneer Table)
*/
#include "arm_cmse.h"
#include "stm32l5xx_hal.h"
/* ============================================================
* NSC函数:安全随机数生成
* 非安全应用可以调用此函数获取硬件真随机数
* ============================================================ */
CMSE_NS_ENTRY uint32_t NSC_GetRandomNumber(void)
{
uint32_t random_value = 0;
RNG_HandleTypeDef hrng;
hrng.Instance = RNG;
/* RNG已在安全世界初始化,直接生成随机数 */
if (HAL_RNG_GenerateRandomNumber(&hrng, &random_value) != HAL_OK) {
/* 生成失败,返回0 */
return 0;
}
return random_value;
}
/* ============================================================
* NSC函数:AES-128-GCM加密
* 非安全应用可以调用此函数进行数据加密
* ============================================================ */
CMSE_NS_ENTRY int32_t NSC_AES_GCM_Encrypt(
const uint8_t *plaintext, /* 明文数据(非安全内存) */
uint32_t plaintext_len,
const uint8_t *aad, /* 附加认证数据 */
uint32_t aad_len,
uint8_t *ciphertext, /* 密文输出(非安全内存) */
uint8_t *tag) /* 认证标签输出 */
{
/* 安全检查:验证指针指向非安全内存
* 防止非安全代码通过恶意指针读取安全内存 */
if (cmse_check_address_range((void *)plaintext, plaintext_len,
CMSE_NONSECURE | CMSE_MPU_READ) == NULL) {
return -1; /* 指针验证失败 */
}
if (cmse_check_address_range((void *)ciphertext, plaintext_len,
CMSE_NONSECURE | CMSE_MPU_READWRITE) == NULL) {
return -1;
}
if (cmse_check_address_range((void *)tag, 16,
CMSE_NONSECURE | CMSE_MPU_READWRITE) == NULL) {
return -1;
}
/* 使用安全世界的AES硬件加速器执行加密 */
CRYP_HandleTypeDef hcryp;
hcryp.Instance = AES;
/* 从安全存储加载密钥(密钥永远不会暴露给非安全世界) */
uint8_t secure_key[16];
Secure_Storage_GetKey(secure_key, sizeof(secure_key));
hcryp.Init.DataType = CRYP_DATATYPE_8B;
hcryp.Init.KeySize = CRYP_KEYSIZE_128B;
hcryp.Init.pKey = (uint32_t *)secure_key;
hcryp.Init.Algorithm = CRYP_AES_GCM_GMAC;
hcryp.Init.DataWidthUnit = CRYP_DATAWIDTHUNIT_BYTE;
if (HAL_CRYP_Init(&hcryp) != HAL_OK) {
/* 清除密钥副本 */
memset(secure_key, 0, sizeof(secure_key));
return -2;
}
/* 执行AES-GCM加密 */
int32_t result = 0;
if (HAL_CRYP_Encrypt(&hcryp, (uint32_t *)plaintext, plaintext_len,
(uint32_t *)ciphertext, HAL_MAX_DELAY) != HAL_OK) {
result = -3;
}
/* 生成认证标签 */
if (result == 0) {
if (HAL_CRYPEx_AESGCM_GenerateAuthTAG(&hcryp, (uint32_t *)tag,
HAL_MAX_DELAY) != HAL_OK) {
result = -4;
}
}
/* 清除密钥副本 */
memset(secure_key, 0, sizeof(secure_key));
HAL_CRYP_DeInit(&hcryp);
return result;
}
/* ============================================================
* NSC函数:获取安全启动状态
* ============================================================ */
CMSE_NS_ENTRY uint32_t NSC_GetSecureBootStatus(void)
{
/* 返回安全启动验证结果
* 0x5A5A5A5A = 启动验证通过
* 其他值 = 启动异常 */
extern uint32_t g_secure_boot_status;
return g_secure_boot_status;
}
非安全世界调用示例:
c复制/**
* @file main_ns.c
* @brief 非安全世界应用 - 调用安全服务示例
*/
/* 声明NSC函数(由链接器从Secure工程导入) */
extern uint32_t NSC_GetRandomNumber(void);
extern int32_t NSC_AES_GCM_Encrypt(const uint8_t *plaintext,
uint32_t plaintext_len,
const uint8_t *aad,
uint32_t aad_len,
uint8_t *ciphertext,
uint8_t *tag);
extern uint32_t NSC_GetSecureBootStatus(void);
int main(void)
{
HAL_Init();
SystemClock_Config();
/* 检查安全启动状态 */
uint32_t boot_status = NSC_GetSecureBootStatus();
if (boot_status != 0x5A5A5A5A) {
/* 安全启动异常,进入安全模式 */
Error_Handler();
}
/* 获取硬件真随机数 */
uint32_t random = NSC_GetRandomNumber();
printf("Hardware RNG: 0x%08lX\r\n", random);
/* 使用安全世界的AES加密数据 */
uint8_t plaintext[] = "Hello, Secure World!";
uint8_t ciphertext[32] = {0};
uint8_t tag[16] = {0};
int32_t ret = NSC_AES_GCM_Encrypt(plaintext, sizeof(plaintext),
NULL, 0,
ciphertext, tag);
if (ret == 0) {
printf("AES-GCM encryption successful\r\n");
}
while (1) {
/* 用户业务逻辑 */
}
}
5. 实战二:安全固件安装(SFI)完整流程
5.1 SFI概述与安全模型
SFI(Secure Firmware Install)是ST提供的一整套安全生产服务,用于解决在不可信第三方产线上安全烧录固件的问题。它要解决两大核心安全问题:
固件保密性与完整性:OEM开发的固件在传输和烧录过程中不会被窃取或篡改。传递给产线的是经过AES-128-GCM加密的固件密文,无论通过USB、USART还是SWD/JTAG,线上传输的都是密文。
防止过量生产:OEM授权烧录N颗芯片,产线就只能烧录N颗,无法多烧。这通过HSM智能卡中的计数器实现。
5.2 SFI三方角色与组件
SFI服务涉及三个参与方:
ST(芯片厂商):
- 在芯片出厂前预置Secure Bootloader(RSS - Root Secure Services)
- 为每颗芯片生成唯一的ECDSA密钥对,私钥烧入芯片OTP,公钥封装为X.509证书
- STM32L5全系列出厂即支持SFI,无需额外定制
OEM(设备制造商):
- 使用Trusted Package Creator对固件进行签名和加密
- 将加密密钥和授权烧录数量写入HSM智能卡
- 将加密固件包和HSM智能卡交付给产线
产线(代工厂):
- 使用STM32CubeProgrammer + ST-LINK + HSM执行烧录
- 全程接触的只有固件密文,无法获取固件明文
- HSM计数器耗尽后无法继续烧录
5.3 使用Trusted Package Creator加密固件
步骤1:启动Trusted Package Creator
打开STM32CubeProgrammer,在左侧导航栏选择Trusted Package Creator,或直接启动独立的TPC工具。
步骤2:配置SFI参数
在TPC界面中选择SFI选项卡:
code复制Target MCU: STM32L5xx
Firmware Type: Secure + Non-Secure (TrustZone)
Encryption: AES-128-GCM
Signing: ECDSA-P256
Input Files:
Secure Binary: path/to/secure_app.bin
Non-Secure Binary: path/to/nonsecure_app.bin
Output:
SFI Package: path/to/firmware_package.sfi
Key Configuration:
AES Key: [自动生成或手动指定128-bit密钥]
ECDSA Private Key: path/to/oem_private_key.pem
步骤3:使用TPC CLI生成加密固件包
bash复制# 生成ECDSA密钥对(首次使用时)
openssl ecparam -genkey -name prime256v1 -out oem_private_key.pem
openssl ec -in oem_private_key.pem -pubout -out oem_public_key.pem
# 使用TPC CLI创建SFI固件包
STM32TrustedPackageCreator_CLI \
-sfi \
-target STM32L5 \
-iv 000102030405060708090A0B \
-key 000102030405060708090A0B0C0D0E0F \
-nonce 00112233445566778899AABB \
-sb path/to/secure_app.bin \
-nsb path/to/nonsecure_app.bin \
-sign oem_private_key.pem \
-o firmware_package.sfi \
-hv 1.0.0
# 验证生成的固件包
STM32TrustedPackageCreator_CLI \
-verify firmware_package.sfi \
-pubkey oem_public_key.pem
注意:以上密钥和IV仅为示例。实际生产中,AES密钥必须使用密码学安全的随机数生成器生成,并妥善保管。
5.4 配置HSM智能卡
HSM(Hardware Secure Module)是一张智能卡形态的安全芯片,用于在产线现场安全地存储和分发固件加密密钥。
bash复制# 初始化HSM智能卡
STM32TrustedPackageCreator_CLI \
-hsm init \
-key 000102030405060708090A0B0C0D0E0F \
-count 10000 \
-license path/to/license.bin
# 参数说明:
# -key: 与固件加密使用的相同AES-128密钥
# -count: 授权烧录的最大芯片数量
# -license: ST提供的HSM许可证文件
每次成功烧录一颗芯片,HSM内部的计数器会减1。当计数器归零时,HSM将拒绝提供会话密钥,从而阻止继续烧录。
5.5 执行SFI烧录
产线烧录环境搭建:
code复制PC (STM32CubeProgrammer)
│
├── USB ──── ST-LINK V3 ──── SWD ──── STM32L5目标板
│
└── USB ──── HSM智能卡读卡器 ──── HSM智能卡
使用STM32CubeProgrammer CLI执行SFI烧录:
bash复制# 连接目标芯片(通过ST-LINK)
STM32_Programmer_CLI -c port=SWD mode=UR
# 执行SFI安全烧录
STM32_Programmer_CLI \
-c port=SWD mode=UR \
-sfi firmware_package.sfi \
-hsm COM3 \
--verify
# 参数说明:
# -sfi: 指定加密固件包
# -hsm: HSM智能卡的串口号
# --verify: 烧录后验证
# 烧录成功后的预期输出:
# SFI: Connecting to HSM...OK
# SFI: Reading chip certificate...OK
# SFI: Requesting session key from HSM...OK (remaining: 9999)
# SFI: Transferring encrypted firmware...OK
# SFI: Chip decryption and verification...OK
# SFI: Programming secure flash...OK
# SFI: Setting option bytes...OK
# SFI: Verification...PASSED
# SFI: Programming completed successfully
5.6 SFI烧录过程中的芯片内部流程
当加密固件通过SWD或USART传输到STM32L5后,芯片内部的Secure Bootloader(RSS)执行以下操作:
c复制/**
* @brief SFI芯片内部处理流程(伪代码,说明原理)
* @note 此代码由ST预置在ROM中,OEM无法修改
*/
void RSS_SFI_Handler(void)
{
/* 1. 接收加密固件包头 */
SFI_Header_t header;
RSS_ReceiveData(&header, sizeof(header));
/* 2. 使用芯片私钥与HSM建立安全通道
* 基于ECDH密钥协商生成会话密钥 */
uint8_t session_key[16];
RSS_ECDH_KeyAgreement(chip_private_key, hsm_public_key, session_key);
/* 3. 接收加密固件数据块 */
uint8_t encrypted_block[4096];
uint8_t decrypted_block[4096];
while (RSS_HasMoreData()) {
RSS_ReceiveData(encrypted_block, sizeof(encrypted_block));
/* 4. 使用AES-128-GCM解密并验证完整性 */
if (AES_GCM_Decrypt(session_key, encrypted_block,
decrypted_block, tag) != SUCCESS) {
/* 解密失败或完整性校验失败 → 中止烧录 */
RSS_AbortAndLock();
return;
}
/* 5. 将解密后的明文写入安全Flash区域 */
FLASH_Program(target_address, decrypted_block, sizeof(decrypted_block));
target_address += sizeof(decrypted_block);
}
/* 6. 验证完整固件的数字签名(ECDSA) */
if (ECDSA_Verify(oem_public_key, firmware_hash, signature) != SUCCESS) {
/* 签名验证失败 → 擦除已写入的固件并锁定 */
FLASH_Erase(secure
