Intel Software Guard Extensions (SGX) is a set of processor extensions for building a protected execution environment that is referred to as an enclave. This topic describes how to use the Intel SGX SDK to develop and build an application named hello_world. This application generates messages in an enclave on a regular basis, sends messages to an untrusted buffer, and then sends the messages to terminals.

Prerequisites

How SGX works

Figure 1. How SGX works
The basic concepts related to an SGX application are described as follows:
  • An SGX application consists of two parts:
    • Untrusted part

      The untrusted part is an unencrypted part of the memory. If you store the code and data of an application in this part, the main() function of the application must also be placed in the untrusted part. In the preceding figure, functions main() and bar() are placed in the untrusted part.

    • Trusted part or enclave

      The trusted part or enclave is an encrypted part of the memory. This part is created by the CPU and cannot be accessed by the rest of the system. In the preceding figure, functions helloworld() and foo() are placed in the enclave.

  • To invoke a function in the enclave from the untrusted part, the application must perform an Enclave Call (ECALL).
  • To invoke a function in the untrusted part from the enclave, the application must perform an Outside Call (OCALL).
  • ECALLs and OCALLs are declared in the Enclave Definition Language (EDL) file.

Examples

The following example describes how to develop, build, and deploy an SGX application named hello_world by using the Intel SGX SDK. For more information about the source code, see GitHub. The source code includes the code for application compilation, image building, and application deployment.

Directory structure of the sample code
sgx-device-plugin/samples/hello_world/
                                    ├── Dockerfile
                                    ├── Makefile
                                    ├── README.md
                                    └── src
                                         ├── App
                                         │   ├── App.cpp
                                         │   └── App.h
                                         ├── Enclave
                                         │   ├── Enclave.config.xml
                                         │   ├── Enclave.cpp
                                         │   ├── Enclave.edl
                                         │   ├── Enclave.h
                                         │   ├── Enclave.lds
                                         │   └── Enclave_private.pem
                                         ├── Include
                                         └── Makefile
