4、Quote与签名机制:TPM2_Quote命令详解、签名算法选择(RSA/ECC)、Nonce机制与重放攻击防护
Quote 是远程证明的核心操作。说白了,就是让 TPM 给你签个名,证明「当前平台状态是真实的」。但签名怎么签、用什么算法签、怎么防止别人拿着你的签名去骗人——这里面的坑,我踩过不少。
4.1 TPM2_Quote 命令到底干了什么?
很多人以为 TPM2_Quote 就是「读取 PCR 值然后签名」。其实没那么简单。我拆开来讲。
TPM2_Quote 的输入有三个关键东西:
- PCR 选择器:你要对哪些 PCR 做证明?是只做 PCR[0-7],还是全部 24 个?
- Nonce:一个一次性随机数,由验证方生成。后面会细讲。
- 签名密钥句柄:用哪个密钥来签名?通常是 AK(Attestation Key)。
命令执行后,TPM 会返回一个 TPMS_ATTEST 结构体,里面包含了 PCR 值的摘要、Nonce、时钟信息等。然后 TPM 用 AK 对这个结构体签名,生成 TPMT_SIGNATURE。
核心要点:Quote 返回的不是 PCR 原始值,而是 PCR 值的哈希摘要。验证方需要自己重新计算 PCR 摘要,然后比对。
我在项目中遇到过有人直接把 PCR 原始值塞进 Quote 结构体,结果 TPM 报错。嗯,TPM 的 Quote 只接受摘要,不接受原始数据。这个设计是为了固定长度,也为了隐私——你不希望把每个 PCR 的原始值都暴露出去。
4.2 签名算法选择:RSA 还是 ECC?
TPM 2.0 支持多种签名算法。但实际项目中,你主要面临两个选择:RSA 和 ECC。我个人的建议是——
| 对比维度 | RSA (RSASSA-PKCS1-v1_5 / RSASSA-PSS) | ECC (ECDSA) |
|---|---|---|
| 签名速度 | 较慢(尤其密钥长度 2048+) | 快(约快 3-5 倍) |
| 验证速度 | 快 | 较慢 |
| 签名大小 | 固定 256 字节(2048 位) | 可变,通常 64-72 字节 |
| 密钥生成 | 慢,需要大素数 | 快 |
| 硬件支持 | 几乎所有 TPM 2.0 都支持 | 较新 TPM 才支持,部分旧设备没有 |
| 安全性 | 成熟,2048 位以上安全 | 同等安全强度下密钥更短 |
你想想看,如果你的设备是 IoT 网关,每次证明都要签名,签名次数频繁,ECC 明显更合适。但如果是服务器场景,验证方压力大,RSA 的验证速度快反而是优势。
我的经验:如果 TPM 固件版本较老(比如 2018 年以前的),优先选 RSA。我遇到过一台设备,TPM 声称支持 ECDSA,但实际签名时总是返回 TPM_RC_COMMAND_CODE——固件没实现完整。后来换成 RSA 就稳了。
4.3 Nonce 机制:为什么不能少?
Nonce 是 Number used ONCE 的缩写。在 Quote 里,Nonce 的作用是防止重放攻击。
假设没有 Nonce:
- 攻击者截获了一次合法的 Quote 数据(包含 PCR 值和签名)。
- 攻击者把设备搞坏了,PCR 值变了。
- 攻击者把之前截获的 Quote 数据重新发给验证方。
- 验证方验证签名通过,以为设备还是好的。
这就是重放攻击。Nonce 怎么防?
- 验证方每次发起挑战时,生成一个随机 Nonce。
- TPM 在签名时,把 Nonce 放进
TPMS_ATTEST结构体。 - 验证方收到 Quote 后,先检查 Nonce 是否和自己发出去的一致。
- 如果不一致,直接拒绝。
Nonce 必须满足两个条件:唯一性和不可预测性。用时间戳当 Nonce 行不行?我见过有人这么干。结果呢?时间戳可以被预测,攻击者可以提前构造合法的 Quote。所以,一定要用密码学安全的随机数生成器。
避坑指南:我曾经在一个项目中,验证方没有检查 Nonce 的唯一性。结果攻击者用同一个 Nonce 发起了多次挑战,每次 Quote 都一样。虽然 Nonce 匹配了,但攻击者其实是在重放。正确的做法是:验证方维护一个 Nonce 列表,已经用过的 Nonce 不能再接受。
4.4 重放攻击防护:不止 Nonce
Nonce 是基础,但还不够。Quote 结构体里还有两个字段可以用来增强防护:
- clock:TPM 内部的单调递增时钟。
- resetCount:TPM 复位次数。
验证方可以检查 clock 是否比上一次大,resetCount 是否和预期一致。如果 clock 变小了,说明有人可能回滚了 TPM 状态。
我建议的做法是:
- 验证方保存每个设备的
lastClock和lastResetCount。 - 每次收到 Quote,检查
clock > lastClock。 - 如果
resetCount变了,说明设备重启过,需要重新评估信任。
你想想看,如果攻击者把设备断电,然后恢复到一个旧的状态,PCR 值可能回到一个「干净」的状态。但 clock 会暴露这个行为——因为断电后 clock 不会回退,但 resetCount 会增加。
4.5 签名验证的完整流程
说了这么多,我把完整的 Quote 验证流程画出来:
4.6 代码示例:TPM2_Quote 调用
下面是一个使用 tpm2-tools 的 Quote 示例。实际项目中你可能用 C 或 Rust 调用 TPM 库,但原理一样。
# 1. 验证方生成 Nonce(16字节随机数)
openssl rand -hex 16 > nonce.bin
# 2. 设备端执行 Quote(使用 AK 句柄 0x81000000,对 PCR[0-7] 做证明)
tpm2_quote \
--key-context 0x81000000 \
--pcr-list sha256:0,1,2,3,4,5,6,7 \
--message quote.msg \
--signature quote.sig \
--qualification nonce.bin
# 3. 验证方验证签名(需要 AK 公钥)
tpm2_verifysignature \
--key-context ak.pub \
--message quote.msg \
--signature quote.sig \
--ticket quote.tkt
# 4. 解析 TPMS_ATTEST,提取 PCR 摘要和 Nonce
tpm2_print --tpm2 quote.msg
注意:--qualification 参数就是 Nonce。有些工具叫 --nonce,但 TPM 规范里叫 qualification data。别搞混了。
4.7 避坑总结
我把 Quote 和签名机制里常见的坑列出来,你对照检查:
| 坑 | 后果 | 解决方案 |
|---|---|---|
| Nonce 用时间戳 | 可预测,重放攻击 | 用密码学安全随机数 |
| 不检查 Nonce 唯一性 | 同一 Nonce 可重复使用 | 维护已用 Nonce 列表 |
| AK 证书链不验证 | 伪造 AK 可冒充设备 | 验证 AK 证书到根 CA |
| 忽略 clock 和 resetCount | 状态回滚攻击 | 保存并比对 clock/resetCount |
| 签名算法选错 | 性能差或兼容性问题 | 根据场景选 RSA 或 ECC |
| PCR 选择器不全 | 关键 PCR 未被证明 | 明确需要证明的 PCR 集合 |
嗯,Quote 和签名机制就讲到这里。记住一句话:Quote 不是简单的签名,而是一个包含 Nonce、PCR 摘要、时钟信息的结构化证明。每个字段都有它的作用,少一个都不行。