远程证明协议基础:挑战-响应模型、Nonce 机制、证明数据(Quote)的结构
好,我们进入第三章。这一章讲的是远程证明的“握手”细节。说白了,就是验证者怎么问,证明者怎么答,以及答回来的数据长什么样。
我刚开始接触远程证明时,觉得这东西不就是“你发个请求,我回个报告”吗?后来踩了几个坑才发现,这里的门道比想象中深。尤其是 Nonce 的设计,稍不注意就会留下安全漏洞。
挑战-响应模型:一问一答的信任基础
远程证明的核心交互模式,就是挑战-响应(Challenge-Response)。
你可以把它想象成一次安检:
- 验证者(Verifier):安检员,负责提问。
- 证明者(Prover):被检人,负责出示证据。
流程很简单,但每一步都有讲究:
- 验证者发起挑战:生成一个随机数(Nonce),发给证明者。
- 证明者收集证据:基于 Nonce 和当前设备状态,生成一份证明数据(Quote)。
- 证明者返回响应:把 Quote 发回给验证者。
- 验证者校验:验证 Quote 的签名、Nonce 是否匹配、设备状态是否可信。
关键点:挑战-响应模型的核心在于“新鲜性”和“绑定”。Nonce 保证了本次证明不是重放攻击,Quote 将 Nonce 与设备状态绑定,防止篡改。
我在项目中见过一个典型错误:验证者没有校验 Nonce 是否过期。结果攻击者截获一次合法的 Quote,反复重放,直到设备状态变坏为止。嗯,这其实是个很隐蔽的坑。
Nonce 机制:防重放的“盐”
Nonce(Number Used Once)是远程证明的基石。它就是一个随机数,只用一次。
为什么需要 Nonce?你想想看,如果没有 Nonce,证明者可以提前生成一份“设备状态良好”的 Quote,然后反复使用。哪怕设备已经被攻陷,验证者收到的还是那份“良好”的证明。
Nonce 的作用就是:
- 保证新鲜性:每次挑战的 Nonce 都不同,证明者必须实时生成 Quote。
- 防止重放:攻击者无法复用旧的 Quote,因为 Nonce 不匹配。
- 绑定上下文:Nonce 可以携带会话 ID 或时间戳,让 Quote 与特定会话绑定。
我的习惯:Nonce 至少 64 位,最好 128 位。太短容易碰撞,太长浪费带宽。另外,Nonce 必须由密码学安全的随机数生成器产生,别用 rand()。
我曾经在一个项目中,看到有人用时间戳当 Nonce。结果呢?攻击者只要在同一个秒级窗口内发起多次挑战,就能拿到相同的 Nonce,然后交换 Quote。这其实是个很经典的教训——时间戳不是 Nonce,它是 predictable 的。
证明数据(Quote)的结构:证据的“体检报告”
Quote 是证明者返回的核心数据。它包含了设备当前的可信状态,并且由硬件(如 TPM、Intel SGX、ARM TrustZone)签名。
一个典型的 Quote 结构如下:
| 字段 | 说明 | 长度(示例) |
|---|---|---|
| Header | 版本号、算法标识、保留字段 | 16 字节 |
| Nonce | 验证者发来的挑战值 | 16 字节 |
| PCR 值 | 平台配置寄存器(Platform Configuration Register)的摘要 | 32 字节 × N |
| 其他度量 | 如 IMA 度量、容器哈希、内核版本等 | 可变 |
| 签名 | 由硬件私钥对上述字段签名 | 256 字节(RSA-2048) |
| 证书链 | 用于验证签名的公钥证书 | 可变 |
Quote 的生成过程,我习惯用伪代码表示:
// 证明者侧
function generateQuote(nonce, pcrValues, otherMetrics):
// 1. 组装数据
quoteData = {
header: { version: 2, algorithm: "SHA256" },
nonce: nonce,
pcr: hash(pcrValues),
metrics: hash(otherMetrics)
}
// 2. 硬件签名
signature = hardwareSign(quoteData, privateKey)
// 3. 返回完整 Quote
return {
data: quoteData,
signature: signature,
certificate: getAttestationCertificate()
}
注意:Quote 中的 PCR 值必须是“扩展”后的结果,而不是直接测量值。TPM 的 PCR 扩展机制是:newPCR = hash(oldPCR || measuredValue)。这个顺序不能乱,否则度量链会断裂。
我个人习惯在 Quote 里额外加一个“时间戳”字段。虽然 Nonce 已经保证了新鲜性,但时间戳可以帮助验证者快速判断 Quote 是否在有效窗口内,减少 Nonce 的存储压力。
交互流程全景图
下面这张图展示了挑战-响应模型的完整交互流程:
这张图把流程拆成了四个步骤。注意看,Nonce 从验证者流向证明者,Quote 从证明者流回验证者。整个交互是“一问一答”的闭环。
Quote 的校验逻辑
验证者收到 Quote 后,需要做以下几件事:
- 解析 Quote:提取 Header、Nonce、PCR 值、签名、证书链。
- 验证证书链:确保证书是由可信的硬件厂商签发,且未过期。
- 验证签名:用证书中的公钥验证 Quote 的签名。
- 校验 Nonce:确保 Nonce 与之前发出的挑战一致。
- 校验 PCR 值:将 PCR 值与预期值(白名单)比对。
- 校验其他度量:如 IMA 度量、内核版本等。
避坑指南:我曾经遇到过一个案例,验证者只校验了签名,没校验 Nonce。结果攻击者用同一个 Quote 反复证明,直到 PCR 值被篡改。所以,Nonce 校验和签名校验同等重要,缺一不可。
总结一下
这一章我们讲了三个核心概念:
- 挑战-响应模型:一问一答,保证交互的实时性。
- Nonce 机制:一次性随机数,防重放攻击。
- Quote 结构:包含 Nonce、PCR 值、签名等,是设备可信状态的“体检报告”。