Enclave开发

Enclave开发包括以下几个步骤:

  1. 使用EDL文件定义不可信app和encalve(飞地)之间的接口(由ecall和ocall来实现)
  2. 实现app和encalve函数。
  3. 编译app和enclave。编译中,Edger8r生成可信和不可信的代理/桥函数,enclave签名工具生成enclave的metadata和签名。需生成Enclave_private.pem
  4. 在模拟和硬件模式下运行和调试app,详细看看调试飞地的内容。
  5. 准备发布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开发

开发流程:

  1. 首先需要放置基础依赖common/inc(c的头文件)和 edl
  2. 使用rust编写 app 和 encalve
  3. 编写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

Licensed under CC BY-NC-SA 4.0
Built with Hugo
主题 StackJimmy 设计