apache / incubator-teaclave-sgx-sdk

Apache Teaclave (incubating) SGX SDK helps developers to write Intel SGX applications in the Rust programming language, and also known as Rust SGX SDK.
https://teaclave.apache.org
Apache License 2.0
1.17k stars 263 forks source link

Question on developing a "hybrid" library using Rust SGX #317

Closed victorzz0 closed 3 years ago

victorzz0 commented 3 years ago

Hi everyone,

Context

I'm trying to port Timely Dataflow to work in SGX using this SDK as part of my dissertation. An experimental (and perhaps stupid) idea I have is too run some of the dataflow operations and store their associated data in untrusted environment. Therefore, I am trying to develop a library where the majority runs in SGX with sgx_tstd as std but will use some ocalls to do some things outside SGX (Eg. using remote attestation to set up an encrypted communication channel with a remote worker process), where these ocalls potentially use the untrusted std and perhaps things that ultimately depend on untrusted std.

Question

I have tried out the examples in this repo, looked at their make files, but I'm still not sure what's the best way to structure a reusable Rust library that contains ocalls and want to use both std and sgx_tstd. In addition, I'm also confused to how the compilation should go for such a library and someone who uses such a library. Tho I'm quite sure that I can reused the structure (and the make files) in the examples if I were to simply make an application.

Example

A more concrete example would be as the following:

I have a crate called timely with the following structure:

lib.rs

pub mod enclave_timely;

enclave_timely.rs

#![no_std]
extern crate sgx_tstd as sgx_std;
extern crate sgx_types;

use sgx_std::vec::Vec;

use sgx_types::sgx_status_t;

extern "C" {
    pub fn test_ocall(retval: *mut sgx_status_t, some_value: usize);
}

pub fn test_lib_call() {
    let v = Vec::new();
    let mut ret = sgx_status_t::SGX_SUCCESS;
    unsafe {test_ocall(&mut ret, 8);}
}

untrusted_timely.rs

extern crate sgx_types;

use sgx_types::sgx_status_t;

pub extern "C" fn test_ocall (some_val: usize) {
    let v = Vec::new();
    v.push(some_val);
    println!("hello from enclave: {}", some_val);
}

timely.edl

enclave {

    untrusted {
        sgx_status_t test_ocall(uint32_t some_val);
    };
};

Then I would have someone who uses this like

extern crate timely;
use timely::enclave_timely::test_lib_call;

pub fn test() {
    test_lib_call();
}
enclave {

   from "timely.edl" import *;
};

This example obviously does not work because enclave_timely wants [no_std] but untrusted_timely wants a std environment and the compiler complains about duplicated definitions. From my understanding, to make this work, I have to put the trusted part in one crate, and to put the untrusted part in another crate. Then the untrusted crate will be compiled as a library and linked with the enclave_u files generated by edger8r and the trusted crate will be compiled as a library and linked with the enclave_t files. I'm not sure if this is correct and if this is the best way to do this.

I'm new to SGX and Rust (and a compiler noob) so I might be missing some obvious things here. I would also really appreciate if someone could point me to additional resources for self-help.

volcano0dr commented 3 years ago

Your understanding is correct. You must put the trusted part and the untrusted part in two different crates. xxx_t.c and xxx_u.c generated by edger8r are linked with trusted and untrusted library.

victorzz0 commented 3 years ago

Thanks! I tried this out the other day by compiling the untrusted crate as a static lib and compiling the trusted crate as a dynamic lib. It worked but I forgot to resolve this issue...