2、硬件信任根:BootROM原理、eFuse与OTP配置、物理不可克隆函数(PUF)
大家好,我是你们这节课程的讲师。咱们今天聊的,是整个安全启动体系的基石——硬件信任根。说白了,就是芯片上电后,第一个“信得过”的东西是什么?它凭什么信得过?
我做了这么多年嵌入式安全,见过太多因为信任根没扎牢,导致整个系统被攻破的案例。你想想看,如果芯片刚上电跑的第一段代码就是不可信的,那后面再怎么加固都是白搭。所以,这一章咱们得把底层的几个关键模块彻底搞清楚。
2.1 BootROM:芯片的第一口“奶”
芯片上电后,CPU 从哪开始执行代码?答案是 BootROM。这是一块固化在芯片内部的只读存储器,出厂就写死了,谁也改不了。
核心要点:BootROM 是信任链的起点。它不可篡改,所以它说的话,芯片无条件相信。
BootROM 里通常放了什么?我给大家拆解一下:
- 最基本的硬件初始化:比如设置时钟、配置内存控制器。没有这一步,芯片就是个“植物人”。
- 启动介质选择逻辑:它要去检查是从 SPI Flash 启动,还是从 NAND、SD 卡,甚至是 USB 下载。我记得有个项目,就是因为 BootROM 里启动介质优先级没配好,导致量产时一批板子死活起不来。
- 安全验证的入口:这是咱们今天的重点。BootROM 里会包含公钥或者哈希值,用来验证下一级镜像(通常是 SPL 或 U-Boot)的签名。
这里有个容易踩的坑。BootROM 虽然不可改,但它的执行环境是确定的。攻击者可以通过分析 BootROM 的代码逻辑,找到漏洞。比如,通过电压毛刺(Glitch)攻击,让 BootROM 跳过签名验证的步骤。
避坑指南:我曾经在一个 IoT 芯片项目里,发现 BootROM 的签名验证循环存在一个边界条件没处理好。攻击者可以构造一个超长的镜像头,导致缓冲区溢出,直接跳转到攻击代码。所以,BootROM 的代码审查,一定要像看银行金库的监控录像一样仔细。
2.2 eFuse 与 OTP:一次性写入的“身份证”
BootROM 是只读的,但芯片需要一些“个性化”的信息。比如,每颗芯片独有的密钥、芯片的版本号、是否开启了安全调试功能。这些信息不能放在 Flash 里,因为 Flash 可以被擦除。这时候,就需要 eFuse 和 OTP 上场了。
它们俩本质上都是“一次性可编程存储器”。写进去就改不了,像给芯片烙上一个永久的印记。
| 特性 | eFuse | OTP (One-Time Programmable) |
|---|---|---|
| 物理原理 | 通过大电流熔断金属丝(类似保险丝) | 通过击穿栅氧化层(反熔丝) |
| 编程方式 | 需要外部高压或内部电荷泵 | 通常需要更高的编程电压 |
| 可靠性 | 熔断后电阻变化明显,但可能“再生长” | 击穿后不可逆,可靠性更高 |
| 典型容量 | 几百到几千比特 | 几比特到几十比特 |
| 常见用途 | 存储密钥、芯片ID、安全配置位 | 存储一次性密码、熔断调试接口 |
我个人习惯把 eFuse 比作芯片的“基因”。它决定了这颗芯片是“普通版”还是“安全增强版”。比如,我们会在 eFuse 里烧录一个“安全启动使能位”。一旦这个位被置为 1,BootROM 就会强制校验所有后续镜像的签名。如果这个位是 0,芯片就跟普通单片机一样,随便什么代码都能跑。
小技巧:在量产时,我建议把 eFuse 的编程分成两步。第一步,在芯片贴片前,先烧录芯片 ID 和部分公共密钥。第二步,在整机测试通过后,再烧录“安全使能位”。这样即使第一步出了问题,芯片还能返修。一旦烧了使能位,芯片就“锁死”了,只能报废。
2.3 物理不可克隆函数 (PUF):芯片的“生物指纹”
eFuse 虽然好,但它有个致命弱点:密钥是明文存储在芯片里的。攻击者可以用电子显微镜、聚焦离子束(FIB)等物理手段,直接读出 eFuse 里的比特位。这就像把密码写在纸上,然后锁在保险柜里——保险柜再结实,也怕有人把墙挖开。
PUF 的出现,就是为了解决这个问题。它不存储密钥,而是“生成”密钥。怎么生成?利用芯片制造过程中不可避免的物理差异。
你想想看,同一片晶圆上,不同位置的晶体管,它们的阈值电压、延迟时间、甚至氧化层厚度都有微小的随机差异。PUF 就是把这些微观差异“放大”,变成一个独一无二的、不可克隆的“指纹”。
常见的 PUF 类型有几种:
- SRAM PUF:利用 SRAM 上电时,每个比特位随机初始化为 0 或 1 的特性。这是最常用的一种,因为几乎任何芯片都有 SRAM。
- 环形振荡器 PUF (RO PUF):测量多个环形振荡器的频率差异。因为工艺偏差,每个振荡器的频率都略有不同。
- 仲裁器 PUF (Arbiter PUF):让两个信号沿着不同的路径传播,比较谁先到达终点。路径的延迟差异就是指纹。
关键区别:eFuse 是“存储”密钥,PUF 是“生成”密钥。PUF 生成的密钥只在芯片上电时存在,掉电即消失。攻击者即使物理剖片,也拿不到密钥。
我在一个金融支付终端项目里用过 PUF。当时客户要求密钥必须“零存储”。我们用 SRAM PUF 生成根密钥,然后用这个根密钥去解密存储在 Flash 里的其他密钥。即使攻击者读出了 Flash 里的密文,没有 PUF 生成的根密钥,也毫无办法。
不过,PUF 也不是完美的。它有个“可靠性”问题。因为温度、电压、芯片老化都会影响 PUF 的输出。今天生成的密钥,明天可能因为温度变化,有几个比特位就翻掉了。
避坑指南:我曾经在一个项目中,PUF 的误码率高达 5%。这意味着每次生成的密钥都不一样。解决方案是引入“辅助数据”和“纠错码”。在芯片注册阶段,生成一个 Helper Data,它记录了 PUF 输出的冗余信息。在重构密钥时,用 Helper Data 和纠错算法,把 PUF 的输出“纠正”回原始的密钥。这个 Helper Data 可以公开存储,因为它不泄露密钥的任何信息。
2.4 三者如何协同工作?
好了,咱们把三个核心组件都讲了一遍。它们不是孤立的,而是紧密配合的。我画了一张图,帮大家理清关系。
流程其实很清晰:
- 芯片上电,CPU 从 BootROM 开始执行。
- BootROM 读取 eFuse 中的安全使能位。如果使能了,就进入安全启动模式。
- 如果需要,BootROM 调用 PUF 模块,生成根密钥。这个密钥不会离开芯片内部总线。
- BootROM 使用 eFuse 里存储的公钥(或 PUF 生成的密钥),去验证下一级镜像(比如 SPL)的签名。
- 验证通过,才把控制权交给下一级镜像。信任链就这样一级一级传递下去。
个人经验:我建议在设计安全启动方案时,优先考虑“eFuse + PUF”的组合方案。eFuse 负责存储不可变的安全策略(比如“是否强制校验”),PUF 负责生成动态的密钥。这样即使 eFuse 被物理读取,攻击者拿到的也只是一堆配置位,而不是真正的密钥。