Ubuntu 18.04下Intel SGX应用程序程序开发——获得OCALL调用的返回值

本文中,我们介绍在Enclave函数中调用不可信OCALL函数,并获得OCALL函数的返回值。

1. 复制SampleEnclave示例并建立自己的OcallRetSum项目

SampleEnclave示例实现了安全printf函数,可以被用来安全打印我们需要打印的字符串。我们使用OCALL计算两个整数之间的所有整数的累加和(包括这两个整数),并调用printf函数打印复制的目的字符串。我们把SampleEnclave示例复制到自己的文件夹中,基于该示例,我们开发一个在Enclave函数中调用OCALL函数计算两个整数之间的所有整数的累加和(包括这两个整数)。我们把复制得到的SampleEnclave文件夹名称改成OcallRetSum文件夹。

将文件中的冗余文件删除仅剩下图目录

image-20210305113512956

2. 各个文件内容介绍

2.1 App文件夹

该文件夹存放应用程序中的不可信代码部分,共有2个文件和2个文件夹,App.cpp文件、App.h文件、Edger8rSyntax文件夹和TrustedLibrary文件夹

  • App.cpp文件:该文件是应用程序中的不可信部分代码,其中包括了创建Enclave及销毁Enclave的代码,也定义了一些相关的返回码供使用者查看Enclave程序的执行状态。其中的main函数是整个项目的入口函数。是我们要修改的文件。

  • App.h文件:该文件是应用程序中的不可信部分代码的头文件,定义了一些宏常量和函数声明。我们不用进行更改。

  • Edger8rSyntax文件夹:该文件夹提供了一些工具,我们不用进行更改。

  • TrustedLibrary文件夹:该文件夹提供了一些函数库,我们不用进行更改。、

2.2 Enclave文件夹

该文件夹存放应用程序中的可信代码部分和可信与不可信代码接口文件,共有6个文件和2个文件夹,Enclave.config.xml文件、Enclave.cpp文件、Enclave.h文件、Enclave.edl文件、Enclave.lds文件、Enclave_private.pem文件、Edger8rSyntax文件夹和TrustedLibrary文件夹

  • Enclave.config.xml文件:该文件是Enclave的配置文件,定义了Enclave的元数据信息,我们可以不用更改。

  • Enclave.cpp文件:该文件是应用程序中的可信部分代码,包括了可信函数的实现,是我们要修改的文件。

  • Enclave.h文件:该文件是应用程序中的可信部分代码的头文件,定义了一些宏常量和函数声明。是我们要修改的文件。

  • Enclave.edl文件:该文件是Enclave的接口定义文件,定义了不可信代码调用可信代码的ECALL函数接口和可信代码调用不可信代码的OECALL函数接口,trusted{}中定义了ECALL函数接口,untrusted{}中定义了OECALL函数接口是我们要修改的文件。

  • Enclave.lds文件:该文件定义了一些Enclave可执行文件信息,我们不用更改。

  • Enclave_private.pem文件:该文件是SGX生成的私钥,我们不用更改。

  • Edger8rSyntax文件夹:该文件夹提供了一些工具,我们不用进行更改。

  • TrustedLibrary文件夹:该文件夹提供了一些函数库,我们不用进行更改。

2.3 Include文件夹

该文件夹存放被Enclave接口定义文件Enclave.edl使用的头文件,包括一些宏定义,我们不用管它。共只有一个user_types.h文件。

  • user_types.h文件:该文件定义了用户自定义的类型。

2.4 Makefile文件

该文件是项目的编译文件,定义了项目的编译信息,详细说明还请参考//blog.csdn.net/daichunkai123/article/details/78027895。是需要修改的部分。

3. 改写代码

需要修改的文件问为Enclave.edl文件、Enclave.cpp文件、Enclave.h文件、App.cpp文件和Makefile文件

3.1 Enclave.edl文件修改

我们要在Enclave.edl文件中定义不可信代码调用可信函数的ECALL接口,比如我们定义的可信函数是ecall_myretsum,在该函数中我们调用OCALL函数ocall_retsum计算两个整数之间的所有整数累加和,该函数没有参数也没有返回值。

