Enclave开发包括以下几个步骤:
- 使用EDL文件定义不可信app和encalve(飞地)之间的接口(由ecall和ocall来实现)
- 实现app和encalve函数。
- 编译app和enclave。编译中,Edger8r生成可信和不可信的代理/桥函数,enclave签名工具生成enclave的metadata和签名。需生成Enclave_private.pem
- 在模拟和硬件模式下运行和调试app,详细看看调试飞地的内容。
- 准备发布app和飞地。
名词解释
EDL 即 Enclave Defination Language. 因为 Enclave 不同于共享的资源, 其与外部不可信内容的交互需要特定的规则进行限定. Intel SGX 给出了一种语言来抽象地描述 Enclave 内部函数的调用权限和数据的出入方式, 并通过 sgx_edger8r 翻译成一系列可编译的代码.
EDL 提出了一种“边规则”(edge routine) 的概念. 一个 edge routine 即是运行于 Enclave 内部或外部的函数和它们与 Enclave 外部或内部的调用关系的总称.
ecall(Enclave Call) 是 Enclave 内部程序的接口函数.
ocall (Out Call)是可由 Enclave 内部代码调用的外部函数接口.
具体开发实例
https://github.com/60ke/rust_sgx_test/tree/master/ocall_beta
此实例是基于incubator-teaclave-sgx-sdk 即baidu rust sdk开发
开发流程:
- 首先需要放置基础依赖common/inc(c的头文件)和 edl
- 使用
rust
编写 app 和 encalve - 编写Makefile文件(可参考Makefile)
1 和 3 的开发相对简单,不再具体描述。下面介绍使用rust
编写 app 和 encalve
代码结构:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
├── .vscode-------------------------- vscode配置文件,用于debug
├── Cargo.toml-------------------------- 定义workspace用于debug
├── Makefile-------------------------- 项目编译
├── app
│ ├── Cargo.toml-------------------------- app的cargo配置
│ ├── build.rs-------------------------- app的build文件
│ └── src -------------------------- rust代码目录
│ └── main.rs-------------------------- app主函数文件
├── bin-------------------------- 存放编译后的二进制文件夹
├── common-------------------------- 引用自[common/inc](https://github.com/mesalock-linux/adler32-rs-sgx/tree/master/sgx/adler32-rs-sgx-test/common/inc)
├── edl-------------------------- 引用自[edl](https://github.com/mesalock-linux/adler32-rs-sgx/tree/master/sgx/adler32-rs-sgx-test/edl)
├── enclave
│ ├── Cargo.toml-------------------------- enclave的cargo配置
│ ├── Enclave.config.xml--------------定义 Enclave 的内存布局等高级特性
│ ├── Enclave.edl--------------Edl文件
│ ├── Enclave.lds--------------encalve链接脚本
│ ├── Enclave_private.pem------------用户生成的私匙供 sgxsdk 签名使用
│ ├── Makefile -------------------------- Encalve编译
│ └── src -------------------------- rust代码目录
│ └── lib.rs-------------------------- enclave的lib文件
|
首先在encalve的Enclave.edl中定义ecall和ocall函数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
| enclave {
//导入引用
from "sgx_tstd.edl" import *;
from "sgx_stdio.edl" import *;
from "sgx_backtrace.edl" import *;
from "sgx_tstdc.edl" import *;
include "sgx_quote.h"
trusted {
//定义ecall
public sgx_status_t say_something([in, size=len] const uint8_t* some_string, size_t len);
};
untrusted {
//定义ocall
void ocall_get_quote([in, size = key_len] uint8_t * key,uint32_t key_len,[out,size = 1000] uint8_t* value,[out] uint8_t* value_len);
};
};
|
ecall的入口在app部分,函数的具体实现在enclave中。先看ecall的edl函数定义:
1
| public sgx_status_t say_something([in, size=len] const uint8_t* some_string, size_t len);
|
这里ecall的调用传入的是一个c语言的常量字符串,为了便于c与rust的交互我们将char list(常量字符串)转换为ASCII码以指针的形式传递。这里的ecall直接使用incubator-teaclave-sgx-sdk中的Enclave.edl 首先声明public,返回的数据类型为sgx_status_t
函数名为say_something
.ecall传入enclave需要用[in]
修饰。由于我们传入的为常量需要加上size的参数size=len
Edl的语法和C语言语法比较接近uint8_t*
和size_t
为数据类型,some_string
和为变量名len
.
enclave的say_something
实现:lib.rs,然后在app中引入:
1
2
3
4
| extern {
fn say_something(eid: sgx_enclave_id_t, retval: *mut sgx_status_t,
some_string: *const u8, len: usize) -> sgx_status_t;
}
|
然后在main.rs中的main函数对其调用。
ocall的入口在enclave中,函数在app中实现。其Edl部分为:
1
| void ocall_get_quote([in, size = key_len] uint8_t * key,uint32_t key_len,[out,size = 1000] uint8_t* value,[out] uint8_t* value_len);
|
ocall的实现的功能为由enclave传出一个key
到app,然后app生成一个对应的value
再传入enclave。整体流程与ecall类似。需要注意的是,由于value的长度不固定,不能单纯定义为一个常量的类型。我们这里先定义了一个足够大的list(1000),然后再传递一个变量value_len
来表示value的长度。然后在enclave
对其进行截取来实现value由app到enclave的返回。
参考
https://blog.lao-yuan.com/2019/02/20/Linux-SGX-Demo.html#edl
https://zhuanlan.zhihu.com/p/39914622