HyperEnclave:一个开放且跨平台的可信执行环境

HyperEnclave:一个开放且跨平台的可信执行环境
XRHyperEnclave:一个开放且跨平台的可信执行环境
本文是论文《HyperEnclave: An Open and Cross-platform Trusted Execution Environment》的原文翻译
摘要
学术界和工业界已经提出了许多可信执行环境(TEE)。然而,它们中的大多数都需要特定的硬件或固件更改,并且与特定的硬件供应商(如英特尔、AMD、ARM 和 IBM)绑定。在本文中,我们提出了 HyperEnclave,这是一个开放且跨平台的、基于进程的 TEE,它依赖于广泛可用的虚拟化扩展来创建隔离的执行环境。特别是,HyperEnclave 旨在支持灵活的 enclave 操作模式,以满足各种 enclave 工作负载下的安全和性能需求。我们提供了 enclave SDK,以便在 HyperEnclave 上运行现有的 SGX 程序,只需很少或无需更改源代码。我们已在商用 AMD 服务器上实现了 HyperEnclave,并将该系统部署在一家世界领先的金融科技公司,以支持真实的隐私保护计算。在微基准测试和应用基准测试上的评估表明,HyperEnclave 的设计只引入了很小的开销。
1 引言
近年来,由于对能够处理海量数据样本的隐私保护数据处理技术的高需求,可信执行环境(TEE)正作为一种新的计算范式——机密计算——而兴起。TEE 提供了硬件强制的内存分区,敏感数据可以在其中被安全地处理。现有的 TEE 设计支持不同级别的 TEE 抽象,例如基于进程的(英特尔的软件防护扩展(SGX)[55])、基于虚拟机的(AMD SEV [45])、分离的世界(ARM TrustZone [16])以及混合模式(Keystone [49])。目前,TEE 最突出的例子是英特尔 SGX,它在商用现成品(COTS)台式机和服务器处理器中广泛可用。
动机。 当今大多数 TEE 技术都是闭源的,并且需要特定的硬件或固件更改,这些更改难以审计、演进缓慢,因此不如基于公开算法和广泛可用硬件的密码学替代方案(如同态加密)。此外,大多数现有的 TEE 设计限制了 enclave(即受保护的 TEE 区域)只能在固定模式下运行。这很难满足需要由 TEE 保护的各种类型应用的安全和性能要求。例如,英特尔 SGX enclave 在用户模式下运行,无法访问特权资源(如文件系统、IDT 和页表)和处理特权事件(中断和异常)。因此,运行 I/O 密集型和内存要求高的任务会导致显著的性能下降。
为了填补这一空白,我们在本文中提出了 HyperEnclave 的设计,以支持机密云计算,它可以在云中现成的传统服务器上安全运行,也可以在新兴的 ARM(或未来的 RISC-V)服务器上运行,而无需特定的硬件功能。为此,我们的设计使用广泛可用的虚拟化扩展(用于隔离)和 TPM(用于信任根和随机性等)提供了一个基于进程的 TEE 抽象。为了更好地满足特定 enclave 工作负载的需求,HyperEnclave 支持灵活的 enclave 操作模式,即 enclave 可以在不同的特权级别运行,并且可以访问某些特权资源(详见第 4 节)。
设计细节。 在我们的设计中,系统以三种模式运行。一个名为 RustMonitor 的可信软件层(用 Rust 编写的安全监视器)在监视器模式下运行,该模式映射到 VMX root 模式。RustMonitor 负责强制隔离,是可信计算基(TCB)的一部分。非可信操作系统(称为主操作系统)为应用程序的非可信部分提供执行环境;非可信操作系统和应用程序部分在正常模式下运行,该模式映射到 VMX non-root 模式。应用程序的可信部分(即 enclave)在安全模式下运行,可以灵活地映射到 VMX non-root 模式的 ring-3 或 ring-0,或 VMX root 模式的 ring-3。
内存隔离由内存管理单元(MMU)的基于硬件的内存保护强制执行。正如我们观察到现有的基于进程的 TEE(例如,Inktag [38] 和英特尔 SGX [55])容易受到基于页表的攻击 [74],我们的内存隔离方案选择完全由可信代码管理 enclave 的页表和页错误事件,从而消除了主操作系统的参与。该设计还防止了某些类型的 enclave 恶意软件攻击(第 3.2 节)。
为了最小化攻击面,我们采用了一种称为”度量延迟启动”的方法:首先启动主操作系统内核;然后,一段作为主操作系统内核模块实现的特殊内核代码运行,以在最高特权级别(即监视器模式)初始化 RustMonitor,并将主操作系统降级到正常模式。启动过程中的所有已启动组件都被度量并扩展到 TPM 平台配置寄存器(PCRs)。由于 TPM 证明保证了 PCRs 不能回滚,该设计确保了 RustMonitor 的安全启动;否则,在远程证明期间将检测到 TPM quote 的违规。
我们已在商用 AMD 服务器上实现了 HyperEnclave。RustMonitor 总共包含约 7,500 行 Rust 代码。我们的 enclave SDK 的 API 与官方 SGX SDK 兼容。因此,为 SGX 编写的代码可以通过重新编译,只需很少(或没有)源代码更改,就可以轻松地移植到 HyperEnclave 上运行。我们已经将一些 SGX 应用程序以及 Rust SGX SDK [71] 和 Occlum 库操作系统 [64] 移植到了 HyperEnclave。微基准测试显示,ECALL 和 OCALL 的开销分别小于 9,700 和 5,260 个周期(在英特尔 SGX 上分别为 14,432 和 12,432 个周期)。在一系列真实世界应用上的评估表明,开销很小(例如,在 SQLite 上的开销仅为 5%)。
贡献。 总而言之,本文提出了 HyperEnclave 的设计,具有以下贡献:
- 一个开放的、跨平台的、基于进程的 TEE,具有最低的硬件要求(虚拟化扩展和 TPM),可以运行现有的 SGX 程序,只需很少或没有源代码更改,从而能够重用英特尔 SGX 丰富的工具链和生态系统。
- 支持灵活的 enclave 操作模式,以满足 enclave 应用程序多样化的安全和性能需求,而无需硬件或固件更改。
- 一种内存隔离方案,其中 enclave 的页表和页错误完全由可信代码管理,这减轻了基于页表的攻击和 enclave 恶意软件攻击。
- 一种度量延迟启动方法,结合基于 TPM 的证明来减少攻击面。
- 在商用服务器上(主要)使用内存安全语言 Rust 实现,并在真实硬件和应用上进行评估,证明了所提出的设计是实用的,并且只有很小的开销。
2 背景
2.1 可信执行环境
可信执行环境(TEE)旨在确保敏感数据在一个隔离和可信的环境中被存储、处理和保护。隔离区域可以是一个独立于正常操作系统的系统(如 TrustZone [16] 的安全世界),也可以是进程地址空间的一部分(如英特尔 SGX [55] enclave),或一个独立的虚拟机(如受 AMD SEV [45] 或英特尔 TDX [41] 保护的虚拟机)。为了抵抗特权攻击者,TEE 不仅需要挫败操作系统级别的对手,还需要防御对平台有物理访问权限的恶意方。为此,它提供了硬件强制的安全功能,包括隔离执行、enclave 的完整性和机密性保护,以及通过远程证明来验证在可信平台内运行的代码的能力。
隔离。 TEE 的核心是内存隔离方案,它保证了 enclave 的代码、数据和运行时状态不能被非可信方访问或篡改。对于英特尔 SGX,受保护的内存(即 enclave)被映射到一个称为 Enclave 页缓存(EPC)的特殊物理内存区域,该区域被加密,不能被其他软件、固件、BIOS 和直接内存访问(DMA)直接访问。
证明。 远程证明的目标是生成一个证明 quote,其中包括对软件状态的度量,并用嵌入在硬件中的证明密钥签名。远程用户通过检查签名(反映硬件身份)和度量(证明软件状态)来验证 quote 的有效性。
2.2 可信平台模块
可信平台模块(TPM)既是行业标准 [36],也是一个用于安全加密处理器的 ISO/IEC 标准 [4]。几乎所有的个人电脑和服务器制造商都在使用它。固件 TPM(fTPMs)是基于固件(例如 UEFI)的 TPM 实现。在撰写本文时,英特尔、AMD 和高通都已实现 fTPM。
TPM 有一组平台配置寄存器(PCRs),可用于在启动过程中度量已启动的代码。PCRs 在系统重启或开关机时被重置为零。在每个启动过程中,PCRs 只能用新的度量值进行扩展(称为 PCR extend),因此不能被设置为任意值。
每个 TPM 都带有一个独特的非对称密钥,称为签注密钥(EK),由制造商嵌入作为信任根。TPM 可以生成 PCR 值的 quote,用 TPM 证明身份密钥(AIK)签名,而 AIK 在 TPM 内部生成并使用 EK 认证。对已启动代码的任何修改都将反映在 quote 中。收到 quote 后,远程方可以验证签名密钥来自一个真实的 TPM,并可以确信 PCR 摘要报告没有被更改。
2.3 威胁模型
与其他 TEE 提议 [23, 49] 一样,我们信任底层硬件,包括建立基于虚拟化的隔离的处理器、系统管理模式(SMM)代码以及 TPM。我们假设测量核心信任根(CRTM)是可信且不可变的。HyperEnclave 通过硬件对内存加密的支持,减轻了某些物理内存攻击,例如冷启动攻击和总线嗅探攻击。我们不完全信任操作员,并假设攻击者在启动过程中无法进行物理攻击,即我们假设系统最初是良性的(在系统启动期间),并且启动阶段的早期操作系统是 TCB 的一部分。这可以通过两种方式实现。
- 首先,开机事件可以用硬件设备(如 HSM,即硬件安全模块)来保护。平台只有在拥有 HSM 的可信方的参与和监督下才能进入启动过程。之后,负责维护的操作员是不被信任的。
- 其次,可以增强启动过程以防御具有物理访问权限的对手。为了防止 I/O 攻击,我们可以强化操作系统以移除不必要的设备,并在启用 IOMMU 之前禁用外围设备的 DMA 功能。我们可以在早期阶段(例如,在 BIOS 中,在任何片外内存被使用之前)启用内存加密以防止物理内存攻击。
然而,在 RustMonitor 启动后,主操作系统被降级到正常模式,并可能受到攻击者的控制,攻击者可能会试图破坏 RustMonitor 或 enclave,例如,试图直接或通过 DMA 访问受保护的内存。我们认为 enclave 代码可能是恶意的或由于内存错误而被攻击者控制。我们的设计需要防止一个受损的 enclave 污染其他 enclave 或 RustMonitor。我们还防止了针对主操作系统或应用程序代码的攻击,如 [63] 中所述。与其他 TEE 类似,在本文中,我们不关注拒绝服务(DoS)攻击或侧信道攻击的预防,例如缓存时序和推测执行攻击 [48]。
3 设计
HyperEnclave 旨在支持机密云计算,而无需特定的硬件功能。因此,HyperEnclave 是建立在广泛可用的虚拟化扩展之上的。特别是,HyperEnclave 旨在支持基于进程的 TEE 模型(类似于英特尔 SGX),原因如下。
- 最小化 TCB。 使用基于进程的 TEE 保护应用程序时,TCB 仅包括受保护的代码本身,而在其他形式的 TEE 中,必须包括更多的代码,例如基于 VM 的 TEE 的客户机操作系统。
- 已建立的生态系统。 由于英特尔 SGX 目前是云中支持最普遍的 TEE(包括 GCP、Azure 和阿里云在内的主要 CSP 都提供基于 SGX 的实例 [9, 62]),已经开发了丰富的工具链和应用程序。支持 SGX 模型减少了移植工作,并使其易于在云中部署机密计算任务。
- 云计算趋势。 我们已经看到了在云中运行基于容器的无服务器应用程序的明显趋势。使用 TEE 保护这些应用程序免受非可信云的攻击非常重要。考虑到这类计算任务通常是短暂的,并且偏好较短的启动时间,维护一个虚拟机似乎过于重量级。
在本节中,我们使用 x86 符号介绍 HyperEnclave,因为我们在 AMD 服务器上对 HyperEnclave 进行了原型设计。
3.1 系统概述
HyperEnclave 支持以下模式:监视器模式,即 VMX root 操作模式;用于主操作系统和应用程序非可信部分的正常模式,即分别为 VMX non-root 操作模式的 ring-0 和 ring-3;以及用于 enclave 的安全模式,根据 enclave 操作模式,它可以是 VMX non-root 操作模式的 ring-3 和 ring-0,或 VMX root 操作模式的 ring-3。我们将在第 4 节中介绍 HyperEnclave 支持的灵活操作模式。如图 1 所示,HyperEnclave 由以下组件组成:
- RustMonitor 是一个在监视器模式下运行的轻量级虚拟机管理程序,它管理 enclave 内存,强制执行内存隔离,并控制 enclave 状态转换。它作为一个资源监视器工作,而复杂的任务则被卸载到主操作系统。
- RustMonitor 创建一个独特的客户机虚拟机(称为正常虚拟机),该虚拟机运行主操作系统(如 Linux)并在正常模式下托管应用程序的非可信部分。主操作系统仍然负责进程调度和 I/O 设备管理,但它不被 RustMonitor 和 enclave 信任。
- 应用程序是在主操作系统中运行的应用程序的非可信部分。
- 内核模块。我们在主操作系统中提供一个内核模块,用于加载、度量和启动 RustMonitor,以及调用模拟的特权操作。
- 为了方便开发,HyperEnclave 提供了一个 enclave SDK,其 API 与官方英特尔 SGX SDK [12] 兼容,包括非可信运行时和可信运行时(即 SDK uRTS 和 SDK tRTS)。因此,大多数 SGX 程序只需很少或没有源代码更改即可在 HyperEnclave 上运行。
- Enclave 是在安全模式下运行的应用程序的可信部分。
3.2 内存管理和保护
挑战。 对于基于进程的 TEE,enclave 在用户模式下运行,无法管理自己的页表。现有设计(例如,英特尔 SGX、TrustVisor [54])允许非可信操作系统管理 enclave 的页表。为了防止内存映射攻击(即通过操纵 enclave 的地址映射进行的攻击,如图 9,附录 A.1 所示),SGX 的设计扩展了缺页处理程序(PMH)并引入了一个名为 EPCM 的新元数据,用于对 TLB 未命中进行额外的安全检查 [32]。在没有安全硬件支持的情况下,一种普遍的软件解决方案 [19, 54, 75] 是通过设置持有页表的页的页表项(PTEs)来使页表写保护,即对页表的任何更新都会陷入虚拟机管理程序然后进行验证。然而,在 x86 平台上,PTE 的访问位和脏位的更新也会陷入虚拟机管理程序,导致不可忽略的开销。更糟糕的是,由于 enclave 页错误也由操作系统处理,上述设计仍然容易受到基于页表的攻击,例如受控信道攻击 [74]。
支持 enclave 动态内存管理(即 SGX2 平台上的 EDMM [34])时,设计变得更具挑战性,即在 enclave 初始化后动态添加或删除 enclave 页面,或更改 enclave 页面属性或类型。没有 EDMM,enclave 可能曾经使用的所有物理内存都必须在 enclave 初始化之前提交。因此,EDMM 减少了 enclave 构建时间并启用了新的 enclave 功能,例如按需堆栈和堆增长,以及按需创建代码页以支持即时(JIT)编译。在 SGX2 平台上,enclave 需要通过 OCALL 将 EDMM 请求发送到 SGX 驱动程序,然后由驱动程序进行请求的更改。由于驱动程序不受 enclave 信任,因此更改需要由 enclave 明确检查和接受才能生效,这涉及到繁重的 enclave 模式切换。
HyperEnclave 内存管理。 我们观察到,上述挑战根源于 enclave 的页表和页错误都由主操作系统管理。在 HyperEnclave 中,尽管 enclave 仍然是应用程序地址空间的一部分,我们为 enclave 创建了一个单独的页表,并让 RustMonitor 管理 enclave 的页表和页错误,而无需主操作系统的参与³,而正常虚拟机中的页表仍由主操作系统管理。然而,该设计面临新的挑战:由于 enclave 可以访问应用程序的整个地址空间,一旦应用程序中页表的映射发生变化,例如由于页面交换,更新的映射需要同步到由 RustMonitor 管理的 enclave 页表中。
为了消除同步开销,我们在应用程序的地址空间中预分配一个编组缓冲区,该缓冲区与 enclave 共享。编组缓冲区的映射在整个 enclave 生命周期内通过预填充物理内存并将其固定在内存中来保持固定。enclave 和应用程序之间交换的所有数据都必须通过编组缓冲区传递。应用程序的内存映射(除了编组缓冲区的映射)对 enclave 是不需要的,并且不包括在 enclave 的页表中。这样的设计也减轻了已知的 enclave 恶意软件攻击 [63],因为 enclave 不能访问应用程序的地址空间,只能访问编组缓冲区(详见第 6 节)。我们提醒攻击者可能会操纵编组缓冲区,但这不会导致额外的安全问题,因为该缓冲区在设计上是不受信任的,开发者有责任确保通过该缓冲区传输的数据是真实的并受到保护(与 SGX 模型相同)。
当 enclave 访问一个未提交物理页的虚拟地址时(例如,由于页面交换或 EDMM),会引发一个页错误,enclave 会陷入 RustMonitor。RustMonitor 从 enclave 内存池中挑选一个空闲页,在 enclave 的页表中插入一个新的映射,并恢复 enclave 的执行。当 enclave 请求更改页面权限时,enclave 会向 RustMonitor 发出一个 hypercall,以更新 enclave 页表中的权限并清除相应的 TLB 条目。⁴
HyperEnclave 内存隔离。 图 2 显示了正常虚拟机内应用程序和 enclave 的内存映射。正常虚拟机内的应用程序内存通过嵌套分页进行管理,而 enclave 的内存可以通过嵌套分页或通过正常的 1 级地址转换进行管理,具体取决于相应的操作模式(第 4 节)。因此,HyperEnclave 强制执行以下安全要求。
- R-1: 主操作系统和应用程序不允许访问属于 RustMonitor 和 enclave 的物理内存。
- R-2: enclave 不允许访问属于 RustMonitor 和其他 enclave 的物理内存。它被设计为只能访问与非可信应用程序共享的特定内存区域以进行参数传递(即编组缓冲区)。
- R-3: 不允许恶意外围设备通过 DMA 访问属于 RustMonitor 和 enclave 的物理内存。为了防止此类攻击,HyperEnclave 在现代处理器中利用输入输出内存管理单元(IOMMU)的支持来限制外围设备使用的物理内存。
内存加密。 为了挫败物理内存攻击,例如冷启动和总线嗅探攻击,HyperEnclave 可以利用硬件内存加密(例如 AMD SME [44] 和 Intel MKTME [42])来以页面粒度加密部分物理内存。如果平台不支持硬件内存加密,HyperEnclave 可以考虑应用软件方法 [76] 来加密隔离的内存。然而,与基于硬件的解决方案相比,这种方法可能会带来巨大的开销。
3.3 可信启动、证明和封装
度量延迟启动。 HyperEnclave 的启动过程如图 3 所示。系统启动时,一段称为测量核心信任根(CRTM)的静态且不可变的代码首先执行,以引导构建后续固件和软件的测量链,包括 BIOS、grub、主操作系统内核和 initramfs。测量值存储在 TPM PCR 中,用于每个启动组件,因此任何修改都将反映在证明 quote 中。
为了减少来自主操作系统的攻击面,我们将 RustMonitor 镜像放入 initramfs。内核测量 RustMonitor 镜像并将值扩展到 TPM PCR,然后它在早期用户空间中启动 RustMonitor,即在任何依赖于磁盘文件系统的用户空间程序开始运行之前。结合度量启动,它确保了 RustMonitor 加载时的软件状态是可信的。RustMonitor 加载后,执行在预定义的入口点继续。RustMonitor 设置自己的运行上下文(如堆栈、页表、IDT 等)并为每个 CPU 准备虚拟 CPU(vCPU)配置。然后 RustMonitor 启动正常虚拟机并将主操作系统降级到正常模式。返回内核模块后,内核在正常模式下继续启动,并且不知道 RustMonitor 的存在。
HyperEnclave 应用上述方法(称为度量延迟启动),以便 RustMonitor 作为类型-2 虚拟机管理程序(如 KVM)加载,同时作为类型-1 虚拟机管理程序(如 Xen)运行。通过这种方式,在主操作系统被降级到正常模式后,RustMonitor 不再需要信任主操作系统。
远程证明。 通过度量延迟启动,所有启动的组件都被测量并扩展到 TPM。在 RustMonitor 启动后,它需要将信任扩展到 enclave。为此,RustMonitor 派生一个证明密钥对,用于签署 enclave 测量值。然后 RustMonitor 将派生的公钥扩展到 TPM PCR,私钥永远不会离开受内存隔离和加密保护的 RustMonitor。
在 enclave 创建期间,添加到 enclave 的所有页面(包括相应的页面内容、页面类型和 RWX 权限)都由 RustMonitor 测量,以生成 enclave 测量值。(中间)测量值存储在 RustMonitor 的内存中,对 enclave 和主操作系统是不可见的。
与 TPM 和英特尔 SGX 类似,HyperEnclave 采用 SIGn-and-MAc (SIGMA) 证明协议进行远程证明流程。如图 4 所示,我们将 RustMonitor 的证明密钥的公钥表示为虚拟机管理程序证明公钥(hapk)。enclave 测量值使用 RustMonitor 的证明密钥签名,形成 enclave 测量签名(ems)。TPM quote TMP_Quote,使用 TPM 证明密钥签名,包括所有启动代码测量的 PCRs,以及 hapk 的测量值。收到证明报告后,远程用户可以通过比较启动代码(包括 CRTM、BIOS、grub、内核、initramfs 和虚拟机管理程序)和 enclave 的测量值,以及验证生成签名的证书链来验证报告。
密钥生成。 当 RustMonitor 首次初始化时,它从 TPM 的随机数生成器(RNG)模块生成一个根密钥 Kroot。Kroot 使用 TPM 的封装操作存储在 TPM 之外。在系统重置的启动过程中,RustMonitor 使用 TPM 的解封操作解密 Kroot,这保证了 Kroot 只能在完全相同的 TPM 芯片和匹配的 PCR 配置下被解封。此外,在将控制权转移给主操作系统之前,RustMonitor 用一个常量填充 PCRs,以防止其检索 Kroot。所有其他密钥材料,包括 enclave 的封装密钥和报告密钥,都由 Kroot 和 enclave 的测量值派生而来。
3.4 Enclave SDK
将现有应用程序移植到 enclave 中可能很麻烦,因为 TEE 通常暴露有限的硬件和软件接口,并提供额外的安全服务(例如,证明和封装)。对于基于进程的 TEE,应用程序需要被划分为可信和非可信部分,并且需要仔细设计接口以避免各种安全陷阱 [27, 46, 69]。由于英特尔 SGX 在市场上的主导地位,已经投入了大量精力并开发了许多工具,包括库操作系统 [64, 67]、容器 [18]、自动分区和保护工具 [50, 68]、WebAssembly 微运行时 [57] 和接口保护 [65]。因此,英特尔 SGX 已经支持安全地运行用 C/C++、Rust、Java、Python 等编写的应用程序,而无需昂贵的代码重构。
我们提供的 enclave SDK 的 API 与官方英特尔 SGX SDK 兼容,以简化在 HyperEnclave 上的应用程序开发。enclave SDK 是对官方 SGX SDK 的改造。通过用 hypercall 替换 SGX 用户叶函数(例如,EENTER、EEXIT 和 ERESUME),SGX 程序可以在 HyperEnclave 上运行,只需很少或没有源代码更改。一旦 enclave 执行这些用户叶函数,它就会陷入 RustMonitor,RustMonitor 会模拟相应 SGX 指令的功能。
enclave 被编译为应用程序的可信库,而应用程序本身在主操作系统中运行。enclave 的生命周期通过模拟一组特权 SGX 指令(即 ECREATE、EADD、EINIT 等)来管理。为此,在主操作系统中运行的内核模块通过调用 RustMonitor 的 hypercall 来提供类似的功能,并通过 ioctl() 接口向应用程序公开这些功能。通过模拟特权 SGX 指令,RustMonitor 负责 enclave 生命周期管理(第 4 节)。
为了与官方英特尔 SGX SDK 兼容,HyperEnclave 中涉及的大多数数据结构(例如 SIGSTRUCT 结构、SECS 页面和 TCS 页面)都与 SGX 相似。通过 HyperEnclave 的设计,在 enclave 中支持动态 enclave 管理非常直接,因为 enclave 内存和页错误都由 RustMonitor 管理。通过为 enclave 中的每个线程关联一个 TCS 页面来支持 enclave 内的多线程。通过为每个 TCS 设置超过 1 个 SSA 页面来支持 enclave 内的异常处理。由于空间限制,细节被省略,我们建议读者参考 SGX 手册 [11] 以获取更多细节。
4 灵活的 Enclave 操作模式
现有的大量应用程序可以被卸载到 TEE 中,例如计算密集型任务(机器学习 [60])、输入输出(IO)密集型任务(如 Apache 和 Nginx Web 服务器 [18])、内存密集型任务(Redis 和 Memcached [18]),以及偏好在 enclave 内进行异常处理和特权分离的任务 [21]。大多数 TEE 只支持在固定模式下运行 enclave,特别是英特尔 SGX(以及 TrustVisor [54] 和 Secage [51])enclave,作为应用程序地址空间的一部分,在用户模式下运行。因此,用户模式 enclave 不允许访问特权资源(如 IDT 和页表)和处理特权事件(中断和异常)。它必须切换到非可信代码才能访问特权资源和处理事件。IO 密集型和内存密集型任务基本上涉及频繁的 world switch,这些切换是昂贵的,并引入了不可忽略的性能损失,尽管已经提出了软件和硬件优化来尝试减少上下文切换延迟 [61, 66, 73]。在本节中,我们介绍 HyperEnclave 支持的三种 enclave 操作模式,如图 5 所示。不同 enclave 操作模式下的 world switch 如图 6 所示。
图 5:基于进程的 TEE 支持的 enclave 操作模式比较。 (a) 英特尔 SGX 在主机用户模式(或虚拟化环境中的客户机用户模式)下运行 enclave。(b) TrustVisor 在客户机用户模式下运行受保护的代码(应用程序逻辑片段,PALs)。(c) HyperEnclave 支持 3 种共存的 enclave 操作模式:① 在客户机用户模式下运行的 GU-Enclaves;② 在客户机特权模式和可选的客户机用户模式下运行的 P-Enclaves;③ 在主机用户模式下运行的 HU-Enclaves。
4.1 客户机用户 Enclaves
客户机用户 enclave (GU-Enclave) 是基本的 enclave 操作模式,通常运行计算密集型任务。enclave 在客户机用户模式下运行(即 VMX non-root 操作模式的客户机 ring-3)。
在 enclave 创建期间,RustMonitor 准备一个 vCPU 结构,其中包含一个客户机页表(GPT)和一个用于 GU-Enclave 的嵌套页表(NPT)。在正常虚拟机和 enclave 虚拟机之间进入和退出时,RustMonitor 会相应地切换 vCPU 状态(例如指令指针、线程指针、NPT 和 GPT)。
为了处理 enclave 运行期间的中断和异常,RustMonitor 配置 vCPU 以将所有中断和异常陷入监视器模式。然后 RustMonitor 保存 enclave 的上下文,将中断或异常转发到正常虚拟机。在主操作系统完成处理中断或异常后,应用程序调用 ERESUME hypercall,该 hypercall 陷入 RustMonitor 以恢复 enclave 的上下文并继续执行 enclave。
4.2 主机用户 Enclaves
主机用户 enclave (HU-Enclave) 在主机用户模式下运行。它通过用环切换(syscalls:在我们的平台上约 120 个 CPU 周期)替代模式切换(hypercalls:在我们的平台上约 880 个 CPU 周期)来提供最佳的 world switch 效率(图 6)。它进一步消除了 GU-Enclave 中的额外虚拟化开销(例如 vCPU 上下文切换和二维页表遍历)。根据我们在第 7 节的评估,HU-Enclave 可能有利于 I/O 密集型工作负载。相比之下,在客户机用户模式下运行 enclave 提供了更深的防御层次。
加载 HU-Enclave 时,RustMonitor 准备一个进程上下文,例如创建一个 1 级页表。在 enclave 进入时,RustMonitor 更新 CPU 状态并调用系统调用返回指令(即 x86 平台上的 SYSRET)以进入 HU-Enclave。相应地,在 enclave 退出时,HU-Enclave 调用系统调用指令(即 x86 平台上的 SYSCALL)并陷入 RustMonitor。ENCLU 叶指令(例如,EGETKEY、EREPORT)被模拟为系统调用。HU-Enclaves 内的中断和异常也会陷入 RustMonitor。其过程与第 4.1 节中描述的 GU-Enclaves 相似。
图 6:支持的 enclave 操作模式的 world switch。 (a) 使用 hypercall 进入和退出 GU-Enclave 和 P-Enclave。(b) 使用 syscall 和 syscall 返回进入和退出 HU-Enclave。
4.3 特权 Enclaves
受基于 VM 的 TEE(如 AMD SEV [45])的启发,HyperEnclave 支持在客户机特权模式下运行的特权 enclave (P-Enclaves)。P-Enclave 被允许访问 GDT、IDT 和 1 级页表,这有利于各种应用,如 Dune [21] 所示。一个这样的例子是垃圾收集器,它是 Java 应用程序的一个基本特性(现有工作将 JVM 移植到 enclave [26, 43])。垃圾收集器频繁更改页面权限以触发页错误,以便跟踪页面状态。对于用户模式 enclave(例如,GU-Enclaves 和 HU-Enclaves),它必须涉及主操作系统来更新页表和处理页错误,这由于 world switch 而遭受巨大的性能损失。P-Enclaves 通过支持 enclave 内异常处理和 1 级页表管理来消除 world switch。更具体地说,P-Enclaves 配置自己的异常处理程序来处理某些异常(如页错误)。RustMonitor 将白名单中的异常传递给 P-Enclave,并将其他异常转发给主操作系统。此外,P-Enclaves 还可以支持基于页表的 enclave 内隔离方案,例如,沙箱化非可信的第三方库。
由于能够在 enclave 内接收中断,P-Enclaves 还可以通过计算频率来检测异常中断事件,然后请求 RustMonitor 将它们路由到主操作系统。因此,可以检测和减轻现有的基于中断的侧信道攻击 [24, 37, 40, 58, 59, 70]。由于空间限制,我们把对这方面的进一步探索留给未来的工作。
5 实现
我们报告了我们在支持硬件虚拟化技术和内存加密的 AMD 平台上实现 HyperEnclave 的情况。在当前实现中,RustMonitor 包含约 7,500 行主要用 Rust 编写的代码,主操作系统的内核模块有约 3,500 行 C 代码。此外,我们对官方英特尔 SGX SDK(版本 2.13)进行了约 2,000 行代码的更改。
5.1 RustMonitor
RustMonitor 运行在最高特权级别,并为 enclave 强制执行隔离。为了减少由内存损坏或并发错误引起的风险,我们主要用内存安全的语言 Rust 实现了 RustMonitor,只有几行汇编代码用于上下文切换。与现有的虚拟机管理程序如 KVM [47] 和 Xen [29] 相比,RustMonitor 小得多,因此更容易进行形式化验证。我们正在进行 RustMonitor 的形式化验证,并计划将结果作为单独的报告发布。
平台启动时,我们在 grub 中配置内核命令行参数以保留物理内存区域,这些区域专供 RustMonitor 和 enclave 使用。RustMonitor 通过维护一个空闲页面列表来管理保留的物理内存。当需要一个 enclave 页面时,例如,在 enclave 创建期间添加一个 enclave 页面时,从池中检索一个空闲页面;当 enclave 页面被释放时,该页面再次附加到列表中。此外,RustMonitor 还管理 enclave 的页表并处理页错误。
5.2 内核模块
内核模块在启动过程中由主操作系统加载。然后它加载、度量和启动 RustMonitor,并将测量值作为 TPM quote 的一部分扩展到 TPM PCR。内核模块加载后,会创建一个设备文件并挂载在 /dev/hyper_enclave。应用程序可以打开它并通过 ioctl() 调用来调用模拟的特权操作。
5.3 Enclave SDK
HyperEnclave 对官方 SGX SDK 进行了如下改造。
支持 SGX SDK API。 我们用 hypercall 或系统调用替换了 SGX SDK 中的 SGX 用户叶函数(例如 EENTER、EEXIT、ERESUME 等)。为了兼容性,我们的实现保留了与 SGX 相同的参数语义和顺序。
通过编组缓冲区进行参数传递。 在 HyperEnclave 中,enclave 只能访问自己的地址空间和与应用程序共享的编组缓冲区。编组缓冲区的大小可以在 enclave 的配置文件中配置,有默认大小。在调用边缘调用之前,数据需要被传输到编组缓冲区。我们修改了 SGX SDK 来处理这些转换,因此对开发者是透明的。
我们修改了 SDK 中的非可信运行时库(即 libsgx_urts.so),以便在 enclave 初始化期间使用 mmap() 和 MAP_POPULATE 标志分配一个编组缓冲区。因此,编组缓冲区的 GPA 被预填充。然后发出一个 ioctl() 请求主操作系统在 enclave 的生命周期内不要压缩或换出编组缓冲区的物理页面。当应用程序调用模拟的 EINIT 指令来标记 enclave 的初始化时,编组缓冲区的基地址和大小被传递给 RustMonitor,RustMonitor 会在 enclave 的页表中添加编组缓冲区的映射。这样,编组缓冲区现在在 enclave 和非可信应用程序之间共享。编组缓冲区的基地址和大小也被传递给可信运行时库,以便将数据从编组缓冲区传输到 enclave。
当前 SGX SDK 中的 OCALL 实现是在 enclave 内部调用 sgx_ocalloc(),在非可信应用程序的堆栈区域分配一个缓冲区,然后用于跨 enclave 数据传输。因此,我们只需要修改 sgx_ocalloc() 函数,在编组缓冲区中分配一个内存区域。为了支持通过编组缓冲区为 ECALL 传递参数,我们修改了 SGX 的 Edger8r 工具,以自动生成将传输数据复制到编组缓冲区的代码。
SGX 编程模型支持使用 user_check 属性传递参数。对于此类参数,SDK 工具不会生成代码来检查地址范围或执行数据移动。由于 enclave 代码可以访问整个进程的地址空间,一些 enclave 程序可能会使用带有 user_check 属性的指针直接操纵 enclave 外部的数据缓冲区,而不考虑跨 enclave 边界复制数据的开销。为了处理这个问题,我们为开发者增加了一个接口,以便在开发者可能使用带有 user_check 属性的参数时,在编组缓冲区内分配缓冲区。
远程证明流程与 SGX 类似,遵循相同的 SIGn-and-MAc (SIGMA) 协议。我们扩展了 SDK 中的 sgx_quote_t 结构以包括 HyperEnclave quote,并且该修改对 enclave 代码是透明的。
通过以上设计,大多数 SGX 程序可以在 HyperEnclave 上运行而无需更改源代码。此外,为了简化 HyperEnclave 应用程序的开发,我们还已将 Rust SGX SDK [71] 和 Occlum 库操作系统 [64] 移植到 HyperEnclave。
6 安全性分析
信任建立。 HyperEnclave 依赖度量启动来引导 RustMonitor 的信任,这是 TEE 设计的常用方法(例如,TrustZone [16] 和 Keystone [49])。启动过程中的所有组件(包括 CRTM、BIOS、grub、内核和 initramfs)都被测量并扩展到 TPM PCR。因此,对启动代码的任何篡改都将通过远程证明反映和审计。我们认为 HyperEnclave 部署在受控环境中(即云计算场景中的数据中心),因此攻击者对平台的物理访问权限有限。为了减少攻击面并最小化 TCB,我们将 RustMonitor 镜像放入 initramfs,并在早期用户空间中加载和测量 RustMonitor。在此阶段,主操作系统内核不接受来自用户的外部输入,并且网络连接等外围设备被禁用。通过度量延迟启动方法,在操作系统被降级到正常模式后,RustMonitor 不再需要信任主操作系统。
Enclave 内存隔离。 如第 3.2 节所述,enclave 的内存和页表由 RustMonitor 维护,主操作系统无法访问。TLB 在 world switch 时被清除,以防止使用过时的 TLB 条目进行非法内存访问。RustMonitor 通过从其 NPT 中移除相应映射来防止主操作系统访问保留的物理内存。RustMonitor 还配置 IOMMU 以防止未经授权的设备访问保留的物理内存。
我们的设计防止了内存映射攻击,因为主操作系统不能干扰 enclave 的地址映射。我们引入编组缓冲区以支持 enclave 和应用程序之间的内存共享。应用程序在正常内存中预分配一个编组缓冲区,并在 enclave 初始化期间将缓冲区的基地址和大小传递给 RustMonitor。如果应用程序可能传递伪造的地址(例如,覆盖 enclave 内存),在将编组缓冲区的映射添加到 enclave 的页表之前,RustMonitor 会确保编组缓冲区的地址范围在 enclave 地址范围之外。
防御受损的 Enclave。 先前的工作表明,enclave 恶意软件可能会窃取秘密数据或劫持 enclave 外部应用程序的控制流 [63]。HyperEnclave 旨在限制潜在的恶意 enclave,如下所示。
- 防止对应用程序的任意内存访问。 SGX enclave 可以访问应用程序的整个地址空间,这可能导致诸如泄露密钥或堆栈金丝雀,或篡改代码指针以进行控制流攻击等攻击。在 HyperEnclave 中,enclave 只能访问自己的内存和用于参数传递的编组缓冲区。
- 防止 EEXIT 后的任意控制流。 SGX 设计允许 enclave 通过在执行 EEXIT 指令(即退出 enclave)之前设置 rbx 来跳转到任意地址,这为 enclave 恶意软件攻击打开了大门 [63]。在我们的设计中,由于 EEXIT 指令由 RustMonitor 模拟,因此通过在调用 EEXIT 时添加有效性检查,很容易防止此类攻击。
物理攻击。 诸如 AMD SME 之类的硬件内存加密技术可用于保护 enclave 免受冷启动攻击或总线嗅探攻击等物理攻击。通过内存加密,数据在内存或内存总线上始终是加密的,并且仅在 CPU 内部解密。内存加密密钥在系统启动时随机生成并存储在 CPU 中,软件无法显式访问。
侧信道攻击。 与 SGX 相比,HyperEnclave 可以减轻某些类型的侧信道攻击。由于 enclave 的客户机页表和页错误事件由 RustMonitor 处理,而没有主操作系统的参与,因此后者无法发起基于页表的攻击 [25, 72, 74]。我们将针对微架构攻击(如推测执行攻击)的防护留作未来工作。
7 评估
我们在一个配备两颗 AMD EPYC 7601 CPU(每核 2 线程,共 128 个逻辑核心)和 512 GB DDR4 RAM 的服务器上部署了 HyperEnclave。我们为 RustMonitor 配置了 2 GB 的保留内存,为 EPC 内存配置了 24 GB。主操作系统是 Ubuntu 18.04 LTS,Linux 内核版本为 4.19.91。为了比较,我们在一个启用 SGX 的 Intel Xeon E3-1270 v6 CPU 上运行了相同的实验,该 CPU 配备 64 GB DDR4 RAM,运行相同的操作系统。HyperEnclave 和原生 SGX 硬件的 SGX SDK 版本均为 2.13。所有程序都使用 GCC 7.5.0 和相同的优化级别进行编译。
我们试图排除硬件之间的差异。除非明确说明,评估没有超过 EPC 大小,以免引发过多的页面交换。所有评估都在单线程模式下进行。对于微基准测试(表 1 和表 2),我们使用相同的 SGX SDK 在 AMD 硬件上将 HyperEnclave 与在 Intel 硬件上的 SGX 进行比较。我们测量了核心周期以避免 CPU 频率的影响。对于真实世界的工作负载,我们分别在 Intel 和 AMD 平台上将没有安全保护的对应物设置为基线,并比较 SGX 和 HyperEnclave 引入的相对减速。由于我们只比较相对减速,我们强调绝对性能结果是在不同的平台上得出的,不能直接比较。所有 HyperEnclave 评估都是在启用内存加密的情况下进行的。
我们已谨慎确保 HyperEnclave 正确实现了 TEE 功能。尽管如此,SGX1 和 HyperEnclave 之间的内存加密是不同的,即 Merkel 树和 AES-CTR 对比 AES-XTS(有关内存加密开销的评估,请参见第 A.3 节中的图 11),这可能解释了内存密集型工作负载的改进。此外,HyperEnclave(特别是 HU-enclave)的 world switch 更快,这解释了 I/O 密集型工作负载的改进。
7.1 World Switch 性能
我们在 HyperEnclave(在不同的 enclave 操作模式下)和 Intel SGX 上都测量了边缘调用(即 ECALL 和 OCALL)的延迟。测试代码运行了 1,000,000 次没有显式参数的空边缘调用,并取中值。我们还测量了 HyperEnclave 上模拟的 EENTER 和 EEXIT 指令的指令级延迟。我们无法在 SGX 上测量指令级延迟,因为我们的 SGX 平台上的 enclave 内不支持 RDTSCP 指令。
结果如表 1 所示。结果表明,HU-Enclave 具有最佳的边缘调用性能,因为它将模式切换(约 880 周期)减少为环切换(约 120 周期),而 P-Enclave 比 GU-Enclave 慢,因为它在 world switch 期间需要切换更多的特权状态。所有结果都与 Intel SGX 相当。
7.2 Enclave 异常处理
我们使用未定义指令异常(#UD)和页错误异常(#PF)来评估 enclave 异常处理性能。在 #UD 基准测试中,测试代码在 enclave 中执行一条未定义指令以触发异常 1,000,000 次。异常处理程序推进指令指针并返回。对于 P-Enclave,异常完全在 enclave 内部捕获和处理,无需 enclave 模式切换。对于 GU-Enclave 和 SGX,异常会导致异步 enclave 退出(AEX)并将 CPU 切换到非可信操作系统,然后执行一个两阶段异常处理 [7]。结果表明,P-Enclave 内的异常处理分别比 GU-Enclave 和 Intel SGX 快约 68 倍和 110 倍(表 2)。
我们进一步模拟了一个典型的垃圾收集器(GC)场景,测试代码首先分配一个大的内存缓冲区,然后通过更改 enclave 的页表来撤销对该缓冲区的写权限。之后,enclave 访问该缓冲区以触发页错误。在异常处理程序中,写权限被恢复。结果(表 2)显示,P-Enclave 比 GU-Enclave 快约 2.3 倍,因为 P-Enclave 自己更新页表并处理页错误,而 GU-Enclave 需要陷入 RustMonitor 来更新页表。请注意,我们没有在 Intel SGX 上评估 GC,因为我们的 SGX1 平台在 enclave 初始化后不支持页面权限修改。
7.3 编组缓冲区开销
为了测量引入编组缓冲区的开销,我们构建了一个不使用编组缓冲区的 GU-Enclave 变体作为基线。我们测量了 ECALL 和 OCALL 在不同大小的传输数据和不同数据移动方向(即”in”、”out”和”in&out”)下的开销。我们使用 CLFLUSH 指令确保要传输的数据没有被缓存。我们评估了在 SGX 上传输相同数据的性能以进行比较。
(原文此处有缺失,但根据上下文推断)开销随着数据大小的增加而增加。对于 ECALL,由于额外的内存复制,传输 16 KB 数据时,”in”、”out” 和 “in&out” 方向的开销分别为 8%、11% 和 21%。对于 OCALL,开销可以忽略不计,因为它在编组缓冲区上分配一个缓冲区而没有额外的内存复制(第 5.3 节)。我们提醒,在许多真实世界的计算工作负载中,尤其是在计算或内存密集型任务中,ECALL 中的数据传输只占处理时间的一小部分。
7.4 真实世界工作负载
评估在四个真实世界的应用程序上进行:一个算法基准套件 NBench [53],一个轻量级 Web 服务器 Lighttpd [13],两个流行的数据库 SQLite [14] 和 Redis [15],作为 CPU 密集型、I/O 密集型和内存密集型任务的代表。我们将库操作系统 Occlum [64] (v0.21) 移植到 enclave SDK,以减少 Lighttpd 和 Redis 的移植工作。我们使用在 SDK 模拟模式下编译的相同代码作为基线(不提供安全保证),在 HyperEnclave 和 Intel SGX 上测量了性能。
NBench
NBench 测量系统的 CPU、FPU 和内存系统的性能,不涉及 I/O 和系统调用。我们使用了 NBench 的一个 SGX 改编版,即 SGX-NBench [8],在我们的评估中没有修改源代码。如图 8a 所示,HyperEnclave 和 SGX 引入的开销分别约为 1% 和 3%。
SQLite
我们将 SQLite (v3.19.3) 与 enclave SDK 进行了移植,并使用 YCSB [30] 工作负载 A(50% 读取,50% 更新)在 Intel SGX 和 HyperEnclave(GU-Enclave 和 HU-Enclave)上对其进行了评估。在此评估中,我们专注于内存性能,因此我们将数据库配置为内存数据库,并将客户端嵌入到 enclave 中以避免 I/O 操作。我们增加了记录数量,并测量了 100,000 次数据库操作的时间。如图 8b 所示,在 SGX 上,对于小内存使用量,吞吐量约为基线的 75%。当内存使用量超过 EPC 大小(约 90 MB)时,由于页面交换,性能下降到 50%。在 HyperEnclave 上,GU-Enclave 和 HU-Enclave 的性能几乎与基线相同(开销 < 5%)。我们推测这是因为 AMD SME 的内存加密性能(没有完整性保护)比 SGX 快。
Lighttpd
我们在 SGX 和 HyperEnclave(GU-Enclave 和 HU-Enclave 模式)上使用 Occlum 运行了一个 Lighttpd (v1.4.40) 服务器。我们使用了 Apache HTTP 基准测试工具 [10],并通过本地环回运行了 100 个并发客户端来获取各种大小的网页以评估吞吐量。在此评估中,开销主要来自频繁的 enclave 模式切换(表 1)。如图 8c 所示,HU-Enclave 提供了预期的最佳性能(基线的 81% ~ 88%)。GU-Enclave 达到了基线的 69% ~ 78%,而 SGX 达到了基线的 51% ~ 63%。
Redis
我们使用 Redis 来评估在内存和 I/O 都很密集的综合场景下的性能。我们在 SGX 和 HyperEnclave(GU-Enclave 和 HU-Enclave 模式)上使用 Occlum 运行了一个 Redis (v6.0.9) 数据库服务器。与 SQLite 类似,我们将数据库配置为内存数据库,并使用了 YCSB 工作负载 A。对于评估,我们首先加载了 50,000 条记录(总共 50 MB 数据),然后通过本地环回从 20 个客户端执行了 100,000 次操作。我们增加了请求频率,并测量了不同吞吐量下的延迟。如图 8d 所示,HU-Enclave 达到了基线最大吞吐量的 89%,而 GU-Enclave 和 SGX 分别约为基线的 72% 和 48%。
8 讨论
HyperEnclave 在其他平台上的应用。 HyperEnclave 需要虚拟化扩展(特别是两级地址转换)进行隔离,以及 TPM 用于信任根和随机性等。许多 ARM 服务器(如 ARMv8 平台 [2])都支持虚拟化。RISC-V H-扩展规范已于 2021 年演进到 v0.6.1。ARM 和 RISC-V 虚拟化都支持两级地址转换。某些 TPM 产品已经支持 ARM 服务器。已经有研究在 RISC-V 上支持固件 TPM [22]。因此,有迹象表明 HyperEnclave 可以被适配到 ARM 和 RISC-V 平台上运行。
然而,将 HyperEnclave 移植到 ARM 和 RISC-V 平台需要大量的工程工作,考虑到指令集架构(ISA)完全不同。以 ARMv8 架构为例。软件模块可以映射到不同的异常级别(EL):RustMonitor 的监视器模式可以映射到 EL2;主操作系统和应用程序的非可信部分可以分别映射到 EL1 和 EL0;enclave 的安全模式可以灵活地映射到 EL1 或 EL0。内存隔离可以通过 stage 2 地址转换的支持类似地实现。此外,官方的 Intel SGX SDK 只支持 x86 平台。特别是,跨 enclave 边界的转换是用平台相关的汇编代码处理的,需要根据目标平台的应用程序二进制接口(ABI)重写。我们将把 HyperEnclave 适配到其他平台的进一步探索留作未来工作。
不同 enclave 操作模式下的攻击面。 非可信的主操作系统仍然在虚拟机内运行,从主操作系统到 enclave 的攻击面没有改变。然而,在特权模式或主机中运行 enclave,可能会向恶意 enclave 暴露更多的攻击面。例如,如果 enclave 已经在主机中运行,它可能会使 enclave 恶意软件更容易升级到主机 ring-0。













