Windows10下使用Intel SGX功能(二):helloworld流程分析

参考文献

helloworld 代码解读

代码运行环境: windows 10

代码目录结构

调用流程

1. 首先定义 host 和 enclave 之间相互调用的函数

定义见 helloworld.edl

enclave {
    from "openenclave/edl/syscall.edl" import *;
    from "platform.edl" import *;

    trusted {
        public void enclave_helloworld();
    };

    untrusted {
        void host_helloworld();
    };
};

其中 public void enclave_helloworld() 是 enclave 向 host 暴露的接口。 void host_helloworld(); 是 enclave 向 host 返回数据的回调函数。

2. 实现 enclave 端的 enclave_helloworld() 逻辑

helloworld/enclave/enc.c 中代码。

// 导入 enclave 库, 完整的 enclave 库见【API reference and supported libraries】
// 此示例中包含 stdio.h 头文件,因为我们调用 CRT 函数 fprintf  在屏幕上打印消息。
// 但是,此函数依赖于内核在屏幕上打印消息,因此此代码无法在安全区本身内执行。
// 相反,这个函数将调用转交给主机,以代表enclave执行调用。
#include <stdio.h>

// 导入在构建过程中生成的受信任的helloworld头文件。
// 该文件是通过针对 helloworld.edl 文件调用sdk工具oeedger8r生成的。
#include "helloworld_t.h"

// 这是主机调用的函数。它在 enclave 中打印一条信息,然后再回调到主机中打印一条信息。
void enclave_helloworld()
{
    // 打印一个来自 enclave 的信息。
    // 注意,不是直接调用fprintf,而是将调用转交到host,并从那里调用fprintf。
    // 这是因为fprintf函数不是enclave的一部分,因为它需要内核的支持。
    fprintf(stdout, "Hello world from the enclave\n");

    // Call back into the host
    oe_result_t result = host_helloworld();
    if (result != OE_OK) // 验证是否成功
    {
        fprintf(stderr, "Call to host_helloworld failed: result=%u (%s)\n", result,
         oe_result_str(result));
    }
}

3. 实现 host 端的 host_helloworld() 逻辑

代码见 helloworld/host/host.c

// 包括本文件中使用的Open Enclave函数的头,(例如oe_terminate_enclave)。
#include <openenclave/host.h>

// 包括标准的CRT库。与 enclave 实现不同的是,enclave 实现包括一个特殊的 enclave 版本的 stdio 库,该库将API传递给主机。
// 而 host 不受保护,所以使用所有正常的C库和函数。
#include <stdio.h>

// Include the untrusted helloworld header that is generated
// during the build. This file is generated by calling the
// sdk tool oeedger8r against the helloworld.edl file.
#include "helloworld_u.h"

// 这是 enclave 调用的实际的 host 函数。该函数在 helloworld.edl 文件中定义,并在这里实现。
void host_helloworld()
{
    fprintf(stdout, "Enclave called into host to print: Hello World!\n");
}

