【SGX系列教程】(四)Intel-SGX 官方示例分析(SampleCode)——LocalAttestation

文章目录

  • 一.LocalAttestation原理介绍
    • 1.1本地认证原理
    • 1.2 本地认证基本流程
    • 1.3 本地认证核心原理
  • 二.源码分析
    • 2.1 README
      • 2.1.1 编译流程
      • 2.1.2 执行流程(双进程执行 or 单进程执行,在后面执行部分有展示效果)
      • 2.1.3 如何获取已签名的Enclave的MRSIGNER
    • 2.2 重点代码分析
      • 2.2.1 App文件夹
        • 2.2.1.1 App/App.cpp
        • 2.2.1.2 App/UntrustedEnclaveMessageExchange.cpp
      • 2.2.2 AppInitiator文件夹
        • 2.2.2.1 AppInitiator/App.cpp
        • 2.2.2.2 AppInitiator/UntrustedEnclaveMessageExchange.cpp
      • 2.2.3 AppResponder文件夹
        • 2.2.3.1 AppResponder/App.cpp
        • 2.2.3.2 AppResponder/CPServer.cpp
        • 2.2.3.3 AppResponder/CPTask.cpp
      • 2.2.4 EnclaveInitiator文件夹
      • 2.2.5 EnclaveResponder文件夹
    • 2.3 编译执行
      • 2.3.1 Makefile文件分析
      • 2.3.2 执行
    • 2.4 总结
  • 三.参考文献
  • 四.感谢支持

    下面将给出一些sgx源码包中的示例LocalAttestation分析,从中学习本地认证和SGX的基本使用方法:关于 SGX 开发运行环境的搭建可参考之前的一篇博客:【SGX系列教程】(一)。

一.LocalAttestation原理介绍

1.1本地认证原理

    本地认证是一种允许两个在同一台机器上运行的SGX实例(Enclave)相互认证(也就是一台机器上的两个enclave互相认证),确保它们都在可信环境中运行,并且它们的签名和测量值(MRENCLAVE和MRSIGNER)符合预期。这一过程无需依赖外部的可信第三方(如远程认证中的IAS服务),可以高效地验证在本地运行的两个Enclave的可信性。

在这里插入图片描述

1.2 本地认证基本流程

1.初始化

  • 准备工作:在进行本地认证之前,需要确保系统上安装了英特尔SGX驱动和SGX SDK,并创建和签署了两个至少包含认证代码的Enclave。

2.创建会话

  • 会话请求:一个EnclaveB(称为请求者)启动会话请求(1),通过指定的本地认证API向另一个EnclaveA(称为响应者)发送认证请求。
  • 会话处理:响应者接收到请求者的会话请求后,生成会话密钥,并准备响应。

3.生成认证报告

  • 认证报告生成:请求者和响应者各自生成包含测量值(MRENCLAVEMRSIGNER)、会话密钥等信息的报告REPORT。此报告用于证明Enclave的身份和状态。
  • 认证报告交换:请求者和响应者交换各自生成的认证报告。

4.验证认证报告

  • 验证报告:请求者和响应者分别验证对方的认证报告。这一步包括检查报告中的测量值、签名情况和认证会话密钥。
  • 验证MRENCLAVE:验证对方Enclave的测量值(MRENCLAVE),确保其为预期的可信代码。
  • 验证MRSIGNER:验证签名者的测量值(MRSIGNER),确保其由受信任的签名者生成。
  • 验证会话密钥:确保会话密钥的正确性,用于后续的安全通讯。

5.交换密钥并通信

  • 密钥交换:交换经过验证的会话密钥。
  • 安全通信:一旦认证成功,请求者和响应者可以使用协商好的会话密钥进行安全通信,该密钥用于加密和解密传输的数据,确保数据传递的机密性和完整性。

总结:Intel SGX本地认证的步骤主要包括以下几个关键步骤:

  • 两个Enclave初始化并准备各自的会话请求和响应。
  • 请求者向响应者发起会话请求,双方生成并交换认证报告。
  • 验证报告的真实性和可信度,确保双方的Enclave环境是可信的。
  • 交换会话密钥,建立安全的通信渠道。

    以上这些步骤确保了两个在同一台机器上运行的Enclave可以安全、高效地相互认证,从而在本地保护敏感数据和计算过程。

1.3 本地认证核心原理

   就是响应方EnclaveA生成MAC值并返回给请求方,MAC值是由一个报告密钥生成,这个密钥只对目标EnclaveB和相同平台的EREPORT指令可见。当EnclaveB收到REPORT信息之后,调用EGETKEY指令来获取密钥,用来计算MAC值,进而将重新计算的MAC值和收到的REPORT的MAC值进行比较(核心思想:验证MAC值),来确认EnclaveA运行在相同平台之上。当可信硬件部分得以证实之后(也就是说MAC值验证的是硬件可信),EnclaveB在通过REPORT中的信息认证EnclaveA的身份。最后EnclaveA再以同样的方式认证EnclaveB的身份,完成双向互认。

二.源码分析

2.1 README

---------------------------
The project aims to demo SGX local attestation flow. 
------------------------------------
How to Build the Sample Code
------------------------------------
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<EnclaveInitiator_private_test.pem>/<EnclaveResponder_private_test.pem> automatically when you build the project.b. Rename your test key(3072-bit RSA private key) to <EnclaveInitiator_private_test.pem>/<EnclaveResponder_private_test.pem> and put it under the <EnclaveInitiator>/<EnclaveResponder> folder.
3. Build the project with the prepared Makefile:a. Hardware Mode, Debug build:$ makeb. Hardware Mode, Pre-release build:$ make SGX_PRERELEASE=1 SGX_DEBUG=0c. Hardware Mode, release build:$ make SGX_DEBUG=0d. Simulation Mode, Debug build:$ make SGX_MODE=SIMe. Simulation Mode, Pre-release build:$ make SGX_MODE=SIM SGX_PRERELEASE=1 SGX_DEBUG=0f. Simulation Mode, Release build:$ make SGX_MODE=SIM SGX_DEBUG=0g. Use Local Attestation 2.0 protocol, Hardware Mode, Debug build:$ make LAv2=1Note: Local Attestation 2.0 protocol will be used if 'LAv2' is defined.
When build is successful, you can find executable binaries in "bin" sub-folder.
------------------------------------
How to Execute the Sample Code
------------------------------------
1. Install SGX driver and PSW for Linux* OS
2. If you want to try local attestation flow from two process, you can goto "bin" sub-foldera. run "./appresponder".It would launch a process to act as local attestation responder.b. run "./appinitiator"It would launch a process to act as local attestation initator.
3. If you want to try local attestation flow from one process, you can goto "bin" sub-folder and run "./app"------------------------------------
How to Get Signed Enclave's MRSIGNER
------------------------------------
1. Install Intel(R) Software Guard Extensions (Intel(R) SGX) SDK for Linux* OS
2. Execute blow command to get your signed enclave's MRSIGNER: <SGX_SDK Installation Path>/bin/x64/sgx_sign dump -enclave <Signed Enclave> -dumpfile mrsigner.txt
3. Find the signed enclave's MRSIGNER in the mrsigner.txt(mrsigner->value:)

    该示例的主要功能是测试enclave本地认证功能,根据上面README中的内容总结一下LocalAttestation的执行步骤如下:

2.1.1 编译流程

  1. 首先需要安装SGX sdk,以及等等对SGX的支持测试,具体可参考之前的一篇博客:【SGX系列教程】(一);
  2. 安装openssl,目的是为enclave.so生成签名私钥:

openssl genrsa -out Enclave/Enclave_private_test.pem -3 3072

  1. 确保sdk路径生效(source),基本在安装sdk时会指定安装路径为:/opt/intel/sgxsdk
  2. 使用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
    • 使用 2.0 认证协议, 硬件模式, Debug build::
      make LAv2=1

2.1.2 执行流程(双进程执行 or 单进程执行,在后面执行部分有展示效果)

  1. Linux系统下安装SGX驱动和PSW(具体可以参考系列教程一);
  2. 可以通过两个进程进行本地认证流程,进入bin子文件夹:
    a. 运行 ./appresponder: 这将启动一个进程,使其充当本地认证响应者
    b. 运行 ./appinitiator: 这将启动一个进程,使其充当本地认证发起者
  3. 如果您想通过一个进程进行完成全部本地认证流程,进入bin子文件夹直接运行 ./app

2.1.3 如何获取已签名的Enclave的MRSIGNER

  1. 安装SDK,适用于Linux操作系统。
  2. 执行以下命令以获取已签名的Enclave的MRSIGNER:
<SGX_SDK Installation Path>/bin/x64/sgx_sign dump -enclave <Signed Enclave> -dumpfile mrsigner.txt
  1. 在文件mrsigner.txt中找到已签名的Enclave的MRSIGNERmrsigner->value:)(找到 mrsigner->value: 字段,即为签名该Enclave的签名者的测量值)

   注意,这里的<Signed Enclave>指的是编译之后针对每个enclave生成的签名文件,通常是.signed.so.signed.dll 扩展名(具体取决于目标平台)。这里是在bin目录下生成的libenclave_initiator.signed.solibenclave_responder.signed.so两个文件。
因为分别执行下面两个命令,为initiator和responder两个enclave分别生成相应的签名者信息文件:

// 1.为initiator enclave生成initiatormrsigner.txt
/opt/intel/sgxsdk/bin/x64/sgx_sign dump -enclave ./bin/libenclave_initiator.signed.so -dumpfile initiatormrsigner.txt// 2.为responder enclave生成respondermrsigner.txt
/opt/intel/sgxsdk/bin/x64/sgx_sign dump -enclave ./bin/libenclave_responder.signed.so -dumpfile respondermrsigner.txt

   生成成功会输出Succeed.,并在目录下面会生成如下两个文件,分别代表两个enclave的签名信息:

在这里插入图片描述

然后需要将mrsigner->value:值分别copy到程序代码中以使用真实的签名者测量值(相当于公钥):

在这里插入图片描述

在这里插入图片描述

   通过上述步骤,可以成功生成并替换已签名的EnclaveMRSIGNER信息。

2.2 重点代码分析

    SGX工程的整个构建流程在之前的文章中已经给出给出了详细教程:【SGX系列教程】(二)第一个 SGX 程序: HelloWorld,因此在此处仅给出部分重点代码分析,主要讲清楚enclave如何实现本地认证,其他通用代码不做展开分析。

2.2.1 App文件夹

