TRUSTORE: Side-Channel Resistant Storage for SGX using Intel Hybrid CPU-FPGA
TRUSTORE发表于CCS20的Session 6C Side Channels上,是一篇针对于现有SGX技术很容易遭到Side Channel攻击这一问题来进行研究的文章,其中应用了FPGA,将可信区从SGX本身拓展到了FPGA,实现了一个可信的存储模块。
摘要
现有的SGX容易遭受到侧信道攻击(页错误、缓存、分支预测、推测式执行等),虽然ORAM技术从理论上保证了SGX可以免除侧信道的危害,但是其具有严重的性能问题。这篇文章介绍了一个新成果:TRUSTORE,利用FPGA这一外设,实现了一个可信的存储服务,并且保证该存储服务与侧信道完全隔绝。TRUSTORE主要解决了以下三个问题:
- 在不改变系统架构的前提下,将可信范围从SGX拓展到FPGA上;
- 为SGX应用与FPGA之间提供一个可验证的安全连接通道;
- 为SGX应用提供无缝的多重操作支持(怎样理解无缝?)。
SGX中的侧信道攻击以及常规ORAM方法的局限性
目前,常见的针对SGX的侧信道攻击大都基于存储结构,例如基于页错误的攻击,基于缓存的攻击(flush+reload,evict+reload,evict+time等等),基于分支预测的攻击以及基于推测执行的攻击等等。通过这些侧信道攻击,攻击者可以获取到本应被SGX保护的敏感信息。
针对这一系列攻击,许多基于ORAM的方法被提出并应用于侧信道的保护上。但由于对于每次内存访问,ORAM都需要访问整个访问路径树(对大小为N的数据,需要额外O(LogN)的内存访问次数),这使得ORAM的性能受到了极大的限制。实验结果表明,ORAM的额外开销相较于原生enclave执行缓慢了两个数量级。此外,当访问的数据大小变大时,ORAM的速度呈指数级衰减。
正因常规ORAM方法在解决SGX的侧信道攻击时具有很大的局限性,如何高效地预防SGX中的侧信道攻击成为了亟待解决的问题。事实上,与SGX相关的基于存储的侧信道攻击,大都因为相应的存储单元(内存,缓存,页表,分支预测单元等)是被计算机上不同应用(不管被信任与否)所共享的。在这篇文章中,作者提出了一个观点:如果我们将SGX所利用的存储单元与这些共享存储单元隔绝开,其相应的侧信道是否在很大程度上会被隔绝?
TRUSTORE简介
基于上文中的观点,作者设计了TRUSTORE,也即一个在与其他存储设备相隔离的FPGA中的可信存储模块。由于TRUSTORE有着独立的内存单元,故其从设计上阻断了存储单元相关的侧信道。在设计TRUSTORE并将其与SGX相结合的过程中,作者遇到并解决了以下三个挑战:
- 在不改变系统架构的前提下,将可信范围从SGX拓展到FPGA上;
- 为SGX应用与FPGA之间提供一个高速的可验证安全连接通道;
- 为SGX应用提供无缝的多重操作支持(怎样理解无缝?)。
本文的威胁模型
Enclave的假设
假定用户想要在一台远程机器上里用SGX安全地运行一个应用。攻击者了解这个应用的内容和源代码,所以代码本身并不是敏感信息。但,用户向enclave提供的数据是敏感的,因此这些数据需要被保护以免受到侧信道攻击。在TRUSTORE中,作者假设单一一方(如enclave应用开发者)会将TRUSTORE服务引入FPGA设备中。但对于某些服务,开发者可以在一台设备中运行多个enclave,并且这些enclave可以同时访问同一块FPGA。
硬件假设
TRUSTORE的有效性基于以下硬件层面的假设:CPU与FPGA芯片不会被篡改且其功能被正确实现。此外,作者假设攻击者不能从芯片封装内直接获取到秘密信息或者使当前状态混乱。温度与功耗侧信道不在TRUSTORE的考虑范围中。
攻击者的能力
攻击者拥有控制全部软件组成(如BIOS,OS,VMM以及设备驱动程序)的特权。此外,攻击者可以控制除FPGA之外的所有外设,所有的非EPC内存也都可以被攻击者控制(例如DMA与MMIO缓冲区)。与EPC类似,尽管攻击者不能直接访问被TRUSTORE保护的FPGA DRAM,他们也可以发起相关的侧信道攻击。
TRUSTORE的设计
TRUSTORE分为两部分,TRUSTLIB和TRUSTMOD。
- TRUSTLIB是运行在enclave内的模块,其主要作用是建立和维护enclave程序和可信存储设备(FPGA)之间的通信通道。
- TRUSTMOD是运行在FPGA上的模块,其是可信存储的核心模块。(其实现了一个基于FPGA板载内存的key-value存储机制,该机制可以在为多个enclave提供存储服务的同时,通过强行访问控制来保证enclave中的数据安全。)
在介绍完这两个模块之后,文章讨论了如何利用MMIO与DMA来高效的将这辆各模块进行连接与组合。
名词解读:
- MMIO,也既内存映射IO,在MMIO中,内存和I/O设备共享同一个地址空间。 MMIO是应用得最为广泛的一种IO方法,它使用相同的地址总线来处理内存和I/O设备,I/O设备的内存和寄存器被映射到与之相关联的地址。当CPU访问某个内存地址时,它可能是物理内存,也可以是某个I/O设备的内存。因此,用于访问内存的CPU指令也可来访问I/O设备。每个I/O设备监视CPU的地址总线,一旦CPU访问分配给它的地址,它就做出响应,将数据总线连接到需要访问的设备硬件寄存器。为了容纳I/O设备,CPU必须预留给I/O一个地址区域,该地址区域不能给物理内存使用。
- DMA,也既直接内存访问,DMA传输时将数据从一个地址空间复制到另外一个地址空间。当CPU初始化这个传输动作,传输动作本身是由 DMA控制器来实行和完成。典型的例子就是移动一个外部内存的区块到芯片内部更快的内存区。像是这样的操作并没有让处理器工作拖延,反而可以被重新排程去处理其他的工作。DMA 传输对于高效能嵌入式系统算法和网络是很重要的。在实现DMA传输时,是由DMA控制器直接掌管总线,因此,存在着一个总线控制权转移问题。即DMA传输前,CPU要把总线控制权交给DMA控制器,而在结束DMA传输后,DMA控制器应立即把总线控制权再交回给CPU。一个完整的DMA传输过程必须经过DMA请求、DMA响应、DMA传输、DMA结束4个步骤。
TRUSTMOD的加载与验证
TRUSTMOD需要被编译成比特流并加载进FPGA开发板上,因此,如何保证其在加载到板上之后的保密性和完整性是这部分主要解决的问题。
比特流的加载
通过FPGA的安全启动流程,TRUSTMOD可以保证比特流在加载到板上之后,其中内容不会被窃取。具体来说,开发者首先准备好TRUSTMOD的比特流,并将该比特流上传到FPGA厂商来进行签名和加密(假设FPGA厂商可信),最后将正确加密并签名比特流烧入FPGA中,FPGA自身解密并写入自身逻辑模块。但显然,这一过程只能保证烧入的比特流的内容不被外界知晓,并不能保证FPGA始终运行正确可靠的比特流。因此,TRUSTMOD借鉴了SGX中的remote attestation过程,并借助TRUSTLIB实现了一套自身的验证流程。
密钥管理与验证过程
在介绍如何验证之前,我们先来介绍密钥管理方法,其过程如下图所示:
图1:TRUSTMOD加载过程中的密钥管理
步骤⓪指出,FPGA厂商在出厂前会为每块开发板准备好相应的三个密钥\(k_{Pub}^{bitstr},k_{Priv}^{bitstr},k_{AES}^{bitstr}\)来用作安全启动的保证。为了实现验证操作,如步骤①所示,TRUSTMOD也会在每次编译之前(来保证不同TRUSTMOD设备的密钥不同)生成一对密钥\(k_{Pub}^{attest},k_{Priv}^{attest}\),并把私钥\(k_{Priv}^{attest}\)附加在编译好的比特流中,公钥则会被提供给TRUSTLIB以便进行后续操作,这是图示中的步骤②。当FPGA接收到加密并签名过的TRUSTMOD比特流之后,其就会进行相应的验证操作,如果验证无误,则将解密之后的TRUSTMOD模块载入逻辑阵列中,如步骤③。
接下来,为了在CPU与FPGA之间建立起一条可靠的信道,TRUSTLIB将会发起对TRUSTMOD的验证。其过程如下图所示:
图2:TRUSTMOD与TRUSTLIB的验证过程
为了保证验证消息的新鲜性,在发起验证的最初,TRUSTLIB首先要产生一个随机nonce并发给TRUSTMOD,TRUSTMOD接受到这个nonce之后,利用\(k_{Priv}^{attest}\)对其进行签名并发回至TRUSTLIB供其进行验证。若验证通过,则开始发起Diffie–Hellman密钥交换,最终得到一个公共密钥,至此安全信道建立成功。这个机制保证了,只要\(k_{Priv}^{attest}\)不被泄露,即使攻击者改写了FPGA的比特流内容,也会在进行验证时被TRUSTLIB发现。
TRUSTMOD的存储模型
关于存储模型,主要考虑三个问题,第一:怎么存,也既存储内容在存储器中的物理和逻辑组织结构是什么;第二:怎么管,也既内容可以被谁访问,可以被怎样访问。对于怎么存这个问题,从概念出发,我们要考虑内容以什么格式组织在存储器中,并通过怎样的方式寻址。对于怎么管这个问题,我们主要考虑访问控制以及如何响应相应的存储操作请求。此外,可能存在的内存碎片问题也是TRUSTMOD需要解决的问题之一。
怎么存
- 寻址:由于FPGA硬件本身的特性,TRUSTMOD直接访问FPGA的存储器,所以寻址方式可以被简单地定义为直接寻址。由于不存在CPU中的应用所必须涉及到的缓存,页表等结构,其可能遭受的侧信道攻击面被显著地削减。
- 存储格式:由于FPGA的板载存储器(DRAM)的地址空间是线性的,因此数据可以简单的按起始地址+偏移量(也既数据大小)这一方式来管理。具体地,TRUSTMOD将所有被分配的内存记录到一个叫片上内存分配表(On-Chip Memory Allocation Table, OCMAT)的数据结构中,这个表包含以下属性:1)ID,也既一个数据对象的唯一标识符,被TRUSTMOD内部分配;2)EID,也既Enclave ID,是拥有该数据对象的enclave的唯一标识符;3)片上地址,也既该数据对象在FPGA片上存储器内的起始物理地址;4)长度,很显然,既是数据对象的大小。
怎么管
TRUSTMOD维护一个FIFO队列来记录每个enclave的内存操作请求,因此这些请求将会以先来先服务的形式被响应。如果队列满无法再接受新请求,TRUSTMOD将会通过推迟发送ACK信号的方式来让总线暂停接受新请求。
- 访问控制:如上文所述,TRUSTMOD对每个与其协作的enclave分配一个唯一的标识符Enclave ID,EID是由该enclave与TRUSTMOD之间的共享密钥推导出来的。因此,每个内存操作请求都会被与其发起请求的enclave的EID绑定,这保证了每个enclave都只能访问它拥有的内存区域,任何访问不属于其的内存区域的操作将会被丢弃。
- 内存分配与回收请求的响应:对于分配请求,首先确认TRUSTLIB与TRUSTMOD是否建立了安全信道,如未建立,则拒绝响应。若已经建立,则为传入的数据对象分配一个可用的ID,并根据其请求的大小为其分配一块足够大的内存空间。最后,将这些信息存入OCMAT的新行内。当请求回收内存时,根据请求的ID,检查请求的enclave是否为该内存对象的拥有者(也既ID是否一致),若一致,则将匹配到的行移除。
- 读写请求的响应:这一部分的解决方案比较简单,对于读,只要EID匹配,则去将请求内ID对应的数据对象传回给TRUSTLIB;同样,对于写,在EID匹配的前提下,TRUSTMOD将收到的数据顺序写入FPGA的DRAM中。值得注意的是,为了降低在访问FPGA的DRAM时被侧信道攻击的可能性,TRUSTMOD通过其内部机制(使请求停滞)保证了不管访问请求的数据大小如何,其访问时间均保持一致,这使得利用row hits和row conflicts之间的访问时间差这一侧信道来进行攻击的手段失效,从而在一定程度上保证了数据的安全。后续实验证实了这一手段对性能的影响微乎其微(只增加了0.7%的访问时间开销)。
内存碎片管理
由于不同的应用以及不同的enclave对于内存的需求不尽相同,随着内存碎片的积累,很大一部分空间会被浪费。因此,TRUSTMOD定期运行一个内存整理算法来保证内存碎片的存留时间不会超过一个确定的时长。更具体地,在处理了一定数量的内存回收请求之后,TRUSTMOD同样会对内存进行整理。整理之后,相应的OCMAT也会被更新。
TRUSTLIB与TRUSTMOD的连接
连接具有两层含义,第一层是逻辑上的连接,也既怎样建立一个安全的通信信道,第二层是物理的含义,也既通过什么协议和方式建立连接。
安全通信信道的建立
安全信道的建立基于Diffie-Hellman密钥交换机制,图二详细地描述了这一过程。为了获取真正的随机数(硬件随机数),作者分别利用了rdrand指令(TRUSTLIB中)与一种FPGA真随机数生成器(TRUSTMOD中)来作为DHKE中的RNG。在生成DHKE所需的秘密信息这一过程中,为了避免运行时间的差异所招致的基于时间的侧信道攻击,作者在实现中,将乘法的次数固定(通过引入一些虚假的乘法,例如乘以1),并且让数据的宽度保持一致,这样,不同的密钥交换过程的运行时间均可以保持一致。在密钥交换完并获得共享密钥之后,作者利用了SGX库内已经实现好的AES-GCM-128算法,来进行数据的加解密,这一实现基于Intel的AES-NI指令集,该指令集可以有效的防止基于时间的侧信道攻击。由于AES-GCM算法本身的性质,freshness和完整性也可以得到保证。
I/O协议
TRUSTORE支持两种I/O协议,一种是MMIO,另一种为DMA。
MMIO
MMIO信道由设备驱动建立,具体来说,驱动被安装在不可信OS中,其将FPGA设备注册到Linux设备文件系统devfs中(原文如此,实际上在Linux内核4.16中,devfs已经被弃用,取而代之的是devtmpfs,相较于devfs其性能更好)。由于信道都是加密的,驱动至多能发起DDoS攻击,但这并不在SGX的保护范畴之内。在访问时,TRUSTLIB要求驱动将TRUSTMOD的内存映射到非EPC区域,此后,TRUSTLIB即可直接访问该区域,无须退出enclave。
DMA
对于大文件的传输,DMA是更为高效的选择。在TRUSTMOD中,DMA由两部分组成:1)一个MIMO区域,用来传递DMA请求;2)一个DMA缓冲区,用来传输与请求相关的实际内存区域。由于DMA支持在IO总线上的burst模式传输,因此在块大小较大时,DMA的传输效率相较于MIMO更高一些。
MIMO/DMA中侧信道的防护
在TRUSTMOD中,所有的MIMO访问都被固定在同一地址中,因此,内存访问路径不会泄露任何信息。此外,数据块的大小在运行过程中也都保持一致,因此,不管请求内容如何,TRUSTORE都会读写相同大小的数据区域,这防止了体积信息的泄露(volume-hiding)。
支持的编程接口
为了让enclave能够方便的访问TRUSTMOD中的内存,TRUSTMOD会暴露如下接口(原型)给TRUSTLIB:
OUT ID alloc();
OUT STATUS dealloc(IN ID id);
OUT STATUS access(IN type, IN id, IN SIZE_T offset, IN void* dat_in, OUT void* dat_out, IN SIZE_T size);
其中,IN和OUT为类型修饰符:IN表示TRUSTLIB需要给这个参数提供一个值,OUT表示TRUSTMOD需要提供这个值或者填充这片区域。ID是数据对象的id类型,STATUS则为记录本次调用是否成功的数据类型。接口的具体语义通过名称和参数很容易理解,故不再赘述。需要注意的既是对于读和写,其用一个统一接口access来避免了不同函数调用对信息的额外泄露。其通信包的格式见图3。
图3:TRUSTORE中通信数据包的格式