但是定义的可信函数式ocall_retsum时,该函数有2个参数:起始整数start和结束整数end。该函数有一个整数返回值。

3.1.1 添加ECALL函数ecall_myretsum,将以下代码添加到Enclave.edl文件中的untrusted{}之前。

trusted{
        public void ecall_myretsum();
};

同时因为需要start和end参数的返回值,还将下列代码添加到“untrusted{}`中

size_t ocall_retsum(size_t start, size_t end);

3.1.2 修改后的Enclave.edl文件如下所示

enclave {
    
    include "user_types.h" /* buffer_t */

    /* Import ECALL/OCALL from sub-directory EDLs.
     *  [from]: specifies the location of EDL file. 
     *  [import]: specifies the functions to import, 
     *  [*]: implies to import all functions.
     */
    
    from "Edger8rSyntax/Types.edl" import *;
    from "Edger8rSyntax/Pointers.edl" import *;
    from "Edger8rSyntax/Arrays.edl" import *;
    from "Edger8rSyntax/Functions.edl" import *;

    from "TrustedLibrary/Libc.edl" import *;
    from "TrustedLibrary/Libcxx.edl" import ecall_exception, ecall_map;
    from "TrustedLibrary/Thread.edl" import *;

    /* 
     * ocall_print_string - invokes OCALL to display string buffer inside the enclave.
     *  [in]: copy the string buffer to App outside.
     *  [string]: specifies 'str' is a NULL terminated buffer.
     */

		trusted{
       	 	public void ecall_myretsum();
    	};


    untrusted {
        void ocall_print_string([in, string] const char *str);
				size_t ocall_retsum(size_t start, size_t end);
    };

};

3.2 Enclave.cpp文件修改

在Enclave.cpp文件中实现ecall_myretsum函数,该函数比较简单,就是定义两个整数参数和一个存储返回值的整数变量,并调用OCALL函数ocall_retsum计算累加和,获得计算结果,然后调用printf函数打印计算的累加和。

这个地方需要注意的是:获得OCALL函数的返回值和获得普通的C/C++函数的返回值是不一样的,获得C/C++函数的返回值可以直接通过赋值得到,但是获得OCALL函数的返回值需要把存储返回值的变量的地址传给Enclave函数,OCALL函数执行完之后,SGX会把返回值复制到地址参数指向的内存地址处,从而将返回值返回给想返回的变量。

事实上,在SGX中,OCALL函数执行时是有返回值的,返回的是该函数执行的状态,可以通过赋值获得OCALL函数执行的状态,从而判断有没有正确执行。

3.2.1 将以下代码添加到Enclave.cpp文件中

void ecall_myretsum()
{
    size_t start = 5;
    size_t end = 10;
    size_t sum = 0;
 
    ocall_retsum(&sum,start, end);
    printf("%d\n", sum);
}

3.2.2 修改后的Enclave.cpp文件如下所示

#include "Enclave.h"
#include "Enclave_t.h" /* print_string */
#include <stdarg.h>
#include <stdio.h> /* vsnprintf */
#include <string.h>

/* 
 * printf: 
 *   Invokes OCALL to display the enclave buffer to the terminal.
 */
int printf(const char* fmt, ...)
{
    char buf[BUFSIZ] = { '\0' };
    va_list ap;
    va_start(ap, fmt);
    vsnprintf(buf, BUFSIZ, fmt, ap);
    va_end(ap);
    ocall_print_string(buf);
    return (int)strnlen(buf, BUFSIZ - 1) + 1;
}

void ecall_myretsum()
{
    size_t start = 5;
    size_t end = 10;
    size_t sum = 0;
 
    ocall_retsum(&sum,start, end);
    printf("%d\n", sum);
}

3.3 Enclave.h文件修改

我们在Enclave.h文件中添加ecall_myretsum函数声明,该文件修改比较简单。

3.3.1将以下代码添加到Enclave.h文件中

void ecall_myretsum();

3.3.2 修改后的Enclave.h文件如下所示

#ifndef _ENCLAVE_H_
#define _ENCLAVE_H_

#include <stdlib.h>
#include <assert.h>

#if defined(__cplusplus)
extern "C" {
#endif

void printf(const char *fmt, ...);
void ecall_myretsum();

#if defined(__cplusplus)
}
#endif

#endif /* !_ENCLAVE_H_ */

3.4 App.cpp文件修改

我们在该文件中调用自定义的OCALL函数ocall_retsum并调用自定义的ECALL函数ecall_myretsum。

3.4.1 修改main函数

由于我们不需要Edger8rSyntax和TrustedLibrary相关的函数调用,所以我们把main函数中与这两部分相关的函数调用删除,也就是把下面部分删除:

/* Utilize edger8r attributes */
    edger8r_array_attributes();
    edger8r_pointer_attributes();
    edger8r_type_attributes();
    edger8r_function_attributes();

/* Utilize trusted libraries */
		ecall_libc_functions();
		ecall_libcxx_functions();
		ecall_thread_functions();    

image-20210304164923690

3.4.2 main函数中插入函数调用

在main函数之前插入OCALL函数ocall_retsum的实现,该函数计算两个整数之间的所有整数的累加和(包括这两个整数),并返回计算的累加值。即将以下代码插入到main函数之前

size_t ocall_retsum(size_t start, size_t end){  
    size_t sum = 0;  
    for (; start <= end; start++)  
        sum += start;  
    return sum;  
}

在main函数中的Enclave创建和销毁之间插入我们定义的ecall_myretsum函数的调用代码,也就是把以下代码放到sgx_destroy_enclave(global_eid);之前。global_eid参数是必须的,是enclave的id。

ecall_myretsum(global_eid);

3.4.3 删除无关代码

我们也可以将main函数后面的无关代码给删了,也就是删除以下代码。

printf("Info: SampleEnclave successfully returned.\n");
printf("Enter a character before exit ...\n");
getchar();

修改后的main函数及其之前如下所示

size_t ocall_retsum(size_t start, size_t end){  
    size_t sum = 0;  
    for (; start <= end; start++)  
        sum += start;  
    return sum;  
}

/* Application entry */
int SGX_CDECL main(int argc, char *argv[])
{
    (void)(argc);
    (void)(argv);


    /* Initialize the enclave */
    if(initialize_enclave() < 0){
        printf("Enter a character before exit ...\n");
        getchar();
        return -1; 
    }

    ecall_myretsum(global_eid);
    /* Destroy the enclave */
    sgx_destroy_enclave(global_eid);
    
    return 0;
}

3.5 Makefile文件修改

我们在Makefile文件中修改编译相关的信息。主要更改

SGX_SDK ?= /opt/intel/sgxsdk
App_Cpp_Files := App/App.cpp $(wildcard App/Edger8rSyntax/*.cpp) $(wildcard App/TrustedLibrary/*.cpp)

Enclave_Cpp_Files := Enclave/Enclave.cpp $(wildcard Enclave/Edger8rSyntax/*.cpp) $(wildcard Enclave/TrustedLibrary/*.cpp)

3.5.1 修改SGX SDK路径

如果SGX SDK路径不是/opt/intel/sgxsdk,要将其改成为所在目录下/home/zjz/sgxsdk,

image-20210304170155481

3.5.2 修改不可信代码编译的源文件

由于我们的不可信代码可能是多个文件,因此,我们最好在App_Cpp_Files中包括App文件夹中的所有cpp文件,更改后的结果如下。本项目中App_Cpp_Files可以不用更改。

image-20210304170232535

3.5.3 修改可信代码编译的源文件

由于我们的可信代码可能是多个文件,因此,我们最好在Enclave_Cpp_Files中包括App文件夹中的所有cpp文件,更改后的结果如下。本项目中Enclave_Cpp_Files可以不用更改。

image-20210304170133548

4. 编译文件

打开文件下的终端输入make

image-20210305163529173

image-20210305163601054

5. 运行程序

执行 ./app 运行程序

其中遇到一个lib库的问题 使用export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/zjz/sgxsdk/lib64 解决后重新运行

最终成功输出两数之和为45

image-20210305163623973