第二章:信任链模型——从BootROM到操作系统的完整信任链

大家好,我是你们的嵌入式安全讲师。今天我们来聊聊信任链模型。说实话,这是整个安全启动体系里最核心的概念,没有之一。

我经常跟团队里的新人说:安全启动的本质,就是建立一条从硬件到软件的信任传递链条。你想想看,如果连最底层的代码都不值得信任,那上层再安全又有什么用?

2.1 什么是信任链?

信任链,说白了就是一级验证一级,环环相扣的验证机制。从芯片上电的那一刻起,BootROM作为第一级信任根,开始验证下一级代码的完整性和合法性。验证通过后,控制权才交给下一级。

我习惯把这个过程比作「接力赛」——每一棒都要确认下一棒的身份,才能把接力棒传出去。任何一个环节出问题,整个链条就断了。

信任链的核心原则:

  • 最小信任原则:只信任硬件固化的BootROM
  • 逐级验证原则:每一级只验证下一级
  • 不可逆原则:验证失败后不能回退到不安全状态

2.2 完整的信任链:从BootROM到操作系统

我们来看一个典型的嵌入式设备信任链。嗯,这里我以ARM TrustZone架构为例,因为我在项目中用得最多。

层级 组件 验证对象 存储位置
第1级 BootROM 信任根,不可修改 芯片内部ROM
第2级 BootROM → Bootloader (SPL) 验证SPL签名 片上SRAM
第3级 SPL → 主Bootloader (U-Boot) 验证U-Boot签名 DDR
第4级 U-Boot → 内核 验证内核镜像签名 DDR
第5级 内核 → 文件系统 验证根文件系统完整性 DDR/Flash

我曾经在一个IoT网关项目里,遇到过客户说「我们只需要验证内核就够了」。结果呢?攻击者直接替换了U-Boot,加载了一个带后门的内核。嗯,从那以后我坚持:信任链必须从BootROM开始,一级都不能少

2.3 信任传递机制

信任传递是怎么实现的?核心就是数字签名验证。每一级代码都携带一个数字签名,下一级用公钥去验证这个签名。

具体流程是这样的:

  1. BootROM:芯片上电,执行固化在ROM里的代码。它内置了芯片厂商的公钥(或者公钥哈希)。
  2. 验证Bootloader:BootROM从Flash读取Bootloader镜像,用内置公钥验证其签名。验证通过,跳转到Bootloader执行。
  3. 验证内核:Bootloader从存储介质读取内核镜像,用自身携带的公钥验证内核签名。验证通过,加载内核。
  4. 验证文件系统:内核启动后,验证根文件系统的完整性(通常用dm-verity或IMA机制)。

我的经验之谈:

公钥的存储位置很关键。我建议把公钥哈希固化在BootROM里,公钥本身放在Bootloader中。这样既保证了灵活性,又防止了公钥被篡改。

2.4 度量启动 vs 验证启动

这两个概念经常被混淆。我刚开始做安全启动时也搞混过,后来在一个项目里踩了坑才彻底搞明白。

2.4.1 度量启动(Measured Boot)

度量启动的核心思想是:我不阻止你运行,但我记录你的状态

具体做法是:每一级代码启动前,计算它的哈希值,然后把这个哈希值扩展到平台配置寄存器(PCR)中。操作系统启动后,可以读取PCR值,判断系统是否被篡改过。

我举个例子:

// 伪代码:度量启动流程
BootROM:
    hash_bootloader = SHA256(bootloader_image)
    TPM_PCR_Extend(PCR_0, hash_bootloader)
    jump_to(bootloader)

Bootloader:
    hash_kernel = SHA256(kernel_image)
    TPM_PCR_Extend(PCR_1, hash_kernel)
    jump_to(kernel)

Kernel:
    // 读取PCR值,判断系统状态
    if (TPM_PCR_Read(PCR_0) != expected_value) {
        // 系统被篡改,但内核仍然运行
        log_alert("System integrity compromised")
    }

度量启动的优点是灵活——即使系统被篡改,也能启动起来,只是记录下异常。适合需要远程取证分析的场景。

2.4.2 验证启动(Verified Boot)

验证启动就严格多了:验证不通过,直接拒绝启动

每一级代码在加载下一级之前,必须验证其数字签名。签名无效,系统就卡住或者进入恢复模式。

// 伪代码:验证启动流程
BootROM:
    if (!verify_signature(bootloader_image, bootrom_public_key)) {
        halt("Bootloader signature invalid")
    }
    jump_to(bootloader)

Bootloader:
    if (!verify_signature(kernel_image, bootloader_public_key)) {
        halt("Kernel signature invalid")
    }
    jump_to(kernel)

注意:验证启动的缺点是「一刀切」。我曾经在一个医疗设备项目里,因为签名验证失败导致设备变砖,现场工程师不得不拆机烧录。所以,一定要设计好恢复机制

2.4.3 两者的对比

特性 度量启动 验证启动
核心目标 记录系统状态 阻止未授权代码运行
验证方式 哈希度量,不验证签名 数字签名验证
失败处理 继续启动,记录异常 拒绝启动,进入恢复模式
安全性 中等(依赖远程验证) 高(本地强制验证)
适用场景 云服务器、取证设备 消费电子、汽车、医疗

2.5 实际项目中的选择建议

我个人习惯的做法是:验证启动为主,度量启动为辅

什么意思呢?就是BootROM到内核这一路,用验证启动,确保核心系统没有被篡改。内核启动后,再用度量启动来监控文件系统和运行时环境。

我曾经在一个车载信息娱乐系统项目里,就是这么设计的。BootROM验证U-Boot,U-Boot验证内核,内核启动后用IMA(Integrity Measurement Architecture)度量文件系统。既保证了启动安全,又兼顾了运行时的监控。

总结一下:

  • 信任链从BootROM开始,逐级验证,环环相扣
  • 信任传递靠数字签名,公钥要安全存储
  • 度量启动记录状态,验证启动阻止运行
  • 实际项目中,两者结合使用效果最好

下一章,我们会深入讨论BootROM的设计和实现。说实话,BootROM是整个信任链的基石,写错了就全完了。到时候我会分享一些我在芯片原厂工作时踩过的坑,敬请期待。