// host 是创建和调用enclave的应用程序,所以host是一个正常的C可执行文件,有一个标准的主函数。
int main(int argc, const char* argv[])
{
    oe_result_t result;
    int ret = 1;
    oe_enclave_t* enclave = NULL;

    if (argc != 2)
    {
        fprintf(stderr, "Usage: %s enclave_image_path\n", argv[0]);
        goto exit;
    }

    // 1. 通过调用oe_create_helloworld_enclave来创建一个enclave,该enclave的签名库文件的路径刚好作为第一个参数传递给启动应用程序。
    // oe_create_helloworld_enclave函数是由oeedger8r生成的,默认名字方式是oe_create_[xxxx]_enclave,其中xxx是 EDL文件的名称。该函数创建了一个enclave 镜像供主机进程使用。
    // 该函数功能包括:
    //   (1). 分配 enclave 地址空间。
    //   (2). 从enclave 库文件中加载 enclave 代码和数据到它的地址空间。
    //   (3). 设置enclave环境,包括enclave堆和每个enclave线程的数据结构。
    //   (4). 测量产生的enclave身份,并确保它与enclave签名相匹配。
    //   (5). 初始化 "enclave",使其准备好从主机上调用。
    // 2. OE_ENCLAVE_FLAG_DEBUG标志允许在没有签署enclave二进制的情况下创建enclave。
    // 它还允许开发人员调试进程并获得对enclave内存的访问。这意味着**不要发送带有OE_ENCLAVE_FLAG_DEBUG的代码**,因为它是不安全的。它所提供的是更容易开发你的enclave的能力。
    // 在你运送代码之前,你需要为enclave可执行文件准备一个适当的代码签名故事。
    // 一些较新的英特尔SGX平台允许使用自签名的证书,但一些较老的英特尔SGX平台要求英特尔对你的enclave可执行文件进行签名。
    // 3. 在成功创建后,该函数会返回一个不透明的enclave句柄,用于今后对enclae的任何操作。
    result = oe_create_helloworld_enclave(
        argv[1], OE_ENCLAVE_TYPE_AUTO, OE_ENCLAVE_FLAG_DEBUG, NULL, 0, &enclave);
    if (result != OE_OK)
    {
        fprintf(stderr, "oe_create_helloworld_enclave(): result=%u (%s)\n", result, oe_result_str(result));
        goto exit;
    }

    // 调用了从helloworld.edl文件中生成的主机marshaling函数。它处理任何参数的代码,并在enclave本身中调用该函数。
    // 在这个例子中,没有任何实际的函数参数。
    // 同时,尽管函数enclave_helloworld()是一个无效的返回类型,但marshaling代码本身可能会失败,所以需要验证与之相关的返回代码。
    // 如果enclave_helloworld()要返回一个值,这将作为一个out参数传回来。
    // Open Enclave处理host模式和enclave模式之间的所有上下文切换。
    result = enclave_helloworld(enclave);
    if (result != OE_OK)
    {
        fprintf(stderr, "calling into enclave_helloworld failed: result=%u (%s)\n", result, oe_result_str(result));
        goto exit;
    }

    ret = 0;

exit:
    // 终止enclave并释放与之相关的所有资源。
    if (enclave)
        oe_terminate_enclave(enclave);

    return ret;
}

4. 编译 enclave 应用程序

4.1 使用 oeedger8r 编译 helloworld.edl 的 untrusted 部分,生成 host 端的相关文件

