TPM 技术详解:TPM 2.0架构、PCR寄存器与度量扩展、EK与AK密钥体系、Quote与Sign操作

各位同学,今天我们聊一个硬核话题——TPM。说实话,TPM 这东西在零信任远程证明里,就像人的身份证加诚信记录。没有它,你根本没法证明“我是我,我没被篡改”。

我在做第一个远程证明项目时,踩过不少坑。当时对 TPM 的理解停留在“就是个安全芯片”的层面。结果一上手,发现 PCR 怎么扩展、EK 和 AK 怎么配、Quote 怎么验,全是细节。今天我把这些经验掰开揉碎了讲给你听。

TPM 2.0 架构:不只是个芯片

TPM 2.0 相比 1.2,最大的变化是架构更灵活了。1.2 时代,算法被锁死在 SHA-1 和 RSA,你没法选。2.0 则支持多种算法族,你甚至可以自定义。

它的核心组件包括:

  • I/O 接口:LPC、SPI、I2C,跟主板通信用
  • 密码协处理器:硬件加速 RSA、ECC、哈希
  • 密钥生成器:真随机数发生器,物理噪声源
  • 非易失性存储器:存 EK 证书、NV 索引
  • 易失性存储器:PCR 值、会话上下文
  • 执行引擎:跑固件代码,处理 TPM 命令

我个人习惯把 TPM 2.0 架构理解成三层:

  1. 命令层:你发 TPM2_Quote、TPM2_PCR_Extend 这些命令
  2. 资源管理层:管理密钥句柄、会话、对象
  3. 硬件执行层:真正做密码运算、存储保护

下面这张图帮你理清整体脉络:

TPM 2.0 核心架构与远程证明流程 TPM 2.0 核心架构与远程证明流程 命令层(Command Layer) TPM2_Startup | TPM2_PCR_Extend | TPM2_Quote | TPM2_Sign TPM2_Create | TPM2_Load | TPM2_ActivateCredential 资源管理层(Resource Manager) 密钥句柄管理 | 会话管理 | 对象生命周期 上下文保存/恢复 | NV 索引管理 硬件执行层(Hardware Execution) RSA/ECC 加速器 | SHA-256/SHA-384 哈希引擎 真随机数发生器(TRNG) | 非易失性存储器 PCR 寄存器阵列 | 防篡改保护 关键点 TPM 2.0 支持算法灵活配置
我的经验: 选 TPM 芯片时,别只看型号。有些低成本 TPM 阉割了 ECC 支持,导致远程证明只能用 RSA,性能差一大截。我建议优先选支持 ECC P-256 的型号。

PCR 寄存器与度量扩展

PCR(Platform Configuration Register)是 TPM 里最核心的寄存器。说白了,它就是一组“只增不减”的哈希值记录器。

每个 PCR 有 20 字节(SHA-1)或 32 字节(SHA-256)。系统启动时,BIOS、Bootloader、OS 内核依次度量自己,然后把度量值“扩展”进 PCR。

扩展操作的数学公式很简单:

PCR_new = SHA256( PCR_old || 度量值 )

注意,这里不是覆盖,是拼接后哈希。所以 PCR 的最终值,依赖所有历史度量值的顺序和内容。你改任何一个环节,最终值都不一样。

常见的 PCR 分配如下:

PCR 索引 度量内容 典型用途
0 BIOS/固件代码 验证固件完整性
1 主板配置 检测硬件变更
2 Option ROM 代码 扩展卡完整性
3 Option ROM 配置 扩展卡配置验证
4 MBR/VBR 代码 引导扇区完整性
5 MBR/VBR 配置 引导配置验证
6 状态转换/唤醒 休眠恢复验证
7 安全策略 Secure Boot 策略
8-15 OS 组件 内核、驱动、initrd
16-23 调试/动态度量 IMA、容器度量
我曾经踩过的坑: 有一次 PCR 值对不上,查了两天发现是 BIOS 版本升级后,PCR 0 的度量顺序变了。所以做远程证明时,一定要把预期的 PCR 参考值跟固件版本绑定,否则验证必失败。

