Enclave.edl文件中主要包括trusted和untrusted两部分。
一、trusted文件内容
(一)trusted中出现的不带返回值的函数如:
public void process_log([in,string] char *str);
它必须得是public类型的,否则在enclave外无法进行ecall的调用。[in]表示数据流将从不可信部分拷贝进入enclave内部。[out]则方向相反。如果是untrusted中的函数[in]和[out]的数据流则刚好跟trusted中的相反。关于[in,string]等的详细讲解在开发指南Page52中。
凡是出现在trusted部分的函数都必须在Enclave.cpp或者Enclave文件夹中的.cpp文件实现。同时上面函数在经过sgx_edger8r工具处理后会在Enclave_u.h中进行声明,成为下面函数:
sgx_status_t process_log(sgx_enclave_id_t eid, char* str);
并在Enclave_u.c文件中实现,如下:
sgx_status_t process_log(sgx_enclave_id_t eid, char* str)
{
sgx_status_t status;
ms_process_log_t ms;
ms.ms_str = str;
ms.ms_str_len = str ? strlen(str) + 1 : 0;
status = sgx_ecall(eid, 0, &ocall_table_Enclave, &ms);
return status;
}
其中ms_process_log_t也是在enclave_u.c中,其定义为:
typedef struct ms_process_log_t {
char* ms_str;
size_t ms_str_len;
} ms_process_log_t;
因为process_log函数中是有参数的,参数为char *str,所以ms_process_log_t结构中第一个是char* ms_str,第二个是ms_str_len。
思考一下,因为process_log([in,string] char *str)是出现在trusted部分的,那么它应该是要被不可信代码ecall从而在enclave内部执行,所以需要出现在Enclave_u部分里面,从而才能允许不可信代码的调用。它在Enclave_u部分的返回值为sgx_status_t,如果成功则为SGX_SUCCESS(0),否则为别的值。而在Enclave_u部分的代码是通过上面黄色部分sgx_ecall代码与Enclave_c部分进行关联的。因为process_log函数在Enclave.edl文件中的trusted部分是第一个函数,所以它的索引是0。在Enclave_t.h中其声明为void process_log(char* str);,但其实现是在Enclave.cpp中,在Enclave_t.c中有一个与其相关的函数为:
static sgx_status_t SGX_CDECL sgx_process_log(void* pms){……}
综上:Enclave.edl中trusted部分中的函数会在:
1Enclave.cpp中定义其实现;2经过sgx_edger8r处理会在Enclave_u.h中声明;3并在Enclave_u.c中实现.h中的声明;4同时还会在Enclave_t.h中进行声明;5并在Enclave_t.c中有一个sgx开头的相关函数。
(二)trusted中出现的带返回值的函数如:
public uint32_t verify_block_messages();
注意该函数不需要参数,同时返回值类型为uint32_t。
1则同样会在Enclave.cpp中定义其实现;
2经过sgx_edger8r处理会在Enclave_u.h中声明,其声明为:
sgx_status_t verify_block_messages(sgx_enclave_id_t eid, uint32_t* retval);
注意其返回值同样为sgx_status_t,成功则为SGX_SUCCESS,否则为别的值。该函数的参数中eid为Enclave的id号,uint32_t* retval用于保存返回的值。
3在Enclave_u.c中实现.h中的声明:
sgx_status_t verify_block_messages(sgx_enclave_id_t eid, uint32_t* retval)
{
sgx_status_t status;
ms_verify_block_messages_t ms;
status = sgx_ecall(eid, 1, &ocall_table_Enclave, &ms);
if (status == SGX_SUCCESS && retval) *retval = ms.ms_retval;
return status;
}
该函数在Enclave.edl中的trusted部分第2个出现,所以索引为1.
typedef struct ms_verify_block_messages_t {
uint32_t ms_retval;
} ms_verify_block_messages_t;
因为verify_block_messages()的原始定义中是没有参数的,所以ms_verify_block_messages_t结构中只有一个是uint32_t ms_retval用来做返回值。
4同时还会在Enclave_t.h中进行声明:
uint32_t verify_block_messages(void);
5在Enclave_t.c中有一个sgx开头的相关函数:
static sgx_status_t SGX_CDECL sgx_verify_block_messages(void* pms)
二、untrusted文件内容
(一)untrusted中出现不带返回值的函数如:
void ocall_print_string([in, string] const char *str);
注意:该函数无返回值,且参数为const char *str类型。
1凡是出现在untrusted部分的函数都必须在不可信代码如App.cpp中实现;
2经过sgx_edger8r工具的处理会在Enclave_t.h中声明为:
sgx_status_t SGX_CDECL ocall_print_string(const char* str);
注意:它的返回值为sgx_status_t,成功则为SGX_SUCCESS,否则为别的值。
3在Enclave_t.c中的实现为:
sgx_status_t SGX_CDECL ocall_print_string(const char* str)
{
sgx_status_t status = SGX_SUCCESS;
size_t _len_str = str ? strlen(str) + 1 : 0;
ms_ocall_print_string_t* ms = NULL;
size_t ocalloc_size = sizeof(ms_ocall_print_string_t);
void *__tmp = NULL;
CHECK_ENCLAVE_POINTER(str, _len_str);
if (ADD_ASSIGN_OVERFLOW(ocalloc_size, (str != NULL) ? _len_str : 0))
return SGX_ERROR_INVALID_PARAMETER;
__tmp = sgx_ocalloc(ocalloc_size);
if (__tmp == NULL) {
sgx_ocfree();
return SGX_ERROR_UNEXPECTED;
}
ms = (ms_ocall_print_string_t*)__tmp;
__tmp = (void *)((size_t)__tmp + sizeof(ms_ocall_print_string_t));
ocalloc_size -= sizeof(ms_ocall_print_string_t);
if (str != NULL) {
ms->ms_str = (const char*)__tmp;
if (_len_str % sizeof(*str) != 0) {
sgx_ocfree();
return SGX_ERROR_INVALID_PARAMETER;
}
if (memcpy_s(__tmp, ocalloc_size, str, _len_str)) {
sgx_ocfree();
return SGX_ERROR_UNEXPECTED;
}
__tmp = (void *)((size_t)__tmp + _len_str);
ocalloc_size -= _len_str;
} else {
ms->ms_str = NULL;
}
status = sgx_ocall(0, ms);
if (status == SGX_SUCCESS) {
}
sgx_ocfree();
return status;
}
思考一下:untrusted部分的代码是为了enclave向外部进行ocall的函数,所以它应该是在Enclave内部执行,因此它应该出现在Enclave_t部分,从而才能允许Enclave进行调用。它与不可信部分进行关联是通过sgx_ocall(0,ms)实现的,因为它是untrusted部分的第一个函数,所以它的索引为0。ms的定义为ms_ocall_print_string_t* ms,其中ms...结构的定义为:
typedef struct ms_ocall_print_string_t {
const char* ms_str;
} ms_ocall_print_string_t;
结构中只有一个const char* ms_str,不再像trusted部分的函数参数生成的结构中还会有个长度。
4同时还会在Enclave_u.h中进行声明:
void SGX_UBRIDGE(SGX_NOCONVENTION, ocall_print_string, (const char* str));
5在Enclave_u.c中的实现为:
static sgx_status_t SGX_CDECL Enclave_ocall_print_string(void* pms)
{
ms_ocall_print_string_t* ms = SGX_CAST(ms_ocall_print_string_t*, pms);
ocall_print_string(ms->ms_str);
return SGX_SUCCESS;
}
(二)untrusted中出现带返回值的函数如:
uint32_t ocall_write_region_data([in, size=bloblen] uint8_t *blob, uint32_t bloblen);
该函数返回值为uint32_t,也需要有参数。
1在App.cpp中实现;
2经过sgx_edger8r工具的处理会在Enclave_t.h中声明为:
sgx_status_t SGX_CDECL ocall_write_region_data(uint32_t* retval, uint8_t* blob, uint32_t bloblen);
注意:其返回值为sgx_status_t,成功则为SGX_SUCCESS,否则为其它值。参数变为
uint32_t* retval, uint8_t* blob, uint32_t bloblen
比原始函数的参数多了个uint32_t* retval,这个参数用作返回值。
3在Enclave_t.c中的实现为:
sgx_status_t SGX_CDECL ocall_write_region_data(uint32_t* retval, uint8_t* blob, uint32_t bloblen)
{
sgx_status_t status = SGX_SUCCESS;
size_t _len_blob = bloblen;
ms_ocall_write_region_data_t* ms = NULL;
size_t ocalloc_size = sizeof(ms_ocall_write_region_data_t);
void *__tmp = NULL;
CHECK_ENCLAVE_POINTER(blob, _len_blob);
if (ADD_ASSIGN_OVERFLOW(ocalloc_size, (blob != NULL) ? _len_blob : 0))
return SGX_ERROR_INVALID_PARAMETER;
__tmp = sgx_ocalloc(ocalloc_size);
if (__tmp == NULL) {
sgx_ocfree();
return SGX_ERROR_UNEXPECTED;
}
ms = (ms_ocall_write_region_data_t*)__tmp;
__tmp = (void *)((size_t)__tmp + sizeof(ms_ocall_write_region_data_t));
ocalloc_size -= sizeof(ms_ocall_write_region_data_t);
if (blob != NULL) {
ms->ms_blob = (uint8_t*)__tmp;
if (_len_blob % sizeof(*blob) != 0) {
sgx_ocfree();
return SGX_ERROR_INVALID_PARAMETER;
}
if (memcpy_s(__tmp, ocalloc_size, blob, _len_blob)) {
sgx_ocfree();
return SGX_ERROR_UNEXPECTED;
}
__tmp = (void *)((size_t)__tmp + _len_blob);
ocalloc_size -= _len_blob;
} else {
ms->ms_blob = NULL;
}
ms->ms_bloblen = bloblen;
status = sgx_ocall(4, ms);
if (status == SGX_SUCCESS) {
if (retval) *retval = ms->ms_retval;
}
sgx_ocfree();
return status;
}
它与不可信部分的关联是通过sgx_ocall(4,ms)实现的,4是这个函数的索引,它在untrusted中是第5个。
ms_ocall_write_region_data_t的定义为:
typedef struct ms_ocall_write_region_data_t {
uint32_t ms_retval;
uint8_t* ms_blob;
uint32_t ms_bloblen;
} ms_ocall_write_region_data_t;
4同时还会在Enclave_u.h中进行声明:
uint32_t SGX_UBRIDGE(SGX_NOCONVENTION, ocall_write_region_data, (uint8_t* blob, uint32_t bloblen));
5在Enclave_u.c中的实现为:
static sgx_status_t SGX_CDECL Enclave_ocall_write_region_data(void* pms)
{
ms_ocall_write_region_data_t* ms = SGX_CAST(ms_ocall_write_region_data_t*, pms);
ms->ms_retval = ocall_write_region_data(ms->ms_blob, ms->ms_bloblen);
return SGX_SUCCESS;
}
(三)untrusted中出现带allow的函数如:
// This OCALL can make an ECALL to function “verify_block_messages”.
uint32_t ocall_read_sealed_data( [in, string] char * str) allow(verify_block_messages);