3、可信计算基础:TPM的架构与功能,PCR寄存器与度量存储
聊远程证明,绕不开TPM。这玩意儿说白了就是一颗专门干脏活累活的“安全小钢炮”。我早年做可信启动方案时,第一次拆开TPM的datasheet,说实话,头有点大。但后来摸熟了,发现它的设计逻辑其实很清晰——就是给你一个硬件级别的信任锚点。
3.1 TPM的物理架构
TPM是一个独立的硬件芯片,焊在主板上。它有自己的处理器、非易失性存储(NVRAM)、易失性内存,甚至还有独立的随机数发生器。你想想看,这意味着什么?意味着就算操作系统被攻破了,攻击者也拿不到TPM内部的数据。
核心设计原则:TPM的私钥永远不出芯片。签名、解密都在芯片内部完成。外界只能拿到结果,拿不到密钥本身。
我在项目中遇到过一种情况:客户觉得软件模拟TPM就够了,省成本。结果做渗透测试时,攻击者直接dump了内存中的“虚拟密钥”。嗯,从那以后,但凡涉及生产环境,我都坚持用物理TPM。
3.2 TPM的功能模块
TPM的功能可以归纳为几个关键模块。我习惯用一张表来对比,这样更直观:
| 功能模块 | 作用 | 我常用的场景 |
|---|---|---|
| RSA/ECC引擎 | 非对称加解密、签名验签 | 远程证明中的签名操作 |
| 随机数生成器 | 产生真随机数 | 生成密钥对、nonce |
| 哈希引擎 | SHA-1/SHA-256计算 | PCR扩展操作 |
| NVRAM | 持久化存储密钥、证书 | 存储EK(背书密钥) |
| PCR寄存器 | 平台度量值存储 | 度量启动链 |
这里有个关键点:TPM的密钥层级。最底层是EK(Endorsement Key),这是芯片出厂时烧录的,全球唯一。上面是SRK(Storage Root Key),用来保护用户密钥。我建议你把这个层级关系记牢,因为远程证明的信任链就是从这里开始的。
3.3 PCR寄存器:信任链的“账本”
PCR(Platform Configuration Register)是TPM里最核心的组件之一。你可以把它理解成一组“只写不读”的寄存器——不对,准确说是“只扩展不重置”。
为什么会这样设计?
因为PCR的更新方式很特殊。它不是简单的覆盖写入,而是:
PCR_new = SHA256(PCR_old || 新度量值)
这种“哈希扩展”机制保证了:
- 不可逆性:你无法从PCR值反推出历史度量数据
- 顺序依赖性:度量顺序变了,最终PCR值就变了
- 不可伪造性:除非你知道所有历史度量值,否则无法构造出特定的PCR值
我的经验:调试PCR扩展时,最容易犯的错误是搞混扩展顺序。我曾经因为BIOS版本升级导致度量顺序微调,结果远程证明一直失败。排查了两天才发现是PCR-0的值对不上。从那以后,我每次做度量启动方案,都会先打印一份完整的PCR扩展日志。
3.4 PCR寄存器的分配与用途
TPM 2.0规范定义了多个PCR寄存器,每个都有特定的用途。我整理了一份常用清单:
| PCR索引 | 度量内容 | 典型用途 |
|---|---|---|
| PCR-0 | BIOS/固件代码 | 验证固件完整性 |
| PCR-1 | 主板配置 | 检测硬件变更 |
| PCR-2 | Option ROM代码 | 验证扩展卡固件 |
| PCR-3 | UEFI启动管理器 | 验证启动配置 |
| PCR-4 | MBR/GPT引导记录 | 验证引导加载器 |
| PCR-5 | MBR/GPT分区表 | 检测分区篡改 |
| PCR-7 | 安全启动策略 | 验证签名策略 |
| PCR-8-15 | 操作系统组件 | 验证内核、驱动等 |
注意看,PCR-0到PCR-7通常由固件和引导加载器负责度量。操作系统启动后,会接管PCR-8及之后的寄存器。这种分层设计,说白了就是把信任链从硬件一直延伸到操作系统。
3.5 度量启动与远程证明的关系
度量启动(Measured Boot)是远程证明的基础。它的流程大致是这样的:
- BIOS度量自身:计算BIOS代码的哈希值,扩展到PCR-0
- BIOS度量Option ROM:扩展到PCR-2
- UEFI度量引导加载器:扩展到PCR-4
- 引导加载器度量内核:扩展到PCR-8
- 内核度量关键服务:扩展到PCR-9及之后
每一步的度量结果都“叠加”到对应的PCR中。最终,PCR的值就代表了整个启动链的“指纹”。
注意:度量启动只负责“记录”,不负责“阻止”。即使度量到恶意代码,系统仍然会启动。真正做决策的是远程证明服务端——它通过比对PCR值来判断平台是否可信。
我个人习惯把度量启动比作“黑匣子”。它不干预飞行,但一旦出事,你可以从黑匣子里还原出整个飞行过程。远程证明就是读取这个黑匣子,然后判断飞机是否被劫持过。
3.6 PCR值的读取与远程证明
远程证明时,验证方需要获取平台的PCR值。但这里有个问题:PCR值怎么安全地传给验证方?
直接传明文肯定不行,中间人一改就废了。所以TPM提供了一种机制——TPM2_Quote。
Quote操作的大致流程:
- 验证方生成一个随机数(nonce),发给被验证方
- 被验证方调用TPM2_Quote,传入nonce和要引用的PCR索引
- TPM用AIK(Attestation Identity Key)对PCR值+nonce进行签名
- 被验证方把签名结果和PCR值一起发给验证方
- 验证方验签,确认PCR值没有被篡改,且是实时生成的
这里nonce的作用是防止重放攻击。我建议你在设计远程证明协议时,一定要加上nonce校验。曾经有团队因为忘了加nonce,导致攻击者用旧的PCR值骗过了验证服务。
3.7 一张图看懂TPM与PCR的关系
下面这张图展示了TPM内部的核心组件以及PCR在度量启动中的位置。我画的时候特意把信任链的流向标清楚了。
从这张图可以看得很清楚:左侧是TPM芯片内部的硬件模块,右侧是度量启动的信任链。PCR寄存器就是连接两者的桥梁——它接收来自启动链的度量值,通过哈希扩展不断累积。最终,这些PCR值就是远程证明时用来判断平台是否可信的依据。
3.8 实践中的注意事项
最后,分享几个我在实际项目中踩过的坑:
- PCR重置问题:系统重启或休眠唤醒时,部分PCR会被重置。如果你在远程证明中依赖PCR值,一定要确认当前PCR状态是否有效。
- TPM 1.2 vs 2.0:TPM 2.0的PCR数量更多(24个 vs 16个),且支持SHA-256。老项目迁移时要注意兼容性。
- 虚拟TPM:云环境通常使用vTPM。vTPM的PCR值由Hypervisor管理,安全性取决于Hypervisor的可信度。我建议在云环境中结合Intel SGX或AMD SEV一起使用。
- PCR引用策略:不要一股脑把所有PCR都引用了。只引用与你关心的组件相关的PCR。比如只验证内核完整性,就只引用PCR-8到PCR-11。
我的习惯:每次部署远程证明方案前,我都会先在测试环境跑一遍完整的度量启动,记录所有PCR的基准值。然后故意篡改一个组件(比如替换内核文件),看PCR值是否变化。这个“破坏性测试”能帮你快速验证度量链是否完整。
嗯,TPM和PCR的内容就讲到这里。记住一句话:TPM是信任的根,PCR是信任的账本。远程证明就是拿着这个账本去证明——我的平台没有被篡改过。