1. 程式人生 > >OpenSGX 安裝編譯教程

OpenSGX 安裝編譯教程

介紹:

英特爾提供的SGX是一個擴充套件的x86指令集架構,它能讓應用程式在叫做enclave的受保護的容器內執行。為不信任特權軟體(如:作業系統和虛擬機器管理程式)上的應用程式提供隔離的執行和記憶體保護。通過減少可信計算基(TCB),SGX提供窄攻擊面。掌握了所有的軟體元件和硬體,除CPU包外的敵人不能危害到SGX應用的程式碼和資料。SGX不僅保證了程式程式碼和資料的完整性,還保證了SGX程式的保密性。

     OpenSGX是基於QEMU的二進位制翻譯實現的軟體SGX模擬器,它在指令集水平模擬SGX硬體元件。此外,OpenSGX完全是一個用於SGX開發的平臺,它包含模擬硬體和作業系統元件,enclave程式載入器,一個OpenSGX使用者庫,還支援除錯和效能監控。使用OpenSGX能實現除錯和評估一個應用程式。

    

     使用QEMU實現SGX指令以及模擬硬體元件。特別的,利用它的二進位制翻譯功能,在QEMU的使用者空間模擬模式的頂層實現OpenSGX。Figure 1展示整體設計元件。OpenSGX包含6個一起工作的元件,提供功能齊全的SGX開發環境。6個元件如下:

  1. Intel SGX硬體模擬(Emulated Intel SGX hardware):硬體元件包括SGX指令,SGX資料結構,EPC和它的訪問保護,以及SGX處理鍵內QEMU    軟體。
  2. 作業系統模擬(OS emulation):系統呼叫模擬enclave程式用於執行SGX操作(如:enclave配置和動態記憶體分配)。
  3. enclave程式載入(Enclave program loader):一個用於自動處理通過enclave程式碼和資料段載入到EPC及適當的提供enclave棧和堆區域的載入器。
  4. OpenSGX使用者庫(libsgx):一個用在enclave內部和外部的方便的庫。它為所有SGX使用者級指令提供封裝,為高階應用程式提供介面。
  5. 偵錯程式支援( Debugger support):擴充套件gbd來對映模擬指令(這裡,,OpenSGX也通過gdb公開SGX資料結構的金鑰)
  6. 效能監視(Performance monitoring):效能計數器/分析器,允許使用者手機enclave程式的效能資料

     需要注意的是OpenSGX不支援英特爾SGX二進位制相容性。現在還沒有具體或標準的二進位制級別的互操作。雖然OpenSGX支援大部分的具體的指令,我們並不實現所有的指令。例如,OpenSGX並沒有實現Intel SGX指定的除錯指令,但是軟體層能提供豐富的除錯環境(例如,熟悉的GBD存根)。OpenSGx是一種軟體模擬器,不提供安全保證。它的安全保證和Intel SGX不是一個層次的。

    首先,下載原始碼

$git clone https://github.com/sslab-gatech/opensgx.git

    安裝OpenSGX需要的依賴庫: libglib2.0-dev zlib1g-dev libaio-dev autoconf libtool libssl-dev libelf-dev。最好進入root許可權安裝,否則可能會導致失敗:

$su     進入root許可權
#apt-get install libglib2.0-dev zlib1g-dev libaio-dev autoconf libtool libssl-dev libelf-dev    安裝所需要的庫</span>
     接下來就是編譯QEMU:
    
#cd opensgx/qemu
# ./configure-arch
# make –j $(nproc)</span>

編譯sgxlib和user-level code

# cd ..
# make –C libsgx
# make –C user
如果上述編譯都成功,那麼程式碼就可以算是編譯成功了

原始碼組成:

  1. opensgx:用於執行應用程式OpenSGX工具鏈。
  2. qemu:用於SGX指令的硬體模擬 
    qemu/target-i386/sgx-helper.c :ENCLU/ENCLS指令的輔助功能
     qemu/target-i386/sgx.h:EPC, EPCM以及SGX的資料結構
  3. libsgx:OpenSGX庫
    libsgx/musl-libc/:為OpenSGX程式定製的libc庫
     libsgx/polarssl/:OpenSGX程式輕加密庫
    libsgx/sgx-basic.c:OpenSGX為封裝ENCLU指令封裝的函式
    libsgx/sgx-entry.c :進入封裝的enclave程式
  4. user:系統模擬,系統呼叫介面,使用者水平API,OpenSGX測試用例 
    user/sgx-tool.c:用於建立金鑰和資料結構的工具
    user/sgx-runtime.c:啟動enclave的執行庫
    user/sgx-user.c :使用者水平API及系統呼叫介面
    ser/sgx-kern.c:用於建立enclave的作業系統級封裝
    user/sgx-loader.c:用於執行enclave程式的enclave載入器
    user/sgx-trampoline.c:用於SGX庫支援的Trampoline封裝 
    user/sgx.lds:OpenSGX的連結指令碼
    user/test/:簡單測試用例
  5. gdb:用於除錯EPCM,EPC和SECS的Python指令碼

OpenSGX硬體配置

最初,硬體元件如EPC,EPCM和處理器金鑰實際上是處理器的組成部分。此外,軟體元件,如SIGSTRUCT和EINITTOKEN應駐留在EPC為受保護的資料結構的一部分。要模擬這些元件,OpenSGX提供了一個名為“SGX-tool”來指定硬體配置,如EPC的大小和SGX處理器金鑰。它位於user/directory。

配置硬體元件 

    要編譯,構建和執行程式OpenSGX,兩個金鑰必需的。一個是處理器金鑰,而另一個是enclave金鑰。這兩個金鑰可以通過使用sgx-tool -k選項生成。預設情況下,OpenSGX使用處理器的金鑰對為128位,使用RSA的enclave金鑰對為3072位。

Generate RSA key with give bits
$ sgx-tool -k [BITS]
Default setting
$ sgx-tool -k 3072 ( For enclave key pair )
$ sgx-tool -k 128 ( For device key pair )
   EPC和EPCM的大小也是可配置的。在qemu/target-i386/directory中,有一個“sgx.h”標頭檔案。在第26行,定義了NUM_EPC變數(預設,1500)。它是一個enclave開始包含的EPC頁的數量。如果你想改變一個enclave的大小,你可以更改此值,並重新編譯qemu。

配置資料結構
有兩個核心資料結構來驗證enclave和enclave程式設計師的身份。那是SIGSTRUCT和EINITTOKEN。 SIGSTRUCT從enclave簽名者(OpenSGX程式設計師)得到enclave資訊,並且還具有enclave的雜湊值。它用來驗證發動encalve時enclave簽名者的身份。 EINITTOKEN在EINIT指令驗證目標enclave是否允許推出時使用。它包含一個通過啟動金鑰(處理器金鑰)計算的加密的MAC,以檢查enclave是否是在執行

SGX啟用的平臺。下面是產生SIGSTRUCT和EINITTOKEN資料結構的具體過程。

Measure binary (生成enclave雜湊)
$ sgx-tool -m path/to/binary --begin=STARTADDR --end=ENDADDR --entry=ENTRYADDR
(e.g., sgx-tool -m test/simple --begin=0x426000 --end=0x426018 --entry=0x426000)
Note: 偏移可以通過使用“readelf-S”,找到對應部分的偏移量來獲得。

Sign on sigstruct format with given key (after manually fill the fields)
$ sgx-tool -s path/to/sigstructfile --key=path/to/enclavekeyfile
Note,註冊後用戶需要在sigstruct檔案中手動填寫註冊資訊 user should manually fill the sign information in the sigstruct file after sign
Generate einittoken format (with reserved fields filled with 0s)
<span style="font-family: Arial, Helvetica, sans-serif;">$ sgx-tool -E</span>
Generate MAC over einittoken format with given key (after manually fill the fields)
$ sgx-tool -M path/to/einittokenfile --key=path/to/devicekeyfile
(e.g., sgx-tool -M sig-einittoken.conf --key=sgx-intel-processor.conf)
Note, 使用者需要手動填寫MAC到einittoken檔案.
為了方便OpenSGX使用者,我們提供一個叫“opensgx”的工具鏈,上述過程是由簡單的命令自動完成。使用opensgx工具鏈的解釋在下面描述。

OpenSGX二進位制編譯

    要編譯二進位制OpenSGX,它需要多個前提步驟。首先,SGX應用程式開發者建立了他/她自己的簽名金鑰簽名者身份認證。在這之後,你可以編譯OpenSGX程式和使用自己的金鑰進行簽名。在這個階段中,OpenSGX還可以通過計算程式碼和資料區域的雜湊值計算enclave程式同一性(身份)。然後,你可以執行你自己OpenSGX的應用程式。

生成自己的簽名金鑰
$ ./opensgx –k
(Then, sign.key 檔案被建立)
生成hello.sgx (編譯一個OpenSGX應用程式)
$ ./opensgx –c user/demo/hello.c
(Then, hello.sgx建立在user/demo 目錄
生成 hello.conf (註冊並計算出一個OpenSGX應用程式的身份)
$ ./opensgx –s user/demo/hello.sgx --key sign.key
(Then, hello.conf 建立在 user/demo 目錄)
執行OpenSGX程式
$ ./opensgx user/demo/hello.sgx user/demo/hello.conf


實現OpenSGX應用
    實現一個OpenSGX程式類似於正常的C程式。由於對SGX程式的作業系統支援,OpenSGX支援系統呼叫介面和使用者級的API,使用者只需用他們建立並執行OpenSGX二進位制檔案。唯一的區別在於,使用enclave_main()而不是主main(),使用sgx_exit(null)代替return。在OpenSGX程式中,在編譯OpenSGX程式時,有可能使用現有的libc庫函式或通過將存檔檔案加密的庫函式如polarssl。由於OpenSGX提供它自己的定製連結指令碼和載入器,所以通過修改,它們用到其他庫中的enclave二進位制檔案。下面是簡單的Hello World程式的原始碼:
Hello world example

#include “test.h”
void enclave_main()
{
char *hello = "hello sgx!\n";
puts(hello);
sgx_exit(NULL);
}

Trampoline & Stub

    OpenSGX通過使用共享程式碼和資料記憶體提供更嚴格通訊協議,分別為Trampoline & Stub。使用Trampoline & Stub的定義狹窄enclave介面,這使得強制執行相關的安全特性易於處理。圖2示出Trampoline & Stub的示例情況。enclave首先在stub設定輸入引數,然後呼叫一個預定義的處理程式,trampoline ,通過退出其enclave模式(即,通過呼叫EEXIT)。

使用libc 庫函式

    在OpenSGX應用程式中使用libc庫,使用trampoline and stub介面是通過修改幾個原glibc庫的程式碼。下面是一個包含trampoline and stub介面修改libc函式的原始碼。紅色的是新新增部分。在此,stub的資料結構設定對應的共享區(STUB_ADDR),函式型別(FCODE)和在庫函式輸入/輸出引數。在這裡,輸出引數(out_arg)是函式的輸入變數,輸入引數(in_arg)是函式的返回值。out_arg值被設定之後,它會呼叫“sgx_exit(stub->蹦床)”退出enclave模式。

ssize_t read(int fd, void *buf, size_t count)
{
<span style="color:#ff6666;">sgx_stub_info *stub = (sgx_stub_info *)STUB_ADDR;</span>
int tmp_len;
ssize_t rt;
int i;
rt = 0;
for (i = 0; i < count / SGXLIB_MAX_ARG + 1; i++) {
<span style="color:#ff6666;">stub->fcode = FUNC_READ;
stub->out_arg1 = fd;</span>
if (i == count / SGXLIB_MAX_ARG)
tmp_len = (int)count % SGXLIB_MAX_ARG;
else
tmp_len = SGXLIB_MAX_ARG;
<span style="color:#ff6666;">stub->out_arg2 = tmp_len;
sgx_exit(stub->trampoline);</span>
memcpy((uint8_t *)buf + i * SGXLIB_MAX_ARG, stub->in_data1, tmp_len);
<span style="color:#ff6666;">rt += stub->in_arg1;</span>
return rt;
}

一但主機程式或OS處理enclave請求時,它把結果或返回值儲存到stub中,而且通過呼叫ERESUME.重新進入enclave模式。將程式的控制權交還給已知enclave位置後,enclave最終能得到返回值(e.g,in_arg1 in stub)。
static int sgx_read_tramp(int fd, void *buf, size_t count)
{
return read(fd, buf, count);
}
void sgx_trampoline()
{
switch (stub->fcode) {
case FUNC_PUTS:
…
case FUNC_READ:
stub->in_arg1 = sgx_read_tramp(stub->out_arg1, stub->in_data1, (size_t)stub->out_arg2);
break;
…
}
     要使用enclave程式中的第三方庫,他們的存檔檔案(*.a)中或目標檔案(*.o)應該被編譯以及連線到SGX程式。目前,OpenSGX支援mbedtls(輕量級SSL庫)和OpenSSL庫。您可以不需要任何修改的使用enclave程式內的庫,在OpenSGX,我們使用mbedtls庫來計算用於mrenclave的SHA256雜湊和用於報告資料結構的MAC(訊息身份驗證碼Message Authenticate Code)。使用這些庫的唯一事情就是修改Makefile檔案;你應該在編譯過程中連結歸檔檔案或目標檔案複製到目標SGX程式(見user/ Makefile中的細節),這是使用enclave內OpenSSL庫函式的示例程式碼。

/user/test/openssl/simple-openssl.c

#include "../test.h"
#include <openssl/bn.h>
#include <openssl/rsa.h>
BIGNUM *bn = NULL;
RSA *rsa = NULL;
void enclave_main()
{
// Bignum allocation for global variable
bn = BN_new();
// Bignum allocation for local variable
BIGNUM *bn2 = BN_new();
// RSA allocation for global variable
rsa = RSA_new();
// RSA allocation for local variable
RSA *rsa2 = RSA_new();
printf("%x\n", (unsigned long)rsa2);
}
引數向量
    OpenSGX還支援引數向量指令輸入傳遞給enclave程式。為了實現這個屬性,OpenSGX提供sgx-entry.S中的彙編程式碼傳遞位於libsgx目錄中argc和argv,呼叫enclave_main之前,sgx-runtime儲存引數向量到使用者空間暫存器(RDI,RDX等),並將其傳遞到入口點(enclave_start)。在這裡,“SGX-entry.S”檔案定義了enclave_start象徵,它得到的暫存器的值,並把它們進棧。你可以在/user/sgx-runtime.c和/libsgx/sgx-entry.S檔案中看到引數向量處理過程的細節。下面是使用引數向量的示例程式碼。

/user/test/simple-arg.c

#include "test.h"
void enclave_main(int argc, char **argv)
{
printf("argc = %d\n", argc);
puts(argv[0]);
puts(argv[1]);
puts(argv[2]);
}

上述“test_arg”為引數簡單的帶引數的程式執行的結果如下。類似的引數向量的一般用法,它可以傳遞引數argc和argv到enclave_main.需要注意的是argv[0] 包括“./../user/sgx-runtime”字串,因為test.sh指令碼執行sgx-runtime來載入SGX二進位制和呼叫EENTER指令。
[email protected]:~/src/SGX/opensgx/user$ ./test.sh test/simple-arg test_arg
make: `test/simple-arg' is up to date.
--------------------------------------------
kern in count : 2
kern out count : 2
--------------------------------------------
…
…
…
--------------------------------------------
Pre-allocated EPC SSA region : 0x2000
Pre-allocated EPC Heap region : 0x12c000
Later-Augmented EPC Heap region : 0x0
Total EPC Heap region : 0x12c000
argc = 3                      print argc
./../user/sgx-runtime         print argv[0]
test/simple-arg               print argv[1]
test_arg                      print argv[2]


enclave和non-enclave過程的分離

    要實現SGX程式,最重要的是減少了TCB(可信計算基)。為了實現這一目標,SGX程式設計師需要儘量減少SGX程式的大小,例如,如果程式設計師嘗試實現和執行現有的應用程式,他們需要把核心部分分開(例如,私鑰建立簽名),把他們變成enclave程式。在本教程中我們把在enclave內執行的程序叫做enclave程序,把其他的程序叫做non-enclave程序。在本節中,我們提供了他們如何互相通訊,並給他們一個簡單的例子。

    enclave和non-enclave程序通過管道協議進行通訊,當nonenclave程序請求核心操作的(例如,要求建立簽名),它傳送請求資訊給enclave程序,然後,它傳送輸入的資料(例如,資料將簽署)到enclave程序。enclave程序接收non-enclave的輸入資料並在enclave執行祕密操作(例如,建立一個簽名)。表明祕密應用程式(例如,私有金鑰)不會被洩露以及在enclave內部被安全保護,安全操作完成後,將結果傳送回no-encalve程序。enclave 程序和non-enclave程序位於 user/test/simple-pipe.c和user/non_enclave/simple-pipe.c。

遠端認證

     認證相關的原始碼OpenSGX支援本地/遠端認證。在libsgx目錄有幾個與認證相關函式。您可以通過include ‘sgx-lib.h'使用本地/遠端認證庫函式到enclave程式。下面是相關認證函式原型

Part of /libsgx/include/sgx-lib.h

extern int sgx_enclave_read(void *buf, int len);
extern int sgx_enclave_write(void *buf, int len);

/* SIGSTRUCT parsing function */
extern sigstruct_t *sgx_load_sigstruct(char *conf);

/* Attestation supporting funcitons */
extern int sgx_make_server(int port);
extern int sgx_connect_server(const char *target_ip, int target_port);
extern int sgx_get_report(int fd, report_t *report);
extern int sgx_read_sock(int fd, void *buf, int len);
extern int sgx_write_sock(int fd, void *buf, int len);
extern int sgx_match_mac(unsigned char *report_key, report_t *report);
extern int sgx_make_quote(const char* pers, report_t *report, unsigned char *rsa_N, unsigned char *rsa_E);

/* Intra attestation supporting functions */
extern int sgx_intra_attest_challenger(int target_port, char *conf);
extern int sgx_intra_attest_target(int port);

/* Remote attestation supporing functions */
extern int sgx_remote_attest_challenger(const char *target_ip, int target_port, const char *challenge);
extern int sgx_remote_attest_target(int challenger_port, int quote_port, char *conf);
extern int sgx_remote_attest_quote(int target_port)

內/遠端認證機制

   OpenSGX產生兩種認證特性,一種是本地認證,一種是遠端認證。

    本地認證出現在兩個enclave之間,challenger和target。challenger enclave想要驗證target enclave,使用套接字與target enclave相連。然後challenger enclave從配置檔案(.conf)中得到target enclave的身份。challenger enclave通過target enclave的配置檔案載入target enclave 的SIGSTRUCT ,然後challenger enclave通過EREPORT指令建立REPORT 並通過套接字寫傳送給target enclave,target enclave收到REPORT 然後提取報告金鑰來計算MAC。然後REPORT 驗證MAC的REPORT 並把它的REPORT 傳送給challenger enclave。最後,challenger enclave使用target enclave的REPORT相互驗證來反覆驗證這個過程。

    遠端認證比本地部認證更加困難。遠端認證過程需要三個enclave, challenger, target, 和quoting enclave。首先,challenger enclave建立與target enclave的套接字連線,然後向target enclave傳送請求。target enclave收到target enclave傳送的請求後,就和存在於同一個SGX平臺的quoting enclave開始內認證,兩個enclave完成相互驗證後,target enclave傳送認證成功資訊給quoting enclave,然後quoting enclave生成QUOTE and RSA 金鑰併發送傳送給target enclave。target enclave收到QUOTE and RSA 金鑰後將其轉發給challenger enclave。最後challenger enclave使用RSA金鑰對驗證QUOTE來驗證target enclave。OpenSGX中所有的遠端認證都是基於SGX標準實現的,使用RSA金鑰方案作為替代EPID。我們使用公共簽名方案(RSA)作為證明的概念,並留下采用EPID作為今後的工作。

測試本/遠端認證

OpenSGX支援相關的內部/遠端認證測試程式碼,因為target/quoting enclave配置檔案對內/遠端認證是必需的,所以首先需要在demo目錄新建配置檔案。可以通過鍵入下面的命令簡單地測試認證碼。

本地認證測試

Terminal #1

$ ./opensgx –k
$ ./opensgx –c user/demo/simple-intra-attest-target.c
$ ./opensgx –s use user/demo/simple-intra-attest-target.sgx --key sign.key
$ ./opensgx user/demo/simple-intra-attest-target.sgx user/demo/simple-intra-attest-target.conf


Terminal  #2

$ cd user
$ ./test.sh test/simple-intra-attest-challenger


遠端認證測試

Terminal #1

$ ./opensgx –k
$ ./opensgx –c user/demo/simple-remote-attest-quote.c
$ ./opensgx –s use user/demo/simple-remote-attest-quote.sgx --key sign.key
$ ./opensgx user/demo/simple-remote-attest-quote.sgx user/demo/simple-remote-attest-quote.conf
Terminal #2
$ cd user
$ ./test.sh test/simple-remote-attest-target

Terminal #3
$ cd user
$ ./test.sh test/simple-remote-attest-challenger
    由於上面的測試在本地計算機環境設計的測試,遠端驗證測試本地使用套接字連線。然而如果你想測試遠端認證在遠端OpenSGX環境,可以簡單的修改測試程式碼中的IP地址和埠。

    如果測試成功,會打印出 “Intra/Remote Attestation Success!”,另外,遠端認證測試,您可能需要等待幾秒鐘,因為生成RSA金鑰和解密QUOTE需要一些時間。

除錯支援

由於OpenSGX基於QEMU的使用者模式模擬實現,其二進位制轉換使除錯更加困難,因為一個偵錯程式只能觀察轉換後的指令。 從而,
OpenSGX擴充套件gdb對映模擬的該指令。這下面是一個除錯OpenSGX應用程式過程的例子。

用除錯操作執行目標應用程式

$ ./opensgx -d 1234 user/demo/hello.sgx user/demo/hello.conf &
連線遠端dbg到目標埠
$ gdb user/sgx-runtime
(gdb) target remote localhost:1234
Remote debugging using localhost:1234
[New Remote target]
Reading symbols from /lib64/ld-linux-x86-64.so.2...(no debugging symbols found)...done.
Loaded symbols for /lib64/ld-linux-x86-64.so.2
[Switching to Remote target]
0x0000004000802190 in ?? () from /lib64/ld-linux-x86-64.so.2
(gdb) b sgx-runtime.c:63
Breakpoint 1 at 0x401a80: file sgx-runtime.c, line 63.
(gdb) c
Continuing.
Breakpoint 1, 0x0000000000401a80 in main ()

Find a text section 
$ readelf -s user/demo/hello.sgx | grep text
[ 2] .text   PROGBITS   0000000050000110 00000110

Add symbol file in gdb by specifying a text section offset

(gdb) add-symbol-file user/demo/hello.sgx 0x0000000050000110
add symbol table from file "user/demo/hello.sgx" at
.text_addr = 0x50000110
(y or n) y
Reading symbols from /home/mingwei/gatech/opensgx_test/user/demo/hello.sgx...done.

Set a breakpoint on the enclave binary and start debugging

(gdb) b enclave_main
Breakpoint 2 at 0x50000110
(gdb) c
Continuing.
Breakpoint 2, 0x0000000050000110 in enclave_main ()
(gdb)

效能監控

OpenSGX還支援效能監視功能。因為它是一個軟體模擬器,它不能提供精確的效能指標。但是,OpenSGX通過提供類似於效能計數器的模擬效能統計資料的幫助開發人員和研究人員推測潛在效能問題。它公開了一個系統呼叫來查詢OpenSGX模擬資料。發生上下文切換次數、SGX指令的執行次數等等。此外,opensgx工具鏈提供-i選項(通過使用QEMU的tcg-外掛),以計算enclave程式以軟體方式消耗的CPU週期數。

$ cd user
$ ./test.sh –i test/simple-hello
Example:給定的Hello World程式的效能測試
--------------------------------------------
kern in count : 2
kern out count : 2
--------------------------------------------
encls count : 6071
ecreate count : 1
eadd count : 357
eextend count : 5712
einit count : 1
eaug count : 0
--------------------------------------------
enclu count : 2
eenter count : 1
eresume count : 0
eexit count : 1
egetkey count : 0
ereport count : 0
eaccept count : 0
--------------------------------------------
mode switch count : 2tlb flush count
: 2
--------------------------------------------
Pre-allocated EPC SSA region : 0x2000
Pre-allocated EPC Heap region : 0x64000
Later-Augmented EPC Heap region : 0x0
Total EPC Heap region
: 0x64000           Statistics of SGX program
hello sgx!          Result of program
number of executed instructions on CPU #0 = 120950414        Consumed CPU cycles