TPM 2.0 基础:架构、PCR、密钥与远程证明原语
各位同学,今天我们来啃一块硬骨头——TPM 2.0。说实话,我刚接触这东西的时候也觉得头大,一堆缩写、一堆寄存器、一堆密钥类型。但后来我发现,只要抓住三条主线:信任根、度量链、证明原语,整个 TPM 就清晰了。
这一章,我会带你把 TPM 2.0 的核心组件拆开揉碎。咱们不讲大而全的规范,只讲远程证明里真正用得上的东西。
TPM 2.0 架构概览
TPM 2.0 本质上是一个独立的密码协处理器。它有自己的 CPU、内存、非易失性存储,还有一堆硬件防篡改机制。你想想看,主机的操作系统可能被攻破,但 TPM 里的秘密,攻击者拿不到。
我个人习惯把 TPM 2.0 的架构分成三层来看:
- 接口层:通过 LPC、SPI 或 I²C 总线与 CPU 通信,命令格式是 TPM 2.0 标准定义的 TPM Command/Response Buffer。
- 核心服务层:包括随机数生成器、哈希引擎(SHA-1/SHA-256/SM3)、非对称引擎(RSA/ECC)、对称引擎(AES)。
- 存储与密钥管理层:PCR 寄存器、NVRAM 区域、密钥层次结构(Hierarchy)。
关键认知:TPM 2.0 不是用来做高速运算的。它的设计哲学是「慢但安全」。我在项目中见过有人试图用 TPM 做大量签名,结果性能惨不忍睹。记住,TPM 只做最关键的操作——度量、签名、解密。
下面这张图,是我自己画的一个 TPM 2.0 内部结构简图,帮你建立整体印象:
PCR 寄存器:平台状态的「指纹」
PCR 的全称是 Platform Configuration Register。说白了,它就是一组只能追加、不能篡改的哈希值累加器。TPM 2.0 规范定义了至少 24 个 PCR,每个 PCR 负责记录不同维度的度量信息。
为什么会设计成只能 Extend 不能直接写?
因为安全。PCR 的 Extend 操作是:PCR_new = SHA256(PCR_old || 新度量值)。你没法回退,也没法伪造一个特定的值。我在项目中遇到过有人想「重置」PCR 来绕过度量检查,结果发现除非重启机器,否则根本做不到。
实战经验:PCR 0-15 由 BIOS/固件在启动过程中度量,操作系统和应用程序通常用 PCR 16-23。我个人习惯把关键的容器镜像度量放在 PCR 14 或 PCR 15,这样不会和系统 PCR 冲突。
下面这张表,是我整理的最常用的 PCR 分配:
| PCR 编号 | 度量内容 | 谁在度量 | 远程证明关注度 |
|---|---|---|---|
| PCR 0 | BIOS/固件代码 | CRTM (Core Root of Trust for Measurement) | ⭐⭐⭐⭐⭐ |
| PCR 1 | 主板配置 | BIOS | ⭐⭐⭐ |
| PCR 2-3 | Option ROM 代码 | BIOS | ⭐⭐⭐ |
| PCR 4 | MBR / 引导加载器 | BIOS | ⭐⭐⭐⭐ |
| PCR 5 | 引导加载器配置 | BIOS | ⭐⭐⭐⭐ |
| PCR 7 | 安全启动策略 | BIOS | ⭐⭐⭐⭐⭐ |
| PCR 8-9 | 操作系统内核 | 引导加载器 | ⭐⭐⭐⭐ |
| PCR 14 | 自定义度量(如容器) | 应用程序 | ⭐⭐⭐⭐⭐ |
注意:PCR 值在系统重启后会被重置为初始值(通常是全 0 或全 1,取决于 TPM 厂商)。如果你需要跨启动周期保存度量状态,必须使用 NVRAM 或磁盘持久化。我曾经踩过这个坑——写了一个远程证明服务,重启后 PCR 全变了,验证方直接拒绝。
EK 与 AK:证明中的「身份证」和「临时工牌」
TPM 2.0 里有两类密钥,在远程证明中扮演完全不同的角色。
EK(Endorsement Key)
EK 是 TPM 的「出生证明」。它在 TPM 出厂时生成,私钥永远不出 TPM 芯片。EK 的公钥会由 TPM 厂商签名,形成一个证书链。你可以把 EK 理解为 TPM 的硬件身份证——全球唯一,不可伪造。
但 EK 有个问题:它是静态的。如果你每次远程证明都用 EK 签名,那么所有验证方都能关联你的身份,隐私就没了。
AK(Attestation Key)
AK 是 TPM 的「临时工牌」。它由 EK 派生或由平台 Owner 创建,专门用于远程证明的签名操作。AK 可以频繁更换,甚至每次证明都生成一个新的。
关键点:AK 必须被 EK 签名认证过,验证方才能信任 AK。这个过程叫 AK 认证(AK Certify)。
一句话总结:EK 证明「我是这个 TPM」,AK 证明「我代表这个 TPM 做证明」。EK 是信任锚,AK 是操作代理。
下面这个表格,帮你快速区分:
| 属性 | EK | AK |
|---|---|---|
| 生成时机 | TPM 出厂时 | 运行时由 Owner 创建 |
| 私钥位置 | 永远在 TPM 内 | 永远在 TPM 内 |
| 是否可更换 | 不可(除非销毁 TPM) | 可(随时生成新 AK) |
| 用途 | 身份认证、AK 签名 | Quote 签名、远程证明 |
| 隐私保护 | 差(固定身份) | 好(可匿名化) |
Quote 与 Sign:证明的「核心操作」
好了,前面铺垫了这么多,终于到了实操环节。远程证明最核心的两个 TPM 命令就是 TPM2_Quote 和 TPM2_Sign。
TPM2_Quote
Quote 命令的作用是:用 AK 对一组 PCR 值进行签名,并返回签名结果和 PCR 值。验证方拿到后,可以验证签名是否来自可信的 AK,以及 PCR 值是否符合预期。
Quote 的输入参数包括:
- AK 的句柄(Handle)
- 要引用的 PCR 列表(如 PCR 0, 4, 7)
- 一个随机数(Nonce),防止重放攻击
Quote 的输出包括:
- PCR 值(实际度量值)
- 签名(用 AK 私钥对 PCR 值 + Nonce 的签名)
- TPMS_ATTEST 结构体(包含时间戳、固件版本等元数据)
我的习惯:Nonce 一定要用足够长的随机数(至少 16 字节),而且每次 Quote 都要换。我曾经见过一个系统,Nonce 是固定的,结果攻击者直接重放了一个旧的 Quote 响应,验证方还傻傻地通过了。
TPM2_Sign
Sign 命令更通用。它可以用任何具有签名能力的密钥(包括 AK)对任意数据进行签名。但在远程证明场景里,Sign 通常用于对 Quote 之外的额外数据进行签名,比如证明一个特定的文件哈希、一个容器镜像摘要。
Sign 和 Quote 的区别在于:
- Quote 是「TPM 主动告诉你 PCR 是什么」
- Sign 是「你告诉 TPM 要签什么」
下面是一个典型的 Quote 调用示例(使用 tpm2-tools):
# 1. 创建 AK(如果还没有)
tpm2_createak -C o -G rsa -c ak.ctx -u ak.pub -r ak.priv
# 2. 生成一个随机 Nonce(16 字节)
openssl rand -hex 16 > nonce.bin
# 3. 执行 Quote,引用 PCR 0, 4, 7
tpm2_quote -c ak.ctx -l sha256:0,4,7 -q nonce.bin -m quote.msg -s quote.sig -o pcr.bin
# 4. 验证 Quote(在验证方执行)
tpm2_checkquote -c ak.pub -m quote.msg -s quote.sig -f pcr.bin -q nonce.bin
避坑指南:我曾经在生产环境遇到过一个问题——Quote 返回的 PCR 值和我预期的不一致。排查了半天,发现是 PCR 选择算法搞错了。TPM 2.0 支持多个 Bank(如 SHA-1 Bank 和 SHA-256 Bank),你必须指定用哪个 Bank。默认情况下,很多工具会用 SHA-1,但现代系统应该用 SHA-256。
小结:远程证明的信任链
把上面这些串起来,远程证明的信任链就是:
- 信任根:TPM 芯片本身(硬件信任根)
- 身份锚:EK(由 TPM 厂商证书担保)
- 操作密钥:AK(由 EK 签名认证)
- 度量证据:PCR 值(通过 Extend 操作累积)
- 证明原语:Quote(用 AK 签名 PCR + Nonce)
验证方只需要验证 EK 证书 → 验证 AK 认证 → 验证 Quote 签名 → 比对 PCR 预期值,就能判断平台是否可信。