信任根(RoT)设计:硬件信任根与软件信任根
信任根,说白了就是整个安全体系的「第一块基石」。你想想看,如果连最底层的信任都不可靠,上面堆再多安全措施也是白搭。我在做安全启动方案时,第一步永远是先把 RoT 定下来。
硬件信任根 vs 软件信任根
这两者的区别,我习惯用一个比喻来解释:硬件信任根就像你家的地基,软件信任根就像地基上砌的墙。地基不牢,墙再高也没用。
| 类型 | 存储位置 | 不可篡改性 | 典型实现 |
|---|---|---|---|
| 硬件信任根 | 芯片内部(eFuse/OTP) | 物理不可更改 | 公钥哈希、芯片唯一ID |
| 软件信任根 | BootROM / 不可变Flash | 逻辑不可更改 | BootROM代码、签名校验逻辑 |
硬件信任根,我一般把它放在 eFuse 或 OTP 里。这些存储单元出厂后只能写一次,或者根本不能写。你想想看,攻击者想改?除非他把芯片拆了。
软件信任根呢,通常是固化在 BootROM 里的代码。BootROM 是芯片上电后执行的第一段代码,它本身是只读的。嗯,这里要注意:BootROM 虽然叫「软件」,但它存储在硬件只读区域里,所以本质上还是靠硬件保护。
核心原则:硬件信任根提供「不可伪造的身份」,软件信任根提供「不可绕过的校验逻辑」。两者缺一不可。
eFuse 与 OTP 存储
这两个东西,我刚开始做的时候也搞混过。其实它们都是「一次可编程」存储,但实现方式不同。
- eFuse: 通过熔断金属丝来存储数据。编程后不可恢复。我见过一个项目,因为 eFuse 编程电压没控制好,直接把整批芯片烧废了……后来我们加了电压监控电路。
- OTP: 通过击穿栅氧化层来存储数据。比 eFuse 更稳定,但面积更大。我个人习惯在需要存储较大数据量时用 OTP。
实际项目中,eFuse 和 OTP 经常配合使用。比如:
// 伪代码:信任根初始化流程
if (eFuse_is_blank()) {
// 首次启动,写入公钥哈希到 eFuse
write_eFuse(PUBLIC_KEY_HASH, hash_value);
// 锁定 eFuse,防止后续修改
lock_eFuse(PUBLIC_KEY_HASH);
} else {
// 正常启动,读取 eFuse 中的公钥哈希
hash = read_eFuse(PUBLIC_KEY_HASH);
// 用这个哈希校验 BootROM 中的公钥
if (!verify_public_key(hash)) {
halt(); // 校验失败,死循环
}
}
警告:eFuse 编程时一定要做「读回校验」。我曾经遇到过编程电压波动导致位翻转,结果芯片变成了「砖头」——再也无法启动。
信任根建立流程
整个流程,我画了一张图,你看完就明白了:
这个流程,我拆成几个关键步骤:
- 芯片上电: 复位后,PC 指针直接指向 BootROM 入口地址。这一步是硬件强制行为,软件无法干预。
- BootROM 执行: 第一件事就是读取 eFuse 中存储的公钥哈希。这个哈希是芯片出厂时烧录的,代表「这个芯片是谁家的」。
- 公钥校验: BootROM 用 eFuse 里的哈希,去校验自己代码段里嵌入的公钥。如果哈希匹配,说明公钥没被篡改。
- 信任链传递: 公钥校验通过后,BootROM 用这个公钥去校验下一级 Bootloader 的签名。以此类推,形成一条信任链。
经验之谈:我建议在 eFuse 里至少预留 8 个字节的「厂商自定义区域」。有些客户会要求写入芯片序列号或生产日期,方便后期追溯。预留空间能省很多麻烦。
信任链的层级结构
信任根建立后,剩下的就是「链式传递」。我习惯用三层结构:
| 层级 | 内容 | 校验方式 | 存储介质 |
|---|---|---|---|
| 第0层 | BootROM + eFuse | 硬件强制 | ROM / eFuse |
| 第1层 | 一级Bootloader | RSA/ECDSA签名校验 | Flash |
| 第2层 | 操作系统内核 | 哈希校验 + 签名校验 | Flash / 外部存储 |
每一层只信任上一层的校验结果。第0层是硬件信任根,它校验第1层;第1层校验第2层。攻击者想绕过?除非他能物理修改 eFuse 或 BootROM——这在现代芯片上几乎不可能。
避坑指南:我曾经遇到一个项目,工程师把公钥直接硬编码在 BootROM 里,却没有用 eFuse 做哈希校验。结果攻击者通过电压毛刺攻击,让 BootROM 跳过了公钥校验步骤,直接加载了恶意 Bootloader。从那以后,我坚持「公钥必须由 eFuse 保护」。
嗯,信任根的设计,说到底就是一句话:用物理不可更改的硬件,保护逻辑不可绕过的软件。 你把这个原则吃透了,安全启动的骨架就搭起来了。