Note The following table describes the directory of src and related files.
  • The App directory contains untrusted code, such as the main() entry and code of the OCALL function.
  • The Enclave directory contains trusted code, such as the code of the ECALL function.
    File Description
    Enclave.edl The EDL file.
    Enclave.lds The enclave linker script.
    Enclave_private.pem The private key used to sign the enclave.so file.
    Enclave.config.xml The enclave configuration file that specifies parameters such as the stack size and whether to enable debugging.
    Enclave.h and Enclave.cpp The code that implements the untrusted part.
  • The Include directory contains a header file shared by untrusted code and trusted code.
  1. In the command-line interface (CLI), connect to the managed confidential computing cluster that you create and compile hello_world.
    In the project directory, run the make command. A binary file named hello_world is generated in the root directory. Run the ./hello_world command to run the hello_world application.
    make
    GEN  =>  App/Enclave_u.h
    CC   <=  App/Enclave_u.c
    CXX  <=  App/App.cpp
    LINK =>  app
    GEN  =>  Enclave/Enclave_t.h
    CC   <=  Enclave/Enclave_t.c
    CXX  <=  Enclave/Enclave.cpp
    LINK =>  enclave.so
    <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>
    tcs_num 10, tcs_max_num 10, tcs_min_pool 1
    The required memory is 3960832B.
    The required memory is 0x3c7000, 3868 KB.
    Succeed.
    SIGN =>  enclave.signed.so
    The project has been built in debug hardware mode.
    ./hello_world
    Wed May  6 06:53:33 2020
    Hello world From SGX Enclave!
    Wed May  6 06:53:34 2020
    Hello world From SGX Enclave!
    ...
    The compilation process and code directory structure after compilation are described as follows:
    • Use a makefile to compile the application.
      1. Use the sgx_edger8r tool and the sgx_ecall function to generate untrusted code Enclave_u.c and Enclave_u.h in the App directory.
      2. Compile untrusted binary files in the App directory.
      3. Use the sgx_edger8r tool to generate trusted code Enclave_t.c and Enclave_t.h in the Enclave directory.
      4. Compile the enclave.so file. It is a trusted dynamic-link library.
      5. Use the sgx_sign tool to sign enclave.signed.so. It is a trusted dynamic-link library.
      6. The application is compiled.
    • The following is the code directory structure after compilation.
      sgx-device-plugin/samples/hello_world/src/
                                               ├── hello_world      #[generated]
                                               ├── App
                                               │   ├── App.cpp
                                               │   ├── App.h
                                               │   ├── App.o        #[generated]
                                               │   ├── Enclave_u.c  #[generated] 
                                               │   ├── Enclave_u.h  #[generated] 
                                               │   └── Enclave_u.o  #[generated]
                                               ├── Enclave
                                               │   ├── Enclave.config.xml
                                               │   ├── Enclave.cpp
                                               │   ├── Enclave.edl
                                               │   ├── Enclave.h
                                               │   ├── Enclave.lds
                                               │   ├── Enclave.o     #[generated]
                                               │   ├── Enclave_private.pem
                                               │   ├── Enclave_t.c   #[generated]
                                               │   ├── Enclave_t.h   #[generated]
                                               │   └── Enclave_t.o   #[generated]
                                               ├── enclave.signed.so #[generated]
                                               ├── enclave.so        #[generated]
                                               ├── Include
                                               └── Makefile
  2. Build an image and deploy an application named hello_world.
    Build an image for the compiled application based on alibabatee/centos_sgx:7. The image must contain the dynamic-link library required by the compiled application.
    The Dockerfile contains the following information:
    FROM alibabatee/centos_sgx:7
    
    COPY src/hello_world src/enclave.signed.so /app/
    
    WORKDIR /app
    
    ENTRYPOINT ["/app/hello_world"]
    1. Run the following commands to compile the application and build an image.
      cd sgx-device-plugin/samples/hello_world
      TARGET_IMAGE=registry-vpc.cn-shanghai.aliyuncs.com/${namespace}/${image_name}:${image_tag} make image
      docker push registry-vpc.cn-shanghai.aliyuncs.com/${namespace}/${image_name}:${image_tag}
    2. Run the following commands to deploy the hello_world application.
      cat <<EOF | kubectl create -f -
      apiVersion: apps/v1
      kind: Deployment
      metadata:
        name: helloworld
        namespace: default
      spec:
        replicas: 2
        selector:
          matchLabels:
            app: helloworld
        template:
          metadata:
            labels:
              app: helloworld
          spec:
            containers:
            - command:
              - /app/hello_world
              image: {{TARGET_IMAGE}}
              imagePullPolicy: Always
              name: helloworld
              resources:
                limits:
                  cpu: 250m
                  memory: 512Mi
                  alibabacloud.com/sgx_epc_MiB: 2
              volumeMounts:
              - mountPath: /var/run/aesmd/aesm.socket
                name: aesmsocket
            volumes:
            - hostPath:
                path: /var/run/aesmd/aesm.socket
                type: Socket
              name: aesmsocket
      EOF
File path Description Example
Encalve/Enclave.edl The EDL file that declares a public ECALL function. A minimum of one public ECALL function must be declared in the EDL file of an SGX application. trusted {...} declares the ECALL function. untrusted {...} declares the OCALL function. In this example, the application does not have to invoke an OCALL. Therefore, only one ECALL (ecall_hello_from_enclave) is declared. This ECALL is used to create a buffer in the enclave and deploy the hello_world application. Then, the information in the buffer is copied to the untrusted part. The application invokes printf in the untrusted part to print the information.
enclave {
    trusted {
        public void ecall_hello_from_enclave([out, size=len] char* buf, size_t len);
    };
};
Enclave/Enclave.lds -
enclave.so
{
    global:
        g_global_data_sim;
        g_global_data;
        enclave_entry;
        g_peak_heap_used;
    local:
        *;
};
Enclave/Enclave.config.xml -
<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/Enclave.h -
#ifndef _ENCLAVE_H_
#define _ENCLAVE_H_
#endif
Enclave/Enclave.cpp -
#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/Enclave_private.pem The private key that is used to sign the enclave.so file.
$ openssl genrsa -out Enclave/Enclave_private.pem -3 3072
App/App.h -
#ifndef _APP_H_
#define _APP_H_

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

#include "sgx_error.h"       /* sgx_status_t */
#include "sgx_eid.h"     /* sgx_enclave_id_t */

#ifndef TRUE
# define TRUE 1
#endif

#ifndef FALSE
# define FALSE 0
#endif

# define TOKEN_FILENAME   "enclave.token"
# define ENCLAVE_FILENAME "enclave.signed.so"

extern sgx_enclave_id_t global_eid;    /* global enclave id */

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


#if defined(__cplusplus)
}
#endif

#endif /* ! _APP_H_ */
App/App.cpp -
#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");

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

References