ya0guang commented on a change in pull request #5: URL: https://github.com/apache/incubator-teaclave-website/pull/5#discussion_r690730153
########## File path: site/blog/2021-08-13-developing-sgx-application-with-teaclave-sgx-sdk.md ########## @@ -0,0 +1,331 @@ +--- +title: 使用 Teaclave SGX SDK 开发 SGX 应用 +date: 2021-08-13 +author: Wenwen Ruan +--- + +[[TOC]] + + +## Teaclave SGX SDK应用开发环境简介以及搭建 + +Intel SGX (软件防护扩展,Software Guard Extension) 提供了在一个安全的 enclave(飞地)中执行代码的能力,enclave 可以提供一个隔离的可信执行环境,在操作系统、BIOS 和虚拟机监控器等系统软件均不可信的情况下,仍然对 enclave 内部的代码和数据提供保护,保障用户的关键数据和代码的机密性和完整性。 Review comment: ……除了CPU之外的……均不可信 ########## File path: site/blog/2021-08-13-developing-sgx-application-with-teaclave-sgx-sdk.md ########## @@ -0,0 +1,331 @@ +--- +title: 使用 Teaclave SGX SDK 开发 SGX 应用 +date: 2021-08-13 +author: Wenwen Ruan +--- + +[[TOC]] + + +## Teaclave SGX SDK应用开发环境简介以及搭建 + +Intel SGX (软件防护扩展,Software Guard Extension) 提供了在一个安全的 enclave(飞地)中执行代码的能力,enclave 可以提供一个隔离的可信执行环境,在操作系统、BIOS 和虚拟机监控器等系统软件均不可信的情况下,仍然对 enclave 内部的代码和数据提供保护,保障用户的关键数据和代码的机密性和完整性。 Review comment: > ……除了CPU之外的……均不可信 建议顺便一句话介绍SGX和enclave **是什么** ########## File path: site/blog/2021-08-13-developing-sgx-application-with-teaclave-sgx-sdk.md ########## @@ -0,0 +1,331 @@ +--- +title: 使用 Teaclave SGX SDK 开发 SGX 应用 +date: 2021-08-13 +author: Wenwen Ruan +--- + +[[TOC]] + + +## Teaclave SGX SDK应用开发环境简介以及搭建 + +Intel SGX (软件防护扩展,Software Guard Extension) 提供了在一个安全的 enclave(飞地)中执行代码的能力,enclave 可以提供一个隔离的可信执行环境,在操作系统、BIOS 和虚拟机监控器等系统软件均不可信的情况下,仍然对 enclave 内部的代码和数据提供保护,保障用户的关键数据和代码的机密性和完整性。 + +但如果 Intel SGX 程序仍然使用 C/C++ 这类内存不安全的语言开发的话,就会和传统软件一样面临着传统的内存破坏漏洞。对于 enclave 来说,受到的危害会更为严重,因为 enclave 中保存的多是机密数据和代码。Teaclave SGX 的主要目标就是通过使用高效的内存安全语言 —— Rust 来支持 enclave 应用程序的开发,从而在保证 Intel SGX enclave 内存安全的同时不会带来显著的性能开销。 + +Teaclave SGX SDK 内部结构分为三层: + ++ 最底层是使用 C/C++ 和汇编实现的 Intel SGX SDK。 ++ 中间层是 Rust 对 C/C++ 的 FFI (Foreign function Interfaces, 外部函数接口)。 ++ 最高层是 Teaclave SGX SDK。 + +![Teaclave SGX SDK 概要图](./img/2021-08-13-overview-of-teaclave-sgx-sdk.png) + +Teaclave SGX SDK 应用程序开发者在进行开发时就只需要基于最上层的 Teaclave SGX SDK 来进行开发,底层的实现对于开发者来说是透明的。本文将从开发者的角度介绍基于 Teaclave SGX SDK 开发自己的应用程序的过程。 + +### 准备条件 + ++ Ubuntu16.04 或者 18.04 或者 20.04 (Teaclave SGX SDK v1.1.3 中增加了对 Ubuntu 20.04 的支持) ++ docker 环境 + +*本文基于 Teaclave SGX SDK v1.1.3 提交哈希值:d107bd0718f723221750a4f2973451b386cbf9d2* + + +### 基于 docker 配置 Teaclave SGX SDK 开发环境 + +首先需要用户机器 CPU 支持 Intel SGX 并且在 BIOS 上开启了 Intel SGX 支持。用户可以通过 [SGX-hardware项目](https://github.com/ayeks/SGX-hardware) 或者在 [Intel 官网](https://www.intel.com/content/www/us/en/products/details/processors.html) 中搜索自己的 CPU 型号查看是否支持 Intel SGX。下图以 Intel Core i7-7700K 处理器为例,如下图所示,该机型支持 SGX。 + +![sgx-enable.png](./img/2021-08-13-sgx-enable.png) + +当确定 CPU 支持 Intel SGX 之后,还需要开启 BIOS 中的 SGX 选项。CPU 上的 SGX 选项可能有 `enabled` 或者 `software controlled`。具有 `enabled` 选项的主机直接在 BIOS 上选择 `enabled` 即可,而`software controlled` 表示 SGX 的开启需要由软件触发,还需通过 Intel 官方提供的 [sgx-software-enable](https://github.com/intel/sgx-software-enable) 开启。下载好 `sgx-software-enable` 之后,运行 `Makefile` 编译生成可执行代码 `sgx_enable` ,执行 `sudo ./sgx_enable` 顺利运行后重启主机,即可顺利开启 Intel SGX。 + +硬件条件准备完毕之后,还需要安装 [Linux SGX 驱动](https://download.01.org/intel-sgx/sgx-linux/2.10/distro/ubuntu16.04-server/sgx_linux_x64_driver_2.6.0_602374c.bin) ,安装完毕之后需要确认 `/dev/isgx` 的存在。 Review comment: 这个驱动是不是只针对Ubuntu 16.04 server的?前面提到了对于Ubuntu其他版本的支持,如果是的话建议注明或者介绍其他如何找到对应版本的驱动。 ########## File path: site/blog/2021-08-13-developing-sgx-application-with-teaclave-sgx-sdk.md ########## @@ -0,0 +1,331 @@ +--- +title: 使用 Teaclave SGX SDK 开发 SGX 应用 +date: 2021-08-13 +author: Wenwen Ruan +--- + +[[TOC]] + + +## Teaclave SGX SDK应用开发环境简介以及搭建 + +Intel SGX (软件防护扩展,Software Guard Extension) 提供了在一个安全的 enclave(飞地)中执行代码的能力,enclave 可以提供一个隔离的可信执行环境,在操作系统、BIOS 和虚拟机监控器等系统软件均不可信的情况下,仍然对 enclave 内部的代码和数据提供保护,保障用户的关键数据和代码的机密性和完整性。 + +但如果 Intel SGX 程序仍然使用 C/C++ 这类内存不安全的语言开发的话,就会和传统软件一样面临着传统的内存破坏漏洞。对于 enclave 来说,受到的危害会更为严重,因为 enclave 中保存的多是机密数据和代码。Teaclave SGX 的主要目标就是通过使用高效的内存安全语言 —— Rust 来支持 enclave 应用程序的开发,从而在保证 Intel SGX enclave 内存安全的同时不会带来显著的性能开销。 + +Teaclave SGX SDK 内部结构分为三层: + ++ 最底层是使用 C/C++ 和汇编实现的 Intel SGX SDK。 ++ 中间层是 Rust 对 C/C++ 的 FFI (Foreign function Interfaces, 外部函数接口)。 ++ 最高层是 Teaclave SGX SDK。 + +![Teaclave SGX SDK 概要图](./img/2021-08-13-overview-of-teaclave-sgx-sdk.png) + +Teaclave SGX SDK 应用程序开发者在进行开发时就只需要基于最上层的 Teaclave SGX SDK 来进行开发,底层的实现对于开发者来说是透明的。本文将从开发者的角度介绍基于 Teaclave SGX SDK 开发自己的应用程序的过程。 + +### 准备条件 + ++ Ubuntu16.04 或者 18.04 或者 20.04 (Teaclave SGX SDK v1.1.3 中增加了对 Ubuntu 20.04 的支持) ++ docker 环境 + +*本文基于 Teaclave SGX SDK v1.1.3 提交哈希值:d107bd0718f723221750a4f2973451b386cbf9d2* + + +### 基于 docker 配置 Teaclave SGX SDK 开发环境 + +首先需要用户机器 CPU 支持 Intel SGX 并且在 BIOS 上开启了 Intel SGX 支持。用户可以通过 [SGX-hardware项目](https://github.com/ayeks/SGX-hardware) 或者在 [Intel 官网](https://www.intel.com/content/www/us/en/products/details/processors.html) 中搜索自己的 CPU 型号查看是否支持 Intel SGX。下图以 Intel Core i7-7700K 处理器为例,如下图所示,该机型支持 SGX。 + +![sgx-enable.png](./img/2021-08-13-sgx-enable.png) + +当确定 CPU 支持 Intel SGX 之后,还需要开启 BIOS 中的 SGX 选项。CPU 上的 SGX 选项可能有 `enabled` 或者 `software controlled`。具有 `enabled` 选项的主机直接在 BIOS 上选择 `enabled` 即可,而`software controlled` 表示 SGX 的开启需要由软件触发,还需通过 Intel 官方提供的 [sgx-software-enable](https://github.com/intel/sgx-software-enable) 开启。下载好 `sgx-software-enable` 之后,运行 `Makefile` 编译生成可执行代码 `sgx_enable` ,执行 `sudo ./sgx_enable` 顺利运行后重启主机,即可顺利开启 Intel SGX。 + +硬件条件准备完毕之后,还需要安装 [Linux SGX 驱动](https://download.01.org/intel-sgx/sgx-linux/2.10/distro/ubuntu16.04-server/sgx_linux_x64_driver_2.6.0_602374c.bin) ,安装完毕之后需要确认 `/dev/isgx` 的存在。 + +下载 Teaclave SGX SDK 以及支持编译 SGX 设备的 docker image。 + +`$ https://github.com/apache/incubator-teaclave-sgx-sdk` + +`$ docker pull baiduxlab/sgx-rust` + +启动一个 docker,并且把 Teaclave SGX SDK 项目目录映射到 docker 中。 + +`$ docker run -v /your/absolute/path/to/incubator-teaclave-sgx-sdk:/root/sgx -ti --device /dev/isgx baiduxlab/sgx-rust` + +在运行的 docker container 中启动 aesm 服务,**White list update request successful for Version** 语句意味着启动成功。 + +````bash +root@docker:/# LD_LIBRARY_PATH=/opt/intel/sgx-aesm-service/aesm/ /opt/intel/sgx-aesm-service/aesm/aesm_service & +aesm_service[17]: [ADMIN]White List update requested +aesm_service[17]: Failed to load QE3: 0x4004 +aesm_service[17]: The server sock is 0x56096ab991c0 +aesm_service[17]: [ADMIN]White list update request successful for Version: 103 +```` + +执行 Teaclave SGX SDK 中的简单实例 helloworld ,检查是否正常运行。 + +```bash +root@docker:~# cd sgx/samplecode/helloworld/ +root@docker:~/sgx/samplecode/helloworld# make +root@docker:~/sgx/samplecode/helloworld# cd bin/ +root@docker:~/sgx/samplecode/helloworld/bin# ./app +[+] global_eid: 2 +This is normal world string passed into enclave! +This is a Rust string! +[+] say_something success ... +``` +至此,我们已经成功在自己的机器上跑起来了 Teaclave SGX SDK 的 helloworld 示例啦! + +## Teaclave SGX SDK 示例 helloworld 剖析 + +接下来,我们通过阅读 helloworld 这个简单的例子来理解 Teaclave SGX SDK 应用程序的组织结构和运行方式。 + +### helloworld 目录结构 + +```tree +helloworld/ +├── app +│ ├── app.c +│ └── app.h +├── bin +│ └── readme.txt +├── enclave +│ ├── Cargo.toml +│ ├── Enclave.config.xml +│ ├── Enclave.edl +│ ├── Enclave.lds +│ ├── Enclave_private.pem +│ ├── Makefile +│ ├── src +│ │ └── lib.rs +│ ├── x86_64-unknown-linux-sgx.json +│ └── Xargo.toml +├── lib +│ └── readme.txt +└── Makefile +``` + +helloworld 的目录结构和 Intel SGX 的 [SampleEnclave](https://github.com/intel/linux-sgx/blob/HEAD/SampleCode/SampleEnclave) 目录结构非常类似。 ++ app 目录中存放的是不可信部分代码,包括 `main` 函数以及 `OCALL` 函数具体逻辑实现。 ++ enclave 目录中存放的是可信部分代码,主要是 `ECALL` 函数具体逻辑实现。 + + 不同于 SGX ,应用安全区的代码实现位于 **`src/lib.rs`**, 该文件是整个 `helloworld` 文件夹中唯一使用 Rust 编写的文件,程序员可以在该文件中增加需要的功能。 + + 另外,enclave 文件夹下多了 `Cargo.toml`, `src/lib.rs`, `x86_64-unknown-linux-sgx.json`, `Xargo.toml`: + + **`Cargo.toml`**: 项目清单文件,包括项目名称、项目版本以及依赖项等。 + + **`x86_64-unknown-linux-sgx.json`** 和 **`Xargo.toml`** 描述了用于项目交叉编译的信息。 + +### 重要代码文件解析 + ++ **`Enclave.edl`** \ +该文件规定了 Enclave 边界 `ECALL/OCALL` 的定义。 + +```edl +enclave { + from "sgx_tstd.edl" import *; + from "sgx_stdio.edl" import *; + from "sgx_backtrace.edl" import *; + from "sgx_tstdc.edl" import *; + + trusted { + /* define ECALLs here. */ + + public sgx_status_t say_something([in, size=len] const uint8_t* some_string, size_t len); + }; + + untrusted { + + }; +}; +``` + +`trusted {...}` 中声明 `ECALL` 函数, `untrusted {...}` 中声明 `OCALL` 函数。本例中声明了一个 `ECALL` 函数 `say_something`,该函数的具体实现在 `src/lib.rs` 中,它的参数包括 `uint8_t *` 类型的指针和长度参数 `len`。 + ++ **`app/app.c`** + +在 `app/app.c` 的 `main` 函数中有一个完整的调用 `ECALL` 的例子。 + +```c +sgx_ret = say_something(global_eid, + &enclave_ret, + (const uint8_t *) str, + len); +``` + +这里的 `say_something` 似乎和 `Enclave.edl` 中的声明不太一样,ECALL传递参数时多了两个隐参数:`enclave_eid` 和 `say_something` 的返回值 `&enclave_ret`。而 `sgx_ret` 表示的是 ECALL 执行是否成功,是 SGX 的返回值。 + ++ **`enclave/`文件夹部分** + +`enclave/Cargo.toml` 中声明了这是一个 `staticlib`,表明 Enclave 在最后会被编译成一个 `.a` 文件,该文件会和 Intel 提供的 `sgx_tstdc.a` 等文件链接形成 `enclave.so`,再经由 `sgx_sign` 工具配合 `Enclave.config.xml` 配置文件、`Enclave_private.pem` 签名私钥做签名并计算 `measurement` ,最后生成 `enclave.signed.so`,这是 Enclave 的完全体。 + ++ **`enclave/src/lib.rs`** + +```rust +pub extern "C" fn say_something(some_string: *const u8, some_len: usize) -> sgx_status_t { + + let str_slice = unsafe { slice::from_raw_parts(some_string, some_len) }; + let _ = io::stdout().write(str_slice); + + // A sample &'static string + let rust_raw_string = "This is a "; + // An array + let word:[u8;4] = [82, 117, 115, 116]; + // An vector + let word_vec:Vec<u8> = vec![32, 115, 116, 114, 105, 110, 103, 33]; + + // Construct a string from &'static string + let mut hello_string = String::from(rust_raw_string); + + // Iterate on word array + for c in word.iter() { + hello_string.push(*c as char); + } + + // Rust style convertion + hello_string += String::from_utf8(word_vec).expect("Invalid UTF-8") + .as_str(); + + // Ocall to normal world for output + println!("{}", &hello_string); + + sgx_status_t::SGX_SUCCESS +} +``` +该函数实现了一个简单的将 `u8` 数组转化为字符串输出的函数,注意在函数的最后调用的 `println!` 函数是一个 `OCALL`。 `println!` 的具体实现中加入了内置的 `OCALL`,并定义了内置的 `EDL` ,import到了 `Enclave.edl` 中。 +```edl +enclave { + from "sgx_tstd.edl" import *; + from "sgx_stdio.edl" import *; + from "sgx_backtrace.edl" import *; + from "sgx_tstdc.edl" import *; +``` + + +### 编译后的代码目录 +经过编译之后的代码目录如下所示,这里省略了 `release` 文件夹下的内容。 +```tree +├── app +│ ├── app.c +│ ├── app.h +│ ├── app.o #[generate] +│ ├── Enclave_u.c #[generate] +│ ├── Enclave_u.h #[generate] +│ └── Enclave_u.o #[generate] +├── bin +│ ├── app #[generate] +│ ├── enclave.signed.so #[generate] +│ └── readme.txt +├── enclave +│ ├── Cargo.lock #[generate] +│ ├── Cargo.toml +│ ├── Enclave.config.xml +│ ├── Enclave.edl +│ ├── Enclave.lds +│ ├── Enclave_private.pem +│ ├── enclave.so #[generate] +│ ├── Enclave_t.c #[generate] +│ ├── Enclave_t.h #[generate] +│ ├── Enclave_t.o #[generate] +│ ├── Makefile +│ ├── src +│ │ └── lib.rs +│ ├── target #[generate] +│ │ ├── CACHEDIR.TAG #[generate] +│ │ └── release #[generate] +│ ├── x86_64-unknown-linux-sgx.json +│ └── Xargo.toml +├── lib +│ ├── libenclave.a #[generate] +│ ├── libsgx_ustdc.a #[generate] +│ └── readme.txt +└── Makefile +``` +helloworld 编译的基本流程类似于 Intel SGX: ++ `EDL` 编译器 `edger8r` 将输入的 `EDL` 在 `app/` 目录下生成不可信代码 `Enclave_u.h` 和 `Enclave_u.c`; ++ 编译不可信部分生成 `bin/app`; ++ `edger8r` 在 `enclave/` 目录下生成可信代码 `Enclave_t.h` 和 `Enclave_t.c`; ++ 编译并签名生成可信动态链接库 `enclave.signed.so`。 + +## 开发者如何开发自己的 Rust SGX Application +同样类似于开发 Intel SGX Application,用户可以通过改写 Teaclave SGX SDK 所提供的 `samplecode`,在这里,我以一个简单的例子抛砖引玉。 + +### 添加自定义的函数 +假设用户希望在 Teaclave SGX SDK 中实现一个简单的求两个数组的交集的函数,只需要直接在 `src/lib.rs` 中添加实现的函数。下面的实例代码 `intersection` 函数是希望添加的求交集函数。传入的两个参数是需要求交集的 `i32` 数组,最后返回的是两个数组的交集。其具体的实现是通过一个额外的散列集,记录 `num1` 出现的元素,再对 `num2` 进行遍历,如果 `num2` 出现了散列集中的元素,则将该值 `push` 到交集数组中,并将散列表中的对应元素移除。当 `num2` 遍历完毕之后,返回交集数组。 + +```rust +pub fn intersection(nums1: Vec<i32>, nums2: Vec<i32>) -> Vec<i32> { + use std::collections::HashSet; + let mut set: HashSet<i32> = HashSet::new(); + let mut vec: Vec<i32> = Vec::new(); + + for i in nums1.iter() { + set.insert(*i); + } + + for i in nums2.iter() { + if set.contains(i) { + vec.push(*i); + set.remove(i); Review comment: 这里为什么需要`remove`?如果`nums2`里面有一个数字重复出现了两次,并且在`nums1`里面出现了一次,那么是否会`vec.push`两次? ########## File path: site/blog/2021-08-13-developing-sgx-application-with-teaclave-sgx-sdk.md ########## @@ -0,0 +1,331 @@ +--- +title: 使用 Teaclave SGX SDK 开发 SGX 应用 +date: 2021-08-13 +author: Wenwen Ruan +--- + +[[TOC]] + + +## Teaclave SGX SDK应用开发环境简介以及搭建 + +Intel SGX (软件防护扩展,Software Guard Extension) 提供了在一个安全的 enclave(飞地)中执行代码的能力,enclave 可以提供一个隔离的可信执行环境,在操作系统、BIOS 和虚拟机监控器等系统软件均不可信的情况下,仍然对 enclave 内部的代码和数据提供保护,保障用户的关键数据和代码的机密性和完整性。 + +但如果 Intel SGX 程序仍然使用 C/C++ 这类内存不安全的语言开发的话,就会和传统软件一样面临着传统的内存破坏漏洞。对于 enclave 来说,受到的危害会更为严重,因为 enclave 中保存的多是机密数据和代码。Teaclave SGX 的主要目标就是通过使用高效的内存安全语言 —— Rust 来支持 enclave 应用程序的开发,从而在保证 Intel SGX enclave 内存安全的同时不会带来显著的性能开销。 + +Teaclave SGX SDK 内部结构分为三层: + ++ 最底层是使用 C/C++ 和汇编实现的 Intel SGX SDK。 ++ 中间层是 Rust 对 C/C++ 的 FFI (Foreign function Interfaces, 外部函数接口)。 ++ 最高层是 Teaclave SGX SDK。 + +![Teaclave SGX SDK 概要图](./img/2021-08-13-overview-of-teaclave-sgx-sdk.png) + +Teaclave SGX SDK 应用程序开发者在进行开发时就只需要基于最上层的 Teaclave SGX SDK 来进行开发,底层的实现对于开发者来说是透明的。本文将从开发者的角度介绍基于 Teaclave SGX SDK 开发自己的应用程序的过程。 + +### 准备条件 + ++ Ubuntu16.04 或者 18.04 或者 20.04 (Teaclave SGX SDK v1.1.3 中增加了对 Ubuntu 20.04 的支持) ++ docker 环境 + +*本文基于 Teaclave SGX SDK v1.1.3 提交哈希值:d107bd0718f723221750a4f2973451b386cbf9d2* + + +### 基于 docker 配置 Teaclave SGX SDK 开发环境 + +首先需要用户机器 CPU 支持 Intel SGX 并且在 BIOS 上开启了 Intel SGX 支持。用户可以通过 [SGX-hardware项目](https://github.com/ayeks/SGX-hardware) 或者在 [Intel 官网](https://www.intel.com/content/www/us/en/products/details/processors.html) 中搜索自己的 CPU 型号查看是否支持 Intel SGX。下图以 Intel Core i7-7700K 处理器为例,如下图所示,该机型支持 SGX。 + +![sgx-enable.png](./img/2021-08-13-sgx-enable.png) + +当确定 CPU 支持 Intel SGX 之后,还需要开启 BIOS 中的 SGX 选项。CPU 上的 SGX 选项可能有 `enabled` 或者 `software controlled`。具有 `enabled` 选项的主机直接在 BIOS 上选择 `enabled` 即可,而`software controlled` 表示 SGX 的开启需要由软件触发,还需通过 Intel 官方提供的 [sgx-software-enable](https://github.com/intel/sgx-software-enable) 开启。下载好 `sgx-software-enable` 之后,运行 `Makefile` 编译生成可执行代码 `sgx_enable` ,执行 `sudo ./sgx_enable` 顺利运行后重启主机,即可顺利开启 Intel SGX。 + +硬件条件准备完毕之后,还需要安装 [Linux SGX 驱动](https://download.01.org/intel-sgx/sgx-linux/2.10/distro/ubuntu16.04-server/sgx_linux_x64_driver_2.6.0_602374c.bin) ,安装完毕之后需要确认 `/dev/isgx` 的存在。 + +下载 Teaclave SGX SDK 以及支持编译 SGX 设备的 docker image。 + +`$ https://github.com/apache/incubator-teaclave-sgx-sdk` + +`$ docker pull baiduxlab/sgx-rust` + +启动一个 docker,并且把 Teaclave SGX SDK 项目目录映射到 docker 中。 + +`$ docker run -v /your/absolute/path/to/incubator-teaclave-sgx-sdk:/root/sgx -ti --device /dev/isgx baiduxlab/sgx-rust` + +在运行的 docker container 中启动 aesm 服务,**White list update request successful for Version** 语句意味着启动成功。 + +````bash +root@docker:/# LD_LIBRARY_PATH=/opt/intel/sgx-aesm-service/aesm/ /opt/intel/sgx-aesm-service/aesm/aesm_service & +aesm_service[17]: [ADMIN]White List update requested +aesm_service[17]: Failed to load QE3: 0x4004 +aesm_service[17]: The server sock is 0x56096ab991c0 +aesm_service[17]: [ADMIN]White list update request successful for Version: 103 +```` + +执行 Teaclave SGX SDK 中的简单实例 helloworld ,检查是否正常运行。 + +```bash +root@docker:~# cd sgx/samplecode/helloworld/ +root@docker:~/sgx/samplecode/helloworld# make +root@docker:~/sgx/samplecode/helloworld# cd bin/ +root@docker:~/sgx/samplecode/helloworld/bin# ./app +[+] global_eid: 2 +This is normal world string passed into enclave! +This is a Rust string! +[+] say_something success ... +``` +至此,我们已经成功在自己的机器上跑起来了 Teaclave SGX SDK 的 helloworld 示例啦! + +## Teaclave SGX SDK 示例 helloworld 剖析 + +接下来,我们通过阅读 helloworld 这个简单的例子来理解 Teaclave SGX SDK 应用程序的组织结构和运行方式。 + +### helloworld 目录结构 + +```tree +helloworld/ +├── app +│ ├── app.c +│ └── app.h +├── bin +│ └── readme.txt +├── enclave +│ ├── Cargo.toml +│ ├── Enclave.config.xml +│ ├── Enclave.edl +│ ├── Enclave.lds +│ ├── Enclave_private.pem +│ ├── Makefile +│ ├── src +│ │ └── lib.rs +│ ├── x86_64-unknown-linux-sgx.json +│ └── Xargo.toml +├── lib +│ └── readme.txt +└── Makefile +``` + +helloworld 的目录结构和 Intel SGX 的 [SampleEnclave](https://github.com/intel/linux-sgx/blob/HEAD/SampleCode/SampleEnclave) 目录结构非常类似。 ++ app 目录中存放的是不可信部分代码,包括 `main` 函数以及 `OCALL` 函数具体逻辑实现。 ++ enclave 目录中存放的是可信部分代码,主要是 `ECALL` 函数具体逻辑实现。 + + 不同于 SGX ,应用安全区的代码实现位于 **`src/lib.rs`**, 该文件是整个 `helloworld` 文件夹中唯一使用 Rust 编写的文件,程序员可以在该文件中增加需要的功能。 + + 另外,enclave 文件夹下多了 `Cargo.toml`, `src/lib.rs`, `x86_64-unknown-linux-sgx.json`, `Xargo.toml`: + + **`Cargo.toml`**: 项目清单文件,包括项目名称、项目版本以及依赖项等。 + + **`x86_64-unknown-linux-sgx.json`** 和 **`Xargo.toml`** 描述了用于项目交叉编译的信息。 + +### 重要代码文件解析 + ++ **`Enclave.edl`** \ +该文件规定了 Enclave 边界 `ECALL/OCALL` 的定义。 + +```edl +enclave { + from "sgx_tstd.edl" import *; + from "sgx_stdio.edl" import *; + from "sgx_backtrace.edl" import *; + from "sgx_tstdc.edl" import *; + + trusted { + /* define ECALLs here. */ + + public sgx_status_t say_something([in, size=len] const uint8_t* some_string, size_t len); + }; + + untrusted { + + }; +}; +``` + +`trusted {...}` 中声明 `ECALL` 函数, `untrusted {...}` 中声明 `OCALL` 函数。本例中声明了一个 `ECALL` 函数 `say_something`,该函数的具体实现在 `src/lib.rs` 中,它的参数包括 `uint8_t *` 类型的指针和长度参数 `len`。 + ++ **`app/app.c`** + +在 `app/app.c` 的 `main` 函数中有一个完整的调用 `ECALL` 的例子。 + +```c +sgx_ret = say_something(global_eid, + &enclave_ret, + (const uint8_t *) str, + len); +``` + +这里的 `say_something` 似乎和 `Enclave.edl` 中的声明不太一样,ECALL传递参数时多了两个隐参数:`enclave_eid` 和 `say_something` 的返回值 `&enclave_ret`。而 `sgx_ret` 表示的是 ECALL 执行是否成功,是 SGX 的返回值。 + ++ **`enclave/`文件夹部分** + +`enclave/Cargo.toml` 中声明了这是一个 `staticlib`,表明 Enclave 在最后会被编译成一个 `.a` 文件,该文件会和 Intel 提供的 `sgx_tstdc.a` 等文件链接形成 `enclave.so`,再经由 `sgx_sign` 工具配合 `Enclave.config.xml` 配置文件、`Enclave_private.pem` 签名私钥做签名并计算 `measurement` ,最后生成 `enclave.signed.so`,这是 Enclave 的完全体。 + ++ **`enclave/src/lib.rs`** + +```rust +pub extern "C" fn say_something(some_string: *const u8, some_len: usize) -> sgx_status_t { + + let str_slice = unsafe { slice::from_raw_parts(some_string, some_len) }; + let _ = io::stdout().write(str_slice); + + // A sample &'static string + let rust_raw_string = "This is a "; + // An array + let word:[u8;4] = [82, 117, 115, 116]; + // An vector + let word_vec:Vec<u8> = vec![32, 115, 116, 114, 105, 110, 103, 33]; + + // Construct a string from &'static string + let mut hello_string = String::from(rust_raw_string); + + // Iterate on word array + for c in word.iter() { + hello_string.push(*c as char); + } + + // Rust style convertion + hello_string += String::from_utf8(word_vec).expect("Invalid UTF-8") + .as_str(); + + // Ocall to normal world for output + println!("{}", &hello_string); + + sgx_status_t::SGX_SUCCESS +} +``` +该函数实现了一个简单的将 `u8` 数组转化为字符串输出的函数,注意在函数的最后调用的 `println!` 函数是一个 `OCALL`。 `println!` 的具体实现中加入了内置的 `OCALL`,并定义了内置的 `EDL` ,import到了 `Enclave.edl` 中。 +```edl +enclave { + from "sgx_tstd.edl" import *; + from "sgx_stdio.edl" import *; + from "sgx_backtrace.edl" import *; + from "sgx_tstdc.edl" import *; +``` + + +### 编译后的代码目录 +经过编译之后的代码目录如下所示,这里省略了 `release` 文件夹下的内容。 +```tree +├── app +│ ├── app.c +│ ├── app.h +│ ├── app.o #[generate] +│ ├── Enclave_u.c #[generate] +│ ├── Enclave_u.h #[generate] +│ └── Enclave_u.o #[generate] +├── bin +│ ├── app #[generate] +│ ├── enclave.signed.so #[generate] +│ └── readme.txt +├── enclave +│ ├── Cargo.lock #[generate] +│ ├── Cargo.toml +│ ├── Enclave.config.xml +│ ├── Enclave.edl +│ ├── Enclave.lds +│ ├── Enclave_private.pem +│ ├── enclave.so #[generate] +│ ├── Enclave_t.c #[generate] +│ ├── Enclave_t.h #[generate] +│ ├── Enclave_t.o #[generate] +│ ├── Makefile +│ ├── src +│ │ └── lib.rs +│ ├── target #[generate] +│ │ ├── CACHEDIR.TAG #[generate] +│ │ └── release #[generate] +│ ├── x86_64-unknown-linux-sgx.json +│ └── Xargo.toml +├── lib +│ ├── libenclave.a #[generate] +│ ├── libsgx_ustdc.a #[generate] +│ └── readme.txt +└── Makefile +``` +helloworld 编译的基本流程类似于 Intel SGX: ++ `EDL` 编译器 `edger8r` 将输入的 `EDL` 在 `app/` 目录下生成不可信代码 `Enclave_u.h` 和 `Enclave_u.c`; Review comment: `edger8r`可能不能算是编译器。官方的定义: > Generates interfaces between the untrusted components and enclaves. 它从edl生成了enclave 和untrusted world之间的接口函数 ~~`EDL` 编译器~~ ########## File path: site/blog/2021-08-13-developing-sgx-application-with-teaclave-sgx-sdk.md ########## @@ -0,0 +1,331 @@ +--- +title: 使用 Teaclave SGX SDK 开发 SGX 应用 +date: 2021-08-13 +author: Wenwen Ruan +--- + +[[TOC]] + + +## Teaclave SGX SDK应用开发环境简介以及搭建 + +Intel SGX (软件防护扩展,Software Guard Extension) 提供了在一个安全的 enclave(飞地)中执行代码的能力,enclave 可以提供一个隔离的可信执行环境,在操作系统、BIOS 和虚拟机监控器等系统软件均不可信的情况下,仍然对 enclave 内部的代码和数据提供保护,保障用户的关键数据和代码的机密性和完整性。 + +但如果 Intel SGX 程序仍然使用 C/C++ 这类内存不安全的语言开发的话,就会和传统软件一样面临着传统的内存破坏漏洞。对于 enclave 来说,受到的危害会更为严重,因为 enclave 中保存的多是机密数据和代码。Teaclave SGX 的主要目标就是通过使用高效的内存安全语言 —— Rust 来支持 enclave 应用程序的开发,从而在保证 Intel SGX enclave 内存安全的同时不会带来显著的性能开销。 + +Teaclave SGX SDK 内部结构分为三层: + ++ 最底层是使用 C/C++ 和汇编实现的 Intel SGX SDK。 ++ 中间层是 Rust 对 C/C++ 的 FFI (Foreign function Interfaces, 外部函数接口)。 ++ 最高层是 Teaclave SGX SDK。 + +![Teaclave SGX SDK 概要图](./img/2021-08-13-overview-of-teaclave-sgx-sdk.png) + +Teaclave SGX SDK 应用程序开发者在进行开发时就只需要基于最上层的 Teaclave SGX SDK 来进行开发,底层的实现对于开发者来说是透明的。本文将从开发者的角度介绍基于 Teaclave SGX SDK 开发自己的应用程序的过程。 + +### 准备条件 + ++ Ubuntu16.04 或者 18.04 或者 20.04 (Teaclave SGX SDK v1.1.3 中增加了对 Ubuntu 20.04 的支持) ++ docker 环境 + +*本文基于 Teaclave SGX SDK v1.1.3 提交哈希值:d107bd0718f723221750a4f2973451b386cbf9d2* + + +### 基于 docker 配置 Teaclave SGX SDK 开发环境 + +首先需要用户机器 CPU 支持 Intel SGX 并且在 BIOS 上开启了 Intel SGX 支持。用户可以通过 [SGX-hardware项目](https://github.com/ayeks/SGX-hardware) 或者在 [Intel 官网](https://www.intel.com/content/www/us/en/products/details/processors.html) 中搜索自己的 CPU 型号查看是否支持 Intel SGX。下图以 Intel Core i7-7700K 处理器为例,如下图所示,该机型支持 SGX。 + +![sgx-enable.png](./img/2021-08-13-sgx-enable.png) + +当确定 CPU 支持 Intel SGX 之后,还需要开启 BIOS 中的 SGX 选项。CPU 上的 SGX 选项可能有 `enabled` 或者 `software controlled`。具有 `enabled` 选项的主机直接在 BIOS 上选择 `enabled` 即可,而`software controlled` 表示 SGX 的开启需要由软件触发,还需通过 Intel 官方提供的 [sgx-software-enable](https://github.com/intel/sgx-software-enable) 开启。下载好 `sgx-software-enable` 之后,运行 `Makefile` 编译生成可执行代码 `sgx_enable` ,执行 `sudo ./sgx_enable` 顺利运行后重启主机,即可顺利开启 Intel SGX。 + +硬件条件准备完毕之后,还需要安装 [Linux SGX 驱动](https://download.01.org/intel-sgx/sgx-linux/2.10/distro/ubuntu16.04-server/sgx_linux_x64_driver_2.6.0_602374c.bin) ,安装完毕之后需要确认 `/dev/isgx` 的存在。 + +下载 Teaclave SGX SDK 以及支持编译 SGX 设备的 docker image。 + +`$ https://github.com/apache/incubator-teaclave-sgx-sdk` + +`$ docker pull baiduxlab/sgx-rust` + +启动一个 docker,并且把 Teaclave SGX SDK 项目目录映射到 docker 中。 + +`$ docker run -v /your/absolute/path/to/incubator-teaclave-sgx-sdk:/root/sgx -ti --device /dev/isgx baiduxlab/sgx-rust` + +在运行的 docker container 中启动 aesm 服务,**White list update request successful for Version** 语句意味着启动成功。 + +````bash +root@docker:/# LD_LIBRARY_PATH=/opt/intel/sgx-aesm-service/aesm/ /opt/intel/sgx-aesm-service/aesm/aesm_service & +aesm_service[17]: [ADMIN]White List update requested +aesm_service[17]: Failed to load QE3: 0x4004 +aesm_service[17]: The server sock is 0x56096ab991c0 +aesm_service[17]: [ADMIN]White list update request successful for Version: 103 +```` + +执行 Teaclave SGX SDK 中的简单实例 helloworld ,检查是否正常运行。 + +```bash +root@docker:~# cd sgx/samplecode/helloworld/ +root@docker:~/sgx/samplecode/helloworld# make +root@docker:~/sgx/samplecode/helloworld# cd bin/ +root@docker:~/sgx/samplecode/helloworld/bin# ./app +[+] global_eid: 2 +This is normal world string passed into enclave! +This is a Rust string! +[+] say_something success ... +``` +至此,我们已经成功在自己的机器上跑起来了 Teaclave SGX SDK 的 helloworld 示例啦! + +## Teaclave SGX SDK 示例 helloworld 剖析 + +接下来,我们通过阅读 helloworld 这个简单的例子来理解 Teaclave SGX SDK 应用程序的组织结构和运行方式。 + +### helloworld 目录结构 + +```tree +helloworld/ +├── app +│ ├── app.c +│ └── app.h +├── bin +│ └── readme.txt +├── enclave +│ ├── Cargo.toml +│ ├── Enclave.config.xml +│ ├── Enclave.edl +│ ├── Enclave.lds +│ ├── Enclave_private.pem +│ ├── Makefile +│ ├── src +│ │ └── lib.rs +│ ├── x86_64-unknown-linux-sgx.json +│ └── Xargo.toml +├── lib +│ └── readme.txt +└── Makefile +``` + +helloworld 的目录结构和 Intel SGX 的 [SampleEnclave](https://github.com/intel/linux-sgx/blob/HEAD/SampleCode/SampleEnclave) 目录结构非常类似。 ++ app 目录中存放的是不可信部分代码,包括 `main` 函数以及 `OCALL` 函数具体逻辑实现。 ++ enclave 目录中存放的是可信部分代码,主要是 `ECALL` 函数具体逻辑实现。 + + 不同于 SGX ,应用安全区的代码实现位于 **`src/lib.rs`**, 该文件是整个 `helloworld` 文件夹中唯一使用 Rust 编写的文件,程序员可以在该文件中增加需要的功能。 + + 另外,enclave 文件夹下多了 `Cargo.toml`, `src/lib.rs`, `x86_64-unknown-linux-sgx.json`, `Xargo.toml`: + + **`Cargo.toml`**: 项目清单文件,包括项目名称、项目版本以及依赖项等。 + + **`x86_64-unknown-linux-sgx.json`** 和 **`Xargo.toml`** 描述了用于项目交叉编译的信息。 + +### 重要代码文件解析 + ++ **`Enclave.edl`** \ +该文件规定了 Enclave 边界 `ECALL/OCALL` 的定义。 + +```edl +enclave { + from "sgx_tstd.edl" import *; + from "sgx_stdio.edl" import *; + from "sgx_backtrace.edl" import *; + from "sgx_tstdc.edl" import *; + + trusted { + /* define ECALLs here. */ + + public sgx_status_t say_something([in, size=len] const uint8_t* some_string, size_t len); + }; + + untrusted { + + }; +}; +``` + +`trusted {...}` 中声明 `ECALL` 函数, `untrusted {...}` 中声明 `OCALL` 函数。本例中声明了一个 `ECALL` 函数 `say_something`,该函数的具体实现在 `src/lib.rs` 中,它的参数包括 `uint8_t *` 类型的指针和长度参数 `len`。 + ++ **`app/app.c`** + +在 `app/app.c` 的 `main` 函数中有一个完整的调用 `ECALL` 的例子。 + +```c +sgx_ret = say_something(global_eid, + &enclave_ret, + (const uint8_t *) str, + len); +``` + +这里的 `say_something` 似乎和 `Enclave.edl` 中的声明不太一样,ECALL传递参数时多了两个隐参数:`enclave_eid` 和 `say_something` 的返回值 `&enclave_ret`。而 `sgx_ret` 表示的是 ECALL 执行是否成功,是 SGX 的返回值。 + ++ **`enclave/`文件夹部分** + +`enclave/Cargo.toml` 中声明了这是一个 `staticlib`,表明 Enclave 在最后会被编译成一个 `.a` 文件,该文件会和 Intel 提供的 `sgx_tstdc.a` 等文件链接形成 `enclave.so`,再经由 `sgx_sign` 工具配合 `Enclave.config.xml` 配置文件、`Enclave_private.pem` 签名私钥做签名并计算 `measurement` ,最后生成 `enclave.signed.so`,这是 Enclave 的完全体。 + ++ **`enclave/src/lib.rs`** + +```rust +pub extern "C" fn say_something(some_string: *const u8, some_len: usize) -> sgx_status_t { + + let str_slice = unsafe { slice::from_raw_parts(some_string, some_len) }; + let _ = io::stdout().write(str_slice); + + // A sample &'static string + let rust_raw_string = "This is a "; + // An array + let word:[u8;4] = [82, 117, 115, 116]; + // An vector + let word_vec:Vec<u8> = vec![32, 115, 116, 114, 105, 110, 103, 33]; + + // Construct a string from &'static string + let mut hello_string = String::from(rust_raw_string); + + // Iterate on word array + for c in word.iter() { + hello_string.push(*c as char); + } + + // Rust style convertion + hello_string += String::from_utf8(word_vec).expect("Invalid UTF-8") + .as_str(); + + // Ocall to normal world for output + println!("{}", &hello_string); + + sgx_status_t::SGX_SUCCESS +} +``` +该函数实现了一个简单的将 `u8` 数组转化为字符串输出的函数,注意在函数的最后调用的 `println!` 函数是一个 `OCALL`。 `println!` 的具体实现中加入了内置的 `OCALL`,并定义了内置的 `EDL` ,import到了 `Enclave.edl` 中。 +```edl +enclave { + from "sgx_tstd.edl" import *; + from "sgx_stdio.edl" import *; + from "sgx_backtrace.edl" import *; + from "sgx_tstdc.edl" import *; +``` + + +### 编译后的代码目录 +经过编译之后的代码目录如下所示,这里省略了 `release` 文件夹下的内容。 +```tree +├── app +│ ├── app.c +│ ├── app.h +│ ├── app.o #[generate] +│ ├── Enclave_u.c #[generate] +│ ├── Enclave_u.h #[generate] +│ └── Enclave_u.o #[generate] +├── bin +│ ├── app #[generate] +│ ├── enclave.signed.so #[generate] +│ └── readme.txt +├── enclave +│ ├── Cargo.lock #[generate] +│ ├── Cargo.toml +│ ├── Enclave.config.xml +│ ├── Enclave.edl +│ ├── Enclave.lds +│ ├── Enclave_private.pem +│ ├── enclave.so #[generate] +│ ├── Enclave_t.c #[generate] +│ ├── Enclave_t.h #[generate] +│ ├── Enclave_t.o #[generate] +│ ├── Makefile +│ ├── src +│ │ └── lib.rs +│ ├── target #[generate] +│ │ ├── CACHEDIR.TAG #[generate] +│ │ └── release #[generate] +│ ├── x86_64-unknown-linux-sgx.json +│ └── Xargo.toml +├── lib +│ ├── libenclave.a #[generate] +│ ├── libsgx_ustdc.a #[generate] +│ └── readme.txt +└── Makefile +``` +helloworld 编译的基本流程类似于 Intel SGX: ++ `EDL` 编译器 `edger8r` 将输入的 `EDL` 在 `app/` 目录下生成不可信代码 `Enclave_u.h` 和 `Enclave_u.c`; ++ 编译不可信部分生成 `bin/app`; ++ `edger8r` 在 `enclave/` 目录下生成可信代码 `Enclave_t.h` 和 `Enclave_t.c`; ++ 编译并签名生成可信动态链接库 `enclave.signed.so`。 + +## 开发者如何开发自己的 Rust SGX Application +同样类似于开发 Intel SGX Application,用户可以通过改写 Teaclave SGX SDK 所提供的 `samplecode`,在这里,我以一个简单的例子抛砖引玉。 + +### 添加自定义的函数 +假设用户希望在 Teaclave SGX SDK 中实现一个简单的求两个数组的交集的函数,只需要直接在 `src/lib.rs` 中添加实现的函数。下面的实例代码 `intersection` 函数是希望添加的求交集函数。传入的两个参数是需要求交集的 `i32` 数组,最后返回的是两个数组的交集。其具体的实现是通过一个额外的散列集,记录 `num1` 出现的元素,再对 `num2` 进行遍历,如果 `num2` 出现了散列集中的元素,则将该值 `push` 到交集数组中,并将散列表中的对应元素移除。当 `num2` 遍历完毕之后,返回交集数组。 + +```rust +pub fn intersection(nums1: Vec<i32>, nums2: Vec<i32>) -> Vec<i32> { + use std::collections::HashSet; + let mut set: HashSet<i32> = HashSet::new(); + let mut vec: Vec<i32> = Vec::new(); + + for i in nums1.iter() { + set.insert(*i); + } + + for i in nums2.iter() { + if set.contains(i) { + vec.push(*i); + set.remove(i); + } + } + return vec; + } +``` + +需要调用该函数时,在 `say_something` 中添加: +```rust + println!("\nTry intersection for our own"); + let nums1: Vec<i32> = vec![1, 2, 3, 4, 5]; + let nums2: Vec<i32> = vec![4, 5, 6, 7, 8]; + let vec: Vec<i32> = intersection(nums1, nums2); + println!("intersection set is {:?}", vec); + +``` + +`say_something` 是 `ECALL` 的入口,或许你也发现了,这里我将 `say_something` 当成了 Enclave 的 `"main"` 函数来使用XD。 + +重新编译并运行,得到运行结果: +```bash +Try intersection for our own +intersection set is [4, 5] +``` +我们基于 Teaclave SGX SDK 的 helloworld 实现了自己的求交集函数。 + +### 调用 Teaclave SGX SDK 提供的 `crate` + +Teaclave SGX SDK 重写了很多 SGX 的库,当我们需要用某个库时,可以先在仓库中查看是否有相应的 `crate` 实现以及对应的 [doc](https://teaclave.apache.org/api-docs/crates-enclave/)。比如在上述的编程环境下,假如我们进一步希望两个数组 `nums1` 和 `nums2` 的内容不是固定的,而是随机生成的。在 `C++` 或者 `Rust` 环境下,会自然而然地想到使用 `rand` 库。自然而然地,Teaclave SGX SDK 也用 Rust 重写了 [`sgx_rand`](https://github.com/apache/incubator-teaclave-sgx-sdk/tree/master/sgx_rand) 库。 + +首先在 `enclave/Cargo.toml` 中的 `[target.'cfg(not(target_env = "sgx"))'.dependencies]` 部分添加 `sgx_rand` 库的地址,这里的地址也可以换成本机上的 `sgx_rand` 文件夹所在位置。 + +```toml +sgx_rand = {git = "https://github.com/apache/teaclave-sgx-sdk.git" } +``` + Review comment: ```toml [target.'cfg(not(target_env = "sgx"))'.dependencies] sgx_rand = {git = "https://github.com/apache/teaclave-sgx-sdk.git" } ``` ########## File path: site/blog/2021-08-13-developing-sgx-application-with-teaclave-sgx-sdk.md ########## @@ -0,0 +1,331 @@ +--- +title: 使用 Teaclave SGX SDK 开发 SGX 应用 +date: 2021-08-13 +author: Wenwen Ruan +--- + +[[TOC]] + + +## Teaclave SGX SDK应用开发环境简介以及搭建 + +Intel SGX (软件防护扩展,Software Guard Extension) 提供了在一个安全的 enclave(飞地)中执行代码的能力,enclave 可以提供一个隔离的可信执行环境,在操作系统、BIOS 和虚拟机监控器等系统软件均不可信的情况下,仍然对 enclave 内部的代码和数据提供保护,保障用户的关键数据和代码的机密性和完整性。 + +但如果 Intel SGX 程序仍然使用 C/C++ 这类内存不安全的语言开发的话,就会和传统软件一样面临着传统的内存破坏漏洞。对于 enclave 来说,受到的危害会更为严重,因为 enclave 中保存的多是机密数据和代码。Teaclave SGX 的主要目标就是通过使用高效的内存安全语言 —— Rust 来支持 enclave 应用程序的开发,从而在保证 Intel SGX enclave 内存安全的同时不会带来显著的性能开销。 + +Teaclave SGX SDK 内部结构分为三层: + ++ 最底层是使用 C/C++ 和汇编实现的 Intel SGX SDK。 ++ 中间层是 Rust 对 C/C++ 的 FFI (Foreign function Interfaces, 外部函数接口)。 ++ 最高层是 Teaclave SGX SDK。 + +![Teaclave SGX SDK 概要图](./img/2021-08-13-overview-of-teaclave-sgx-sdk.png) + +Teaclave SGX SDK 应用程序开发者在进行开发时就只需要基于最上层的 Teaclave SGX SDK 来进行开发,底层的实现对于开发者来说是透明的。本文将从开发者的角度介绍基于 Teaclave SGX SDK 开发自己的应用程序的过程。 + +### 准备条件 + ++ Ubuntu16.04 或者 18.04 或者 20.04 (Teaclave SGX SDK v1.1.3 中增加了对 Ubuntu 20.04 的支持) ++ docker 环境 + +*本文基于 Teaclave SGX SDK v1.1.3 提交哈希值:d107bd0718f723221750a4f2973451b386cbf9d2* + + +### 基于 docker 配置 Teaclave SGX SDK 开发环境 + +首先需要用户机器 CPU 支持 Intel SGX 并且在 BIOS 上开启了 Intel SGX 支持。用户可以通过 [SGX-hardware项目](https://github.com/ayeks/SGX-hardware) 或者在 [Intel 官网](https://www.intel.com/content/www/us/en/products/details/processors.html) 中搜索自己的 CPU 型号查看是否支持 Intel SGX。下图以 Intel Core i7-7700K 处理器为例,如下图所示,该机型支持 SGX。 + +![sgx-enable.png](./img/2021-08-13-sgx-enable.png) + +当确定 CPU 支持 Intel SGX 之后,还需要开启 BIOS 中的 SGX 选项。CPU 上的 SGX 选项可能有 `enabled` 或者 `software controlled`。具有 `enabled` 选项的主机直接在 BIOS 上选择 `enabled` 即可,而`software controlled` 表示 SGX 的开启需要由软件触发,还需通过 Intel 官方提供的 [sgx-software-enable](https://github.com/intel/sgx-software-enable) 开启。下载好 `sgx-software-enable` 之后,运行 `Makefile` 编译生成可执行代码 `sgx_enable` ,执行 `sudo ./sgx_enable` 顺利运行后重启主机,即可顺利开启 Intel SGX。 + +硬件条件准备完毕之后,还需要安装 [Linux SGX 驱动](https://download.01.org/intel-sgx/sgx-linux/2.10/distro/ubuntu16.04-server/sgx_linux_x64_driver_2.6.0_602374c.bin) ,安装完毕之后需要确认 `/dev/isgx` 的存在。 + +下载 Teaclave SGX SDK 以及支持编译 SGX 设备的 docker image。 + +`$ https://github.com/apache/incubator-teaclave-sgx-sdk` + +`$ docker pull baiduxlab/sgx-rust` + +启动一个 docker,并且把 Teaclave SGX SDK 项目目录映射到 docker 中。 + +`$ docker run -v /your/absolute/path/to/incubator-teaclave-sgx-sdk:/root/sgx -ti --device /dev/isgx baiduxlab/sgx-rust` + +在运行的 docker container 中启动 aesm 服务,**White list update request successful for Version** 语句意味着启动成功。 + +````bash +root@docker:/# LD_LIBRARY_PATH=/opt/intel/sgx-aesm-service/aesm/ /opt/intel/sgx-aesm-service/aesm/aesm_service & +aesm_service[17]: [ADMIN]White List update requested +aesm_service[17]: Failed to load QE3: 0x4004 +aesm_service[17]: The server sock is 0x56096ab991c0 +aesm_service[17]: [ADMIN]White list update request successful for Version: 103 +```` + +执行 Teaclave SGX SDK 中的简单实例 helloworld ,检查是否正常运行。 + +```bash +root@docker:~# cd sgx/samplecode/helloworld/ +root@docker:~/sgx/samplecode/helloworld# make +root@docker:~/sgx/samplecode/helloworld# cd bin/ +root@docker:~/sgx/samplecode/helloworld/bin# ./app +[+] global_eid: 2 +This is normal world string passed into enclave! +This is a Rust string! +[+] say_something success ... +``` +至此,我们已经成功在自己的机器上跑起来了 Teaclave SGX SDK 的 helloworld 示例啦! + +## Teaclave SGX SDK 示例 helloworld 剖析 + +接下来,我们通过阅读 helloworld 这个简单的例子来理解 Teaclave SGX SDK 应用程序的组织结构和运行方式。 + +### helloworld 目录结构 + +```tree +helloworld/ +├── app +│ ├── app.c +│ └── app.h +├── bin +│ └── readme.txt +├── enclave +│ ├── Cargo.toml +│ ├── Enclave.config.xml +│ ├── Enclave.edl +│ ├── Enclave.lds +│ ├── Enclave_private.pem +│ ├── Makefile +│ ├── src +│ │ └── lib.rs +│ ├── x86_64-unknown-linux-sgx.json +│ └── Xargo.toml +├── lib +│ └── readme.txt +└── Makefile +``` + +helloworld 的目录结构和 Intel SGX 的 [SampleEnclave](https://github.com/intel/linux-sgx/blob/HEAD/SampleCode/SampleEnclave) 目录结构非常类似。 ++ app 目录中存放的是不可信部分代码,包括 `main` 函数以及 `OCALL` 函数具体逻辑实现。 ++ enclave 目录中存放的是可信部分代码,主要是 `ECALL` 函数具体逻辑实现。 + + 不同于 SGX ,应用安全区的代码实现位于 **`src/lib.rs`**, 该文件是整个 `helloworld` 文件夹中唯一使用 Rust 编写的文件,程序员可以在该文件中增加需要的功能。 + + 另外,enclave 文件夹下多了 `Cargo.toml`, `src/lib.rs`, `x86_64-unknown-linux-sgx.json`, `Xargo.toml`: + + **`Cargo.toml`**: 项目清单文件,包括项目名称、项目版本以及依赖项等。 + + **`x86_64-unknown-linux-sgx.json`** 和 **`Xargo.toml`** 描述了用于项目交叉编译的信息。 + +### 重要代码文件解析 + ++ **`Enclave.edl`** \ +该文件规定了 Enclave 边界 `ECALL/OCALL` 的定义。 + +```edl +enclave { + from "sgx_tstd.edl" import *; + from "sgx_stdio.edl" import *; + from "sgx_backtrace.edl" import *; + from "sgx_tstdc.edl" import *; + + trusted { + /* define ECALLs here. */ + + public sgx_status_t say_something([in, size=len] const uint8_t* some_string, size_t len); + }; + + untrusted { + + }; +}; +``` + +`trusted {...}` 中声明 `ECALL` 函数, `untrusted {...}` 中声明 `OCALL` 函数。本例中声明了一个 `ECALL` 函数 `say_something`,该函数的具体实现在 `src/lib.rs` 中,它的参数包括 `uint8_t *` 类型的指针和长度参数 `len`。 + ++ **`app/app.c`** + +在 `app/app.c` 的 `main` 函数中有一个完整的调用 `ECALL` 的例子。 + +```c +sgx_ret = say_something(global_eid, + &enclave_ret, + (const uint8_t *) str, + len); +``` + +这里的 `say_something` 似乎和 `Enclave.edl` 中的声明不太一样,ECALL传递参数时多了两个隐参数:`enclave_eid` 和 `say_something` 的返回值 `&enclave_ret`。而 `sgx_ret` 表示的是 ECALL 执行是否成功,是 SGX 的返回值。 + ++ **`enclave/`文件夹部分** + +`enclave/Cargo.toml` 中声明了这是一个 `staticlib`,表明 Enclave 在最后会被编译成一个 `.a` 文件,该文件会和 Intel 提供的 `sgx_tstdc.a` 等文件链接形成 `enclave.so`,再经由 `sgx_sign` 工具配合 `Enclave.config.xml` 配置文件、`Enclave_private.pem` 签名私钥做签名并计算 `measurement` ,最后生成 `enclave.signed.so`,这是 Enclave 的完全体。 + ++ **`enclave/src/lib.rs`** + +```rust +pub extern "C" fn say_something(some_string: *const u8, some_len: usize) -> sgx_status_t { + + let str_slice = unsafe { slice::from_raw_parts(some_string, some_len) }; + let _ = io::stdout().write(str_slice); + + // A sample &'static string + let rust_raw_string = "This is a "; + // An array + let word:[u8;4] = [82, 117, 115, 116]; + // An vector + let word_vec:Vec<u8> = vec![32, 115, 116, 114, 105, 110, 103, 33]; + + // Construct a string from &'static string + let mut hello_string = String::from(rust_raw_string); + + // Iterate on word array + for c in word.iter() { + hello_string.push(*c as char); + } + + // Rust style convertion + hello_string += String::from_utf8(word_vec).expect("Invalid UTF-8") + .as_str(); + + // Ocall to normal world for output + println!("{}", &hello_string); + + sgx_status_t::SGX_SUCCESS +} +``` +该函数实现了一个简单的将 `u8` 数组转化为字符串输出的函数,注意在函数的最后调用的 `println!` 函数是一个 `OCALL`。 `println!` 的具体实现中加入了内置的 `OCALL`,并定义了内置的 `EDL` ,import到了 `Enclave.edl` 中。 +```edl +enclave { + from "sgx_tstd.edl" import *; + from "sgx_stdio.edl" import *; + from "sgx_backtrace.edl" import *; + from "sgx_tstdc.edl" import *; +``` + + +### 编译后的代码目录 +经过编译之后的代码目录如下所示,这里省略了 `release` 文件夹下的内容。 +```tree +├── app +│ ├── app.c +│ ├── app.h +│ ├── app.o #[generate] +│ ├── Enclave_u.c #[generate] +│ ├── Enclave_u.h #[generate] +│ └── Enclave_u.o #[generate] +├── bin +│ ├── app #[generate] +│ ├── enclave.signed.so #[generate] +│ └── readme.txt +├── enclave +│ ├── Cargo.lock #[generate] +│ ├── Cargo.toml +│ ├── Enclave.config.xml +│ ├── Enclave.edl +│ ├── Enclave.lds +│ ├── Enclave_private.pem +│ ├── enclave.so #[generate] +│ ├── Enclave_t.c #[generate] +│ ├── Enclave_t.h #[generate] +│ ├── Enclave_t.o #[generate] +│ ├── Makefile +│ ├── src +│ │ └── lib.rs +│ ├── target #[generate] +│ │ ├── CACHEDIR.TAG #[generate] +│ │ └── release #[generate] +│ ├── x86_64-unknown-linux-sgx.json +│ └── Xargo.toml +├── lib +│ ├── libenclave.a #[generate] +│ ├── libsgx_ustdc.a #[generate] +│ └── readme.txt +└── Makefile +``` +helloworld 编译的基本流程类似于 Intel SGX: ++ `EDL` 编译器 `edger8r` 将输入的 `EDL` 在 `app/` 目录下生成不可信代码 `Enclave_u.h` 和 `Enclave_u.c`; ++ 编译不可信部分生成 `bin/app`; ++ `edger8r` 在 `enclave/` 目录下生成可信代码 `Enclave_t.h` 和 `Enclave_t.c`; ++ 编译并签名生成可信动态链接库 `enclave.signed.so`。 + +## 开发者如何开发自己的 Rust SGX Application +同样类似于开发 Intel SGX Application,用户可以通过改写 Teaclave SGX SDK 所提供的 `samplecode`,在这里,我以一个简单的例子抛砖引玉。 + +### 添加自定义的函数 +假设用户希望在 Teaclave SGX SDK 中实现一个简单的求两个数组的交集的函数,只需要直接在 `src/lib.rs` 中添加实现的函数。下面的实例代码 `intersection` 函数是希望添加的求交集函数。传入的两个参数是需要求交集的 `i32` 数组,最后返回的是两个数组的交集。其具体的实现是通过一个额外的散列集,记录 `num1` 出现的元素,再对 `num2` 进行遍历,如果 `num2` 出现了散列集中的元素,则将该值 `push` 到交集数组中,并将散列表中的对应元素移除。当 `num2` 遍历完毕之后,返回交集数组。 + +```rust +pub fn intersection(nums1: Vec<i32>, nums2: Vec<i32>) -> Vec<i32> { + use std::collections::HashSet; + let mut set: HashSet<i32> = HashSet::new(); + let mut vec: Vec<i32> = Vec::new(); + + for i in nums1.iter() { + set.insert(*i); + } + + for i in nums2.iter() { + if set.contains(i) { + vec.push(*i); + set.remove(i); + } + } + return vec; + } +``` + +需要调用该函数时,在 `say_something` 中添加: +```rust + println!("\nTry intersection for our own"); + let nums1: Vec<i32> = vec![1, 2, 3, 4, 5]; + let nums2: Vec<i32> = vec![4, 5, 6, 7, 8]; + let vec: Vec<i32> = intersection(nums1, nums2); + println!("intersection set is {:?}", vec); + +``` + +`say_something` 是 `ECALL` 的入口,或许你也发现了,这里我将 `say_something` 当成了 Enclave 的 `"main"` 函数来使用XD。 Review comment: 看到了后面,消除了这个疑问。~~`say_something` 函数的定义是否发生了变化?如果是,它如何被调用的?~~ 什么叫做 *当成了 Enclave 的 `"main"` 函数来使用*?按理说不能assume enclave里面函数被调用的顺序。 如果方便的话也可以把修改过的示例变成SDK项目的示例 ########## File path: site/blog/2021-08-13-developing-sgx-application-with-teaclave-sgx-sdk.md ########## @@ -0,0 +1,331 @@ +--- +title: 使用 Teaclave SGX SDK 开发 SGX 应用 +date: 2021-08-13 +author: Wenwen Ruan +--- + +[[TOC]] + + +## Teaclave SGX SDK应用开发环境简介以及搭建 + +Intel SGX (软件防护扩展,Software Guard Extension) 提供了在一个安全的 enclave(飞地)中执行代码的能力,enclave 可以提供一个隔离的可信执行环境,在操作系统、BIOS 和虚拟机监控器等系统软件均不可信的情况下,仍然对 enclave 内部的代码和数据提供保护,保障用户的关键数据和代码的机密性和完整性。 + +但如果 Intel SGX 程序仍然使用 C/C++ 这类内存不安全的语言开发的话,就会和传统软件一样面临着传统的内存破坏漏洞。对于 enclave 来说,受到的危害会更为严重,因为 enclave 中保存的多是机密数据和代码。Teaclave SGX 的主要目标就是通过使用高效的内存安全语言 —— Rust 来支持 enclave 应用程序的开发,从而在保证 Intel SGX enclave 内存安全的同时不会带来显著的性能开销。 + +Teaclave SGX SDK 内部结构分为三层: + ++ 最底层是使用 C/C++ 和汇编实现的 Intel SGX SDK。 ++ 中间层是 Rust 对 C/C++ 的 FFI (Foreign function Interfaces, 外部函数接口)。 ++ 最高层是 Teaclave SGX SDK。 + +![Teaclave SGX SDK 概要图](./img/2021-08-13-overview-of-teaclave-sgx-sdk.png) + +Teaclave SGX SDK 应用程序开发者在进行开发时就只需要基于最上层的 Teaclave SGX SDK 来进行开发,底层的实现对于开发者来说是透明的。本文将从开发者的角度介绍基于 Teaclave SGX SDK 开发自己的应用程序的过程。 + +### 准备条件 + ++ Ubuntu16.04 或者 18.04 或者 20.04 (Teaclave SGX SDK v1.1.3 中增加了对 Ubuntu 20.04 的支持) ++ docker 环境 + +*本文基于 Teaclave SGX SDK v1.1.3 提交哈希值:d107bd0718f723221750a4f2973451b386cbf9d2* + + +### 基于 docker 配置 Teaclave SGX SDK 开发环境 + +首先需要用户机器 CPU 支持 Intel SGX 并且在 BIOS 上开启了 Intel SGX 支持。用户可以通过 [SGX-hardware项目](https://github.com/ayeks/SGX-hardware) 或者在 [Intel 官网](https://www.intel.com/content/www/us/en/products/details/processors.html) 中搜索自己的 CPU 型号查看是否支持 Intel SGX。下图以 Intel Core i7-7700K 处理器为例,如下图所示,该机型支持 SGX。 + +![sgx-enable.png](./img/2021-08-13-sgx-enable.png) + +当确定 CPU 支持 Intel SGX 之后,还需要开启 BIOS 中的 SGX 选项。CPU 上的 SGX 选项可能有 `enabled` 或者 `software controlled`。具有 `enabled` 选项的主机直接在 BIOS 上选择 `enabled` 即可,而`software controlled` 表示 SGX 的开启需要由软件触发,还需通过 Intel 官方提供的 [sgx-software-enable](https://github.com/intel/sgx-software-enable) 开启。下载好 `sgx-software-enable` 之后,运行 `Makefile` 编译生成可执行代码 `sgx_enable` ,执行 `sudo ./sgx_enable` 顺利运行后重启主机,即可顺利开启 Intel SGX。 + +硬件条件准备完毕之后,还需要安装 [Linux SGX 驱动](https://download.01.org/intel-sgx/sgx-linux/2.10/distro/ubuntu16.04-server/sgx_linux_x64_driver_2.6.0_602374c.bin) ,安装完毕之后需要确认 `/dev/isgx` 的存在。 + +下载 Teaclave SGX SDK 以及支持编译 SGX 设备的 docker image。 + +`$ https://github.com/apache/incubator-teaclave-sgx-sdk` + +`$ docker pull baiduxlab/sgx-rust` + +启动一个 docker,并且把 Teaclave SGX SDK 项目目录映射到 docker 中。 + +`$ docker run -v /your/absolute/path/to/incubator-teaclave-sgx-sdk:/root/sgx -ti --device /dev/isgx baiduxlab/sgx-rust` + +在运行的 docker container 中启动 aesm 服务,**White list update request successful for Version** 语句意味着启动成功。 + +````bash +root@docker:/# LD_LIBRARY_PATH=/opt/intel/sgx-aesm-service/aesm/ /opt/intel/sgx-aesm-service/aesm/aesm_service & +aesm_service[17]: [ADMIN]White List update requested +aesm_service[17]: Failed to load QE3: 0x4004 +aesm_service[17]: The server sock is 0x56096ab991c0 +aesm_service[17]: [ADMIN]White list update request successful for Version: 103 +```` + +执行 Teaclave SGX SDK 中的简单实例 helloworld ,检查是否正常运行。 + +```bash +root@docker:~# cd sgx/samplecode/helloworld/ +root@docker:~/sgx/samplecode/helloworld# make +root@docker:~/sgx/samplecode/helloworld# cd bin/ +root@docker:~/sgx/samplecode/helloworld/bin# ./app +[+] global_eid: 2 +This is normal world string passed into enclave! +This is a Rust string! +[+] say_something success ... +``` +至此,我们已经成功在自己的机器上跑起来了 Teaclave SGX SDK 的 helloworld 示例啦! + +## Teaclave SGX SDK 示例 helloworld 剖析 + +接下来,我们通过阅读 helloworld 这个简单的例子来理解 Teaclave SGX SDK 应用程序的组织结构和运行方式。 + +### helloworld 目录结构 + +```tree +helloworld/ +├── app +│ ├── app.c +│ └── app.h +├── bin +│ └── readme.txt +├── enclave +│ ├── Cargo.toml +│ ├── Enclave.config.xml +│ ├── Enclave.edl +│ ├── Enclave.lds +│ ├── Enclave_private.pem +│ ├── Makefile +│ ├── src +│ │ └── lib.rs +│ ├── x86_64-unknown-linux-sgx.json +│ └── Xargo.toml +├── lib +│ └── readme.txt +└── Makefile +``` + +helloworld 的目录结构和 Intel SGX 的 [SampleEnclave](https://github.com/intel/linux-sgx/blob/HEAD/SampleCode/SampleEnclave) 目录结构非常类似。 ++ app 目录中存放的是不可信部分代码,包括 `main` 函数以及 `OCALL` 函数具体逻辑实现。 ++ enclave 目录中存放的是可信部分代码,主要是 `ECALL` 函数具体逻辑实现。 + + 不同于 SGX ,应用安全区的代码实现位于 **`src/lib.rs`**, 该文件是整个 `helloworld` 文件夹中唯一使用 Rust 编写的文件,程序员可以在该文件中增加需要的功能。 + + 另外,enclave 文件夹下多了 `Cargo.toml`, `src/lib.rs`, `x86_64-unknown-linux-sgx.json`, `Xargo.toml`: + + **`Cargo.toml`**: 项目清单文件,包括项目名称、项目版本以及依赖项等。 + + **`x86_64-unknown-linux-sgx.json`** 和 **`Xargo.toml`** 描述了用于项目交叉编译的信息。 + +### 重要代码文件解析 + ++ **`Enclave.edl`** \ +该文件规定了 Enclave 边界 `ECALL/OCALL` 的定义。 + +```edl +enclave { + from "sgx_tstd.edl" import *; + from "sgx_stdio.edl" import *; + from "sgx_backtrace.edl" import *; + from "sgx_tstdc.edl" import *; + + trusted { + /* define ECALLs here. */ + + public sgx_status_t say_something([in, size=len] const uint8_t* some_string, size_t len); + }; + + untrusted { + + }; +}; +``` + +`trusted {...}` 中声明 `ECALL` 函数, `untrusted {...}` 中声明 `OCALL` 函数。本例中声明了一个 `ECALL` 函数 `say_something`,该函数的具体实现在 `src/lib.rs` 中,它的参数包括 `uint8_t *` 类型的指针和长度参数 `len`。 + ++ **`app/app.c`** + +在 `app/app.c` 的 `main` 函数中有一个完整的调用 `ECALL` 的例子。 + +```c +sgx_ret = say_something(global_eid, + &enclave_ret, + (const uint8_t *) str, + len); +``` + +这里的 `say_something` 似乎和 `Enclave.edl` 中的声明不太一样,ECALL传递参数时多了两个隐参数:`enclave_eid` 和 `say_something` 的返回值 `&enclave_ret`。而 `sgx_ret` 表示的是 ECALL 执行是否成功,是 SGX 的返回值。 + ++ **`enclave/`文件夹部分** + +`enclave/Cargo.toml` 中声明了这是一个 `staticlib`,表明 Enclave 在最后会被编译成一个 `.a` 文件,该文件会和 Intel 提供的 `sgx_tstdc.a` 等文件链接形成 `enclave.so`,再经由 `sgx_sign` 工具配合 `Enclave.config.xml` 配置文件、`Enclave_private.pem` 签名私钥做签名并计算 `measurement` ,最后生成 `enclave.signed.so`,这是 Enclave 的完全体。 + ++ **`enclave/src/lib.rs`** + +```rust +pub extern "C" fn say_something(some_string: *const u8, some_len: usize) -> sgx_status_t { + + let str_slice = unsafe { slice::from_raw_parts(some_string, some_len) }; + let _ = io::stdout().write(str_slice); + + // A sample &'static string + let rust_raw_string = "This is a "; + // An array + let word:[u8;4] = [82, 117, 115, 116]; + // An vector + let word_vec:Vec<u8> = vec![32, 115, 116, 114, 105, 110, 103, 33]; + + // Construct a string from &'static string + let mut hello_string = String::from(rust_raw_string); + + // Iterate on word array + for c in word.iter() { + hello_string.push(*c as char); + } + + // Rust style convertion + hello_string += String::from_utf8(word_vec).expect("Invalid UTF-8") + .as_str(); + + // Ocall to normal world for output + println!("{}", &hello_string); + + sgx_status_t::SGX_SUCCESS +} +``` +该函数实现了一个简单的将 `u8` 数组转化为字符串输出的函数,注意在函数的最后调用的 `println!` 函数是一个 `OCALL`。 `println!` 的具体实现中加入了内置的 `OCALL`,并定义了内置的 `EDL` ,import到了 `Enclave.edl` 中。 +```edl +enclave { + from "sgx_tstd.edl" import *; + from "sgx_stdio.edl" import *; + from "sgx_backtrace.edl" import *; + from "sgx_tstdc.edl" import *; +``` + + +### 编译后的代码目录 +经过编译之后的代码目录如下所示,这里省略了 `release` 文件夹下的内容。 +```tree +├── app +│ ├── app.c +│ ├── app.h +│ ├── app.o #[generate] +│ ├── Enclave_u.c #[generate] +│ ├── Enclave_u.h #[generate] +│ └── Enclave_u.o #[generate] +├── bin +│ ├── app #[generate] +│ ├── enclave.signed.so #[generate] +│ └── readme.txt +├── enclave +│ ├── Cargo.lock #[generate] +│ ├── Cargo.toml +│ ├── Enclave.config.xml +│ ├── Enclave.edl +│ ├── Enclave.lds +│ ├── Enclave_private.pem +│ ├── enclave.so #[generate] +│ ├── Enclave_t.c #[generate] +│ ├── Enclave_t.h #[generate] +│ ├── Enclave_t.o #[generate] +│ ├── Makefile +│ ├── src +│ │ └── lib.rs +│ ├── target #[generate] +│ │ ├── CACHEDIR.TAG #[generate] +│ │ └── release #[generate] +│ ├── x86_64-unknown-linux-sgx.json +│ └── Xargo.toml +├── lib +│ ├── libenclave.a #[generate] +│ ├── libsgx_ustdc.a #[generate] +│ └── readme.txt +└── Makefile +``` +helloworld 编译的基本流程类似于 Intel SGX: ++ `EDL` 编译器 `edger8r` 将输入的 `EDL` 在 `app/` 目录下生成不可信代码 `Enclave_u.h` 和 `Enclave_u.c`; ++ 编译不可信部分生成 `bin/app`; ++ `edger8r` 在 `enclave/` 目录下生成可信代码 `Enclave_t.h` 和 `Enclave_t.c`; ++ 编译并签名生成可信动态链接库 `enclave.signed.so`。 + +## 开发者如何开发自己的 Rust SGX Application +同样类似于开发 Intel SGX Application,用户可以通过改写 Teaclave SGX SDK 所提供的 `samplecode`,在这里,我以一个简单的例子抛砖引玉。 + +### 添加自定义的函数 +假设用户希望在 Teaclave SGX SDK 中实现一个简单的求两个数组的交集的函数,只需要直接在 `src/lib.rs` 中添加实现的函数。下面的实例代码 `intersection` 函数是希望添加的求交集函数。传入的两个参数是需要求交集的 `i32` 数组,最后返回的是两个数组的交集。其具体的实现是通过一个额外的散列集,记录 `num1` 出现的元素,再对 `num2` 进行遍历,如果 `num2` 出现了散列集中的元素,则将该值 `push` 到交集数组中,并将散列表中的对应元素移除。当 `num2` 遍历完毕之后,返回交集数组。 Review comment: `i32`向量 ########## File path: site/blog/2021-08-13-developing-sgx-application-with-teaclave-sgx-sdk.md ########## @@ -0,0 +1,331 @@ +--- +title: 使用 Teaclave SGX SDK 开发 SGX 应用 +date: 2021-08-13 +author: Wenwen Ruan +--- + +[[TOC]] + + +## Teaclave SGX SDK应用开发环境简介以及搭建 + +Intel SGX (软件防护扩展,Software Guard Extension) 提供了在一个安全的 enclave(飞地)中执行代码的能力,enclave 可以提供一个隔离的可信执行环境,在操作系统、BIOS 和虚拟机监控器等系统软件均不可信的情况下,仍然对 enclave 内部的代码和数据提供保护,保障用户的关键数据和代码的机密性和完整性。 + +但如果 Intel SGX 程序仍然使用 C/C++ 这类内存不安全的语言开发的话,就会和传统软件一样面临着传统的内存破坏漏洞。对于 enclave 来说,受到的危害会更为严重,因为 enclave 中保存的多是机密数据和代码。Teaclave SGX 的主要目标就是通过使用高效的内存安全语言 —— Rust 来支持 enclave 应用程序的开发,从而在保证 Intel SGX enclave 内存安全的同时不会带来显著的性能开销。 + +Teaclave SGX SDK 内部结构分为三层: + ++ 最底层是使用 C/C++ 和汇编实现的 Intel SGX SDK。 ++ 中间层是 Rust 对 C/C++ 的 FFI (Foreign function Interfaces, 外部函数接口)。 ++ 最高层是 Teaclave SGX SDK。 + +![Teaclave SGX SDK 概要图](./img/2021-08-13-overview-of-teaclave-sgx-sdk.png) + +Teaclave SGX SDK 应用程序开发者在进行开发时就只需要基于最上层的 Teaclave SGX SDK 来进行开发,底层的实现对于开发者来说是透明的。本文将从开发者的角度介绍基于 Teaclave SGX SDK 开发自己的应用程序的过程。 + +### 准备条件 + ++ Ubuntu16.04 或者 18.04 或者 20.04 (Teaclave SGX SDK v1.1.3 中增加了对 Ubuntu 20.04 的支持) ++ docker 环境 + +*本文基于 Teaclave SGX SDK v1.1.3 提交哈希值:d107bd0718f723221750a4f2973451b386cbf9d2* + + +### 基于 docker 配置 Teaclave SGX SDK 开发环境 + +首先需要用户机器 CPU 支持 Intel SGX 并且在 BIOS 上开启了 Intel SGX 支持。用户可以通过 [SGX-hardware项目](https://github.com/ayeks/SGX-hardware) 或者在 [Intel 官网](https://www.intel.com/content/www/us/en/products/details/processors.html) 中搜索自己的 CPU 型号查看是否支持 Intel SGX。下图以 Intel Core i7-7700K 处理器为例,如下图所示,该机型支持 SGX。 + +![sgx-enable.png](./img/2021-08-13-sgx-enable.png) + +当确定 CPU 支持 Intel SGX 之后,还需要开启 BIOS 中的 SGX 选项。CPU 上的 SGX 选项可能有 `enabled` 或者 `software controlled`。具有 `enabled` 选项的主机直接在 BIOS 上选择 `enabled` 即可,而`software controlled` 表示 SGX 的开启需要由软件触发,还需通过 Intel 官方提供的 [sgx-software-enable](https://github.com/intel/sgx-software-enable) 开启。下载好 `sgx-software-enable` 之后,运行 `Makefile` 编译生成可执行代码 `sgx_enable` ,执行 `sudo ./sgx_enable` 顺利运行后重启主机,即可顺利开启 Intel SGX。 + +硬件条件准备完毕之后,还需要安装 [Linux SGX 驱动](https://download.01.org/intel-sgx/sgx-linux/2.10/distro/ubuntu16.04-server/sgx_linux_x64_driver_2.6.0_602374c.bin) ,安装完毕之后需要确认 `/dev/isgx` 的存在。 + +下载 Teaclave SGX SDK 以及支持编译 SGX 设备的 docker image。 + +`$ https://github.com/apache/incubator-teaclave-sgx-sdk` + +`$ docker pull baiduxlab/sgx-rust` + +启动一个 docker,并且把 Teaclave SGX SDK 项目目录映射到 docker 中。 + +`$ docker run -v /your/absolute/path/to/incubator-teaclave-sgx-sdk:/root/sgx -ti --device /dev/isgx baiduxlab/sgx-rust` + +在运行的 docker container 中启动 aesm 服务,**White list update request successful for Version** 语句意味着启动成功。 + +````bash +root@docker:/# LD_LIBRARY_PATH=/opt/intel/sgx-aesm-service/aesm/ /opt/intel/sgx-aesm-service/aesm/aesm_service & +aesm_service[17]: [ADMIN]White List update requested +aesm_service[17]: Failed to load QE3: 0x4004 +aesm_service[17]: The server sock is 0x56096ab991c0 +aesm_service[17]: [ADMIN]White list update request successful for Version: 103 +```` + +执行 Teaclave SGX SDK 中的简单实例 helloworld ,检查是否正常运行。 + +```bash +root@docker:~# cd sgx/samplecode/helloworld/ +root@docker:~/sgx/samplecode/helloworld# make +root@docker:~/sgx/samplecode/helloworld# cd bin/ +root@docker:~/sgx/samplecode/helloworld/bin# ./app +[+] global_eid: 2 +This is normal world string passed into enclave! +This is a Rust string! +[+] say_something success ... +``` +至此,我们已经成功在自己的机器上跑起来了 Teaclave SGX SDK 的 helloworld 示例啦! + +## Teaclave SGX SDK 示例 helloworld 剖析 + +接下来,我们通过阅读 helloworld 这个简单的例子来理解 Teaclave SGX SDK 应用程序的组织结构和运行方式。 + +### helloworld 目录结构 + +```tree +helloworld/ +├── app +│ ├── app.c +│ └── app.h +├── bin +│ └── readme.txt +├── enclave +│ ├── Cargo.toml +│ ├── Enclave.config.xml +│ ├── Enclave.edl +│ ├── Enclave.lds +│ ├── Enclave_private.pem +│ ├── Makefile +│ ├── src +│ │ └── lib.rs +│ ├── x86_64-unknown-linux-sgx.json +│ └── Xargo.toml +├── lib +│ └── readme.txt +└── Makefile +``` + +helloworld 的目录结构和 Intel SGX 的 [SampleEnclave](https://github.com/intel/linux-sgx/blob/HEAD/SampleCode/SampleEnclave) 目录结构非常类似。 ++ app 目录中存放的是不可信部分代码,包括 `main` 函数以及 `OCALL` 函数具体逻辑实现。 ++ enclave 目录中存放的是可信部分代码,主要是 `ECALL` 函数具体逻辑实现。 + + 不同于 SGX ,应用安全区的代码实现位于 **`src/lib.rs`**, 该文件是整个 `helloworld` 文件夹中唯一使用 Rust 编写的文件,程序员可以在该文件中增加需要的功能。 + + 另外,enclave 文件夹下多了 `Cargo.toml`, `src/lib.rs`, `x86_64-unknown-linux-sgx.json`, `Xargo.toml`: + + **`Cargo.toml`**: 项目清单文件,包括项目名称、项目版本以及依赖项等。 + + **`x86_64-unknown-linux-sgx.json`** 和 **`Xargo.toml`** 描述了用于项目交叉编译的信息。 + +### 重要代码文件解析 + ++ **`Enclave.edl`** \ +该文件规定了 Enclave 边界 `ECALL/OCALL` 的定义。 + +```edl +enclave { + from "sgx_tstd.edl" import *; + from "sgx_stdio.edl" import *; + from "sgx_backtrace.edl" import *; + from "sgx_tstdc.edl" import *; + + trusted { + /* define ECALLs here. */ + + public sgx_status_t say_something([in, size=len] const uint8_t* some_string, size_t len); + }; + + untrusted { + + }; +}; +``` + +`trusted {...}` 中声明 `ECALL` 函数, `untrusted {...}` 中声明 `OCALL` 函数。本例中声明了一个 `ECALL` 函数 `say_something`,该函数的具体实现在 `src/lib.rs` 中,它的参数包括 `uint8_t *` 类型的指针和长度参数 `len`。 + ++ **`app/app.c`** + +在 `app/app.c` 的 `main` 函数中有一个完整的调用 `ECALL` 的例子。 + +```c +sgx_ret = say_something(global_eid, + &enclave_ret, + (const uint8_t *) str, + len); +``` + +这里的 `say_something` 似乎和 `Enclave.edl` 中的声明不太一样,ECALL传递参数时多了两个隐参数:`enclave_eid` 和 `say_something` 的返回值 `&enclave_ret`。而 `sgx_ret` 表示的是 ECALL 执行是否成功,是 SGX 的返回值。 + ++ **`enclave/`文件夹部分** + +`enclave/Cargo.toml` 中声明了这是一个 `staticlib`,表明 Enclave 在最后会被编译成一个 `.a` 文件,该文件会和 Intel 提供的 `sgx_tstdc.a` 等文件链接形成 `enclave.so`,再经由 `sgx_sign` 工具配合 `Enclave.config.xml` 配置文件、`Enclave_private.pem` 签名私钥做签名并计算 `measurement` ,最后生成 `enclave.signed.so`,这是 Enclave 的完全体。 + ++ **`enclave/src/lib.rs`** + +```rust +pub extern "C" fn say_something(some_string: *const u8, some_len: usize) -> sgx_status_t { + + let str_slice = unsafe { slice::from_raw_parts(some_string, some_len) }; + let _ = io::stdout().write(str_slice); + + // A sample &'static string + let rust_raw_string = "This is a "; + // An array + let word:[u8;4] = [82, 117, 115, 116]; + // An vector + let word_vec:Vec<u8> = vec![32, 115, 116, 114, 105, 110, 103, 33]; + + // Construct a string from &'static string + let mut hello_string = String::from(rust_raw_string); + + // Iterate on word array + for c in word.iter() { + hello_string.push(*c as char); + } + + // Rust style convertion + hello_string += String::from_utf8(word_vec).expect("Invalid UTF-8") + .as_str(); + + // Ocall to normal world for output + println!("{}", &hello_string); + + sgx_status_t::SGX_SUCCESS +} +``` +该函数实现了一个简单的将 `u8` 数组转化为字符串输出的函数,注意在函数的最后调用的 `println!` 函数是一个 `OCALL`。 `println!` 的具体实现中加入了内置的 `OCALL`,并定义了内置的 `EDL` ,import到了 `Enclave.edl` 中。 Review comment: `&[u8]`数组, `edl`(大小写) -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: dev-unsubscr...@teaclave.apache.org For queries about this service, please contact Infrastructure at: us...@infra.apache.org --------------------------------------------------------------------- To unsubscribe, e-mail: dev-unsubscr...@teaclave.apache.org For additional commands, e-mail: dev-h...@teaclave.apache.org