EK 与 AK 密钥体系

TPM 的密钥体系,我习惯用“身份证 vs 临时通行证”来类比。

  • EK(Endorsement Key):出厂时烧录的 RSA 或 ECC 密钥对。私钥永远不出 TPM,公钥有证书。它就像你的身份证号——唯一且不可变。
  • AK(Attestation Key):由 EK 派生出的签名密钥。用于远程证明时的签名操作。它就像你每次出入用的临时通行证。

为什么要有两套密钥?直接拿 EK 签名不行吗?

嗯,这里要注意隐私问题。EK 是唯一的,如果你每次都用 EK 签名,验证方就能追踪你的设备。AK 可以随时换,而且验证方只知道“这是一个合法 TPM 签的”,但不知道具体是哪个 TPM。

AK 的创建流程:

  1. 调用 TPM2_Create,指定密钥用途为 signing
  2. TPM 内部生成 AK 公私钥对
  3. 私钥加密存储在 TPM 内部,公钥可导出
  4. 用 EK 对 AK 公钥做证书签名(EK 证书链验证)

验证方拿到 AK 公钥后,怎么确认它是合法 TPM 生成的?

通过 EK 证书链。TPM 厂商会签发 EK 证书,验证方信任该 CA。AK 公钥由 EK 签名,所以信任链是:

根 CA → 厂商 CA → EK 证书 → AK 公钥

核心要点: EK 是身份锚点,AK 是签名代理。远程证明中,验证方只看到 AK,看不到 EK,保护设备隐私。

Quote 与 Sign 操作

这两个操作是远程证明的“临门一脚”。

Sign:TPM 用 AK 私钥对任意数据进行签名。这是基础操作,但远程证明很少直接用 Sign。

Quote:TPM 用 AK 私钥对一组 PCR 值和时间戳(或随机数)做签名。这是远程证明的核心操作。

Quote 的输入参数:

  • AK 句柄(哪个密钥签)
  • PCR 选择(要报哪些 PCR 的值)
  • Nonce(验证方给的随机数,防重放)
  • 额外数据(可选,比如平台信息)

Quote 的输出:

  • TPM2B_ATTEST:包含 PCR 值、Nonce、时间戳的明文结构
  • 签名:对上述结构的签名值

验证方收到 Quote 后,做三步验证:

  1. 用 AK 公钥验签名,确认是合法 TPM 签的
  2. 比对 Nonce,确认不是重放攻击
  3. 比对 PCR 值,确认平台状态符合预期

下面是一个典型的 Quote 调用示例(伪代码):

// 验证方生成随机数
nonce = random_bytes(32)

// 发送给证明方
send(nonce)

// 证明方调用 TPM Quote
pcr_select = [0, 1, 2, 3, 4, 5, 7]
quote = TPM2_Quote(
    keyHandle = ak_handle,
    pcrSelect = pcr_select,
    qualifyingData = nonce
)

// 返回给验证方
send(quote.attested, quote.signature)

// 验证方验签
result = verify_signature(
    publicKey = ak_pub,
    data = quote.attested,
    signature = quote.signature
)

// 验证 Nonce
assert(quote.attested.extraData == nonce)

// 验证 PCR 值
expected_pcrs = load_expected_values()
for pcr_index in pcr_select:
    assert(quote.attested.pcrValues[pcr_index] == expected_pcrs[pcr_index])
我建议: Nonce 一定要用密码学安全的随机数生成器生成。我曾经见过有人用时间戳当 Nonce,结果被重放攻击打穿了。另外,PCR 选择不要贪多,只选跟安全策略相关的 PCR,减少验证开销。

总结一下

TPM 2.0 的远程证明能力,核心就四块:

  • 架构:命令层→资源管理层→硬件执行层,分层清晰
  • PCR:只增不减的度量寄存器,记录平台启动链的完整性
  • EK/AK:EK 是身份锚点,AK 是签名代理,保护隐私
  • Quote/Sign:Quote 是带 PCR 值的签名,远程证明的核心操作