4. TPM 2.0 架构详解:TPM的结构、PCR寄存器、EK与AK密钥、Quote机制
TPM 2.0,说白了就是一颗专门干「脏活累活」的安全芯片。我最早接触它是在做服务器可信启动的项目,那时候觉得这东西就是个黑盒子,往里写数据、往外读摘要。后来踩了几个坑才明白——TPM 的设计哲学其实很优雅:它不信任任何人,包括它自己所在的宿主系统。
4.1 TPM 的物理与逻辑结构
TPM 2.0 的物理结构,你可以想象成一个「带保险柜的微型计算机」。它有自己的 CPU、ROM、RAM、非易失性存储(NVRAM),还有一堆密码学加速器。逻辑上,它分为几个关键组件:
- I/O 接口:通过 LPC、SPI 或 I²C 与 CPU 通信。我见过不少开发板因为 SPI 信号线没做隔离,导致 TPM 通信间歇性失败。
- 密码协处理器:硬件加速 RSA、ECC、SHA-256、AES。纯软件算一个 RSA-2048 签名要几十毫秒,TPM 硬件几毫秒搞定。
- 非易失性存储(NVRAM):存放持久化数据,比如 EK 私钥、AK 私钥、PCR 初始值。这部分掉电不丢。
- 易失性存储器(RAM):运行时存放会话上下文、临时密钥。每次 TPM 复位就清空。
- 随机数生成器(RNG):真随机数发生器,基于硬件噪声。我曾在项目中用 TPM 的 RNG 替代软件伪随机,效果立竿见影。
核心要点:TPM 的私钥永远不出芯片。你只能用它签名、解密,但永远拿不到私钥原文。这是远程证明信任的根基。
下面这张图展示了 TPM 2.0 的内部架构和关键组件之间的关系:
4.2 PCR 寄存器:平台的「指纹记录仪」
PCR(Platform Configuration Register)是 TPM 里最核心的概念之一。你可以把它理解成一组「只能扩展、不能直接写」的哈希累加器。每个 PCR 寄存器存储一个 SHA-256 摘要值(32 字节)。
扩展操作是这样的:
PCR_new = SHA256(PCR_old || 新测量值)
也就是说,你没法把 PCR 设成任意值。你只能把新数据「叠」到当前值上。这个特性保证了——只要测量顺序固定,最终 PCR 值就是确定的。任何篡改都会导致最终值不同。
我的经验:我曾经在调试一个 UEFI 安全启动问题时,发现 PCR[0] 的值总是不对。查了两天才发现,是 BIOS 在加载某个 Option ROM 时多了一次测量。顺序一变,PCR 值全乱。所以 PCR 的「测量顺序」和「测量内容」同等重要。
TPM 2.0 规范定义了 24 个 PCR 寄存器(索引 0-23),每个有特定用途:
| PCR 索引 | 用途 | 测量内容 |
|---|---|---|
| 0-1 | BIOS/固件 | BIOS 代码、ACPI 表、SMBIOS |
| 2-3 | Option ROM | 扩展卡固件、网卡 PXE 代码 |
| 4 | MBR/GPT | 主引导记录或 GPT 头 |
| 5 | MBR/GPT 分区 | 分区表后续部分 |
| 6 | 状态转换 | 平台状态变化(如休眠恢复) |
| 7 | 安全启动策略 | 安全启动策略数据库 |
| 8-15 | 静态操作系统 | 操作系统内核、驱动、initramfs |
| 16-23 | 动态/应用 | 运行时测量、容器、应用代码 |
注意:PCR 在 TPM 复位(如系统重启)时会被重置为初始值。但有些 PCR(如 16-23)可以配置为「仅在 TPM 上电时复位一次」,用于支持动态远程证明。
4.3 EK 与 AK 密钥:身份的「出生证明」和「工作证」
EK(Endorsement Key)是 TPM 的「出生证明」。它在 TPM 出厂时生成,私钥永久固化在 NVRAM 里,公钥由芯片厂商签发证书。EK 的唯一用途是——证明「我是一个真实的 TPM 芯片」。但 EK 不能直接用于远程证明,因为它是全局唯一的,会暴露设备身份。
AK(Attestation Key)是 TPM 的「工作证」。它由 EK 担保生成,用于日常的签名和证明操作。AK 可以随时创建和销毁,每次创建时 TPM 会用 EK 对 AK 的公钥签名,生成 AK 证书。
我习惯把 EK 和 AK 的关系比作「身份证」和「工牌」:身份证证明你是你,但你不能到处亮;工牌证明你有权限,丢了可以补办。
创建 AK 的流程大致如下:
- 调用
TPM2_Create生成 AK 密钥对(RSA-2048 或 ECC P-256)。 - 调用
TPM2_Certify用 EK 对 AK 公钥签名,生成 AK 证书。 - 将 AK 公钥和证书发送给验证方。
- 验证方用 EK 证书链验证 AK 证书的合法性。
关键区别:EK 是静态的、唯一的、不可变的;AK 是动态的、可撤销的、可批量创建的。远程证明中你永远用 AK 签名,EK 只用来证明 AK 的合法性。
4.4 Quote 机制:TPM 的「自证清白」
Quote 是 TPM 远程证明的核心操作。它的本质是——TPM 用 AK 对一组 PCR 值签名,证明「当前平台的 PCR 值就是这些,而且是我这个 TPM 签的」。
Quote 的输入参数包括:
- AK 句柄:指定用哪个 AK 签名。
- PCR 选择:要引用的 PCR 索引列表(如 PCR[0,2,4])。
- Nonce:验证方提供的随机数,防止重放攻击。
- 额外数据:可选,用于绑定上下文。
Quote 的输出是一个 TPMS_ATTEST 结构体,包含:
TPMS_ATTEST {
UINT64 magic; // 固定魔数
TPMI_ST_ATTEST type; // 类型 = TPM_ST_ATTEST_QUOTE
TPM2B_NAME qualifiedSigner; // AK 名称
TPM2B_DATA extraData; // 额外数据
TPMS_CLOCK_INFO clockInfo; // TPM 时钟信息
UINT64 firmwareVersion; // 固件版本
TPM2B_DIGEST attested; // PCR 摘要值(关键!)
}
验证方收到 Quote 后,做三件事:
- 用 AK 公钥验证签名。
- 检查 Nonce 是否匹配。
- 将 attested 中的 PCR 值与「预期值」对比。
如果都通过,说明平台当前状态是可信的。
避坑指南:我曾经在项目中遇到一个问题——Quote 验证总是失败。排查了半天,发现是 Nonce 在传输过程中被 URL 编码了一次,导致长度变了。TPM 对 Nonce 的字节长度非常敏感,多一个字节都不行。所以,Nonce 的传输一定要用二进制安全的方式(如 Base64 编码)。
Quote 的完整流程可以用下面这张图概括:
4.5 小结:TPM 2.0 的信任链
把上面几个概念串起来,TPM 2.0 的远程证明信任链就很清晰了:
- 信任根:TPM 芯片本身(硬件安全)。
- 身份根:EK(出厂固化,不可伪造)。
- 工作密钥:AK(由 EK 担保,可动态创建)。
- 证据:PCR 值(记录平台状态)。
- 证明:Quote(AK 对 PCR 的签名)。
嗯,这套设计其实很巧妙。你想想看——验证方不需要信任宿主操作系统,不需要信任 BIOS,甚至不需要信任网络。只要 TPM 芯片是真实的,只要 EK 证书链是完整的,那么 Quote 给出的 PCR 值就是可信的。这就是远程证明的「信任锚点」。
最后提醒:TPM 2.0 的规范有 2000 多页,我上面讲的只是冰山一角。但如果你理解了 PCR、EK、AK 和 Quote 这四者的关系,你就掌握了远程证明的核心骨架。剩下的细节,都是在骨架上填肉。