TPM 基础:硬件架构、密钥层次、PCR 与命令响应
各位同学,咱们今天聊聊 TPM。这东西听起来像个黑盒子,其实没那么玄乎。我最早接触 TPM 是在做一款工控网关的安全启动方案,那时候芯片手册看得我头皮发麻。后来摸熟了,发现它的设计思路非常清晰——说白了,就是一个专门干密码活儿的“保险柜”。
TPM 硬件架构:一个独立的密码协处理器
TPM 不是跑在 CPU 上的软件,它是一颗独立的芯片。你想想看,如果密钥存在硬盘里,攻击者拿到 root 权限就能偷走。TPM 的做法是:密钥永远不离开芯片,所有密码运算都在芯片内部完成。
它的核心组件包括:
- I/O 接口:通过 LPC 或 SPI 总线与主板通信,接收命令、返回响应。
- 非易失性存储 (NVRAM):存放持久化数据,比如背书密钥 (EK)、存储根密钥 (SRK)。掉电不丢失。
- 易失性存储 (RAM):运行时存放会话密钥、临时数据。
- 密码协处理器:硬件加速 RSA、ECC、SHA-256、HMAC 等运算。比纯软件快一个数量级。
- 随机数生成器 (RNG):真随机数发生器,用于生成密钥和 nonce。
- 平台配置寄存器 (PCR):一组不可篡改的度量值寄存器,后面重点讲。
关键设计原则:TPM 是“被动”设备。它不会主动发起任何操作,只响应外部命令。这意味着你无法远程直接控制 TPM,必须通过本地软件栈(如 Linux 的 tpm2-tools)与它交互。
我在项目中踩过一个坑:某款 TPM 芯片的 SPI 时钟频率设得太高,导致通信不稳定,开机时有时认不到设备。后来我强制降频到 20MHz,问题解决。嗯,硬件细节往往藏在时序里。
TPM 密钥层次:从根密钥到会话密钥
TPM 的密钥管理是一个树形结构。根在芯片内部,叶子在应用层。我习惯把它想象成“俄罗斯套娃”——每一层密钥都由上一层保护。
| 层级 | 密钥类型 | 存储位置 | 用途 |
|---|---|---|---|
| 0 | 背书密钥 (EK) | NVRAM(出厂预置) | 芯片唯一身份标识,用于远程证明 |
| 1 | 存储根密钥 (SRK) | NVRAM(由 EK 派生) | 保护所有存储密钥的根 |
| 2 | 存储密钥 (SK) | 受 SRK 加密后存于外部 | 保护应用密钥或数据 |
| 3 | 身份密钥 (AIK) | 受 SK 加密后存于外部 | 签名 PCR 值,用于远程证明 |
| 4 | 会话密钥 | RAM(运行时生成) | 加密通信通道 |
这里有个重要概念:密钥迁移。TPM 2.0 允许将密钥导出到外部存储,但必须用父密钥加密。我曾经遇到一个需求:需要把 AIK 从一台设备迁移到另一台。当时没注意父密钥的授权策略,结果迁移后签名验证失败。后来发现是策略会话的 nonce 没对齐。细节决定成败啊。
我的建议:生产环境中,EK 和 SRK 永远不要导出。AIK 可以导出备份,但必须用强密码保护。我个人习惯用 TPM 2.0 的 TPM2_Duplicate 命令配合对称加密做迁移,既安全又可控。
平台配置寄存器 (PCR):系统的“指纹”
PCR 是 TPM 最巧妙的设计之一。它是一组 20 字节(SHA-1)或 32 字节(SHA-256)的寄存器,用于记录系统状态。关键特性是:只能扩展,不能重置(除非重启)。
扩展操作是:PCR[n] = SHA256(PCR[n] || new_measurement)。这意味着 PCR 值依赖于所有历史度量值的顺序。你想想看,如果攻击者篡改了某个启动组件,PCR 值就会和预期不同——远程验证方一眼就能看出来。
TPM 2.0 规范定义了 24 个 PCR,每个有特定用途:
| PCR 索引 | 用途 | 度量内容 |
|---|---|---|
| 0 | BIOS/固件 | 主机平台固件代码 |
| 1 | 主机平台配置 | BIOS 设置、跳线状态 |
| 2 | Option ROM | 扩展卡固件 |
| 3 | Option ROM 配置 | 扩展卡配置数据 |
| 4 | MBR/引导加载程序 | 主引导记录或 UEFI 引导加载程序 |
| 5 | 引导加载程序配置 | GRUB 配置、内核命令行 |
| 6 | 状态转换/唤醒 | 休眠恢复、S3 唤醒路径 |
| 7 | 安全策略 | Secure Boot 策略、TPM 策略 |
| 8-15 | 操作系统使用 | 内核、initramfs、模块、应用 |
注意:PCR 0-7 在平台复位时自动重置。PCR 8-15 由操作系统管理,可以跨重启保持。如果你做远程证明,通常引用 PCR 0-7 来证明固件完整性,引用 PCR 8-15 来证明内核和关键服务的完整性。
我曾经调试过一个诡异的问题:某台服务器每次重启后 PCR 0 的值都不一样。查了两天,发现是 BIOS 版本升级后,微码补丁的加载顺序变了。嗯,PCR 对顺序敏感,这个特性既是优点也是坑。
TPM 命令与响应:如何与芯片对话
TPM 的通信模型很简单:你发一条命令,它回一个响应。命令和响应都是 TPM 2.0 规范定义的 TPM2B 结构体。我刚开始看规范时,被那几百页的字节布局吓到了。后来发现,你不需要手拼字节流——有现成的库。
一个典型的命令流程:
- 应用层调用 tpm2-tools 或直接调用 TSS(TPM Software Stack)API。
- TSS 将命令序列化为 TPM2B 格式,加上命令头和授权信息。
- 通过 /dev/tpm0 或 /dev/tpmrm0 发送给 TPM 驱动。
- TPM 芯片执行命令,返回响应字节流。
- TSS 解析响应,返回给应用层。
举个例子,用 tpm2-tools 读取 PCR 0 的值:
# 读取 PCR 0 的当前值
tpm2_pcrread sha256:0
# 输出示例
sha256:
0 : 0x3A4F... (32 字节十六进制)
如果你想做远程证明,核心命令是 TPM2_Quote。它会用 AIK 对一组 PCR 值签名,并附带一个 nonce 防止重放攻击。验证方收到签名后,用 AIK 的公钥验签,再比对 PCR 值是否与预期一致。
核心命令速查:
TPM2_Startup:初始化 TPM,必须在每次上电后调用。TPM2_PCR_Extend:扩展 PCR 值。TPM2_PCR_Read:读取 PCR 值。TPM2_Quote:用 AIK 签名 PCR 值,用于远程证明。TPM2_GetRandom:获取真随机数。TPM2_Create:在 TPM 内部生成密钥。TPM2_Load:加载外部密钥到 TPM。
我建议你从 tpm2_pcrread 和 tpm2_quote 开始练手。先读 PCR,再签名,最后验签。走通这个流程,你就掌握了远程证明的核心。
本章知识体系
下面这张图总结了 TPM 的核心逻辑:硬件是底座,密钥是资产,PCR 是证据,命令是接口。四者缺一不可。
这张图里,硬件在最左边,密钥在中间,PCR 在最右边,命令在最下面。箭头表示依赖关系:硬件提供安全执行环境,密钥层次提供信任链,PCR 记录度量值,命令是操作接口。远程证明的本质就是:用硬件保护的密钥,去签名不可篡改的 PCR 值,然后通过网络发给验证方。