TPM 安全性¶
本文档的目的是描述我们如何在面对外部窥探和数据包篡改攻击(在文献中称为被动和主动中间人攻击)时,使内核对 TPM 的使用具有合理的鲁棒性。当前的安全文档适用于 TPM 2.0。
简介¶
TPM 通常是一个通过某种低带宽总线连接到 PC 的独立芯片。也有例外情况,例如 Intel PTT,它是在靠近 CPU 的软件环境中运行的软件 TPM,容易受到不同的攻击,但目前大多数加固的安全环境都需要独立的硬件 TPM,这是本文讨论的用例。
针对总线的窥探和篡改攻击¶
目前窥探的最新技术是 TPM Genie 硬件中间人,这是一个简单的外部设备,可以在几秒钟内安装在任何系统或笔记本电脑上。最近,针对 Windows Bitlocker TPM 系统的攻击已成功演示。最近,针对 基于 TPM 的 Linux 磁盘加密方案的攻击 也出现了。下一阶段的研究似乎是在总线上入侵现有设备以充当中间人,因此攻击者需要物理访问几秒钟的情况可能会消失。但是,本文档的目标是在这种环境中尽可能地保护 TPM 秘密和完整性,并尝试确保如果无法阻止攻击,至少可以检测到它。
不幸的是,包括硬件重置功能在内的大部分 TPM 功能都可以由可以访问总线的攻击者控制,因此我们将在下面讨论一些中断的可能性。
测量 (PCR) 完整性¶
由于攻击者可以向 TPM 发送自己的命令,因此他们可以发送任意的 PCR 扩展,从而破坏测量系统,这将是一种令人烦恼的拒绝服务攻击。但是,有两类更严重的攻击针对密封以信任测量的实体。
攻击者可以拦截来自系统的所有 PCR 扩展,并完全替换他们自己的值,从而产生未篡改状态的重放,这将导致 PCR 测量证明可信状态并释放秘密。
在某个时间点,攻击者可以重置 TPM,清除 PCR,然后发送自己的测量值,这将有效地覆盖 TPM 已经完成的启动时间测量。
第一个可以通过始终对 PCR 扩展和读取命令进行 HMAC 保护来阻止,这意味着测量值无法被替换,而不会在响应中产生可检测的 HMAC 失败。但是,第二个只能通过依赖某种保护机制来检测,该机制会在 TPM 重置时发生变化。
秘密保护¶
某些进出 TPM 的信息(例如密钥密封、私钥导入和随机数生成)容易受到拦截,而仅靠 HMAC 保护无法防止拦截,因此对于这些类型的命令,我们还必须采用请求和响应加密来防止秘密信息的丢失。
与 TPM 建立初始信任¶
为了从一开始就提供安全性,必须建立初始的共享或非对称秘密,该秘密也必须是攻击者未知的。最明显的途径是背书和存储种子,它们可用于派生非对称密钥。但是,使用这些密钥很困难,因为将它们传递到内核的唯一方法是在命令行上,这需要在引导系统中提供广泛的支持,并且不能保证任何层次结构都没有某种类型的授权。
为 Linux 内核选择的机制是使用标准存储种子参数从空种子派生主椭圆曲线密钥。空种子有两个优点:首先,该层次结构在物理上不能具有授权,因此我们始终可以使用它;其次,空种子在 TPM 重置时会发生变化,这意味着如果我们在一天的开始就在空种子上建立信任,则所有使用派生密钥加盐的会话将在 TPM 重置且种子发生变化时失败。
显然,在没有任何其他先前共享秘密的情况下使用空种子,我们必须创建和读取初始公钥,当然,公钥可能会被总线中间人拦截和替换。但是,TPM 具有密钥认证机制(使用 EK 背书证书,创建证明身份密钥并使用该密钥认证空种子主密钥),该机制太复杂而无法在内核中运行,因此我们保留空主密钥名称的副本,该名称通过 sysfs 导出,以便用户空间可以在启动时运行完整的认证。此处的明确保证是,如果空主密钥正确认证,您就知道自一天开始以来的所有 TPM 事务都是安全的,如果它不正确认证,您就知道您的系统上存在中间人(并且启动期间使用的任何秘密可能已被泄露)。
堆叠信任¶
在当前的空主密钥场景中,TPM 必须在将其交给下一个使用者之前完全清除。但是,内核会将派生的空种子密钥的名称传递给用户空间,然后可以通过用户空间中的认证进行验证。因此,这种名称传递链也可以在各种启动组件之间使用(通过未指定的机制)。例如,grub 可以使用空种子方案来确保安全性,并将名称传递到引导区域中的内核。内核可以自己派生密钥和名称,并明确知道如果它们与传递的版本不同,则发生了篡改。因此,可以通过名称传递将任意启动组件链接在一起(UEFI 到 grub 到内核),前提是每个后续组件都知道如何收集名称并针对其派生密钥进行验证。
会话属性¶
内核使用的所有 TPM 命令都允许会话。HMAC 会话可用于检查请求和响应的完整性,解密和加密标志可用于保护参数和响应。HMAC 和加密密钥通常是从共享的授权秘密派生的,但是对于许多内核操作,它是众所周知的(并且通常为空)。因此,内核使用的每个 HMAC 会话都必须使用空主密钥作为盐密钥来创建,从而为会话密钥派生提供加密输入。因此,内核创建一次空主密钥(作为易失性 TPM 句柄),并将其保留在存储在 tpm_chip 中的已保存上下文中,以供每次在内核中使用 TPM。目前,由于内核资源管理器中缺少去间隙,因此必须为每个操作创建和销毁会话,但是将来,单个会话也可以重用于内核中的 HMAC、加密和解密会话。
保护类型¶
对于每个内核操作,我们使用空主密钥加盐 HMAC 来保护完整性。此外,我们使用参数加密来保护密钥密封,使用参数解密来保护密钥解封和随机数生成。
用户空间中的空主密钥认证¶
每个 TPM 都附带几个用于主背书密钥的 X.509 证书。本文档假设证书的椭圆曲线版本存在于 01C00002,但也可以与 RSA 证书(位于 01C00001)同样工作。
认证的第一步是使用来自 TCG EK 凭据配置文件 的模板创建主密钥,这允许将生成的主密钥与证书中的主密钥进行比较(公钥必须匹配)。请注意,生成 EK 主密钥需要 EK 层次结构密码,但 EC 主密钥的预生成版本应存在于 81010002,并且可以对此执行 TPM2_ReadPublic(),而无需密钥授权。接下来,必须验证证书本身以链接回制造商根(该根应在制造商网站上发布)。完成此操作后,在 TPM 中生成证明密钥 (AK),并且可以使用其名称和 EK 公钥来使用 TPM2_MakeCredential 加密秘密。然后,TPM 运行 TPM2_ActivateCredential,仅当 TPM、EK 和 AK 之间的绑定为真时,它才会恢复秘密。现在可以使用生成的 AK 来运行对内核已导出的空主密钥名称的认证。由于 TPM2_MakeCredential/ActivateCredential 有点复杂,因此下面介绍了一种涉及外部生成的私钥的更简化的过程。
此过程是通常基于隐私 CA 的证明过程的简化缩写。 这里的假设是证明由 TPM 所有者完成,因此只能访问所有者层级。 所有者创建外部公/私钥对(在本例中假设为椭圆曲线),并使用内部包装过程包装私钥以进行导入,并以 EC 派生的存储主密钥为父密钥。 TPM2_Import() 是使用一个参数解密 HMAC 会话完成的,该会话会话由 EK 主密钥加盐(也不需要 EK 密钥授权),这意味着内部包装密钥是加密参数,因此,除非 TPM 拥有经过认证的 EK,否则将无法执行导入,因此,如果命令成功并且 HMAC 在返回时进行验证,我们知道我们有一个仅适用于经过认证的 TPM 的可加载私钥副本。 此密钥现在加载到 TPM 中,存储主密钥被刷新(以释放空间用于空密钥生成)。
现在,使用TCG TPM v2.0 配置指南中概述的存储配置文件生成空 EC 主密钥;计算此密钥的名称(公共区域的哈希值),并将其与内核在 /sys/class/tpm/tpm0/null_name 中提供的空种子名称进行比较。如果名称不匹配,则 TPM 已被入侵。如果名称匹配,则用户使用空主密钥作为对象句柄,并使用加载的私钥作为签名句柄执行 TPM2_Certify(),并提供随机的限定数据。返回的 certifyInfo 的签名会根据加载的私钥的公共部分进行验证,并检查限定数据以防止重放。如果所有这些测试都通过,则用户现在可以确信在此内核的整个启动过程中,TPM 的完整性和隐私性都得到了保护。