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