3、安全启动链:从BootROM到应用层的信任链建立,签名验证与回滚保护机制

大家好,我是老李。今天咱们聊聊安全启动链。说实话,这个主题我特别有感触。几年前我接手过一个灌溉控制器的项目,客户说设备被破解了,固件被人提取出来逆向分析,还改了通信协议。嗯,那叫一个惨。从那以后,我对安全启动的重视程度直接拉满。

安全启动链,说白了就是一条信任链。从芯片上电的第一行代码开始,每一级都要验证下一级的合法性。只要有一环断了,系统就不启动。你想想看,这就像接力赛,每一棒都要确认下一棒是自己人。

3.1 信任链的起点:BootROM

BootROM是芯片出厂时固化在ROM里的代码。它不可修改,所以是信任的根。我个人习惯把BootROM叫做“硬件信任锚点”。为什么?因为它是整个安全体系的基石。

BootROM上电后第一件事,就是验证下一级Bootloader的签名。它手里有一把公钥,这把公钥要么是芯片厂商烧进去的,要么是我们在产线里写进去的。验证通过,才把控制权交出去。

核心要点:BootROM的代码必须足够小、足够简单。越复杂越容易出漏洞。我见过一些芯片,BootROM里塞了太多功能,结果被黑客找到了缓冲区溢出漏洞,直接跳过了签名验证。

3.2 签名验证:用数学保证信任

签名验证用的是非对称加密。简单说,就是一对钥匙:私钥签名,公钥验证。私钥只有我们开发团队有,公钥放在设备里。

流程是这样的:

  1. 编译固件,生成二进制文件
  2. 用私钥对固件做哈希,然后加密,生成签名
  3. 把签名附加到固件后面
  4. 设备启动时,用公钥解密签名,对比哈希值

我在项目中遇到过一个问题:有人把公钥放在了Flash里,结果被黑客读出来了。黑客用自己的私钥重新签名了恶意固件,然后替换了公钥。嗯,这相当于把锁换了,钥匙当然能开。所以,公钥一定要放在一次性写入的区域,比如eFuse或者OTP。

我的建议:签名算法推荐用ECDSA,比RSA效率高,密钥也短。对于资源受限的MCU,ECDSA P-256是个不错的选择。

3.3 多级启动:每一级都要验

现在的嵌入式系统,启动过程通常分好几级:

启动阶段 验证者 被验证者
BootROM 硬件 一级Bootloader
一级Bootloader BootROM 二级Bootloader
二级Bootloader 一级Bootloader 操作系统内核
操作系统内核 二级Bootloader 应用层固件

每一级都要验下一级的签名。我曾经见过一个设计,BootROM只验了一级Bootloader,后面的都不验了。结果黑客攻破了一级Bootloader,然后加载了任意代码。这就像只检查了门口的保安,后面的人随便进。

3.4 回滚保护:防降级攻击

回滚保护,很多人容易忽略。你想想看,如果黑客拿到了一个旧版本的固件,里面有个已知漏洞,他能不能把设备降级到那个版本?

答案是:如果没有回滚保护,完全可以。

回滚保护的实现方式有两种:

  • 版本号检查:固件里带一个版本号,Bootloader检查新固件的版本号必须大于等于当前版本。版本号存在一次性写入区域,比如eFuse。
  • 安全计数器:每次升级,计数器加一。Bootloader检查新固件的计数器值必须大于当前值。这个更安全,因为版本号可能被伪造,但计数器是硬件维护的。

注意:版本号检查有个坑。如果版本号存在Flash里,黑客可以改掉。我曾经遇到一个客户,版本号存在外部Flash里,结果被黑客直接改成了0xFFFFFFFF,然后刷入了旧固件。所以,版本号或者计数器,一定要放在安全存储里。

3.5 实践中的避坑指南

做安全启动链,有几个坑我踩过,分享给大家:

  • 公钥存储位置:别放Flash,放eFuse或者OTP。如果芯片没有这些,那就用芯片的唯一ID做密钥派生。
  • 签名算法选择:别用MD5或者SHA-1,已经被攻破了。用SHA-256起步。
  • 回滚保护:别只依赖软件检查,硬件计数器更靠谱。
  • 调试接口:量产时一定要关闭JTAG/SWD。我见过有人开着调试接口,黑客直接连上读取了固件。

一句话总结:安全启动链,每一环都要锁死。从BootROM到应用层,信任不能断。签名验证保证代码没被改,回滚保护保证代码是最新的。两者缺一不可。

好了,这一章就聊到这儿。下一章咱们讲讲固件加密和密钥管理,那又是一个大坑。到时候见。