2.2.1.1 App/App.cpp
#include <stdio.h>
#include <map>
#include "sgx_eid.h"
#include "sgx_urts.h"
#include "EnclaveInitiator_u.h"
#include "EnclaveResponder_u.h"
#include "sgx_utils.h"// 定义信任域(Enclave)的名称
// 定义两个信任域ID,ENCLAVE_INITIATOR_NAME 和 ENCLAVE_RESPONDER_NAME 分别代表发起者和响应者的已签名共享库文件
#define ENCLAVE_INITIATOR_NAME "libenclave_initiator.signed.so"
#define ENCLAVE_RESPONDER_NAME "libenclave_responder.signed.so"// 定义两个Enclave ID,分别用于发起者和响应者Enclave
sgx_enclave_id_t initiator_enclave_id = 0, responder_enclave_id = 0;int main(int argc, char* argv[])
{int update = 0;                 // 定义更新标志uint32_t ret_status;            // 定义返回状态sgx_status_t status;            // 定义SGX状态类型sgx_launch_token_t token = {0}; // 定义启动令牌,并初始化为0// 避免未使用参数警告,argc和argv被标记为void(void)argc;(void)argv;// 加载发起者和响应者enclaveif (SGX_SUCCESS != sgx_create_enclave(ENCLAVE_INITIATOR_NAME, SGX_DEBUG_FLAG, &token, &update, &initiator_enclave_id, NULL)|| SGX_SUCCESS != sgx_create_enclave(ENCLAVE_RESPONDER_NAME, SGX_DEBUG_FLAG, &token, &update, &responder_enclave_id, NULL)) {// 如果加载Enclave失败,打印错误信息并进入销毁流程printf("failed to load enclave.\n");goto destroy_enclave;}printf("succeed to load enclaves.\n");  // 如果成功加载,打印成功信息// 使用发起者信任域创建ECDH会话,会话对象为响应者信任域status = test_create_session(initiator_enclave_id, &ret_status);if (status != SGX_SUCCESS || ret_status != 0) {// 如果会话创建失败,打印错误信息并进入销毁流程printf("failed to establish secure channel: ECALL return 0x%x, error code is 0x%x.\n", status, ret_status);goto destroy_enclave;}printf("succeed to establish secure channel.\n");// 如果会话创建成功,打印成功信息// 测试发起者信任域和响应者信任域之间的消息交换status = test_message_exchange(initiator_enclave_id, &ret_status);if (status != SGX_SUCCESS || ret_status != 0) {// 如果消息交换失败,打印错误信息并进入销毁流程printf("test_message_exchange Ecall failed: ECALL return 0x%x, error code is 0x%x.\n", status, ret_status);goto destroy_enclave;}printf("Succeed to exchange secure message...\n");// 如果消息交换成功,打印成功信息// 关闭ECDH会话status = test_close_session(initiator_enclave_id, &ret_status);if (status != SGX_SUCCESS || ret_status != 0) {// 如果关闭会话失败,打印错误信息并进入销毁流程printf("test_close_session Ecall failed: ECALL return 0x%x, error code is 0x%x.\n", status, ret_status);goto destroy_enclave;}printf("Succeed to close Session...\n");// 如果关闭会话成功,打印成功信息destroy_enclave:sgx_destroy_enclave(initiator_enclave_id);sgx_destroy_enclave(responder_enclave_id);// 销毁发起者和响应者信任域(Enclave)return 0;// 主函数返回0,表示正常结束
}

程序解释:

  • 加载enclave: sgx_create_enclave,enclave API,函数用于加载Enclave文件,并初始化发起者和响应者的Enclave实例;
  • 创建会话: test_create_session,ECALL函数,用于创建ECDH安全会话,发起者(Initiator Enclave)需要与响应者(Responder Enclave)建立一个安全会话。
  • 消息交换:test_message_exchange,ECALL函数,用于在发起者和响应者之间进行消息传递。
  • 关闭会话:test_close_session,ECALL函数,用于关闭之前建立的安全会话。
  • 销毁Enclave:sgx_destroy_enclave,enclave API,函数用于销毁发起者和响应者的Enclave实例。无论前面的操作是否成功,最后都会执行销毁步骤,确保资源被正确释放。

    总的来说,App.cpp代码用于演示Intel SGX中本地认证(Local Attestation)的基本过程,包括加载Enclave建立会话消息交换关闭会话。每一步都使用了SGX提供的API,并结合错误处理机制,确保在失败时能正确退出并释放资源。

2.2.1.2 App/UntrustedEnclaveMessageExchange.cpp
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include "sgx_eid.h"          // 用于SGX Enclave ID定义的头文件
#include "error_codes.h"      // 自定义错误码的头文件
#include "datatypes.h"        // 自定义数据类型的头文件
#include "sgx_urts.h"         // 用于SGX User Runtime库的头文件
#include "UntrustedEnclaveMessageExchange.h" // 非受信任部分的消息交换头文件
#include "sgx_dh.h"           // 用于Diffie-Hellman密钥交换的SGX头文件#include "fifo_def.h"         // FIFO定义文件
#include "EnclaveResponder_u.h" // 自动生成的Enclave Responder的未受信部分头文件// 声明外部变量 responder_enclave_id,用于存储响应者Enclave的ID
extern sgx_enclave_id_t responder_enclave_id;/* 函数描述: 这是发起者Enclave获取响应者Enclave的ECDH消息1和会话ID的OCALL接口* 参数描述:*      [输入,输出] dh_msg1: 指向ECDH消息1缓冲区的指针,这个缓冲区在发起者Enclave中分配,并由响应者Enclave填充*      [输出] session_id: 指向由响应者Enclave分配的会话ID的指针* */
extern "C" ATTESTATION_STATUS session_request_ocall(sgx_dh_msg1_t* dh_msg1, uint32_t* session_id)
{sgx_status_t ret;uint32_t retcode;// 调用 session_request 函数,获取来自响应者Enclave的ECDH消息1和会话IDret = session_request(responder_enclave_id, &retcode, dh_msg1, session_id);if (ret != SGX_SUCCESS || retcode != SGX_SUCCESS)return ATTESTATION_ERROR;return (ATTESTATION_STATUS)0;
}/* 函数描述: 这是发起者Enclave发送ECDH消息2到响应者Enclave,并接收来自响应者Enclave的ECDH消息3的OCALL接口* 参数描述:*      [输入] dh_msg2: 指向由发起者Enclave生成的ECDH消息2的指针*      [输入,输出] dh_msg3: 指向ECDH消息3的指针,这个缓冲区在发起者Enclave中分配,并由响应者Enclave填充*      [输入] session_id: 由响应者Enclave分配的会话ID* */
ATTESTATION_STATUS exchange_report_ocall(sgx_dh_msg2_t *dh_msg2, sgx_dh_msg3_t *dh_msg3, uint32_t session_id)
{sgx_status_t ret;uint32_t retcode;// 调用 exchange_report 函数,将消息2发送给响应者Enclave,并接收消息3ret = exchange_report(responder_enclave_id, &retcode, dh_msg2, dh_msg3, session_id);if (ret != SGX_SUCCESS || retcode != SGX_SUCCESS)return ATTESTATION_ERROR;return (ATTESTATION_STATUS)0;
}/* 函数描述: 这是发起者Enclave发送加密请求消息到响应者Enclave,并接收来自响应者Enclave响应消息的OCALL接口* 参数描述:*      [输入] session_id: 由响应者Enclave分配的会话ID*      [输入] req_message: 指向请求消息的指针*      [输入] req_message_size: 请求消息的大小*      [输入] max_payload_size: 响应消息中的最大有效负载大小*      [输入,输出] resp_message: 指向响应消息的指针,该缓冲区由发起者Enclave分配并由响应者Enclave填充*      [输入] response message size: 响应消息的大小* */
ATTESTATION_STATUS send_request_ocall(uint32_t session_id, secure_message_t* req_message, size_t req_message_size, size_t max_payload_size, secure_message_t* resp_message, size_t resp_message_size)
{sgx_status_t ret;uint32_t retcode; // 调用 generate_response 函数,向响应者Enclave发送请求并接收响应ret = generate_response(responder_enclave_id, &retcode, req_message, req_message_size, max_payload_size, resp_message, resp_message_size, session_id);if (ret != SGX_SUCCESS || retcode != SGX_SUCCESS)return INVALID_SESSION;return (ATTESTATION_STATUS)0;
}/* 函数描述: 这是发起者Enclave关闭安全会话的OCALL接口* 参数描述:*      [输入] session_id: 由响应者Enclave分配的会话ID* */
ATTESTATION_STATUS end_session_ocall(uint32_t session_id)
{sgx_status_t ret;uint32_t retcode = (uint32_t) INVALID_SESSION;// 调用 end_session 函数,关闭会话ret = end_session(responder_enclave_id, &retcode, session_id);if (ret != SGX_SUCCESS || retcode != SGX_SUCCESS)return INVALID_SESSION;return (ATTESTATION_STATUS)0;
}

程序解释:
    这段代码实现了本地认证过程中发起者Enclave响应者Enclave之间的一些OCALL接口。OCALL是指从受信任的环境调用未受信任环境中的功能。具体实现了以下4个OCALL接口:

  • session_request_ocall:
    功能:从响应者Enclave获取ECDH协议的消息1和会话ID。
    实现:调用session_request函数,传递响应者Enclave的ID、返回代码、消息1缓冲区和会话ID指针。如果调用失败,返回认证错误代码。
  • exchange_report_ocall:
    功能:向响应者Enclave发送ECDH协议的消息2,并接收来自响应者Enclave的消息3。
    实现:调用exchange_report函数,传递响应者Enclave的ID、返回代码、消息2、消息3和会话ID。如果调用失败,返回认证错误代码。
  • send_request_ocall:
    功能:向响应者Enclave发送加密请求消息,并接收响应消息。
    实现:调用generate_response函数,传递响应者Enclave的ID、返回代码、请求消息及其大小、最大有效载荷大小、响应消息及其大小和会话ID。如果调用失败,返回无效会话错误代码。
  • end_session_ocall:
    功能:关闭发起者Enclave与响应者Enclave之间的安全会话。
    实现:调用end_session函数,传递响应者Enclave的ID、返回代码和会话ID。如果调用失败,返回无效会话错误代码。

    这些OCALL接口将请求传递到响应者Enclave并返回结果,用于协调本地认证流程,确保两个Enclave之间的通信安全。

2.2.2 AppInitiator文件夹

    上面App是单线程实现两个enclave之间的互相认证,下面介绍的AppInitiator和AppResponder是通过两个线程分别实现请求者和响应者程序。

