基本原理
SGX2.0應用由untrusted不可信區和trusted可信區兩部分組成:
非可信區只能通過ECALL函數調用可信區內的函數;可信區只能通過OCALL函數調用非可信區的函數。ECALL函數和OCALL函數通過EDL檔案聲明。
範例程式碼及目錄結構
本樣本中SGX2.0應用helloworld
所涉及的應用編譯、鏡像構建以及部署等樣本源碼位於github。範例程式碼的目錄結構如下。
App.hsgx-device-plugin/samples/hello_world/
├── Dockerfile
├── Makefile
├── README.md
└── src
├── App
│ ├── App.cpp
│ └── App.h
├── Enclave
│ ├── Enclave.config.xml
│ ├── Enclave.cpp
│ ├── Enclave.edl
│ ├── Enclave.h
│ ├── Enclave.lds
│ └── Enclave_private.pem
└── Makefile
src
代碼目錄和檔案說明如下。
目錄 | 說明 | 檔案 | 說明 |
App | 存放不可信地區代碼,包括main入口、OCALL函數內具體邏輯代碼等。 | App.cpp | 不可信地區代碼。 |
App.h | 標頭檔。 |
Enclave | 存放可信地區代碼,包括ECALL函數內具體邏輯代碼。 | Enclave.edl | EDL(Enclave Description Language)檔案。 |
Enclave.lds | Enclave linker script。 |
Enclave_private.pem | enclave.so的簽名密鑰。 |
Enclave.config.xml | Enclave設定檔,如堆棧大小、是否開啟Debug等。 |
Enclave.h和Enclave.cpp | 應用安全區代碼實現。 |
步驟一:編譯hello_world
執行以下命令,安裝Git開發工具。
sudo yum install git
執行以下命令,編譯hello_world應用。
git clone https://github.com/AliyunContainerService/sgx-device-plugin
cd sgx-device-plugin/samples/hello_world
SGX_SDK=/opt/alibaba/teesdk/intel/sgxsdk make build
專案根目錄下會產生一個名為hello_world的Binary檔案。
展開查看完整的Binary檔案
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
在支援SGX的執行個體上執行命令./hello_world
運行hello_world應用。
cd src/
./hello_world
預期輸出:
Wed May 6 06:53:33 2020
Hello world From SGX Enclave!
Wed May 6 06:53:34 2020
Hello world From SGX Enclave!
...
編譯流程和編譯後的代碼目錄結構說明如下。
展開查看編譯流程
通過sgx_edger8r工具在App/目錄下產生不可信代碼(Enclave_u.c
和Enclave_u.h
),這部分產生的程式碼主要會調用ECALL(sgx_ecall)
。
編譯不可信部分Binary:app。
通過sgx_edger8r工具在Enclave/目錄下產生可信代碼(Enclave_t.c
和Enclave_t.h
)。
編譯可信動態連結程式庫(enclave.so)。
通過sgx_sign工具簽名可信動態連結程式庫(enclave.signed.so)。
結束。
展開查看編譯後代碼目錄結構
sgx-device-plugin/samples/hello_world/src/
├── hello_world #[generated]
├── 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]
└── Makefile
檔案路徑 | 說明 | 範例程式碼 |
Encalve/Enclave.edl | EDL中聲明了一個公用ECALL函數,每個SGX應用的EDL必須至少聲明一個: 由於本例中安全區不需要向非安全區調用(OCALL),所以只聲明了一個ECALL函數ecall_hello_from_enclave ,這個ECALL函數目的是在安全區建立一個Buffer並填充helloworld ,然後將這個Buffer的內容拷貝到非安全的Buffer中,非安全區調用printf 列印這個非安全Buffer內容。 | enclave {
trusted {
public void ecall_hello_from_enclave([out, size=len] char* buf, size_t len);
};
};
|
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_ */
|
App/App.cpp | 無。 | #include <stdio.h>
#include <string.h>
#include <assert.h>
#include <time.h>
#include <ctime>
# 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;
char enclavefile[256];
getcwd(enclavefile, sizeof(enclavefile));
strcat(enclavefile, "/enclave.signed.so");
/* Call sgx_create_enclave to initialize an enclave instance */
/* Debug Support: set 2nd parameter to 1 */
ret = sgx_create_enclave(enclavefile, SGX_DEBUG_FLAG, NULL, NULL, &global_eid, NULL);
if (ret != SGX_SUCCESS) {
printf("Failed to create enclave, ret code: %d, enclave file: %s\n", ret, enclavefile);
return -1;
}
return 0;
}
tm* get_time() {
time_t rawtime;
struct tm * timeinfo;
time ( &rawtime );
timeinfo = localtime ( &rawtime );
return timeinfo;
}
/* Application entry */
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};
/* Initialize the enclave */
if(initialize_enclave() < 0){
printf("Enter a character before exit ...\n");
getchar();
return -1;
}
/* Enclave calls */
while(1) {
ecall_hello_from_enclave(global_eid, buffer, max_buf_len);
printf("%s%s\n", asctime(get_time()), buffer);
fflush(stdout);
sleep(1);
}
/* Destroy the enclave */
sgx_destroy_enclave(global_eid);
printf("Info: SampleEnclave successfully returned.\n");
printf("Enter a character before exit ...\n");
getchar();
return 0;
}
|
步驟二:構建並部署helloworld應用
建議您使用Alibaba Cloud Linux構建鏡像,安裝最新的SGX SDK並定期更新以避免潛在的安全問題。
Dockerfile樣本
以Alibaba Cloud Linux 3為例,Dockerfile檔案內容如下。
FROM registry.cn-hangzhou.aliyuncs.com/alinux/alinux3
ARG REGION_ID=cn-hangzhou
RUN yum install -y curl && \
repo_url=https://enclave-${REGION_ID}.oss-${REGION_ID}.aliyuncs.com/repo/alinux/enclave-expr.repo && \
yum install -y yum-utils && \
yum-config-manager --add-repo ${repo_url} && \
yum install -y libsgx-urts libsgx-uae-service # 按需添加更多SGX運行時依賴
WORKDIR /src
COPY src/hello_world src/enclave.signed.so /src
ENTRYPOINT ["/src/hello_world"]
操作步驟
安裝Docker。
具體操作,請參見部署並使用Docker。
執行以下命令編譯並構建測試鏡像。
請將${IMAGE_URL}
替換為您的測試鏡像地址。
cd sgx-device-plugin/samples/hello_world
TARGET_IMAGE=${IMAGE_URL} SGX_SDK=/opt/alibaba/teesdk/intel/sgxsdk make image
docker push ${IMAGE_URL}
執行以下命令部署helloworld
應用。
請將${IMAGE_URL}
替換為您在上一步操作中構建的測試鏡像地址。
cat <<EOF | kubectl create -f -
apiVersion: apps/v1
kind: Deployment
metadata:
name: helloworld
namespace: default
spec:
replicas: 2
selector:
matchLabels:
app: helloworld
template:
metadata:
labels:
app: helloworld
spec:
containers:
- image: ${IMAGE_URL}
imagePullPolicy: Always
name: helloworld
resources:
limits:
cpu: 250m
memory: 512Mi
alibabacloud.com/sgx_epc_MiB: 2
EOF