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 内部结构简图,帮你建立整体印象:

TPM 2.0 内部架构与远程证明数据流 接口层:LPC / SPI / I²C 总线 TPM Command/Response Buffer · TIS (TPM Interface Specification) 核心密码服务层 RNG · SHA-1/SHA-256/SM3 RSA/ECC · AES/HMAC 密钥生成 · 签名验证 · 加密解密 存储与密钥管理层 PCR 寄存器 平台配置寄存器 (24+ 个) NVRAM 索引 持久化存储区 密钥层次 EK / AK / SRK 远程证明数据流 ① 度量 (Extend PCR) → ② 读取 PCR 值 → ③ 用 AK 签名 (Quote) → ④ 验证 信任根 (RTR) → 信任链 → 远程验证方

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。

小结:远程证明的信任链

把上面这些串起来,远程证明的信任链就是:

  1. 信任根:TPM 芯片本身(硬件信任根)
  2. 身份锚:EK(由 TPM 厂商证书担保)
  3. 操作密钥:AK(由 EK 签名认证)
  4. 度量证据:PCR 值(通过 Extend 操作累积)
  5. 证明原语:Quote(用 AK 签名 PCR + Nonce)

验证方只需要验证 EK 证书 → 验证 AK 认证 → 验证 Quote 签名 → 比对 PCR 预期值,就能判断平台是否可信。