Intel SGX程序编写

1 背景知识

当程序在enclave里和enclave外切换时,会发生上下文切换、保存CPU状态,参考https://eprint.iacr.org/2016/086.pdf,因此会有一定的开销。enclave内被视为是可信的,enclave外被视为是不可信的。从enclave外调用enclave内的程序为ECALL,从enclave里调用enclave外的程序为OCALL,https://download.01.org/intel-sgx/linux-2.3/docs/Intel_SGX_Developer_Reference_Linux_2.3_Open_Source.pdf,具体如下图所示。
在这里插入图片描述
关于Ubuntu18.04下SGX开发环境的搭建,可以参考:Intel SGX开发环境搭建

2 代码编写

以安装时SDK文件夹下Sample Code里Sample Enclave工程代码为模板,进行修改,这里参考了https://blog.csdn.net/cyLee_/article/details/90200470https://blog.csdn.net/qiu_pengfei/article/details/78803148

2.1 代码结构

App文件夹
在这里插入图片描述
Enclave文件夹
在这里插入图片描述
其它文件
在这里插入图片描述
项目下的.settings是隐藏文件夹,里面的language.settings.xml定义了一些与语言相关的信息。.cproject和.project是隐藏文件,我们也不用管它们。README.txt可以删除。App文件夹下的Edger8rSyntax和TrustedLibrary文件夹及其中的文件在这个项目中是没有用的,我们把它们删除,同理Enclave文件夹下的Edger8rSyntax和TrustedLibrary文件夹在这个项目中也是没有用的,我们把它们删除。最后项目的目录结构如下图所示,参考https://blog.csdn.net/qiu_pengfei/article/details/78803148
在这里插入图片描述

App文件夹:该文件夹存放应用程序中的不可信代码部分,共有2个文件,App.cpp文件和App.h文件。
App.cpp文件:该文件是应用程序中的不可信部分代码,其中包括了创建Enclave及销毁Enclave的代码,也定义了一些相关的返回代码供使用者查看Enclave程序的执行状态。其中的main函数是整个项目的入口函数。是我们要修改的文件。
App.h文件:该文件是应用程序中的不可信部分代码的头文件,定义了一些宏常量和函数声明。
Enclave文件夹:该文件夹存放应用程序中的可信代码部分和可信与不可信代码接口文件,共有6个文件,Enclave.config.xml文件、Enclave.cpp文件、Enclave.h文件、Enclave.edl文件、Enclave.lds文件和Enclave_private.pem文件
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生成的私钥,我们不用更改。
Include文件夹:该文件夹存放被Enclave接口定义文件Enclave.edl使用的头文件,包括一些宏定义,我们不用管它。共有一个文件,user_types.h文件。
user_types.h文件:该文件定义了用户自定义的类型。
Makefile文件:该文件是项目的编译文件,定义了项目的编译信息,详细说明还请参考https://blog.csdn.net/cyLee_/article/details/90200470

2.2 代码修改

这里以EmptyEcallHelloWorld工程为例,这个工程的运行逻辑是App文件夹下的app.cpp调用Ecall_main()函数进入enclave,Ecall_main()函数调用enclave外的Ocall_print()函数,打印”Hello World!” ,然后返回到enclave,再从enclave返回。需要修改的文件包括:Enclave.edl文件、Enclave.cpp文件、Enclave.h文件、App.cpp文件、App.h文件和Makefile文件。
1.Enclave.edl。在该文件中人为将与ECALL和OCALL相关的函数分为trusted和untrusted部分。把与ECALL相关的函数放到trusted部分,把与OCALL相关的函数放到untrusted部分。因为不需要Edger8rSyntax和TrustedLibrary,所以我们把与这两部分相关的函数删除,也就是把以下的代码删除。

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 *;

由于这里Ecall_main()与Ocall_print()函数并未涉及返回值,所以定义为void类型,而与ECALL相关的函数需要定义为public类型,放入trusted部分;Ocall_print()函数放入untrusted部分。如果涉及到返回值,需要指明方向属性,,并通过指针传递返回值,可以参考https://download.01.org/intel-sgx/linux-2.3/docs/Intel_SGX_Developer_Reference_Linux_2.3_Open_Source.pdf里关于direction attribute的介绍。修改完的代码如下图。
在这里插入图片描述
2. Enclave.cpp。只需要在该文件里实现Ecall_main()函数即可,由于不需要void printf(const char *fmt, ...)函数,故删除Enclave.cpp文件中的void printf(const char *fmt, ...)函数,并将以下代码添加到Enclave.cpp文件中。

void Ecall_main(){}

修改完的代码如下图所示。
在这里插入图片描述
4. Enclave.h。这里只需要声明Ecall_main()函数即可。删除Enclave.h文件中的void printf(const char *fmt, ...);函数声明,并将以下代码添加到Enclave.h文件中。

void Ecall_main();

修改后的代码如下图。
在这里插入图片描述
5. App.cpp。我们在该文件中自定义OcallPrint()函数打印”Hello World!“。由于我们不需要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();

由于我们不需要void ocall_print_string(const char *str)函数,所以我们把该函数删除,也就是把下面部分删除。

/* OCall functions */
void ocall_print_string(const char *str)
{
    /* Proxy/Bridge will check the length and null-terminate 
     * the input string to prevent buffer overflow. 
     */
    printf("%s", str);
}

需要实现我们的不可信函数Ocall_print(),该函数的功能是打印”Hello World!“,将以下代码放到main函数之前。

void Ocall_print (){
        printf("Hello World\n");
    }

我们在main函数中的Enclave创建和销毁之间通过代码Ecall_main(global_eid);调用进入enclave,也就是把这句代码放到sgx_destroy_enclave(global_eid);之前。global_eid参数是是enclave的id。在main函数里,先初始化enclave,然后调用Ecall_main();进入创建的enclave里,最后销毁该enclave。
修改完的main函数如下图所示。
在这里插入图片描述
5. App.h。在该头文件里声明Ocall_print()函数,删除其中的与Edger8rSyntax和TrustedLibrary相关的函数声明,也就是将以下语句删除。

void edger8r_array_attributes(void);
void edger8r_type_attributes(void);
void edger8r_pointer_attributes(void);
void edger8r_function_attributes(void);
void ecall_libc_functions(void);
void ecall_libcxx_functions(void);
void ecall_thread_functions(void);

添加以下代码。

void Ocall_print();

修改完的app.h如下图所示。
在这里插入图片描述

2.3 代码测试

以2.3中的EmptyEcallHelloWorld工程为例,运行代码。
首先cd EmptyEcallHelloWorld,进入到项目路径下。
ls列出当前文件夹下的文件。
在这里插入图片描述
make clean清除上次make生成的文件。
make对当前工程编译(如果是在模拟模式下编译,则make SGX_MODE=SIM)
命令行会打印出编译的信息,包括项目所占内存。
在这里插入图片描述
在最后会生成一个enclave.signed.so文件,并提示The project has been built in debug hardware mode,即工程是在硬件模式下编译的。
在这里插入图片描述
ls可以看到项目下生成了一个app可执行文件。

./app

命令行打印出“Hello World!”程序运行成功!

发布了10 篇原创文章 · 获赞 6 · 访问量 4万+

猜你喜欢

转载自blog.csdn.net/qq_35565669/article/details/99759565