You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@teaclave.apache.org by GitBox <gi...@apache.org> on 2021/08/19 11:40:32 UTC

[GitHub] [incubator-teaclave-website] RMheng commented on a change in pull request #5: add developing Teaclave SGX Application doc

RMheng commented on a change in pull request #5:
URL: https://github.com/apache/incubator-teaclave-website/pull/5#discussion_r692031826



##########
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`中的数字第一次出现时,`vec`会进行一次`push`操作,但是当`nums2`中的该数字第二次出现时,`set`中的该数字已经被`move`了,所以第二次出现时,`if set.contains(i)`会得到`false`,所以不会`push`两次。




-- 
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-unsubscribe@teaclave.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@teaclave.apache.org
For additional commands, e-mail: dev-help@teaclave.apache.org