本文将向大家展示如何基于 Intel SGX SDK 開發一個最簡單 SGX 應用:HelloWorld,這個程式在可信區生産 "Hello world"并傳遞給不可信代碼(緩沖區)列印輸出到終端。
雖然 Intel SGX SDK 安裝目錄中預設提供了數個 Sample,但每個 Sample 對于初學者來說非常複雜和難以了解。
前提條件
- [必須] 你的開發環境必須安裝了 Intel SGX SDK。
- 預設安裝到
。/opt/intel/sgxsdk
- 預設安裝到
- [可選] 開發環境主機 CPU 支援 SGX;若不支援,可采用模拟器編譯運作。
SGX IDE
目前隻有微軟 Visual Studio 2012 或 2013 專業版支援 SGX,且隻支援 Windows; VS 提供了 SGX 項目模闆以及 編譯和調試相關功能。
基本原理
在示範代碼之前,有必要先了解下 SGX 程式最基本的原理:
- SGX應用由兩部分組成:
- untrusted 不可信區
- 代碼和資料運作在普通非加密記憶體區域,程式
入口必須在非可信區;上圖中的main
和main()
函數均在非可信區。bar()
- 代碼和資料運作在普通非加密記憶體區域,程式
- truested 可信區
- 代碼和資料運作在硬體加密記憶體區域,此區域由 CPU 建立的且隻有CPU有權限通路; 上圖中的
helloworld()
函數運作在可信區。foo()
- 代碼和資料運作在硬體加密記憶體區域,此區域由 CPU 建立的且隻有CPU有權限通路; 上圖中的
- untrusted 不可信區
- 非可信區隻能通過 ECALL 函數調用可信區内的函數。
- 可信區隻能通過 OCALL 函數調用非可信區的函數。
- ECALL 函數和 OCALL 函數通過 EDL 檔案聲明。
第一個 SGX 程式: HelloWorld
目錄結構
HelloWorld/
├── App
│ ├── App.cpp
│ └── App.h
├── Enclave
│ ├── Enclave.config.xml
│ ├── Enclave.cpp
│ ├── Enclave.edl
│ ├── Enclave.h
│ ├── Enclave.lds
│ └── Enclave_private.pem
├── Include
└── Makefile
上面目錄結構仿照了
sgxsdk/SampleCode
目錄下示例代碼目錄:
- App 目錄内為不可信區域代碼,包括
入口、OCALL 函數内具體邏輯代碼等等。main
- Enclave 目錄為可信區域代碼,包括 ECALL 函數内具體邏輯代碼實作。
- Enclave.lds
- EDL(Enclave Description Language) 檔案。
-
- Enclave linker script。
- Enclave_private.pem
- enclave.so 的簽名私鑰。
- Enclave.config.xml
- Enclave 配置檔案,如堆棧大小、是否客氣 Debug 等。
- Enclave.h & Enclave.cpp
- 應用安全區代碼實作。
- Enclave.lds
- Include 目錄是不可信代碼和可信代碼共享的頭檔案。
編譯 & 運作
比較簡單,直接在項目目錄下執行
make
,在項目根目錄下會生成一個名為
app
的 Binary,運作這個
app
:
$ make
GEN => App/Enclave_u.h
CC <= App/Enclave_u.c
CXX <= App/App.cpp
LINK => app
GEN => Enclave/Enclave_t.h
CC <= Enclave/Enclave_t.c
CXX <= Enclave/Enclave.cpp
LINK => enclave.so
<EnclaveConfiguration>
<ProdID>0</ProdID>
<ISVSVN>0</ISVSVN>
<StackMaxSize>0x40000</StackMaxSize>
<HeapMaxSize>0x100000</HeapMaxSize>
<TCSNum>10</TCSNum>
<TCSPolicy>1</TCSPolicy>
<!-- Recommend changing 'DisableDebug' to 1 to make the enclave undebuggable for enclave release -->
<DisableDebug>0</DisableDebug>
<MiscSelect>0</MiscSelect>
<MiscMask>0xFFFFFFFF</MiscMask>
</EnclaveConfiguration>
tcs_num 10, tcs_max_num 10, tcs_min_pool 1
The required memory is 3960832B.
The required memory is 0x3c7000, 3868 KB.
Succeed.
SIGN => enclave.signed.so
The project has been built in debug hardware mode.
$ ./app
Hello world
Info: SampleEnclave successfully returned.
Enter a character before exit ...
編譯基本流程(Makefile):
- 通過
工具在sgx_edger8r
目錄下生成不可信代碼(Enclave_u.c 和 Enclave_u.h),這部分生成代碼主要會調用 ECALL (sgx_ecall);App/
- 編譯不可信部分 Binary:
;app
-
sgx_edger8r
目錄下生成可信代碼(Enclave_t.c 和 Enclave_t.h);Enclave/
- 編譯可信動态連結庫(enclave.so);
-
工具簽名可信動态連結庫(enclave.signed.so);sgx_sing
- 結束。
編譯後的代碼目錄結構:
HelloWorld
├── app
├── App
│ ├── App.cpp
│ ├── App.h
│ ├── App.o #[generated]
│ ├── Enclave_u.c #[generated]
│ ├── Enclave_u.h #[generated]
│ └── Enclave_u.o #[generated]
├── Enclave
│ ├── Enclave.config.xml
│ ├── Enclave.cpp
│ ├── Enclave.edl
│ ├── Enclave.h
│ ├── Enclave.lds
│ ├── Enclave.o #[generated]
│ ├── Enclave_private.pem
│ ├── Enclave_t.c #[generated]
│ ├── Enclave_t.h #[generated]
│ └── Enclave_t.o #[generated]
├── enclave.signed.so #[generated]
├── enclave.so #[generated]
├── Include
└── Makefile
代碼檔案
- Encalve/Enclave.edl
enclave {
trusted {
public void ecall_hello_from_enclave([out, size=len] char* buf, size_t len);
};
};
EDL 中聲明了一個公共 ECALL 函數,每個 SGX 應用的 EDL 必須至少聲明一個
public
類型的 ECALL 函數。
trusted {...}
内聲明 ECALL 函數,
untrusted {...}
内申明 OCALL 函數,由于本例中安全區不需要向非安全區調用(OCALL),是以隻聲明了一個 ECALL 函數
ecall_hello_from_enclave
,這個 ECALL 函數目的是在安全區建立一個 Buffer 并填充 "Hello world",然後這個 Buffer 的内容拷貝到非安全的 Buffer 中,非安全區調用
printf
列印這個非安全 Buffer 内容。
- Enclave/Enclave.lds
enclave.so
{
global:
g_global_data_sim;
g_global_data;
enclave_entry;
g_peak_heap_used;
local:
*;
};
- Enclave/Enclave.config.xml
<EnclaveConfiguration>
<ProdID>0</ProdID>
<ISVSVN>0</ISVSVN>
<StackMaxSize>0x40000</StackMaxSize>
<HeapMaxSize>0x100000</HeapMaxSize>
<TCSNum>10</TCSNum>
<TCSPolicy>1</TCSPolicy>
<!-- Recommend changing 'DisableDebug' to 1 to make the enclave undebuggable for enclave release -->
<DisableDebug>0</DisableDebug>
<MiscSelect>0</MiscSelect>
<MiscMask>0xFFFFFFFF</MiscMask>
</EnclaveConfiguration>
- Enclave/Enclave.h
這個頭檔案内容基本為空的。
#ifndef _ENCLAVE_H_
#define _ENCLAVE_H_
#endif
- Enclave/Enclave.cpp
#include "Enclave.h"
#include "Enclave_t.h" /* print_string */
#include <string.h>
void ecall_hello_from_enclave(char *buf, size_t len)
{
const char *hello = "Hello world";
size_t size = len;
if(strlen(hello) < len)
{
size = strlen(hello) + 1;
}
memcpy(buf, hello, size - 1);
buf[size-1] = '\0';
}
- Enclave/Enclave_private.pem
生成簽名秘鑰:
$ openssl genrsa -out Enclave/Enclave_private.pem -3 3072
- App/App.h
#ifndef _APP_H_
#define _APP_H_
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include "sgx_error.h" /* sgx_status_t */
#include "sgx_eid.h" /* sgx_enclave_id_t */
#ifndef TRUE
# define TRUE 1
#endif
#ifndef FALSE
# define FALSE 0
#endif
# define TOKEN_FILENAME "enclave.token"
# define ENCLAVE_FILENAME "enclave.signed.so"
extern sgx_enclave_id_t global_eid; /* global enclave id */
#if defined(__cplusplus)
extern "C" {
#endif
#if defined(__cplusplus)
}
#endif
#endif /* !_APP_H_ */
#include <stdio.h>
#include <string.h>
#include <assert.h>
# include <unistd.h>
# include <pwd.h>
# define MAX_PATH FILENAME_MAX
#include "sgx_urts.h"
#include "App.h"
#include "Enclave_u.h"
/* Global EID shared by multiple threads */
sgx_enclave_id_t global_eid = 0;
int initialize_enclave(void)
{
sgx_status_t ret = SGX_ERROR_UNEXPECTED;
/* 調用 sgx_create_enclave 建立一個 Enclave 執行個體 */
/* Debug Support: set 2nd parameter to 1 */
ret = sgx_create_enclave(ENCLAVE_FILENAME, SGX_DEBUG_FLAG, NULL, NULL, &global_eid, NULL);
if (ret != SGX_SUCCESS) {
printf("Failed to create enclave, ret code: %d\n", ret);
return -1;
}
return 0;
}
/* 應用程式入口 */
int SGX_CDECL main(int argc, char *argv[])
{
(void)(argc);
(void)(argv);
const size_t max_buf_len = 100;
char buffer[max_buf_len] = {0};
/* 建立并初始化 Enclave */
if(initialize_enclave() < 0){
printf("Enter a character before exit ...\n");
getchar();
return -1;
}
/* ECALL 調用 */
ecall_hello_from_enclave(global_eid, buffer, max_buf_len);
printf("%s\n", buffer);
/* 銷毀 Enclave */
sgx_destroy_enclave(global_eid);
printf("Info: SampleEnclave successfully returned.\n");
printf("Enter a character before exit ...\n");
getchar();
return 0;
}
總結
即便最簡單的 SGX HelloWold 也比較複雜,當然“安全性”和“成本”(技術壁壘門檻、開發成本、維護成本、物料成本等)總是成正比的,和“效率”成反比的。希望這篇文章對那些想入門開發 SGX 應用的使用者有所幫助。
參考資料
- 知乎專欄
- 了解Intel SGX
- 飛地開發基礎
- 飛地開發基礎(一)
- 飛地開發基礎(二)
- 飛地開發基礎(三)
- 示例-SampleEnclave
- PowerTransition
- 開發準備
- Intel SGX 官方的入門教程和中英雙語的視訊
- SGX應用開發入門
- SGX 及 SDK 應用開發環境簡介及搭建 — 第1部分
- 第一個 SGX 應用 “Hello World” — 第2部分
- SGX應用開發進階篇— 第3部分