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,清除 PCRs,然后发送他们自己的测量值,这将有效地覆盖 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,否则 TPM 将无法执行导入,因此如果命令成功并且返回时 HMAC 验证,我们知道我们有一个仅适用于经过认证的 TPM 的可加载私钥副本。此密钥现在已加载到 TPM 中,并且存储主密钥已刷新(以释放空密钥生成空间)。
现在使用 TCG TPM v2.0 供应指南中概述的存储配置文件生成空 EC 主密钥;计算此密钥的名称(公共区域的哈希)并与内核在 /sys/class/tpm/tpm0/null_name 中提供的空种子名称进行比较。如果名称不匹配,则 TPM 已被篡改。如果名称匹配,用户执行 TPM2_Certify(),使用空主密钥作为对象句柄,加载的私钥作为签名句柄,并提供随机限定数据。返回的 certifyInfo 的签名将与加载的私钥的公共部分进行验证,并检查限定数据以防止重放。如果所有这些测试都通过,用户现在可以确信此内核的整个启动序列中 TPM 的完整性和隐私性得到了保持。