4. BootROM 安全机制:签名校验、抗回滚与调试锁定
BootROM 是整个安全启动链的“根”。它不可更改,出厂即固化。一旦 BootROM 被攻破,后面所有环节都是白搭。所以芯片厂商在 BootROM 里塞了三道硬关卡:签名校验、抗回滚、调试接口锁定。咱们一个一个拆开看。
4.1 签名校验:BootROM 如何确认下一级镜像可信?
BootROM 上电后第一件事,不是跳转到 Flash 执行代码,而是先验证镜像的签名。这个过程说白了就是:用公钥解签名,比对哈希。
具体流程我画了个图,你看一眼就明白了:
这里有个关键点:公钥存在哪? 我见过两种做法:
- 熔丝(eFuse):一次性写入,物理不可逆。适合量产锁定。
- ROM 硬编码:公钥直接刻在 BootROM 里。适合开放生态。
核心原则:BootROM 只信任它自己携带的公钥。任何外部传入的“公钥”都是不可信的。我曾经在审计一个 IoT 芯片时发现,某厂商把公钥放在 Flash 头部让 BootROM 读取——这等于把保险柜钥匙挂在门上。
4.2 签名算法选型:RSA 还是 ECC?
BootROM 里能塞的算法有限,ROM 空间寸土寸金。主流选择就两个:
| 特性 | RSA-2048 | ECC-256 (secp256r1) |
|---|---|---|
| 签名长度 | 256 字节 | 64 字节 |
| 验签速度 | 较慢(指数运算) | 较快(标量乘法) |
| ROM 代码量 | ~8 KB | ~4 KB |
| 抗量子攻击 | 弱 | 弱(但密钥更短) |
我个人习惯选 ECC。原因很简单:省空间、省时间。BootROM 每多 1 KB 代码,芯片成本就涨一截。而且 ECC 的签名短,Flash 头部开销也小。
避坑指南:我曾经遇到一个项目,选了 RSA-4096,结果 BootROM 验签花了 800 毫秒。客户要求冷启动 500 毫秒以内。最后只能切到 ECC-384 才过关。所以选算法时一定要算时间预算。
4.3 抗回滚机制:为什么不能刷旧版本?
签名校验只能保证镜像“是谁签的”,不能保证镜像“是不是最新版”。攻击者可以拿一个有漏洞的旧版本镜像,重新签名(如果私钥泄露)或者直接利用未修复的漏洞。
抗回滚机制就是解决这个问题的。常见做法有两种:
- 版本号熔丝:每次升级把版本号写入 eFuse,BootROM 比较镜像版本号 ≥ 熔丝值才放行。
- 单调计数器:芯片内部维护一个只增不减的计数器,镜像头部带版本号,BootROM 比较。
你想想看,如果熔丝烧了版本 5,你想刷版本 3 的镜像,BootROM 直接拒绝。这就是抗回滚的本质。
⚠️ 注意:版本号熔丝是一次性编程的。如果生产时烧错了版本号,芯片就永远无法升级到更低版本。我见过一个团队在产线测试时烧了 0xFF,结果后续所有正式版本都无法部署——因为版本号不可能比 255 大。最后只能报废一批芯片。
4.4 调试接口锁定:JTAG/SWD 的安全管理
调试接口是开发者的好朋友,也是攻击者的后门。通过 JTAG 或 SWD,攻击者可以:
- 读取 Flash 内容
- 单步执行 BootROM 代码
- 修改寄存器绕过校验
所以 BootROM 必须在启动早期就把调试接口锁死。锁定策略分三级:
| 等级 | 描述 | 适用场景 |
|---|---|---|
| Level 0 | 完全开放,无限制 | 开发板、EVK |
| Level 1 | 需要密码/挑战应答 | 预量产、小批量 |
| Level 2 | 永久锁定,不可恢复 | 量产设备 |
锁定时机也很讲究。我建议在 BootROM 完成签名校验后、跳转到下一级镜像前,立即锁定调试接口。为什么?因为校验通过说明镜像可信,此时锁定不会影响后续调试(如果镜像本身支持调试)。
实战经验:有一次客户反馈设备变砖,远程升级后无法启动。我查了日志,发现 BootROM 在锁定调试接口后,下一级镜像的校验失败了。但调试口已经锁死,无法通过 JTAG 读取任何信息。最后只能拆机用 SPI Flash 编程器读镜像——费了三天。所以我的建议是:调试接口锁定前,留一个“安全窗口”,比如等待 5 秒,期间可以通过特定 GPIO 电平阻止锁定。
4.5 三者的协同关系
签名校验、抗回滚、调试锁定不是孤立的。它们共同构成 BootROM 的安全三角:
- 签名校验:保证镜像来源可信
- 抗回滚:保证镜像版本最新
- 调试锁定:保证攻击者无法旁路前两者
缺一个都不行。没有抗回滚,攻击者可以刷旧版利用已知漏洞。没有调试锁定,攻击者可以直接 halt CPU 修改校验结果。没有签名校验……那前面两个都是摆设。
我的习惯:在做安全启动方案评审时,我会先检查 BootROM 是否同时实现了这三条。少一条就直接打回。这不是苛刻,是吃过亏后的本能反应。
4.6 常见故障排查清单
如果你遇到 BootROM 启动失败,按这个顺序排查:
- 检查公钥是否匹配:熔丝值是否烧录正确?ROM 代码是否对应正确的公钥索引?
- 检查签名格式:签名长度、填充方式(PKCS#1 v1.5 还是 PSS)是否与 BootROM 一致?
- 检查版本号:镜像版本号是否 ≥ 熔丝中的版本号?熔丝是否被意外烧写?
- 检查调试接口状态:是否被意外锁定?锁定等级是否符合预期?
- 检查 Flash 头部布局:签名、版本号、镜像起始地址是否在正确偏移?
嗯,这五步走完,90% 的问题都能定位。剩下的 10%,往往是芯片硬件 bug 或者 BootROM 本身的设计缺陷——那种情况就只能找芯片原厂了。