Intel 全加密内存技术介绍

市场需求

人们对透明全内存加密这个功能的需求主要来自对机密和敏感数据的保护。普通 RAM 里面储存的数据,在掉电之后,一般都以为是彻底消失了。但其实在一些复杂的离线攻击下,这些数据仍然是能被恢复出来并导致泄密;而持久性存储器(即外存,包括磁盘、SSD、eMMC 等)的数据更加容易泄露。这些设备可能有硬件锁机制的保护,但是用户其实希望的是更细粒度的保护,比如 per 进程 / 容器 /VM 级的。

Intel 制定了 Intel 架构下的内存加密技术规范,即 TME 架构,以及构建在这个架构之上的、能满足更细粒度内存加密需求的 MKTME。

TME 架构

TME 是 Total Memory Encryption 的缩写。 该架构定义并实现了透明全内存加密特性的最基本功能:使用由处理器内的 RNG 生成的单一的临时密钥对全部物理内存(包括 NVRAM)以透明地方式进行加密。 所谓“临时”指的是每次处理器启动时都会通过 RNG 重新生成一把密钥,这把密钥被称为 平台密钥 。一旦开启 TME 特性,TME exclusion 区域以外的全部内存区域全都使用平台密钥进行加解密。

为了防止平台密钥的泄露,软件或处理器接口都无法访问到这把平台密钥。

下图是 TME 架构在一个具有双 NUMA 节点系统中的参考实现图:

在系统启动的早期阶段,BIOS 就会开启该 TME。 一旦开启了该特性,在外部内存总线上和 DIMM 中观测到的一律都是加密后的数据。 能够达到这种效果的原因是处在 cache 与 DRAM/NVRAM 控制器之间的 AES-XTS 加解密引擎对所有进出 cache 的数据一律要进行解密和加密处理。由于 cache、寄存器以及更偏向处理器核一侧的其他微架构层组件仍旧使用明文数据,因此 TME 架构对已有的软件和 I/O 模型来说是完全透明的。

总体而言,TME 架构具有以下特点:

  • 架构和功能简单直接:使用单一的平台密钥对内存进行加密
  • 一些更复杂的安全特性(比如 SGX2 和 TDX)可以轻易地叠加在这个基础架构之上
  • 对软件几乎完全透明,兼容性好
  • 对性能的影响与工作负载强相关

TME 的最大缺点是只能使用一把平台密钥来加密内存,不支持在系统里划分出多个基于加密密钥构建的加密内存 domain;但 MKTME 就支持使用多把密钥,进而实现 per 进程 / 容器 /VM 粒度的加密内存 domain。

MKTME

MKTME 是 Multi-Key Total Memory Encryption 的缩写。它在 TME 架构的基础上,实现了以页为粒度、支持使用多把密钥对内存进行加密的功能,同时还允许由软件设置 AES-XTS 加解密引擎所使用的密钥。

下图是将 MKTME 用在虚拟化场景中的一个示例图:

在这个示例中:

  • Hypervisor 使用 KeyID 0 (即 TME 定义的平台密钥) 来访问所有的加密内存
  • VM1 和 VM2 都可以使用 KeyID 0 来访问自己的加密内存
  • VM1 使用 KeyID 1 来访问自己的私有加密内存
  • VM2 使用 KeyID 2 来访问自己的私有加密内存
  • VM1 和 VM2 可以使用 KeyID 3 来访问两个 VM 共享的加密内存

KeyID 字段被包含在 PTE 中,且位于物理地址字段的高位,就像是物理地址字段的一部分(即通过减少一部分物理地址宽度来实现),同时被用在处理器内部的微架构层中(除了 AES-XTS 引擎),这个特性叫做 物理地址位超卖 (oversubscribing)。该特性使物理地址具有了别名,即具有相同物理地址的页可以有不同的 KeyID。

KeyID 信息是不会出现在处理器外部的(比如内存总线上)。物理地址位超卖不会影响 cache 和 TLB 的行为,因为 KeyID 仅被当做成物理地址的一部分来处理;但物理地址位超卖会影响大多数的页表类型:Host 普通 IA 页表、EPT 和 IOMMU 页表。

  • IA paging

    MKTME 会影响 Host 侧的 IA paging(含每一级页表),即在物理地址字段的高位中包含 KeyID 字段;CR3 寄存器也受此影响,也包含了 KeyID。

    MKTME 不会影响 Guest 中的 IA paging,因为 Guest 中的 IA paging 用于产生 GPA,而不是最终的 HPA,所以设计设计上没必要在 Guest 的 IA paging 中设置 KeyID 字段,即 GPA 中不包含 KeyID,因此也无法为 VM 的应用再提供更细粒度的密码学隔离(即 per VM 应用级的 KeyID)。

  • EPT paging

    MKTME 会影响 EPT paging(含每一级页表),因为 EPT 用于将 GPA 映射到 HPA,而 HPA 必须要包含 KeyID。

  • 其他物理地址

    其他的物理地址结构(如 VMCS 指针、物理地址位图等)也都需要包含 KeyID。

