文章目录
- 0. SGX基础原理分析
- 一.准备工作
- 1.1 前提条件
- 1.2 SGX IDE
- 1.3 基本原理
- 二.程序设计
- 2.1 目录结构
- 2.2 源码设计
- 2.2.1 Encalve/Enclave.edl:Enclave Description Language
- 2.2.2 Enclave/Enclave.lds: Enclave linker script
- 2.2.3 Enclave/Enclave.config.xml: Enclave 配置文件,如堆栈大小、是否支持 Debug 等
- 2.2.4 Enclave/Enclave.h
- 2.2.5 Enclave/Enclave.cpp
- 2.2.6 Enclave/Enclave_private.pem:enclave.so 的签名私钥
- 2.2.7 App/App.h
- 2.2.8 App/App.cpp
- 2.3 Makefile
- 2.4 编译 & 运行
- 2.5 总结
- 三.参考链接
- 四. 感谢支持
0. SGX基础原理分析
本文将向大家展示如何基于 Intel SGX SDK
开发一个最简单 SGX 应用:HelloWorld
,这个程序在可信区生产 "Hello world"并传递给不可信代码(缓冲区)打印输出到终端。 虽然 Intel SGX SDK
安装目录中默认提供了数个 Sample,但每个 Sample 对于初学者来说非常复杂和难以理解。 我们先快速上手,然后再逐个分析每个Sample。关于 SGX 开发运行环境的搭建可参考之前的一篇博客:【SGX系列教程】(一)。
为了更深刻理解理解SGX工程上运行原理,我们先对SGX原理做介绍:
1. 开发者眼中SGX长什么样子?
简单来说SGX就是提供了一个安全内存及其相关。下面稍微讲一下SGX软件栈结构(具体见《SGX软件栈》文档)。
总的来说,SGX是划分两个世界的——可信世界和不可信世界。每一个世界中,想要使用SGX的开发都需要开发哪一个世界的代码,一般来说,不可信世界开发非敏感代码(称为APP,另一种理解就是APP也包含Enclave,这样为了区分,就把不可信的叫做APP),可信世界开发敏感代码(Enclave),或者说敏感代码移入了可信世界。
既然有了两个世界,他们之间的连接就需要有一个叫做桥函数的东西,ECALL桥能让APP可以调用桥函数间接调用Enclave中写好的API函数。反向的有个叫OCALL桥的东西。桥函数上承载着两个世界间传递的参数,而且ECALL中,Enclave并不信任APP传给Enclave的ECALL参数,所以需要参数的消毒检查。OCALL有点类似。
既然要开发程序,就要用到SDK(我这里是把可信Enclave使用的SDK称为SDK,这也符合Intel的叫法,另一种理解是SDK包括给不可信APP使用的PSW、给可信Enclave使用的SDK、桥函数)和PSW,这两个都是Intel提供的(linux下见: github.com/intel/linux-sgx)。由于Enclave要保证自己内部开发的函数尽可能不会离开Enclave,所以Enclave内部用的SDK都是用静态库链接,除非万不得已,比如系统调用等,那么就得同OCALL桥到不可信世界完成任务。然后顶多是启发式的对OCALL返回值进行检查(而且目前Intel并无消毒检查,除非Enclave开发者自己做检查)。
PSW、SDK一部分功能是用于我们传统的那种为了具有某个功能而开发的函数,还有一部分是对CPU提供的SGX功能指令的包装,主要用于SGX特性的支持,为了让你真正和CPU沟通,并获得SGX特性支持。SGX特性是通过CPU向外面提供Ring0指令和Ring3指令,其中Ring0指令ENCLS主要有一些比如创建Enclave这种生命周期管理、页权限管理的指令。Ring3指令ENCLU主要是让控制流能够在两个世界之间流动,比如进出Enclave这种。这一块的细节可以看《SGX软件栈》。
2. SGX访问控制是什么?
SGX访问控制是说对Enclave安全内存进行访问控制,不能让攻击者非法访问敏感内存。这主要还是通过CPU内部实现的。有SGX特性CPU能够让不可信APP只有满足进入它的Enclave的条件时才能放行,而且Enclave A和Enclave B之间是互相不可访问的。这种逻辑是CPU里面的EPCM和内存RAM中被CPU定义为EPC里面的SECS结构体、TCS结构体这些单元连动完成的。《SGX技术的分析和研究》有介绍具体有哪几则访问控制。
3. CPU里面SGX长什么样子?
4. SGX内存分配方式?
一.准备工作
1.1 前提条件
[必须] 你的开发环境必须安装了 Intel SGX SDK
。在安装时安装到了 /opt/intel/sgxsdk
。
[可选] 开发环境主机 CPU 支持 SGX;若不支持,可采用模拟器编译运行(本文实际在硬件支持条件下测试)。
1.2 SGX IDE
本文在ubuntu22.04
中执行,主要是参考了Sample
中工程代码的组织方法。
1.3 基本原理
在演示代码之前,有必要先了解下 SGX 程序最基本的原理:
-
SGX应用由两部分组成:
1. untrusted 不可信区:代码和数据运行在普通非加密内存区域,程序 main 入口必须在非可信区;上图中的 main() 和 bar() 函数均在非可信区。
2. truested 可信区:代码和数据运行在硬件加密内存区域,此区域由 CPU 创建的且只有CPU有权限访问; 上图中的 helloworld() 和 foo() 函数运行在可信区。 -
非可信区只能通过 ECALL 函数调用可信区内的函数。
-
可信区只能通过 OCALL 函数调用非可信区的函数。
-
ECALL 函数和 OCALL 函数通过 EDL 文件声明。
二.程序设计
第一个SGX 程序: HelloWorld
2.1 目录结构
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.edl: EDL(Enclave Description Language)文件。
- Enclave.lds: Enclave linker script。
- Enclave_private.pem: enclave.so 的签名私钥。
- Enclave.config.xml: Enclave 配置文件,如堆栈大小、是否支持 Debug 等。
- Enclave.h & Enclave.cpp: 应用安全区代码实现。
Include
目录是不可信代码和可信代码共享的头文件。
2.2 源码设计
2.2.1 Encalve/Enclave.edl:Enclave Description Language
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 内容。
2.2.2 Enclave/Enclave.lds: Enclave linker script
enclave.so
{
global:
g_global_data_sim;
g_global_data;
enclave_entry;
g_peak_heap_used;
local:
*;
};
2.2.3 Enclave/Enclave.config.xml: Enclave 配置文件,如堆栈大小、是否支持 Debug 等
<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>
这个配置文件是用来定义Intel SGX信任域(Enclave)的各种参数的。下面是对每一行代码的详细分析,包括它们的功能和用途:
<EnclaveConfiguration>
开始标签:表示SGX信任域配置文件的开始。<ProdID>0</ProdID>
ProdID:表示产品ID。这个值可以由开发者定义,用于区分不同的产品版本。在这里,它被设置为0,表示默认产品ID。<ISVSVN>0</ISVSVN>
ISVSVN:表示独立软件供应商(ISV)的安全版本号。这个值由ISV管理,用于跟踪代码的安全更新。这里设置为0,表示初始的安全版本。<StackMaxSize>0x40000</StackMaxSize>
StackMaxSize:堆栈的最大大小,以十六进制表示。这里设置为0x40000(262144字节或256KB),表示信任域的堆栈内存上限为256KB
。<HeapMaxSize>0x100000</HeapMaxSize>
HeapMaxSize:堆的最大大小,以十六进制表示。这里设置为0x100000(1048576字节或1MB),表示信任域的堆内存上限为1MB
。<TCSNum>10</TCSNum>
TCSNum:线程控制结构(TCS)的数量。这里设置为10,表示可以有10个并发线程在信任域中运行。<TCSPolicy>1</TCSPolicy>
TCSPolicy:TCS的策略。一般情况下,设置为1表示使用默认策略。<DisableDebug>0</DisableDebug>
DisableDebug:定义是否禁用调试。设置为0表示启用调试模式,方便开发调试。但建议在发布版本中设置为1,这样信任域将无法被调试,增强安全性。<MiscSelect>0</MiscSelect>
MiscSelect:选择Miscellaneous功能。这里设置为0,表示未选择任何特定的Misc功能。<MiscMask>0xFFFFFFFF</MiscMask>
MiscMask:定义可用Miscellaneous功能的掩码。0xFFFFFFFF表示允许所有Misc功能。</EnclaveConfiguration>
结束标签:表示SGX信任域配置文件的结束。
总的来说,这个配置文件定义了一个SGX信任域的各种参数和属性,包括产品ID、安全版本号、堆栈和堆内存大小、并发线程数量和策略、调试功能以及Misc功能掩码等。通过设置这些参数,开发者可以控制信任域的内存使用、并发控制以及调试属性,确保信任域在运行时具有所需的功能和安全性。
2.2.4 Enclave/Enclave.h
这个头文件内容基本为空的。
#ifndef _ENCLAVE_H_
#define _ENCLAVE_H_
#endif
2.2.5 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';
}
2.2.6 Enclave/Enclave_private.pem:enclave.so 的签名私钥
基于openssl命令生成私钥:
openssl genrsa -out Enclave/Enclave_private.pem -3 3072
2.2.7 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_ */
2.2.8 App/App.cpp
#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;
}
2.3 Makefile
随便在Sample中找一个工程中的Makefile,稍加改改就能用!!!
######## SGX SDK Settings ########
# 设置 SGX SDK 的路径、模式、架构和调试选项
# SGX SDK 安装路径
SGX_SDK ?= /opt/intel/sgxsdk
# SGX 模式:硬件模式(HW)
SGX_MODE ?= HW
# SGX 架构:64 位(x64)
SGX_ARCH ?= x64
# SGX 调试模式:启用(1)
SGX_DEBUG ?= 1
# 判断系统的位数,如果是 32 位,设置架构为 x86
ifeq ($(shell getconf LONG_BIT), 32)
SGX_ARCH := x86
# 如果编译器标志中包含 -m32,也设置架构为 x86
else ifeq ($(findstring -m32, $(CXXFLAGS)), -m32)
SGX_ARCH := x86
endif
# 根据架构选择编译器标志、库路径和工具路径
ifeq ($(SGX_ARCH), x86)
SGX_COMMON_FLAGS := -m32 # 设置 32 位编译标志
SGX_LIBRARY_PATH := $(SGX_SDK)/lib # 32 位库路径
SGX_ENCLAVE_SIGNER := $(SGX_SDK)/bin/x86/sgx_sign # 使用 32 位签名工具
SGX_EDGER8R := $(SGX_SDK)/bin/x86/sgx_edger8r # 使用 32 位 Edger8r 工具
else
SGX_COMMON_FLAGS := -m64 # 设置 64 位编译标志
SGX_LIBRARY_PATH := $(SGX_SDK)/lib64 # 64 位库路径
SGX_ENCLAVE_SIGNER := $(SGX_SDK)/bin/x64/sgx_sign # 使用 64 位签名工具
SGX_EDGER8R := $(SGX_SDK)/bin/x64/sgx_edger8r # 使用 64 位 Edger8r 工具
endif
# 检查是否同时设置了调试模式和预发布模式
ifeq ($(SGX_DEBUG), 1)
ifeq ($(SGX_PRERELEASE), 1)
$(error Cannot set SGX_DEBUG and SGX_PRERELEASE at the same time!!)
# 如果两个都设置了,报错 "不能同时设置调试模式和预发布模式"
endif
endif
# 根据是否启用调试模式设置编译标志
ifeq ($(SGX_DEBUG), 1)
SGX_COMMON_FLAGS += -O0 -g # 调试模式:不进行优化,包含调试信息
else
SGX_COMMON_FLAGS += -O2 # 生产模式:优化代码
endif
# 设置更多的编译器警告标志,确保代码质量
SGX_COMMON_FLAGS += -Wall -Wextra -Winit-self -Wpointer-arith -Wreturn-type \
-Waddress -Wsequence-point -Wformat-security \
-Wmissing-include-dirs -Wfloat-equal -Wundef -Wshadow \
-Wcast-align -Wcast-qual -Wconversion -Wredundant-decls
SGX_COMMON_CFLAGS := $(SGX_COMMON_FLAGS) -Wjump-misses-init -Wstrict-prototypes -Wunsuffixed-float-constants # C 编译标志
SGX_COMMON_CXXFLAGS := $(SGX_COMMON_FLAGS) -Wnon-virtual-dtor -std=c++11 # C++ 编译标志,使用 C++11 标准
######## App Settings ########
# 应用程序设置
ifneq ($(SGX_MODE), HW)
Urts_Library_Name := sgx_urts_sim # 如果不是硬件模式,使用仿真库
else
Urts_Library_Name := sgx_urts # 如果是硬件模式,使用真实库
endif
# 指定应用程序的源文件和包含路径
App_Cpp_Files := App/App.cpp $(wildcard App/TrustedLibrary/*.cpp)
App_Include_Paths := -IApp -I$(SGX_SDK)/include
# 设置应用程序的编译标志
App_C_Flags := -fPIC -Wno-attributes $(App_Include_Paths)
# 根据不同的配置模式,设置不同的宏定义
# Three configuration modes - Debug, prerelease, release
# Debug - Macro DEBUG enabled.
# Prerelease - Macro NDEBUG and EDEBUG enabled.
# Release - Macro NDEBUG enabled.
ifeq ($(SGX_DEBUG), 1)
App_C_Flags += -DDEBUG -UNDEBUG -UEDEBUG
else ifeq ($(SGX_PRERELEASE), 1)
App_C_Flags += -DNDEBUG -DEDEBUG -UDEBUG
else
App_C_Flags += -DNDEBUG -UEDEBUG -UDEBUG
endif
App_Cpp_Flags := $(App_C_Flags)
App_Link_Flags := -L$(SGX_LIBRARY_PATH) -l$(Urts_Library_Name) -lpthread
# 生成对象文件的规则
App_Cpp_Objects := $(App_Cpp_Files:.cpp=.o)
# 指定生成的应用程序名称
App_Name := app
######## Enclave Settings ########
# 设置信任域版本脚本文件根据调试模式与硬件模式判断
Enclave_Version_Script := Enclave/Enclave.lds
ifeq ($(SGX_MODE), HW)
ifneq ($(SGX_DEBUG), 1)
ifneq ($(SGX_PRERELEASE), 1)
# 硬件发布模式下使用的版本脚本文件
# Choose to use 'Enclave.lds' for HW release mode
Enclave_Version_Script = Enclave/Enclave.lds
endif
endif
endif
# 仿真模式与硬件模式下使用不同的库
ifneq ($(SGX_MODE), HW)
Trts_Library_Name := sgx_trts_sim # 仿真模式库
Service_Library_Name := sgx_tservice_sim # 仿真模式服务库
else
Trts_Library_Name := sgx_trts # 硬件模式库
Service_Library_Name := sgx_tservice # 硬件模式服务库
endif
Crypto_Library_Name := sgx_tcrypto # 加密库
# 指定信任域的源文件和包含路径
Enclave_Cpp_Files := Enclave/Enclave.cpp $(wildcard Enclave/TrustedLibrary/*.cpp)
Enclave_Include_Paths := -IEnclave -I$(SGX_SDK)/include -I$(SGX_SDK)/include/libcxx -I$(SGX_SDK)/include/tlibc
# 设置信任域的编译标志
Enclave_C_Flags := -nostdinc -fvisibility=hidden -fpie -fstack-protector -fno-builtin-printf $(Enclave_Include_Paths)
Enclave_Cpp_Flags := $(Enclave_C_Flags) -nostdinc++
# 启用安全链接选项
Enclave_Security_Link_Flags := -Wl,-z,relro,-z,now,-z,noexecstack
# 生成信任域的正确链接规则
# 按步骤链接信任库
Enclave_Link_Flags := $(Enclave_Security_Link_Flags) \
-Wl,--no-undefined -nostdlib -nodefaultlibs -nostartfiles -L$(SGX_LIBRARY_PATH) \
-Wl,--whole-archive -l$(Trts_Library_Name) -Wl,--no-whole-archive \
-Wl,--start-group -lsgx_tstdc -lsgx_tcxx -l$(Crypto_Library_Name) -l$(Service_Library_Name) -Wl,--end-group \
-Wl,-Bstatic -Wl,-Bsymbolic -Wl,--no-undefined \
-Wl,-pie,-eenclave_entry -Wl,--export-dynamic \
-Wl,--defsym,__ImageBase=0 \
-Wl,--version-script=$(Enclave_Version_Script)
# 指定信任域生成的对象文件
Enclave_Cpp_Objects := $(Enclave_Cpp_Files:.cpp=.o)
# 指定生成的信任域库名
Enclave_Name := enclave.so
Signed_Enclave_Name := enclave.signed.so
# 配置文件和测试密钥文件
Enclave_Config_File := Enclave/Enclave.config.xml
Enclave_Key := Enclave/Enclave_private.pem
# 生成不同的构建模式名称
ifeq ($(SGX_MODE), HW)
ifeq ($(SGX_DEBUG), 1)
Build_Mode = HW_DEBUG
else ifeq ($(SGX_PRERELEASE), 1)
Build_Mode = HW_PRERELEASE
else
Build_Mode = HW_RELEASE
endif
else
ifeq ($(SGX_DEBUG), 1)
Build_Mode = SIM_DEBUG
else ifeq ($(SGX_PRERELEASE), 1)
Build_Mode = SIM_PRERELEASE
else
Build_Mode = SIM_RELEASE
endif
endif
.PHONY: all run target
all: .config_$(Build_Mode)_$(SGX_ARCH)
@$(MAKE) target
ifeq ($(Build_Mode), HW_RELEASE)
target: $(App_Name) $(Enclave_Name)
@echo "The project has been built in release hardware mode."
@echo "Please sign the $(Enclave_Name) first with your signing key before you run the $(App_Name) to launch and access the enclave."
@echo "To sign the enclave use the command:"
@echo " $(SGX_ENCLAVE_SIGNER) sign -key <your key> -enclave $(Enclave_Name) -out <$(Signed_Enclave_Name)> -config $(Enclave_Config_File)"
@echo "You can also sign the enclave using an external signing tool."
@echo "To build the project in simulation mode set SGX_MODE=SIM. To build the project in prerelease mode set SGX_PRERELEASE=1 and SGX_MODE=HW."
else
target: $(App_Name) $(Signed_Enclave_Name)
ifeq ($(Build_Mode), HW_DEBUG)
@echo "The project has been built in debug hardware mode."
else ifeq ($(Build_Mode), SIM_DEBUG)
@echo "The project has been built in debug simulation mode."
else ifeq ($(Build_Mode), HW_PRERELEASE)
@echo "The project has been built in pre-release hardware mode."
else ifeq ($(Build_Mode), SIM_PRERELEASE)
@echo "The project has been built in pre-release simulation mode."
else
@echo "The project has been built in release simulation mode."
endif
endif
# 运行目标规则,首先构建所有目标,然后运行应用程序,除非是在硬件发布模式下。
run: all
ifneq ($(Build_Mode), HW_RELEASE)
@$(CURDIR)/$(App_Name)
@echo "RUN => $(App_Name) [$(SGX_MODE)|$(SGX_ARCH), OK]"
endif
# 配置文件生成规则,如果构建模式和架构改变,重新生成配置
.config_$(Build_Mode)_$(SGX_ARCH):
@rm -f .config_* $(App_Name) $(Enclave_Name) $(Signed_Enclave_Name) $(App_Cpp_Objects) App/Enclave_u.* $(Enclave_Cpp_Objects) Enclave/Enclave_t.*
@touch .config_$(Build_Mode)_$(SGX_ARCH)
######## App Objects ########
# 生成 Enclave_u.h,如果 Enclave.edl 改变
App/Enclave_u.h: $(SGX_EDGER8R) Enclave/Enclave.edl
@cd App && $(SGX_EDGER8R) --untrusted ../Enclave/Enclave.edl --search-path ../Enclave --search-path $(SGX_SDK)/include
@echo "GEN => $@"
App/Enclave_u.c: App/Enclave_u.h
# 编译 Enclave_u.c 生成对象文件
App/Enclave_u.o: App/Enclave_u.c
@$(CC) $(SGX_COMMON_CFLAGS) $(App_C_Flags) -c $< -o $@
@echo "CC <= $<"
# 编译应用程序源文件生成对象文件
App/%.o: App/%.cpp App/Enclave_u.h
@$(CXX) $(SGX_COMMON_CXXFLAGS) $(App_Cpp_Flags) -c $< -o $@
@echo "CXX <= $<"
# 链接对象文件生成应用程序可执行文件
$(App_Name): App/Enclave_u.o $(App_Cpp_Objects)
@$(CXX) $^ -o $@ $(App_Link_Flags)
@echo "LINK => $@"
######## Enclave Objects ########
# 生成 Enclave_t.h,如果 Enclave.edl 改变
Enclave/Enclave_t.h: $(SGX_EDGER8R) Enclave/Enclave.edl
@cd Enclave && $(SGX_EDGER8R) --trusted ../Enclave/Enclave.edl --search-path ../Enclave --search-path $(SGX_SDK)/include
@echo "GEN => $@"
Enclave/Enclave_t.c: Enclave/Enclave_t.h
# 编译 Enclave_t.c 生成对象文件
Enclave/Enclave_t.o: Enclave/Enclave_t.c
@$(CC) $(SGX_COMMON_CFLAGS) $(Enclave_C_Flags) -c $< -o $@
@echo "CC <= $<"
# 编译信任域源文件生成对象文件
Enclave/%.o: Enclave/%.cpp
@$(CXX) $(SGX_COMMON_CXXFLAGS) $(Enclave_Cpp_Flags) -c $< -o $@
@echo "CXX <= $<"
# 生成信任域对象文件
$(Enclave_Cpp_Objects): Enclave/Enclave_t.h
# 链接对象文件生成信任域共享库文件
$(Enclave_Name): Enclave/Enclave_t.o $(Enclave_Cpp_Objects)
@$(CXX) $^ -o $@ $(Enclave_Link_Flags)
@echo "LINK => $@"
# 使用测试私钥签名信任域共享库文件
$(Signed_Enclave_Name): $(Enclave_Name)
ifeq ($(wildcard $(Enclave_Key)),)
@echo "There is no enclave test key<Enclave_private_test.pem>."
@echo "The project will generate a key<Enclave_private_test.pem> for test."
@openssl genrsa -out $(Enclave_Key) -3 3072
endif
@$(SGX_ENCLAVE_SIGNER) sign -key $(Enclave_Key) -enclave $(Enclave_Name) -out $@ -config $(Enclave_Config_File)
@echo "SIGN => $@"
# clean 目标,删除生成的文件
.PHONY: clean
clean:
@rm -f .config_* $(App_Name) $(Enclave_Name) $(Signed_Enclave_Name) $(App_Cpp_Objects) App/Enclave_u.* $(Enclave_Cpp_Objects) Enclave/Enclave_t.*
2.4 编译 & 运行
比较简单,直接在项目目录下执行 make
,在项目根目录下会生成一个名为 app
的 Binary,运行这个 app
:
$ make
make[1]: 进入目录“/home/hututu/myproject/sgx/helloworld”
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
INFO: Enclave configuration 'MiscSelect' and 'MiscSelectMask' will prevent enclave from using dynamic features. To use the dynamic features on SGX2 platform, suggest to set MiscMask[0]=0 and MiscSelect[0]=1.
The required memory is 3997696B.
The required memory is 0x3d0000, 3904 KB.
handle_compatible_metadata: Overwrite with metadata version 0x100000004
Succeed.
SIGN => enclave.signed.so
The project has been built in debug hardware mode.
make[1]: 离开目录“/home/hututu/myproject/sgx/helloworld”
$ ./app
Hello world
Info: SampleEnclave successfully returned.
Enter a character before exit ...
编译基本流程(Makefile):
- 通过
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_sing
工具签名可信动态链接库(enclave.signed.so
); - 结束。
编译后的代码目录结构:
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
2.5 总结
即便最简单的 SGX HelloWold
也比较复杂,当然“安全性”和“成本”(技术壁垒门槛、开发成本、维护成本、物料成本等)总是成正比的,和“效率”成反比的。希望这篇文章对那些想入门开发 SGX 应用的用户有所帮助。
三.参考链接
https://developer.aliyun.com/article/740793
Intel SGX入门(一)——背景篇_sgx的psw是什么-CSDN博客
四. 感谢支持
完结撒花!后续将持续输出,形成Intel SGX的系列教程,并结合密码学算法设计更复杂功能。希望看到这里的小伙伴能点个关注,也欢迎志同道合的小伙伴一起广泛交流。
码字实属不易,如果本文对你有10分帮助,就赏个10分把,感谢各位大佬支持!