百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 热门文章 > 正文

使用SGX开发一个简单的Hello World程序

bigegpt 2024-08-10 12:05 6 浏览

不管是学习哪种编程语言,入门都喜欢写一个Hello World程序。这里就看看怎么写一个SGX的Hello World。对于SGX来说,首先程序在逻辑上要分为安全区和非安全区。

在这个示例程序中,安全区生成一段Hello World字符串,非安全区通过ECALL调用非安全区代码,获得字符串然后打印到界面上。

文件组织如下

├── App
│   ├── App.cpp # 创建
│   ├── App.h # 创建
│   ├── App.o
│   ├── Enclave_u.c # 胶合代码
│   ├── Enclave_u.h # 胶合代码
│   └── Enclave_u.o
├── Enclave
│   ├── Enclave.config.xml # 创建
│   ├── Enclave.cpp # 创建
│   ├── Enclave.edl # 创建
│   ├── Enclave.h # 创建
│   ├── Enclave.lds # 创建
│   ├── Enclave.o
│   ├── Enclave_private.pem # 创建
│   ├── Enclave_t.c  # 胶合代码
│   ├── Enclave_t.h  # 胶合代码
│   └── Enclave_t.o
├── enclave.signed.so
├── enclave.so
├── hello_world
└── Makefile # 创建
```

标注创建的文件是需要自己创建的,标注胶合代码是通过sgx_edger8r工具生成的接口代码。App下包含的是untrusted的代码,Enclave下包含的是trusted的代码。

Makefile从其它示例代码中拷贝过来即可用(结构、文件名不变即可)

EDL

首先要创建一个edl文件(Enclave Description Language),它用于描述trusted和untrusted的接口。这个Hello world只定义了一个ECALL:

enclave {
    trusted {
        public void ecall_hello_from_enclave([out, size=len] char* buf, size_t len);
    };
};

edl有自己的描述格式。

有了edl以后,就可以通过sgx_edger8r工具生成接口描述文件,比较像Wrapper代码这种东西。

$ sgx_edger8r --trusted --trusted-dir Enclave/ Enclave/Enclave.edl
$ sgx_edger8r --untrusted --untrusted-dir App/ Enclave/Enclave.edl

Enclave_t.h

就是一个函数声明。

#ifndef ENCLAVE_T_H__
#define ENCLAVE_T_H__
 
#include <stdint.h>
#include <wchar.h>
#include <stddef.h>
#include "sgx_edger8r.h" /* for sgx_ocall etc. */

#include <stdlib.h> /* for size_t */

#define SGX_CAST(type, item) ((type)(item))

#ifdef __cplusplus
extern "C" {
#endif

void ecall_hello_from_enclave(char* buf, size_t len);

#ifdef __cplusplus
}
#endif /* __cplusplus */

#endif

Enclave_t.c

生成了一些框架代码和宏,最终调用的是Enclave.cpp里的我们需要实现的函数。

#include "Enclave_t.h"

#include "sgx_trts.h" /* for sgx_ocalloc, sgx_is_outside_enclave */
#include "sgx_lfence.h" /* for sgx_lfence */

#include <errno.h>
#include <mbusafecrt.h> /* for memcpy_s etc */
#include <stdlib.h> /* for malloc/free etc */

#define CHECK_REF_POINTER(ptr, siz) do {        \
        if (!(ptr) || ! sgx_is_outside_enclave((ptr), (siz)))   \
                return SGX_ERROR_INVALID_PARAMETER;\
} while (0)

#define CHECK_UNIQUE_POINTER(ptr, siz) do {     \
        if ((ptr) && ! sgx_is_outside_enclave((ptr), (siz)))    \
                return SGX_ERROR_INVALID_PARAMETER;\
} while (0)

#define CHECK_ENCLAVE_POINTER(ptr, siz) do {    \
        if ((ptr) && ! sgx_is_within_enclave((ptr), (siz)))     \
                return SGX_ERROR_INVALID_PARAMETER;\
} while (0)

#define ADD_ASSIGN_OVERFLOW(a, b) (     \
        ((a) += (b)) < (b)      \
)
  

typedef struct ms_ecall_hello_from_enclave_t {
        char* ms_buf;
        size_t ms_len;
} ms_ecall_hello_from_enclave_t;

static sgx_status_t SGX_CDECL sgx_ecall_hello_from_enclave(void* pms)
{
        CHECK_REF_POINTER(pms, sizeof(ms_ecall_hello_from_enclave_t));
        //
        // fence after pointer checks
        //
        sgx_lfence();
        ms_ecall_hello_from_enclave_t* ms = SGX_CAST(ms_ecall_hello_from_enclave_t*, pms);
        sgx_status_t status = SGX_SUCCESS;
        char* _tmp_buf = ms->ms_buf;
        size_t _tmp_len = ms->ms_len;
        size_t _len_buf = _tmp_len;
        char* _in_buf = NULL;

        CHECK_UNIQUE_POINTER(_tmp_buf, _len_buf);

        //
        // fence after pointer checks
        //
        sgx_lfence();

        if (_tmp_buf != NULL && _len_buf != 0) {
                if ( _len_buf % sizeof(*_tmp_buf) != 0)
                {
                        status = SGX_ERROR_INVALID_PARAMETER;
                        goto err;
                }
                if ((_in_buf = (char*)malloc(_len_buf)) == NULL) {
                        status = SGX_ERROR_OUT_OF_MEMORY;
                        goto err;
                }

                memset((void*)_in_buf, 0, _len_buf);
        }

        ecall_hello_from_enclave(_in_buf, _tmp_len);  // 重要
        if (_in_buf) {
                if (memcpy_s(_tmp_buf, _len_buf, _in_buf, _len_buf)) {
                        status = SGX_ERROR_UNEXPECTED;
                        goto err;
                }
        }

err:
        if (_in_buf) free(_in_buf);
        return status;
}

SGX_EXTERNC const struct {
        size_t nr_ecall;
        struct {void* ecall_addr; uint8_t is_priv; uint8_t is_switchless;} ecall_table[1];
} g_ecall_table = {
        1,
        {
                {(void*)(uintptr_t)sgx_ecall_hello_from_enclave, 0, 0},
        }
}; 

SGX_EXTERNC const struct {
        size_t nr_ocall;
} g_dyn_entry_table = {
        0,
};

Enclave.cpp

安全区的实现,把Hello World字符串拷到缓冲区里。

#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';
}

Enclave.h是空,就不贴了

Enclave_u.cpp

Enclave_u.cpp封装了安全区的接口调用。

#include "Enclave_u.h"
#include <errno.h>

typedef struct ms_ecall_hello_from_enclave_t {
        char* ms_buf;
        size_t ms_len;
} ms_ecall_hello_from_enclave_t;

static const struct {
        size_t nr_ocall;
        void * table[1];
} ocall_table_Enclave = {
        0,
        { NULL },
};

sgx_status_t ecall_hello_from_enclave(sgx_enclave_id_t eid, char* buf, size_t len)
{
        sgx_status_t status;
        ms_ecall_hello_from_enclave_t ms;
        ms.ms_buf = buf;
        ms.ms_len = len;
        status = sgx_ecall(eid, 0, &ocall_table_Enclave, &ms);
        return status;
}

App.cpp

App.cpp是非安全区的业务代码,从代码来看,先要创建一个enclave,加载的是一个签名过的动态库。


#include <stdio.h>
#include <string.h>
#include <assert.h>

#include <time.h>
#include <ctime>

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

    char enclavefile[256];
    getcwd(enclavefile, sizeof(enclavefile));
    strcat(enclavefile, "/enclave.signed.so");  // so文件名

    /* Call sgx_create_enclave to initialize an enclave instance */
    /* Debug Support: set 2nd parameter to 1 */
    ret = sgx_create_enclave(enclavefile, SGX_DEBUG_FLAG, NULL, NULL, &global_eid, NULL);  // 重要
    if (ret != SGX_SUCCESS) {
        printf("Failed to create enclave, ret code: %d, enclave file: %s\n", ret, enclavefile);
        return -1;
    }

    return 0;
}

tm* get_time() {
    time_t rawtime;
    struct tm * timeinfo;
    time ( &rawtime );
    timeinfo = localtime ( &rawtime );
    return timeinfo;
}

/* Application entry */
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};

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

    /* Enclave calls */
    while(1) {
        ecall_hello_from_enclave(global_eid, buffer, max_buf_len);  // 重要

        printf("%s%s\n", asctime(get_time()), buffer);
        fflush(stdout);

        sleep(1);
    }

    /* Destroy the enclave */
    sgx_destroy_enclave(global_eid);

    printf("Info: SampleEnclave successfully returned.\n");

    printf("Enter a character before exit ...\n");
    getchar();
    return 0;
}

Enclave.config.xml/Enclave.lds/Enclave_private.pem

要让程序可编译,还需要生成几个文件,这个XML是enclave的配置文件,包含堆大小,栈大小,是否开启调试等。

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

Enclave.lds是链接脚本。

enclave.so
{
    global:
        g_global_data_sim;
        g_global_data;
        enclave_entry;
        g_peak_heap_used;
    local:
        *;
};

Enclave_private.pem就是一个普通的RSA密钥,示例是用 openssl genrsa -out Enclave_private.pem -3 3072 生成,Enclave.signed.so就是用该密钥签名的。

编译和运行

make执行编译,sdk支持模拟方式。

make SGX_MODE=SIM 可以在不支持SGX的系统上编译并运行SGX程序。

SDK示范代码的README中有几种编译模式的详细描述。

3. 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

enclave的库是静态的

$ ldd enclave.signed.so
        statically linked

app侧会有一些sgx的依赖

$ ldd hello_world
        linux-vdso.so.1 (0x00007ffd7effe000)
        libsgx_urts_sim.so => /home/kaifeng/intel/sgxsdk/sdk_libs/libsgx_urts_sim.so (0x00007f6461149000)
        libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f6460965000)
        libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007f6460761000)
        libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007f6460542000)
        libcrypto.so.1.1 => /usr/lib/x86_64-linux-gnu/libcrypto.so.1.1 (0x00007f6460077000)
        libsgx_uae_service_sim.so => /home/kaifeng/intel/sgxsdk/sdk_libs/libsgx_uae_service_sim.so (0x00007f6461121000)
        libstdc++.so.6 => /usr/lib/x86_64-linux-gnu/libstdc++.so.6 (0x00007f645fcee000)
        libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007f645fad6000)
        /lib64/ld-linux-x86-64.so.2 (0x00007f6460f59000)
        libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007f645f738000)

运行可以得到预期行为

$ ./hello_world
Tue Dec 28 16:26:59 2021
Hello world
Tue Dec 28 16:27:00 2021
Hello world
Tue Dec 28 16:27:01 2021
Hello world
Tue Dec 28 16:27:02 2021
Hello world
Tue Dec 28 16:27:03 2021
Hello world
^C

以Hardware方式编译的程序,在开启SGX的系统上能够运行。

目标系统也需要安装sdk的库,这些需要集成到标准路径下。

相关推荐

10w qps缓存数据库——Redis(redis缓存调优)

一、Redis数据库介绍:Redis:非关系型缓存数据库nosql:非关系型数据库没有表,没有表与表之间的关系,更不存在外键存储数据的形式为key:values的形式c语言写的服务(监听端口),用来存...

Redis系列专题4--Redis配置参数详解

本文基于windowsX64,3.2.100版本讲解,不同版本默认配置参数不同在Redis中,Redis的根目录中有一个配置文件(redis.conf,windows下为redis.windows....

开源一夏 | 23 张图,4500 字从入门到精通解释 Redis

redis是目前出场率最高的NoSQL数据库,同时也是一个开源的数据结构存储系统,在缓存、数据库、消息处理等场景使用的非常多,本文瑞哥就带着大家用一篇文章入门这个强大的开源数据库——Redis。...

redis的简单与集群搭建(redis建立集群)

Redis是什么?是开源免费用c语言编写的单线程高性能的(key-value形式)内存数据库,基于内存运行并支持持久化的nosql数据库作用主要用来做缓存,单不仅仅是做缓存,比如:redis的计数器生...

推荐几个好用Redis图形化客户端工具

RedisPlushttps://gitee.com/MaxBill/RedisPlusRedisPlus是为Redis可视化管理开发的一款开源免费的桌面客户端软件,支持Windows、Linux...

关于Redis在windows上运行及fork函数问题

Redis在将数据库进行持久化操作时,需要fork一个进程,但是windows并不支持fork,导致在持久化操作期间,Redis必须阻塞所有的客户端直至持久化操作完成。微软的一些工程师花费时间在解决在...

你必须懂的Redis十大应用场景(redis常见应用场景)

Redis作为一款高性能的键值存储数据库,在互联网业务中有着广泛的应用。今天,我们就来详细盘点一下Redis的十大常用业务场景,并附上Golang的示例代码和简图,帮助大家更好地理解和应用Redis。...

极简Redis配置(redis的配置)

一、概述Redis的配置文件位于Redis安装目录下,文件名为redis.conf(Windows名为redis.windows.conf,linux下的是redis.conf)你可以通过C...

什么是redis,怎么启动及如何压测

从今天起咱们一起来学习一下关于“redis监控与调优”的内容。一、Redis介绍Redis是一种高级key-value数据库。它跟memcached类似,不过数据可以持久化,而且支持的数据类型很丰富。...

一款全新Redis UI可视化管理工具,支持WebUI和桌面——P3X Redis UI

介绍P3XRedisUI这是一个非常实用的RedisGUI,提供响应式WebUI访问或作为桌面应用程序使用,桌面端是跨平台的,而且完美支持中文界面。Githubhttps://github....

windows系统的服务器快速部署java项目环境地址

1、mysql:https://dev.mysql.com/downloads/mysql/(msi安装包)2、redis:https://github.com/tporadowski/redis/r...

window11 下 redis 下载与安装(windows安装redis客户端)

#热爱编程是一种怎样的体验#window11下redis下载与安装1)各个版本redis下载(windows)https://github.com/MicrosoftArchive/r...

一款轻量级的Redis客户端工具,贼好用!

使用命令行来操作Redis是一件非常麻烦的事情,我们一般会选用客户端工具来操作Redis。今天给大家分享一款好用的Redis客户端工具TinyRDM,它的界面清新又优雅,希望对大家有所帮助!简介Ti...

一个.NET开发且功能强大的Windows远程控制系统

我们致力于探索、分享和推荐最新的实用技术栈、开源项目、框架和实用工具。每天都有新鲜的开源资讯等待你的发现!项目介绍SiMayRemoteMonitorOS是一个基于Windows的远程控制系统,完...

Redis客户端工具详解(4款主流工具)

大家好,我是mikechen。Redis是大型架构的基石,也是大厂最爱考察内容,今天就给大家重点详解4款Redis工具@mikechen本篇已收于mikechen原创超30万字《阿里架构师进阶专题合集...