4、根信任建立:硬件信任根(RoT)、一次性可编程存储器(OTP/fuse)、公钥哈希存储
安全启动,说白了就是「信任的传递」。但有个问题——链条的起点在哪?谁又来信任第一个验证者?
这就是我们今天要聊的核心:硬件信任根(Root of Trust, RoT)。它是一切的起点,是安全启动链条上那个「绝对可信」的锚点。我个人习惯把它比作「信任的种子」——这颗种子必须是物理上不可篡改的,否则整个安全体系就是空中楼阁。
4.1 硬件信任根:为什么必须是硬件?
你想想看,如果信任根放在Flash里,攻击者只要把Flash拆下来,用编程器一读一写,你的「信任」就崩塌了。所以,信任根必须固化在芯片内部,且无法通过软件或物理手段修改。
硬件信任根通常由以下几部分组成:
- 不可变存储:存放公钥哈希或根证书,比如一次性可编程存储器(OTP/fuse)。
- 固定逻辑:芯片上电后,第一段执行代码(BootROM)是掩膜在硅片上的,无法更改。
- 专用硬件引擎:比如硬件哈希计算模块、公钥加速器,防止软件层面的侧信道攻击。
核心原则:信任根必须是「不可伪造、不可篡改、不可绕过」的。任何软件层面的安全机制,最终都要追溯到这颗硬件种子。
我在项目中遇到过一件事:某款芯片的BootROM存在一个漏洞,攻击者可以通过特定的GPIO电平组合跳过公钥校验。这就是典型的「信任根被绕过」——硬件逻辑设计有缺陷,再强的密码学算法也白搭。
4.2 一次性可编程存储器(OTP/fuse):芯片的「身份证」
OTP,全称One-Time Programmable,中文叫一次性可编程存储器。在芯片领域,大家更习惯叫它fuse(熔丝)。为什么叫熔丝?因为早期的OTP就是靠烧断芯片内部的金属丝来实现「写0」操作的——烧断了就回不来了。
现在的OTP技术虽然不再真的「烧熔丝」,但原理类似:每个bit只能从默认值(通常是1)编程为0,且不可逆。这就保证了公钥哈希一旦写入,就永远无法被篡改。
4.2.1 OTP的典型用途
| 用途 | 说明 | 我的经验 |
|---|---|---|
| 公钥哈希存储 | 存放根公钥的哈希值(SHA-256等),用于验证Bootloader签名 | 建议至少预留2个OTP区域,用于密钥轮换 |
| 芯片唯一ID | 每颗芯片独一无二的序列号,用于设备身份认证 | 有些厂商把UID和公钥哈希绑定,增加破解难度 |
| 安全配置位 | 控制调试接口(JTAG/SWD)是否开放、是否允许回退到旧版本 | 量产前一定要确认这些位烧录正确,否则无法返工 |
| 生命周期管理 | 标记芯片处于「开发态」「生产态」「RMA态」等 | 开发态下调试接口全开,生产态下必须锁死 |
警告:OTP烧录是一次性的,烧错了就是废片。量产前务必在开发板上反复验证烧录脚本和校验流程。我曾经见过一个团队因为烧录脚本的字节序搞反了,导致3000片芯片全部报废——那叫一个惨。
4.2.2 OTP的物理安全考量
OTP虽然不可改写,但物理攻击仍然是个威胁。攻击者可以用聚焦离子束(FIB)探针直接读取OTP单元的状态,或者用激光照射改变熔丝状态。所以,高端安全芯片会在OTP上层覆盖金属屏蔽层,一旦被破坏,芯片会自动擦除敏感数据。
嗯,这里要注意:OTP的物理安全等级直接决定了整个安全启动的强度。如果你的产品只卖几百块,攻击者犯不着用FIB去读你的公钥哈希;但如果是金融支付终端、车规级ECU,那就必须上主动防护罩了。
4.3 公钥哈希存储:为什么存哈希,不存公钥本身?
这个问题我经常被问到。直接存公钥不行吗?非要存个哈希,多此一举?
原因有两点:
- OTP空间有限:一颗RSA-2048公钥有2048bit(256字节),而SHA-256哈希只有256bit(32字节)。OTP是按bit算钱的,能省则省。
- 安全冗余:公钥本身如果泄露,攻击者虽然不能直接伪造签名,但可以针对公钥进行密码分析。存哈希的话,攻击者连公钥长什么样都不知道——除非他能从哈希反推出公钥,这在计算上是不可能的。
具体流程是这样的:
- 烧录阶段:在安全环境下(比如产线),计算根公钥的SHA-256哈希,写入OTP。
- 验证阶段:BootROM从Flash读取公钥,计算其哈希,与OTP中的哈希比对。一致则信任该公钥,不一致则拒绝启动。
小技巧:我建议在公钥哈希比对时加入「抗毛刺」设计——连续比对3次,任意两次一致即通过。这样可以防止电压波动或电磁干扰导致的单次误判。别问我怎么知道的,都是泪。
4.4 根信任建立的整体流程
说了这么多,我们画张图把整个流程串起来。这张图我每次讲课时都会画,因为它把「信任的种子」到「信任的传递」讲得清清楚楚。
4.5 实际项目中的避坑指南
做安全启动这么多年,我踩过的坑不少。挑几个典型的说说:
- OTP烧录顺序:有些芯片的OTP区域是分块的,必须先烧录配置位,再烧录数据位。顺序搞反了,配置位锁死了数据位区域,你就再也写不进去了。我建议在产线脚本里加一个「预检查」步骤,先读OTP状态,确认可写再执行烧录。
- 公钥哈希长度:别为了省OTP空间用SHA-1。虽然SHA-1只有160bit,但现在已经不安全了。老老实实用SHA-256,256bit的OTP空间,大部分芯片都负担得起。
- 多级信任链:如果Bootloader分多级(比如SPL→U-Boot→Kernel),每一级的公钥哈希都要存吗?不一定。我习惯的做法是:只存根公钥哈希,后续各级公钥由上一级签名验证。这样OTP只需要一个哈希值,信任链自然传递下去。
- 回滚保护:OTP里最好留一个「最小安全版本号」的区域。每次升级Bootloader时,版本号只能增加不能减少。这样攻击者就无法把Bootloader回滚到有漏洞的旧版本。
个人经验:量产前,一定要做一次「破坏性测试」——故意烧录一个错误的公钥哈希,看看芯片是不是真的拒绝启动。我见过有厂商的BootROM代码有bug,哈希比对结果取反了,导致「正确的公钥被拒绝,错误的公钥反而通过」。这种低级错误,一次测试就能发现。
4.6 小结
硬件信任根是整个安全启动的基石。OTP/fuse是它的物理载体,公钥哈希是它的逻辑载体。三者缺一不可。
记住一句话:信任不能靠软件保证,必须靠硬件固化。只要OTP里的哈希值不被篡改,你的安全启动链条就是完整的。至于后续的Bootloader签名验证、内核校验,那都是在这个信任根上生长出来的枝叶。