2.2.2.1 AppInitiator/App.cpp
#include <stdio.h>
#include <map>
#include <sched.h>
#include <sys/sysinfo.h>
#include <unistd.h> // POSIX标准API,如sleep、write等#include "sgx_eid.h" // 用于SGX Enclave ID定义的头文件
#include "sgx_urts.h" // 用于SGX User Runtime库的头文件#include "EnclaveInitiator_u.h" // 自动生成的未受信任部分的头文件,包含发起者Enclave的ECALL声明#define ENCLAVE_INITIATOR_NAME "libenclave_initiator.signed.so"
// 定义发起者Enclave的已签名共享库文件名称int main(int argc, char* argv[])
{int update = 0; // 更新标志uint32_t ret_status; // 返回状态sgx_status_t status; // SGX状态类型sgx_launch_token_t token = {0}; // 启动令牌,初始化为全零sgx_enclave_id_t initiator_enclave_id = 0; // 定义发起者Enclave的ID(void)argc;(void)argv;// 避免未使用参数的警告// 创建ECDH发起者Enclavestatus = sgx_create_enclave(ENCLAVE_INITIATOR_NAME, SGX_DEBUG_FLAG, &token, &update, &initiator_enclave_id, NULL);if (status != SGX_SUCCESS) {// 如果加载Enclave失败,打印错误信息并返回-1printf("failed to load enclave %s, error code is 0x%x.\n", ENCLAVE_INITIATOR_NAME, status);return -1;}printf("succeed to load enclave %s\n", ENCLAVE_INITIATOR_NAME);// 成功加载,打印成功信息// 使用发起者Enclave创建ECDH会话status = test_create_session(initiator_enclave_id, &ret_status);if (status != SGX_SUCCESS || ret_status != 0) {// 如果会话创建失败,打印错误信息、销毁Enclave并返回-1printf("failed to establish secure channel: ECALL return 0x%x, error code is 0x%x.\n", status, ret_status);sgx_destroy_enclave(initiator_enclave_id);return -1;}printf("succeed to establish secure channel.\n");// 成功建立会话,打印成功信息// 测试发起者Enclave和响应者Enclave之间的消息交换status = test_message_exchange(initiator_enclave_id, &ret_status);if (status != SGX_SUCCESS || ret_status != 0) {// 如果消息交换失败,打印错误信息、销毁Enclave并返回-1printf("test_message_exchange Ecall failed: ECALL return 0x%x, error code is 0x%x.\n", status, ret_status);sgx_destroy_enclave(initiator_enclave_id);return -1;}printf("Succeed to exchange secure message...\n");// 成功消息交换,打印成功信息// 关闭ECDH会话status = test_close_session(initiator_enclave_id, &ret_status);if (status != SGX_SUCCESS || ret_status != 0) {// 如果关闭会话失败,打印错误信息、销毁Enclave并返回-1printf("test_close_session Ecall failed: ECALL return 0x%x, error code is 0x%x.\n", status, ret_status);sgx_destroy_enclave(initiator_enclave_id);return -1;}printf("Succeed to close Session.\n");// 成功关闭会话,打印成功信息sgx_destroy_enclave(initiator_enclave_id);// 销毁发起者Enclavereturn 0;// 程序正常退出,返回0
}

总的来说,与上面单线程的App/App.cpp相差无几。

2.2.2.2 AppInitiator/UntrustedEnclaveMessageExchange.cpp
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h> // POSIX标准API,如sleep、write等
#include "sgx_eid.h" // 用于SGX Enclave ID定义的头文件
#include "error_codes.h" // 自定义错误码的头文件
#include "datatypes.h" // 自定义数据类型的头文件
#include "sgx_urts.h" // 用于SGX User Runtime库的头文件
#include "UntrustedEnclaveMessageExchange.h" // 非受信任部分的消息交换头文件
#include "sgx_dh.h" // 用于Diffie-Hellman密钥交换的SGX头文件#include "fifo_def.h" // FIFO定义文件#include <map> // 标准库map,用于字典结构/* 函数描述:这是发起者Enclave获取响应者Enclave的ECDH消息1和会话ID的OCALL接口* 参数描述:*      [输入,输出] dh_msg1: 指向ECDH消息1缓冲区的指针,这个缓冲区在发起者Enclave中分配,并由响应者Enclave填充*      [输出] session_id: 指向由响应者Enclave分配的会话ID的指针* */
extern "C" ATTESTATION_STATUS session_request_ocall(sgx_dh_msg1_t* dh_msg1, uint32_t* session_id)
{FIFO_MSG msg1_request; // 定义消息1请求对象FIFO_MSG *msg1_response; // 指向消息1响应的指针SESSION_MSG1_RESP *msg1_respbody = NULL; // 指向消息1响应体的指针size_t msg1_resp_size; // 响应消息的大小msg1_request.header.type = FIFO_DH_REQ_MSG1; // 设置消息类型为DH密钥交换请求msg1_request.header.size = 0; // 设置消息大小为0// 发送并接收消息1请求if ((client_send_receive(&msg1_request, sizeof(FIFO_MSG), &msg1_response, &msg1_resp_size) != 0)|| (msg1_response == NULL)){// 如果发送接收失败,打印错误信息并返回无效会话错误printf("fail to send and receive message.\n");return INVALID_SESSION;}// 将响应消息填充至dh_msg1和session_idmsg1_respbody = (SESSION_MSG1_RESP *)msg1_response->msgbuf;memcpy(dh_msg1, &msg1_respbody->dh_msg1, sizeof(sgx_dh_msg1_t));*session_id = msg1_respbody->sessionid;free(msg1_response); // 释放响应消息return (ATTESTATION_STATUS)0;
}/* 函数描述:这是发起者Enclave发送ECDH消息2到响应者Enclave,并接收来自响应者Enclave的ECDH消息3的OCALL接口* 参数描述:*      [输入] dh_msg2: 指向由发起者Enclave生成的ECDH消息2的指针*      [输入,输出] dh_msg3: 指向ECDH消息3的指针,这个缓冲区在发起者Enclave中分配,并由响应者Enclave填充*      [输入] session_id: 由响应者Enclave分配的会话ID* */
ATTESTATION_STATUS exchange_report_ocall(sgx_dh_msg2_t *dh_msg2, sgx_dh_msg3_t *dh_msg3, uint32_t session_id)
{FIFO_MSG *msg2 = NULL, *msg3 = NULL; // 定义消息2和消息3对象FIFO_MSG_HEADER *msg2_header = NULL; // 定义消息2头部指针SESSION_MSG2 *msg2_body = NULL; // 定义消息2体指针SESSION_MSG3 *msg3_body = NULL; // 定义消息3体指针size_t msg2size, msg3size; // 定义消息2和消息3大小msg2size = sizeof(FIFO_MSG_HEADER) + sizeof(SESSION_MSG2); // 设置消息2大小msg2 = (FIFO_MSG *)malloc(msg2size); // 为消息2分配内存if (!msg2){// 如果内存分配失败,返回内存不足错误return ERROR_OUT_OF_MEMORY;}memset(msg2, 0, msg2size); // 清空消息2内存msg2_header = (FIFO_MSG_HEADER *)msg2; // 消息2头部指针msg2_header->type = FIFO_DH_MSG2; // 设置消息类型为DH消息2msg2_header->size = sizeof(SESSION_MSG2); // 设置消息大小msg2_body = (SESSION_MSG2 *)msg2->msgbuf; // 消息2体指针memcpy(&msg2_body->dh_msg2, dh_msg2, sizeof(sgx_dh_msg2_t)); // 复制消息2内容msg2_body->sessionid = session_id; // 设置会话ID// 发送并接收消息2if (client_send_receive(msg2, msg2size, &msg3, &msg3size) != 0){free(msg2); // 释放消息2内存printf("failed to send and receive message.\n");return INVALID_SESSION;}// 将消息3内容复制到输出参数dh_msg3中msg3_body = (SESSION_MSG3 *)msg3->msgbuf;memcpy(dh_msg3, &msg3_body->dh_msg3, sizeof(sgx_dh_msg3_t));free(msg3); // 释放消息3内存free(msg2); // 再次释放消息2内存return (ATTESTATION_STATUS)0;
}/* 函数描述:这是发起者Enclave发送加密请求消息到响应者Enclave,并接收来自响应者Enclave响应消息的OCALL接口* 参数描述:*      [输入] session_id: 由响应者Enclave分配的会话ID*      [输入] req_message: 指向请求消息的指针*      [输入] req_message_size: 请求消息的大小*      [输入] max_payload_size: 响应消息中的最大有效负载大小*      [输入,输出] resp_message: 指向响应消息的指针,该缓冲区由发起者Enclave分配并由响应者Enclave填充*      [输入] response message size: 响应消息的大小* */
ATTESTATION_STATUS send_request_ocall(uint32_t session_id, secure_message_t* req_message, size_t req_message_size, size_t max_payload_size, secure_message_t* resp_message, size_t resp_message_size)
{FIFO_MSG *msgreq = NULL, *msgresp = NULL; // 定义请求消息和响应消息对象FIFO_MSGBODY_REQ *msgbody; // 定义消息体指针size_t reqsize, respsize; // 定义请求和响应大小reqsize = sizeof(FIFO_MSG_HEADER) + sizeof(FIFO_MSGBODY_REQ) + req_message_size; // 设置请求大小msgreq = (FIFO_MSG *)malloc(reqsize); // 为请求消息分配内存if (!msgreq){// 如果内存分配失败,返回内存不足错误return ERROR_OUT_OF_MEMORY;}memset(msgreq, 0, reqsize); // 清空请求消息内存msgreq->header.type = FIFO_DH_MSG_REQ; // 设置消息类型为DH请求消息msgreq->header.size = sizeof(FIFO_MSGBODY_REQ) + req_message_size; // 设置消息大小msgbody = (FIFO_MSGBODY_REQ *)msgreq->msgbuf; // 消息体指针msgbody->max_payload_size = max_payload_size; // 设置最大有效负载大小msgbody->size = req_message_size; // 设置请求消息大小msgbody->session_id = session_id; // 设置会话IDmemcpy(msgbody->buf, req_message, req_message_size); // 复制请求消息内容// 发送并接收请求消息if (client_send_receive(msgreq, reqsize, &msgresp, &respsize) != 0){free(msgreq); // 释放请求消息内存printf("fail to send and receive message.\n");return INVALID_SESSION;}// 将响应消息内容复制到输出参数resp_message中memcpy(resp_message, msgresp->msgbuf, msgresp->header.size < resp_message_size ? msgresp->header.size : resp_message_size);free(msgresp); // 释放响应消息内存free(msgreq); // 再次释放请求消息内存return (ATTESTATION_STATUS)0;
}/* 参数描述:*      [输入] session_id: 由响应者Enclave分配的会话ID* */
ATTESTATION_STATUS end_session_ocall(uint32_t session_id)
{FIFO_MSG *msgresp = NULL; // 定义指向响应消息的指针FIFO_MSG *closemsg; // 定义关闭消息对象SESSION_CLOSE_REQ *body; // 定义会话关闭请求体指针size_t reqsize, respsize; // 请求和响应的大小reqsize = sizeof(FIFO_MSG) + sizeof(SESSION_CLOSE_REQ); // 设置请求消息的大小closemsg = (FIFO_MSG *)malloc(reqsize); // 为关闭消息分配内存if (!closemsg){// 如果内存分配失败,返回内存不足错误return ERROR_OUT_OF_MEMORY;}memset(closemsg, 0, reqsize); // 清空关闭消息内存closemsg->header.type = FIFO_DH_CLOSE_REQ; // 设置消息类型为会话关闭请求closemsg->header.size = sizeof(SESSION_CLOSE_REQ); // 设置消息体大小body = (SESSION_CLOSE_REQ *)closemsg->msgbuf; // 消息体指针body->session_id = session_id; // 设置会话ID// 发送关闭消息并接收响应if (client_send_receive(closemsg, reqsize, &msgresp, &respsize) != 0){free(closemsg); // 释放关闭消息内存printf("fail to send and receive message.\n");return INVALID_SESSION;}free(closemsg); // 释放关闭消息内存free(msgresp); // 释放响应消息内存return (ATTESTATION_STATUS)0;
}

总的来说,与上面单线程的App/UntrustedEnclaveMessageExchange.cpp相差无几。

2.2.3 AppResponder文件夹

