安全启动和安全固件更新(SBSFU)一:SBSFU 概念

第1篇:什么是安全启动和安全固件更新 ------ SBSFU 概念入门

系列定位:从概念到实战,深入理解 STM32 安全启动与安全固件更新


目录

  1. [什么是 IAP/OTA?](#什么是 IAP/OTA?)
  2. 为什么需要"安全"启动?
  3. 为什么需要"安全"固件更新?
  4. [SBSFU 是什么?](#SBSFU 是什么?)
  5. 核心概念速览
  6. [单镜像 vs 双镜像](#单镜像 vs 双镜像)
  7. 本系列文章学习路线图

1. 什么是 IAP/OTA?

1.1 传统固件升级方式

想象一下,你买了一台智能家电(比如智能门锁或智能灯泡),半年后发现了一个bug需要修复。如果是传统的嵌入式设备,固件升级方式是这样的:

复制代码
      [电脑/笔记本]
           |
           | USB线 / J-Link / ST-LINK 调试器
           |
      [调试接口 SWD/JTAG]
           |
           |
      [单片机 Flash]

你需要:

  1. 把设备拆开,找到电路板上的调试接口
  2. 用物理线缆(ST-LINK、J-Link、USB转串口)连接调试接口
  3. 在 PC 上运行烧录软件(Keil、STM32CubeProgrammer、J-Flash 等)
  4. 把新固件烧录到芯片 Flash 中
  5. 装回设备外壳

问题非常明显:

  • 设备必须物理接触(拆机、接线),费时费力
  • 需要专业人员操作(普通用户无法自己完成)
  • 部署在野外的设备(如路灯控制器、农业传感器、基站设备)几乎无法升级
  • 每台设备需要逐个升级,批量化几乎不可能
  • 用户体感极差("为什么买个门锁还要拆机?")

1.2 IAP(In-Application Programming,在应用编程)

IAP 是一种让程序在运行过程中更新自身的技术。

打个生活化的比方:传统升级就像你要换房子的地基------必须先把房子拆了,再把新地基铺上去,这段时间你无处可住。而 IAP 则像是你住在二楼,一边正常生活,一边让施工队在房子旁边帮你盖一栋新楼,盖好了验收通过后,你再搬过去。

技术原理:

复制代码
┌──────────────────────────────────────────┐
│              芯片 Flash 空间              │
│                                          │
│  ┌─────────────┐  ┌──────────────────┐   │
│  │  Bootloader  │  │   用户程序 (App)  │   │
│  │  (不变代码)   │  │   (可被替换)      │   │
│  │             │  │                  │   │
│  │ 负责:       │  │  你的业务逻辑     │   │
│  │ 1.接收新固件 │  │  正常运行中......     │   │
│  │ 2.验证签名   │  │                  │   │
│  │ 3.烧写Flash  │  │  也可以通过接口    │   │
│  │ 4.跳转执行   │  │  接收新固件数据,   │   │
│  └─────────────┘  │  然后请求Bootloader │   │
│                   │  帮你完成替换      │   │
│                   └──────────────────┘   │
└──────────────────────────────────────────┘

IAP 的核心思想只有一句话:Flash 中驻留一段永远不动的代码(Bootloader),它负责接收、验证和写入新的 App 代码,App 可以通过通信接口接收数据并委托 Bootloader 完成自更新

在 STM32 上,我们通常会把 Bootloader 放在 Flash 的起始地址(比如 0x08000000------复位后 CPU 最先执行的地址),而用户程序放在 Bootloader 之后的一段区域。上电后先跑 Bootloader,由它决定是跳转到已有的 App,还是进入固件更新模式。

IAP 的传输通道可以是多种多样的:

  • UART 串口(本项目使用 YMODEM 协议,通过板载 ST-LINK 虚拟串口)
  • USB(DFU 模式------Device Firmware Upgrade)
  • SPI / I2C(连接外部 Flash 或 SD 卡等存储芯片)
  • SD 卡(离线升级方案)
  • CAN 总线(汽车电子常用)
  • 以太网 / Wi-Fi(进入 OTA 范畴)
  • 蓝牙 BLE(穿戴设备常用)

1.3 OTA(Over-The-Air,空中升级)

OTA 是 IAP 的"无线版本"------固件通过无线信道(Wi-Fi、蓝牙、LoRa、NB-IoT、4G/5G)从云端服务器直接传输到设备,无需任何物理接触。

复制代码
   ┌─────────┐        ┌─────────┐        ┌──────────────┐
   │ 云端服务器│ ═════>│ 网关/App │ ═════>│  IoT 终端设备 │
   │(新固件)   │  互联网 │          │  无线   │  (STM32芯片)  │
   │ v2.0.sfb │        │(路由器/  │ BLE/WiFi│              │
   │          │        │ 手机App) │        │              │
   └─────────┘        └─────────┘        └──────────────┘

OTA 是物联网(IoT)的"杀手级功能"。没有 OTA 的 IoT 设备,本质上是一个"一次性交付"的产品------出厂什么样,报废时还是什么样。

1.4 为什么物联网设备必须拥有 OTA 能力?

场景 没有 OTA 的后果 有 OTA 的好处
安全漏洞修复 设备变成僵尸网络的一员(Mirai 事件) 远程推送补丁,数小时内全部修复
功能迭代 硬件召回,成本动辄数十万 远程推送新功能,零硬件成本
Bug 修复 批量退货、客户投诉、品牌声誉受损 静默修复,用户无感知
协议升级 设备无法与新版本服务器通信 兼容性持续保持
商业模型 一次性买卖,收入天花板低 持续服务收入,SaaS 模式可能

现实案例:Mirai 僵尸网络(2016)

攻击者利用数十万台 IoT 设备(网络摄像头、路由器、DVR)的固件漏洞,将其感染为僵尸网络的"肉鸡",对 DNS 服务商 Dyn 发动了史上最大规模的 DDoS 攻击,导致 Twitter、Netflix、Reddit 等网站在美国东海岸大面积瘫痪。
根本原因:这些设备出厂固件留有漏洞,且根本不具备远程更新能力。一旦被感染,唯一的处理方式是物理销毁。


2. 为什么需要"安全"启动?

2.1 不安全启动的风险

如果你的设备上电后直接跳转到 App 代码,不做任何检查,那就像一栋没有门禁系统的大楼------任何人都可以随意进出。

三大核心威胁:

复制代码
威胁一:恶意固件替换 (Malicious Firmware Replacement)
   ┌──────────────────────────────────────┐
   │ 攻击者通过调试接口或漏洞,            │
   │ 将 Flash 中的合法程序替换成恶意程序。  │
   │                                      │
   │ 正常固件:  [智能灯光控制器 v1.0]      │
   │             ↓ 被攻击者替换            │
   │ 恶意固件:  [僵尸网络木马 + 挖矿程序]   │
   │                                      │
   │ 后果:你的设备变成攻击者的工具。       │
   └──────────────────────────────────────┘

威胁二:固件篡改 (Firmware Tampering)
   ┌──────────────────────────────────────┐
   │ 攻击者修改固件中的关键参数,          │
   │ 比如将温度传感器的阈值从80°C调到130°C │
   │                                      │
   │ 原始代码: if(temp > 80°C) shutdown();│
   │ 篡改代码: if(temp > 130°C) shutdown();│
   │                                      │
   │ 后果:设备在130°C才会保护关机------       │
   │       但那时设备可能已经烧毁了。       │
   └──────────────────────────────────────┘

威胁三:知识产权盗窃 (IP Theft)
   ┌──────────────────────────────────────┐
   │ 竞争对手通过调试器直接读取 Flash,    │
   │ 获得二进制文件后进行逆向工程,        │
   │ 分析出你的核心算法和控制逻辑。         │
   │                                      │
   │ 后果:你的产品被快速克隆,            │
   │       商业壁垒瞬间消失。              │
   └──────────────────────────────────────┘

2.2 真实案例

年份 事件名称 影响规模
2016 Mirai 僵尸网络 感染超过 60 万台 IoT 设备,DDoS 攻击导致美国东海岸大面积断网
2017 BrickerBot 永久性破坏不安全的 IoT 设备,使其"砖化"报废
2018 VPNFilter 感染 50 万台路由器,可远程截获流量、窃取凭证
2020 Ripple20 影响数亿台设备的 TCP/IP 协议栈漏洞,涵盖医疗、工业、能源
2021 PrintNightmare 系列 嵌入式打印服务漏洞可被远程利用,工业打印机成攻击入口

2.3 STM32 硬件安全基础:RDP、WRP、PCROP

在理解安全启动之前,你需要先认识 STM32 提供的最底层三道硬件安全防线。

2.3.1 RDP(读保护,Read Protection)

RDP 是 STM32 的第一道硬件锁,控制调试接口能否访问 Flash 内容:

RDP 等级 Option Bytes 值 调试访问 Flash Flash 内容 级别切换
Level 0 0xAA 完全开放(调试器可随意读写) 明文可见 →L1 自由切换
Level 1 0xBB 禁止(调试器连接即阻止) 不可读,但 Flash 仍可执行 →L0 时触发 Mass Erase(全部擦除)
Level 2 0xCC 永久禁止(JTAG/SWD 物理熔断) 永久锁定 不可逆! 芯片永远无法再调试

关键安全特性:RDP Level 1 → Level 0 的降级操作会自动触发全片擦除(Mass Erase)。这意味着攻击者无法通过"临时降到 Level 0 → 读出 Flash → 再升回 Level 1"来窃取固件------降级的同时,固件就已经被销毁了。

复制代码
RDP 等级的安全模型:

Level 0 (开发阶段)          Level 1 (生产阶段)           Level 2 (极端安全)
┌─────────────────┐       ┌─────────────────┐       ┌─────────────────┐
│ 调试器: 自由访问  │       │ 调试器: 禁止访问  │       │ 调试器: 永久禁止  │
│ Flash: 可读写     │       │ Flash: 仅执行     │       │ Flash: 仅执行     │
│                  │       │                  │       │                  │
│ 用途: 开发调试    │       │ 用途: 量产固件保护  │       │ 用途: 军工/支付    │
│                  │  ───> │                  │  ───> │                  │
│                  │       │ 降级=L0触发全擦   │       │ 不可逆! 再无法    │
│                  │       │ 固件不泄露 ✓      │       │ 调试/烧录/恢复    │
└─────────────────┘       └─────────────────┘       └─────────────────┘
2.3.2 WRP(写保护,Write Protection)

WRP 是区域粒度的 Flash 写保护------它可以锁定指定扇区的写入操作:

  • 保护对象 :SBSFU 引导代码区(0x08006600 - 0x0800FFFF)、SE 核心代码区
  • 作用:即使攻击者通过漏洞获得了代码执行能力,也无法擦除或篡改受 WRP 保护的 Flash 区域
  • 粒度:G4 系列为 2KB Flash Page 对齐
  • 注意:WRP 只保护"写",不保护"读"(需配合 RDP 和 PCROP)
2.3.3 PCROP(专有代码读保护,Proprietary Code Read Protection)

PCROP 是 STM32 最具特色的一项硬件安全技术:

复制代码
普通 Flash:                          PCROP 保护的 Flash:
┌──────────────────────┐           ┌──────────────────────┐
│ CPU 可以:             │           │ CPU 可以:             │
│  ✓ 执行代码           │           │  ✓ 执行代码            │
│  ✓ 读取数据           │           │  ✗ 读取数据 (硬件阻止) │
│  ✓ 写入数据           │           │  ✗ 写入数据 (WRP+PCROP)│
│                      │           │                      │
│ 调试器可以:           │           │ 调试器可以:            │
│  ✓ 读取全部内容       │           │  ✗ 读取 (RDP已锁)      │
│  ✓ 修改全部内容       │           │  ✗ 修改 (WRP已锁)      │
└──────────────────────┘           └──────────────────────┘

PCROP 允许 CPU 执行 保护区中的代码(取指令),但禁止任何形式的数据读取(数据访问)。这非常适合存放密钥:

  • 密钥以 MOVW/MOVT 立即数指令的形式嵌入在汇编代码中
  • CPU 执行这些指令时可以获取密钥值(加载到寄存器中用于加密运算)
  • 但任何尝试通过 LDR 指令或调试器读取该地址的企图都会触发硬件错误

三条防线协同工作

复制代码
攻击者想要窃取密钥:

尝试 1: 通过调试器读取 Flash
       → RDP Level 1 阻止调试器访问 ✗

尝试 2: 降级 RDP 到 Level 0 再读
       → 降级触发 Mass Erase,密钥随 Flash 一起销毁 ✗

尝试 3: 不降级 RDP,写一段代码把 Flash 内容通过串口发出来
       → PCROP 阻止数据读取,即使用代码也无法读取 ✗
       (只有受信任的 SE 代码可以"执行"该区域,但执行 ≠ 读取)

尝试 4: 修改 SBSFU 代码,让它发送密钥
       → WRP 保护 SBSFU 代码区,无法篡改 ✗

尝试 5: 通过 JTAG/SWD 扫描链窃取
       → HDP (硬件调试保护) 在启动后禁用调试接口 ✗

2.4 不同 STM32 系列的安全机制差异

并非所有 STM32 系列都有相同的安全硬件。不同系列采用不同的隔离技术:

系列 安全隔离技术 密钥存储 调试保护 安全等级
STM32G4 (本项目) Secure User Memory + MPU PCROP 保护 HDP + RDP ⭐⭐⭐⭐
STM32G0 Secure User Memory + MPU PCROP 保护 HDP + RDP ⭐⭐⭐
STM32H7 Secure User Memory + MPU PCROP 保护 HDP + RDP ⭐⭐⭐⭐
STM32L4/L0 Firewall(防火墙) Firewall 隔离 RDP ⭐⭐⭐
STM32L5/U5 ARM TrustZone-M SAU/IDAU 隔离 TZ-aware 调试 ⭐⭐⭐⭐⭐
STM32F4/F7 MPU 软件隔离 WRP 保护 RDP ⭐⭐
STM32WB CKS + MPU CKS 加密密钥 RDP + CKS ⭐⭐⭐⭐

G4 系列的 Secure User Memory 是其区别于 F4 的核心安全升级。它提供 4KB 粒度的安全存储区,可以配合 PCROP 和 MPU 实现多层密钥保护。详细机制将在第 11 篇(PCROP 与 HDP 硬件保护)中展开。

2.5 安全启动的目标

安全启动(Secure Boot)就像给设备装了一套"身份验证门禁系统"------每次有人试图进入大楼,门禁系统都会验证他的身份。

复制代码
每次上电或复位后,安全启动执行以下流程:

   ┌──────────────────┐
   │ 1. 硬件自检      │ ← 检查芯片安全配置是否正确
   │                   │   RDP(读保护等级)是否正确?
   │                   │   PCROP(代码读保护)是否激活?
   │                   │   WRP(写保护)是否覆盖关键区域?
   │    ↓             │
   │ 2. 激活动态保护   │ ← 打开 MPU(内存保护单元)
   │                   │   打开 HDP(硬件调试保护)
   │    ↓             │
   │ 3. 固件验签      │ ← 用预置公钥验证用户程序的数字签名
   │                   │   签名有效 → 固件来源可信
   │                   │   签名无效 → 固件被篡改或伪造
   │    ↓             │
   │ 4. 完整性校验    │ ← 用哈希值检查固件内容是否被修改
   │                   │   SHA-384 计算结果 == FwTag?
   │                   │   匹配 → 固件完整
   │                   │   不匹配 → 固件已被修改(哪怕1位)
   │    ↓             │
   │ 5. 跳转或拒绝    │ ← 全部验证通过 → 跳转到 App 执行
   │                   │   任一验证失败 → 拒绝执行,进入错误处理
   └──────────────────┘

核心原则:只运行"经过授权的"、"未被篡改的"代码。这就像公司大楼的门禁------只有持有有效员工卡的员工(经过签名验证的固件)才能进入办公区。


3. 为什么需要"安全"固件更新?

3.1 不安全固件更新的风险

即使设备有安全启动,如果固件更新过程不安全,攻击者照样可以在传输环节动手脚:

复制代码
风险场景:中间人攻击 (Man-in-the-Middle)

  ┌──────────┐       ┌──────────────────────┐       ┌──────────┐
  │ 官方服务器│       │      传输通道          │       │ IoT 设备 │
  │ (合法固件)│ ════>│  Wi-Fi / 互联网 / 串口  │ ════>│          │
  │ v2.0     │       │                      │       │ 接受v2.0 │
  └──────────┘       └──────────────────────┘       └──────────┘
                            ↑      ↑
                  攻击者截获固件  │
                  分析代码逻辑    │ 攻击者冒充服务器
                                │ 注入假冒固件
                          ┌─────┴─────┐
                          │  攻击者    │
                          │ 伪装固件v2.0│
                          │ (含后门)    │
                          └───────────┘

3.2 CIA 三要素

安全固件更新需要在三个维度 上提供保护------信息安全领域简称为 CIA 三要素

维度 英文 核心问题 通俗类比 SBSFU 如何实现
机密性 Confidentiality "别人能看到我的固件吗?" 给快递上锁 AES-256-CBC 加密
完整性 Integrity "固件在路上被掉包了吗?" 给快递贴防拆封条 SHA-384 哈希校验
认证性 Authenticity "这个固件真的是我发的吗?" 发件人签名盖章 ECDSA P-384 数字签名
复制代码
CIA 三要素在 SBSFU 中的协作:

  原始固件:  [版本号=2.0, 代码区=0x4801..., 配置区=...]
       │
       ├── 加密保护 → AES-256-CBC        机密性 (Confidentiality)
       │   明文 → 密文
       │   即使被截获,攻击者看到的是一堆乱码
       │   就像快递包裹被锁在保险箱里
       │
       ├── 哈希校验 → SHA-384            完整性 (Integrity)
       │   计算固件的384位"指纹"
       │   传输后重新计算 → 对比
       │   哪怕固件中只改了1个bit,指纹就完全不同
       │   就像快递包裹上的防拆封条
       │
       └── 数字签名 → ECDSA P-384       认证性 (Authenticity)
           用只有开发者持有的私钥签名
           设备用内置的公钥验证签名
           证明"这个固件确实是我们发布的"
           就像快递单上盖了发件人的印章

3.3 实际攻击场景对照

攻击类型 没有安全更新 有 SBSFU 安全更新
固件传输中被替换(中间人) 成功------设备接受假固件 失败------签名验证不通过,固件被拒绝
固件服务器被黑客入侵 所有设备收到恶意固件 失败------攻击者没有合法私钥,无法生成有效签名
固件回滚攻击(降级到有漏洞的旧版本) 成功------旧固件也有"合法"签名历史 失败------版本号单调递增检查 + 防回滚指纹机制
固件内容嗅探(窃取 IP) 成功------抓包即可获得明文固件 失败------固件已使用 AES-256-CBC 加密,窃取后是乱码
伪造更新通知 成功------设备盲目接受 失败------签名绑定固件头部元数据,整体不可伪造

4. SBSFU 是什么?

4.1 官方定义

SBSFU = Secure Boot + Secure Firmware Update

它是 STMicroelectronics 官方提供的 X-CUBE-SBSFU 软件扩展包,为 STM32 单片机提供一套完整的、可裁剪的、支持多种加密方案的安全启动和安全固件更新解决方案。

你可以把 SBSFU 理解为一个"开箱即用的安全固件管理框架"------ST 已经帮你写好了最复杂的加密操作、状态机管理、Flash 保护和双镜像交换逻辑,你只需要:

  1. 选择加密方案(本系列使用 ECC384 + AES256 + SHA384)
  2. 生成密钥对
  3. 编译三个工程
  4. 把你的应用逻辑写在 UserApp 中

本系列文章基于以下具体版本和配置:

  • SBSFU 版本: v2.6.2
  • 目标芯片: STM32G474RE(Cortex-M4, 170MHz, 512KB Flash, 128KB SRAM)
  • 开发板: NUCLEO-G474RE
  • 加密方案 : SECBOOT_ECCDSA_WITH_AES256_CBC_SHA384(ECC P-384 + AES-256-CBC + SHA-384)
  • IDE: Keil MDK-ARM
  • 镜像模式: 双镜像(2_Images)

4.2 版本历史简述

版本 发布时间 主要更新
v2.0 2018 初始发布,支持基本的安全启动和固件更新,支持 F4/F7/L4 系列
v2.4 2020 新增 STM32L5/G0/WB 系列支持,增强双镜像原子交换机制,增加 X.509 证书方案
v2.6 2022 新增 G4 系列全支持,新增 ECC384/SHA384 高安全强度方案,优化 MPU 多区域隔离,改进 HDP 激活时序

4.3 支持的 STM32 系列

  • STM32G4 系列(本系列教程使用 NUCLEO-G474RE)
  • STM32F4 / F7 / H7 系列
  • STM32L0 / L1 / L4 / L4+ / L5 系列
  • STM32G0 系列
  • STM32WB 系列(无线 MCU)

4.4 关键配置项:SFU_IMAGE_OFFSET

在开始深入了解工程结构之前,有一个重要的配置需要提前了解:SFU_IMAGE_OFFSET。它定义了用户固件镜像在 Flash 槽位中的起始偏移(以字节为单位)。

为什么不同的芯片有不同的偏移?

MCU 系列 SFU_IMAGE_OFFSET 原因
STM32F4 / F7 / H7 512 字节 Flash 页大小为 512B(F4)/ 256B(F7),对齐到单页之后
STM32F7 / H7(双 Bank) 1024 字节 双 Bank 配置,需对齐到更大边界
STM32G0 2048 字节 (2KB) G0 最小页大小为 2KB,槽位状态记录需要独立一页
STM32G4(本项目) 4096 字节 (4KB) G4 的 Secure User Memory 需要 4KB 扇区粒度,状态记录占整个 4KB 扇区

这个偏移量决定了:

  1. 固件头的存放位置 :在槽位的第 SFU_IMAGE_OFFSET 字节处
  2. 槽位状态记录的粒度:必须能独立地擦除状态区域而不影响固件数据
  3. Flash 页的磨损均衡:状态记录需要频繁更新(每次下载/安装都更新),必须放在独立页中

mapping_fwimg.h 中,这个值被定义为:

c 复制代码
#define SFU_IMAGE_OFFSET  ((uint32_t)4096)

4.5 本项目工程目录结构

复制代码
2_Images/                               ← 双镜像配置的根目录
  ├── 2_Images_SECoreBin/               ← 安全引擎工程
  │   ├── Inc/                          ←   头文件
  │   │   ├── se_crypto_config.h        ←     加密方案选择(核心配置!)
  │   │   ├── se_def_metadata.h         ←     固件头数据结构定义
  │   │   └── se_crypto_bootloader.h    ←     加密操作 API
  │   └── MDK-ARM/                      ←   Keil 工程文件
  │       └── Project.uvprojx
  ├── 2_Images_SBSFU/                   ← 安全引导程序工程
  │   ├── SBSFU/App/                    ←   SBSFU 源代码
  │   │   ├── sfu_boot.c               ←     安全启动状态机
  │   │   ├── sfu_fwimg_swap.c         ←     双镜像交换逻辑
  │   │   ├── sfu_loader.c             ←     本地加载器(YMODEM 接收)
  │   │   └── sfu_mpu_isolation.c      ←     MPU 隔离配置
  │   └── MDK-ARM/                      ←   Keil 工程文件
  │       └── Project.uvprojx
  ├── 2_Images_UserApp/                 ← 用户应用工程(示例)
  │   ├── Inc/
  │   └── MDK-ARM/
  │       └── Project.uvprojx
  └── Linker_Common/                    ← 链接脚本(定义内存布局)
      └── MDK-ARM/
          ├── mapping_sbsfu.h           ←     SBSFU/SE 内存映射
          └── mapping_fwimg.h           ←     固件槽位内存映射

编译顺序严格要求:

复制代码
SECoreBin → SBSFU → UserApp
   (1)        (2)       (3)

为什么这个顺序?
  1. SECoreBin 生成 SE_CORE_Bin 二进制 → 作为 SBSFU 的链接输入
  2. SBSFU 链接 SE_CORE_Bin + 自身代码 → 生成完整的 SBSFU 固件
  3. UserApp 依赖前两步的产物 → 生成可安全传输的 .sfb 加密固件包

5. 核心概念速览

在深入技术细节之前,你需要先理解这五个核心概念。它们就像一栋安全大楼的不同组成部分:

5.1 信任根(Root of Trust, RoT)

信任根是整个安全体系的基础------它是绝对不可篡改、永远最先执行、无条件可信的那部分代码和数据。在 SBSFU 中,信任根 = 安全引擎(SE)+ 安全引导程序(SBSFU)。

复制代码
信任链 (Chain of Trust) 模型:

┌──────────────────────────────────────────────────────┐
│                                                      │
│   信任根 ──────> 安全引导 ──────> 用户程序            │
│   (RoT)          (SB)            (App)              │
│                                                      │
│   硬件保证       验证 App         被验证通过后        │
│   不可篡改       签名有效性       才允许运行           │
│   首先执行                                             │
│   绝对可信                                             │
│                                                      │
│   如果信任根被破坏 → 整个安全体系崩溃                   │
│   就像身份证印制机器被黑客控制 → 假证就能以假乱真        │
└──────────────────────────────────────────────────────┘

生活化类比:信任根就像是政府颁发的身份证。你信任一张身份证,不是因为它用的是特种纸张或者特殊的印刷工艺,而是因为它的发行机构(公安局)是可信的权威机构。同理,芯片出厂时的硬件安全机制保证了信任根不可篡改------这是整个安全体系的逻辑起点。

5.2 安全启动(Secure Boot, SB)

安全启动是每次系统复位后第一个执行的安全检查流程,它像一道不可绕过的安检门:

复制代码
            ┌──────────────────┐
  系统复位 →│   安全启动 (SB)   │
  或上电    │                  │
            │ 第一步: 静态检查  │
            │ · RDP等级是否正确?│
            │ · PCROP是否激活? │
            │ · WRP是否配置?   │
            │ · DBANK是否关闭? │
            │        ↓         │
            │ 第二步: 动态保护  │
            │ · 配置MPU隔离SE区 │
            │ · 激活HDP防调试   │
            │        ↓         │
            │ 第三步: 固件验签  │
            │ · 读取Active槽头部│
            │ · ECDSA验证签名  │
            │ · SHA384校验完整性│
            │        ↓         │
            │ 第四步: 状态判断  │
            │ · State=VALID?   │
            │ · State=SELFTEST?│
            │        ↓         │
            │ 第五步: 跳转/拒绝 │
            │ · 通过→跳转App   │
            │ · 失败→进入Loader │
            └──────────────────┘

安全启动保证了一条绝对铁律:凡是在芯片上运行的代码,都必须经过验证。没有例外。

5.3 安全固件更新(Secure Firmware Update, SFU)

安全固件更新负责安全地接收、解密、验证和安装新固件。它是安全启动的"配套服务"------光有门禁(安全启动)还不够,你还需要确保新来的"访客"(新固件)是你邀请的那个人。

复制代码
完整的固件更新流程:

  云端/PC (开发者)                         设备端 (STM32)
  ═══════════════                         ══════════════
  ┌──────────────┐                    ┌──────────────────┐
  │ 编译UserApp   │                    │ ① SBSFU 启动     │
  │ 生成UserApp.bin│                   │ ┌──────────────┐ │
  │       ↓       │                    │ │ 安全启动流程   │ │
  │ prepareimage  │                    │ │ → 进入App     │ │
  │  .py 脚本     │                    │ └──────────────┘ │
  │       ↓       │                    │        ↓         │
  │ 加密+签名+打包 │                    │ ② App 运行中     │
  │       ↓       │                    │ ┌──────────────┐ │
  │ UserApp.sfb   │   UART/YMODEM      │ │ 发现新固件可用 │ │
  │ (加密固件包)   │ ═══════════════════>│ │ 请求Bootloader│ │
  └──────────────┘                    │ └──────────────┘ │
                                      │        ↓         │
                                      │ ③ 进入Loader模式  │
                                      │ ┌──────────────┐ │
                                      │ │ YMODEM接收    │ │
                                      │ │ 写入下载槽     │ │
                                      │ │ 解密(AES-256) │ │
                                      │ │ 验签(ECDSA)   │ │
                                      │ │ 校验(SHA384)  │ │
                                      │ │ 全部通过→交换  │ │
                                      │ └──────────────┘ │
                                      │        ↓         │
                                      │ ④ 下次启动        │
                                      │ ┌──────────────┐ │
                                      │ │ 安全启动→     │ │
                                      │ │ 验证新固件→   │ │
                                      │ │ 跳转新App     │ │
                                      │ └──────────────┘ │
                                      └──────────────────┘

5.4 安全引擎(Secure Engine, SE)

安全引擎是 SBSFU 的"保密室"------它垄断了所有加密操作,密钥存放在里面,加密算法在里面执行,外部代码只能通过唯一的入口(CallGate)提交请求。

复制代码
┌──────────────────────────────────────────┐
│            安全引擎 (SE) 内部结构          │
│                                          │
│  ┌────────────────────────────────────┐  │
│  │  CallGate (0x08000204)              │  │
│  │  唯一对外入口                        │  │
│  │  就像银行的柜台窗口:                  │  │
│  │  - 你可以提交"帮我验证这个签名"的请求  │  │
│  │  - 但你不能走进金库                    │  │
│  │  - 也不能看到柜台后面的操作            │  │
│  └────────────────────────────────────┘  │
│                                          │
│  ┌────────────────────────────────────┐  │
│  │  密钥区 (0x08000400)                │  │
│  │  PCROP 硬件保护,CPU可执行但不可读取   │  │
│  │  - ECDSA P-384 公钥 (96字节)        │  │
│  │  - AES-256 对称密钥 (32字节)        │  │
│  └────────────────────────────────────┘  │
│                                          │
│  ┌────────────────────────────────────┐  │
│  │  加密算法实现 (0x08000700)           │  │
│  │  - ECDSA 签名验证                   │  │
│  │  - AES-256-CBC 解密                │  │
│  │  - SHA-384 哈希计算                 │  │
│  └────────────────────────────────────┘  │
└──────────────────────────────────────────┘

生活化类比:安全引擎就像是银行的保险库(金库)。你可以在柜台窗口(CallGate)办理各种业务------存款、取款、转账(请求加密/解密/验签服务),但作为客户,你永远进不了金库(看不到密钥),也无法绕过柜台直接接触里面的现金(密钥数据)。金库有防弹玻璃(PCROP保护)、武装警卫(MPU隔离)、24小时监控(硬件监视),层层设防。

5.5 双镜像机制(Dual Image)

双镜像是 SBSFU 实现高可靠性固件更新的核心机制。它的基本思想是:运行和下载分离,两个槽位独立运作

复制代码
┌───────────────────────────────────────────┐
│             Flash 空间 (512KB)             │
│                                           │
│  ┌─────────────────────────────────────┐  │
│  │  Active Slot (活动槽)   216KB        │  │  ← 当前运行的固件
│  │  [SFU1][固件头部][代码+数据]          │  │     设备从这里执行
│  │  地址: 0x08010000 - 0x08045FFF      │  │
│  └─────────────────────────────────────┘  │
│  ┌─────────────────────────────────────┐  │
│  │  Swap Area (交换区)     8KB          │  │  ← 交换操作的"临时仓库"
│  │  保证原子性: 要么全完成, 要么全回滚  │  │
│  │  地址: 0x08046000 - 0x08047FFF      │  │
│  └─────────────────────────────────────┘  │
│  ┌─────────────────────────────────────┐  │
│  │  Download Slot (下载槽) 216KB        │  │  ← 新固件的下载存放区
│  │  [SFU1][固件头部][加密的代码+数据]    │  │     App运行时下载到此
│  │  地址: 0x08048000 - 0x0807DFFF      │  │
│  └─────────────────────────────────────┘  │
│                                           │
└───────────────────────────────────────────┘

双镜像机制的三大优势:

  1. 运行与下载并行:App 在 Active Slot 正常运行的同时,通过通信接口将新固件下载到 Download Slot。用户完全不会感知到后台正在下载。
  2. 断电可恢复:即使下载过程中突然断电,下次上电后 SBSFU 可以从断点继续下载(通过固件状态机记录下载进度)。
  3. 安全回滚:新固件下载完成后,SBSFU 会在下次启动时进行完整的安全验证。如果签名无效或自检不通过,自动回滚到旧固件,保证设备始终有可运行的固件。

6. 单镜像 vs 双镜像

这是 SBSFU 架构设计中最核心的选择。不同的应用场景需要不同的配置。

6.1 全面对比表

特性 单镜像 (1_Image) 双镜像 (2_Images, 本项目采用)
Flash 布局 SBSFU(64KB) + 1个App区(448KB) SBSFU(64KB) + Active(216KB) + Swap(8KB) + Download(216KB)
用户程序最大尺寸 ~448KB ~216KB
从 App 内发起下载 不支持(必须先跳回 Bootloader) 支持(App 一边运行一边后台下载)
更新期间设备状态 停止服务,等待下载完成 继续运行,用户无感知
断电保护 直接覆盖原固件,断电即"变砖" 断电续传,Swap 区保证原子性
固件回滚 不支持 支持自动回滚到上一个有效版本
实现复杂度 低(Flash 管理简单) 中(需要 Swap 区 + 状态机管理)
适用场景 Flash 空间紧张、不需要无线升级、更新频率极低 IoT 设备、需要 OTA、对可靠性要求高

6.2 单镜像工作原理(为什么不够安全)

复制代码
单镜像的固件更新流程:

  (1) 设备正常运行 App v1.0
       ┌──────────────────────────┐
       │ SBSFU │   App v1.0       │
       │ 64KB  │   448KB          │
       └──────────────────────────┘

  (2) 检测到更新 → 跳回 Bootloader → 接收新固件
       ┌──────────────────────────┐
       │ SBSFU │ App v1.0 (被覆盖) │ ← 直接写入 App 区域
       │ 64KB  │ ████████████████ │
       └──────────────────────────┘
       ↑ 正在擦除+写入中......

  (3) 如果此时断电!!!
       ┌──────────────────────────┐
       │ SBSFU │ 半旧半新的垃圾数据 │ ← 设备变砖!
       │ 64KB  │                  │   没有完整固件可运行
       └──────────────────────────┘

单镜像的致命缺陷:下载过程直接覆盖正在运行的 App 区域。如果在覆盖过程中断电,Flash 中既没有完整的旧固件,也没有完整的新固件------设备彻底"变砖"(bricked),只能通过调试器重新烧录。

6.3 双镜像工作原理(本项目采用)

复制代码
双镜像的安全更新流程(分阶段展示):

═══════════════════════════════════════════════════════
阶段1: 设备正常运行
═══════════════════════════════════════════════════════
  ┌──────────┐  ┌──────────┐  ┌──────────┐
  │ Active槽  │  │ Swap区   │  │ Download槽│
  │ [App v1] │  │  [空]     │  │  [空]    │
  │ 正在运行! │  │          │  │          │
  └──────────┘  └──────────┘  └──────────┘

═══════════════════════════════════════════════════════
阶段2: App 运行中启动后台下载(不会中断 App 运行!)
═══════════════════════════════════════════════════════
  ┌──────────┐  ┌──────────┐  ┌──────────┐
  │ Active槽  │  │ Swap区   │  │ Download槽│
  │ [App v1] │  │  [空]     │  │ [接收中]  │ ← YMODEM 逐包接收
  │ 正常服务! │  │          │  │ 40%完成  │
  └──────────┘  └──────────┘  └──────────┘

  如果此时断电? → 下次上电,SBSFU 检测到 Download 槽有未完成的下载
                  → 可选: 继续下载(续传)或丢弃并启动旧 App

═══════════════════════════════════════════════════════
阶段3: 下载完成,SBSFU 在下次启动时验证并交换
═══════════════════════════════════════════════════════
  SBSFU 启动 → 检测到 Download 槽有完整新固件
            → ECDSA 验签 → 通过 ✓
            → SHA384 完整性 → 通过 ✓
            → AES 解密 → 成功 ✓
            → 执行 Swap(使用 Swap 区逐页交换)

  交换完成后:
  ┌──────────┐  ┌──────────┐  ┌──────────┐
  │ Active槽  │  │ Swap区   │  │ Download槽│
  │ [App v2] │  │ [已清空]  │  │  [App v1] │
  │ 新固件!   │  │          │  │  (旧固件) │
  └──────────┘  └──────────┘  └──────────┘

  如果 Swap 中途断电? → Swap 区记录了交换进度
                       → 下次上电后继续完成交换(原子性保证)

═══════════════════════════════════════════════════════
阶段4: SBSFU 验证新 Active 槽固件 → 跳转执行
═══════════════════════════════════════════════════════
  SBSFU → 验证 Active 槽 App v2 签名 → 通过
        → 跳转到 App v2 执行
        → App v2 首次运行自检通过 → State 标记为 VALID

为什么只需要 8KB 的 Swap 区?

Swap 区的作用不是存储整个固件镜像,而是逐页交换时的中转站。具体来说:

  1. 将 Active 槽的一页(2KB)读到 Swap 区
  2. 将 Download 槽的对应页复制到 Active 槽
  3. 将 Swap 区的旧数据写回 Download 槽
  4. 更新交换进度状态
  5. 重复直到 216KB 全部交换完成

Swap 区只存储当前正在交换的一页(2KB)+ 交换状态记录(在另一个页中),8KB = 4个Flash页足够。

比喻:搬家时你不需要一个和旧房子一样大的仓库------你只需要一辆小货车(Swap区),每次只搬一车东西,搬完一车再回来搬下一车。就算搬家过程中突然下雨(断电),你也能从记录本上看到搬到了第几车,下次继续从那里搬。

Swap Trailer 结构:断电恢复的秘密

Swap 区之所以能在断电后恢复,是因为它的底部存放了一份精密的 Trailer(尾记录)。它类似于数据库的 WAL(Write-Ahead Log,预写日志):

复制代码
Swap 区底部 Trailer 结构:
┌──────────────────────┐ 高地址 (Swap区末尾)
│   Magic (16 字节)     │  ← "SFU_SWAP" 魔数,标识这是一个有效的 Swap 区
├──────────────────────┤
│ Image Resume (16 字节)│  ← 记录当前活跃的镜像信息(FW size、version、offset)
├──────────────────────┤
│   Clean (32 字节)     │  ← 填充 0x55,用于检测断电中断后的"脏"状态
├──────────────────────┤
│  Swap State #1        │  ← 槽位 1 的交换状态(逐页记录进度)
│  Swap State #2        │  ← 槽位 2 的交换状态
│  ...                  │
├──────────────────────┤
│  Swap State #N        │  ← 最后一页的状态记录
└──────────────────────┘ 低地址

Clean 字段为什么是 0x55 (0b01010101)?
  · 0xFF = Flash 擦除态(全 1),不可用
  · 0x00 = 全 0,可能与编程后的"全写 0"混淆
  · 0x55 = 交替 0/1 模式,可明确区分为"已编程为 Clean"
  → SBSFU 上电时检查 Clean 字段:
     · Clean == 0x55 → 上次 Swap 正常完成,交换区可用
     · Clean != 0x55 → 上次 Swap 被中断,需要从 Image Resume 恢复

V1 vs V2 交换算法

SBSFU v2.6.2 提供了两种交换算法。V2 是对 V1 的重要优化:

特性 V1 交换算法 V2 交换算法 (默认)
单页擦写次数 3 次 (读→写→擦 旧位置) 1 次 (直接交换,原位改写)
断电恢复能力 支持 支持(Clean 标记检测)
Flash 寿命影响 每次更新多 3 倍磨损 每次更新正常磨损
交换速度 较慢(每页多 2 次擦写) 较(快速每页仅 1 次擦写)
适用芯片 所有系列 G4/G0/H7/L5(推荐)

V2 算法的核心优化:通过重新设计交换状态机,将每页的"读出→擦除旧位置→写入新位置"三步操作合并为一步直接交换。对于一个 216KB 的固件镜像(约 108 个 2KB Flash 页),V2 每次更新节省约 216 次无必要的 Flash 擦除操作。考虑到 Flash 典型寿命为 10,000 次擦除/页,这显著延长了芯片寿命。

6.4 本项目实际 Flash 地址分配

复制代码
完整 Flash 内存地图(512KB):

地址              大小       区域名称          说明
──────────────────────────────────────────────────────
0x08000000 ───┐
              │  64KB      SBSFU + SE        安全引导 + 安全引擎
0x08010000 ───┤                              64KB边界对齐
              │  216KB     Active Slot #1    当前运行的用户固件
0x08046000 ───┤
              │  8KB       Swap Area         原子交换临时区
0x08048000 ───┤
              │  216KB     Download Slot     新固件下载区
0x0807E000 ───┤
              │  8KB       (保留)             未来扩展
0x08080000 ───┘  (512KB Flash 结束)

(第 4 篇文章将对内存布局进行逐字节级别的详细解析。)


7. 本系列文章学习路线图

本系列计划共 15 篇文章,按四个阶段递进,从零基础到可以独立移植和部署。

第一阶段:概念与基础(第 1-4 篇)

编号 标题 内容定位 适合谁读
01 什么是安全启动和安全固件更新(本文) IAP/OTA 概念、SBSFU 介绍、核心概念速览、单/双镜像对比 所有读者,必读
02 环境搭建:Keil MDK 与硬件准备 开发板介绍、Keil/CubeProgrammer/TeraTerm/Python 安装、路径问题解决 准备动手的读者,必读
03 加密基础:密码学概念速通 Hash、AES、ECC、ECDSA、数字签名、本项目加密方案全景 零密码学基础,必读
04 内存布局:SBSFU 的 Flash 与 RAM 分配 精确地址图、MPU/PCROP/HDP 保护机制、每个区域的设计意图 想深入理解架构,必读

第二阶段:核心原理(第 5-9 篇)

编号 标题 核心内容
05 安全引擎(SE)深度解析 CallGate 调用机制、密钥混淆存储、PCROP 保护原理、SE 加密 API
06 固件头部结构(FW Header)详解 Magic 字段、版本号、FW大小、IV、FwTag、HeaderSignature、ImageState、Fingerprint
07 SBSFU 状态机(FSM)全解析 状态枚举、状态转换图、下载→验证→安装→交换 全流程
08 安全启动流程(Secure Boot)代码走读 从 Reset_Handler 到 App main() 的完整代码执行路径
09 安全固件更新流程(SFU)代码走读 YMODEM 协议接收、流式解密、在线验签、Swap 原子操作

第三阶段:保护机制(第 10-12 篇)

编号 标题 核心内容
10 MPU 内存保护单元配置 8个 MPU 区域的分配策略、权限矩阵、上下文切换时的 MPU 重编程
11 PCROP 与 HDP 硬件保护 Option Bytes 配置、PCROP 激活流程、HDP 时序、调试接口管理
12 固件状态与防回滚机制 State 字段编码(INVALID/VALID/SELFTEST/NEW)、自检测试、版本单调性检查

第四阶段:实战与进阶(第 13-15 篇)

编号 标题 核心内容
13 编译、烧录与首次运行 三步编译实操、STM32CubeProgrammer 烧录、Tera Term YMODEM 传输、终端日志解读
14 密钥生成与固件打包 KeysAndImages 脚本详解、密钥对生成、固件加密签名、.sfb 文件格式
15 从开发模式到生产模式 RDP 等级锁定、安全配置固化、量产烧录策略、密钥管理最佳实践

学习路径建议

复制代码
        适合所有读者的主线:
        ═══════════════
        ① 概念入门 → ② 环境搭建 → ③ 加密基础 → ④ 内存布局
              │            │            │            │
              └────────────┴────────────┴────────────┘
                                │
                    ⑤~⑨ 核心原理深度理解
                                │
                    ⑩~⑫ 保护机制深入掌握
                                │
                    ⑬~⑮ 动手实战 + 量产准备


        - 零基础读者: 严格按 1→2→3→4→5→...→15 顺序阅读
        - 有一定 STM32 基础: 可快速浏览①②,重点读③④,深入⑤~⑮
        - 只关心"怎么用": ②→⑬→⑭→⑮(快速实操通道)
        - 需要移植到其他芯片: 重点读④⑩⑪⑭

小结

本文帮你建立了 SBSFU 的完整认知框架。以下是需要记住的关键点:

  1. IAP 让程序可以在运行中更新自身,OTA 是 IAP 的无线实现,IoT 设备不可或缺
  2. 安全启动 = 设备上电后的第一道安检门,确保只有经过授权(有效签名)的固件才能运行
  3. 安全固件更新 在三个维度保护固件:机密性(AES 加密)、完整性(SHA 哈希)、认证性(ECDSA 签名)
  4. SBSFU = SB + SFU,ST 官方提供的成熟解决方案,开箱即用
  5. 信任根(RoT) 是整个安全体系的基石,由硬件保证不可篡改
  6. 安全引擎(SE) 是密钥和加密算法的"金库",只通过 CallGate 对外服务
  7. 双镜像机制 是可靠性保证:运行与下载分离、断电可恢复、失败可回滚
  8. 本项目配置:ECC P-384 + AES-256-CBC + SHA-384,提供 192 位安全强度

一句话总结:SBSFU 就像是给 STM32 穿上了一套防弹衣------别人不能偷看你的固件(加密)、不能篡改你的代码(签名+哈希)、不能注入恶意程序(安全启动)、更新失败也能自动恢复(双镜像+Swap区)。


下一篇:第2篇:环境搭建 ------ Keil MDK 与硬件准备

相关推荐
weixin_514253181 小时前
509-qwen3.5-9b csdn tmux
安全
深邃-1 小时前
【Web安全】-计算机网络协议(1):IP协议详解,HTTP协议介绍
linux·tcp/ip·计算机网络·安全·web安全·http·网络安全
@insist1231 小时前
信息安全工程师-网络安全审计核心知识与考点解析
安全·软考·信息安全工程师·软件水平考试
2601_955256471 小时前
服务器入侵应急响应SOP:从发现挖矿病毒到安全加固的完整操作流程
服务器·chrome·安全
带娃的IT创业者2 小时前
Microsoft Edge 密码泄露事件深度剖析:当“安全”成为幻影
安全·microsoft·edge·microsoft edge·密码安全·内存安全·明文存储
Paranoid-up2 小时前
安全启动和安全固件更新(SBSFU)8:安全启动状态机
安全·iap·安全启动·安全升级·sbsfu
不灭锦鲤2 小时前
网络安全学习第105天
学习·安全·web安全
2301_7807896612 小时前
云服务器被黑能恢复吗?云服务器被黑的解决办法
运维·服务器·网络·安全·web安全
GuiltyFet13 小时前
【无标题】
安全·ai