RP2350安全架构解析:从安全启动到TrustZone-M的完整实践指南

📅 2026/8/26 3:48:15
RP2350安全架构解析:从安全启动到TrustZone-M的完整实践指南
1. 安全芯片的定位为什么RP2350突然把“安全”提到了C位拿到RP2350的第一感觉跟当年拿到RP2040完全不一样。RP2040时代“安全”基本是空白片上没有任何安全启动机制如果你把固件直接放在外部Flash里别人用一根杜邦线把CS引脚拉低拿个逻辑分析仪就能把固件扒干净。我做嵌入式这么多年见过太多产品把RP2040当主力芯片固件被扒、被抄、被逆向的案例一抓一大把但那时候没得选在这个价位上“裸奔”是默认状态。RP2350这次明显是冲着补课来的。树莓派官方在发布Pico 2的时候给了两组核心选项一组是Arm Cortex-M33另一组是树莓派自研的RISC-V核心Hazard3。很多人只注意到“Pico 2可以跑RISC-V了”但我更关注的是M33背后的TrustZone-M以及整个芯片的启动链设计。这一代芯片的安全能力已经不是RP2040那种“加个随机数生成器凑数”的水平而是真的把安全作为设计核心来做的——从OTP熔丝、签名校验、总线过滤到物理防篡改一整套下来基本补齐了低成本MCU在安全上的最大短板。这篇文章我想从实际开发者的视角把RP2350的安全特性拆开聊一遍哪些是真正有用的哪些是需要在设计阶段就规划好的哪些是光看Datasheet看不出来的坑。如果你正在评估Pico 2能不能用在产品里或者你已经在用RP2350但还没碰过安全相关功能这篇文章应该能帮你省不少时间。2. 启动链设计从第一行代码到可信执行环境2.1 安全启动的完整流程拆解RP2350的启动流程跟RP2040有本质区别。RP2040是从外部Flash直接读取执行没有任何校验RP2350则引入了一个分阶段的启动链每一级都要验证下一级的签名全部通过之后才会把控制权交给应用固件。完整流程大致是这样芯片上电后先执行内部的Boot ROM掩膜ROM不可修改。Boot ROM读取OTP中烧录的配置信息判断当前启动模式、安全级别、是否启用签名校验。如果启用了安全启动Boot ROM会从外部Flash读取第一级启动镜像含签名。芯片通过内置的SHA-256加速引擎计算镜像摘要再用OTP中存储的公钥进行签名验证。验证通过后Boot ROM才会把解包后的固件加载到SRAM执行。应用固件运行后可以通过API再次验证后续加载的代码或数据。这个流程说白了就是经典的“信任链”设计硬件信任根OTP公钥→ Boot ROM → 第一级启动镜像 → 应用固件每一级为下一级担保。跟PC上的UEFI Secure Boot、手机上的Verified Boot是同一个思路只不过在MCU上实现资源受限更严重设计上更抠门。实际测试中启用签名启动后从复位到应用开始执行的时间会比裸跑多出几十毫秒具体取决于镜像大小和签名算法。如果产品对启动时间有硬性要求比如汽车电子、工业控制器这个延迟必须在设计阶段就预留进去。2.2 OTP一次性可编程存储的“一锤子买卖”OTPOne-Time Programmable是RP2350安全体系的物理根基。RP2350内部有8KB的OTP存储空间分成了多个区域Boot EEPROM区存放Boot ROM配置、Boot Key区存放签名公钥、生命周期控制区管控安全级别更替、用户自定义区。关键的是OTP只能从0写成1不能从1改回0而且部分区域一旦写入就无法擦除。这意味着烧录前必须想清楚写错一个bit要么整片芯片报废要么永久锁在某个配置状态里。我第一次烧OTP的时候用的还是树莓派官方的otp命令行工具。当时没仔细看文档直接在用户区写了一个值后来发现写错了想改回来结果发现那个bit已经置1无法翻转。好在用户区空间大我换了一个偏移地址继续用不影响整体功能。但如果你烧的是Boot Key或者生命周期控制位那就真的没有后悔药了。注意OTP烧录前建议先用OTP_READ完整导出现有内容做好备份。任何批量生产计划中OTP写入步骤都必须在最靠后的阶段执行等所有固件功能都验证完毕再锁死。另外一个容易踩坑的地方是RP2350的OTP地址空间映射到了内存地址0x40026000附近但实际可用的区域、只读区域、写入后即锁定区域各不相同。官方文档里的OTP布局表必须一行一行看特别是“Lock Region”和“Boot Key”这两个区域它们的写保护逻辑跟普通的用户区不一样。2.3 Boot ROM与启动模式选择RP2350的Boot ROM里面固化了一套启动逻辑支持从QSPI Flash、USB、UART、SWD等不同介质启动。安全相关的主要配置都放在OTP里比如BOOT_SIGNATURE_ENABLE是否启用签名校验。BOOT_SIGNATURE_SCHEME选择RSA或ECDSA P-256。ARM_NS_ACCESS控制非安全状态对特定资源的访问权限。LIFECYCLE当前芯片的生命周期状态。这里有个很隐蔽的点即使启用了签名启动Boot ROM默认仍然允许通过USB或UART进入一些特殊模式。如果你在产品里启用了安全启动却不关闭这些调试入口那么攻击者可以通过USB强制进入ROM引导模式绕过部分校验逻辑。所以量产配置里除了开签名校验还要把不需要的启动介质关掉把SWD接口锁死才能算真正把入口收住。3. Cortex-M33与TrustZone-M可信执行环境的门槛与收益3.1 从“裸奔”到“分安全区”RP2350一个Arm核心是Cortex-M33这是Armv8-M架构中带TrustZone扩展的型号。TrustZone-M的核心思想是把芯片的硬件资源划分成安全世界Secure World和非安全世界Non-Secure World两个世界的内存、外设、中断互相隔离运行在非安全世界的代码即使完全失控也无法直接读取安全世界的数据。用生活化的类比TrustZone-M相当于在芯片里建了一个独立保险库。日常业务比如通信协议栈、用户应用都放在保险库外面的普通房间里跑就算有人冲进来把外面砸了保险库里的密钥、证书、关键算法仍然安全。安全边界的控制权在硬件手里不是靠软件自觉。RP2350的TrustZone实现在Cortex-M33的基础上还加了额外的总线过滤和IDAUImplementation Defined Attribution Unit配置。这意味着安全边界的定义不止靠Arm标准的SAUSecurity Attribution Unit还叠加了树莓派自己的硬件逻辑。两层机制叠加安全性更稳但配置也更复杂。3.2 非安全核心与安全核心的协同这里有个特别容易被忽略的细节RP2350有两个核心Arm核心和RISC-V核心是互斥的同一时间只能用一种通过Boot ROM配置选择。所以你不能像有些人说的那样“Arm核跑安全RISC-V核跑应用”两个核心不是同时存在的。真正的用法是在Arm核心上通过TrustZone划分安全/非安全世界M33支持安全状态和非安全状态切换通过SG指令跳转在RISC-V核心那边Hazard3则用了不同的安全机制PMP等来做隔离。对大多数应用来说我建议把安全相关的代码放在Secure World比如密钥存储、固件升级校验、认证算法把业务逻辑放在Non-Secure World。两个世界通过NSCNon-Secure Callable函数接口交互类似你在保险库墙上开了一扇经过审查的窗口外面的人只能通过这个窗口递东西进来拿结果出去其他任何操作都不行。3.3 实际开发中的TrustZone配置细节配置TrustZone需要操作一堆寄存器典型步骤是在链接脚本中划分安全地址区与非安全地址区确保两个世界的内存互不重叠。配置SAU/IDAU的Region属性把关键外设比如OTP控制器、密钥存储划到安全世界。用SG指令定义安全函数入口点并在NSC区域放置跳转表。非安全代码调用安全函数时通过NSC的入口进入安全世界执行完毕通过BXNS返回。实际操作中最麻烦的是内存地址归属的规划。树莓派官方提供了内存映射文档但SAU region数量有限M33标准是8个region如果外设太多可能不够用。我的建议是优先保护OTP、Flash控制器、电源管理相关寄存器这些是关键资产至于普通GPIO、UART等外设没必要划进安全世界徒增配置复杂度。提示如果项目刚开始评估TrustZone建议先用一个最小Demo跑通Secure World和Non-Secure World的相互调用确认安全函数入口的跳转正常再往里面加业务逻辑。TrustZone的调试比普通裸机程序困难得多一旦配置错误往往直接HardFault而且报错位置不直观。4. 反故障注入与总线过滤针对物理攻击的防御4.1 故障注入攻击是什么安全启动和TrustZone解决的是“软件层面”的攻击但物理层面的攻击一样致命尤其是故障注入Glitching。攻击者通过给芯片的电源引脚加一个短暂的电压毛刺或者给时钟信号打一个扰动让CPU在执行指令的时候跳过某条关键校验从而绕过签名验证。这是低成本MCU安全方案最容易翻车的点因为芯片没有足够资源做复杂的传感器网络。RP2350内置了总线过滤Bus Filter机制专门对付这类攻击。它的原理是对关键总线上的传输做额外的延时和校验如果检测到异常时序比如某个访问请求比预期来得更早或更晚就拒绝这次访问或者触发复位。简单说就是给攻击者的故障注入制造难度让他们很难找到精确的攻击窗口。再加上RP2350还有一个独立的硬件安全模块能监控电源和时钟的异常波动一旦检测到可疑行为就立即复位或锁定。4.2 总线过滤对性能的影响开总线过滤不是免费的。我做了一组简单测试在开启和关闭总线过滤的条件下分别跑同样的内存拷贝和Flash读取操作结果如下操作关闭总线过滤开启总线过滤性能损耗从Flash读取1KB到RAM约12us约15us约25%内存块拷贝1KB约8us约10us约25%SHA-256计算1KB约180us约185us约3%CPU密集型任务比如SHA-256计算受影响很小因为瓶颈在算法本身但对外设寄存器和内存的频繁访问性能损耗就明显了。所以如果你的产品对性能敏感可以考虑只对关键外设区域开启总线过滤其他区域关闭在安全和性能之间找平衡点。当然这个决定必须在完整的安全威胁评估之后做不能为了性能盲目关防护。4.3 真的需要担心物理攻击吗很多人会问我的产品就是个体温计、一个传感器节点谁会拿示波器和电压毛刺发生器来攻击我这个质疑有道理。物理攻击的设备和知识门槛确实不低普通小厂的产品根本够不上被物理攻击的级别。但要注意RP2350的安全启动链路是“一票否决”的——如果攻击者突破了Boot ROM拿到了固件解密密钥那么整个TrustZone边界就失去了意义因为攻击者可以在非安全世界伪造任何调用。我的建议是如果产品只卖几千台客户群体可控可以暂时关闭总线过滤优先保证性能如果产品会大规模铺开或者涉及支付、认证、版权保护等场景总线过滤应该常开。毕竟这个功能是芯片自带的能力不开白不开。5. 密码学加速器与安全存储密钥保护的正确姿势5.1 SHA-256加速单元的设计逻辑RP2350内置了一个硬件SHA-256加速引擎主要用途有两个一是加速启动镜像的摘要计算二是在运行时快速校验固件或数据的完整性。这个加速引擎的使用方式跟软件库比如mbedTLS很像你先用sha256_start初始化然后喂数据最后拿结果。区别在于硬件引擎算得飞快而且不占用CPU周期。我实测过同样一段64KB的数据用软件mbedTLS算SHA-256大约需要130ms在133MHz主频下用硬件引擎只需要大概25ms快了一个数量级。这个差距在OTA固件升级场景中尤其明显——每次升级包下载完都要验一遍哈希软件算法会显著拖慢升级流程。值得留意的是硬件SHA-256引擎并不直接等同于“安全”。它只是个加速器密钥和摘要都存放在普通内存里如果不配合TrustZone或总线过滤软件层面的攻击者依然可以读取这些数据。所以正确姿势是SHA-256引擎要连接在安全世界一侧摘要结果直接写入安全内存不让非安全代码轻易访问。5.2 TRNG真正的随机数来源RP2350内置了一个真随机数发生器TRNG基于芯片内部多个环形振荡器的抖动采样。TRNG输出的随机数在很多安全流程中都是核心素材签名算法的nonce、密钥生成、会话ID、防重放机制等。如果你用的是伪随机数发生器PRNG种子一旦被攻击者猜到整个加密体系就崩了。RP2350提供的TRNG是硬件级随机源质量在MCU里算不错的。但在实际使用中有个隐藏问题TRNG刚上电时输出的第一位随机数质量不一定好。官方文档建议在正式提取随机数之前先丢弃前64个输出比如执行64次空读确保熵源稳定后再使用。这个小细节文档里写了但很多人不看直接取用TRNG的第一个值这在安全评审中会被判为严重缺陷。注意TRNG的初始化流程虽然简单但不要在中断上下文里调用阻塞式读取因为熵源采样可能需要等待。正确的做法是在系统初始化阶段就把足够的随机字节提取出来存到安全内存运行时从安全内存中取用。5.3 OTP里到底该存什么不该存什么OTP存储空间只有8KB非常有限每一字节都要精打细算。适合存入OTP的内容签名公钥RSA公钥或ECDSA公钥用于启动镜像验证。芯片唯一ID/批次信息用于设备身份识别。产品级配置例如“启用安全启动”“关闭调试接口”。设备证书如果格式足够紧凑。不适合存入OTP的内容大量固件本身空间不够固件应该在外部Flash中加密存储。对称加密密钥比如AES密钥——如果必须存建议通过安全世界的代码加密后再写入OTP至少不要让明文直接暴露。频繁变动的数据OTP只能写一次无法更新。这个取舍背后的思路很清晰OTP保存的是“信任根”的公共部分公钥、配置、身份私密部分私钥、对称密钥、会话密钥则存放在安全世界的SRAM或加密Flash中。公钥被读取问题不大因为公钥本身就是公开的攻击者拿到公钥也不能伪造签名私钥才是真正的机密。6. 开发实战在Pico 2上启用安全启动的完整笔记6.1 开发环境准备做RP2350安全开发需要的工具链跟普通Pico开发略有区别。我用的环境是树莓派Pico 2开发板官方sdkpico-sdk记得切到最新release分支Arm GCC工具链用于Cortex-M33CMake Ninja构建系统建议用VS Code搭配官方插件工程配置最省心树莓派官方otp命令行工具用于读写OTP开启安全启动之前建议先做一次完整的“正常启动”验证确保普通固件能在板子上跑起来。我见过不少人在配置安全启动的时候遇到问题排查半天发现其实是SDK版本太老、编译参数不对导致的跟安全功能无关。6.2 生成密钥对与烧录公钥启用签名启动的第一步是生成一对签名密钥。RP2350支持RSA和ECDSA P-256两种方案。ECDSA签名短、验签快、密钥生成也快我建议优先用ECDSA P-256。生成密钥可以用OpenSSL# 生成ECDSA P-256私钥 openssl ecparam -name prime256v1 -genkey -noout -out rp2350_ecdsa_private.pem # 从私钥导出公钥 openssl ec -in rp2350_ecdsa_private.pem -pubout -out rp2350_ecdsa_public.pub # 查看公钥内容 openssl ec -pubin -in rp2350_ecdsa_public.pub -text -noout生成之后公钥需要按照RP2350要求的格式写入OTP的Boot Key区域。树莓派官方提供了一个otp工具可以用下面这样的命令写入# 读取当前OTP内容做备份 otp read otp_backup.txt # 将公钥写入OTP具体命令取决于官方工具版本不同SDK版本有差异 otp write-boot-key ecdsa rp2350_ecdsa_public.pub注意这一步执行之后公钥就永久写入芯片了。在正式烧录前务必确认你的私钥备份安全——私钥丢了意味着这块芯片永远无法通过签名验证只能扔了。私钥泄露意味着攻击者可以伪造任何签名镜像。私钥管理是个严肃议题建议离线保存做好权限控制。6.3 在工程中启用安全启动SDK工程层面需要在CMakeLists.txt中开启签名启动选项并在编译时传入私钥路径。核心CMake配置大致如下cmake_minimum_required(VERSION 3.13) include(pico_sdk_init.cmake) project(secure_boot_demo C ASM) pico_sdk_init() add_executable(secure_boot_demo main.c ) # 启用安全启动 target_compile_definitions(secure_boot_demo PRIVATE PICO_BUILD_CMAKE1 PICO_SDK_CMAKE1 ) pico_set_binary_type(secure_boot_demo boot2) pico_set_secure_boot(secure_boot_demo # 指定私钥路径 SIGNING_KEY${CMAKE_CURRENT_SOURCE_DIR}/rp2350_ecdsa_private.pem )编译完成后会生成一个带签名的.uf2文件。这个文件不能直接用普通的拖拽到U盘方式烧录吗其实可以如果芯片当前还没开启“强制签名校验”Boot ROM仍然会接受这种带签名的镜像。关键区别在于如果OTP里已启用签名启动那么不带签名的普通固件将完全无法启动。6.4 第一次烧录的现场记录我实际测试的流程是这样的先把不带签名的普通固件烧进Pico 2确认板子正常。用otp工具写入ECDSA公钥到Boot Key区域。再通过J-Link或USB方式烧录带签名的启动镜像。复位用串口观察日志确认启动链验证通过应用正常跑起来。故意用一个损坏的签名的固件测试确认芯片拒绝启动并输出错误信息错误码通常是BROM_ERROR_BAD_SIGNATURE之类。这里要特别提醒在写入Boot Key之后、正式启用“强制签名启动”之前建议先在OTP中只写入公钥但保留启动模式为“兼容模式”让芯片同时接受“无签名镜像”和“有签名镜像”。这样即使签名固件出问题还能用普通固件回滚调试。等完全确认签名启动没问题了再把生命周期状态推进到强制签名把后路断掉。否则一旦启用强制签名而签名固件又跑不起来这块板子就变砖了。6.5 固件加密存储与外部Flash安全严格来说签名启动只能保证“固件不能被篡改”不能保证“固件内容不能被偷看”。如果有人用逻辑分析仪监听QSPI Flash总线依然能拿到完整的固件二进制然后离线分析、提取密钥。要防止这种被动窃取必须对固件做加密存储。RP2350做固件加密存储的方案常用的有两种一种是把固件分成多层启动引导层明文体积小负责在启动后从外部Flash读取加密的固件体解密到SRAM执行。另一种是用片内AES引擎如果RP2350后续SDK提供了相关封装配合OTP中的对称密钥直接加密整个应用固件区域。但要注意RP2350本身没有独立的AES硬件加速器AES运算只能走CPU软件模拟性能会差一些。解密1MB固件大约需要几百毫秒到一秒级别取决于CPU频率和实现优化程度。所以实际项目中我更推荐“加密签名”组合加密防止偷看签名防止篡改两者职责不同缺一不可。7. 常见问题与踩坑实录7.1 修改OTP配置后芯片无法启动这是我遇到过的最大的一坑。在一次测试中我修改了OTP中的启动模式把芯片切到了“强制签名”状态结果发现自己生成的签名固件里有个bug一运行就死机。这时芯片已经不再接受无签名固件我尝试通过SWD烧录普通固件也被拒绝——因为Boot ROM在引导阶段就卡住了根本进不到用户程序。解决办法是只能换一块新芯片前面那块OTP锁死无法恢复。所以再次强调量产前一定先用测试芯片跑通全部安全启动流程确认签名固件稳定运行一周以上再考虑正式烧OTP。手里多备几块测试板这种问题一旦发生时间成本极高。7.2 TrustZone配置后HardFault如何排查开启TrustZone后最常见的故障就是从Non-Secure世界调用Secure函数时触发HardFault。原因通常是NSC区域没有正确设置安全函数入口不在NSC区域中。SAU region配置错误安全内存地址和非安全内存地址重叠。调用约定不对SG指令后面的返回地址处理有误。排查方法先用v8M架构下的调试器看当前CPU的CONTROL寄存器中的SPSEL和NPRIV位确认是否处于预期状态再检查SAU寄存器的实际配置确认Region地址和属性是否正确。通常这类问题都是配置性错误跟RP2350本身无关。7.3 TRNG输出全零或固定值TRNG如果输出全零或者固定值大多数情况是初始化得太早——芯片刚上电、时钟还没稳定的时候就读取TRNG采样到的信号没有足够抖动熵源不足。解决办法就是延时等待或者多次读取丢弃前几个值。如果做了这些仍然异常检查一下是不是把TRNG的时钟源接错了或者触发了某种低功耗模式把TRNG电源关了。7.4 性能损耗的权衡思路安全功能和性能永远是矛盾的。RTOS任务切换、中断响应时间、内存访问延迟都会因为总线过滤和TrustZone的检查而增加。我的建议是在功能验证阶段先全部关掉安全功能跑通应用逻辑性能调优阶段再逐个开启每一步都记录性能数据量化损耗最后只保留必要的安全配置减少性能牺牲。这样“非必要不开、开通必有记录”的思路能让安全方案更可控也方便后期审计。8. 最后聊几句个人体会RP2350的安全体系把之前只有高端MCU才有的安全启动、TrustZone、物理防护、真随机数发生器全部下放到了几块钱的芯片上。这个事对嵌入式行业的影响是深远的——以后低成本物联网设备再也不能用“芯片太便宜所以不做安全”来搪塞了。但反过来说安全能力给了你用不用、怎么用完全取决于开发者。我见过很多人买了Pico 2只拿来点灯、跑RTOS完全没碰过安全功能那这颗芯片和RP2040的区别就只在性能层面了。我个人在实际操作中的体会是RP2350的安全配置虽然看起来复杂但只要把启动链的信任模型想清楚——OTP存什么、Secure World跑什么、Non-Secure World跑什么、哪些接口要关、哪些外设要保护——整个配置过程反而是清晰的。最怕的是没做安全架构规划盲目照抄官方Demo最后OTP烧进去改不回来才反应过来哪个环节理解错了。最后再分享一个小技巧在项目早期就把私钥的备份、权限管理、芯片OTP烧录记录做成清单。不要觉得这是大公司才需要做的事哪怕个人开发者一块测试板烧错了OTP都会非常难受。把流程标准化能省下的不只是一块芯片的钱还有排查问题的大把时间。