虽然前面的例子中 Hypervisor 使用的是 KeyID 0,但 Hypervisor 具有特权,可以使用任意 KeyID 访问自己的加密内存,也能管理和设置每个 VM 所能使用的 KeyID。

MKTME 支持的密钥数量总是固定的,而具体数量由特定的处理器实现来决定。软件可以通过配置只使用其中的部分密钥,这组密钥被称为可用密钥。软件负责管理可用密钥,并可以使用可用密钥对任意一个内存页进行加密。

在软件不进行任何显式配置的情况下,MKTME 引擎默认使用 TME 的平台密钥进行内存加密。 MKTME 也允许使用软件提供的密钥或处理器 RNG 生成的密钥。 在虚拟化场景中,VMM 或 Hypervisor 负责管理每个 VM 所使用的密钥,并透明地对 Guest OS 实施加密保护(在这个场景中,可以将 MKTME 视为 TME 虚拟化技术)。

总而言之,MKTME 希望在系统层面能够创建 多个独立的加密内存 domain 。这对用户来说也更加安全。

安全威胁模型分析

TME 和 MKTME 的安全性依赖于特权软件(OS 和 VMM/Hypervisor),这点与传统虚拟化技术的安全边界完全一致。假设在攻击者拥有特权的情况下,攻击者能将所有物理页的加密模式都改为非加密模式。事实上只要攻击者拥有特权,就已经能够访问任意内存了,只不过需要使用正确的 KeyID 来访问 per 进程 / 容器 /VM 实例的加密内存,比如在访问 VM 实例内的数据前需要在 EPT PTE 中找出正确的 KeyID,然后建立一个使用该 KeyID 的 PTE 映射来访问该物理页。

此外,TME 和 MKTME 没有对数据提供完整性保护,因此软件使用错误的 KeyID 访问加密内存、或直接篡改加密内存中的内容都是可行的(比如纯粹的破坏数据的行为,或是用已知明文的密文来覆盖以实施重放攻击)。

由于软件和处理器接口无法访问到 TME 平台密钥以及 MKTME 中由处理器硬件自生成的密钥,因此密钥本身是存储安全的;但由软件提供的 MKTME 密钥可能会因调用者考虑不周而遭到泄露,这个难题需要软件设计者自己来解决。

由于 cache 中的数据是明文的,因此 TME 和 MKTME 无法抵御像 L1TF 这种利用处理器 speculative execution 侧信道漏洞的攻击方式来间接 probe cache 中的明文数据的这种攻击方式。

综上所述, 由于特权软件仍有足够的权限来降低 TME 和 MKTME 的安全性,因此 TME/MKTME 技术目前还不属于机密计算的范畴,即无法做到哪怕在被攻破的 OS/VMM 环境里也能够保护租户机密数据的强度。 TME 和 MKTME 防范的攻击路径是从恶意 VM 实例到 Hypervisor。更具体来说,只要攻击者无法跨域安全域(指从 guest ring0 到 host ring0)且在软件采用了正确配置的情况下,TME 和 MKTME 就能够在一定程度上抵御恶意 VM 实例对 Host 或其他 VM 实例的数据泄露攻击;但前提是租户必须信任 CSP(即 Hypervisor 和 host 所能提供的安全性能够抵御攻击者的入侵,同时 CSP 自身不会作恶)和 Intel CPU。

目前 Intel MKTME 也在继续进行迭代,相信很快就能看到能与 AMD SEV 中防止特权软件修改 VM 实例内存加密模式的保护机制了。

Linux 支持情况

在 Icelake 这一代不会在 Linux kernel upstream 中提供对 MKTME 的单独支持了,需要等到 Intel 下一代处理器;但 Icelake 不单独支持不意味着这个技术没用。Icelake 支持 SGX2,而 SGX2 的 MEE 相较于 SGX1 就改用 MKTME 提供内存加密的能力,只不过这个 MKTME 对系统软件不可见。

目前 Linux upstream 对 MKTME 的支持仅仅提供了最最基本的 CPU 枚举和 PCONFIG 的实现,之前提的一些 patch 也没有合入 upstream。

参考