在这之前,一直想学习关于SGX实操性的、应用相关的内容,我浏览了相关资料,总觉得跟不上那些作者的脚步,学到的都是些零零碎碎的知识,收获都不是很大,一边阅读还在一边质疑资料的可读性,所以我决定返朴归真,静下心来好好看一看官网的手册,并作一些笔记方便自己的日后学习。
手册下载地址: Intel® Software Guard Extensions (Intel® SGX) SDK for Linux* OS
Arrays
Enclave定义语言(EDL)支持多维的、固定大小的数组,用于数据结构定义和参数声明。
数组还应该显式地使用[in], [out]或[user_ check]属性装饰,这与指针类似。
NOTE
数组使用有以下一些限制:
- size/count不能用于数组类型
- const不能用于数组类型
- 零长度的数组和灵活数组不被EDL语法支持
- 指针数组不被EDL语法支持
Example
enclave {
include "user_types.h" //for uArray - typedef int uArray[10];
trusted {
public void test_array([in] int arr[4]);
public void test_array_multi([in] int arr[4][4]);
};
};
Unsupported Syntax:
enclave {
include "user_types.h" //for uArray - typedef int uArray[10];
trusted {
// Flexible array is not supported灵活数组不被支持
public void test_flexible(int arr[][4]);
// Zero-length array is not supported.零长度的数组不被支持
public void test_zero(int arr[0]);
};
};
特殊属性 isary 用于表示用户自定义类型数组的函数参数。
用户定义的数据类型
Enclave定义语言(EDL)支持用户定义的数据类型,但应该在头文件中定义。任何被类型定义为另一种类型的基本数据类型都成为用户定义的数据类型。
一些用户数据类型需要用特殊的EDL属性进行注释,比如isptr、isary和readonly。如果用户定义的类型参数缺少所要求的这些属性之一,那么编译器将在sgx_edger8r生成的代码中发出编译错误。
- 当指针有用户定义的数据类型时,使用isptr表示用户定义的参数是指针。
- 当数组有用户定义的数据类型时,使用isary表示用户定义的参数是一个数组
- 当ECALL或OCALL形参是指针的用户定义类型时(指向const数据类型的指针),形参应带有readonly属性的注释。Readonly属性只能与isptr属性一起使用。
Example
enclave {
include "user_types.h" // for typedef void * pBuf;
//and typedef void const * pBuf2; and typedef int uArray[10];
trusted {
public void test_isptr([in, isptr, size=len] pBuf pBufptr,size_t len);
public void test_isptr_readonly([in, isptr, readonly, size=len] pBuf2 pBuf2ptr,size_t len);
public void test_isary([in, isary] uArray arr);
};
};
Unsupported Syntax
不明白 pBuf pBufptr 与 pBuf* pBufptr 的区别 ?
enclave {
include "user_types.h" //for typedef void const * pBuf2;
//and typedef int uArray[10];
trusted {
// [out] 与[readonly] 属性不能同时使用
void test_isptr_readonly_cant([in, out, isptr, readonly, size=len] pBuf2 pBuf2ptr,size_t len);
// isptr cannot be used for pointers/arrays
public void test_isptr_cant1([in, isptr, size=len] pBuf* pBufptr,
size_t len);
public void test_isptr_cant2([in, isptr, size=len] void* pBufptr,
size_t len);
// User-defined array types need "isary"
public void test_miss_isary([in] uArray arr);
// size/count attributes cannot be used for user-defined array types
public void test_isary_cant_size([in, size=len] uArray arr,size_t len);,
// isary cannot be used for pointers/arrays
public void test_isary_cant([in, isary] uArray arr[4]);
};
};
引入EDL库
你可以在外部可信库中实现导入和导出函数,类似于不可信域中的静态库。要将这些函数添加到enclave中,请使用enclave定义语言(EDL)库导入机制。
使用EDL关键字from和import将 库EDL文件 添加到一个enclave EDL文件中。
from关键字 指定库EDL文件的位置。可以接受相对路径和完整路径。相对路径是相对于EDL文件的位置。建议使用不同的名称区分库EDL文件和enclave EDL文件。import关键字 指定要导入的函数。星号(*)可用于从库中导入所有函数。通过编写一个以逗号分隔的函数列表,可以导入多个函数。
Syntax
from "lib_filename.edl" import func_name,func2_name;
from "lib_filename.edl" import *;
Example
enclave {
from "secure_comms.edl" import send_email, send_sms;
from "../../sys/other_secure_comms.edl" import *;
};
一个库的EDL文件可以导入另一个EDL文件,这个EDL文件又可以导入另一个EDL文件,创建如下所示的层次结构:
// enclave.edl
enclave {
from "other/file_L1.edl" import *; // Import all functions
};
// Trusted library file_L1.edl
enclave {
from "file_L2.edl" import *;
trusted {
public void test_int(int val);
};
};
// Trusted library file_L2.edl
enclave {
from "file_L3.edl" import *;
trusted {
public void test_ptr(int* ptr);
};
};
// Trusted library file_L3.edl
enclave {
trusted {
public void test_float(float flt);
};
};
授予对ECALLs的访问权限
默认下,任何不受信任的函数都不能调用ECALL函数。
为了使应用程序代码能够直接调用一个ECALL作为root ECALL, ECALL应该显式地用public关键字装饰为一个公共的ECALL。如果没有这个关键字,ECALLs将被视为私有ECALLs,不能作为root ECALLs直接调用。
Syntax
trusted {
public <function prototype>;
};
enclave EDL必须有一个或多个公共ECALLs,否则根本无法调用enclave函数,在这种情况下,sgx_edger8r将报告一个错误。
若要授予一个OCALL函数对一个ECALL函数的访问权限,请使用allow关键字指定此访问权限。公共和私有的ECALLs都可以放到allow列表中。
Syntax
untrusted {
<function prototype> allow (func_name, func2_name, …);
};
Example
//在该示例中,不受信任的代码被授予对ECALLs的不同访问权限。
enclave {
trusted {
public void clear_secret();
public void get_secret([out] secret_t* secret);
void set_secret([in] secret_t* secret);
};
untrusted {
void replace_secret([in] secret_t* new_secret,[out] secret_t* old_secret) allow (set_secret, clear_secret);
};
};
ECALL | 被root ECALL调用 | 被replace_secret调用 |
---|---|---|
clear_secret | Yes | Yes |
get_secret | Yes | No |
set_secret | No | Yes |
使用Switchless调用
ECALLs和OCALLs可以使用transition_using_threads属性作为EDL文件中函数声明的后缀。当您使用这个属性时,sgx_edger8r会产生不同的边例程。
带有 transition_using_threads属性的ECALLs和OCALLs使用Switchless操作模式去服务呼叫。
Example
enclave{
trusted{
public void ecall_empty(void);
public void ecall_empty_switchless(void) transition_using_threads;
};
untrusted{
void ocall_empty(void);
void ocall_empty_switchless(void) transition_using_threads;
};
};
Switchless Calls的使用详见文档-72-
Enclave Configuration File(Enclave配置文件)
enclave配置文件是一个XML文件,其中包括用户定义的enclave参数。这个XML文件时enclave项目的一部分。名为sgx_sign的工具使用这个文件作为输入,为enclave创建签名和元数据。下面是一个配置文件的例子:
<EnclaveConfiguration>
<ProdID>100</ProdID>
<ISVSVN>1</ISVSVN>
<StackMaxSize>0x50000</StackMaxSize>
<StackMinSize>0x2000</StackMinSize>
<HeapMaxSize>0x100000</HeapMaxSize>
<HeapMinSize>0x40000</HeapMinSize>
<HeapInitSize>0x80000</HeapInitSize>
<TCSNum>3</TCSNum>
<TCSMaxNum>4</TCSMaxNum>
<TCSMinPool>2</TCSMinPool>
<TCSPolicy>1</TCSPolicy>
<DisableDebug>0</DisableDebug>
<MiscSelect>0</MiscSelect>
<MiscMask>0xFFFFFFFF</MiscMask>
<EnableKSS>1</EnableKSS>
<ISVEXTPRODID_H>1</ISVEXTPRODID_H>
<ISVEXTPRODID_L>2</ISVEXTPRODID_L>
<ISVFAMILYID_H>3</ISVFAMILYID_H>
<ISVFAMILYID_L>4</ISVFAMILYID_L>
</EnclaveConfiguration>
下表列出了配置文件中定义的元素。它们都是可选的。如果没有配置文件,或者配置文件中没有元素,则使用默认值。Enclave配置默认值详见文档-66-。
Enclave Project Configurations(enclave项目配置)
根据你所处的开发阶段,选择以下项目配置之一来构建飞地:
- Simulation 模拟模式
- Debug 调试模式
- Prerelease 预发布模式
- Release 发布模式
-----------------------------具体内容,详见手册-70-