oeedger8r --search-path c:\openenclave\include --search-path c:\openenclave\include\openenclave\edl\sgx ..\helloworld.edl --untrusted
file description
host/helloworld_args.h 所有函数的参数定义
host/helloworld_u.c 包含 "enclave_helloworld() "函数,该函数具有调用 enclave 版本的 "enclave_helloworld() "函数的 marshaling 代码。
host/helloworld_u.h `enclave_helloworld()'函数的函数原型

Open Enclave edger8r 的说明文档在这儿

4.2 编译

与英特尔SGX SDK一样,Open Enclave SDK目前只支持构建单一的二进制 enclave 。与英特尔SGX SDK一样,这些二进制文件也必须用一组特定的包含物和构建标志来构建。
为了简化指定正确构建参数的过程,Open Enclave SDK提供了一套pkg-config设置:

  • Building enclave or host binary
  • Using GCC or Clang build tools
  • Compiling C or C++ code

具体的语法是:

oe<enclave|host>-<gcc|g++|clang|clang++>

helloworld 编译方法如下:

$(CC) -g -c $(CFLAGS) -DOE_API_VERSION=2 enc.c -o enc.o
$(CC) -g -c $(CFLAGS) -DOE_API_VERSION=2 helloworld_t.c -o helloworld_t.o
$(CC) -o helloworldenc helloworld_t.o enc.o $(LDFLAGS) $(CRYPTO_LDFLAGS)

4.3 签名 enclave 程序

在运行SGX enclave 之前,需要为 enclave 指定定义 enclave 应如何加载的属性。这些属性与签名密钥一起,定义了用于验证和密封操作的 enclave 身份。

使用 essign 的 SDK 工具对 encalve 库进行签名。

oesign sign -e helloworldenc -c helloworld.conf -k private.pem

enclave 的签名证书必须是 3072-bit RSA keys with exponent 3
密钥生成方法如下:

openssl genrsa -out myprivate.pem -3 3072

签名的配置文件在 helloworld.conf。该文件定义了enclave的配置。

必选项

  • Debug: enclave 是否可以以 debug 模式加载
  • NumTCS:要在 enclave 中分配的线程控制结构(TCS)的数量。这决定了在 enclave 中可以执行的最大并发线程数。
  • NumStackPages:为 enclave 中每个线程分配的堆栈页数。
  • NumHeapPages:为 enclave 分配的作为堆内存的页数。

上述4个属性也将反映在产生的 enclave 的UniqueID(MRENCLAVE)中。此外,以下两个属性是由开发者定义的,并直接映射到以下SGX身份属性。

  • ProductID:产品标识(ISVPRODID),供开发者区分以相同MRSIGNER值签署的不同 enclave。
  • SecurityVersion:enclave 的安全版本号(ISVSVN),它可以用来防止对密封密钥的回滚攻击。每当对 enclave 代码进行安全修复时,这个值都应该被递增。

可选项

  • Linux, PSW >= 2.14.1

    • CreateZeroBaseEnclave:是否应该用基础地址0x0创建 enclave ?默认为0。

    • StartAddress:当 enclave 基址为0x0(CreateZeroBaseEnclave=1)时, enclave 镜像将在此地址创建。该值需要与OE_PAGE_SIZE(0x1000)对齐,并大于mmap最小地址(/proc/sys/vm/mmap_min_addr)。

      基于0的 enclave 在访问0页时保证了NullPointerException行为。依赖于这种行为的应用程序现在可以在 enclave 内运行(例如,.NET运行时)。

  • 指定额外的密钥共享和分离(KSS)身份属性(for SGX enclave only)

    • FamilyID:开发者可以指定产品系列标识(SGX的ISVFAMILYID),将不同的 enclave 组合在一个共同的标识下,如包括几个 enclave 应用程序的应用套件的标识。
    • ExtendedProductID:扩展的产品标识(SGX的ISVEXTPRODID)值,当16位的产品标识被证明过于局限时,开发者可以将其作为enclave的128位全球唯一标识。更多细节,请参见《英特尔软件》的表37-19。
      比如:
      FamilyID=47183823-2574-4bfd-b411-99ed177d3e43
      ExtendedProductID=2768c720-1e28-11eb-adc1-0242ac120002
      
  • SGX2 机器

    • CapturePFGPExceptions:是否应该启用(1)或不启用(0) enclave 内异常处理程序来捕获#PF和#GP异常(SGX2特性,默认值:0)。

上述所有设置可以通过宏定义(OE_SET_ENCLAVE_SGX2 OE_SET_ENCLAVE_SGX)来进行设置。
OE_SET_ENCLAVE_SGX2 支持 KSS 设置。

OE_SET_ENCLAVE_SGX2(
    1,     /* ProductID */
    1,     /* SecurityVersion */
    ({0x47, 0x18, 0x38, 0x23, 0x25, 0x74, 0x4b, 0xfd, 0xb4, 0x11, 0x99, 0xed, 0x17, 0x7d, 0x3e, 0x43}),   /* ExtendedProductID */
    ({0x27, 0x68, 0xc7, 0x20, 0x1e, 0x28, 0x11, 0xeb, 0xad, 0xc1, 0x02, 0x42, 0xac, 0x12, 0x00, 0x02}),   /* FamilyID */
    true,  /* Debug */
    true,  /* CapturePFGPExceptions */
    true   /* RequireKSS */
    false, /* CreateZeroBaseEnclave */
    0,     /* StartAddress */
    1024,  /* NumHeapPages */
    1024,  /* NumStackPages */
    1);    /* NumTCS */

OE_SET_ENCLAVE_SGX设置下,允许在调试模式下运行enclave,而不用先签署它。在这种情况下,enclave 被视为具有标准签名者ID(MRSIGNER)的值。

OE_SET_ENCLAVE_SGX(
    1,    /* ProductID */
    1,    /* SecurityVersion */
    1,    /* Debug */
    1024, /* NumHeapPages: heap size in units of 4KB pages */
    1024, /* NumStackPages: stack size, in units of 4KB pages */
    1);   /* NumTCS */

如果是 OP-TEE (ARM TrustZone),则使用sign.py对其进行签名。具体用法参考Signing an OP-TEE Enclave

总结

更多关于Intel TEE API的操作参考 tee-internal-core-api-specification

5. 编译 host 端应用程序

5.1 使用 oeedger8r 编译 helloworld.edl 的 trusted 部分,生成 enclave 端的相关文件

oeedger8r --search-path c:\openenclave\include --search-path c:\openenclave\include\openenclave\edl\sgx ..\helloworld.edl --trusted
file description
enclave/helloworld_args.h 所有函数的参数定义
enclave/helloworld_t.c 包含 "host_helloworld() "函数,其中包含调用 host 的 "host_helloworld() "函数的 marshaling 代码。
enclave/helloworld_t.h host_helloworld()函数的函数原型

6. helloworld 调用流程

ninja 编译过程

ninja run 过程

posted @ 2023-03-02 11:24  水中墨色  阅读(343)  评论(0编辑  收藏  举报