蓬莱 TEE 介绍
蓬莱 TEE 论文
蓬莱 TEE 文档
蓬莱 TEE 项目
@
1 简介
蓬莱TEE满足不同的场景需求:
- Penglai-TVM: 基于OpenSBI,支持不可信host和enclaves的 细粒度隔离(页级别隔离)。代码。
- Penglai-sPMP: 它利用 PMP或者 sPMP (S-mode PMP)提供基本的enclave功能。代码。
- Penglai-ZONE: 是一个旨在支持可信执行环境(TEE)中的特权区域的项目,例如TEEOS,统一可扩展固件接口(UEFI)中的独立MM。代码。
- PengLai-MCU:支持Global Platform,支持PSA。非开源。
为了实现软件可信的通用性,蓬莱TEE架构在安全监控器和具体的硬件原语间,设计了一层“安全原语”接口。 可信环境实例的管理逻辑将实现在这层通用的接口上,而不需要关心具体的硬件隔离和保护机制。
PengLai的新颖特征
- 跨安全区通信:蓬莱支持使用零拷贝内存传输机制的同步和异步跨 enclave 通信。
- 安全文件系统:蓬莱采用 xv6fs 和 littlefs 作为其文件系统,服务于飞地。 它利用 SDK 中的加密库来加密/解密数据并提供完整性保护。
- SDK 中的加密库:支持SM4等国密算法,蓬莱利用Mbed TLS 提供其他加密算法支持,Mbed TLS 是一个 C 库,用于实现加密原语、X.509 证书操作以及 SSL/TLS 和 DTLS 协议。其代码占用空间小,适用于嵌入式系统。
2 PengLai 于2021RISC-V中国峰会
2.1 TEE/enclave有什么用
各大厂商对TEE的重视
近年来,各大CPU厂商都纷纷推出了安全和隔离的特性。
Intel在近6年推出的安全特性数量超过了之前46年的总和。
最近发布的ARMv9也把安全作为与AI、性能并重的三大特性之一,其重点CCA(Confidential Computing Architecture),就是TEE技术之一。
TEE是什么?
TEE就是将操作系统的部分功能下沉到CPU硬件,由硬件直接提供一个隔离的黑盒子执行环境,软件无法干预。
手机端使用指纹解锁、人脸支付等功能的时候,生物特征数据的采集和比对就是在这个黑盒子里完成的,使用的技术是ARM TrustZone。
在服务器端,TEE用来实现更通用的数据保护,比如加密数据库(微软Azure、蚂蚁金服等采用)或加密虚拟机(Google Cloud采用),保证即便是云管理员也无法窃取或篡改云租户的数据。
如果能够保证数据只在黑盒子里运行,那么把这些黑盒子连在一起,就形成了一个可靠的管道,从而允许数据的安全流动。这也是为什么ARM、AMD、Intel、Redhat、Facebook、Google、华为、阿里云、腾讯、百度、字节跳动等公司要在2019年成立机密计算联盟(Confidential Computing Consortium)的原因之一。这里的主要支撑技术之一,就是TEE。
蓬莱TEE
现存的问题推动蓬莱的发展
- 硬件接口的碎片化
- 运行规模的不可扩展
- 对性能尤其是时延的影响
- 复杂度的增加
- TEE自身出现可靠性问题
问:RISC-V平台不是已经有keystone了么,为什么还要重新造轮子?
答:需要有中国自己的TEE
蓬莱的特点
- 科研+应用驱动
- 场景多样化,考虑云和端不同需求,支持大内存的完整性保护,同时能运行1,000实例,且能做到4KB粒度的内存保护
- 开放平台
3 蓬莱文档
蓬莱-TVM:通过受保护的页表(参考论文)的纯软件实现实现了一个可扩展的飞地系统,蓬莱TVM可以实现可扩展的内存保护、零拷贝通信和安全区快速启动。
蓬莱-PMP:利用 PMP/sPMP 来保护安全区内存。蓬莱-PMP版本更轻巧,可用于物联网设备。
3.1 教程(蓬莱TVM)
3.1.1 运行飞地程序hello world
主机应用程序是运行在主机内核中的不受信任的部分,可以创建或运行安全区。Enclave 应用,是在 enclave 中运行的受信任部分。
主机操作飞地生命周期
为基本主机应用程序提供的操作 enclave 的 API:PLenclave_create,PLenclave_run。
创建:使用PLenclave_create
接口来创建飞地。Monitor 将实例化一个 enclave 实例并返回相应的eid ,使用不同的参数,主机可以创建不同类型的 enclave:影子 enclave、服务器 enclave 等。
1 | PLenclave_create(enclave, enclaveFile, params) |
运行:创建后,enclave 不会运行,直到调用PLenclave_run,enclave才运行
1 | result = PLenclave_run(enclave) |
结束:enclave 线程不会返回到主机, 除非:(1) Enclave 已完成并退出。(2) Enclave 执行 ocall,需要返回用户级别进行处理。如果成功完成 enclave,则返回结果(不是返回值)将保持为零。 enclave 的返回值存储在返回参数enclave->user_param.retval
中。
主机操作飞地的结构体
有三种基本结构可以操纵飞地,其初始化如下:
1 | elf_args_init(enclaveFile, eappfile) |
enclave应用
hello-world 在 enclave 中运行,只需要对原始程序进行微小的修改。
1 | #include "eapp.h" |
3.1.2 enclave-enclave和enclave-host之间的IPC
有两种方法可以实现IPC:共享内存,所有权转移。
主机分配共享内存
提供以下接口来为主机分配共享内存:
- PLenclave_shmget获取具有给定大小的共享内存 ID,每个 ID 绑定一系列共享内存。
- PLenclave_shmat使用给定的共享内存 ID 获取相应的共享内存地址,并将共享内存映射到主机地址空间。
- PLenclave_shmdt从主机地址空间取消映射共享内存。
- PLenclave_shmctl撤销共享内存 ID,任何人都无法使用此 ID 检索共享内存。
host 和 enclave 可以同时使用共享内存。
创建enclave 前,host需要设置与共享内存相关的参数
1 | enclave_params->shmid = shmid; |
主机进行所有权转移
将所有权转移的内存命名为 for host 和 for enclave,提供以下接口来为主机分配schrodinger page。
- PLenclave_schrodinger_get获取具有给定大小的schrodinger pageID。每个 ID都与一系列schrodinger page绑定。
- PLenclave_schrodinger_at使用给定的schrodinger page ID 获取相应的schrodinger page地址,并将schrodinger page映射到主机地址空间。
- PLenclave_schrodinger_dt从主机地址空间取消映schrodinger page。
- PLenclave_schrodinger_ctl撤销schrodinger page ID,任何人都无法使用此 ID 检索schrodinger page。
schrodinger page只能映射在主机或飞地中。当分配schrodinger page时,它们首先被映射到主机空间中。所以主机可以把这些页面读/写作为普通内存。 当 enclave 运行时,monitor 将保证所有schrodinger page在主机中取消映射,并重新映射到 enclave。所以 enclave 可以读取/写入这些页面。schrodinger page用于防御TOCTTOU(Time-Of-Check-To-Time-Of-Use)攻击,可实现零拷贝通信。
主机可以使用PLenclave_set_mem_arg(enclave, mm_arg_id, 0, mm_arg_size)
函数将schrodinger page与给定的 enclave 绑定。
飞地IPC
enclave可以在相应的寄存器中获取共享内存和schrodinger page,a0和a1保留,用于存放共享内存的基地址和大小;a3和a4保留,用于存放schrodinger page的基地址和大小。
类似于上面所述的host-enclave IPC,Enclave-Enclave IPC也可以使用这两种方法,主机可以将单个共享内存分配给多个 enclave。因此,这些飞地可以共享相同的内存。在call_enclave_arg_t结构中,定义了请求和响应参数。
1 | struct call_enclave_arg_t |
请求参数可以在寄存器或传输的内存中传递。 响应参数的格式与请求参数类似,支持返回寄存器和传输的内存。可以通过req_vaddr = eapp_mmap(NULL, size)
接口分配enclave中所有权转移页面。
还在 eapp 库中定义了 enclave 调用,它可以支持服务器 enclave 的同步 IPC 调用。 调用方 enclave 将等到被调用方返回。 IPC 结构是调用参数,可以在调用方和被调用方 enclave 之间传输。 IPC 结构中定义的内存将更改其所有权并重新映射到目标飞地,此机制可确保只有一个 enclave 可以访问此内存范围。
3.1.3 影子飞地
影子 enclave 是一个干净的模板,另一个 enclave 可以fork影子 enclave 来实现快速启动。 影子 enclave 适用于需要使用单个源代码自动扩展多个 enclave 实例的场景。
创建影子 enclave 与创建普通 enclave 类似,只需将 creation 参数中的 enclave type 设置为 SHADOW_ENCLAVE
, 创建影子飞地后,它将返回其 eid。fork的 enclave 可以使用此影子 enclave eid 来初始化新的 enclave 实例。
1 | struct elf_args* enclaveFile = malloc(sizeof(struct elf_args)); |
主机可以从单个影子 enclave fork多个 enclave 实例。每个 enclave 实例不会相互干扰,有自己的堆栈和堆。这样可以减少测量开销,适用于快速启动和自动缩放的场景。
3.1.4 服务器enclave
服务器 enclave 是一种特殊类型的 enclave,它不会运行,直到其他 enclave 调用它。 通过服务器 enclave,可以在蓬莱实现一个 enclave 链。服务器安全区可以充当单独的库或操作系统服务器等。
服务器 enclave 的创建类似于普通 enclave。要创建服务器 enclave,只需将 enclave 创建参数中的 enclave 类型设置为SERVER_ENCLAVE
,并为服务器 enclave 分配一个唯一的ID。 在调用此服务器 enclave 时,enclave 名称用作标识。
主机不需要运行服务器 enclave,服务器 enclave 可以由其他 enclave 或服务器 enclave 调用,并重用其主机上下文。目前,服务器 enclave 只能按顺序调用,稍后将支持并发服务器 enclave 调用。
当不再需要服务器 enclave 时,主机可以使用PLenclave_destroy(server1_enclave)
API 销毁服务器 enclave
3.1.5 证明
监视器返回二点证明报告,包括 enclave 和安全监视器的度量。
主机可以调用PLenclave_attest
函数来获取 enclave 证明报告。证明报告由设备公钥、安全监视器报告和enclave报告组成。 主机可以使用证明报告对合法 enclave 进行身份验证。设备公钥和安全监控公钥用于验证相应的签名。 证明中需要 nonce,以确保其他人无法重用过时的证明报告。
1 | struct sm_report_t |
Enclave 可以使用get_report()
获取证明报告。证明报告的结构与主机使用的报告相同。 如果证明的 enclave 名称设置为 NULL,它将返回当前 enclave 的证明报告,否则,它将返回具有给定服务器 enclave 名称的证明报告。 在调用服务器 enclave 之前,最好使用get_report验证服务器 enclave 测量值。
1 | int get_report(char* name, struct report_t *report, unsigned long nonce); |
可以基于本地证明实现远程认证。
3.1.6 enclave停止、恢复、销毁
当 enclave 运行时,另一个主机线程可以使用PLenclave_stop
停止正在运行的 enclave。正在运行的 enclave 将被中断,监视器将存储其上下文。
主机可以使用PLenclave_resume
恢复已停止的 enclave,该 enclave 将继续从最后一个停止点运行。
主机可以使用PLenclave_destory
销毁 enclave 并回收其资源。 主机无法销毁已退出的 enclave。PLenclave_destroy
还可以销毁服务器 enclave 和 shadow enclave。在销毁这些特殊类型的 enclave 之前,需要确保以后永远不会调用服务器 enclave 或影子 enclave。PLenclave_stop
和PLenclave_resume
并且不能用于停止/恢复服务器 enclave 或影子 enclave,因为它们不是可运行的实例。
1 | // Stop a running enclave |
3.2 蓬莱-TVM用户手册
蓬莱TVM是RISC-V上的可扩展飞地系统,实现了细粒度、可扩展的内存管理。利用 RISC-V 功能:陷阱虚拟内存 (TVM) 通过纯软件设计实现保护页表。
3.2.1 文件结构
1 | . |
蓬莱有三个关键的子模块:Linux(支持保护页表)、sdk(主机、飞地库和飞地驱动)和Opensbi(包括安全监控)。
3.2.2 主机端接口
int PLenclave_init(struct PLenclave *PLenclave)
- 描述:初始化 enclave 元数据
- 参数:
- PLenclave:主机程序中使用的 enclave 结构
int PLenclave_create(struct PLenclave* PLenclave, struct elf_args* u_elffile, struct enclave_args* u_param)
- 描述:使用给定的参数和 enclave 文件创建 enclave。
- 参数:
- PLenclave:主机程序中使用的 enclave 结构。
- u_elffile:创建的 Enclave 的 ELF 文件结构。
- u_param:用户给定的参数,用于创建 enclave。
int PLenclave_run(struct PLenclave *PLenclave)
- 描述:运行一个 enclave,除非 (1) enclave 完成、停止或销毁,(2) enclave 触发一个需要在用户模式(主机)下处理的 ocall,否则此函数不会返回。
int PLenclave_attest(struct PLenclave *PLenclave, uintptr_t nonce)
- 描述:根据给定的随机数获取证明报告
int PLenclave_stop(struct PLenclave *PLenclave)
- 描述:停止正在运行的 enclave。主机可以强制停止正在运行的 enclave,因此来自不受信任的主机的 DoS 攻击超出了范围。
int PLenclave_resume(struct PLenclave *PLenclave)
- 描述:恢复已停止的 enclave。enclave 可以从上次退出的点继续运行。
int PLenclave_destroy(struct PLenclave *PLenclave)
- 描述:摧毁飞地。Enclave 驱动程序和安全监视器将释放 Enclave 资源和元数据。主机可以强制销毁正在运行的 enclave,例如,在接收到终止信号时。值得注意的是,如果 enclave 成功退出,host 就不需要再调用PLenclave_destroy了,因为 enclave 的元数据和资源将在退出过程中释放。
int PLenclave_destruct(struct PLenclave *PLenclave)
- 描述:破坏 enclave 结构及其元数据。目前,它只是关闭了 enclave 驱动程序的文件描述,该文件在PLenclave_init中打开。
void elf_args_init(struct elf_args* elf_args, char *filename)
- 描述:使用给定的文件名初始化 elf 结构。
void elf_args_destroy(struct elf_args* elf_args)
- 描述:回收资源。
void enclave_args_init(struct enclave_args* enclave_args)
- 描述:初始化 enclave 参数。
int PLenclave_set_shm(struct PLenclave *enclave, int shmid, uintptr_t offset, uintptr_t size)
描述:在enclave中配置共享内存,创建参数。
参数:
- enclave:主机程序中使用的 enclave 结构。
- shmid:共享内存标识。
- offset:共享内存中的偏移量。
- size:共享内存大小。
int PLenclave_set_mem_arg(struct PLenclave *enclave, int id, uintptr_t offset, uintptr_t size)
- 描述:设置schrodinger page在飞地运行参数。schrodinger page的所有权在 enclave 运行时转移。
int PLenclave_set_rerun_arg(struct PLenclave *enclave, int rerun_reason)
- 描述:在其参数中设置 enclave 重新运行原因。
Pint PLenclave_shmget(unsigned long size)
- 描述:在 enclave 和主机之间分配共享内存,并返回共享内存标识。
- 参数:
- size:共享内存大小。
- 返回值:
- shmid:识别所需的共享内存
void* PLenclave_shmat(int shmid, void* addr)
- 描述:获取具有给定 shmid 的共享内存地址。将此共享内存映射到主机 VA 空间。
- 参数:
- shmid:共享内存标识。
- addr:共享内存地址。0(默认值)表示共享内存可以映射到主机 VA 空间中的任何可用虚拟地址。
- 返回值:
- addr:共享内存的虚拟地址。
int PLenclave_shmdt(int shmid, void* addr)
- 描述:使用给定的 shmid 取消映射主机 VA 空间中的共享内存。但是,共享内存 ID 未解除分配,因此其他人仍然可以使用此 shmid 来获取相应的共享内存。
int PLenclave_shmctl(int shmid)
- 描述:回收共享内存标识。主机不能再使用此 shmid 来获取相应的共享内存。
int PLenclave_schrodinger_get(unsigned long size)
- 描述:在enclave和host之间分配schrodinger页面(实现零拷贝通信),并返回其标识。
void* PLenclave_schrodinger_at(int id, void* addr)
- 描述:获取具有给定 ID 的schrodinger页面地址。将这些schrodinger页面映射到主机 VA 空间。
int PLenclave_schrodinger_dt(int id, void* addr)
- 描述:使用给定的 shmid 取消映射主机 VA 空间中的schrodinger页面。但是,schrodinger页面 ID 未解除分配,因此其他人仍然可以使用此 ID 获取相应的schrodinger页面。
int PLenclave_schrodinger_ctl(int id)
- 回收schrodinger页面标识。主机不能再使用此 ID 来获取相应的schrodinger页面。
3.2.3 安全区端口
Libc支持
集成到飞地端库musl libc
中,它可以支持几个未修改的 libc 接口:
- Print
- printf
- Memory related:
- malloc
- calloc
- free
- sbrk
- FS related:
- fopen
- fputs
- fgets
- fclose
- stat
其他接口(如memset()
、memcpy()
等)与内核没有交互,蓬莱 enclave 也支持这些接口。
特定于 Enclave 的接口
void EAPP_RETURN(unsigned long retval) __attribute__((noreturn))
- 描述:退出 enclave 并给出返回值。
unsigned long get_enclave_id()
- 描述:获取当前 enclave 标识。
void* eapp_mmap(void* vaddr, unsigned long size)
- 描述:分配 enclave 内存并将其映射到 enclave VA 空间中。这些内存可用作 enclave 中的中继页面(enclave 之间的零拷贝通信)。
int eapp_unmap(void* vaddr, unsigned long size)
- 描述:取消映射 enclave 内存
unsigned long acquire_enclave(char* name)
- 描述:获取具有给定 enclave 名称的服务器 enclave 处理程序。enclave 处理程序可用于 enclave 调用。
int call_enclave(unsigned long handle, struct call_enclave_arg_t* arg)
- 描述:同步 enclave 调用。调用方 enclave 可以使用此接口调用其他 enclave。调用方 enclave 必须等到被叫方 enclave 返回。
int asyn_enclave_call(char* name, struct call_enclave_arg_t *arg)
- 描述:异步 enclave 调用。调用方 enclave 可以使用此接口将 IPC 参数传递给被调用方 enclave。调用方 enclave 将继续运行,被调用方 enclave 将在创建时接收 IPC 参数。
void SERVER_RETURN(struct call_enclave_arg_t *arg) __attribute__((noreturn))
- 描述:被调用方 enclave 调用此函数以返回到调用方 enclave,并同时传输返回 IPC 结构。
int EAPP_GET_REPORT(char * name, struct report_t *report, unsigned long nonce)
- 描述:获取 enclave 证明报告。如果设置了 name,它将返回具有给定名称的 enclave 报告,否则,它将返回当前 enclave 的证明报告。
3.2.4 配置
Enclave 内存布局
1 | /* default layout of enclave */ |
Enclave 内存布局在/Penglai-Opensbi-TVM/include/sm/enclave_vm.h
ENCLAVE_DEFAULT_STACK_SIZE
- 默认围圈堆栈大小:64K
ENCLAVE_DEFAULT_STACK_BASE
- 默认围圈堆栈基址:0x0000003800000000UL
ENCLAVE_DEFAULT_KBUFFER
- 内核和 enclave 之间共享内存的起始地址:0xffffffe000000000UL
ENCLAVE_DEFAULT_KBUFFER_SIZE
- 内核和 enclave 之间共享内存的默认大小:1K
ENCLAVE_DEFAULT_SHM_BASE
- 主机和enclave之间的共享内存,enclave VA 空间中的默认起始地址:0x0000003900000000UL
ENCLAVE_DEFAULT_MM_ARG_BASE
- enclave VA 空间中的零拷贝内存起始地址:0x0000003900000000UL
ENCLAVE_DEFAULT_MMAP_BASE
- enclave VA 空间中的 Mmap 内存范围起始地址:0x0000003000000000UL
ENCLAVE_DEFAULT_HEAP_BASE
- enclave VA 空间中的堆起始地址: 0x0000001000000000UL
ENCLAVE_DEFAULT_TEXT_BASE
- enclave VA 空间中的文本部分起始地址(入口点):0x00000000000001000UL
DEFAULT_SHADOW_ENCLAVE_ORDER
- 影子 enclave 的初始化页面顺序。这些页面稍后将用于创建影子 enclave 实例(用作堆和堆栈内存)
DEFAULT_SECURE_PAGES_ORDER
- 安全内存的页面顺序。当显示器持有的安全内存耗尽时,蓬莱驱动程序将一定范围的内存传输到监视器(用作安全内存)中。
DEFAULT_SCHRODINGER_ORDER
- 零拷贝通信页面的页面顺序。蓬莱驱动在初始化时会分配所有schrodinger。如果需要,这些页面将被分配给每个飞地。
转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达,可以在下面评论区评论