信任链模型:从硬件信任根到操作系统

安全启动这件事,说白了就是回答一个问题:你怎么证明你启动的代码是可信的?

答案不是靠某个软件拍胸脯保证,而是从硬件层面开始,一级一级往上验证。我习惯把这个过程叫做「信任链」——每一级都先度量下一级,再验证它,最后才把控制权交出去。

信任根:一切信任的起点

信任链必须有一个不可篡改的起点。这个起点就是硬件信任根(Root of Trust, RoT)。

在我参与过的项目中,最常见的信任根是固化在 SoC 内部的一段只读代码,通常叫 BootROM。它有几个关键特征:

  • 物理不可改——出厂后无法通过软件更新
  • 执行优先级最高——上电后 CPU 第一条指令就从这里取
  • 只做一件事——验证下一级 Bootloader 的签名

核心原则:信任根本身不能被验证,它是信任链的锚点。如果信任根被攻破,整个安全体系就崩塌了。

嗯,这里要注意。有些芯片厂商会把信任根放在片上 OTP(一次性可编程存储器)里,而不是 BootROM。OTP 的好处是可以定制公钥,但坏处是一旦写错就废了。我曾经见过一个项目,因为 OTP 烧录时校验位没算对,整批芯片只能报废——那叫一个心疼。

信任链的传递机制

信任链的传递,本质上是一个度量→验证→移交的循环。我画了一张图,你看完就明白了。

信任链传递机制流程图 信任链传递机制 BootROM(信任根) 度量:计算 Bootloader 的哈希值 → 验证:比对签名 Bootloader 度量:计算 OS Kernel 的哈希值 → 验证:比对签名 OS Kernel 信任链断裂点:任何一级验证失败,立即停止启动

你看这个流程,每一级都只信任上一级。BootROM 信任 Bootloader,但前提是 Bootloader 的签名必须通过。Bootloader 信任 OS Kernel,同样要验证签名。这就是链式信任的核心思想。

度量与验证:信任链的「安检」

度量(Measure)和验证(Verify)是两个不同的动作,很多人容易搞混。我帮你拆开讲。

动作 做什么 用什么 输出
度量 计算目标代码的哈希值 SHA-256 / SHA-384 摘要(digest)
验证 比对摘要与签名中的期望值 RSA / ECDSA 公钥 通过 / 拒绝

度量是「你是什么」,验证是「你是不是我允许的那个」。举个例子:

// 伪代码:信任链中的度量与验证
// 第1步:度量——计算下一级代码的哈希
digest = SHA256(bootloader_image);

// 第2步:验证——用公钥解密签名,比对摘要
expected_digest = RSA_Decrypt(signature, public_key);
if (digest == expected_digest) {
    // 验证通过,移交控制权
    jump_to(bootloader_entry);
} else {
    // 验证失败,进入安全失败模式
    halt_with_error();
}

实战经验:我在做车规级芯片的安全启动时,发现很多工程师只验证签名,不验证代码的完整性。结果有人篡改了代码但保留了原签名——虽然签名验证会失败,但系统没有做完整性检查,导致启动到一半崩溃。记住:签名验证 ≠ 完整性验证,两者都要做。

信任链的层级结构

一个典型的信任链通常包含 4 到 5 层。我列一个常见的结构给你看:

  1. 第0层:BootROM——硬件信任根,不可更改
  2. 第1层:SPL / 一级Bootloader——初始化内存,验证下一级
  3. 第2层:U-Boot / 二级Bootloader——加载内核,验证签名
  4. 第3层:OS Kernel——验证内核模块和驱动
  5. 第4层:用户空间——验证关键服务和应用程序

你想想看,如果每一层都只验证直接下一级,那攻击者能不能跳过某一层?比如直接篡改 OS Kernel,绕过 Bootloader 的验证?

答案是:不能。因为 Bootloader 在移交控制权之前,已经把 Kernel 的度量值存到了安全寄存器里。Kernel 启动后,会再次读取这个寄存器,确认自己是被合法加载的。这就是信任链的闭环。

避坑指南:我曾经在一个 IoT 项目里发现,工程师把公钥硬编码在 Bootloader 里,但 Bootloader 本身没有做完整性保护。攻击者修改了 Bootloader 里的公钥,用自己的私钥签了一个恶意内核——信任链瞬间崩塌。记住:公钥的完整性必须由上一级保证,不能自己保护自己。

信任链的异常阻断

信任链的另一个重要作用是异常阻断。当某一级验证失败时,系统不能继续启动,必须进入安全状态。

常见的阻断策略有三种:

  • 硬阻断——直接死循环或复位,不输出任何信息
  • 软阻断——进入恢复模式,等待外部干预(如 USB 刷机)
  • 日志阻断——记录失败原因后停机,便于调试

我个人习惯在产品阶段用硬阻断,开发阶段用日志阻断。为什么?因为硬阻断不给攻击者任何信息,但开发时你总得知道哪里出了问题。

嗯,这里有个细节。有些芯片支持熔断机制——如果检测到多次验证失败,就永久熔断某个 fuse,让设备永远无法启动。这招够狠,但确实有效。我在做金融终端的安全设计时就用过,效果很好。

信任链的局限性

信任链不是万能的。我遇到过几个坑:

  • 侧信道攻击——攻击者通过功耗或电磁泄露,窃取验证过程中的密钥
  • 回滚攻击——攻击者用旧版本的合法镜像替换新镜像,利用已知漏洞
  • 硬件篡改——直接物理修改 BootROM 或 OTP

针对回滚攻击,我建议在信任链中加入版本号验证。每次度量时,不仅要验证签名,还要检查版本号是否大于等于当前版本。这个逻辑很简单,但很多人会忘。

// 带版本号验证的信任链
if (signature_valid && version >= current_version) {
    // 验证通过
    update_current_version(version);
    jump_to(next_stage);
} else {
    // 验证失败
    halt();
}

总结一句话:信任链不是银弹,但它是最基础的安全防线。没有信任链,其他安全机制都是空中楼阁。