文章目录
下面将给出一些sgx源码包中的示例分析,从中学习SGX的基本使用方法:
关于 SGX 开发运行环境的搭建可参考之前的一篇博客:【SGX系列教程】(一)。
示例一. Cxx11SGXDemo
1.1 README
-----------------------
Purpose of Cxx11SGXDemo
-----------------------
The project demonstrates serveral C++11 features inside the Enclave:
- lambda expressions;
- rvalue references and move semantics;
- automatic type deduction with auto and decltype;
- nullptr type;
- strongly typed enum classes;
- Range-based for statements;
- static_assert keyword for compile-time assertion;
- initializer lists and uniform initialization syntax;
- New virtual function controls: override, final, default, and delete;
- delegating constructors;
- new container classes (unordered_set, unordered_map, unordered_multiset, and unordered_multimap);
- tuple class;
- function object wrapper;
- atomic, mutexes, condition_variables;
- new smart pointer classes: shared_ptr, unique_ptr;
- new c++ algorithms: all_of, any_of, none_of;
- variadic templates;
- SFINAE;
---------------------------------------------
How to Build/Execute the C++11 sample program
---------------------------------------------
1. Install Intel(R) Software Guard Extensions (Intel(R) SGX) SDK for Linux* OS
2. Enclave test key(two options):
a. Install openssl first, then the project will generate a test key<Enclave_private_test.pem> automatically when you build the project.
b. Rename your test key(3072-bit RSA private key) to <Enclave_private_test.pem> and put it under the <Enclave> folder.
3. Make sure your environment is set:
$ source ${sgx-sdk-install-path}/environment
4. Build the project with the prepared Makefile:
a. Hardware Mode, Debug build:
$ make
b. Hardware Mode, Pre-release build:
$ make SGX_PRERELEASE=1 SGX_DEBUG=0
c. Hardware Mode, Release build:
$ make SGX_DEBUG=0
d. Simulation Mode, Debug build:
$ make SGX_MODE=SIM
e. Simulation Mode, Pre-release build:
$ make SGX_MODE=SIM SGX_PRERELEASE=1 SGX_DEBUG=0
f. Simulation Mode, Release build:
$ make SGX_MODE=SIM SGX_DEBUG=0
5. Execute the binary directly:
$ ./app
6. Remember to "make clean" before switching build mode
-------------------------------------------------
Launch token initialization
-------------------------------------------------
If using libsgx-enclave-common or sgxpsw under version 2.4, an initialized variable launch_token needs to be passed as the 3rd parameter of API sgx_create_enclave. For example,
sgx_launch_token_t launch_token = {
0};
sgx_create_enclave(ENCLAVE_FILENAME, SGX_DEBUG_FLAG, launch_token, NULL, &global_eid, NULL);
工程的主要功能是测试c++11的一些特性在enclave中的执行情况,根据上面README
中的内容总结一下Cxx11SGXDemo
的执行步骤如下:
- 首先需要安装
SGX sdk
,以及等等对SGX的支持测试,具体可参考之前的一篇博客:【SGX系列教程】(一); - 安装
openssl
,目的是为enclave.so
生成签名私钥:
openssl genrsa -out Enclave/Enclave_private_test.pem -3 3072
- 确保sdk路径生效(source),基本在安装sdk时会指定安装路径为:
/opt/intel/sgxsdk
; - 使用Makefile构建工程:
- 硬件模式, Debug build:
make
- 硬件模式, Pre-release build:
make SGX_PRERELEASE=1 SGX_DEBUG=0
- 硬件模式, Release build:
make SGX_DEBUG=0
- 仿真模式, Debug build:
make SGX_MODE=SIM
- 仿真模式, Pre-release build:
make SGX_MODE=SIM SGX_PRERELEASE=1 SGX_DEBUG=0
- 仿真模式, Release build:
make SGX_MODE=SIM SGX_DEBUG=0
- 硬件模式, Debug build:
- 执行:
./app
- 在切换编译模式之前记得
make clean
!
1.2 重点代码分析
SGX工程的整个构建流程在上一篇文章中已经给出给出了详细教程:【SGX系列教程】(二)第一个 SGX 程序: HelloWorld,因此在此处仅给出部分重点代码分析,主要讲清楚enclave如何支持c++11的运行,并提供哪些支持,其他通用代码不做展开分析。
文件目录如下图,主要关注图中框出来的两个文件,其中Enclave文件夹中的Libxx.cpp提供enclave中函数执行功能,是系统的核心功能文件(将在后面详细分析文件功能),并封装成ECALL函数由外部调用;而App文件夹中的Libxx.cpp负责调用上述的ECALL函数。
整个程序调用过程可先总结为下图:
1.2.1 App/App.cpp
#include <stdio.h> // 标准输入输出库,提供如 printf 等函数
#include <string.h> // 字符串处理函数库
#include <assert.h> // 断言函数库,用于调试
# include <unistd.h> // 提供对 POSIX 操作系统 API 的访问,包括文件和进程
# include <pwd.h> // 提供对用户密码信息的访问
# define MAX_PATH FILENAME_MAX // 定义 MAX_PATH 为系统最大文件名长度
#include "sgx_urts.h" // SGX 用户运行时库,提供 SGX 相关 API
#include "App.h" // 应用程序相关的头文件
#include "Enclave_u.h" // Untrusted Enclave 的接口头文件
/* 全局 EID 由多个线程共享 */
sgx_enclave_id_t global_eid = 0; // 定义一个全局的 SGX enclave ID
/* 错误代码结构体 */
typedef struct _sgx_errlist_t {
sgx_status_t err; // 错误码
const char *msg; // 错误信息
const char *sug; // 错误建议
} sgx_errlist_t;
/* 由 sgx_create_enclave 返回的错误代码 */
static sgx_errlist_t sgx_errlist[] = {
// 错误列表,包含常见的 SGX 错误码及其信息
{
SGX_ERROR_UNEXPECTED,
"Unexpected error occurred.",
NULL
},
{
SGX_ERROR_INVALID_PARAMETER,
"Invalid parameter.",
NULL
},
{
SGX_ERROR_OUT_OF_MEMORY,
"Out of memory.",
NULL
},
{
SGX_ERROR_ENCLAVE_LOST,
"Power transition occurred.",
"Please refer to the sample \"PowerTransition\" for details."
},
{
SGX_ERROR_INVALID_ENCLAVE,
"Invalid enclave image.",
NULL
},
{
SGX_ERROR_INVALID_ENCLAVE_ID,
"Invalid enclave identification.",
NULL
},
{
SGX_ERROR_INVALID_SIGNATURE,
"Invalid enclave signature.",
NULL
},
{
SGX_ERROR_OUT_OF_EPC,
"Out of EPC memory.",
NULL
},
{
SGX_ERROR_NO_DEVICE,
"Invalid SGX device.",
"Please make sure SGX module is enabled in the BIOS, and install SGX driver afterwards."
},
{
SGX_ERROR_MEMORY_MAP_CONFLICT,
"Memory map conflicted.",
NULL
},
{
SGX_ERROR_INVALID_METADATA,
"Invalid enclave metadata.",
NULL
},
{
SGX_ERROR_DEVICE_BUSY,
"SGX device was busy.",
NULL
},
{
SGX_ERROR_INVALID_VERSION,
"Enclave version was invalid.",
NULL
},
{
SGX_ERROR_INVALID_ATTRIBUTE,
"Enclave was not authorized.",
NULL
},
{
SGX_ERROR_ENCLAVE_FILE_ACCESS,
"Can't open enclave file.",
NULL
},
{
SGX_ERROR_NDEBUG_ENCLAVE,
"The enclave is signed as product enclave, and can not be created as debuggable enclave.",
NULL
},
{
SGX_ERROR_MEMORY_MAP_FAILURE,
"Failed to reserve memory for the enclave.",
NULL
},
};
/* 打印错误信息 */
void print_error_message(sgx_status_t ret)
{
size_t idx = 0; // 循环索引
size_t ttl = sizeof sgx_errlist / sizeof sgx_errlist[0]; // 错误列表的总长度
// 遍历错误列表,查找对应的错误
for (idx = 0; idx < ttl; idx++) {
if (ret == sgx_errlist[idx].err) {
// 找到对应的错误码
if (NULL != sgx_errlist[idx].sug) // 如果存在建议,打印建议
printf("Info: %s\n", sgx_errlist[idx].sug);
printf("Error: %s\n", sgx_errlist[idx].msg); // 打印错误信息
break;
}
}
if (idx == ttl) // 如果遍历后未找到相应错误码,打印未预期的错误发生
printf("Error: Unexpected error occurred.\n");
}
/* 初始化信任域:
* 调用 sgx_create_enclave 来初始化一个信任域实例
*/
int initialize_enclave(void)
{
sgx_status_t ret = SGX_ERROR_UNEXPECTED; // 初始化错误状态
/* 调用 sgx_create_enclave 来初始化一个信任域实例 */
/* 调试支持:将第二个参数设置为 1 */
ret = sgx_create_enclave(ENCLAVE_FILENAME, SGX_DEBUG_FLAG, NULL, NULL, &global_eid, NULL);
if (ret != SGX_SUCCESS) {
// 如果初始化失败,打印错误信息
print_error_message(ret);
return -1; // 返回错误状态
}
return 0; // 成功初始化,返回0
}
/* OCall 函数,由enclave.cpp中函数调用:ocall_print_string(buf) */
void ocall_print_string(const char *str)
{
printf("%s", str);
}
/* 主应用程序main入口 */
int SGX_CDECL main(int argc, char *argv[])
{
(void)(argc); // 防止未使用参数警告
(void)(argv);
/* 初始化信任域 */
if (initialize_enclave() < 0) {
// 如果信任域初始化失败,打印信息并退出
printf("Enter a character before exit ...\n");
getchar(); // 等待用户输入
return -1;
}
/* 使用信任库函数,定义在App/TrustedLibrary/Libcxx.cpp文件中*/
ecall_libcxx_functions(); // 调用 ECALL 函数,利用受信任的 C++11库
/* 销毁信任域 */
sgx_destroy_enclave(global_eid); // 销毁信任域实例
printf("Info: Cxx11DemoEnclave successfully returned.\n");
// 打印成功信息
//printf("Enter a character before exit ...\n");
//getchar(); // 此行代码被注释掉,仅供调试时使用
return 0; // 返回0,表示程序正常退出
}
上述实现了一个简单的SGX应用程序,主要功能包括:初始化SGX信任域、调用受信任代码、打印错误信息和与主机间通信。用于展示如何在C++中利用Intel SGX进行安全函数的调用和管理。可以看出函数ecall_libcxx_functions()
是核心功能函数。下面介绍该函数。
1.2.2 App/TrustedLibrary/Libcxx.cpp
#include <stdio.h>
#include "../App.h"
#include "Enclave_u.h"
#include <thread>
// 该函数调用了标准的C++11功能。
// 这个函数是互斥锁演示的一部分
void demo_counter_without_mutex()
{
sgx_status_t ret = SGX_ERROR_UNEXPECTED;
// ecall调用不使用保护的互斥锁计数演示
ret = ecall_mutex_demo_no_protection(global_eid);
if (ret != SGX_SUCCESS)
abort(); // 如果调用失败,程序终止
}
// 这个函数是互斥锁演示的一部分
void demo_counter_mutex()
{
sgx_status_t ret = SGX_ERROR_UNEXPECTED;
// ecall调用使用保护的互斥锁计数演示
ret = ecall_mutex_demo(global_eid);
if (ret != SGX_SUCCESS)
abort(); // 如果调用失败,程序终止
}
// 这个函数是条件变量演示的处理线程使用
void demo_cond_var_run()
{
sgx_status_t ret = SGX_ERROR_UNEXPECTED;
// ecall调用条件变量运行演示
ret = ecall_condition_variable_run(global_eid);
if (ret != SGX_SUCCESS)
abort(); // 如果调用失败,程序终止
}
// 这个函数是条件变量演示的加载线程使用
void demo_cond_var_load()
{
sgx_status_t ret = SGX_ERROR_UNEXPECTED;
// ecall调用条件变量加载演示
ret = ecall_condition_variable_load(global_eid);
if (ret != SGX_SUCCESS)
abort(); // 如果调用失败,程序终止
}
// 示例展示了C++11库和编译器特性,被App.cpp中main函数调用
void ecall_libcxx_functions(void)
{
sgx_status_t ret = SGX_ERROR_UNEXPECTED;
// lambda函数特性的示例
ret = ecall_lambdas_demo(global_eid);
if (ret != SGX_SUCCESS)
abort(); // 如果调用失败,程序终止
// auto特性的示例
ret = ecall_auto_demo(global_eid);
if (ret != SGX_SUCCESS)
abort(); // 如果调用失败,程序终止
// decltype特性的示例
ret = ecall_decltype_demo(global_eid);
if (ret != SGX_SUCCESS)
abort(); // 如果调用失败,程序终止
// 强类型枚举特性的示例
ret = ecall_strongly_typed_enum_demo(global_eid);
if (ret != SGX_SUCCESS)
abort(); // 如果调用失败,程序终止
// 基于范围的for循环特性的示例
ret = ecall_range_based_for_loops_demo(global_eid);
if (ret != SGX_SUCCESS)
abort(); // 如果调用失败,程序终止
// 静态断言特性的示例
ret = ecall_static_assert_demo(global_eid);
if (ret != SGX_SUCCESS)
abort(); // 如果调用失败,程序终止
// 虚函数控制特性的示例:override, final, default, and delete
ret = ecall_virtual_function_control_demo(global_eid);
if (ret != SGX_SUCCESS)
abort(); // 如果调用失败,程序终止
// 委派构造函数特性的示例
ret = ecall_delegating_constructors_demo(global_eid);
if (ret != SGX_SUCCESS)
abort(); // 如果调用失败,程序终止
// std::function特性的示例
ret = ecall_std_function_demo(global_eid);
if (ret != SGX_SUCCESS)
abort(); // 如果调用失败,程序终止
// 算法特性的示例(std::all_of, std::any_of, std::none_of)
ret = ecall_cxx11_algorithms_demo(global_eid);
if (ret != SGX_SUCCESS)
abort(); // 如果调用失败,程序终止
// 可变参数模板特性的示例
ret = ecall_variadic_templates_demo(global_eid);
if (ret != SGX_SUCCESS)
abort(); // 如果调用失败,程序终止
// SFINAE特性的示例
ret = ecall_SFINAE_demo(global_eid);
if (ret != SGX_SUCCESS)
abort(); // 如果调用失败,程序终止
// 初始化列表特性的示例
ret = ecall_initializer_list_demo(global_eid);
if (ret != SGX_SUCCESS)
abort(); // 如果调用失败,程序终止
// 右值引用特性的示例
ret = ecall_rvalue_demo(global_eid);
if (ret != SGX_SUCCESS)
abort(); // 如果调用失败,程序终止
// nullptr特性的示例
ret = ecall_nullptr_demo(global_eid);
if (ret != SGX_SUCCESS)
abort(); // 如果调用失败,程序终止
// 枚举类特性的示例
ret = ecall_enum_class_demo(global_eid);
if (ret != SGX_SUCCESS)
abort(); // 如果调用失败,程序终止
// 新容器类特性的示例(unordered_set, unordered_map, unordered_multiset, 和 unordered_multimap)
ret = ecall_new_container_classes_demo(global_eid);
if (ret != SGX_SUCCESS)
abort(); // 如果调用失败,程序终止
// 元组特性的示例
ret = ecall_tuple_demo(global_eid);
if (ret != SGX_SUCCESS)
abort(); // 如果调用失败,程序终止
// shared_ptr特性的示例
ret = ecall_shared_ptr_demo(global_eid);
if (ret != SGX_SUCCESS)
abort(); // 如果调用失败,程序终止
// 原子操作特性的示例
ret = ecall_atomic_demo(global_eid);
if (ret != SGX_SUCCESS)