2.2.3.1 AppResponder/App.cpp
// App.cpp : Defines the entry point for the console application.
#include <stdio.h>
#include <map>
#include <sys/types.h>
#include <sys/stat.h>
#include <sched.h>
#include <sys/sysinfo.h>
#include <signal.h>#include "EnclaveResponder_u.h" // 自动生成的未受信任部分头文件,包含响应者Enclave的ECALL声明
#include "sgx_eid.h" // 用于SGX Enclave ID定义的头文件
#include "sgx_urts.h" // 用于SGX User Runtime库的头文件#include "fifo_def.h" // FIFO定义文件
#include "datatypes.h" // 自定义数据类型头文件#include "CPTask.h" // 任务处理类的头文件
#include "CPServer.h" // 服务器类的头文件#define __STDC_FORMAT_MACROS
#include <inttypes.h> // 提供大整数格式化的头文件#define UNUSED(val) (void)(val) // 定义未使用参数的宏
#define TCHAR   char
#define _TCHAR  char
#define _T(str) str
#define scanf_s scanf
#define _tmain  mainCPTask * g_cptask = NULL; // 全局变量,任务处理类指针
CPServer * g_cpserver = NULL; // 全局变量,服务器类指针// 信号处理函数
void signal_handler(int sig)
{switch(sig){case SIGINT: // 处理SIGINT信号case SIGTERM: // 处理SIGTERM信号{if (g_cpserver)g_cpserver->shutDown(); // 如果服务器存在,调用其关闭函数}break;default:break;}exit(1); // 退出程序
}// 清理函数,销毁全局对象
void cleanup()
{if(g_cptask != NULL)delete g_cptask;if(g_cpserver != NULL)delete g_cpserver;
}// 主函数
int  main(int argc, char* argv[])
{(void)argc;(void)argv; // 忽略未使用的参数// 创建服务器实例,它将在套接字上监听并处理客户端请求g_cptask = new (std::nothrow) CPTask;g_cpserver = new (std::nothrow) CPServer(g_cptask);if (!g_cptask || !g_cpserver)return -1; // 如果创建失败,返回-1atexit(cleanup); // 注册退出时的清理函数// 注册信号处理器函数,以响应用户中断signal(SIGINT, signal_handler);signal(SIGTERM, signal_handler);g_cptask->start(); // 启动任务处理if (g_cpserver->init() != 0){printf("fail to init server\n"); // 初始化服务器失败,打印错误信息}else{printf("Server is ON...\n");printf("Press Ctrl+C to exit...\n");g_cpserver->doWork(); // 服务器初始化成功,启动服务器工作}return 0; // 程序正常结束
}

程序解释:
    这段代码主要用于设置和启动一个SGX应用程序中的服务器实例。它包括以下几个主要步骤:

  • 创建和初始化CPTask任务处理器和CPServer服务器实例。
  • 注册信号处理器和退出时的清理函数,以确保资源在程序终止时被正确释放。
  • 启动任务处理器和服务器,处理客户端请求和本地认证流程。
2.2.3.2 AppResponder/CPServer.cpp
#include <stdio.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <sys/un.h>#include "CPServer.h"#define BACKLOG 5 // 最大连接等待队列长度
#define CONCURRENT_MAX 32 // 最大并发连接数
#define SERVER_PORT 8888 // 服务器端口号(Windows)
#define BUFFER_SIZE 1024 // 缓冲区大小#define UNIX_DOMAIN "/tmp/UNIX.domain" // Unix Domain Socket 文件路径/* 函数描述:* 这是服务器初始化例程,它创建TCP套接字并监听一个端口。* 在Linux上,它会监听名为 '/tmp/UNIX.domain' 的域套接字* 在Windows上,它会监听8888端口,仅用于示范目的* */
int CPServer::init()
{struct sockaddr_un srv_addr;// 创建UNIX域套接字m_server_sock_fd = socket(AF_UNIX, SOCK_STREAM, 0);if (m_server_sock_fd == -1){printf("socket initiazation error\n");return -1; // 套接字初始化失败,返回-1}srv_addr.sun_family = AF_UNIX;strncpy(srv_addr.sun_path, UNIX_DOMAIN, sizeof(srv_addr.sun_path) - 1); // 设置域套接字路径unlink(UNIX_DOMAIN); // 删除已存在的文件// 绑定套接字int bind_result = bind(m_server_sock_fd, (struct sockaddr*)&srv_addr, sizeof(srv_addr));if (bind_result == -1){printf("bind error\n");close(m_server_sock_fd); // 绑定失败,关闭套接字并返回-1return -1;}// 监听套接字if (listen(m_server_sock_fd, BACKLOG) == -1){printf("listen error\n");close(m_server_sock_fd); // 监听失败,关闭套接字并返回-1return -1;}m_shutdown = 0; // 初始化停止标志return 0; // 成功返回0
}/* 函数描述:* 这是服务器的主要例程,它使用select()来接受新连接并接收来自客户端的消息。* 当它接收到客户端的请求消息后,它会将消息放入任务队列并唤醒工作线程处理请求。* */
void CPServer::doWork()
{int client_fds[CONCURRENT_MAX] = {0}; // 客户端连接描述符数组,初始化为0fd_set server_fd_set; // 文件描述符集合int max_fd = -1; // 最大文件描述符struct timeval tv; // 超时时间char input_msg[BUFFER_SIZE]; // 输入消息缓冲区char recv_msg[BUFFER_SIZE]; // 接收消息缓冲区while (!m_shutdown){// 为select()设置20秒超时tv.tv_sec = 20;tv.tv_usec = 0;FD_ZERO(&server_fd_set); // 清空文件描述符集合FD_SET(STDIN_FILENO, &server_fd_set); // 将标准输入加入集合if (max_fd < STDIN_FILENO)max_fd = STDIN_FILENO; // 更新最大文件描述符// 监听服务器套接字FD_SET(m_server_sock_fd, &server_fd_set);if (max_fd < m_server_sock_fd)max_fd = m_server_sock_fd; // 更新最大文件描述符// 监听所有客户端连接for (int i = 0; i < CONCURRENT_MAX; i++) {if (client_fds[i] != 0) {FD_SET(client_fds[i], &server_fd_set);if (max_fd < client_fds[i])max_fd = client_fds[i]; // 更新最大文件描述符}}// 调用select()等待事件int ret = select(max_fd + 1, &server_fd_set, NULL, NULL, &tv);if (ret < 0) {printf("Warning: server would shutdown\n");continue; // select()失败,继续循环} else if (ret == 0) {// 超时,继续循环continue;}if (FD_ISSET(m_server_sock_fd, &server_fd_set)) {// 如果有新的连接请求struct sockaddr_un clt_addr;socklen_t len = sizeof(clt_addr);// 接受连接请求int client_sock_fd = accept(m_server_sock_fd, (struct sockaddr *)&clt_addr, &len);if (client_sock_fd > 0) {// 如果连接池未满,将新连接加入连接池int index = -1;for (int i = 0; i < CONCURRENT_MAX; i++) {if (client_fds[i] == 0) {index = i;client_fds[i] = client_sock_fd;break;}}if (index < 0) {printf("server reach maximum connection!\n");bzero(input_msg, BUFFER_SIZE);strcpy(input_msg, "server reach maximum connection\n");send(client_sock_fd, input_msg, BUFFER_SIZE, 0);} else if (client_sock_fd < 0) {printf("server: accept() return failure, %s, would exit.\n", strerror(errno));close(m_server_sock_fd);break;}}}for (int i = 0; i < CONCURRENT_MAX; i++) {if ((client_fds[i] != 0)&& (FD_ISSET(client_fds[i], &server_fd_set))) {// 如果有来自客户端的请求消息FIFO_MSG *msg;bzero(recv_msg, BUFFER_SIZE);long byte_num = recv(client_fds[i], recv_msg, BUFFER_SIZE, 0);if (byte_num > 0) {if (byte_num > BUFFER_SIZE)byte_num = BUFFER_SIZE;recv_msg[byte_num] = '\0';msg = (FIFO_MSG *)malloc(byte_num);if (!msg) {printf("memory allocation failure\n");continue;}memset(msg, 0, byte_num);memcpy(msg, recv_msg, byte_num);msg->header.sockfd = client_fds[i];// 将请求消息放入事件队列m_cptask->puttask(msg);} else if (byte_num < 0) {printf("failed to receive message.\n");} else {// 客户端连接关闭FD_CLR(client_fds[i], &server_fd_set);close(client_fds[i]);client_fds[i] = 0;}}}}
}/* 函数描述:* 这是关闭服务器的函数。当进程退出时调用。* */
void CPServer::shutDown()
{printf("Server would shutdown...\n");m_shutdown = 1; // 设置停止标志m_cptask->shutdown(); // 停止任务处理close(m_server_sock_fd); // 关闭服务器套接字
}

功能分析

  • 初始化服务器(init方法):
    功能:创建UNIX域套接字绑定到路径/tmp/UNIX.domain并开始监听连接请求。
    实现:创建套接字 socket(AF_UNIX, SOCK_STREAM, 0)。
    设置套接字地址结构 srv_addr 的家庭类型为 AF_UNIX,并指定路径 UNIX_DOMAIN。调用bind函数绑定套接字到地址。通过listen函数使套接字开始监听连接请求。如果过程中任何一步失败,关闭套接字并返回-1,否则返回0表示成功。
  • 处理客户端连接和消息(doWork方法):
    功能:使用select函数接受新的连接和处理来自客户端的消息。
    实现:使用select函数监视多个文件描述符,检测新的连接请求和来自客户端的消息。如果select检测到新连接请求,调用accept函数接受连接并将新客户端加入client_fds数组中。如果select检测到客户端的请求消息,调用recv函数接收消息并将其复制到FIFO_MSG结构中,之后将消息放入任务队列中供处理。如果客户端连接关闭或发生错误,关闭相应的客户端套接字并从client_fds数组中移除。
  • 关闭服务器(shutDown方法):
    功能:当服务器需要关闭时调用的函数,控制服务器的关机流程。
    实现:打印关闭服务器的消息。设置停止标志 m_shutdown 为 1。
2.2.3.3 AppResponder/CPTask.cpp
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <map>
#include <sys/stat.h>
#include <sched.h>#include "EnclaveResponder_u.h" // 自动生成的未受信任部分头文件,包含响应者Enclave的ECALL声明
#include "sgx_eid.h" // 用于SGX Enclave ID定义的头文件
#include "sgx_urts.h" // 用于SGX User Runtime库的头文件#include "cpdef.h" // 自定义的协议定义头文件
#include "fifo_def.h" // FIFO定义文件
#include "datatypes.h" // 自定义数据类型头文件#include "CPTask.h" // 任务处理类的头文件
#include "CPServer.h" // 服务器类的头文件sgx_enclave_id_t e2_enclave_id = 0; // 定义响应者Enclave ID#define ENCLAVE_RESPONDER_NAME "libenclave_responder.signed.so" // 定义响应者Enclave的已签名共享库文件名称/* 函数描述:加载响应者Enclave* */
int load_enclaves()
{sgx_status_t ret = SGX_SUCCESS; // SGX状态类型sgx_launch_token_t token = {0}; // 启动令牌,初始化为全零int update = 0; // 更新标志ret = sgx_create_enclave(ENCLAVE_RESPONDER_NAME, SGX_DEBUG_FLAG, &token, &update, &e2_enclave_id, NULL);if (ret != SGX_SUCCESS) {// 如果加载Enclave失败,打印错误信息并返回-1printf("failed to load enclave %s, error code is 0x%x.\n", ENCLAVE_RESPONDER_NAME, ret);return -1;}return 0; // 加载成功返回0
}/* 函数描述:* 该函数响应发起者Enclave的连接请求,通过生成并返回ECDH消息1* 参数描述:*  [输入] clientfd: 客户端的连接ID。在生成ECDH消息1后,服务器将通过此连接ID发送响应。* */
int generate_and_send_session_msg1_resp(int clientfd)
{int retcode = 0; // 初始化返回码uint32_t status = 0; // SGX状态码sgx_status_t ret = SGX_SUCCESS; // SGX状态类型SESSION_MSG1_RESP msg1resp; // 会话消息1响应结构体FIFO_MSG *fifo_resp = NULL; // 指向FIFO响应消息的指针size_t respmsgsize; // 响应消息大小memset(&msg1resp, 0, sizeof(SESSION_MSG1_RESP)); // 清空消息1响应结构体// 调用响应者Enclave生成ECDH消息1ret = session_request(e2_enclave_id, &status, &msg1resp.dh_msg1, &msg1resp.sessionid);if (ret != SGX_SUCCESS){printf("failed to do ECALL session_request.\n");return -1; // 如果生成失败,打印错误信息并返回-1}respmsgsize = sizeof(FIFO_MSG) + sizeof(SESSION_MSG1_RESP); // 计算响应消息大小fifo_resp = (FIFO_MSG *)malloc(respmsgsize); // 分配内存if (!fifo_resp){printf("memory allocation failure.\n");return -1; // 如果内存分配失败,打印错误信息并返回-1}memset(fifo_resp, 0, respmsgsize); // 清空分配的内存fifo_resp->header.type = FIFO_DH_RESP_MSG1; // 设置消息类型fifo_resp->header.size = sizeof(SESSION_MSG1_RESP); // 设置消息大小memcpy(fifo_resp->msgbuf, &msg1resp, sizeof(SESSION_MSG1_RESP)); // 复制响应内容到FIFO消息中// 发送消息1到客户端if (send(clientfd, reinterpret_cast<char *>(fifo_resp), static_cast<int>(respmsgsize), 0) == -1){printf("fail to send msg1 response.\n");retcode = -1; // 如果发送失败,打印错误信息,并设置返回码为-1}free(fifo_resp); // 释放内存return retcode; // 返回结果码
}/* 函数描述:* 该函数处理从客户端接收的ECDH消息2,并向客户端发送消息3* 参数描述:*  [输入] clientfd: 客户端的连接ID*  [输入] msg2: 包含从客户端接收的ECDH消息2* */
int process_exchange_report(int clientfd, SESSION_MSG2 *msg2)
{uint32_t status = 0; // SGX状态码sgx_status_t ret = SGX_SUCCESS; // SGX状态类型FIFO_MSG *response; // 指向FIFO响应消息的指针SESSION_MSG3 *msg3; // 指向ECDH消息3的指针size_t msgsize; // 消息大小if (!msg2)return -1; // 检查消息2是否为空,如果为空返回-1msgsize = sizeof(FIFO_MSG_HEADER) + sizeof(SESSION_MSG3); // 计算消息大小response = (FIFO_MSG *)malloc(msgsize); // 分配内存if (!response){printf("memory allocation failure\n");return -1; // 如果内存分配失败,打印错误信息并返回-1}memset(response, 0, msgsize); // 清空分配的内存response->header.type = FIFO_DH_MSG3; // 设置消息类型response->header.size = sizeof(SESSION_MSG3); // 设置消息大小msg3 = (SESSION_MSG3 *)response->msgbuf; // 消息体指针msg3->sessionid = msg2->sessionid; // 设置会话ID// 调用响应者Enclave处理ECDH消息2并生成消息3ret = exchange_report(e2_enclave_id, &status, &msg2->dh_msg2, &msg3->dh_msg3, msg2->sessionid);if (ret != SGX_SUCCESS){printf("EnclaveResponse_exchange_report failure.\n");free(response); // 如果失败,打印错误信息并释放内存return -1;}// 发送消息3到客户端if (send(clientfd, reinterpret_cast<char *>(response), static_cast<int>(msgsize), 0) == -1){printf("server_send() failure.\n");free(response); // 如果发送失败,打印错误信息并释放内存return -1;}free(response); // 释放内存return 0; // 返回0表示成功
}/* 函数描述:* 该函数处理从客户端接收的消息通信* 参数描述:*  [输入] clientfd: 客户端的连接ID*  [输入] req_msg: 指向从客户端接收的请求消息的指针* */
int process_msg_transfer(int clientfd, FIFO_MSGBODY_REQ *req_msg)
{uint32_t status = 0; // SGX状态码sgx_status_t ret = SGX_SUCCESS; // SGX状态类型secure_message_t *resp_message = NULL; // 指向响应消息的指针FIFO_MSG *fifo_resp = NULL; // 指向FIFO响应消息的指针size_t resp_message_size; // 响应消息大小if (!req_msg){printf("invalid parameter.\n");return -1; // 检查请求消息是否为空,如果为空返回-1}resp_message_size = sizeof(secure_message_t) + req_msg->max_payload_size; // 计算响应消息大小// 分配内存给响应消息resp_message = (secure_message_t*)malloc(resp_message_size);if (!resp_message){printf("memory allocation failure.\n");return -1; // 如果内存分配失败,打印错误信息并返回-1}memset(resp_message, 0, resp_message_size); // 清空分配的内存// 调用响应者Enclave生成响应消息ret = generate_response(e2_enclave_id, &status, (secure_message_t *)req_msg->buf, req_msg->size, req_msg->max_payload_size, resp_message, resp_message_size, req_msg->session_id);if (ret != SGX_SUCCESS){printf("EnclaveResponder_generate_response error.\n");free(resp_message); // 如果失败,打印错误信息并释放内存return -1;}fifo_resp = (FIFO_MSG *)malloc(sizeof(FIFO_MSG) + resp_message_size); // 分配内存给FIFO响应消息if (!fifo_resp){printf("memory allocation failure.\n");free(resp_message); // 释放响应消息内存return -1; // 返回内存分配失败错误}memset(fifo_resp, 0, sizeof(FIFO_MSG) + resp_message_size); // 清空分配的FIFO响应消息内存fifo_resp->header.type = FIFO_DH_MSG_RESP; // 设置消息类型为响应消息fifo_resp->header.size = resp_message_size; // 设置消息大小memcpy(fifo_resp->msgbuf, resp_message, resp_message_size); // 复制响应消息内容到FIFO消息缓冲区free(resp_message); // 释放响应消息内存// 发送响应消息到客户端if (send(clientfd, reinterpret_cast<char *>(fifo_resp), sizeof(FIFO_MSG) + static_cast<int>(resp_message_size), 0) == -1){printf("server_send() failure.\n");free(fifo_resp); // 如果发送失败,打印错误信息并释放FIFO消息内存return -1; // 返回发送失败错误}free(fifo_resp); // 释放FIFO消息内存return 0; // 返回0表示成功
}/* 函数描述:处理来自客户端的会话关闭请求* 参数描述:*  [输入] clientfd: 客户端的连接ID*  [输入] close_req: 指向客户端的会话关闭请求的指针* */
int process_close_req(int clientfd, SESSION_CLOSE_REQ *close_req)
{uint32_t status = 0; // SGX状态码sgx_status_t ret = SGX_SUCCESS; // SGX状态类型FIFO_MSG close_ack; // 定义关闭确认消息if (!close_req)return -1; // 检查关闭请求是否为空,如果为空返回-1// 调用响应者Enclave关闭会话ret = end_session(e2_enclave_id, &status, close_req->session_id);if (ret != SGX_SUCCESS)return -1; // 如果失败,返回会话关闭错误// 发送会话关闭确认响应memset(&close_ack, 0, sizeof(FIFO_MSG)); // 清空关闭确认消息内存close_ack.header.type = FIFO_DH_CLOSE_RESP; // 设置消息类型为关闭响应close_ack.header.size = 0; // 设置消息大小为0if (send(clientfd, reinterpret_cast<char *>(&close_ack), sizeof(FIFO_MSG), 0) == -1){printf("server_send() failure.\n");return -1; // 如果发送失败,打印错误信息并返回发送失败错误}return 0; // 返回0表示成功
}void CPTask::run()
{FIFO_MSG *message = NULL; // 定义指向消息的指针sgx_launch_token_t token = {0}; // 启动令牌,初始化为全零sgx_status_t status; // SGX状态类型int update = 0; // 更新标志// 加载响应者Enclave status = sgx_create_enclave(ENCLAVE_RESPONDER_NAME, SGX_DEBUG_FLAG, &token, &update, &e2_enclave_id, NULL);if (status != SGX_SUCCESS){printf("failed to load enclave %s, error code is 0x%x.\n", ENCLAVE_RESPONDER_NAME, status);return; // 如果加载失败,打印错误信息并返回}while (!isStopped()){/* 从队列中接收任务 */message = m_queue.blockingPop();if (isStopped()){free(message); // 如果已停止,释放消息内存并跳出循环break;}switch (message->header.type){case FIFO_DH_REQ_MSG1:{// 处理ECDH会话连接请求int clientfd = message->header.sockfd;if (generate_and_send_session_msg1_resp(clientfd) != 0){printf("failed to generate and send session msg1 resp.\n");break; // 如果生成并发送会话消息1失败,打印错误信息并跳出循环}}break;case FIFO_DH_MSG2:{// 处理ECDH消息2int clientfd = message->header.sockfd;SESSION_MSG2 *msg2 = NULL;msg2 = (SESSION_MSG2 *)message->msgbuf;if (process_exchange_report(clientfd, msg2) != 0){printf("failed to process exchange_report request.\n");break; // 如果处理消息交换报告失败,打印错误信息并跳出循环}}break;case FIFO_DH_MSG_REQ:{// 处理消息传输请求int clientfd = message->header.sockfd;FIFO_MSGBODY_REQ *msg = NULL;msg = (FIFO_MSGBODY_REQ *)message->msgbuf;if (process_msg_transfer(clientfd, msg) != 0){printf("failed to process message transfer request.\n");break; // 如果处理消息传输请求失败,打印错误信息并跳出循环}}break;case FIFO_DH_CLOSE_REQ:{// 处理关闭会话请求int clientfd = message->header.sockfd;SESSION_CLOSE_REQ *closereq = NULL;closereq = (SESSION_CLOSE_REQ *)message->msgbuf;process_close_req(clientfd, closereq); // 处理关闭会话请求}break;default:{printf("Unknown message.\n"); // 未知消息类型,打印错误信息}break;}free(message); // 释放消息内存message = NULL;}sgx_destroy_enclave(e2_enclave_id); // 销毁响应者Enclave
}void CPTask::shutdown()
{stop(); // 停止任务m_queue.close(); // 关闭消息队列join(); // 等待任务结束
}void CPTask::puttask(FIFO_MSG* requestData)
{if (isStopped()) {return; // 如果已停止,不处理任务}m_queue.push(requestData); // 将任务放入消息队列
}

   这段代码实现了一个负责处理从客户端接收的任务类CPTask,并通过与SGX Enclave进行交互完成任务。主要功能包括以下几个部分:
函数 process_close_req
功能:处理来自客户端的会话关闭请求。
实现:检查关闭请求参数是否为空,如果为空则返回-1。调用 end_session 函数通过Enclave关闭会话。发送会话关闭确认响应到客户端。成功关闭会话返回0,否则返回-1。
方法 CPTask::run
功能:任务处理的主逻辑,加载Enclave并处理消息队列中的任务。
实现:加载响应者Enclave。在循环中不断从消息队列中取出任务,根据任务类型分别处理不同的任务。包括会话请求、消息交换、消息传输和会话关闭等任务。如果遇到未知的消息类型,打印错误信息。结束后销毁Enclave。
方法 CPTask::shutdown
功能:停止任务处理,关闭消息队列并等待任务结束。
实现:调用 stop 方法停止任务。关闭消息队列。等待任务结束。
方法 CPTask::puttask
功能:将任务放入消息队列。
实现:检查任务是否已停止,如果已停止则不处理任务。
将任务放入消息队列。

   综上所述,这段代码通过注册信号处理器和任务队列的使用,确保了从客户端接收任务并对其进行处理的完整流程。主要实现了本地认证过程中的消息交换和会话管理,并与SGX Enclave进行交互,确保数据和通信的安全性。

2.2.4 EnclaveInitiator文件夹

  • EnclaveInitiator.cpp
// Enclave1.cpp : Defines the exported functions for the .so application
#include "sgx_eid.h" // 用于SGX Enclave ID定义的头文件
#include "EnclaveInitiator_t.h" // 自动生成的可信部分头文件,包含发起者Enclave的ECALL声明
#include "EnclaveMessageExchange.h" // 消息交换相关头文件
#include "error_codes.h" // 自定义错误码头文件
#include "Utility_E1.h" // 实用函数头文件
#include "sgx_dh.h" // 用于Diffie-Hellman密钥交换的SGX头文件
#include "sgx_utils.h" // 用于SGX实用函数的头文件
#include <map> // 标准库map,用于字典结构#define UNUSED(val) (void)(val) // 忽略未使用参数的宏定义
#define RESPONDER_PRODID 1 // 响应者Enclave的产品IDstd::map<sgx_enclave_id_t, dh_session_t> g_src_session_info_map; // 定义全局映射,存储源会话信息dh_session_t g_session; // 定义一个全局会话变量// 这是响应者Enclave的MRSIGNER,需要与响应者Enclave的签名密钥一致
// 请在您的项目中替换为您的响应者Enclave的真实MRSIGNER!!!(参考上面MRSIGNER的生成方法,这里已经替换)
// 获取签名的Enclave的MRSIGNER的命令:<SGX_SDK安装路径>/bin/x64/sgx_sign dump -enclave <已签名的Enclave,.signer.so文件> -dumpfile mrsigner.txt
// 在mrsigner.txt中找到签名的Enclave的MRSIGNER(mrsigner->value:),然后替换下面的值
sgx_measurement_t g_responder_mrsigner = {{0x68, 0xfb, 0x5b, 0x3c, 0x69, 0x38, 0x34, 0x4f, 0x7b, 0x0e, 0x3d, 0x68, 0x2a, 0x46, 0x36, 0x70, 0xca, 0xc5, 0x2b, 0xf2, 0xfd, 0xf6, 0xc8, 0x90, 0x48, 0xc3, 0x6d, 0xcb, 0x85, 0x3f, 0xc8, 0x59 }
};
/* 函数描述:* 这是创建ECDH会话的ECALL例程。* 当成功创建ECDH会话时,会话上下文保存到g_session中。* */
extern "C" uint32_t test_create_session()
{return create_session(&g_session); // 调用create_session函数创建会话
}/* 函数描述:* 这是与ECDH对等方传输消息的ECALL例程。* */
uint32_t test_message_exchange()
{ATTESTATION_STATUS ke_status = SUCCESS; // 定义验证状态uint32_t target_fn_id, msg_type; // 目标函数ID和消息类型char* marshalled_inp_buff; // 序列化输入缓冲区指针size_t marshalled_inp_buff_len; // 序列化输入缓冲区长度char* out_buff; // 输出缓冲区指针size_t out_buff_len; // 输出缓冲区长度size_t max_out_buff_size; // 最大输出缓冲区大小char* secret_response; // 秘密响应指针uint32_t secret_data; // 秘密数据target_fn_id = 0;msg_type = MESSAGE_EXCHANGE;max_out_buff_size = 50;   // 假设响应消息中的最大有效负载大小为50字节,出于演示目的secret_data = 0x12345678; // 秘密数据在此仅用于演示目的// 将秘密数据序列化到缓冲区中ke_status = marshal_message_exchange_request(target_fn_id, msg_type, secret_data, &marshalled_inp_buff, &marshalled_inp_buff_len);if(ke_status != SUCCESS){return ke_status;}// 核心参考代码函数ke_status = send_request_receive_response(&g_session, marshalled_inp_buff,marshalled_inp_buff_len, max_out_buff_size, &out_buff, &out_buff_len);if(ke_status != SUCCESS){SAFE_FREE(marshalled_inp_buff);SAFE_FREE(out_buff);return ke_status;}// 反序列化秘密响应数据ke_status = umarshal_message_exchange_response(out_buff, &secret_response);if(ke_status != SUCCESS){SAFE_FREE(marshalled_inp_buff);SAFE_FREE(out_buff);return ke_status;}SAFE_FREE(marshalled_inp_buff);SAFE_FREE(out_buff);SAFE_FREE(secret_response);return SUCCESS;
}/* 函数描述:* 这是关闭安全会话的ECALL接口*/
extern "C" uint32_t test_close_session()
{ATTESTATION_STATUS ke_status = SUCCESS; // 定义验证状态ke_status = close_session(&g_session); // 调用close_session函数关闭会话// 擦除会话上下文memset(&g_session, 0, sizeof(dh_session_t));return ke_status;
}/* 函数描述:* 验证对等Enclave的身份。* 出于演示目的,验证以下几点:*   1. 对等Enclave的MRSIGNER符合预期*   2. 对等Enclave的产品ID(Prod ID)符合预期*   3. 对等Enclave的属性合理:它是已初始化的Enclave;在非调试配置中,Enclave未以调试模式加载。* */
extern "C" uint32_t verify_peer_enclave_trust(sgx_dh_session_enclave_identity_t* peer_enclave_identity)
{if (!peer_enclave_identity)return INVALID_PARAMETER_ERROR;// 检查对等Enclave的MRSIGNER,请在您的项目中启用此检查!!!这步很关键!!!// 注意:一定是上面的g_responder_mrsigner替换为真实的签名值后才能验证通过!!!if (memcmp((uint8_t *)&peer_enclave_identity->mr_signer, (uint8_t*)&g_responder_mrsigner, sizeof(sgx_measurement_t)))return ENCLAVE_TRUST_ERROR;// 检查对等Enclave的产品ID和Enclave属性(应已初始化)if (peer_enclave_identity->isv_prod_id != RESPONDER_PRODID || !(peer_enclave_identity->attributes.flags & SGX_FLAGS_INITTED))return ENCLAVE_TRUST_ERROR;// 检查Enclave未以调试模式加载,除非项目是为调试目的构建的
#if defined(NDEBUG)if (peer_enclave_identity->attributes.flags & SGX_FLAGS_DEBUG)return ENCLAVE_TRUST_ERROR;
#endifreturn SUCCESS;
}/* 函数描述:操作输入秘密并生成输出秘密* */
uint32_t get_message_exchange_response(uint32_t inp_secret_data)
{uint32_t secret_response;// 用户应使用更复杂的加密方法来保护他们的秘密,下面的例子只是一个简单的示例secret_response = inp_secret_data & 0x11111111;return secret_response;
}// 从请求消息生成响应
/* 函数描述:* 处理请求消息并生成响应* 参数描述:*   [输入] decrypted_data: 指向解密消息的指针*   [输出] resp_buffer: 指向响应消息的指针,缓冲区在本函数内分配*   [输出] resp_length: 指向响应长度的指针* */
extern "C" uint32_t message_exchange_response_generator(char* decrypted_data,char** resp_buffer,size_t* resp_length)
{ms_in_msg_exchange_t *ms;uint32_t inp_secret_data;uint32_t out_secret_data;if(!decrypted_data || !resp_length){return INVALID_PARAMETER_ERROR;}ms = (ms_in_msg_exchange_t *)decrypted_data;if(umarshal_message_exchange_request(&inp_secret_data, ms) != SUCCESS)return ATTESTATION_ERROR;out_secret_data = get_message_exchange_response(inp_secret_data);if(marshal_message_exchange_response(resp_buffer, resp_length, out_secret_data) != SUCCESS)return MALLOC_ERROR;return SUCCESS;
}

功能分析:
创建会话 test_create_session
功能:调用 create_session 函数创建一个ECDH会话,并将会话上下文保存到全局变量 g_session 中。
实现:直接调用 create_session 函数并返回其结果。
消息交换 test_message_exchange
功能:与ECDH对等方传输消息,包括序列化发送和反序列化接收。

  • EnclaveInitiator.edl
enclave {include "sgx_eid.h"include "datatypes.h"include "dh_session_protocol.h"trusted{public uint32_t test_create_session();public uint32_t test_message_exchange();public uint32_t test_close_session();};untrusted{uint32_t session_request_ocall([out] sgx_dh_msg1_t *dh_msg1,[out] uint32_t *session_id);uint32_t exchange_report_ocall([in] sgx_dh_msg2_t *dh_msg2, [out] sgx_dh_msg3_t *dh_msg3, uint32_t session_id);uint32_t send_request_ocall(uint32_t session_id, [in, size = req_message_size] secure_message_t* req_message, size_t req_message_size, size_t max_payload_size, [out, size=resp_message_size] secure_message_t* resp_message, size_t resp_message_size);uint32_t end_session_ocall(uint32_t session_id);};
};

   注意,这里只有一点需要解释,在Initiator enclave中定义的edl文件主要包含trusted和untrusted两部分,untrusted部分是该enclave主动发起调用的OCALL接口,这主要因为Initiator enclave作为认证的发起方需要主动向外界发起调用,而下面的Responder Enclave的edl文件中就只有trusted部分而没有untrusted,因为Responder Enclave只要被动响应而不需要主要对外界发起。捋清楚这一点有助于帮我们分析整个程序流程!

2.2.5 EnclaveResponder文件夹

// Enclave2.cpp : Defines the exported functions for the DLL application
#include "sgx_eid.h" // 用于SGX Enclave ID定义的头文件
#include "EnclaveResponder_t.h" // 自动生成的可信部分头文件,包含响应者Enclave的ECALL声明
#include "EnclaveMessageExchange.h" // 消息交换相关头文件
#include "error_codes.h" // 自定义错误码头文件
#include "Utility_E2.h" // 实用函数头文件
#include "sgx_dh.h" // 用于Diffie-Hellman密钥交换的SGX头文件
#include "sgx_utils.h" // 用于SGX实用函数的头文件
#include <map> // 标准库map,用于字典结构#define UNUSED(val) (void)(val) // 忽略未使用参数的宏定义std::map<sgx_enclave_id_t, dh_session_t> g_src_session_info_map; // 定义全局映射,存储源会话信息// 这是预期的发起者的MRSIGNER,请在您的项目中替换为您的响应者Enclave的MRSIGNER!!!(这里已替换为我的真实值)
// 获取签名的Enclave的MRSIGNER的命令:<SGX_SDK安装路径>/bin/x64/sgx_sign dump -enclave <已签名的Enclave> -dumpfile mrsigner.txt
// 在mrsigner.txt中找到签名的Enclave的MRSIGNER(mrsigner->value:),然后替换下面的值
sgx_measurement_t g_initiator_mrsigner = {{0x39, 0xc7, 0xa4, 0xae, 0xa9, 0x64, 0xb9, 0x8e, 0xda, 0xa7, 0x22, 0x69, 0x98, 0x85, 0x0f, 0x92, 0x4d, 0xc3, 0x30, 0xa0, 0xac, 0xfe, 0x8f, 0xe0, 0xe2, 0x0a, 0xef, 0x9a, 0xd4, 0xde, 0xa9, 0x48    }
};/* 函数描述:* 验证对等Enclave的身份* 出于演示目的,验证以下几点:*   1. 对等Enclave的MRSIGNER符合预期*   2. 对等Enclave的产品ID(Prod ID)符合预期*   3. 对等Enclave的属性合理,应为已初始化且未设置DEBUG属性(除非项目是以DEBUG选项构建的)* */
extern "C" uint32_t verify_peer_enclave_trust(sgx_dh_session_enclave_identity_t* peer_enclave_identity)
{if (!peer_enclave_identity)return INVALID_PARAMETER_ERROR; // 检查输入参数是否为空// 检查对等Enclave的MRSIGNER,请在您的项目中启用此检查!!!这步很关键!!!// 注意:一定是上面的g_responder_mrsigner替换为真实的签名值后才能验证通过!!!if (memcmp((uint8_t *)&peer_enclave_identity->mr_signer, (uint8_t*)&g_initiator_mrsigner, sizeof(sgx_measurement_t)))return ENCLAVE_TRUST_ERROR;// 检查对等Enclave的产品ID和属性(应已初始化)if (peer_enclave_identity->isv_prod_id != 0 || !(peer_enclave_identity->attributes.flags & SGX_FLAGS_INITTED))return ENCLAVE_TRUST_ERROR;// 检查Enclave未以调试模式加载,除非项目是为调试目的构建的#if defined(NDEBUG)if (peer_enclave_identity->attributes.flags & SGX_FLAGS_DEBUG)return ENCLAVE_TRUST_ERROR;#endifreturn SUCCESS; // 验证成功
}/* 函数描述:操作输入秘密并生成输出秘密 */
uint32_t get_message_exchange_response(uint32_t inp_secret_data)
{uint32_t secret_response;// 用户应使用更复杂的加密方法来保护他们的秘密,下面的例子只是一个简单的示例secret_response = inp_secret_data & 0x11111111;return secret_response;
}/* 函数描述:从请求消息生成响应* 参数描述:* [输入] decrypted_data: 指向解密数据的指针* [输出] resp_buffer: 指向响应消息的指针,缓冲区在本函数中分配* [输出] resp_length: 指向响应长度的指针* */
extern "C" uint32_t message_exchange_response_generator(char* decrypted_data,char** resp_buffer,size_t* resp_length)
{ms_in_msg_exchange_t *ms; // 定义消息交换结构体指针uint32_t inp_secret_data; // 定义输入秘密数据uint32_t out_secret_data; // 定义输出秘密数据if (!decrypted_data || !resp_length)return INVALID_PARAMETER_ERROR; // 检查输入参数是否为空ms = (ms_in_msg_exchange_t *)decrypted_data; // 将解密数据转换为消息交换结构体if (umarshal_message_exchange_request(&inp_secret_data, ms) != SUCCESS)return ATTESTATION_ERROR; // 解组消息交换请求失败out_secret_data = get_message_exchange_response(inp_secret_data); // 生成响应数据if (marshal_message_exchange_response(resp_buffer, resp_length, out_secret_data) != SUCCESS)return MALLOC_ERROR; // 序列化消息交换响应失败return SUCCESS; // 成功返回
}

功能分析
验证对等Enclave身份 verify_peer_enclave_trust
功能:验证对等Enclave的身份以确保其可信度。
实现:检查输入参数 peer_enclave_identity 是否为空,避免空指针错误。
检查对等Enclave的MRSIGNER是否符合预期(预留代码,实际使用需要取消注释)。
检查对等Enclave的产品ID和属性,确保Enclave已初始化且未设置DEBUG属性。
如果项目是非调试配置,检查Enclave未以调试模式加载。
验证成功则返回 SUCCESS,否则返回适当的错误码。
生成消息交换响应 get_message_exchange_response
功能:根据输入数据生成响应数据。
实现:对输入数据进行简单的按位与操作生成响应数据(实际使用中应使用更复杂的加密方法保护秘密数据)。返回生成的响应数据。
处理请求消息并生成响应 message_exchange_response_generator
功能:从请求消息生成响应消息。
实现:检查输入的解密数据和响应长度指针是否为空,确保有有效输入。将解密数据转换为消息交换结构体。解组请求消息以获取输入秘密数据。调用 get_message_exchange_response 生成响应数据。序列化响应数据到缓冲区。返回成功状态或适当的错误码。代码相关性和用途,这段代码用于在SGX Enclave环境中实现本地认证的响应者端。通过验证对等Enclave的身份,确保通信双方的可信度。处理消息交换请求并生成相应的响应,以确保安全通信和数据交换。
注意事项
在实际项目中,应根据实际情况启用对MRSIGNER的检查,以增强安全性。加密方法应根据实际需求进行调整,以确保数据的安全性和完整性。

  • EnclaveResponder.edl
enclave {include "sgx_eid.h"include "datatypes.h"include "../Include/dh_session_protocol.h"trusted{public uint32_t session_request([out] sgx_dh_msg1_t *dh_msg1, [out] uint32_t *session_id);public uint32_t exchange_report([in] sgx_dh_msg2_t *dh_msg2, [out] sgx_dh_msg3_t *dh_msg3, uint32_t session_id);public uint32_t generate_response([in, size = req_message_size] secure_message_t* req_message, size_t req_message_size, size_t max_payload_size, [out, size=resp_message_size] secure_message_t* resp_message, size_t resp_message_size, uint32_t session_id);public uint32_t end_session(uint32_t session_id);};};

2.3 编译执行

2.3.1 Makefile文件分析

include buildenv.mk
# 包含外部的环境变量定义文件 buildenv.mk。# 根据 SGX_MODE 和相关标志设置 Build_Mode 的变量。
# 如果 SGX_MODE 是硬件模式 (HW), 则根据 SGX_DEBUG 和 SGX_PRERELEASE 决定具体的构建模式。
ifeq ($(SGX_MODE), HW)
ifeq ($(SGX_DEBUG), 1)Build_Mode = HW_DEBUG
else ifeq ($(SGX_PRERELEASE), 1)Build_Mode = HW_PRERELEASE
elseBuild_Mode = HW_RELEASE
endif
endif
# 如果 SGX_MODE 是仿真模式 (SIM), 同样根据 SGX_DEBUG 和 SGX_PRERELEASE 决定具体的构建模式。
ifeq ($(SGX_MODE), SIM)
ifeq ($(SGX_DEBUG), 1)Build_Mode = SIM_DEBUG
else ifeq ($(SGX_PRERELEASE), 1)Build_Mode = SIM_PRERELEASE
elseBuild_Mode = SIM_RELEASE
endif
endif# 定义子目录列表,包含 AppInitiator, AppResponder, EnclaveInitiator, EnclaveResponder 和 App。
SUB_DIR := AppInitiator AppResponder EnclaveInitiator EnclaveResponder App# 如果 OUTDIR 变量不为空,创建输出目录。
ifneq ($(OUTDIR),)
$(shell mkdir -p $(OUTDIR))
endif# 定义伪目标 all 和 clean。伪目标不会被当作实际文件处理。
.PHONY: all clean# all规则,构建每个子目录中的目标。
# 根据不同的构建模式,输出对应的构建模式信息。
all:for dir in $(SUB_DIR); do \$(MAKE) -C $$dir; \done
ifeq ($(Build_Mode), HW_DEBUG)@echo "The project has been built in hardware debug mode."
else ifeq ($(Build_Mode), HW_RELEAESE)@echo "The project has been built in hardware release mode."
else ifeq ($(Build_Mode), HW_PRERELEAESE)@echo "The project has been built in hardware pre-release mode."
else ifeq ($(Build_Mode), SIM_DEBUG)@echo "The project has been built in simulation debug mode."
else ifeq ($(Build_Mode), SIM_RELEAESE)@echo "The project has been built in simulation release mode."
else ifeq ($(Build_Mode), SIM_PRERELEAESE)@echo "The project has been built in simulation pre-release mode."
endif# clean规则,删除输出目录,然后清理每个子目录中的目标文件,并删除 util 目录下的所有 .o 文件。
clean:@rm -rf $(OUTDIR)for dir in $(SUB_DIR); do \$(MAKE) -C $$dir clean; \donerm -f util/*.o

    注意,该Makefile文件依赖于另外一个buildenv.mk文件以及会调用各个目录下的子Makefile文件(App、AppInitiator、AppResponder、EnclaveInitiator、EnclaveResponder),在这些依赖Makefile文件中定义了一些环境变量的值,以及当前目录下编译的方法,此处不再细节展开。
    直接执行make编译。

2.3.2 执行

  • 单线程执行,也即一个进程完成全部本地认证流程(即充当发起者也充当响应者);
./app
succeed to load enclaves.
succeed to establish secure channel.
Succeed to exchange secure message...
Succeed to close Session...
  • 双线程执行(一个线程充当发起者appinitiator ,另一个线程充当响应者appresponder

当响应者进程先启动时:

./appresponder 
Server is ON...
Press Ctrl+C to exit...

启动发起者进程会成功认证并输出如下:

./appinitiator 
succeed to load enclave libenclave_initiator.signed.so
succeed to establish secure channel.
Succeed to exchange secure message...
Succeed to close Session...

但是在没有启动响应者进程的情况下,七点发起者进程会认证失败并输出如下(主要是connection error):

./appinitiator 
succeed to load enclave libenclave_initiator.signed.so
connection error, Connection refused, line 80.
fail to send and receive message.
failed to establish secure channel: ECALL return 0x0, error code is 0xe3.

2.4 总结

    在本地认证(Local Attestation)过程中,SGX Enclave间通过相互认证以确保彼此的可信性。以下是工程中主要代码模块之间的调用和交互关系的总结。

  1. 项目结构: 一个典型的SGX Local Attestation项目通常包括以下主要模块:
    AppInitiator:发起本地认证请求的应用程序。
    AppResponder:响应本地认证请求的应用程序。
    EnclaveInitiator:包含发起本地认证的逻辑的Enclave。
    EnclaveResponder:包含响应本地认证的逻辑的Enclave。

  2. 流程图概述

AppInitiator  <====>  EnclaveInitiator|V
AppResponder  <====>  EnclaveResponder
  1. 具体流程说明
    3.1 初始化阶段
    加载Enclave:
    AppInitiator和AppResponder分别调用sgx_create_enclave函数加载各自的Enclave。
    EnclaveInitiator和EnclaveResponder在加载后各自初始化所需的数据和上下文。
    3.2 会话创建阶段
    发起者创建会话请求:
    AppInitiator通过调用test_create_session函数发起会话创建请求,ECALL进入EnclaveInitiator。
    EnclaveInitiator调用create_session函数,生成会话密钥并保存会话上下文(dh_session_t)。
    发送会话请求到响应者:
    EnclaveInitiator通过OCALL接口session_request_ocall将会话请求消息(包含ECDH消息1)发送给AppResponder。
    3.3 会话处理阶段
    处理会话请求:
    AppResponder接收会话请求消息后,通过OCALL接口调用EnclaveResponder的session_request函数。
    EnclaveResponder生成响应消息(包含ECDH消息2和Session ID),并通过OCALL接口exchange_report_ocall返回给AppInitiator。
    处理会话响应:
    AppInitiator接收到会话响应消息后,通过ECALL接口调用EnclaveInitiator的exchange_report函数。
    EnclaveInitiator处理会话响应消息,生成会话密钥并保存上下文。
    3.4 消息交换阶段
    发送消息:
    AppInitiator调用test_message_exchange函数,通过ECALL进入EnclaveInitiator。
    EnclaveInitiator通过OCALL接口send_request_receive_response发送消息请求到AppResponder。
    处理消息请求:
    AppResponder接收消息请求后,通过OCALL接口调用EnclaveResponder的generate_response函数。
    EnclaveResponder处理消息并生成响应消息,返回给AppInitiator。
    接收消息响应:
    AppInitiator接收到消息响应后,EnclaveInitiator处理响应数据,完成一次消息交换。
    3.5 会话关闭阶段
    关闭会话:
    AppInitiator调用test_close_session函数,通过ECALL进入EnclaveInitiator。
    EnclaveInitiator调用close_session函数关闭会话,并通过OCALL接口end_session_ocall通知AppResponder。
    处理会话关闭请求:
    AppResponder接收会话关闭请求后,通过OCALL接口调用EnclaveResponder的end_session函数销毁会话。

  2. 代码模块交互
    以下是各主要模块的调用关系:
    4.1 AppInitiator/Main.cpp 调用 sgx_create_enclave 初始化 EnclaveInitiator。
    4.2 EnclaveInitiator 有以下交互:
    ECALL:test_create_session 创建会话。
    OCALL:session_request_ocall 从 AppResponder 获取会话响应。
    处理会话响应的 OCALL:exchange_report_ocall。
    ECALL:test_message_exchange 发送消息并接收响应。
    处理消息交换的 OCALL:send_request_receive_response。
    ECALL:test_close_session 关闭会话。
    处理会话关闭的 OCALL:end_session_ocall。
    4.3 AppResponder/Main.cpp 调用 sgx_create_enclave 初始化 EnclaveResponder。
    4.4 EnclaveResponder 有以下交互:
    OCALL:generate_and_send_session_msg1_resp 生成并发送会话消息1。
    处理会话请求的 OCALL:process_exchange_report。
    处理消息交换请求的 OCALL:process_msg_transfer。
    处理会话关闭请求的 OCALL:process_close_req。

三.参考文献

王进文, 江勇, 李琦, 等. SGX 技术应用研究综述[J]. 网络新媒体技术, 2017, 6(5): 3-9.

四.感谢支持

    完结撒花!后续将持续输出,形成Intel SGX的系列教程,并结合密码学算法设计更复杂功能。希望看到这里的小伙伴能点个关注,也欢迎志同道合的小伙伴一起广泛交流。
    码字实属不易,如果本文对你有10分帮助,就赏个10分把,感谢各位大佬支持!

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/37983.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【SpringBoot】SpringBoot核心启动流程源码解析

SpringBoot总体流程 当我们启动一个SpringBoot程序的时候&#xff0c;只需要一个main方法就可以启动&#xff0c;但是对于其中流程时如何执行的&#xff0c;以及如何调用spring的IOC和AOP机制&#xff0c;本篇带着这个问题来整体体系化的梳理下流程。 SpringBootApplication …

OFDM技术简介——背景

l 1966 年&#xff0c; R. W. Chang 提出在带限信道中用 正交信号 同时传输 多路数据 的原理&#xff0c;同时这种传输方式保证系统中不存在符号间串扰和子信道间干扰&#xff0c;该技术可以有效提高频谱利用率&#xff0c;可以有效对抗信道多径衰落。 l 1971 年&#xff0c; …

vue 自定义组件 实现跟使用

新建文件组件 选择器作用 ~ 波浪线这个是选择 li 后面的所有 a标签 调用 到使用的文件下引入 使用 效果

每日算法-插值查找

1.概念 插值查找是一种改良版的二分查找,其优势在于,对于较为均匀分布的有序数列,能够更快地使得mid中间游标快速接近目标值. 2.计算公式 中间游标计算公式. 公式说明: 公式的主要思路是,以第一次定位mid中间游标为例, 在接近平均分配的情况下,左右游标之间的差值表示总计供…

Animate软件基础:从单个图层复制帧

在使用Animate软件制作内容时&#xff0c;有时会需要复制制作好的部分动画&#xff0c;到新的场景中&#xff0c;或者从一个制作文件中复制内容到另一个制作文件&#xff0c;这就需要复制帧的操作&#xff0c;这里讲一下从单个图层复制帧的方法。 在图层中选择一组帧。要选择整…

React@16.x(44)路由v5.x(9)源码(1)- path-to-regexp

目录 1&#xff0c;作用2&#xff0c;实现获取 match 对象2.1&#xff0c;match 对象的内容2.2&#xff0c;注意点2.3&#xff0c;实现 1&#xff0c;作用 之前在介绍 2.3 match 对象 时&#xff0c;提到了 react-router 使用第3方库 path-to-regexp 来匹配路径正则。 我们也…

新能源汽车 LabCar 测试系统方案(二)

什么是LabCar测试 LabCar测试目标是进行整车黄板台架功能测试&#xff0c;用于整车开发和测试阶段&#xff0c;满足设计人员和测试人员的试验需求&#xff0c;以验证整车性能&#xff0c;减少开发工作量。系统主要用于测试静态及动态工况下的纯电动汽车的各项功能实现情况。 …

git 用户名密码Clone代码

#密码中包含&#xff0c;则使用%40代表 cd /disk03/wwwroot/GitDemo/BuildTemp && git clone -b dev --single-branch http://root:test%40123192.168.31.104/root/SaaS.Auto.Api.git git pull origin dev 今天使用LibGit2Sharp在Linux上Clone代码时报错&#xff0c;因…

【计算机网络】HTTP——基于HTTP的功能追加协议(个人笔记)

学习日期&#xff1a;2024.6.29 内容摘要&#xff1a;基于HTTP的功能追加协议和HTTP/2.0 HTTP的瓶颈与各功能追加协议 需求的产生 在Facebook、推特、微博等平台&#xff0c;每分每秒都会有人更新内容&#xff0c;我们作为用户当然希望时刻都能收到最新的消息&#xff0c;为…

Python | Leetcode Python题解之第188题买卖股票的最佳时机IV

题目&#xff1a; 题解&#xff1a; class Solution:def maxProfit(self, k: int, prices: List[int]) -> int:if not prices:return 0n len(prices)k min(k, n // 2)buy [0] * (k 1)sell [0] * (k 1)buy[0], sell[0] -prices[0], 0for i in range(1, k 1):buy[i] …

尚硅谷vue2的todolist案例解析,基本概括了vue2所有知识点,结尾有具体代码,复制粘贴学习即可

脚手架搭建 1-初始化脚手架&#xff08;全局安装&#xff09; npm install -g vue/cli2-切换到创建项目的空目录下 vue create xxxx整体结构 整体思路 App定义所有回调方法 增删改查 还有统一存放最终数据&#xff0c;所有子组件不拿数据&#xff0c;由App下发数据&#xf…

App托管服务分发平台 index-uplog.php 文件上传致RCE漏洞复现

0x01 产品简介 App托管服务分发平台是一个为开发者提供全面、高效、安全的应用程序托管、分发和推广服务的平台。开发者可以将自己开发的应用程序上传到平台上,平台会对上传的应用程序进行审核,确保应用的质量和安全性。平台会根据开发者的要求,将应用分发到不同的应用市场…

5G RAN

两个entity&#xff1a;NodeB、UE entity之间传输数据的东东 entity内部的流水线岗位&#xff1a;L3/L2/L1 岗位之间是消息交互/信令交互

利用labelme制作自己的coco数据集(labelme转coco数据集)

最近刚接触学习mmdetection&#xff0c;需要用到coco格式的数据集。 1.安装labelme 建议在conda(base)环境下安装&#xff08;前提是需要下载anaconda&#xff09;,下面是我已经装过的情况。 2.进入labelme环境下 中间可能会提示安装其它库&#xff0c;自行装上就行。 这里的…

智能社区服务小程序的设计

管理员账户功能包括&#xff1a;系统首页&#xff0c;个人中心&#xff0c;用户管理&#xff0c;房屋信息管理&#xff0c;住户信息管理&#xff0c;家政服务管理&#xff0c;家政预约管理&#xff0c;报修信息管理 微信端账号功能包括&#xff1a;系统首页&#xff0c;房屋信…

Flood Fill——AcWing 1097. 池塘计数

Flood Fill 定义 Flood Fill算法&#xff0c;又称为洪水填充或种子填充算法&#xff0c;是一种在图或网格数据结构中探索连通区域的搜索算法。它从一个初始节点&#xff08;种子点&#xff09;开始&#xff0c;将具有相同属性&#xff08;如颜色、值等&#xff09;的相邻节点…

P4. 微服务: 匹配系统(下)

P4. 微服务: 匹配系统 下 0 概述1 游戏同步系统1.1 游戏同步的设计1.2 游戏同步的实现 2 匹配系统微服务的实现2.1 微服务概述2.2 匹配系统接口url的实现2.3 微服务之间的通信2.4 匹配逻辑的实现2.5 匹配系统的权限控制 3 bug的解决3.1 自己匹配自己3.2 断开连接问题 0 概述 本…

大数据之Linux部署常用命令脚本封装

文章目录 编写集群命令执行脚本xcall集群命令执行1. 脚本需求2. 需求分析3. 脚本实现3.1 创建脚本存放目录3.2 编写xcall脚本3.3 修改脚本执行权限3.4 测试脚本 编写集群分发脚本xsync集群分发脚本1. 脚本需求2. 需求分析3. 脚本实现3.1 创建脚本存放目录3.2 编写xsync脚本3.3 …

计算机毕业设计PyFlink+Spark+Hive民宿推荐系统 酒店推荐系统 民宿酒店数据分析可视化大屏 民宿爬虫 民宿大数据 知识图谱 机器学习

本科毕业设计(论文) 开题报告 学院 &#xff1a; 计算机学院 课题名称 &#xff1a; 民宿数据可视化分析系统的设计与实现 姓名 &#xff1a; 庄贵远 学号 &#xff1a; 2020135232 专业 &#xff1a; 数据科学与大数据技术 班级 &#xff1a; 20大数据本科2…

重温react-06(初识函数组件和快速生成格式的插件使用方式)

开始 函数组件必然成为未来发展的趋势(个人见解),总之努力的去学习,才能赚更多的钱.加油呀! 函数组件的格式 import React from reactexport default function LearnFunction01() {return (<div>LearnFunction01</div>) }以上是函数式组件的组基本的方式 快捷生…