笔记分享 | 组队学习TEE(1)— TEE-数据安全新范式
The following article is from 隐私计算研习社 Author LaiskyCai 裴君翎
OpenMPC社区主办的组队学习TEE,邀请到了翼方健数系统安全工程师Laisky Cai老师为大家带来了主题为《TEE-数据安全新范式》的精彩分享。
本次分享长达两个小时,干货满满,详细介绍了基于硬件的机密计算 TEE 领域的相关概念和方法论。Laisky Cai老师从What、Why、How三个角度展开关于TEE(基于硬件实现的可信执⾏环境)的第一课。本篇笔记由南开大学 裴君翎提供。
TEE-数据安全新范式(上)
TEE是什么
可信执⾏环境(TEE)是⼀种旨在为处理器上运⾏的数据提供机密性(Confidentiality)和完整性(Integrity)的保障。数据完整性指的是在数据的整个⽣命周期(lifecycle)中,都不会被恶意篡改。我们这⾥对数据采⽤⼴义的定义,即包括程序代码(算⼦)和处理及传输的数据。确保数据完整性的⽅式,就称为“度量(Measurement)”,⼀般来说最常⻅的度量⽅式,就是计算⽐对数据的哈希摘要,如SHA-256。
既然提到了完整性,就不能不提到设计⼀个安全系统时,不得不考虑的CIA 不可能三⻆(Integrity、Confidentiality和 Availability)。在数据的完整⽣命周期中,同时保有如下三个特性的成本是⾮常⾼昂的,往往只能三者取其⼆。⼀般来说 Integrity 是必要的,主要是在 Confidentiality 和 Availability 间做权衡。
前⽂多次提到数据⽣命周期,根据不同的防护设计理念,⼀般将其拆分为三个阶段:数据的存储安全(at-rest)、数据的传输安全(in-transit)、 数据的运⾏时安全(in-use)。要介绍这三者的差异,就需要介绍为什么我们需要 TEE 了。
为什么需要TEE?
在展开以前,先简单回顾⼀下过去的数据安全理念和挑战。
过去,物理机器、⽹络基础设施、操作系统和程序都是⾃⼰管理的,主要的⻛险暴露⾯是外部⽹络或第三⽅程序。对于第三⽅程序,我们可以只从可信的发布者那⾥获取程序,⼩⼼地度量程序的完整性。对于⽆法确认可信的程序,我们可以在虚拟机或沙箱(sandbox)中运⾏,以隔离其对物理机的影响。对于外部⽹络,我们可以⼩⼼地设置⽹络程序,尽量避免漏洞,同时设置防⽕墙,限制⽹络访问,以及使⽤加密协议,避免数据在公⽹上传输时被窃取。
进⼊云时代以后,⼈们延续了过去的做法:⼩⼼翼翼地管理好⾃⼰的应⽤程序,并且设置复杂的防⽕墙和安全组。以前做过的事情,我们在云上都做到了,甚⾄配合云上各类功能强⼤的SaaS,看上去武器库更加丰富了,但这样不足以保护数据安全。
我们从数据生命周期来分析:
•at-rest可以通过AES-XTS等方式对持久化数据加密
•in-transit可以通过 TLS 对数据传输进行加密
•in-use: 无法保证
很容易看出,在过去,⼈们对in-use运⾏时安全的防护是⽐较⽋缺的,毕竟机器和操作系统都在我们⾃⼰的⼿中。但是进⼊云时代后,最⼤的变化就是物理机器和操作系统都不再完全仅处于我们的掌握之中了。云⼚商(CSP, Cloud Service Provider)控制着物理机器、宿主机操作系统和虚拟机操作系统。虚拟机上的⼀切数据,对于虚拟机操作系统、宿主机操作系统⽽⾔,都形同明⽂。
在互联⽹的野蛮⽣⻓阶段,对数据隐私的保护是很漠视的,基本上只要任何双⽅谈妥了,就可以进⾏数据交换。但是随着世界各国数据安全法的出炉,数据的跨境、跨域(法⼈域)流通的渠道逐步被法规堵死。如今甚⾄连同⼀个集团下的各个分公司数据,都不能轻易地再进⾏汇聚。不仅仅是法规要求,随着⼤数据时代和 AI ⼤模型时代的相继到来,数据的价值⽇益凸显,各个数据源对核⼼数据的保护也⽇益重视。这就为数据的隐私流通提出了新的挑战和机遇,这也是过去⼏年 MPC、FL、HE 等隐私计算算法蓬勃发展的根本动⼒。
数据的隐私流通,通俗来说就是:如何在不共享原始明⽂数据的前提下,实现数据价值。这里通过一个简单的例子来说明需要数据隐私流通的场景。你使⽤了⼀款社交 App,这款 App 可以根据通讯录帮你推荐好友。你确实希望 App 给你推荐你通讯录内的好友,但是你不希望上传⾃⼰的通讯录。
隐私计算的⽬的,正是实现这类数据可⽤不可⻅的需求。
⼀般将基于硬件实现的可信执⾏环境(TEE)称为机密计算(CC)。机密计算 TEE,则是在基于算法的隐私计算之外,提出了⼀条基于硬件的新的解决途径。TEE 和传统隐私计算相⽐最⼤的差异在于:⾼性能,媲美原⽣应⽤;易⽤性,⽀持通⽤计算。事实上,TEE 的实⽤路径⽐隐私计算还要更宽⼀些。TEE 不仅能解决隐私计算试图解决的数据共享问题,更是解决了数据全⽣命周期安全的问题。
TEE如何实现?
鉴于软件TEE的脆弱性,⽬前⼈们更多的依赖于硬件TEE。为了获取尽可能⼩的可信计算基(TCB)和尽可能少的暴露⾯,我们需要将硬件信任根(RoT) 放在硬件层⾯。⽆论是AMD、ARM还是Intel的硬件TEE 实现,其核⼼理念是⼀致的。
在CPU上实现数据隔离和加密,软件通过和CPU的指令交互,来实现对敏感数据的加解密和访问。软件的完整性和机密性都通过CPU硬件/固件来保证,从⽽实现了⼀个硬件信任根,尽可能地减少了TCB的⼤⼩。这些⽅案主要的区别,主要在于 TCB 的粒度:
•进程级:Intel SGX、ARM TrustZone
•虚拟机级:AMD SEV、Intel TDX、ARM CCA
后⽂中,主要选择Intel SGX和AMD SEV进⾏简要介绍,这两者正好各⾃作为进程级和虚拟机级硬件TEE的代表。
一般来说,大部分硬件安全设备,最核心的部分都是一个加密处理器(SP, secure processor)和一个防破坏的非易失性存储器(tamper-proof NVRAM)。SP提供密钥生成、密钥派生、加解密等计算功能,NVRAM提供持久化存储功能。而且这个 NVRAM 是具有防御物理攻击的能力的,比如能够检测到外部的物理攻击,或者在物理攻击发生后,能够自动擦除数据。TEE的硬件芯片也是这样,无论是 Intel还是AMD的CPU,出厂前都会在CPU的NVRAM里生成一个硬件私钥,然后设备制造商给这个私钥签发一个证书,这个证书就是设备的唯一标识。而且这个证书就将CPU日后签发的证书和设备制造商的RootCA关联起来,日后可以用设备制造商的RootCA来校验CPU签发的证书的合法性。
前面提到,TEE在加载执行软件时,会对其进行度量,并记录软件的度量值(一般是 SHA-256)。TEE 硬件也会提供接口,让TEE程序可以生成一个由硬件私钥签名的报告(REPORT/QUOTE),这个报告会包含TEE环境和tester软件的度量值。verifier 通过设备制造商的RootCA就可以校验这份Report的真实性,然后就可以确认 TEE 环境的真实有效性和tester软件的完整性。为了防止 reply attack,生成报告时会允许注入一段限定长度的用户数据(chanllenge/nonce)。这个数据也会被tester添加到报告里并且一起被硬件签名,这样verifier就可以确认这个报告是实时生成的。
根据上述特性,人们找到了一种在verifier和tester间建立TLS端对端加密通信的方法——verifier as Server。如果tester是服务端,那么tester可以在 TEE内启动后,生成一对公私钥和数字证书,然后用这个数字证书创建一个TLS 服务端接口。因为私钥是在TEE的内存里生成,其安全性得到了保证。
接下来就是需要向tester证明,这个私钥确实是由TEE程序所持有的。这就需要利用RA流程来进行。
但是需要注意的是,RA-TLS 和传统 TLS 并不完全兼容:传统 TLS,客户端利用预先准备好的 RootCA 先校验对端证书,再建立 TLS 连接。而 RA-TLS,是先建立 TLS 连接,然后再用 Report 校验对端证书。对证书的校验从先验变成了后验,你需要先 skip 证书校验建立 TLS 连接,然后再手动去校验 tls conn 的 peer 证书。
前文已经介绍了 TEE 实现数据 in-use 和 in-transit 安全,接下来介绍一下数据 at-rest 安全。TEE 的硬件芯片都会提供密钥派生(key derive)的功能,可以根据软件度量值,从硬件根密钥派生出一个 Sealing Key。TEE 应用可以将这个 Sealing Key 用于对称加密,从而实现数据 at-rest 安全。因为 TEE 硬件的根密钥不会变,只要软件的度量值没有变,那么 Sealing Key 就不会变,这样就可以保证数据的可解密性。
Sealing Key 的派生密钥除了受到软件度量值和 ISV 公钥的影响,还受到了硬件根密钥的影响, 这使得 Sealing Key 实际上是绑定到生成它的 CPU 的。一旦应用换了个 CPU 运行,那么 Sealing Key 就会跟着变,从而导致无法解密旧数据。(所以在云时代使用 TEE 是需要上层 PaaS 框架支持的)
密钥出域不是说直接把加密用的密钥导出 TEE 环境,这样做就完全破坏了 TEE 的机密性。
任何端对端加密系统,要想保证可用性,一定会有一个 Recover Key。这个 RK 保证了即使硬件密钥丢失,但是你仍然能恢复原始数据。至于 recover key、sealing key 和真实加密数据所用的 master key 间是个什么样的映射和派生关系,这就见仁见智,各有各的实现了。
进程级TEE:SGX
再来简要介绍一下system call(简称syscall)。为了更好的支持权限管理,CPU 提供了RING 0 ~ 3 四个不同的保护域,不同域有不同的指令执行权限。这也被称为分级保护域,Hierarchical Protection Domains。Linux 只使用了其中的两个态,OS 运行于Ring 0(有时候也称为 CPL-0),拥有操作设备的权限。用户程序运行于 Ring 3(CPL-3),仅有使用 CPU 进行计算的权限。我们也习惯将其称之为用户/内核态。
用户态的应用程序没有操作设备指令的权限,那么当这些程序需要调用底层设备的时候(如读写文件、网络 I/O 等)怎么办呢?此时就由操作系统提供一系列封装好的接口,用户可以通过 CPU 提供的指令将控制权交给内核,内核提权后根据用户的需要去调用执行相应的 syscall 函数,执行完成后再通过 CPU 指令降权,控制权交回用户态。但是实际上用户程序一般并不会直接和 kernel 交互(因为除了 syscall 外,还有大量其他相关工作),这些“幕后的相关工作”,一般由 libc 来完成。就目前来说,用户程序和 kernel 的边界究竟是 syscall 还是 libc 尚无定论。
VMM 有时候也称为 hypervisor,根据其是运行于 Host OS 之上还是 Bare Metal 之上可以区分为两个类型。
简而言之,最早的 VMM 就是试图用用户进程运行一整个操作系统。这有很大的难度,原因之一就是对指令权限的控制非常繁琐和困难。后来各家芯片厂商提出了硬件虚拟化方案,Intel-VMX(或叫 VT-x)和 AMD-V。
硬件支持VMM对Ring0的虚拟化操作。可以设定让VM CPU在遇到指定指令时触发VM EXIT,将控制权切换给 VMM,从而得以实现对任意指令的拦截。让VM 完全意识不到自己运行于虚拟环境之中,有时候也被称为blue pill。一句话总结就是,硬件虚拟化极大地降低了VMM的实现难度,也提高了VM的性能。
前文提到 Enclave 没有调用 syscall 的能力,都得重新修改代码,改为通过 EDL 接口转发,这样对用户的开发成本实在过于高昂。所以人们将目光投向了LibOS,这种在用户态实现 OS 的方式,正好可以和 Enclave 内的代码无缝对接。这样实际上就把 Enclave 内的程序又拆分成了两部分:App 业务代码,运行于LibOS 之上,无需改动;LibOS 底座,负责劫持 App 的所有系统调用,转发给 uRTS。
虚拟机级TEE:SEV
TEE 的应用场景已经从早年的数据产权保护,转向了更为广阔的通用计算。面对通用计算的复杂场景需求,以及为了能够给软件开发者实现尽可能低的开发成本。面向 VM 的 TEE 技术显然具有比进程级的 TEE 更大的优势。AMD 在2019 年发布了 SEV 技术,这是一种面向 VM 的 TEE 技术。此后 Intel 也发布了类似的技术,称为 Intel TDX,此外还有 ARM CCA,可以看出 VM 显然是目前 TEE 技术的发展方向。SEV 也经历了三代发展(SEV、SEV-ES、SEV-SNP)
拿 Milan 架构支持的 SNP 来说,其已经具备了前文所述的全部 TEE 四要素。
SEV-SNP 和 SGX 最大的区别就在于,TCB 的大小和易用性的权衡。
SEV 中,整个 guest os 都是被 trust 的。
2016 年,AMD 推出了 SME,以实现对内存的透明加密。同一时间 AMD 也推出了 SEV,这是以 SME 为基础实现的内存加密虚拟机。SEV 硬件借助 SME,为每一个 CVM 提供一个独立的对称密钥,对所有内存数据进行加密。(Intel 上类似技术称为 Intel TMK 和 MKTME)CVM 需要和外界进行 I/O 通信时,需要使用未加密的 shared memory。借助 Memory Access Control 技术的思路,利用 GPA 内存地址中称为 C-bit 的一位作为 flag,标记当前内存页是否需要加密。
SEV 其实就是将 SME 应用到 CPU 的 VM 虚拟化之上,为每一个 VM 都分配一个唯一的内存加密密钥,实现 VM 级的 TEE。
CVM 的内存加密对传统的 OS 内数据流构成了很大的挑战,其中一个经常被讨论的东西就是 SWIOTLB。
这里尝试做一个非常简单的介绍。
进入 64-bit 时代以后,主存的寻址方式变成了 64-bit,而这些 32-bit 的设备无法和主存进行对齐,使得 DMA 的访问遇到了问题。这时候出现了 IOMMU(I/O Memory Management Unit) 设备,这是 AMD-Vi、Intel VT-d、ARM SMMU 等一系列具体实现的统称,代表一种能够将零散的物理内存映射为一块连续的虚拟地址空间中的设备。利用 IOMMU 的地址映射支持,就解决了各类外设的地址映射问题。CPU 可以在一块统一的虚拟地址空间内实现对所有设备内存的访问。IOMMU 这个名字发源于内存管理模块 MMU(Memory Management Unit), MMU 是 CPU 内部的一个模块,用于将虚拟地址映射为物理地址。IOMMU 则是用于将外设的物理地址映射为虚拟地址。
不过 IOMMU 最早是由 AMD 提出的,Intel 最早在适配 x86-64 时,并没有采纳 IOMMU 的设计。
Intel 在 2001 年推出它的第一个 x86-64 处理器架构 Itanium 时,采用了一种软件上实现 IOMMU 的做法,这个软件就称为 SWIOTLB(Software Input Output Translation Lookaside Buffer),有时候更直白地被称为 SWIOMMU(software IOMMU)。
SWIOTLB 的实现方式是,预留一块连续的物理内存区域,作为一个缓冲区,用于存放 I/O 设备的数据。当 I/O 设备需要访问内存时,先由 CPU 将数据写入 SWIOTLB,然后再由 DMA 将 SWIOTLB 中的数据拷贝到目标设备的内存地址中。这种方式的缺点是,需要预留额外的内存空间,而且需要 CPU 的参与,性能不高。随着 32-bit 设备迅速退出历史舞台,IOMMU 和 SWIOTLB 的作用也就不大了。甚至很多人建议禁用这两个机制,以提高性能。随着虚拟化技术的发展,IOMMU 和 SWIOTLB 又重新回到了人们的视野中。在虚拟环境中,所有内存地址都由虚拟机软件重新映射,这会导致DMA设备失败。IOMMU处理此重新映射,允许在客户操作系统中使用本地设备驱动程序。从 vmware 的文档看上去,无论是支持 SR-IOV 虚拟化的设备,还是 passthrough 的设备,都需要 IOMMU 的支持。SWIOTLB/SWIOMMU 最初是为了用软件实现 IOMMU 的功能。它会在 OS 启动前,在物理内存上保留一块 32 位寻址的区域(称为 aperture),一般为 4~64 MB。每当 DMA 因为地址不匹配而出错时,CPU 就会把数据拷贝到 aperture 内(bounce buffer),然后再让 DMA 传递。
随着 AMD SME、Intel TME 等全局内存加密技术的出现,给 DMA 带来了非常大挑战。因为主存数据是加密的, DMA 不能再直接将其传输给设备。于是 AMD 发现 SWIOTLB 指令刚好可以拿来干这事儿。先将内存解密后放进 SWIOTLB 的 bounce buffer 内,然后再让 DMA 发送给设备。SWIOTLB 是为了低速外设而设计的,它根本无力负担一台 SME/SEV 加密机器全部的 I/O 压力,很快就会出现 buffer overflow 或者死锁导致 I/O 性能急剧(-80%)下降。目前 AMD、Intel、Kernel 各家都有各自的解决方案 patch,尝试优化 SWIOTLB 的性能问题。虽然各家的硬件 TEE 方案,其 TCB 理论上都不包含 host os。但是基于 host os 特权用户的侧信道攻击(side channel attack)仍然是一个很大的威胁。而且本着纵深防御的原则,增加对 host os 的防护,也是很有必要的。
UEFI 在启动 OS 以前,会通过固件中的公钥校验 OS 的签名。OS 必须是由可信的 CA 签名过的,才能被启动。这一流程被称为 Secure Boot。
OS 在 secure boot 后被启动,OS 会在之后的启动过程中的数个关键环节,对每一个关键组件进行校验,确保组件的完整性,这一流程被称为 Measured Boot。
为了确保系统启动过程的全流程可信,就需要这个小小的 TPM(Trusted Platform Module) 硬件模块了。
TPM 中包含了数个只读寄存器,称为 PCRs(Platform Configuration Registers)。每当系统重启时,所有的 PCR 都会被清空。然后在系统启动过程的不同阶段,各个 PCR 按照预设的程序会对系统当前的状态进行度量(Measurement),并且将度量值写入到对应的 PCR 之中。
前面提到 PCRs 是只读寄存器,所以在系统启动过程中生成的 PCR 值是不可篡改的。在制作系统镜像时,我们可以将 PCR 的值记录下来,然后在日后系统启动时进行校验,所有的 PCRs 都不应发生任何变化。
前文讨论的 TPM 都是插在主板上的硬件模块,这个硬件有一个致命的缺点,就是不支持虚拟化。为了让虚拟机也能用上 TPM,人们发明了 vTPM。但是这不是常见的硬件虚拟化,而是在 Host OS 上运行的一个软件,模拟出了 TPM 的设备接口。在 guest os 内看上去,就和一个拥有物理 TPM 的设备一模一样。只是这个 TPM 实际上是 host OS 上的一个进程,一般由 VMM 提供。VMM 会为每一个 VM 维护一个 vTPM 服务实例,每一个实例都会将自己模拟成一个物理 TPM 的设备。如果 vTPM 进程可靠,那么确实可以如实地度量 VM,VM 也无法篡改 vTPM 的 PCR。但是这样做其实在无形中扩展了 TCB,你得信任 VMM,才能信任 vTPM。
这是一组常见的 vTPM 架构。TPM 设备 passthrough 进 vTPM server VM 中。
前面聊的所有的 TEE,都是围绕着 CPU 和主存构筑的。而一旦涉及到外部设备,情况就会复杂很多。
分享嘉宾:Laisky Cai,翼方健数系统安全工程师
笔记整理:裴君翎 南开大学
热门文章: