天天看點

SGX入門:如何開發第一個最簡單的 SGX 應用 HelloWorld前提條件SGX IDE基本原理第一個 SGX 程式: HelloWorld總結參考資料

本文将向大家展示如何基于 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()

        函數運作在可信區。
  • 非可信區隻能通過 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 目錄内為不可信區域代碼,包括

    main

    入口、OCALL 函數内具體邏輯代碼等等。
  • 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
      • 應用安全區代碼實作。
  • 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):

  1. 通過

    sgx_edger8r

    工具在

    App/

    目錄下生成不可信代碼(Enclave_u.c 和 Enclave_u.h),這部分生成代碼主要會調用 ECALL (sgx_ecall);
  2. 編譯不可信部分 Binary:

    app

  3. sgx_edger8r

    Enclave/

    目錄下生成可信代碼(Enclave_t.c 和 Enclave_t.h);
  4. 編譯可信動态連結庫(enclave.so);
  5. sgx_sing

    工具簽名可信動态連結庫(enclave.signed.so);
  6. 結束。

編譯後的代碼目錄結構:

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 應用的使用者有所幫助。

參考資料

繼續閱讀