ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

STM32安全启动与固件更新:X-CUBE-SBSFU集成实战指南

STM32安全启动与固件更新:X-CUBE-SBSFU集成实战指南 前阵子接手一个需要OTA升级和防抄板的产品在选型阶段就被安利了ST官方的X-CUBE-SBSFU。说实话第一次看到这个扩展包的名字我以为是某个冷门库翻完AN5056应用笔记才发现这玩意儿其实是STM32整个安全生态里非常关键的一环Secure Boot和Secure Firmware Update也就是安全启动和安全固件更新。如果你打算做带远程升级的物联网设备、表计、网关或者任何对固件完整性和机密性有要求的嵌入式产品这篇笔记值得花时间看完。1. AN5056到底在解决什么SBSFU在固件安全体系中的位置很多工程师第一次接触X-CUBE-SBSFU会有一个困惑我已经有IAPIn-Application Programming了为什么还需要SBSFU这两个东西听起来都是在做引导和固件更新到底有什么区别答案是SBSFU不是简单地把Flash里的代码搬到RAM然后跳转过去它解决的是信任链的问题。IAP只是“从A处拷贝到B处”但SBSFU做了三件IAP做不到的事验签确认固件来源可信、解密防止固件被逆向提取、回滚保护防止攻击者刷入旧版本的漏洞固件。AN5056这份应用笔记描述的就是整个SBSFU的集成方法。它基于STM32CubeMX这个图形化配置工具通过勾选X-CUBE-SBSFU扩展包自动生成一个包含安全启动和安全固件更新的工程骨架。开发者只需要在这个骨架上挂载自己的应用代码就能获得一条完整的信任链。这个信任链的逻辑是这样的芯片上电后第一个执行的代码不是你的应用而是SBSFU自己。SBSFU会先检查内部Flash或者外部Flash里的固件镜像签名是否正确、版本号是否高于当前已安装的版本、加密数据能否被正确解密。全部通过后才会把控制权交给用户应用。如果验签失败或者镜像损坏SBSFU会进入固件升级模式等待接收新的固件包。这就像机场安检你应用固件需要先过安检SBSFU验签安检员会核对你的身份证件签名是不是官方签发、有没有过期版本号确认无误才放你进入候机厅运行。如果有任何黑名单回滚保护哪怕身份证是合法的也会被拦下来。所以X-CUBE-SBSFU适合哪些场景主要有三类一是需要远程升级并且担心固件被篡改的产品二是固件里包含算法或业务逻辑、需要防止被逆向提取的商业设备三是需要满足行业安全规范比如金融、电力、医疗的设备制造商。对于学生项目或者纯学习用SBSFU也能帮你理解安全启动的完整实现。2. 集成前的准备工具链、扩展包环境与真实门槛先别急着打开STM32CubeMX生成工程集成SBSFU之前有几个前置条件必须确认清楚。很多人在这一步栽了跟头然后误以为SBSFU是个“半成品”其实只是准备工作没做到位。2.1 硬件要求不是所有STM32都支持SBSFUSBSFU对MCU是有要求的。它需要芯片具备以下特性之一支持TrustZoneM33/M55内核、带有OTP一次性可编程区域、有足够的内部Flash来存放两份固件镜像、支持RDP读保护级别。像STM32L4系列、STM32F4系列、STM32H7系列这些主流型号大概率没问题但如果是老旧的F1系列或者Flash特别小的型号就得仔细查一下AN5056里面的支持列表了。举例来说STM32F746和STM32L496都有对应的SBSFU工程模板但同一个系列里不同型号的Flash大小差异会导致分区方案不同。如果你用的是F746ZG1024KB Flash但参考的是F746NG1024KB的模板分区配置基本可以照搬但如果用的是Flash只有512KB的型号就得自己重新规划分区。2.2 软件版本STM32CubeMX、扩展包和固件包的三角关系这是最容易出问题的地方。SBSFU扩展包不是独立的它依赖对应系列的固件包STM32Cube FW F1、FW L4、FW H7这些。在STM32CubeMX的Embedded Software Manager里你选择MCU型号后会自动列出推荐的固件包版本但X-CUBE-SBSFU扩展包需要单独在“Additional Software”选项卡里勾选。我遇到过的情况是这样的打开CubeMX选择了STM32H743ZI固件包选了STM32Cube FW_H7 V1.11.0然后在Additional Software里找到了X-CUBE-SBSFU V4.0.0看起来一切正常。但点“Generate Code”的时候CubeMX弹出一个错误提示大意是the firmware package (stm32cube fw_h7 v1.12.1) or one of its dependencies requires...这个报错意味着当前安装的固件包版本比扩展包预期的版本低需要升级FW_H7到V1.12.1。解决办法有两个一是在Help菜单里的Manage Embedded Software Packages中升级固件包二是在Additional Software里选择与当前固件包匹配的SBSFU版本。这个依赖关系非常严格版本差一点点都过不去。2.3 安装SBSFU扩展包的正确姿势如果你用的是较新版的CubeMXV6.8以上安装扩展包非常方便。打开CubeMX进入“Help” - “Manage Embedded Software Packages”在“STMicroelectronics”标签下会看到X-CUBE-SBSFU的列表勾选需要的版本后安装即可。需要注意一个细节SBSFU包下载非常慢经常装到一半就超时。如果遇到这种情况优先检查网络环境或者手动从ST官网下载压缩包后导入。具体操作是在Manage Embedded Software Packages窗口里点击“From Local...”按钮选择下载好的包文件CubeMX会解析并安装。安装完成后扩展包会出现在STM32CubeMX的嵌入式软件管理器中并且会在你新建工程时自动匹配对应的MCU型号。2.4 深入阅读AN5056的必要性最后一点是文档本身的阅读。AN5056这份应用笔记有几十页包含了架构说明、内存映射、集成步骤、错误码定义等多个部分。我建议重点阅读第三章“Architecture”和第五章“Integration”这两章解释了SBSFU代码在Flash中的布局以及用户应用如何链接到SBSFU之后。跳着读容易漏掉关键的内存映射信息后面连接脚本出问题时根本不知道原因。3. 用STM32CubeMX生成SBSFU工程核心配置与分区规划确认前置条件都满足后接下来的操作就顺理成章了。这一节我把生成SBSFU工程的完整步骤、核心配置项、以及最容易踩的分区规划问题一起说清楚。3.1 创建工程并选择扩展包打开STM32CubeMX选择你的MCU型号我以STM32L496ZG为例在顶部菜单栏的“Additional Software”中选择X-CUBE-SBSFU。选好后CubeMX会自动弹出SBSFU的配置界面核心是“Project Type”里的三项选择Security BootSecure Firmware Update完整功能Security Boot only仅安全启动不做固件升级Firmware Update only仅固件升级不校验启动对于新项目我会选择第一项完整功能。原因很简单SBSFU的两种功能安全启动和安全升级本来就是协同工作的割裂开反而增加了后续集成的复杂度。3.2 配置项详解签名算法、密钥长度和Flash布局在SBSFU配置界面里重点需要关注这些配置项配置项可选值我的建议签名算法RSA2048 / RSA3072 / ECDSA P-256新项目推荐ECDSA P-256验签速度快密钥短固件加密AES-GCM / AES-CTR如果固件里有敏感算法选AES-GCM更安全用户应用起始地址由SBSFU自动计算默认即可但需要关注Flash分区情况Slot数量1个或2个如果Flash紧张用1个slot也可以启动时先擦除再写入回滚保护Enable/Disable必须Enable防止攻击者降级固件版本配置完这些之后有一个很关键的环节内存映射检查。CubeMX会显示一个Flash分区的图包括SBSFU自身存放的区域一般是Flash起始的几页安全配置区域存放密钥、版本号等信息Slot 0活动镜像区Slot 1下载待激活镜像区对于L496ZG这种有1MB Flash的芯片默认分区方案是SBSFU占128KBSlot 0占400KBSlot 1占400KB剩下的一些页留给系统数据和安全存储。如果你的应用固件本身比较大比如超过了400KB的编译产物就需要调整分区。调整分区的操作一定要在CubeMX里做不要手动改连接脚本。因为CubeMX会根据分区配置自动生成对应MCU的链接脚本.icf、.sct或.ld文件手动改的话一旦重新生成代码就会丢失。3.3 用户应用工程的正确生成方式SBSFU的工程结构是双工程的一个工程是SBSFU引导程序另一个工程是你的用户应用。在CubeMX里生成时代码会分成两个文件夹每个文件夹都有自己的工程文件。有个很多新手都会犯的错误直接把应用代码塞进SBSFU工程里写。这样做会导致SBSFU代码和应用代码在同一个链接脚本里Flash地址完全错乱。正确的做法是保持SBSFU工程独立不要修改它的核心代码sbsfu.c、sfu_low_level_flash.c这些文件。在生成出来的用户应用工程里写自己的业务代码。编译SBSFU工程得到sbsfu.bin编译用户应用工程得到user_app.bin。用ST提供的签名工具对user_app.bin进行签名生成带签名的user_app_signed.bin。这里我多说一句关于签名工具的使用。AN5056文档里使用的工具通常是STM32CubeProgrammer配套的脚本或者SBSFU工程里的postbuild.bat。它需要你导入一个私钥文件PEM格式执行后会在编译输出的目录里自动生成已签名的固件。整个过程看起来像是一个黑盒但实际上只是把二进制文件和RSA签名以及加密后的AES密钥拼接到一起。3.4 双镜像启动时的硬件依赖还有一点容易忽略SBSFU在跳转到用户应用之前会重新配置系统时钟、关闭之前打开的外设、清理栈区。如果你的用户应用没有自己初始化时钟而是依赖SBSFU配置的状态那么跳转后很可能会跑飞。这一点在AN5056里有明确说明但依然有开发者忽略它。我的建议是用户应用的启动代码SystemInit函数里显式重新初始化时钟和Flash等待周期不要依赖SBSFU留下的任何状态。这是一种“互不信任”的安全设计但恰恰是稳定性的保证。4. 安全启动与安全升级的运行链路从验签到跳转的原理拆解配置完工程并且能编译通过你会发现代码能跑但这不意味着你真的理解了SBSFU在做什么。为了更好地排查问题和调整行为必须把它内部的运行逻辑搞清楚。4.1 安全启动的完整流程SBSFU运行起来之后内部的执行流程大致如下初始化阶段SBSFU从Flash起始地址开始执行首先初始化堆栈指针、中断向量表配置系统时钟。读取镜像头信息在Slot 0和Slot 1的位置读取固件镜像的头部。这个头部包含了魔数、版本号、固件大小、签名区域偏移、实际固件数据的哈希值等信息。版本比较比较两个Slot中固件的版本号选择版本号更高且有效的那个作为“候选镜像”。如果启用回滚保护还会和OTP中记录的最低允许版本比较。验签用预置在Flash安全区域的公钥对镜像头部的签名进行非对称解密运算得到摘要值再和实际固件数据计算出的哈希值比对。这一步是整个信任链的核心因为只有用私钥签过名的固件才能通过验签。解密与装载如果启用了固件加密SBSFU会用存储在安全区域的AES密钥解密固件数据然后装载到RAM或者直接原地解密。跳转执行验签通过后SBSFU设置一个有效标志跳转到用户应用的复位向量。4.2 安全升级流程中Slot切换的关键点安全固件升级的流程通常发生在用户应用运行时。用户应用接收到新的固件包通过UART、SPI、以太网或者无线链路先在RAM里做初步校验然后通过SBSFU提供的API接口把新固件写入非活动Slot即Slot 1。写入完成后应用会触发一个重启。重启后SBSFU再次执行第2步和第3步它发现Slot 1的版本号高于Slot 0于是把Slot 1作为新的候选镜像验签通过后跳转执行。如果验签失败SBSFU会回退到Slot 0启动旧固件同时记录一个“尝试过升级但失败”的事件。这里有个非常重要的概念叫“候选镜像确认”SBSFU不能一验签通过就直接认为新固件永远可用而是要等待新固件自己运行起来之后通过调用SBSFU_RequestAppend或SFU_APPLI_SetInstallationStatus之类的API主动确认。如果新固件没有及时确认下次重启时SBSFU还会认为它是“未确认”状态可能再次回退到旧固件。这个特性很多刚接触SBSFU的人不理解觉得只要写完固件重启就算升级成功了。实际上要让升级动作“永久生效”你必须在新应用的业务代码里主动调用确认函数。否则一旦设备在升级后频繁掉电重启每次都会回退到旧版本造成升级失败的假象。4.3 安全隐患为什么要双Slot而不是直接覆盖你可能想问为什么不直接覆盖旧固件非得搞两个Slot答案是“原子性”。如果升级过程中突然断电直接覆盖会导致旧固件也丢了新固件也没写完设备变成砖。双Slot方案保证了任何时候都至少有一个有效固件可以启动。这也是SBSFU的核心理念系统永远处于可启动状态。所以双Slot是安全设计里很值得借鉴的思想很多量产产品宁愿多花一倍Flash空间也要保证升级的稳健性。5. 从Demo到量产密钥管理、安全配置与Flash分区调整在MDK工程里编译通过、用开发板跑起Demo这只是万里长征第一步。真正到了产品化阶段有几个环节如果处理不好SBSFU形同虚设。5.1 密钥管理私钥等于产品的命脉SBSFU的安全核心在于公钥验证、私钥签名。理论上即使攻击者拿到了固件没有私钥也做不出一个能通过验签的恶意固件。但前提是你的私钥必须妥善保管。ST默认生成工程时会自带一组测试密钥放在工程的SBSFU_Keys目录或者编译脚本引用的路径下。开发阶段用测试密钥没问题但量产前必须换成自己生成的密钥对。否则任何人都可以从网上找到这组默认测试密钥用它们给恶意固件签名然后通过SBSFU的正常升级流程刷入设备。生成新密钥对的方法很简单用OpenSSL命令就可以了openssl genrsa -out private_key.pem 2048 openssl rsa -in private_key.pem -pubout -out public_key.pem生成之后把公钥配置到SBSFU工程中通常是通过key_rsa_pub.c文件或者CubeMX配置界面上导入把私钥保存在一个离线、安全、有权限控制的环境里。私钥一旦泄露整个安全体系就土崩瓦解了。5.2 安全配置区域的烧录RDP和OTPSBSFU的安全性依赖芯片的硬件保护机制。STM32CubeProgrammer支持设置RDP等级Level 0无保护任何人都能读出Flash。Level 1禁止通过调试接口读写Flash但可以通过连接复位等方式恢复Level 0。Level 2永久锁定不可回退芯片变砖级保护。SBSFU的Demo工程通常建议烧录时把RDP设置为Level 1或Level 2。Level 1可以让你在开发阶段还有后悔的余地Level 2一旦设置就永久没了调试和读回Flash的权限。量产前建议直接用Level 2把产品彻底锁定。但要注意设置Level 2之前务必确认你的SBSFU固件和用户应用固件已经完全验证通过因为一旦锁死想通过JTAG/SWD重新刷固件是不可能的。OTP区域用来存什么一部分SBSFU会把“最低允许固件版本号”存在OTP里配合SBSFU的版本号检查实现强制升级。比如你把min_version 5写入OTP那么任何版本号低于5的固件都无法启动。这个机制是用来应对“旧固件被破解”的场景的哪怕攻击者拿到了旧版本固件里面有已知漏洞也无法刷入设备。5.3 Flash分区调整的实战技巧前面说过可以在CubeMX里调整Flash分区。但如果你的产品Flash空间非常局促需要手动调整分区时有几个关键地址需要弄清楚SBSFU_BASESBSFU代码起始地址。SFU_MEM_APPLI_0Slot 0的起始地址。SFU_MEM_APPLI_SLOT_1Slot 1的起始地址。SE_KEY_BASE安全密钥存储区地址。这些地址在CubeMX生成的sfu_low_level_flash.h或stm32l4xx_hal_flash.h中有定义。调整分区的本质是修改这些宏定义同时保持Flash页面对齐。以STM32L4系列为例Flash页大小是2KB所以你设置的地址必须是2KB的整数倍。否则Flash写入函数会返回错误码。我在项目里遇到过一个情况默认分区中Slot 0只给了400KB但应用固件编译出来超过400KB导致签名工具报错“image is too big”。解决方案是把Slot 0和Slot 1都调整为448KB同时把SBSFU区域从128KB压缩到64KB。因为我的SBSFU不需要支持外部Flash64KB足够了。5.4 与VSCode开发方式结合的一点思考有些工程师有疑问sbsfu在vscode里怎么用实际上CubeMX生成的工程是包含多个IDE的Keil、IAR、GCCMakefile。VSCode其实是通过打开GCC Makefile工程来开发的。你只需要在工程根目录执行make或者通过VSCode的Cortex-Debug等插件来完成编译和烧录。SBSFU对开发工具链本身没有绑定关键点是makefile里的路径要配置正确特别是STM32CubeProgrammer的安装路径和Python环境。如果你习惯VSCode我建议保留CubeMX生成GCC Makefile工程然后用VSCode打开这个文件夹。这样既保持了CubeMX的图形化配置能力又能用VSCode的代码跳转、智能提示、Git管理这些功能。注意每次从CubeMX重新生成代码后makefile可能会被覆盖需要留意是否有自定义修改被冲掉。6. 实战中绕不开的坑包依赖、链接脚本与构建问题排查最后这部分重点写踩坑经验。我把常见的SBSFU集成问题整理成一条完整的排查链路按“报错现象 - 原因 - 解决方式”的思路讲。6.1 包依赖错误FW版本和扩展包不匹配这是我在前面提到的Stm32Cube FW包和扩展包版本不匹配的问题。典型报错信息有两种the firmware package (stm32cube fwf1 v1.8.7) or one of its dependencies requires... the firmware package (stm32cube fw_h7 v1.12.1) or one of its dependencies requires...出现这种报错的原因是X-CUBE-SBSFU的配置脚本在生成代码时会检查一个内部版本号清单它需要对应系列固件包的最低版本。修复方式分两步打开STM32CubeMX的“Help” - “Manage Embedded Software Packages”找到对应系列FW_F1/FW_H7等点击升级到报错信息里提示的版本号。回到工程在Additional Software里重新勾选SBSFU扩展包点“Generate Code”前先确认工程设置里的固件包版本已切换到了新版本。如果升级后依然报错重启一下CubeMX再试有时是因为软件包管理器没有自动刷新。6.2 编译通过但链接报错符号未定义与连接脚本不匹配SBSFU工程用了大量自定义的链接段memory region如果你的编译器版本和CubeMX默认的编译器不匹配可能会出现这样的链接错误undefined symbol: sbsfu_region_start undefined symbol: sbsfu_boot_region_end这类符号定义在SBSFU的连接脚本里例如Keil的.sct文件、IAR的.icf文件或GCC的.ld文件。如果你的工程文件被CubeMX重新生成过而你没有重新指定连接脚本就会导致符号丢失。排查方法确认工程选项里链接脚本指向的是CubeMX生成的那份脚本。如果脚本存在打开脚本搜索这些符号看是否拼写一致。如果链接脚本被无关的IDE配置覆盖了从CubeMX重新生成一次工程。6.3 运行后死机或跑飞中断向量表与Flash等待周期SBSFU跑起来后跳转到用户应用时死机是常遇到的问题。可能的原因有两个第一个是中断向量表没有重定位。用户应用的启动文件里必须用SCB-VTOR APP_ADDRESS的方式把中断向量表偏移到用户应用的起始地址。在SBSFU的Demo工程里这一步一般通过CubeMX生成的SystemInit或startup文件完成。但如果你自己写启动代码很容易漏掉这个操作。第二个是Flash等待周期。STM32的主频不同需要的Flash等待周期也不同。如果SBSFU里设置了较高的主频而你跳转到用户应用后没有重新初始化时钟用户应用可能因为Flash等待周期不足而跑飞。解决方式是在用户应用里重新调用SystemClock_Config把时钟和Flash等待状态重置为正确的值。6.4 签名工具生成的固件无法通过验签开发阶段经常遇到的另一个问题是把签名后的固件烧录到设备SBSFU验签失败日志显示错误码是SFU_ERROR_IMG_AUTH_FAIL或者类似的签名验证错误。排查思路确认公钥和私钥是否匹配。ST默认工程的公钥是写死在SBSFU代码里的签名工具使用的是私钥。如果你换过密钥对必须两边一起换。确认固件生成工具的选项。有的签名工具会根据镜像大小自动添加填充字节如果你在生成时指定了错误的slot大小签名后的固件包含的填充数据可能导致哈希不匹配。确认对齐方式。SBSFU要求镜像首地址对齐到16字节如果地址不对齐哈希计算时会读取到不同的数据。6.5 烧录时无法连接RDP等级设置过高的自救这是开发过程中最“刺激”的问题把RDP等级设置成了Level 2然后发现固件还有Bug需要升级但调试接口被永久锁死了。我的建议是开发阶段绝对不要设置Level 2用Level 1就够。Level 1虽然也限制了调试接口但通过STM32CubeProgrammer的“Reset”或者“Remove Protection”操作可以回到Level 0代价是Flash会被全部擦除保不住了但至少芯片还能用。而Level 2一旦写入就算你用各种手段也救不回来除非器件本身支持与ST的特约服务接口相关联的暂存解锁那基本等于送回原厂处理。写在最后一点个人体会如果让我用一句话总结SBSFU的集成经验那就是先理解信任链再做配置最后写代码。X-CUBE-SBSFU不是传统意义上的“库”它对工程结构、编译流程、烧录流程都有影响任何一个环节的配置失误都可能导致固件无法启动。不要试图从网上随便找一段配置直接套在自己的板子上每个MCU的Flash布局、每个工程的链接脚本都可能不同。拿到一块新板子建议按这个顺序走一遍先跑通Demo工程确定SBSFU能启动、能升级再修改密钥然后调整分区最后集成你的应用代码。一步一步验证能省下大量排查问题的精力和时间。我这里记录的多半是走过的弯路和最终的解决方式希望你在集成路上少踩几个和我一样的坑。
返回列表