基本原理
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