diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..8be367b --- /dev/null +++ b/.gitmodules @@ -0,0 +1,6 @@ +[submodule "sdk/example_containers/sgx-task/deps/llhttp"] + path = sdk/example_containers/sgx-task/deps/llhttp + url = https://github.com/nodejs/llhttp.git +[submodule "sdk/example_containers/sgx-task/deps/libuv"] + path = sdk/example_containers/sgx-task/deps/libuv + url = https://github.com/libuv/libuv.git diff --git a/sdk/example_containers/sgx-task/Dockerfile b/sdk/example_containers/sgx-task/Dockerfile new file mode 100644 index 0000000..a4f0ad6 --- /dev/null +++ b/sdk/example_containers/sgx-task/Dockerfile @@ -0,0 +1,155 @@ +FROM ubuntu:20.04 as sgx_dcap_2.14_1.11 + +USER root + +# avoid tzdata interactive config during apt install. +ENV TZ=Asia/Singapore +RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone + +RUN apt-get update && apt-get install -y \ + build-essential \ + git \ + wget + +# install sdk +RUN wget https://download.01.org/intel-sgx/sgx-dcap/1.11/linux/distro/ubuntu20.04-server/sgx_linux_x64_sdk_2.14.100.2.bin \ + && chmod 755 sgx_linux_x64_sdk_2.14.100.2.bin \ + && ./sgx_linux_x64_sdk_2.14.100.2.bin --prefix=/opt/intel \ + source /opt/intel/sgxsdk/environment + +# install dcap libs +RUN echo 'deb [arch=amd64] https://download.01.org/intel-sgx/sgx_repo/ubuntu focal main' | tee /etc/apt/sources.list.d/intel-sgx.list > /dev/null \ + && wget -O - https://download.01.org/intel-sgx/sgx_repo/ubuntu/intel-sgx-deb.key | apt-key add - \ + && apt update + +RUN apt install -y \ + libsgx-epid=2.14.100.2-focal1 \ + libsgx-headers=2.14.100.2-focal1 \ + libsgx-urts=2.14.100.2-focal1 \ + libsgx-launch=2.14.100.2-focal1 \ + libsgx-ae-le=2.14.100.2-focal1 \ + libsgx-ae-pce=2.14.100.2-focal1 \ + libsgx-ae-qe3=1.11.100.2-focal1 \ + libsgx-ae-qve=1.11.100.2-focal1 \ + libsgx-ae-epid=2.14.100.2-focal1 \ + libsgx-qe3-logic=1.11.100.2-focal1 \ + libsgx-pce-logic=1.11.100.2-focal1 \ + libsgx-enclave-common=2.14.100.2-focal1 \ + sgx-aesm-service=2.14.100.2-focal1 \ + libsgx-quote-ex=2.14.100.2-focal1 \ + libsgx-dcap-ql=1.11.100.2-focal1 \ + libsgx-dcap-quote-verify=1.11.100.2-focal1 \ + # workload system additional packages + libsgx-quote-ex-dev=2.14.100.2-focal1 \ + libsgx-dcap-ql-dev=1.11.100.2-focal1 \ + libsgx-dcap-quote-verify-dev=1.11.100.2-focal1 \ + # qpl needed for verification + libsgx-dcap-default-qpl=1.11.100.2-focal1 \ + # runtime + libsgx-uae-service=2.14.100.2-focal1 + +# install sgx-sdk mitigation tools (necessary to build sgxssl) +RUN apt-get install -y \ + build-essential \ + ocaml \ + ocamlbuild \ + automake \ + autoconf \ + libtool \ + wget \ + python-is-python3 \ + libssl-dev \ + git \ + cmake \ + perl \ + libssl-dev \ + libcurl4-openssl-dev \ + protobuf-compiler \ + libprotobuf-dev \ + debhelper \ + reprepro \ + unzip \ + && cd / && git clone https://github.com/intel/linux-sgx.git && cd linux-sgx && git checkout sgx_2.14 && make preparation + +RUN ls /linux-sgx/external/toolset/ubuntu20.04/ && cp /linux-sgx/external/toolset/ubuntu20.04/* /usr/local/bin && cd / && ls /usr/local/bin + +# install sgxssl +RUN wget https://github.com/intel/intel-sgx-ssl/archive/refs/tags/lin_2.14_1.1.1k.tar.gz \ + && tar -xzvf lin_2.14_1.1.1k.tar.gz \ + && cd intel-sgx-ssl-lin_2.14_1.1.1k/openssl_source \ + && wget https://www.openssl.org/source/openssl-1.1.1w.tar.gz \ + && cd ../Linux && make all && make install && cd ../ + +# should remove the sources +RUN rm -rf /linux-sgx && rm -rf /lin_2.14_1.1.1k.tar.gz /intel-sgx-ssl-lin_2.14_1.1.1k + +# image size: 1.94GB + +FROM sgx_dcap_2.14_1.11 as server_builder + +USER root + +ENV SGX_SDK=/opt/intel/sgxsdk + +# avoid tzdata interactive config during apt install. +ENV TZ=Asia/Singapore +RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone + +RUN apt-get update && apt-get install -y \ + # install deps (libuv) + cmake \ + clang + +# # without -i the bash will not load .bashrc. With -i docker complains but we can ignore it. +# SHELL ["/bin/bash", "--login", "-i", "-c"] +# ENV NODE_VERSION=16.15.0 +# ENV NVM_DIR /tmp/nvm +# WORKDIR $NVM_DIR +# RUN wget -qO- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.5/install.sh | bash \ +# && . $NVM_DIR/nvm.sh \ +# && nvm install $NODE_VERSION \ +# && nvm alias default $NODE_VERSION \ +# && nvm use default +# ENV NODE_PATH $NVM_DIR/v$NODE_VERSION/lib/node_modules +# ENV PATH $NVM_DIR/v$NODE_VERSION/bin:$PATH + +# a cleaner version of the above +ENV NVM_DIR /usr/local/nvm +ENV NODE_VERSION v16.15.0 +RUN mkdir -p /usr/local/nvm +RUN wget -qO- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.1/install.sh | bash +RUN /bin/bash -c "source $NVM_DIR/nvm.sh && nvm install $NODE_VERSION && nvm use --delete-prefix $NODE_VERSION" +ENV NODE_PATH $NVM_DIR/versions/node/$NODE_VERSION/bin +ENV PATH $NODE_PATH:$PATH + +RUN node --version && npm --version + +COPY ./deps /sgx-task/deps +COPY ./Makefile /sgx-task/ +RUN cd /sgx-task/ && make mrproper && make deps + +COPY ./common/ /sgx-task/common +COPY ./server/ /sgx-task/server +COPY ./worker/ /sgx-task/worker + +RUN cd /sgx-task/ \ + && cd worker && make mrproper && make all && make install && cd ..\ + && cd server && make mrproper && make all && make install && cd .. + +# image +FROM sgx_dcap_2.14_1.11 + +USER root + +# # default libuv thread pool size to 8. +# ARG UV_THREADPOOL_SIZE=8 +# RUN echo UV_THREADPOOL_SIZE=${UV_THREADPOOL_SIZE} +# ENV UV_THREADPOOL_SIZE ${UV_THREADPOOL_SIZE} +# sequential request execution +ENV UV_THREADPOOL_SIZE 1 + +COPY --from=server_builder /sgx-task/server/install /install + +EXPOSE 8080 + +CMD SGX_AESM_ADDR=1 /install/bin/server /install/lib/Worker_Enclave.signed.so diff --git a/sdk/example_containers/sgx-task/Makefile b/sdk/example_containers/sgx-task/Makefile new file mode 100644 index 0000000..b737cba --- /dev/null +++ b/sdk/example_containers/sgx-task/Makefile @@ -0,0 +1,34 @@ +# used to mainly build dependencies +PROJECT_ROOT_DIR := $(shell readlink -f .) + +DEPS_INSTALL_DIR = $(PROJECT_ROOT_DIR)/deps/install + +LIBUV_DIR = $(PROJECT_ROOT_DIR)/deps/libuv +LLHTTP_DIR = $(PROJECT_ROOT_DIR)/deps/llhttp + +.PHONY: all concurrent_runtime_deps deps mrproper preparation + +preparation: + git submodule update --init --recursive + +server_deps: + mkdir -p $(DEPS_INSTALL_DIR) + cd $(LIBUV_DIR) && mkdir build && cd build && cmake .. -DCMAKE_INSTALL_PREFIX=$(DEPS_INSTALL_DIR)/libuv && make && make install && cd $(PROJECT_ROOT_DIR) + mkdir -p $(DEPS_INSTALL_DIR)/llhttp/include && mkdir $(DEPS_INSTALL_DIR)/llhttp/lib + npm --version + cd $(LLHTTP_DIR) && npm install && make && PREFIX=$(DEPS_INSTALL_DIR)/llhttp make install && cd $(PROJECT_ROOT_DIR) + +server_deps_clean: + make -C $(LLHTTP_DIR) clean + rm -rf $(LLHTTP_DIR)/node_modules + rm -rf $(LIBUV_DIR)/build + +deps: server_deps + +all: preparation deps + +clean: + @echo "nothing to clean" + +mrproper: clean server_deps_clean + rm -rf deps/install diff --git a/sdk/example_containers/sgx-task/README.md b/sdk/example_containers/sgx-task/README.md new file mode 100644 index 0000000..3c54633 --- /dev/null +++ b/sdk/example_containers/sgx-task/README.md @@ -0,0 +1,41 @@ +# sgx-task-worker + +SGX task worker sample: It creates a server that conforms to the meca requirement and respond to users input `name` with `hello name`. + +## setup + +Environment setup + +Follow the DCAP setup guides to register the worker machine to Intel Platform Certification Service and cache its attestation collateral at the meca designated Platform Certification Caching Service server. + +The sgx-task server shall be able to prepare an attestation report during RA. + +## build + +To build the sample app, under `sgx_sample/` run + +```sh +docker build -t sgx-task . +``` + +To test the server + +```sh +docker run -it --net=meca --ip=172.18.0.255 --device /dev/sgx_enclave:/dev/sgx/enclave -v /var/run/aesmd:/var/run/aesmd sgx-task:latest +``` + +issue a request (now requires the client to perform encryption decryption) + + + +```sh +cd client/cpp +make clean && make all +./client_test +``` + +## Mock server + +Without SGX support, one can test the workflow with the mock server provided under [`client/python/mock_server`](./client/python/mock_server/). diff --git a/sdk/example_containers/sgx-task/common/fileutil.cc b/sdk/example_containers/sgx-task/common/fileutil.cc new file mode 100644 index 0000000..b6138ab --- /dev/null +++ b/sdk/example_containers/sgx-task/common/fileutil.cc @@ -0,0 +1,106 @@ +#include "fileutil.h" + +#include +#include + +int ReadFileToString(const std::string& file_path, + std::string* content) { + assert(content); + std::ifstream infile(file_path); + if (!infile.good()) { + printf("file failed to load: %s\n", file_path.c_str()); + return 0; // cannot differentiate file not exist or error in openning + } + std::stringstream ss; + ss << infile.rdbuf(); + *content = ss.str(); + return 0; +} + +/** + * @brief read the content of a file into a char array. + * + * @param file_path + * @param output_len output_len will be updated with the len of read content, + * if succeeded + * @return char* : read content (caller owns the data and shall free it). + */ +char* ReadFileToCharArray(const char* file_path, + size_t* output_len) { + assert(file_path); + assert(output_len); + FILE* fp = std::fopen(file_path, "rb"); + if (fp == nullptr) { + printf("failed to open file: %s\n", file_path); + return nullptr; + } + if (std::fseek(fp, 0, SEEK_END) == -1) { + printf("fseek error\n"); + return nullptr; + } + auto file_len = std::ftell(fp); + if (file_len == -1) { + printf("ftell error\n"); + return nullptr; + } + if (file_len == 0) { + *output_len = 0; + return nullptr; + } + auto ret = malloc(file_len+1); + if (ret == nullptr) { + printf("malloc failed\n"); + return nullptr; + } + std::rewind(fp); + if(std::fread(ret, 1, file_len, fp) != static_cast(file_len)) { + printf("fread error"); + free(ret); + return nullptr; + } + if(std::fclose(fp) == -1) { + printf("close error"); + free(ret); + return nullptr; + } + *output_len = file_len; + ((char*) ret)[file_len] = '\0'; + return (char*) ret; +} + +int WriteStringToFile(const std::string& file_path, + const std::string& content) { + std::ofstream outfile(file_path, std::ios::out); + outfile << content; + outfile.flush(); + outfile.close(); + return 0; +} + +/** + * @brief write a char array to a file + * + * @param file_path + * @param src + * @param len : len of the char array + * @return int : 0 for success; -1 for failure + */ +int WriteCharArrayToFile(const char* file_path, + const char* src, size_t len) { + assert(file_path); + assert(src); + FILE* fp = std::fopen(file_path, "wb"); + if (fp == nullptr) { + printf("failed to open file\n"); + return -1; + } + if(std::fwrite(src, 1, len, fp) != len) { + printf("fwrite error"); + return -1; + } + if(std::fclose(fp) == -1) { + printf("close error"); + return -1; + } + return 0; +} diff --git a/sdk/example_containers/sgx-task/common/fileutil.h b/sdk/example_containers/sgx-task/common/fileutil.h new file mode 100644 index 0000000..3ea6168 --- /dev/null +++ b/sdk/example_containers/sgx-task/common/fileutil.h @@ -0,0 +1,39 @@ +#ifndef MECA_SGX_COMMON_U_FILEUTIL_H_ +#define MECA_SGX_COMMON_U_FILEUTIL_H_ + +#include + +inline bool IsFileExist(const std::string& file_path) { + std::ifstream infile(file_path); + return infile.good(); +} + +int ReadFileToString(const std::string& file_path, + std::string* content); + +/** + * @brief read the content of a file into a char array. + * + * @param file_path + * @param output_len output_len will be updated with the len of read content, + * if succeeded + * @return char* : read content (caller owns the data and shall free it). + */ +char* ReadFileToCharArray(const char* file_path, + size_t* output_len); + +int WriteStringToFile(const std::string& file_path, + const std::string& content); + +/** + * @brief write a char array to a file + * + * @param file_path + * @param src + * @param len : len of the char array + * @return int : 0 for success; -1 for failure + */ +int WriteCharArrayToFile(const char* file_path, + const char* src, size_t len); + +#endif // MECA_SGX_COMMON_U_FILEUTIL_H_ diff --git a/sdk/example_containers/sgx-task/common/hexutil.cc b/sdk/example_containers/sgx-task/common/hexutil.cc new file mode 100644 index 0000000..bdb4cf5 --- /dev/null +++ b/sdk/example_containers/sgx-task/common/hexutil.cc @@ -0,0 +1,82 @@ +#include "hexutil.h" +#include +#include +#include + +namespace { +const char _hextable[]= "0123456789abcdef"; + +struct hex_buffer_deleter { + void operator()(void* p) const { free(p); } +}; + + +thread_local std::unique_ptr _hex_buffer {nullptr}; +thread_local size_t _hex_buffer_size= 0; +} // anonymous namespace + +void print_hexstring(FILE *fp, const void *vsrc, size_t len) { + const unsigned char *sp= (const unsigned char *) vsrc; + size_t i; + for(i= 0; i< len; ++i) { + fprintf(fp, "%02x", sp[i]); + } + fprintf(fp, "\n"); +} + +const char *hexstring(const void *vsrc, size_t len) { + size_t i, bsz; + const char *src= (const char *) vsrc; + char *bp; + + bsz= len*2+1; /* Make room for NULL byte */ + if ( bsz >= _hex_buffer_size ) { + /* Allocate in 1K increments. Make room for the NULL byte. */ + size_t newsz= 1024*(bsz/1024) + ((bsz%1024) ? 1024 : 0); + _hex_buffer_size= newsz; + auto _hex_buffer_ptr = (char*) realloc(_hex_buffer.get(), newsz); + if ( _hex_buffer_ptr == NULL ) { + return "(out of memory)"; + } + _hex_buffer.release(); + _hex_buffer.reset(_hex_buffer_ptr); + } + + for(i= 0, bp= _hex_buffer.get(); i< len; ++i) { + *bp= _hextable[(uint8_t)src[i]>>4]; + ++bp; + *bp= _hextable[(uint8_t)src[i]&0xf]; + ++bp; + } + _hex_buffer.get()[len*2]= 0; + + return (const char *) _hex_buffer.get(); +} + +// need testing +std::string hex_encode(const void* vsrc, size_t len) { + const char* src = (const char*) vsrc; + char ret[len * 2 + 1]; + char* bp = ret; + for (size_t i = 0; i < len; ++i) { + *bp = _hextable[(uint8_t) src[i] >> 4]; + ++bp; + *bp = _hextable[(uint8_t) src[i] & 0xf]; + ++bp; + } + ret[len * 2] = 0; + return std::string(ret, len * 2); +} + +// need testing +std::string hex_decode(const char* vsrc, size_t len) { + const char* src = (const char*) vsrc; + char ret[len / 2]; + char* bp = ret; + for (size_t i = 0; i < len; i += 2) { + *bp = (uint8_t) ((src[i] >= 'a' ? src[i] - 'a' + 10 : src[i] - '0') << 4); + *bp |= (uint8_t) (src[i + 1] >= 'a' ? src[i + 1] - 'a' + 10 : src[i + 1] - '0'); + ++bp; + } + return std::string(ret, len / 2); +} \ No newline at end of file diff --git a/sdk/example_containers/sgx-task/common/hexutil.h b/sdk/example_containers/sgx-task/common/hexutil.h new file mode 100644 index 0000000..082cb9f --- /dev/null +++ b/sdk/example_containers/sgx-task/common/hexutil.h @@ -0,0 +1,15 @@ +#ifndef MECA_SGX_COMMON_IOPATCHT_H_ +#define MECA_SGX_COMMON_IOPATCHT_H_ + +#include +#include + +void print_hexstring(FILE *fp, const void *src, size_t len); + +const char *hexstring(const void *src, size_t len); + +std::string hex_encode(const void* vsrc, size_t len); +std::string hex_decode(const char* vsrc, size_t len); + +#endif // MECA_SGX_COMMON_IOPATCHT_H_ + diff --git a/sdk/example_containers/sgx-task/common/json.cc b/sdk/example_containers/sgx-task/common/json.cc new file mode 100644 index 0000000..c3c23e6 --- /dev/null +++ b/sdk/example_containers/sgx-task/common/json.cc @@ -0,0 +1,297 @@ +/********************************************************************************** +* Obtained from https://github.com/nbsdx/SimpleJSON , under the terms of the WTFPL. +***********************************************************************************/ +#include "json.h" + +namespace json{ + +namespace { + string json_escape( const string &str ) { + string output; + for( unsigned i = 0; i < str.length(); ++i ) + switch( str[i] ) { + case '\"': output += "\\\""; break; + case '\\': output += "\\\\"; break; + case '\b': output += "\\b"; break; + case '\f': output += "\\f"; break; + case '\n': output += "\\n"; break; + case '\r': output += "\\r"; break; + case '\t': output += "\\t"; break; + default : output += str[i]; break; + } + return output; + } + + JSON parse_next( const string &, size_t & ); + + void consume_ws( const string &str, size_t &offset ) { + while( isspace( str[offset] ) ) ++offset; + } + + JSON parse_object( const string &str, size_t &offset ) { + JSON Object = JSON::Make( JSON::Class::Object ); + + ++offset; + consume_ws( str, offset ); + if( str[offset] == '}' ) { + ++offset; return Object; + } + + while( true ) { + JSON Key = parse_next( str, offset ); + consume_ws( str, offset ); + if( str[offset] != ':' ) { + // std::cerr << "Error: Object: Expected colon, found '" << str[offset] << "'\n"; + break; + } + consume_ws( str, ++offset ); + JSON Value = parse_next( str, offset ); + Object[Key.ToString()] = Value; + + consume_ws( str, offset ); + if( str[offset] == ',' ) { + ++offset; continue; + } + else if( str[offset] == '}' ) { + ++offset; break; + } + else { + // std::cerr << "ERROR: Object: Expected comma, found '" << str[offset] << "'\n"; + break; + } + } + + return Object; + } + + JSON parse_array( const string &str, size_t &offset ) { + JSON Array = JSON::Make( JSON::Class::Array ); + unsigned index = 0; + + ++offset; + consume_ws( str, offset ); + if( str[offset] == ']' ) { + ++offset; return Array; + } + + while( true ) { + Array[index++] = parse_next( str, offset ); + consume_ws( str, offset ); + + if( str[offset] == ',' ) { + ++offset; continue; + } + else if( str[offset] == ']' ) { + ++offset; break; + } + else { + // std::cerr << "ERROR: Array: Expected ',' or ']', found '" << str[offset] << "'\n"; + return std::move( JSON::Make( JSON::Class::Array ) ); + } + } + + return Array; + } + + JSON parse_string( const string &str, size_t &offset ) { + JSON String; + string val; + for( char c = str[++offset]; c != '\"' ; c = str[++offset] ) { + if( c == '\\' ) { + switch( str[ ++offset ] ) { + case '\"': val += '\"'; break; + case '\\': val += '\\'; break; + // case '/' : val += '/' ; break; + case 'b' : val += '\b'; break; + case 'f' : val += '\f'; break; + case 'n' : val += '\n'; break; + case 'r' : val += '\r'; break; + case 't' : val += '\t'; break; + case 'u' : { + val += "\\u" ; + for( unsigned i = 1; i <= 4; ++i ) { + c = str[offset+i]; + if( (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F') ) + val += c; + else { + // std::cerr << "ERROR: String: Expected hex character in unicode escape, found '" << c << "'\n"; + return std::move( JSON::Make( JSON::Class::String ) ); + } + } + offset += 4; + } break; + default : val += '\\'; break; + } + } + else + val += c; + } + ++offset; + String = val; + return String; + } + + JSON parse_number( const string &str, size_t &offset ) { + JSON Number; + string val, exp_str; + char c; + bool isDouble = false; + long exp = 0; + while( true ) { + c = str[offset++]; + if( (c == '-') || (c >= '0' && c <= '9') ) + val += c; + else if( c == '.' ) { + val += c; + isDouble = true; + } + else + break; + } + if( c == 'E' || c == 'e' ) { + c = str[ offset++ ]; + if( c == '-' ){ ++offset; exp_str += '-';} + while( true ) { + c = str[ offset++ ]; + if( c >= '0' && c <= '9' ) + exp_str += c; + else if( !isspace( c ) && c != ',' && c != ']' && c != '}' ) { + // std::cerr << "ERROR: Number: Expected a number for exponent, found '" << c << "'\n"; + return std::move( JSON::Make( JSON::Class::Null ) ); + } + else + break; + } + exp = std::stol( exp_str ); + } + else if( !isspace( c ) && c != ',' && c != ']' && c != '}' ) { + // std::cerr << "ERROR: Number: unexpected character '" << c << "'\n"; + return std::move( JSON::Make( JSON::Class::Null ) ); + } + --offset; + + if( isDouble ) + Number = std::stod( val ) * std::pow( 10, exp ); + else { + if( !exp_str.empty() ) + Number = (double) std::stol( val ) * std::pow( 10, exp ); + else + Number = std::stol( val ); + } + return Number; + } + + JSON parse_bool( const string &str, size_t &offset ) { + JSON Bool; + if( str.substr( offset, 4 ) == "true" ) + Bool = true; + else if( str.substr( offset, 5 ) == "false" ) + Bool = false; + else { + // std::cerr << "ERROR: Bool: Expected 'true' or 'false', found '" << str.substr( offset, 5 ) << "'\n"; + return std::move( JSON::Make( JSON::Class::Null ) ); + } + offset += (Bool.ToBool() ? 4 : 5); + return Bool; + } + + JSON parse_null( const string &str, size_t &offset ) { + JSON Null; + if( str.substr( offset, 4 ) != "null" ) { + // std::cerr << "ERROR: Null: Expected 'null', found '" << str.substr( offset, 4 ) << "'\n"; + return std::move( JSON::Make( JSON::Class::Null ) ); + } + offset += 4; + return Null; + } + + JSON parse_next( const string &str, size_t &offset ) { + char value; + consume_ws( str, offset ); + value = str[offset]; + switch( value ) { + case '[' : return std::move( parse_array( str, offset ) ); + case '{' : return std::move( parse_object( str, offset ) ); + case '\"': return std::move( parse_string( str, offset ) ); + case 't' : + case 'f' : return std::move( parse_bool( str, offset ) ); + case 'n' : return std::move( parse_null( str, offset ) ); + default : if( ( value <= '9' && value >= '0' ) || value == '-' ) + return std::move( parse_number( str, offset ) ); + } + // std::cerr << "ERROR: Parse: Unknown starting character '" << value << "'\n"; + // printf("ERROR: Parse: Unknown starting character %02hhX\n", value); + return JSON(); + } +} // anonymous namespace + +string JSON::ToString( bool &ok ) const { + ok = (Type == Class::String); + // return ok ? std::move( json_escape( *Internal.String ) ): string(""); + // return ok ? *Internal.String: string(""); + return ok ? *Internal.String: dump(); +} + +string JSON::dump( int depth, string tab) const { + string pad = ""; + for( int i = 0; i < depth; ++i, pad += tab ); + + switch( Type ) { + case Class::Null: + return "null"; + case Class::Object: { + string s = "{\n"; + bool skip = true; + for( auto &p : *Internal.Map ) { + if( !skip ) s += ",\n"; + s += ( pad + "\"" + p.first + "\" : " + p.second.dump( depth + 1, tab ) ); + skip = false; + } + s += ( "\n" + pad.erase( 0, 2 ) + "}" ) ; + return s; + } + case Class::Array: { + string s = "["; + bool skip = true; + for( auto &p : *Internal.List ) { + if( !skip ) s += ", "; + s += p.dump( depth + 1, tab ); + skip = false; + } + s += "]"; + return s; + } + case Class::String: + return "\"" + json_escape( *Internal.String ) + "\""; + case Class::Floating: + return std::to_string( Internal.Float ); + case Class::Integral: + return std::to_string( Internal.Int ); + case Class::Boolean: + return Internal.Bool ? "true" : "false"; + default: + return ""; + } + return ""; +} +JSON JSON::Load( const string &str ) { + size_t offset = 0; + return std::move( parse_next( str, offset ) ); +} + +JSON Array() { + return std::move( JSON::Make( JSON::Class::Array ) ); +} + +template +JSON Array( T... args ) { + JSON arr = JSON::Make( JSON::Class::Array ); + arr.append( args... ); + return std::move( arr ); +} + +JSON Object() { + return std::move( JSON::Make( JSON::Class::Object ) ); +} + +} \ No newline at end of file diff --git a/sdk/example_containers/sgx-task/common/json.h b/sdk/example_containers/sgx-task/common/json.h new file mode 100644 index 0000000..7d6ba06 --- /dev/null +++ b/sdk/example_containers/sgx-task/common/json.h @@ -0,0 +1,372 @@ +/********************************************************************************** +* Obtained from https://github.com/nbsdx/SimpleJSON , under the terms of the WTFPL. +***********************************************************************************/ +#ifndef MECA_SGX_COMMON_JSON_H_ +#define MECA_SGX_COMMON_JSON_H_ + + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace json { + +using std::map; +using std::deque; +using std::string; +using std::enable_if; +using std::initializer_list; +using std::is_same; +using std::is_convertible; +using std::is_integral; +using std::is_floating_point; + +class JSON { + union BackingData { + BackingData( double d ) : Float( d ){} + BackingData( long l ) : Int( l ){} + BackingData( bool b ) : Bool( b ){} + BackingData( string s ) : String( new string( s ) ){} + BackingData() : Int( 0 ){} + + deque *List; + map *Map; + string *String; + double Float; + long Int; + bool Bool; + } Internal; + + public: + enum class Class { + Null, + Object, + Array, + String, + Floating, + Integral, + Boolean + }; + + template + class JSONWrapper { + Container *object; + + public: + JSONWrapper( Container *val ) : object( val ) {} + JSONWrapper( std::nullptr_t ) : object( nullptr ) {} + + typename Container::iterator begin() { return object ? object->begin() : typename Container::iterator(); } + typename Container::iterator end() { return object ? object->end() : typename Container::iterator(); } + typename Container::const_iterator begin() const { return object ? object->begin() : typename Container::iterator(); } + typename Container::const_iterator end() const { return object ? object->end() : typename Container::iterator(); } + }; + + template + class JSONConstWrapper { + const Container *object; + + public: + JSONConstWrapper( const Container *val ) : object( val ) {} + JSONConstWrapper( std::nullptr_t ) : object( nullptr ) {} + + typename Container::const_iterator begin() const { return object ? object->begin() : typename Container::const_iterator(); } + typename Container::const_iterator end() const { return object ? object->end() : typename Container::const_iterator(); } + }; + + JSON() : Internal(), Type( Class::Null ){} + + JSON( initializer_list list ) : JSON() { + SetType( Class::Object ); + for( auto i = list.begin(), e = list.end(); i != e; ++i, ++i ) + operator[]( i->ToString() ) = *std::next( i ); + } + + JSON( JSON&& other ) : Internal( other.Internal ), Type( other.Type ) { + other.Type = Class::Null; other.Internal.Map = nullptr; + } + + JSON& operator=( JSON&& other ) { + ClearInternal(); + Internal = other.Internal; + Type = other.Type; + other.Internal.Map = nullptr; + other.Type = Class::Null; + return *this; + } + + JSON( const JSON &other ) { + switch( other.Type ) { + case Class::Object: + Internal.Map = + new map( other.Internal.Map->begin(), + other.Internal.Map->end() ); + break; + case Class::Array: + Internal.List = + new deque( other.Internal.List->begin(), + other.Internal.List->end() ); + break; + case Class::String: + Internal.String = + new string( *other.Internal.String ); + break; + default: + Internal = other.Internal; + } + Type = other.Type; + } + + JSON& operator=( const JSON &other ) { + ClearInternal(); + switch( other.Type ) { + case Class::Object: + Internal.Map = + new map( other.Internal.Map->begin(), + other.Internal.Map->end() ); + break; + case Class::Array: + Internal.List = + new deque( other.Internal.List->begin(), + other.Internal.List->end() ); + break; + case Class::String: + Internal.String = + new string( *other.Internal.String ); + break; + default: + Internal = other.Internal; + } + Type = other.Type; + return *this; + } + + ~JSON() { + switch( Type ) { + case Class::Array: + delete Internal.List; + break; + case Class::Object: + delete Internal.Map; + break; + case Class::String: + delete Internal.String; + break; + default:; + } + } + + template + JSON( T b, typename enable_if::value>::type* = 0 ) : Internal( b ), Type( Class::Boolean ){} + + template + JSON( T i, typename enable_if::value && !is_same::value>::type* = 0 ) : Internal( (long)i ), Type( Class::Integral ){} + + template + JSON( T f, typename enable_if::value>::type* = 0 ) : Internal( (double)f ), Type( Class::Floating ){} + + template + JSON( T s, typename enable_if::value>::type* = 0 ) : Internal( string( s ) ), Type( Class::String ){} + + JSON( std::nullptr_t ) : Internal(), Type( Class::Null ){} + + static JSON Make( Class type ) { + JSON ret; ret.SetType( type ); + return ret; + } + + static JSON Load( const string & ); + + template + void append( T arg ) { + SetType( Class::Array ); Internal.List->emplace_back( arg ); + } + + template + void append( T arg, U... args ) { + append( arg ); append( args... ); + } + + template + typename enable_if::value, JSON&>::type operator=( T b ) { + SetType( Class::Boolean ); Internal.Bool = b; return *this; + } + + template + typename enable_if::value && !is_same::value, JSON&>::type operator=( T i ) { + SetType( Class::Integral ); Internal.Int = i; return *this; + } + + template + typename enable_if::value, JSON&>::type operator=( T f ) { + SetType( Class::Floating ); Internal.Float = f; return *this; + } + + template + typename enable_if::value, JSON&>::type operator=( T s ) { + SetType( Class::String ); *Internal.String = string( s ); return *this; + } + + JSON& operator[]( const string &key ) { + SetType( Class::Object ); return Internal.Map->operator[]( key ); + } + + JSON& operator[]( unsigned index ) { + SetType( Class::Array ); + if( index >= Internal.List->size() ) Internal.List->resize( index + 1 ); + return Internal.List->operator[]( index ); + } + + JSON &at( const string &key ) { + return operator[]( key ); + } + + const JSON &at( const string &key ) const { + return Internal.Map->at( key ); + } + + JSON &at( unsigned index ) { + return operator[]( index ); + } + + const JSON &at( unsigned index ) const { + return Internal.List->at( index ); + } + + int length() const { + if( Type == Class::Array ) + return (int) Internal.List->size(); + else + return -1; + } + + bool hasKey( const string &key ) const { + if( Type == Class::Object ) + return Internal.Map->find( key ) != Internal.Map->end(); + return false; + } + + int size() const { + if( Type == Class::Object ) + return (int) Internal.Map->size(); + else if( Type == Class::Array ) + return (int) Internal.List->size(); + else + return -1; + } + + Class JSONType() const { return Type; } + + /// Functions for getting primitives from the JSON object. + bool IsNull() const { return Type == Class::Null; } + + string ToString() const { bool b; return std::move( ToString( b ) ); } + + string ToString( bool &ok ) const; + + double ToFloat() const { bool b; return ToFloat( b ); } + double ToFloat( bool &ok ) const { + ok = (Type == Class::Floating); + return ok ? Internal.Float : 0.0; + } + + long ToInt() const { bool b; return ToInt( b ); } + long ToInt( bool &ok ) const { + ok = (Type == Class::Integral); + return ok ? Internal.Int : 0; + } + + bool ToBool() const { bool b; return ToBool( b ); } + bool ToBool( bool &ok ) const { + ok = (Type == Class::Boolean); + return ok ? Internal.Bool : false; + } + + JSONWrapper> ObjectRange() { + if( Type == Class::Object ) + return JSONWrapper>( Internal.Map ); + return JSONWrapper>( nullptr ); + } + + JSONWrapper> ArrayRange() { + if( Type == Class::Array ) + return JSONWrapper>( Internal.List ); + return JSONWrapper>( nullptr ); + } + + JSONConstWrapper> ObjectRange() const { + if( Type == Class::Object ) + return JSONConstWrapper>( Internal.Map ); + return JSONConstWrapper>( nullptr ); + } + + + JSONConstWrapper> ArrayRange() const { + if( Type == Class::Array ) + return JSONConstWrapper>( Internal.List ); + return JSONConstWrapper>( nullptr ); + } + + string dump( int depth = 1, string tab = " ") const; + + // friend std::ostream& operator<<( std::ostream&, const JSON & ); + + private: + void SetType( Class type ) { + if( type == Type ) + return; + + ClearInternal(); + + switch( type ) { + case Class::Null: Internal.Map = nullptr; break; + case Class::Object: Internal.Map = new map(); break; + case Class::Array: Internal.List = new deque(); break; + case Class::String: Internal.String = new string(); break; + case Class::Floating: Internal.Float = 0.0; break; + case Class::Integral: Internal.Int = 0; break; + case Class::Boolean: Internal.Bool = false; break; + } + + Type = type; + } + + private: + /* beware: only call if YOU know that Internal is allocated. No checks performed here. + This function should be called in a constructed JSON just before you are going to + overwrite Internal... + */ + void ClearInternal() { + switch( Type ) { + case Class::Object: delete Internal.Map; break; + case Class::Array: delete Internal.List; break; + case Class::String: delete Internal.String; break; + default:; + } + } + + private: + + Class Type = Class::Null; +}; + +JSON Array(); + +template +JSON Array( T... args ); + +JSON Object(); + +// std::ostream& operator<<( std::ostream &os, const JSON &json ) { +// os << json.dump(); +// return os; +// } + +} // End Namespace json + +#endif // MECA_SGX_COMMON_JSON_H_ diff --git a/sdk/example_containers/sgx-task/common/tcrypto_ext.cc b/sdk/example_containers/sgx-task/common/tcrypto_ext.cc new file mode 100644 index 0000000..376ae2a --- /dev/null +++ b/sdk/example_containers/sgx-task/common/tcrypto_ext.cc @@ -0,0 +1,360 @@ +#include "tcrypto_ext.h" + +#include +#include + +// for key pair generation +#include "openssl/ec.h" +#include "openssl/evp.h" +#include "openssl/pem.h" +#include "openssl/rsa.h" + +namespace { +// for now we use fixed IV and aad for prototype +// for production, different value should be used for encryption (need to support update and thread synchronization on them) +uint8_t enc_iv_buf[AES_GCM_IV_SIZE] = {0}; +uint8_t enc_aad_buf[AES_GCM_AAD_SIZE] = {0}; +} // anonymous namespace + +/** + * @brief + * + * @param src + * @param size + * @param decryption_key + * @param output caller is responsible to free the resources + * @return sgx_status_t + */ +sgx_status_t decrypt_content_with_key_aes(const uint8_t* src, + size_t size, const uint8_t* decryption_key, uint8_t** output) { + + size_t cipher_text_size = get_aes_decrypted_size(size); + + // temporary small variables + uint8_t iv_buf[AES_GCM_IV_SIZE] = {0}; + uint8_t tag_buf[AES_GCM_TAG_SIZE] = {0}; + uint8_t aad_buf[AES_GCM_AAD_SIZE] = {0}; + + // buffer for decrypted result. ownership transfer at the end to output. + uint8_t* result = (uint8_t*) malloc(cipher_text_size+1); + sgx_status_t ret = (result == NULL) + ? SGX_ERROR_OUT_OF_MEMORY : SGX_SUCCESS; + + if (ret == SGX_SUCCESS) { + // copy contents + const uint8_t* p = src; + p += cipher_text_size; + memcpy(iv_buf, p, AES_GCM_IV_SIZE); + p += AES_GCM_IV_SIZE; + memcpy(tag_buf, p, AES_GCM_TAG_SIZE); + p += AES_GCM_TAG_SIZE; + memcpy(aad_buf, p, AES_GCM_AAD_SIZE); + + // decrypt + ret = sgx_rijndael128GCM_decrypt( + (const sgx_aes_gcm_128bit_key_t*) decryption_key, src, + (uint32_t) cipher_text_size, result, iv_buf, AES_GCM_IV_SIZE, + aad_buf, AES_GCM_AAD_SIZE, (const sgx_aes_gcm_128bit_tag_t*) tag_buf); + result[cipher_text_size]='\0'; + } + + // assign the result to output if success; free the resource otherwise. + if (ret != SGX_SUCCESS) { + free(result); + return ret; + } + *output = result; + + return ret; +} + +/** + * @brief + * + * @param src + * @param size + * @param encryption_key + * @param output caller is responsible to free the resources. + * @return sgx_status_t + */ +sgx_status_t encrypt_content_with_key_aes(const uint8_t* src, + size_t size, const uint8_t* encryption_key, uint8_t** output) { + + size_t whole_cipher_text_size = get_aes_encrypted_size(size); + + // allocate temporary buffers for decryption operation. freed at the end. + uint8_t* whole_cipher_text = (uint8_t*) malloc(whole_cipher_text_size); + if (whole_cipher_text == NULL) return SGX_ERROR_OUT_OF_MEMORY; + + // temporary variables + uint8_t tag_buf[AES_GCM_TAG_SIZE] = {0}; + + // encrypt + sgx_status_t ret = sgx_rijndael128GCM_encrypt( + (const sgx_aes_gcm_128bit_key_t*) encryption_key, src, + (uint32_t) size, whole_cipher_text, enc_iv_buf, AES_GCM_IV_SIZE, + enc_aad_buf, AES_GCM_AAD_SIZE, (sgx_aes_gcm_128bit_tag_t*) tag_buf); + + // free the resource when failure. + if (ret != SGX_SUCCESS) { + free(whole_cipher_text); + return ret; + } + + // assemble the message + uint8_t* pos = whole_cipher_text + size; + memcpy(pos, enc_iv_buf, AES_GCM_IV_SIZE); + pos += AES_GCM_IV_SIZE; + memcpy(pos, tag_buf, AES_GCM_TAG_SIZE); + pos += AES_GCM_TAG_SIZE; + memcpy(pos, enc_aad_buf, AES_GCM_AAD_SIZE); + + // assign the result to output if success; + *output = whole_cipher_text; + + return ret; +} + +// from SampleCode/SampleAttestedTLS/common/utility.cc + +int get_pkey_by_rsa(EVP_PKEY *pk) +{ + int res = -1; + EVP_PKEY_CTX *ctx = NULL; + + ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, NULL); + if (ctx == NULL) + return res; + res = EVP_PKEY_keygen_init(ctx); + if (res <= 0) + { + // PRINT("keygen_init failed (%d)\n", res); + goto done; + } + + res = EVP_PKEY_CTX_set_rsa_keygen_bits(ctx, RSA_3072_PRIVATE_KEY_SIZE); + if (res <= 0) + { + // PRINT("set_rsa_kengen_bits failed (%d)\n", res); + goto done; + } + + /* Generate key */ + res = EVP_PKEY_keygen(ctx, &pk); + if (res <= 0) + { + // PRINT("keygen failed (%d)\n", res); + goto done; + } + +done: + if (ctx) + EVP_PKEY_CTX_free(ctx); + + return res; + +} + +int get_pkey_by_ec(EVP_PKEY *pk) +{ + int res = -1; + EVP_PKEY_CTX *ctx; + + ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_EC, NULL); + if (ctx == NULL) + return res; + res = EVP_PKEY_keygen_init(ctx); + if (res <= 0) + { + // PRINT("EC_generate_key failed (%d)\n", res); + goto done; + } + + res = EVP_PKEY_CTX_set_ec_paramgen_curve_nid(ctx, NID_secp384r1); + if (res <= 0) + { + // PRINT("EC_generate_key failed (%d)\n", res); + goto done; + } + + /* Generate key */ + res = EVP_PKEY_keygen(ctx, &pk); + if (res <= 0) + { + // PRINT("EC_generate_key failed (%d)\n", res); + goto done; + } + +done: + if (ctx) + EVP_PKEY_CTX_free(ctx); + + return res; +} + + +// actually is generating RSA pair +// hardare independant +sgx_status_t generate_key_pair( + int type, + uint8_t** public_key, + size_t* public_key_size, + uint8_t** private_key, + size_t* private_key_size) +{ + sgx_status_t result = SGX_ERROR_UNEXPECTED; + uint8_t* local_public_key = nullptr; + uint8_t* local_private_key = nullptr; + int res = -1; + EVP_PKEY* pkey = nullptr; + BIO* bio = nullptr; + + pkey = EVP_PKEY_new(); + if (!pkey) + { + // PRINT("EVP_PKEY_new failed\n"); + result = SGX_ERROR_UNEXPECTED; + goto done; + } + + if (type != RSA_TYPE && type != EC_TYPE) + { + type = RSA_TYPE; // by default, we use RSA_TYPE + } + + switch(type) + { + case RSA_TYPE: + res = get_pkey_by_rsa(pkey); + break; + case EC_TYPE: + res = get_pkey_by_ec(pkey); + break; + } + + if (res <= 0) + { + // PRINT("get_pkey failed (%d)\n", res); + result = SGX_ERROR_UNEXPECTED; + goto done; + } + + // Allocate memory + local_public_key = (uint8_t*)malloc(RSA_3072_PUBLIC_KEY_SIZE); + if (!local_public_key) + { + // PRINT("out-of-memory:calloc(local_public_key failed\n"); + result = SGX_ERROR_OUT_OF_EPC; + goto done; + } + memset(local_public_key, 0x00, RSA_3072_PUBLIC_KEY_SIZE); + + local_private_key = (uint8_t*)malloc(RSA_3072_PRIVATE_KEY_SIZE); + if (!local_private_key) + { + // PRINT("out-of-memory: calloc(local_private_key) failed\n"); + result = SGX_ERROR_OUT_OF_EPC; + goto done; + } + memset(local_private_key, 0x00, RSA_3072_PRIVATE_KEY_SIZE); + + // Write out the public/private key in PEM format for exchange with + // other enclaves. + bio = BIO_new(BIO_s_mem()); + if (!bio) + { + // PRINT("BIO_new for local_public_key failed\n"); + goto done; + } + + res = PEM_write_bio_PUBKEY(bio, pkey); + if (!res) + { + // PRINT("PEM_write_bio_PUBKEY failed (%d)\n", res); + goto done; + } + + res = BIO_read(bio, local_public_key, RSA_3072_PUBLIC_KEY_SIZE); + if (!res) + { + // PRINT("BIO_read public key failed (%d)\n", res); + goto done; + } + BIO_free(bio); + bio = nullptr; + + bio = BIO_new(BIO_s_mem()); + if (!bio) + { + // PRINT("BIO_new for local_public_key failed\n"); + goto done; + } + + res = PEM_write_bio_PrivateKey( + bio, pkey, nullptr, nullptr, 0, nullptr, nullptr); + if (!res) + { + // PRINT("PEM_write_bio_PrivateKey failed (%d)\n", res); + goto done; + } + + res = BIO_read(bio, local_private_key, RSA_3072_PRIVATE_KEY_SIZE); + if (!res) + { + // PRINT("BIO_read private key failed (%d)\n", res); + goto done; + } + + BIO_free(bio); + bio = nullptr; + + *public_key = local_public_key; + *private_key = local_private_key; + + *public_key_size = strlen(reinterpret_cast(local_public_key)); + *private_key_size = strlen(reinterpret_cast(local_private_key)); + + // PRINT("public_key_size %d, private_key_size %d\n", *public_key_size, *private_key_size); + result = SGX_SUCCESS; + +done: + if (bio) + BIO_free(bio); + if (pkey) + EVP_PKEY_free(pkey); // When this is called, rsa is also freed + if (result != SGX_SUCCESS) + { + if (local_public_key) + free(local_public_key); + if (local_private_key) + free(local_private_key); + } + return result; +} + +// added decryption with encrypted symmetric key and encrypted secret data +// the decrypted data will contains the output encryption key at its tail. +unsigned char* rsa_decrypt_data(const unsigned char* data, int len, const unsigned char* ek, int ek_len, const unsigned char* iv, const uint8_t* rsa_private_key, int rsa_private_key_len, int* output_len) { + BIO* bio = nullptr; + bio = BIO_new_mem_buf(rsa_private_key, rsa_private_key_len); + RSA* loaded_private_key = NULL; + PEM_read_bio_RSAPrivateKey(bio, &loaded_private_key, NULL, NULL); + EVP_PKEY* pkey = EVP_PKEY_new(); + EVP_PKEY_assign_RSA(pkey, loaded_private_key); + EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new(); + EVP_CIPHER_CTX_init(ctx); + unsigned char* decrypted = (unsigned char*)malloc(len + EVP_MAX_IV_LENGTH); + EVP_OpenInit(ctx, EVP_aes_256_cbc(), ek, ek_len, iv, pkey); + int decrypted_len = 0; + int decyprtedBlockLen = 0; + EVP_OpenUpdate(ctx, decrypted, &decyprtedBlockLen, data, len); + decrypted_len = decyprtedBlockLen; + EVP_OpenFinal(ctx, decrypted + decrypted_len, &decyprtedBlockLen); + decrypted_len += decyprtedBlockLen; + *output_len = decrypted_len; + // free ctx + EVP_CIPHER_CTX_free(ctx); + BIO_free(bio); + EVP_PKEY_free(pkey); + return decrypted; +} diff --git a/sdk/example_containers/sgx-task/common/tcrypto_ext.h b/sdk/example_containers/sgx-task/common/tcrypto_ext.h new file mode 100644 index 0000000..2895506 --- /dev/null +++ b/sdk/example_containers/sgx-task/common/tcrypto_ext.h @@ -0,0 +1,67 @@ +#ifndef MECA_SGX_COMMON_TCRYPTOEXT_H_ +#define MECA_SGX_COMMON_TCRYPTOEXT_H_ + +// helper functions to use tcrypto. +#include "sgx_tcrypto.h" +#include + +#define AES_GCM_IV_SIZE 12 +#define AES_GCM_TAG_SIZE 16 +#define AES_GCM_AAD_SIZE 4 + +#define MAX_SESSION_KEY_IV_LENGTH 16 +#define ENCRYPTED_SESSION_KEY_LENGTH 384 +#define OUTPUT_KEY_LENGTH 16 + +inline size_t get_aes_decrypted_size(size_t size) { + return size - AES_GCM_IV_SIZE - AES_GCM_TAG_SIZE - AES_GCM_AAD_SIZE; +} + +inline size_t get_aes_encrypted_size(size_t size) { + return size + AES_GCM_IV_SIZE + AES_GCM_TAG_SIZE + AES_GCM_AAD_SIZE; +} + +/** + * @brief + * + * @param src + * @param size + * @param decryption_key + * @param output caller is responsible to free the resources + * @return sgx_status_t + */ +sgx_status_t decrypt_content_with_key_aes(const uint8_t* src, + size_t size, const uint8_t* decryption_key, uint8_t** output); + +/** + * @brief + * + * @param src + * @param size + * @param encryption_key + * @param output caller is responsible to free the resources. + * @return sgx_status_t + */ +sgx_status_t encrypt_content_with_key_aes(const uint8_t* src, + size_t size, const uint8_t* encryption_key, uint8_t** output); + +// from SampleCode/SampleAttestedTLS/common/utility.h + +#define RSA_PUBLIC_KEY_SIZE 512 +#define RSA_PRIVATE_KEY_SIZE 2048 + +#define RSA_3072_PUBLIC_KEY_SIZE 650 +#define RSA_3072_PRIVATE_KEY_SIZE 3072 + +#define RSA_TYPE 0 +#define EC_TYPE 1 // EC-P384 + +sgx_status_t generate_key_pair(int type, uint8_t** public_key, + size_t* public_key_size, uint8_t** private_key, size_t* private_key_size); + +// decrypt data with RSA private key +unsigned char* rsa_decrypt_data(const unsigned char* data, int len, + const unsigned char* ek, int ek_len, const unsigned char* iv, + const uint8_t* rsa_private_key, int rsa_private_key_len, int* output_len); + +#endif // MECA_SGX_COMMON_TCRYPTOEXT_H_ diff --git a/sdk/example_containers/sgx-task/deps/libuv b/sdk/example_containers/sgx-task/deps/libuv new file mode 160000 index 0000000..e8b7eb6 --- /dev/null +++ b/sdk/example_containers/sgx-task/deps/libuv @@ -0,0 +1 @@ +Subproject commit e8b7eb6908a847ffbe6ab2eec7428e43a0aa53a2 diff --git a/sdk/example_containers/sgx-task/deps/llhttp b/sdk/example_containers/sgx-task/deps/llhttp new file mode 160000 index 0000000..75b4512 --- /dev/null +++ b/sdk/example_containers/sgx-task/deps/llhttp @@ -0,0 +1 @@ +Subproject commit 75b45129db961e1fb3c56044e1b8f7721bfaee5d diff --git a/sdk/example_containers/sgx-task/sample_input.json b/sdk/example_containers/sgx-task/sample_input.json new file mode 100644 index 0000000..abd8d91 --- /dev/null +++ b/sdk/example_containers/sgx-task/sample_input.json @@ -0,0 +1,4 @@ +{ + "value": "check", + "use_sgx": true +} \ No newline at end of file diff --git a/sdk/example_containers/sgx-task/server/Makefile b/sdk/example_containers/sgx-task/server/Makefile new file mode 100644 index 0000000..dcb0a8e --- /dev/null +++ b/sdk/example_containers/sgx-task/server/Makefile @@ -0,0 +1,137 @@ +PROJECT_ROOT_DIR ?= $(shell readlink -f ..) +WORKER_INSTALL_PATH ?= $(PROJECT_ROOT_DIR)/worker/install +INSTALL_PREFIX ?= $(shell readlink -f install) + +DEPS_INSTALL_DIR = $(PROJECT_ROOT_DIR)/deps/install +LIBUV_DIR = $(DEPS_INSTALL_DIR)/libuv +LLHTTP_DIR = $(DEPS_INSTALL_DIR)/llhttp + +CONCURRENT_RT_LINK_FLAGS := -L$(LIBUV_DIR)/lib -l:libuv_a.a -lpthread -ldl +CONCURRENT_RT_CXX_FLAGS := -I$(LIBUV_DIR)/include +CONCURRENT_RT_CXX_FLAGS += -DUSE_LLHTTP -I$(LLHTTP_DIR)/include +CONCURRENT_RT_LINK_FLAGS += -L$(LLHTTP_DIR)/lib -l:libllhttp.a + +deps: + @echo "run make deps under repo root to build deps" + +# build with the Makefile under concurrent_runtime + +SGX_SDK ?= /opt/intel/sgxsdk +SGXSSL_DIR ?= /opt/intel/sgxssl +SGX_MODE ?= HW +SGX_ARCH ?= x64 +SGX_DEBUG ?= 1 + +WORKER_INSTALL_PATH ?= $(PROJECT_ROOT_DIR)/worker/install + +### Intel(R) SGX SDK Settings ### +ifeq ($(shell getconf LONG_BIT), 32) + SGX_ARCH := x86 +else ifeq ($(findstring -m32, $(CXXFLAGS)), -m32) + SGX_ARCH := x86 +endif + +ifeq ($(SGX_ARCH), x86) + SGX_COMMON_CFLAGS := -m32 + SGX_LIBRARY_PATH := $(SGX_SDK)/lib + SGX_ENCLAVE_SIGNER := $(SGX_SDK)/bin/x86/sgx_sign + SGX_EDGER8R := $(SGX_SDK)/bin/x86/sgx_edger8r +else + SGX_COMMON_CFLAGS := -m64 + SGX_LIBRARY_PATH := $(SGX_SDK)/lib64 + SGX_ENCLAVE_SIGNER := $(SGX_SDK)/bin/x64/sgx_sign + SGX_EDGER8R := $(SGX_SDK)/bin/x64/sgx_edger8r +endif + +ifeq ($(SGX_DEBUG), 1) +ifeq ($(SGX_PRERELEASE), 1) +$(error Cannot set SGX_DEBUG and SGX_PRERELEASE at the same time!!) +endif +endif + +ifeq ($(SGX_DEBUG), 1) + SGX_COMMON_CFLAGS += -O0 -g -DSGX_DEBUG +else + SGX_COMMON_CFLAGS += -O2 +endif + +ifneq ($(SGX_MODE), HW) + Urts_Library_Name := sgx_urts_sim +else + Urts_Library_Name := sgx_urts +endif + +APP_DCAP_LIBS := -lsgx_dcap_ql -lsgx_dcap_quoteverify +### Intel(R) SGX SDK Settings ### + +COMMON_CXX_FLAGS := $(CONCURRENT_RT_CXX_FLAGS) -I$(PROJECT_ROOT_DIR) + +.PHONY: all clean install + +all: sample-server + +Sources := main.cc +Sources += worker_service.cc +# setup compile flags +COMMON_CXX_FLAGS += -DUSE_WORKER_SERVICE +COMMON_CXX_FLAGS += $(SGX_COMMON_CFLAGS) -fPIC -Wno-attributes +# Three configuration modes - Debug, prerelease, release +# Debug - Macro DEBUG enabled. +# Prerelease - Macro NDEBUG and EDEBUG enabled. +# Release - Macro NDEBUG enabled. +ifeq ($(SGX_DEBUG), 1) + COMMON_CXX_FLAGS += -DDEBUG -UNDEBUG -UEDEBUG +else ifeq ($(SGX_PRERELEASE), 1) + COMMON_CXX_FLAGS += -DNDEBUG -DEDEBUG -UDEBUG +else + COMMON_CXX_FLAGS += -DNDEBUG -UEDEBUG -UDEBUG +endif # SGX_DEBUG + COMMON_CXX_FLAGS += -I$(SGX_SDK)/include -I$(WORKER_INSTALL_PATH)/include +# setup linker flags + COMMON_LINK_FLAGS += -L$(WORKER_INSTALL_PATH)/lib -l:libworker.a + + COMMON_LINK_FLAGS += $(CONCURRENT_RT_LINK_FLAGS) + +COMMON_LINK_FLAGS += -L$(SGX_LIBRARY_PATH) -l$(Urts_Library_Name) $(APP_DCAP_LIBS) \ +-lpthread -lz -lm -lcrypto +ifneq ($(SGX_MODE), HW) + COMMON_LINK_FLAGS += -lsgx_uae_service_sim +else + COMMON_LINK_FLAGS += -lsgx_uae_service +endif + +COMMON_LINK_FLAGS += -L$(SGXSSL_DIR)/lib64 -lsgx_usgxssl + +Common_CXX_Flags += -Wall -Wextra -Winit-self -Wpointer-arith -Wreturn-type \ + -Waddress -Wsequence-point -Wformat-security \ + -Wmissing-include-dirs -Wfloat-equal -Wundef -Wshadow \ + -Wcast-align -Wcast-qual -Wconversion -Wredundant-decls + +Source_objects = $(Sources:.cc=.o) + +%.o: %.cc + $(CXX) $(COMMON_CXX_FLAGS) -c $< -o $@ + +ifneq ($(USE_WORKER_SERVICE), 1) +json.o: $(PROJECT_ROOT_DIR)/common/json.cc + $(CXX) $(COMMON_CXX_FLAGS) -c $< -o $@ + +Source_objects += json.o + +endif # build default server + +sample-server: $(Source_objects) + $(CXX) $^ -o $@ $(COMMON_LINK_FLAGS) + +install: + install -d $(INSTALL_PREFIX)/bin + install -C -m 755 sample-server $(INSTALL_PREFIX)/bin/server + install -d $(INSTALL_PREFIX)/lib + install -C -m 664 $(WORKER_INSTALL_PATH)/lib/Worker_Enclave.signed.so $(INSTALL_PREFIX)/lib + +clean: + rm -f *.o + rm -f sample-server + +mrproper: clean + rm -rf install diff --git a/sdk/example_containers/sgx-task/server/README.md b/sdk/example_containers/sgx-task/server/README.md new file mode 100644 index 0000000..c002c0e --- /dev/null +++ b/sdk/example_containers/sgx-task/server/README.md @@ -0,0 +1,8 @@ +# server + +build and launch the server in this directory + +```sh +make all +SGX_AESM_ADDR=1 ./sample-server ../worker/install/lib/Worker_Enclave.signed.so +``` diff --git a/sdk/example_containers/sgx-task/server/main.cc b/sdk/example_containers/sgx-task/server/main.cc new file mode 100644 index 0000000..bc588f6 --- /dev/null +++ b/sdk/example_containers/sgx-task/server/main.cc @@ -0,0 +1,356 @@ +#include +#include +#include +#include + +#include +#include + +#include "uv.h" +#include "service.h" + +#include "worker_service.h" + +#include "messager.h" + +#include "llhttp.h" +typedef llhttp_t parser_t; +typedef llhttp_settings_t parser_settings_t; +#define PARSER_SETTINGS_INIT(m) llhttp_settings_init(m) + + + +namespace { + +constexpr char meca_ready[] = "meca-init-done"; + +Service* svc = nullptr; + +Messenger msgr; + +struct HttpRequest { + uint64_t content_len; + std::string url; + std::string body; +}; + +uv_tcp_t server; +uv_loop_t* loop; + +struct client_t { + uv_tcp_t handle; + parser_t parser; + uv_write_t write_req; + std::string response_scratch; // buffer for the resbuf + uv_buf_t resbuf{nullptr, 0}; + bool is_http_req = false; + bool next_header_value_is_content_len = false; + HttpRequest req; + runtime_task_t task_type; + uv_work_t work; // at a time there is only one work scheduled. +}; + +parser_settings_t settings; + +bool check_error(int ret) { + if (!ret) return false; + fprintf(stderr, "libuv error: %s\n", uv_strerror(ret)); + return true; +} + +void on_close(uv_handle_t* handle) { + printf("on close\n"); + client_t* client = (client_t*) handle->data; + client->resbuf.base = nullptr; + client->resbuf.len = 0; + delete client; + client = nullptr; +} + +void after_write(uv_write_t* req, int status) { + check_error(status); + uv_close((uv_handle_t*) req->handle, on_close); +} + +// concurrent runtime functions +std::string build_response(response_code_t code, const std::string& msg) { + auto build = [](const std::string&& code_msg, const std::string&& msg) + -> std::string { + return "HTTP/1.1 " + code_msg + "\r\n" + + "Content-Type: application/json\r\n" + + "Content-Length: "+ std::to_string(msg.size())+"\r\n" + + "\r\n" + + std::move(msg); + }; + + // auto json_msg = "{\"msg\": \"" + std::move(msg) + "\"}"; + switch (code) { + case OK: + return build("200 OK", msgr.PackageResponse(false, msg)); + case FORBIDDEN: + return build("403 Forbidden", msgr.PackageResponse(true, msg)); + case NOT_FOUND: + return build("404 Not Found", msgr.PackageResponse(true, msg)); + default: + return build("500 Internal Server Error", + msgr.PackageResponse(true, msg)); + } +} + + +inline void flush_meca_ready() { + printf("%s\n", meca_ready); + fflush(stdout); +} + +void on_work(uv_work_t* req) { + client_t* client = (client_t*)req->data; + response_code_t code; + std::string msg; +#ifndef NDEBUG + // printf("\nRequest body before processing:\n%s\n", client->req.body.c_str()); +#endif // NDEBUG + switch (client->task_type) { + case RA: + code = svc->Ra(&msg); + break; + case RUN: + code = svc->Run(msgr.ExtractUserJson(client->req.body), &msg); + break; + case INIT: + code = svc->Init(client->req.body, &msg); + break; + default: + code = NOT_FOUND; + msg = "unsupported path: " + client->req.url + "\n"; + break; + } + client->response_scratch = build_response(code, std::move(msg)); + client->resbuf = uv_buf_init( + const_cast(client->response_scratch.c_str()), + client->response_scratch.size()); + // printf("response to sent %.*s\n", (int) client->response_scratch.size(), + // client->response_scratch.c_str()); +} + +void after_work(uv_work_t* req, int status) { + if (check_error(status)) return; + client_t* client = (client_t*)req->data; + uv_write(&client->write_req, (uv_stream_t*) &client->handle, + &client->resbuf, 1, after_write); +} +// concurrent runtime functions + +int on_message_begin(parser_t* _) { + (void)_; +#ifndef NDEBUG + printf("\n***MESSAGE BEGIN***\n\n"); +#endif // NDEBUG + return 0; +} + +int on_headers_complete(parser_t* _) { + (void)_; +#ifndef NDEBUG + printf("\n***HEADERS COMPLETE***\n\n"); +#endif // NDEBUG + return 0; +} + +int on_message_complete(parser_t* parser) { +#ifndef NDEBUG + printf("\n***MESSAGE COMPLETE***\n\n"); +#endif // NDEBUG + client_t* client = (client_t*) parser->data; + if (client->req.url == "/run") { + client->task_type = RUN; + } else if (client->req.url == "/ra") { + client->task_type = RA; + } else if (client->req.url == "/init") { + client->task_type = INIT; + } else { + client->task_type = UNDEFINED; + } + uv_queue_work(client->handle.loop, &client->work, on_work, + after_work); + return 0; +} + +int on_url(parser_t* parser, const char* at, size_t length) { +#ifndef NDEBUG + // printf("Url (%d): %.*s\n", (int)length, (int)length, at); +#endif // NDEBUG + client_t* client = (client_t*) parser->data; + client->req.url = std::string(at, length); + return 0; +} + +int on_header_field(parser_t* parser, const char* at, size_t length) { +#ifndef NDEBUG + // printf("Header field: %.*s\n", (int)length, at); +#endif // NDEBUG + if(strncmp(at, "Content-Type", std::max(length, strlen("Content-Type")))) { + client_t* client = (client_t*) parser->data; + client->next_header_value_is_content_len = true; + } + return 0; +} + +int on_header_value(parser_t* parser, const char* at, size_t /*length*/) { +#ifndef NDEBUG + // printf("Header value: %.*s\n", (int)length, at); +#endif // NDEBUG + client_t* client = (client_t*) parser->data; + if(client->next_header_value_is_content_len) { + client->req.content_len = strtoull(at, NULL, 10); + client->req.body.reserve(client->req.content_len); + client->next_header_value_is_content_len = false; + } + return 0; +} + +int on_body(parser_t* parser, const char* at, size_t length) { + // (void)_; + client_t* client = (client_t*) parser->data; + client->req.body.append(at, length); +#ifndef NDEBUG + // printf("Body: %.*s\n", (int)length, at); +#endif // NDEBUG + return 0; +} + +// all the callbacks implementation are from http-parser/contrib/parsertrace.c +// to print something demonstrating the processing of each phase. +// on_message_complete is rewritten to send back response. +void setup_http_parser_settings() { + PARSER_SETTINGS_INIT(&settings); + settings.on_message_begin = on_message_begin; + settings.on_url = on_url; + settings.on_header_field = on_header_field; + settings.on_header_value = on_header_value; + settings.on_headers_complete = on_headers_complete; + settings.on_body = on_body; + settings.on_message_complete = on_message_complete; +} + +void on_alloc(uv_handle_t* /*handle*/, size_t suggested_size, uv_buf_t* buf) { +#ifndef NDEBUG + // printf("on alloc\n"); +#endif // NDEBUG + *buf = uv_buf_init((char*) malloc(suggested_size), suggested_size); +} + +void on_read(uv_stream_t* stream, ssize_t nread, const uv_buf_t* buf) { +#ifndef NDEBUG + // printf("on read\n"); +#endif // NDEBUG + /*do something*/ + if (nread >= 0) { +#ifndef NDEBUG + // printf("Read:\n%.*s\n", (int) nread, buf->base); +#endif // NDEBUG + /*parse http*/ + client_t* client = (client_t*) uv_handle_get_data((uv_handle_t*) stream); + parser_t* parser = &client->parser; + + + if (!client->is_http_req) { + auto ret = llhttp_execute(parser, buf->base, nread); + if (ret != HPE_OK) { + fprintf(stderr, "Parse error: %s %s\n", llhttp_errno_name(ret), + parser->reason); + printf("Not a http request, no response sent\n"); + } + } else { + // continuous reading the request. append to the body. + client->req.body.append(buf->base, nread); + } + + } else { + // error + if (nread != UV_EOF) { + printf("Read error: %ld\n", nread); + } + uv_close((uv_handle_t*) stream, on_close); + uv_stop(loop); + printf("server schedules to shutdown\n"); + } + + free(buf->base); +} + +void on_connection(uv_stream_t* server_handle, int status) { + assert(server_handle == (uv_stream_t*) &server); + printf("\non_connection\n"); + + if (check_error(status)) return; + + // allocate http parser and a handle for each connection + client_t* client = new client_t; + + // init + llhttp_init(&client->parser, HTTP_BOTH, &settings); + + auto ret = uv_tcp_init(server_handle->loop, &client->handle); + // let the data pointer of handle to point to the client struct, + // so we can access http parser. + uv_handle_set_data((uv_handle_t*) &client->handle, client); + // let the data pointer of parser to point to the client struct, + // so we can access handle. + client->parser.data = client; + uv_req_set_data((uv_req_t*) &client->work, client); + + check_error(ret); + ret = uv_accept(server_handle, (uv_stream_t*) &client->handle); + if (check_error(ret)) { + uv_close((uv_handle_t*) &client->handle, on_close); + } else { + ret = uv_read_start((uv_stream_t*) &client->handle, on_alloc, on_read); + check_error(ret); + } +} +} // anonymous namespace + +int main(int argc, char* argv[]) { +// pick service selection + + if (argc != 2) { + fprintf(stderr, "Usage: worker_enclave\n"); + exit(1); + } + auto enclave_path = argv[1]; + WorkerService s{enclave_path}; + svc = &s; + + // setup http parser settings + setup_http_parser_settings(); + + // we only need a single loop, so use the default loop. + // uv_loop_t* loop = (uv_loop_t*) malloc(sizeof(uv_loop_t)); + // uv_loop_init(loop); + loop = uv_default_loop(); + + // start a server + uv_tcp_init(loop, &server); + + struct sockaddr_in address; + uv_ip4_addr("0.0.0.0", 8080, &address); + // uv_ip4_addr("0.0.0.0", 2531, &address); + + int ret = uv_tcp_bind(&server, (const struct sockaddr*) &address, 0); + check_error(ret); + + printf("sample server launched\n"); + + ret = uv_listen((uv_stream_t*) &server, 128, on_connection); + check_error(ret); + + flush_meca_ready(); + + uv_run(loop, UV_RUN_DEFAULT); + uv_loop_close(loop); + + // using default loop. + // free(loop); + return 0; +} diff --git a/sdk/example_containers/sgx-task/server/messager.h b/sdk/example_containers/sgx-task/server/messager.h new file mode 100644 index 0000000..fe63813 --- /dev/null +++ b/sdk/example_containers/sgx-task/server/messager.h @@ -0,0 +1,40 @@ +#ifndef MECA_SGX_MESSENGER_H_ +#define MECA_SGX_MESSENGER_H_ + +#include + +#include "common/json.h" + +class Messenger { + public: + ~Messenger() = default; + + // extract users request json from the serverless platform request + inline std::string ExtractUserJson( + const std::string& platform_message) const { + auto input = json::JSON::Load(platform_message); + return input["value"].ToString(); + } + + + + // package worker result to serverless platform compatible response format + // for error, it needs a json with a single field called "error" + inline std::string PackageResponse(bool error, const std::string& response) + const { + json::JSON ret; + if (error) { + ret["error"] = std::move(response); + } else { + auto json_response = json::JSON::Load(response); + if (!json_response.IsNull()) { + ret["msg"] = json_response; + } else { + ret["msg"] = std::move(response); + } + } + return ret.dump(); + } +}; + +#endif // MECA_SGX_MESSENGER_H_ diff --git a/sdk/example_containers/sgx-task/server/service.h b/sdk/example_containers/sgx-task/server/service.h new file mode 100644 index 0000000..aab3732 --- /dev/null +++ b/sdk/example_containers/sgx-task/server/service.h @@ -0,0 +1,36 @@ +#ifndef MECA_SGX_CONCURRENTRT_U_OW_SERVICE_H_ +#define MECA_SGX_CONCURRENTRT_U_OW_SERVICE_H_ + +#include + +typedef enum { + OK, // 200 + FORBIDDEN, // 403 + NOT_FOUND, // 404 + SERVER_ERROR // 500 +} response_code_t; + +typedef enum { + INIT, + RUN, + RA, + UNDEFINED +} runtime_task_t; + +// backend service interface for concurrent runtime. +class Service { + public: + virtual ~Service() = default; + + // init is guaranteed to execute once + virtual response_code_t Init(const std::string& request, + std::string* response) = 0; + + virtual response_code_t Ra(std::string* response) = 0; + + // run is a asynchronous scheduled task. needs to be thread-safe + virtual response_code_t Run(const std::string& request, + std::string* response) = 0; +}; + +#endif // MECA_SGX_CONCURRENTRT_U_OW_SERVICE_H_ diff --git a/sdk/example_containers/sgx-task/server/worker_service.cc b/sdk/example_containers/sgx-task/server/worker_service.cc new file mode 100644 index 0000000..8e937cb --- /dev/null +++ b/sdk/example_containers/sgx-task/server/worker_service.cc @@ -0,0 +1,140 @@ +#include "worker_service.h" + +#include +#include // for INT_MAX +#include // for strlen +#include +#include + +#include "common/json.h" +#include "common/hexutil.h" + +// // implementation of ocalls +// void ocall_debug_print(const void* s, size_t len) { +// assert(len < INT_MAX); +// printf("DEBUG PRINT: %.*s\n", (int) len, (const char*) s); +// } +// void ocall_debug_print_string(const char* s) { +// printf("DEBUG PRINT: %s\n", s); +// } +// void ocall_debug_print_hexstring(const char* s) { +// printf("DEBUG PRINT (hex): "); +// for (unsigned int i = 0; i < strlen(s); i++) { +// printf("%02hhx", (unsigned char) s[i]); +// } +// printf("\n"); +// } +// void ocall_debug_print_hex(const void* s, size_t len) { +// printf("DEBUG PRINT (hex): "); +// auto it = (const unsigned char*) s; +// for (unsigned int i = 0; i < len; i++) { +// printf("%02hhx", *(it++)); +// } +// printf("\n"); +// } +// // implementation of ocalls + +void initialize_once(Worker* worker, response_code_t* code) { + // execute logic on the value for initialization + // decode json request + // return 404 for decode failure + printf("init execution\n"); + // execute logic on the value for initialization + *code = worker->Initialize() ? OK : SERVER_ERROR; +} + +response_code_t WorkerService::Init(const std::string& /*request*/, + std::string* response) { + assert(response); + response_code_t code = FORBIDDEN; + + std::call_once(initalized_, initialize_once, &worker_, &code); + switch (code) + { + case FORBIDDEN: + // send response of error 403 + printf("init phase: reinitalize forbidden by default\n"); + *response = "reinit forbidden"; + break; + case OK: + *response = "init finished"; + code = OK; + break; + default: + *response = "unkown server code from init"; + code = SERVER_ERROR; + break; + } + + return code; +} + +response_code_t WorkerService::Ra(std::string* response) { + assert(response); + + response_code_t code = OK; + std::call_once(initalized_, initialize_once, &worker_, &code); + if (code != OK) { + // called init and errors encountered + *response = "init during run and error encountered"; + return SERVER_ERROR; + } + printf("ra execution"); + + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + // upon error return 502 (500 can be used for framework internal error) + + // decode the request + // execute the logic + std::string enclave_public_key; + std::string report = worker_.GetKeyAndReport(&enclave_public_key); + if (report.size() == 0) { + *response = "ra error"; + return SERVER_ERROR; + } + + // wrap the result into a response json + json::JSON json_response; + json_response["report"] = report; + json_response["key"] = enclave_public_key; + *response = json_response.dump(); + + // return 200 with the response + return OK; +} + +response_code_t WorkerService::Run(const std::string& request, + std::string* response) { + assert(response); + + response_code_t code = OK; + std::call_once(initalized_, initialize_once, &worker_, &code); + if (code != OK) { + // called init and errors encountered + *response = "init during run and error encountered"; + return SERVER_ERROR; + } + printf("run execution"); + + printf("Request (len=%ld): %s", request.size(), request.c_str()); + + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + // upon error return 502 (500 can be used for framework internal error) + + // decode the request + auto request_data = hex_decode(request.data(), request.size()); + // execute the logic + std::string output; + auto ret = worker_.Handle(0, request_data, &output); + if (ret != 0) { + // *response = "worker execution error"; + *response = output; + return SERVER_ERROR; + } + + // wrap the result into a response json + *response = std::move(output); + + // return 200 with the response + return OK; +} diff --git a/sdk/example_containers/sgx-task/server/worker_service.h b/sdk/example_containers/sgx-task/server/worker_service.h new file mode 100644 index 0000000..c604eaa --- /dev/null +++ b/sdk/example_containers/sgx-task/server/worker_service.h @@ -0,0 +1,37 @@ +#ifndef MECA_SGX_CONCURRENTRT_U_OW_WORKERSERVICE_H_ +#define MECA_SGX_CONCURRENTRT_U_OW_WORKERSERVICE_H_ + +#include "service.h" + +#include +#include +#include + +#include "worker.h" + +class WorkerService : public Service { + public: + // the logic of worker and where it fetch and store data are configured + // in the worker_config.h + WorkerService(const std::string& enclave_path) + : worker_(std::move(enclave_path)) { + printf("Worker service config:\n enclave file name: %s\n", enclave_path.c_str()); + } + ~WorkerService() = default; + + response_code_t Init(const std::string& request, + std::string* response) override; + + // ra will also attemp to init if it has not done so. + response_code_t Ra(std::string* response) override; + + // run will also attemp to init if it has not done so. + response_code_t Run(const std::string& request, + std::string* response) override; + + private: + std::once_flag initalized_; + Worker worker_; +}; + +#endif // MECA_SGX_CONCURRENTRT_U_OW_WORKERSERVICE_H_ diff --git a/sdk/example_containers/sgx-task/worker/Makefile b/sdk/example_containers/sgx-task/worker/Makefile new file mode 100644 index 0000000..ca6b877 --- /dev/null +++ b/sdk/example_containers/sgx-task/worker/Makefile @@ -0,0 +1,31 @@ +SGX_SDK ?= /opt/intel/sgxsdk +SGX_MODE ?= HW +SGX_ARCH ?= x64 +SGX_DEBUG ?= 1 + +PROJECT_ROOT_DIR := $(shell readlink -f ..) + +INSTALL ?= install +INSTALL_PREFIX ?= ./install +INSTALL_LIB_DIR = $(INSTALL_PREFIX)/lib +INSTALL_INCLUDE_DIR = $(INSTALL_PREFIX)/include + +.PHONY: all install clean mrproper + +all: + $(MAKE) -ef sgx_u.mk all SGX_MODE=$(SGX_MODE) SGX_DEBUG=$(SGX_DEBUG) + $(MAKE) -ef sgx_t.mk all SGX_MODE=$(SGX_MODE) SGX_DEBUG=$(SGX_DEBUG) + +install: + $(INSTALL) -d $(INSTALL_INCLUDE_DIR) + $(INSTALL) -d $(INSTALL_LIB_DIR) + $(INSTALL) -C -m 644 untrusted/worker.h $(INSTALL_INCLUDE_DIR) + $(INSTALL) -C -m 664 *.signed.so $(INSTALL_LIB_DIR) + $(INSTALL) -C -m 644 *.a $(INSTALL_LIB_DIR) + +clean: + $(MAKE) -ef sgx_u.mk clean + $(MAKE) -ef sgx_t.mk clean + +mrproper: clean + rm -rf ./install diff --git a/sdk/example_containers/sgx-task/worker/sgx_t.mk b/sdk/example_containers/sgx-task/worker/sgx_t.mk new file mode 100644 index 0000000..10dca5e --- /dev/null +++ b/sdk/example_containers/sgx-task/worker/sgx_t.mk @@ -0,0 +1,153 @@ +### Project Settings ### +PROJECT_ROOT_DIR ?= $(shell readlink -f ..) + +### Intel(R) SGX SDK Settings ### +SGX_SDK ?= /opt/intel/sgxsdk +SGXSSL_DIR ?= /opt/intel/sgxssl +SGX_MODE ?= HW +SGX_ARCH ?= x64 +SGX_DEBUG ?= 1 +SGX_PRERELEASE ?= 0 +ifeq ($(shell getconf LONG_BIT), 32) + SGX_ARCH := x86 +else ifeq ($(findstring -m32, $(CXXFLAGS)), -m32) + SGX_ARCH := x86 +endif + +ifeq ($(SGX_ARCH), x86) + SGX_COMMON_CFLAGS := -m32 + SGX_LIBRARY_PATH := $(SGX_SDK)/lib + SGX_ENCLAVE_SIGNER := $(SGX_SDK)/bin/x86/sgx_sign + SGX_EDGER8R := $(SGX_SDK)/bin/x86/sgx_edger8r +else + SGX_COMMON_CFLAGS := -m64 + SGX_LIBRARY_PATH := $(SGX_SDK)/lib64 + SGX_ENCLAVE_SIGNER := $(SGX_SDK)/bin/x64/sgx_sign + SGX_EDGER8R := $(SGX_SDK)/bin/x64/sgx_edger8r +endif + +ifeq ($(SGX_DEBUG), 1) +ifeq ($(SGX_PRERELEASE), 1) +$(error Cannot set SGX_DEBUG and SGX_PRERELEASE at the same time!!) +endif +endif + +ifeq ($(SGX_DEBUG), 1) + SGX_COMMON_CFLAGS += -O0 -g -DSGX_DEBUG +else + SGX_COMMON_CFLAGS += -O2 +endif + +ifneq ($(SGX_MODE), HW) + Trts_Library_Name := sgx_trts_sim + Service_Library_Name := sgx_tservice_sim +else + Trts_Library_Name := sgx_trts + Service_Library_Name := sgx_tservice +endif + +ifeq ($(SGX_MODE), HW) +ifneq ($(SGX_DEBUG), 1) +ifneq ($(SGX_PRERELEASE), 1) +Build_Mode = HW_RELEASE +endif +endif +endif + +SGX_COMMON_FLAGS += -Wall -Wextra -Wchar-subscripts -Wno-coverage-mismatch \ + -Winit-self -Wpointer-arith -Wreturn-type \ + -Waddress -Wsequence-point -Wformat-security \ + -Wmissing-include-dirs -Wfloat-equal -Wundef -Wshadow \ + -Wcast-align -Wcast-qual -Wconversion -Wredundant-decls + +DCAP_TVL_LIB = sgx_dcap_tvl +### Intel(R) SGX SDK Settings ### + +### Project Settings ### +SGX_Include_Paths := -I$(SGX_SDK)/include -I$(SGX_SDK)/include/tlibc \ + -I$(SGX_SDK)/include/libcxx -I$(SGXSSL_DIR)/include + +Flags_Just_For_C := -Wno-implicit-function-declaration -std=c11 +Flags_Just_For_Cpp := -Wnon-virtual-dtor -std=c++11 -nostdinc++ +Common_C_Cpp_Flags := $(SGX_COMMON_CFLAGS) $(SGX_COMMON_FLAGS) -nostdinc -fvisibility=hidden -fpie -fstack-protector -fno-builtin -fno-builtin-printf -I. +Common_C_Flags := -Wjump-misses-init -Wstrict-prototypes \ + -Wunsuffixed-float-constants + +Enclave_C_Flags := $(Flags_Just_For_C) $(Common_C_Cpp_Flags) $(Common_C_Flags) -Itrusted $(SGX_Include_Paths) -I$(PROJECT_ROOT_DIR) + +Enclave_Cpp_Flags := $(Flags_Just_For_Cpp) $(Common_C_Cpp_Flags) -DSGXCLIENT -Itrusted $(SGX_Include_Paths) -I$(PROJECT_ROOT_DIR) + +Crypto_Library_Name := sgx_tcrypto +SGXSSL_Link_Flags := -L$(SGXSSL_DIR)/lib64 -Wl,--whole-archive -lsgx_tsgxssl -Wl,--no-whole-archive -lsgx_tsgxssl_crypto -lsgx_pthread + +Enclave_Link_Flags := $(SGX_COMMON_CFLAGS) \ + -Wl,--no-undefined -nostdlib -nodefaultlibs -nostartfiles \ + -L$(SGX_LIBRARY_PATH) \ + -Wl,--whole-archive -l$(DCAP_TVL_LIB) -l$(Trts_Library_Name) -Wl,--no-whole-archive \ + $(SGXSSL_Link_Flags) \ + -Wl,--start-group -lsgx_tstdc -lsgx_tcxx -l$(Crypto_Library_Name) \ + -l$(Service_Library_Name) -Wl,--end-group \ + -Wl,-Bstatic -Wl,-Bsymbolic \ + -Wl,-pie,-eenclave_entry -Wl,--export-dynamic \ + -Wl,--defsym,__ImageBase=0 \ + -Wl,--version-script=trusted/Worker_Enclave.lds + +### Project Settings ### + +### Phony targets ### +.PHONY: all clean + +### Build all ### +ifeq ($(Build_Mode), HW_RELEASE) +all: Worker_Enclave.so + @echo "Build enclave Server_Enclave.so [$(Build_Mode)|$(SGX_ARCH)] success!" + @echo + @echo "*********************************************************************************************************************************************************" + @echo "PLEASE NOTE: In this mode, please sign the Server_Enclave.so first using Two Step Sign mechanism before you run the app to launch and access the enclave." + @echo "*********************************************************************************************************************************************************" + @echo +else +all: Worker_Enclave.signed.so +endif + +### Edger8r related sourcs ### +trusted/Enclave_t.c: $(SGX_EDGER8R) ./trusted/Enclave.edl + @echo Entering ./trusted + cd ./trusted && $(SGX_EDGER8R) --trusted ../trusted/Enclave.edl --search-path ../trusted --search-path $(SGX_SDK)/include --search-path $(SGXSSL_DIR)/include --search-path $(PROJECT_ROOT_DIR) --search-path $(SGX_RA_TLS_DIR)/include $(Enclave_Search_Dirs) + @echo "GEN => $@" + +trusted/Enclave_t.o: ./trusted/Enclave_t.c + $(CC) $(Enclave_C_Flags) -c $< -o $@ + @echo "CC <= $<" +### Edger8r related sourcs ### + +## build files needed from other directory +trusted/json.o: $(PROJECT_ROOT_DIR)/common/json.cc + $(CXX) $(Enclave_Cpp_Flags) -c $< -o $@ + @echo "CXX <= $<" + +trusted/tcrypto_ext.o: $(PROJECT_ROOT_DIR)/common/tcrypto_ext.cc + $(CXX) $(Enclave_Cpp_Flags) -c $< -o $@ + @echo "CXX <= $<" + +## build files needed from other directory +trusted/Worker_Enclave.o: trusted/Worker_Enclave.cc + $(CXX) $(Enclave_Cpp_Flags) -c $< -o $@ + @echo "CXX <= $<" + +### Enclave Image ### +Enclave_Cpp_Objects := trusted/json.o trusted/tcrypto_ext.o trusted/Worker_Enclave.o + +Worker_Enclave.so: trusted/Enclave_t.o $(Enclave_Cpp_Objects) + $(CXX) $^ -o $@ $(Enclave_Link_Flags) + @echo "LINK => $@" + +### Signing ### +Worker_Enclave.signed.so: Worker_Enclave.so + $(SGX_ENCLAVE_SIGNER) sign -key trusted/Worker_Enclave_private.pem -enclave Worker_Enclave.so -out $@ -config trusted/Worker_Enclave.config.xml + @echo "SIGN => $@" +### Sources ### + +### Clean command ### +clean: + rm -f Worker_Enclave.* trusted/Enclave_t.* $(Enclave_Cpp_Objects) diff --git a/sdk/example_containers/sgx-task/worker/sgx_u.mk b/sdk/example_containers/sgx-task/worker/sgx_u.mk new file mode 100644 index 0000000..2f148e8 --- /dev/null +++ b/sdk/example_containers/sgx-task/worker/sgx_u.mk @@ -0,0 +1,159 @@ +### Project Settings ### +PROJECT_ROOT_DIR ?= $(shell readlink -f ..) + +### Intel(R) SGX SDK Settings ### +SGX_SDK ?= /opt/intel/sgxsdk +SGXSSL_DIR ?= /opt/intel/sgxssl +SGX_MODE ?= HW +SGX_ARCH ?= x64 +SGX_DEBUG ?= 1 +SGX_PRERELEASE ?= 0 +ifeq ($(shell getconf LONG_BIT), 32) + SGX_ARCH := x86 +else ifeq ($(findstring -m32, $(CXXFLAGS)), -m32) + SGX_ARCH := x86 +endif + +ifeq ($(SGX_ARCH), x86) + SGX_COMMON_CFLAGS := -m32 + SGX_LIBRARY_PATH := $(SGX_SDK)/lib + SGX_ENCLAVE_SIGNER := $(SGX_SDK)/bin/x86/sgx_sign + SGX_EDGER8R := $(SGX_SDK)/bin/x86/sgx_edger8r +else + SGX_COMMON_CFLAGS := -m64 + SGX_LIBRARY_PATH := $(SGX_SDK)/lib64 + SGX_ENCLAVE_SIGNER := $(SGX_SDK)/bin/x64/sgx_sign + SGX_EDGER8R := $(SGX_SDK)/bin/x64/sgx_edger8r +endif + +ifeq ($(SGX_DEBUG), 1) +ifeq ($(SGX_PRERELEASE), 1) +$(error Cannot set SGX_DEBUG and SGX_PRERELEASE at the same time!!) +endif +endif + +ifeq ($(SGX_DEBUG), 1) + SGX_COMMON_CFLAGS += -O0 -g -DSGX_DEBUG +else + SGX_COMMON_CFLAGS += -O2 +endif + +ifneq ($(SGX_MODE), HW) + Urts_Library_Name := sgx_urts_sim +else + Urts_Library_Name := sgx_urts +endif + +ifeq ($(SGX_MODE), HW) +ifneq ($(SGX_DEBUG), 1) +ifneq ($(SGX_PRERELEASE), 1) +Build_Mode = HW_RELEASE +endif +endif +endif + +APP_DCAP_LIBS := -lsgx_dcap_ql -lsgx_dcap_quoteverify +### Intel(R) SGX SDK Settings ### + +### Project Settings ### +Common_C_Cpp_Flags := $(SGX_COMMON_CFLAGS) -fPIC -Wno-attributes -I. +Common_C_Cpp_Flags += -Wall -Wextra -Winit-self -Wpointer-arith -Wreturn-type \ + -Waddress -Wsequence-point -Wformat-security \ + -Wmissing-include-dirs -Wfloat-equal -Wundef -Wshadow \ + -Wcast-align -Wcast-qual -Wconversion -Wredundant-decls +Common_C_Flags := -Wjump-misses-init -Wstrict-prototypes \ + -Wunsuffixed-float-constants + +# Three configuration modes - Debug, prerelease, release +# Debug - Macro DEBUG enabled. +# Prerelease - Macro NDEBUG and EDEBUG enabled. +# Release - Macro NDEBUG enabled. +ifeq ($(SGX_DEBUG), 1) + Common_C_Cpp_Flags += -DDEBUG -UNDEBUG -UEDEBUG +else ifeq ($(SGX_PRERELEASE), 1) + Common_C_Cpp_Flags += -DNDEBUG -DEDEBUG -UDEBUG +else + Common_C_Cpp_Flags += -DNDEBUG -UEDEBUG -UDEBUG +endif + +App_C_Cpp_Flags := $(Common_C_Cpp_Flags) -Iuntrusted -I$(SGX_SDK)/include -I$(PROJECT_ROOT_DIR) + +### Project Settings ### + +### Linking setting ### +App_Link_Flags := -L$(SGX_LIBRARY_PATH) -l$(Urts_Library_Name) $(APP_DCAP_LIBS) \ + -lpthread -lz -lm -lcrypto + +## Add sgx_uae_service library to link ## +ifneq ($(SGX_MODE), HW) + App_Link_Flags += -lsgx_uae_service_sim +else + App_Link_Flags += -lsgx_uae_service +endif + +## Add sgx ssl library +App_Link_Flags += -L$(SGXSSL_DIR)/lib64 -lsgx_usgxssl +### Linking setting ### + +### Phony targets ### +.PHONY: all clean + +### Build all ### +ifeq ($(Build_Mode), HW_RELEASE) +all: App + @echo "Build App [$(Build_Mode)|$(SGX_ARCH)] success!" + @echo + @echo "*********************************************************************************************************************************************************" + @echo "PLEASE NOTE: In this mode, please sign the Worker_Enclave.so first using Two Step Sign mechanism before you run the app to launch and access the enclave." + @echo "*********************************************************************************************************************************************************" + @echo + +else +all: App App2 +endif + +### Sources ### +## Edger8r related sources ## +untrusted/Enclave_u.c: $(SGX_EDGER8R) trusted/Enclave.edl + @echo Entering ./untrusted + cd ./untrusted && $(SGX_EDGER8R) --untrusted ../trusted/Enclave.edl --search-path ../trusted --search-path $(SGX_SDK)/include --search-path $(SGXSSL_DIR)/include --search-path $(PROJECT_ROOT_DIR) $(Enclave_Search_Dirs) + @echo "GEN => $@" + +untrusted/Enclave_u.o: untrusted/Enclave_u.c + $(CC) $(Common_C_Flags) $(App_C_Cpp_Flags) -c $< -o $@ + @echo "CC <= $<" +## Edger8r related sources ## + +## build files needed from other directory +untrusted/hexutil.o: $(PROJECT_ROOT_DIR)/common/hexutil.cc + $(CXX) $(App_C_Cpp_Flags) -c $< -o $@ + @echo "CXX <= $<" + +untrusted/json.o: $(PROJECT_ROOT_DIR)/common/json.cc + $(CXX) $(App_C_Cpp_Flags) -c $< -o $@ + @echo "CXX <= $<" + +untrusted/%.o: untrusted/%.cc + $(CXX) $(App_C_Cpp_Flags) -c $< -o $@ + @echo "CXX <= $<" + +App_Cpp_Objects := untrusted/worker.o untrusted/json.o untrusted/hexutil.o \ + untrusted/ocall_patches.o + +libworker.a: untrusted/Enclave_u.o $(App_Cpp_Objects) + ar -rcs $@ $^ + @echo "LINK => $@" + +## Build worker app ## +App: untrusted/App.o libworker.a + $(CXX) $< -o $@ -L. -l:libworker.a $(App_Link_Flags) + @echo "LINK => $@" + +App2: untrusted/App2.o libworker.a + $(CXX) $< -o $@ -L. -l:libworker.a $(App_Link_Flags) + @echo "LINK => $@" +### Sources ### + +### Clean command ### +clean: + rm -f App2 App untrusted/App2.o untrusted/App.o $(App_Cpp_Objects) untrusted/Enclave_u.* libworker.a diff --git a/sdk/example_containers/sgx-task/worker/trusted/Enclave.edl b/sdk/example_containers/sgx-task/worker/trusted/Enclave.edl new file mode 100644 index 0000000..386d68e --- /dev/null +++ b/sdk/example_containers/sgx-task/worker/trusted/Enclave.edl @@ -0,0 +1,33 @@ +enclave { + include "stdint.h" + + from "sgx_tstdc.edl" import *; // needed for mutex and lock_guard use + from "sgx_tsgxssl.edl" import *; // needed for sgxssl + from "sgx_pthread.edl" import*; // needed for sgxssl + + include "sgx_report.h" + include "sgx_ql_lib_common.h" + + trusted { + public sgx_status_t enc_get_key_and_report( + [in] const sgx_target_info_t* qe_target_info, + [out] sgx_report_t* app_report, + [out, size=key_size] uint8_t* key, size_t key_size); + public sgx_status_t enc_run( + [in, size=input_data_size] const char* input_data, size_t input_data_size, + [out] size_t* output_size); + public sgx_status_t enc_get_output( + [out, size=prediction_size] uint8_t* prediction, + size_t prediction_size); + public void enc_clear_exec_context(); + }; + + untrusted { +#ifndef NDEBUG + void ocall_debug_print([in, size=len] const void* s, size_t len); + void ocall_debug_print_string([in, string] const char* str); + void ocall_debug_print_hex([in, size=len] const void* str, size_t len); + void ocall_debug_print_hexstring([in, string] const char* str); +#endif // NDEBUG + }; +}; diff --git a/sdk/example_containers/sgx-task/worker/trusted/Worker_Enclave.cc b/sdk/example_containers/sgx-task/worker/trusted/Worker_Enclave.cc new file mode 100644 index 0000000..1d42e0b --- /dev/null +++ b/sdk/example_containers/sgx-task/worker/trusted/Worker_Enclave.cc @@ -0,0 +1,222 @@ +#include "Enclave_t.h" + +#include "sgx_trts.h" + +#include +#include +#include +#include + +#include "common/tcrypto_ext.h" + +#ifndef NDEBUG +#include +#endif // NDEBUG + +#include "sgx_quote_3.h" +#include "sgx_utils.h" +#include "sgx_ql_lib_common.h" + +// for debug +#include "openssl/ec.h" +#include "openssl/evp.h" +#include "openssl/pem.h" +#include "openssl/rsa.h" + +#define OUTPUT_BUF_SZ 4000 + +namespace { + +// global variable of private key hold by the enclave +uint8_t* enclave_public_key = nullptr; +size_t enclave_public_key_size = 0; +uint8_t* enclave_private_key = nullptr; +size_t enclave_private_key_size = 0; + +uint8_t enc_iv_buf[AES_GCM_IV_SIZE] = {0}; + +// --- execution context --- // +// the public key of the enclave +thread_local char output_buf[OUTPUT_BUF_SZ]; +thread_local size_t actual_output_size; + +// invoked after completing an request no matter success/failure +void ClearRequestContext() { + memset(output_buf, 0, OUTPUT_BUF_SZ); +} +// invoke upon failure and final tear down. +void ClearExecutionContext() { + ClearRequestContext(); +} + +inline void printf(const char* msg) { + ocall_debug_print_string(msg); +} + +// TODO +std::string decrypt_content(const char* input_data, size_t input_data_size, std::string* output_key) { + assert(input_data_size >= MAX_SESSION_KEY_IV_LENGTH + ENCRYPTED_SESSION_KEY_LENGTH); + const char* iv = input_data + (input_data_size - MAX_SESSION_KEY_IV_LENGTH); + const char* ek = input_data + (input_data_size - MAX_SESSION_KEY_IV_LENGTH - ENCRYPTED_SESSION_KEY_LENGTH); + int encrypted_input_len = input_data_size - (MAX_SESSION_KEY_IV_LENGTH + ENCRYPTED_SESSION_KEY_LENGTH); + + // perform decryption + int decrypted_len; + unsigned char* decrypted = rsa_decrypt_data((const unsigned char*) input_data, encrypted_input_len, (const unsigned char*) ek, ENCRYPTED_SESSION_KEY_LENGTH, (const unsigned char*) iv, enclave_private_key, enclave_private_key_size, &decrypted_len); + output_key->assign((char*)decrypted + decrypted_len-OUTPUT_KEY_LENGTH, OUTPUT_KEY_LENGTH); + std::string ret{(char*)decrypted, decrypted_len-OUTPUT_KEY_LENGTH}; + + if (decrypted) free(decrypted); + return ret; +} + +std::string exec(const char* input, size_t input_len) { + return "hello " + std::string(input, input_len); +} + +// TODO +sgx_status_t encrypt_output(const std::string& output_plain, const std::string& output_key, std::string* encrypt_output) { + EVP_CIPHER_CTX *ctx; + if(!(ctx = EVP_CIPHER_CTX_new())) return SGX_ERROR_UNEXPECTED; + + if(EVP_EncryptInit_ex(ctx, EVP_aes_128_gcm(), NULL, (const unsigned char*)output_key.data(), enc_iv_buf) != 1) { + // if(EVP_EncryptInit_ex(ctx, EVP_aes_256_cbc(), NULL, key, iv) != 1) { + EVP_CIPHER_CTX_free(ctx); + return SGX_ERROR_UNEXPECTED; + } + + unsigned char ciphertext[output_plain.size()]; + + int ciphertext_len; + int len; + if(EVP_EncryptUpdate(ctx, ciphertext, &len, (const unsigned char*) output_plain.data(), output_plain.size())!=1) { + EVP_CIPHER_CTX_free(ctx); + return SGX_ERROR_UNEXPECTED; + } + ciphertext_len = len; + + if(EVP_EncryptFinal_ex(ctx, ciphertext + len, &len)!=1) { + EVP_CIPHER_CTX_free(ctx); + return SGX_ERROR_UNEXPECTED; + } + ciphertext_len += len; + + unsigned char tag[AES_GCM_TAG_SIZE] = {0,}; + // tag needed for gcm, not for cbc + EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_GET_TAG, 16, tag); + EVP_CIPHER_CTX_free(ctx); + encrypt_output->assign((char*)ciphertext, ciphertext_len); + encrypt_output->append((char*)enc_iv_buf, AES_GCM_IV_SIZE); + encrypt_output->append((char*)tag, AES_GCM_TAG_SIZE); + + // ocall_debug_print_hex((const uint8_t*)encrypt_output->data(), encrypt_output->size()); + return SGX_SUCCESS; +} +} // anonymous namespace + +sgx_status_t enc_get_key_and_report(const sgx_target_info_t* qe_target_info, + sgx_report_t* app_report, uint8_t* key, size_t key_size) { + // prepare in enclave key + uint8_t* public_key_buffer = nullptr; + size_t public_key_size = 0; + uint8_t* private_key_buffer = nullptr; + size_t private_key_size = 0; + sgx_status_t status = generate_key_pair(RSA_TYPE, &public_key_buffer, &public_key_size, &private_key_buffer, &private_key_size); + if (status != SGX_SUCCESS) { + if (private_key_buffer) free(private_key_buffer); + if (public_key_buffer) free(public_key_buffer); + return status; + } + + // ocall_debug_print(public_key_buffer, public_key_size); + // ocall_debug_print(private_key_buffer, private_key_size); + + // copy the private key to enclave global variable + enclave_public_key = public_key_buffer; + enclave_public_key_size = public_key_size; + enclave_private_key = private_key_buffer; + enclave_private_key_size = private_key_size; + + // obtain the hash to include in report + sgx_sha_state_handle_t sha_handle = nullptr; + status = sgx_sha256_init(&sha_handle); + if (status != SGX_SUCCESS) { + return status; + } + + status = sgx_sha256_update(enclave_public_key, (uint32_t)public_key_size, sha_handle); + if (status != SGX_SUCCESS) { + return status; + } + + sgx_sha256_hash_t hash = {0}; + status = sgx_sha256_get_hash(sha_handle, &hash); + if (status != SGX_SUCCESS) { + return status; + } + + sgx_report_data_t report_data = {0}; + memcpy(report_data.d, hash, sizeof(hash)); + + sgx_sha256_close(sha_handle); + + status = sgx_create_report(qe_target_info, &report_data, app_report); + if (key_size == enclave_public_key_size) { + memcpy(key, enclave_public_key, enclave_public_key_size); + // ocall_debug_print_hex(enclave_private_key, enclave_public_key_size); + } else { + std::string msg = "key buffer is not large enough: need" + std::to_string(enclave_public_key_size) + "bytes, but only" + std::to_string(key_size) + "bytes provided."; + ocall_debug_print_string(msg.c_str()); + return SGX_ERROR_INVALID_PARAMETER; + } + + return status; +} + +sgx_status_t enc_run(const char* input_data, size_t input_data_size, + size_t* output_size) { + + // allocate buffer to host decrypted contents + std::string output_key; + // user input decryption + auto decrypt_input = decrypt_content(input_data, input_data_size, &output_key); + + sgx_status_t ret = SGX_SUCCESS; + + // All ready, execute the task on input + + std::string output_plain = exec(decrypt_input.data(), decrypt_input.size()); + + // encrypt the output + std::string output; + sgx_status_t status = encrypt_output(output_plain, output_key, &output); + if (status == SGX_SUCCESS) memcpy(output_buf, output.data(), output.size()); + + // set output size for untrusted memory allocation + actual_output_size = (ret == SGX_SUCCESS) ? output.size() : 0; + *output_size = actual_output_size; + +#ifndef NDEBUG + // ocall_debug_print_hex(output_buf, output_size); +#endif // NDEBUG + + // free resources + return ret; +} + +sgx_status_t enc_get_output(uint8_t* ret, + size_t size) { + // check if enough space in untrusted memory + if ((sgx_is_outside_enclave(ret, size) != 1) + && (size < actual_output_size)) return SGX_ERROR_UNEXPECTED; + + + memcpy(ret, output_buf, actual_output_size); + // clear execution context + ClearRequestContext(); + return SGX_SUCCESS; +} + +void enc_clear_exec_context() { + ClearExecutionContext(); +} diff --git a/sdk/example_containers/sgx-task/worker/trusted/Worker_Enclave.config.xml b/sdk/example_containers/sgx-task/worker/trusted/Worker_Enclave.config.xml new file mode 100644 index 0000000..cdba1d4 --- /dev/null +++ b/sdk/example_containers/sgx-task/worker/trusted/Worker_Enclave.config.xml @@ -0,0 +1,10 @@ + + 0 + 0 + 0x120000 + 0x10000000 + 0x10000000 + 8 + 0 + 0 + diff --git a/sdk/example_containers/sgx-task/worker/trusted/Worker_Enclave.lds b/sdk/example_containers/sgx-task/worker/trusted/Worker_Enclave.lds new file mode 100644 index 0000000..b42342f --- /dev/null +++ b/sdk/example_containers/sgx-task/worker/trusted/Worker_Enclave.lds @@ -0,0 +1,10 @@ +Worker_Enclave.so +{ + global: + g_global_data_sim; + g_global_data; + g_peak_heap_used; + g_peak_rsrv_mem_committed; + local: + *; +}; \ No newline at end of file diff --git a/sdk/example_containers/sgx-task/worker/trusted/Worker_Enclave_private.pem b/sdk/example_containers/sgx-task/worker/trusted/Worker_Enclave_private.pem new file mode 100644 index 0000000..529d07b --- /dev/null +++ b/sdk/example_containers/sgx-task/worker/trusted/Worker_Enclave_private.pem @@ -0,0 +1,39 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIG4gIBAAKCAYEAroOogvsj/fZDZY8XFdkl6dJmky0lRvnWMmpeH41Bla6U1qLZ +AmZuyIF+mQC/cgojIsrBMzBxb1kKqzATF4+XwPwgKz7fmiddmHyYz2WDJfAjIveJ +ZjdMjM4+EytGlkkJ52T8V8ds0/L2qKexJ+NBLxkeQLfV8n1mIk7zX7jguwbCG1Pr +nEMdJ3Sew20vnje+RsngAzdPChoJpVsWi/K7cettX/tbnre1DL02GXc5qJoQYk7b +3zkmhz31TgFrd9VVtmUGyFXAysuSAb3EN+5VnHGr0xKkeg8utErea2FNtNIgua8H +ONfm9Eiyaav1SVKzPHlyqLtcdxH3I8Wg7yqMsaprZ1n5A1v/levxnL8+It02KseD +5HqV4rf/cImSlCt3lpRg8U5E1pyFQ2IVEC/XTDMiI3c+AR+w2jSRB3Bwn9zJtFlW +KHG3m1xGI4ck+Lci1JvWWLXQagQSPtZTsubxTQNx1gsgZhgv1JHVZMdbVlAbbRMC +1nSuJNl7KPAS/VfzAgEDAoIBgHRXxaynbVP5gkO0ug6Qw/E27wzIw4SmjsxG6Wpe +K7kfDeRskKxESdsA/xCrKkwGwhcx1iIgS5+Qscd1Yg+1D9X9asd/P7waPmWoZd+Z +AhlKwhdPsO7PiF3e1AzHhGQwsUTt/Y/aSI1MpHBvy2/s1h9mFCslOUxTmWw0oj/Q +ldIEgWeNR72CE2+jFIJIyml6ftnb6qzPiga8Bm48ubKh0kvySOqnkmnPzgh+JBD6 +JnBmtZbfPT97bwTT+N6rnPqOOApvfHPf15kWI8yDbprG1l4OCUaIUH1AszxLd826 +5IPM+8gINLRDP1MA6azECPjTyHXhtnSIBZCyWSVkc05vYmNXYUNiXWMajcxW9M02 +wKzFELO8NCEAkaTPxwo4SCyIjUxiK1LbQ9h8PSy4c1+gGP4LAMR8xqP4QKg6zdu9 +osUGG/xRe/uufgTBFkcjqBHtK5L5VI0jeNIUAgW/6iNbYXjBMJ0GfauLs+g1VsOm +WfdgXzsb9DYdMa0OXXHypmV4GwKBwQDUwQj8RKJ6c8cT4vcWCoJvJF00+RFL+P3i +Gx2DLERxRrDa8AVGfqaCjsR+3vLgG8V/py+z+dxZYSqeB80Qeo6PDITcRKoeAYh9 +xlT3LJOS+k1cJcEmlbbO2IjLkTmzSwa80fWexKu8/Xv6vv15gpqYl1ngYoqJM3pd +vzmTIOi7MKSZ0WmEQavrZj8zK4endE3v0eAEeQ55j1GImbypSf7Idh7wOXtjZ7WD +Dg6yWDrri+AP/L3gClMj8wsAxMV4ZR8CgcEA0fzDHkFa6raVOxWnObmRoDhAtE0a +cjUj976NM5yyfdf2MrKy4/RhdTiPZ6b08/lBC/+xRfV3xKVGzacm6QjqjZrUpgHC +0LKiZaMtccCJjLtPwQd0jGQEnKfMFaPsnhOc5y8qVkCzVOSthY5qhz0XNotHHFmJ +gffVgB0iqrMTvSL7IA2yqqpOqNRlhaYhNl8TiFP3gIeMtVa9rZy31JPgT2uJ+kfo +gV7sdTPEjPWZd7OshGxWpT6QfVDj/T9T7L6tAoHBAI3WBf2DFvxNL2KXT2QHAZ9t +k3imC4f7U+wSE6zILaDZyzygA4RUbwG0gv8/TJVn2P/Eynf76DuWHGlaiLWnCbSz +Az2DHBQBBaku409zDQym3j1ugMRjzzSQWzJg0SIyBH3hTmnYcn3+Uqcp/lEBvGW6 +O+rsXFt3pukqJmIV8HzLGGaLm62BHUeZf3dyWm+i3p/hQAL7Xvu04QW70xuGqdr5 +afV7p5eaeQIJXyGQJ0eylV/90+qxjMKiB1XYg6WYvwKBwQCL/ddpgOdHJGN8uRom +e7Zq0Csi3hGheMKlKbN3vcxT5U7MdyHtTZZOJbTvxKNNUNYH/8uD+PqDGNneb29G +BfGzvI3EASyLIcGZF3OhKwZd0jUrWk2y7Vhob91jwp2+t73vdMbkKyI4mHOuXvGv +fg95si9oO7EBT+Oqvhccd2J+F1IVXncccYnF4u5ZGWt5lLewN/pVr7MjjykeaHqN +t+rfnQam2psA6fL4zS2zTmZPzR2tnY8Y1GBTi0Ko1OKd1HMCgcAb5cB/7/AQlhP9 +yQa04PLH9ygQkKKptZp7dy5WcWRx0K/hAHRoi2aw1wZqfm7VBNu2SLcs90kCCCxp +6C5sfJi6b8NpNbIPC+sc9wsFr7pGo9SFzQ78UlcWYK2Gu2FxlMjonhka5hvo4zvg +WxlpXKEkaFt3gLd92m/dMqBrHfafH7VwOJY2zT3WIpjwuk0ZzmRg5p0pG/svVQEH +NZmwRwlopysbR69B/n1nefJ84UO50fLh5s5Zr3gBRwbWNZyzhXk= +-----END RSA PRIVATE KEY----- diff --git a/sdk/example_containers/sgx-task/worker/untrusted/App.cc b/sdk/example_containers/sgx-task/worker/untrusted/App.cc new file mode 100644 index 0000000..11dd640 --- /dev/null +++ b/sdk/example_containers/sgx-task/worker/untrusted/App.cc @@ -0,0 +1,213 @@ +#define WORKER_ENCLAVE_FILENAME "Worker_Enclave.signed.so" +#define REQUEST_COUNT 4 + +#include +#include + +#include "worker.h" +#include "common/hexutil.h" + +// for test +#include +#include +#include +#include + +#include +#include +#include + + +#define MAX_SESSION_KEY_IV_LENGTH 16 + + +/* A 128 bit key */ +unsigned char output_key[16] = {0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, + 0x38, 0x39, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35}; + +// /* A 96 bit IV */ +// unsigned char iv[12] = {0x99, 0xaa, 0x3e, 0x68, 0xed, 0x81, 0x73, 0xa0, +// 0xee, 0xd0, 0x66, 0x84}; + +// expect the input should be in the form of |encrytped secret data|encrypted session key(384B)|IV(16B)| +// the secret data should consists of |input|output key(16B)| +std::string encrypt_request(const std::string& data, const std::string& enclave_public_key) { + std::string plaintext{data}; + plaintext.append((char*)output_key, 16); + int encrypted_message_len = 0; + unsigned char* ek = NULL; + int ek_len = 0; + unsigned char iv[MAX_SESSION_KEY_IV_LENGTH]; + memset(iv, 0, MAX_SESSION_KEY_IV_LENGTH); + size_t encrypted_message_max_length = plaintext.size()+EVP_MAX_IV_LENGTH; + unsigned char encrypted[encrypted_message_max_length]; + memset(encrypted, 0, encrypted_message_max_length); + + BIO* bio = NULL; + bio = BIO_new_mem_buf(enclave_public_key.data(), enclave_public_key.size()); + RSA* rsaPublicKey = NULL; + PEM_read_bio_RSA_PUBKEY(bio, &rsaPublicKey, NULL, NULL); + EVP_PKEY *pkey = EVP_PKEY_new(); + EVP_PKEY_assign_RSA(pkey, rsaPublicKey); + EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new(); + EVP_CIPHER_CTX_init(ctx); + ek = (unsigned char*) malloc(EVP_PKEY_size(pkey)); + EVP_SealInit(ctx, EVP_aes_256_cbc(), &ek, &ek_len, iv, &pkey, 1); + printf("encrypted session key length: %d\n", ek_len); + + // BIO_dump_fp(stdout, (const char*) plaintext.data(), plaintext.size()); + // BIO_dump_fp(stdout, (const char*) ek, ek_len); + + int encrypted_block_len = 0; + EVP_SealUpdate(ctx, encrypted, &encrypted_block_len, (const unsigned char*) plaintext.data(), plaintext.size()); + encrypted_message_len = encrypted_block_len; + EVP_SealFinal(ctx, encrypted + encrypted_block_len, &encrypted_block_len); + encrypted_message_len += encrypted_block_len; + EVP_CIPHER_CTX_free(ctx); + BIO_free(bio); + EVP_PKEY_free(pkey); + + std::string ret = std::string((char*)encrypted, encrypted_message_len); + ret.append((char*)ek, ek_len); + ret.append((char*)iv, MAX_SESSION_KEY_IV_LENGTH); + + if (ek) free(ek); + + return ret; +} + +std::string decrypt_response(const std::string& data) { + uint8_t tag[16]; + memcpy(tag, data.data()+data.size()-16, 16); + uint8_t iv[12]; + memcpy(iv, data.data()+data.size()-28, 12); + size_t encrypt_len = data.size()-28; + EVP_CIPHER_CTX *ctx; + /* Create and initialise the context */ + if(!(ctx = EVP_CIPHER_CTX_new())) return ""; + + if(EVP_DecryptInit_ex(ctx, EVP_aes_128_gcm(), NULL, output_key, iv)!=1) { + EVP_CIPHER_CTX_free(ctx); + return ""; + } + + // // BIO_dump_fp(stdout, (const char*) data.data(), data.size()-32); + // printf("encrypted data (%dB): \n", encrypt_len); + // BIO_dump_fp(stdout, (const char*) data.data(), encrypt_len); + // printf("tag: \n"); + // BIO_dump_fp(stdout, (const char*) tag, 16); + + EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_TAG, 16, tag); + + uint8_t plaintext[encrypt_len]; + int plaintext_len; + int len; + if(EVP_DecryptUpdate(ctx, plaintext, &len, (const unsigned char*)data.data(), encrypt_len)!=1) { + EVP_CIPHER_CTX_free(ctx); + return ""; + } + plaintext_len = len; + // printf("so far plaintext len: %d\n", plaintext_len); + + if (EVP_DecryptFinal_ex(ctx, plaintext + len, &len) !=1) { + EVP_CIPHER_CTX_free(ctx); + return ""; + } + plaintext_len += len; + // printf("plaintext (%dB):\n", plaintext_len); + // std::string ret; + // ret.assign((char*)plaintext, plaintext_len); + // printf("%s\n", ret.c_str()); + + EVP_CIPHER_CTX_free(ctx); + return std::string((char*)plaintext, plaintext_len); +} + +int main(int argc, char* argv[]) { + std::cout << "Program starts at: " + << std::chrono::system_clock::now().time_since_epoch() + / std::chrono::microseconds(1) << "\n"; + + std::string sample_request_path; + std::string sample_request; + + if (argc == 2) { + sample_request = argv[1]; + printf("sample request: %s\n", sample_request.c_str()); + } else { + fprintf(stderr, "Usage: sample_request\n"); + exit(1); + } + +// test enclave recreate +for (int j = 0; j < 2; j++) { + printf("Starting a worker\n"); + Worker worker(WORKER_ENCLAVE_FILENAME); + auto ret = worker.Initialize(); + if (!ret) { + printf("failed to initialize worker\n"); + return 1; + } + printf("Worker initialized\n"); + std::cout << "worker init done at: " + << std::chrono::system_clock::now().time_since_epoch() + / std::chrono::microseconds(1) << "\n"; + + std::string key; + std::string report = worker.GetKeyAndReport(&key); + std::cout << "report: " << report << "\n"; + std::cout << "key: " << key << "\n"; + + printf("check input: %s, %ld\n", sample_request.c_str(), sample_request.size()); + // prepare the sample request + std::string prepared_sample = encrypt_request(sample_request, key); + + // parallel + std::vector handlers; + std::array outputs; + for (uint64_t i = 0; i < REQUEST_COUNT; i ++) { + handlers.emplace_back(&Worker::Handle, &worker, i, prepared_sample, + &outputs[i]); + } + for (auto& h : handlers) { + h.join(); + } + + std::cout << "processing " << REQUEST_COUNT << " requests done at: " + << std::chrono::system_clock::now().time_since_epoch() + / std::chrono::microseconds(1) << "\n"; + + // print response + int id = 0; + for (auto& output : outputs) { + std::string outdata = hex_decode(output.data(), output.size()); + outdata = decrypt_response(outdata); + printf("{\"msg\": \"id-%d, %s\"}\n", id++, outdata.c_str()); + } + + // test subsequent + { + std::string out; + worker.Handle(0, prepared_sample, &out); + std::string outdata = hex_decode(out.data(), out.size()); + outdata = decrypt_response(outdata); + printf("decoded: %s", outdata.c_str()); + printf("{\"msg\": \"id-%d, %s\"}\n", 0, outdata.c_str()); + } + + // test subsequent (check reuse) + { + std::string out; + worker.Handle(0, prepared_sample, &out); + std::string outdata = hex_decode(out.data(), out.size()); + outdata = decrypt_response(outdata); + printf("decoded: %s", outdata.c_str()); + printf("{\"msg\": \"id-%d, %s\"}\n", 0, outdata.c_str()); + } + + // tear down + printf("worker closing.\n"); + worker.Close(); +} + return 0; +} diff --git a/sdk/example_containers/sgx-task/worker/untrusted/App2.cc b/sdk/example_containers/sgx-task/worker/untrusted/App2.cc new file mode 100644 index 0000000..f51802f --- /dev/null +++ b/sdk/example_containers/sgx-task/worker/untrusted/App2.cc @@ -0,0 +1,589 @@ +// a fully untrusted version of App for debugging +// #define WORKER_ENCLAVE_FILENAME "Worker_Enclave.signed.so" +#define REQUEST_COUNT 4 + +#include +#include + +#include "worker.h" +#include "common/hexutil.h" + +// for test +#include +#include +#include +#include +#include + +#include "openssl/ec.h" +#include "openssl/evp.h" +#include "openssl/pem.h" +#include "openssl/rsa.h" + +#define RSA_PUBLIC_KEY_SIZE 512 +#define RSA_PRIVATE_KEY_SIZE 2048 + +#define RSA_3072_PUBLIC_KEY_SIZE 650 +#define RSA_3072_PRIVATE_KEY_SIZE 3072 + +#define RSA_TYPE 0 +#define EC_TYPE 1 // EC-P384 + +#define MAX_SESSION_KEY_IV_LENGTH 16 +// #define ENCRYPTED_SESSION_KEY_LENGTH 256 +#define ENCRYPTED_SESSION_KEY_LENGTH 384 +#define OUTPUT_KEY_LENGTH 16 + +#define AES_GCM_IV_SIZE 12 +#define AES_GCM_TAG_SIZE 16 + +#define MAX_SESSION_KEY_IV_LENGTH 16 + + +/* A 128 bit key */ +unsigned char output_key[16] = {0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, + 0x38, 0x39, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35}; + +// /* A 96 bit IV */ +// unsigned char iv[12] = {0x99, 0xaa, 0x3e, 0x68, 0xed, 0x81, 0x73, 0xa0, +// 0xee, 0xd0, 0x66, 0x84}; + +struct EncryptedRequest { + std::string encrypted_secret_data; + std::string encrypted_session_key; + std::string iv; +}; + +uint8_t* enclave_public_key = nullptr; +size_t enclave_public_key_size = 0; +uint8_t* enclave_private_key = nullptr; +size_t enclave_private_key_size = 0; + +uint8_t enc_iv_buf[AES_GCM_IV_SIZE] = {0}; + + +thread_local char output_buf[4000]; +thread_local size_t actual_output_size; + + +int get_pkey_by_rsa(EVP_PKEY *pk) +{ + int res = -1; + EVP_PKEY_CTX *ctx = NULL; + + ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, NULL); + if (ctx == NULL) + return res; + res = EVP_PKEY_keygen_init(ctx); + if (res <= 0) + { + // PRINT("keygen_init failed (%d)\n", res); + goto done; + } + + res = EVP_PKEY_CTX_set_rsa_keygen_bits(ctx, RSA_3072_PRIVATE_KEY_SIZE); + if (res <= 0) + { + // PRINT("set_rsa_kengen_bits failed (%d)\n", res); + goto done; + } + + /* Generate key */ + res = EVP_PKEY_keygen(ctx, &pk); + if (res <= 0) + { + // PRINT("keygen failed (%d)\n", res); + goto done; + } + +done: + if (ctx) + EVP_PKEY_CTX_free(ctx); + + return res; + +} + +int get_pkey_by_ec(EVP_PKEY *pk) +{ + int res = -1; + EVP_PKEY_CTX *ctx; + + ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_EC, NULL); + if (ctx == NULL) + return res; + res = EVP_PKEY_keygen_init(ctx); + if (res <= 0) + { + // PRINT("EC_generate_key failed (%d)\n", res); + goto done; + } + + res = EVP_PKEY_CTX_set_ec_paramgen_curve_nid(ctx, NID_secp384r1); + if (res <= 0) + { + // PRINT("EC_generate_key failed (%d)\n", res); + goto done; + } + + /* Generate key */ + res = EVP_PKEY_keygen(ctx, &pk); + if (res <= 0) + { + // PRINT("EC_generate_key failed (%d)\n", res); + goto done; + } + +done: + if (ctx) + EVP_PKEY_CTX_free(ctx); + + return res; +} + + +// actually is generating RSA pair +// hardware independant +sgx_status_t generate_key_pair( + int type, + uint8_t** public_key, + size_t* public_key_size, + uint8_t** private_key, + size_t* private_key_size) +{ + sgx_status_t result = SGX_ERROR_UNEXPECTED; + uint8_t* local_public_key = nullptr; + uint8_t* local_private_key = nullptr; + int res = -1; + EVP_PKEY* pkey = nullptr; + BIO* bio = nullptr; + + pkey = EVP_PKEY_new(); + if (!pkey) + { + // PRINT("EVP_PKEY_new failed\n"); + result = SGX_ERROR_UNEXPECTED; + goto done; + } + + if (type != RSA_TYPE && type != EC_TYPE) + { + type = RSA_TYPE; // by default, we use RSA_TYPE + } + + switch(type) + { + case RSA_TYPE: + res = get_pkey_by_rsa(pkey); + break; + case EC_TYPE: + res = get_pkey_by_ec(pkey); + break; + } + + if (res <= 0) + { + // PRINT("get_pkey failed (%d)\n", res); + result = SGX_ERROR_UNEXPECTED; + goto done; + } + + // Allocate memory + local_public_key = (uint8_t*)malloc(RSA_3072_PUBLIC_KEY_SIZE); + if (!local_public_key) + { + // PRINT("out-of-memory:calloc(local_public_key failed\n"); + result = SGX_ERROR_OUT_OF_EPC; + goto done; + } + memset(local_public_key, 0x00, RSA_3072_PUBLIC_KEY_SIZE); + + local_private_key = (uint8_t*)malloc(RSA_3072_PRIVATE_KEY_SIZE); + if (!local_private_key) + { + // PRINT("out-of-memory: calloc(local_private_key) failed\n"); + result = SGX_ERROR_OUT_OF_EPC; + goto done; + } + memset(local_private_key, 0x00, RSA_3072_PRIVATE_KEY_SIZE); + + // Write out the public/private key in PEM format for exchange with + // other enclaves. + bio = BIO_new(BIO_s_mem()); + if (!bio) + { + // PRINT("BIO_new for local_public_key failed\n"); + goto done; + } + + res = PEM_write_bio_PUBKEY(bio, pkey); + if (!res) + { + // PRINT("PEM_write_bio_PUBKEY failed (%d)\n", res); + goto done; + } + + res = BIO_read(bio, local_public_key, RSA_3072_PUBLIC_KEY_SIZE); + if (!res) + { + // PRINT("BIO_read public key failed (%d)\n", res); + goto done; + } + BIO_free(bio); + bio = nullptr; + + bio = BIO_new(BIO_s_mem()); + if (!bio) + { + // PRINT("BIO_new for local_public_key failed\n"); + goto done; + } + + res = PEM_write_bio_PrivateKey( + bio, pkey, nullptr, nullptr, 0, nullptr, nullptr); + if (!res) + { + // PRINT("PEM_write_bio_PrivateKey failed (%d)\n", res); + goto done; + } + + res = BIO_read(bio, local_private_key, RSA_3072_PRIVATE_KEY_SIZE); + if (!res) + { + // PRINT("BIO_read private key failed (%d)\n", res); + goto done; + } + + BIO_free(bio); + bio = nullptr; + + *public_key = local_public_key; + *private_key = local_private_key; + + *public_key_size = strlen(reinterpret_cast(local_public_key)); + *private_key_size = strlen(reinterpret_cast(local_private_key)); + + // PRINT("public_key_size %d, private_key_size %d\n", *public_key_size, *private_key_size); + result = SGX_SUCCESS; + +done: + if (bio) + BIO_free(bio); + if (pkey) + EVP_PKEY_free(pkey); // When this is called, rsa is also freed + if (result != SGX_SUCCESS) + { + if (local_public_key) + free(local_public_key); + if (local_private_key) + free(local_private_key); + } + return result; +} + +std::string get_key_and_report() { + uint8_t* public_key_buffer = nullptr; + size_t public_key_size = 0; + uint8_t* private_key_buffer = nullptr; + size_t private_key_size = 0; + sgx_status_t status = generate_key_pair(RSA_TYPE, &public_key_buffer, &public_key_size, &private_key_buffer, &private_key_size); + if (status != SGX_SUCCESS) { + if (private_key_buffer) free(private_key_buffer); + if (public_key_buffer) free(public_key_buffer); + return ""; + } + + // copy the private key to enclave global variable + enclave_public_key = public_key_buffer; + enclave_public_key_size = public_key_size; + enclave_private_key = private_key_buffer; + enclave_private_key_size = private_key_size; + + // print the keys to file + std::ofstream public_key_file; + public_key_file.open("public_key.pem"); + public_key_file << std::string((char*)public_key_buffer, public_key_size); + public_key_file.close(); + + std::ofstream private_key_file; + private_key_file.open("private_key.pem"); + private_key_file << std::string((char*)private_key_buffer, private_key_size); + private_key_file.close(); + + return std::string((char*)enclave_public_key, enclave_public_key_size); +} + +std::string encrypt_request(const std::string& data, const std::string& enclave_public_key) { + std::string plaintext{data}; + plaintext.append((char*)output_key, 16); + int encrypted_message_len = 0; + unsigned char* ek = NULL; + int ek_len = 0; + unsigned char iv[MAX_SESSION_KEY_IV_LENGTH]; + memset(iv, 0, MAX_SESSION_KEY_IV_LENGTH); + size_t encrypted_message_max_length = plaintext.size()+EVP_MAX_IV_LENGTH; + unsigned char encrypted[encrypted_message_max_length]; + memset(encrypted, 0, encrypted_message_max_length); + + BIO* bio = NULL; + bio = BIO_new_mem_buf(enclave_public_key.data(), enclave_public_key.size()); + RSA* rssPublicKey = NULL; + PEM_read_bio_RSA_PUBKEY(bio, &rssPublicKey, NULL, NULL); + EVP_PKEY *pkey = EVP_PKEY_new(); + EVP_PKEY_assign_RSA(pkey, rssPublicKey); + EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new(); + EVP_CIPHER_CTX_init(ctx); + ek = (unsigned char*) malloc(EVP_PKEY_size(pkey)); + EVP_SealInit(ctx, EVP_aes_256_cbc(), &ek, &ek_len, iv, &pkey, 1); + printf("encrypted session key length: %d\n", ek_len); + + printf("secret data to send (%ldB): \n", plaintext.size()); + BIO_dump_fp(stdout, (const char*) plaintext.data(), plaintext.size()); + printf("encrypted symmetric key to send (%dB): \n", ek_len); + BIO_dump_fp(stdout, (const char*) ek, ek_len); + printf("symmetric key iv to send (%dB): \n", MAX_SESSION_KEY_IV_LENGTH); + BIO_dump_fp(stdout, (const char*) iv, MAX_SESSION_KEY_IV_LENGTH); + + int encrypted_block_len = 0; + EVP_SealUpdate(ctx, encrypted, &encrypted_block_len, (const unsigned char*) plaintext.data(), plaintext.size()); + encrypted_message_len = encrypted_block_len; + printf("encrypt message length update to %d\n", encrypted_message_len); + EVP_SealFinal(ctx, encrypted + encrypted_block_len, &encrypted_block_len); + encrypted_message_len += encrypted_block_len; + printf("encrypt message length update to %d\n", encrypted_message_len); + EVP_CIPHER_CTX_free(ctx); + BIO_free(bio); + EVP_PKEY_free(pkey); + + std::string ret = std::string((char*)encrypted, encrypted_message_len); + ret.append((char*)ek, ek_len); + ret.append((char*)iv, MAX_SESSION_KEY_IV_LENGTH); + + if (ek) free(ek); + + printf("the whole message to send (%ldB): \n", ret.size()); + BIO_dump_fp(stdout, ret.data(), ret.size()); + + // save the encrypted message to file + std::ofstream encrypted_message_file; + encrypted_message_file.open("encrypted_message.txt"); + encrypted_message_file << ret; + encrypted_message_file.close(); + + return ret; +} + +std::string decrypt_response(const std::string& data) { + uint8_t tag[16]; + memcpy(tag, data.data()+data.size()-16, 16); + uint8_t iv[12]; + memcpy(iv, data.data()+data.size()-28, 12); + size_t encrypt_len = data.size()-28; + EVP_CIPHER_CTX *ctx; + /* Create and initialise the context */ + if(!(ctx = EVP_CIPHER_CTX_new())) return ""; + + if(EVP_DecryptInit_ex(ctx, EVP_aes_128_gcm(), NULL, output_key, iv)!=1) { + EVP_CIPHER_CTX_free(ctx); + return ""; + } + + // BIO_dump_fp(stdout, (const char*) data.data(), data.size()-32); + printf("encrypted data (%ldB): \n", encrypt_len); + BIO_dump_fp(stdout, (const char*) data.data(), encrypt_len); + printf("tag: \n"); + BIO_dump_fp(stdout, (const char*) tag, 16); + + EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_TAG, 16, tag); + + uint8_t plaintext[encrypt_len]; + int plaintext_len; + int len; + if(EVP_DecryptUpdate(ctx, plaintext, &len, (const unsigned char*)data.data(), encrypt_len)!=1) { + EVP_CIPHER_CTX_free(ctx); + return ""; + } + plaintext_len = len; + // printf("so far plaintext len: %d\n", plaintext_len); + + if (EVP_DecryptFinal_ex(ctx, plaintext + len, &len) !=1) { + EVP_CIPHER_CTX_free(ctx); + return ""; + } + plaintext_len += len; + // printf("plaintext (%dB):\n", plaintext_len); + // std::string ret; + // ret.assign((char*)plaintext, plaintext_len); + // printf("%s\n", ret.c_str()); + + EVP_CIPHER_CTX_free(ctx); + return std::string((char*)plaintext, plaintext_len); +} + +unsigned char* rsa_decrypt_data(const unsigned char* data, int len, const unsigned char* ek, int ek_len, const unsigned char* iv, const uint8_t* rsa_private_key, int rsa_private_key_len, int* output_len) { + printf("iv for decrypt (%dB): \n", MAX_SESSION_KEY_IV_LENGTH); + BIO_dump_fp(stdout, (const char*) iv, MAX_SESSION_KEY_IV_LENGTH); + printf("ek for decrypt (%dB): \n", ENCRYPTED_SESSION_KEY_LENGTH); + BIO_dump_fp(stdout, (const char*) ek, ENCRYPTED_SESSION_KEY_LENGTH); + printf("data to decrypt (%dB): \n", len); + BIO_dump_fp(stdout, (const char*) data, len); + printf("decrypt rsa key (%dB): \n", rsa_private_key_len); + BIO_dump_fp(stdout, (const char*) rsa_private_key, rsa_private_key_len); + + + BIO* bio = nullptr; + bio = BIO_new_mem_buf(rsa_private_key, rsa_private_key_len); + RSA* loaded_private_key = NULL; + PEM_read_bio_RSAPrivateKey(bio, &loaded_private_key, NULL, NULL); + EVP_PKEY* pkey = EVP_PKEY_new(); + EVP_PKEY_assign_RSA(pkey, loaded_private_key); + EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new(); + EVP_CIPHER_CTX_init(ctx); + + unsigned char* decrypted = (unsigned char*)malloc(len + EVP_MAX_IV_LENGTH); + EVP_OpenInit(ctx, EVP_aes_256_cbc(), ek, ek_len, iv, pkey); + int decrypted_len = 0; + int decyprtedBlockLen = 0; + EVP_OpenUpdate(ctx, decrypted, &decyprtedBlockLen, data, len); + decrypted_len = decyprtedBlockLen; + EVP_OpenFinal(ctx, decrypted + decrypted_len, &decyprtedBlockLen); + decrypted_len += decyprtedBlockLen; + *output_len = decrypted_len; + // free ctx + EVP_CIPHER_CTX_free(ctx); + BIO_free(bio); + EVP_PKEY_free(pkey); + return decrypted; +} + + +std::string decrypt_content(const char* input_data, size_t input_data_size, std::string* output_key) { + const char* iv = input_data + (input_data_size - MAX_SESSION_KEY_IV_LENGTH); + const char* ek = input_data + (input_data_size - MAX_SESSION_KEY_IV_LENGTH - ENCRYPTED_SESSION_KEY_LENGTH); + int encrypted_input_len = input_data_size - (MAX_SESSION_KEY_IV_LENGTH + ENCRYPTED_SESSION_KEY_LENGTH); + // perform decryption + int decrypted_len; + unsigned char* decrypted = rsa_decrypt_data((unsigned char*) input_data, encrypted_input_len, (unsigned char*) ek, ENCRYPTED_SESSION_KEY_LENGTH, (unsigned char*) iv, enclave_private_key, enclave_private_key_size, &decrypted_len); + printf("decrypted (%dB):\n", decrypted_len); + // BIO_dump_fp(stdout, (const char*) decrypted, decrypted_len); + output_key->assign((char*)decrypted + decrypted_len-OUTPUT_KEY_LENGTH, OUTPUT_KEY_LENGTH); + std::string ret{(char*)decrypted, decrypted_len-OUTPUT_KEY_LENGTH}; + + if (decrypted) free(decrypted); + return ret; +} + +void encrypt_output(const std::string& output_plain, const std::string& output_key, std::string* encrypt_output) { + EVP_CIPHER_CTX *ctx; + if(!(ctx = EVP_CIPHER_CTX_new())) return; + + if(EVP_EncryptInit_ex(ctx, EVP_aes_128_gcm(), NULL, (const unsigned char*)output_key.data(), enc_iv_buf) != 1) { + // if(EVP_EncryptInit_ex(ctx, EVP_aes_256_cbc(), NULL, key, iv) != 1) { + EVP_CIPHER_CTX_free(ctx); + return; + } + + unsigned char ciphertext[output_plain.size()]; + + int ciphertext_len; + int len; + if(EVP_EncryptUpdate(ctx, ciphertext, &len, (const unsigned char*) output_plain.data(), output_plain.size())!=1) { + EVP_CIPHER_CTX_free(ctx); + return; + } + ciphertext_len = len; + + if(EVP_EncryptFinal_ex(ctx, ciphertext + len, &len)!=1) { + EVP_CIPHER_CTX_free(ctx); + return; + } + ciphertext_len += len; + + unsigned char tag[AES_GCM_TAG_SIZE] = {0,}; + // tag needed for gcm, not for cbc + EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_GET_TAG, 16, tag); + EVP_CIPHER_CTX_free(ctx); + encrypt_output->assign((char*)ciphertext, ciphertext_len); + encrypt_output->append((char*)enc_iv_buf, AES_GCM_IV_SIZE); + encrypt_output->append((char*)tag, AES_GCM_TAG_SIZE); +} + +void run(const char* input_data, size_t input_data_size, size_t* output_size) { + std::string output_key; + auto decrypt_input = decrypt_content(input_data, input_data_size, &output_key); + std::string output_plain = "hello" + decrypt_input; + std::string output; + encrypt_output(output_plain, output_key, &output); + memcpy(output_buf, output.data(), output.size()); + actual_output_size = output.size(); + *output_size = actual_output_size; +} + +void get_output(uint8_t* ret, size_t size) { + memcpy(ret, output_buf, actual_output_size); +} + +void execute(const std::string& request, std::string* output) { + size_t output_size; + run(request.data(), request.size(), &output_size); + uint8_t* output_buf = (uint8_t*) malloc(output_size); + get_output(output_buf, output_size); + *output = std::string((char*) output_buf, output_size); + free(output_buf); +} + +void worker_handle(const std::string& request, std::string* output_encoded) { + std::string output; + execute(request, &output); + const char* hex_result = hexstring(output.data(), output.size()); + output_encoded->assign(hex_result, strlen(hex_result)); +} + + +int main(int argc, char* argv[]) { + std::cout << "Program starts at: " + << std::chrono::system_clock::now().time_since_epoch() + / std::chrono::microseconds(1) << "\n"; + + std::string sample_request_path; + std::string sample_request; + + if (argc == 2) { + sample_request = argv[1]; + printf("sample request: %s\n", sample_request.c_str()); + } else { + fprintf(stderr, "Usage: sample_request\n"); + exit(1); + } + +// test enclave recreate +// for (int j = 0; j < 2; j++) { + std::string key = get_key_and_report(); + + // prepare the sample request as described below + // expect the input should be in the form of |encrytped secret data|encrypted session key(256B)|IV(16)| + // the secret data should consists of |input|output key(16B)| + printf("check input: %s, %ld\n", sample_request.c_str(), sample_request.size()); + printf("check rsa pubkey size: %ld\n", sample_request.size()); + std::string prepared_sample = encrypt_request(sample_request, key); + + // test subsequent + std::string out; + // worker.Handle(0, prepared_sample, &out); + worker_handle(prepared_sample, &out); + std::string outdata = hex_decode(out.data(), out.size()); + outdata = decrypt_response(outdata); + printf("decoded: %s", outdata.c_str()); + printf("{\"msg\": \"id-%d, %s\"}\n", 0, outdata.c_str()); + + if (enclave_public_key) { + free(enclave_public_key); + enclave_public_key = nullptr; + } + if (enclave_private_key) { + free(enclave_private_key); + enclave_private_key = nullptr; + } +// } + return 0; +} diff --git a/sdk/example_containers/sgx-task/worker/untrusted/ocall_patches.cc b/sdk/example_containers/sgx-task/worker/untrusted/ocall_patches.cc new file mode 100644 index 0000000..ec1cdc8 --- /dev/null +++ b/sdk/example_containers/sgx-task/worker/untrusted/ocall_patches.cc @@ -0,0 +1,35 @@ +/** + * implement the ocalls defined in worker edl files. + * packaged into libworker.a +*/ + +#include "Enclave_u.h" // include this helps to solve the undefined reference error for ocalls. Enclave_u.c is compiled as c. + +#include +#include +#include +#include + + +void ocall_debug_print(const void* s, size_t len) { + assert(len < INT_MAX); + printf("DEBUG PRINT: %.*s\n", (int) len, (const char*) s); +} +void ocall_debug_print_string(const char* s) { + printf("DEBUG PRINT: %s\n", s); +} +void ocall_debug_print_hexstring(const char* s) { + printf("DEBUG PRINT (hex): "); + for (unsigned int i = 0; i < strlen(s); i++) { + printf("%02hhx", (unsigned char) s[i]); + } + printf("\n"); +} +void ocall_debug_print_hex(const void* s, size_t len) { + printf("DEBUG PRINT (hex): "); + auto it = (const unsigned char*) s; + for (unsigned int i = 0; i < len; i++) { + printf("%02hhx", *(it++)); + } + printf("\n"); +} diff --git a/sdk/example_containers/sgx-task/worker/untrusted/worker.cc b/sdk/example_containers/sgx-task/worker/untrusted/worker.cc new file mode 100644 index 0000000..c3dac32 --- /dev/null +++ b/sdk/example_containers/sgx-task/worker/untrusted/worker.cc @@ -0,0 +1,187 @@ +#include "worker.h" + +#include "sgx_urts.h" + +#include +#include + +#include + +#include "Enclave_u.h" + +#include "common/hexutil.h" + +#include "sgx_dcap_ql_wrapper.h" + +// for test +#include +#include +#include + +namespace { +std::mutex debug_log_mutex; +int PUBKEY_SIZE = 625; +} // anonymous namespace + +std::string Worker::GetKeyAndReport(std::string* retKey) { + quote3_error_t qe_error = SGX_QL_SUCCESS; + sgx_target_info_t qe_target_info = {0,}; + sgx_report_t app_report = {0,}; + // prepare target info + qe_error = sgx_qe_get_target_info(&qe_target_info); + if (qe_error != SGX_QL_SUCCESS) { + printf("sgx_qe_get_target_info failed with %d\n", qe_error); + return ""; + } + // create report and key + sgx_status_t retval; + uint8_t* pubkey = (uint8_t*) malloc(PUBKEY_SIZE); + + sgx_status_t status = enc_get_key_and_report(eid_, &retval, &qe_target_info, &app_report, pubkey, PUBKEY_SIZE); + + if ((status != SGX_SUCCESS) || (retval != SGX_SUCCESS)) { + printf("ecall_get_key_and_report failed with %d - %d\n", status, retval); + return ""; + } + + uint32_t quote_size = 0; + qe_error = sgx_qe_get_quote_size("e_size); + if (qe_error != SGX_QL_SUCCESS) { + printf("sgx_qe_get_quote_size failed with %d\n", qe_error); + return ""; + } + + uint8_t* quote_buf = (uint8_t*) malloc(quote_size); + if (quote_buf == NULL) { + printf("malloc failed for quote buffer %d\n", quote_size); + return ""; + } + memset(quote_buf, 0, quote_size); + + qe_error = sgx_qe_get_quote(&app_report, quote_size, quote_buf); + if (qe_error != SGX_QL_SUCCESS) { + printf("sgx_qe_get_quote failed with %d\n", qe_error); + free(quote_buf); + return ""; + } + + // prepare the return + retKey->assign((char*)pubkey, PUBKEY_SIZE); + if (pubkey) free(pubkey); + return hex_encode(quote_buf, quote_size); +} + +bool Worker::Initialize() { + + std::cout << "init started at: " + << std::chrono::system_clock::now().time_since_epoch() + / std::chrono::microseconds(1) << "\n"; + + sgx_launch_token_t t; + int updated = 0; + memset(t, 0, sizeof(sgx_launch_token_t)); + auto sgxStatus = sgx_create_enclave(enclave_file_name_.c_str(), + SGX_DEBUG_FLAG, &t, &updated, &eid_, NULL); + if (sgxStatus != SGX_SUCCESS) { + printf("Failed to create Enclave : error %d - %#x.\n", sgxStatus, + sgxStatus); + return false; + } else printf("Enclave launched.\n"); + + std::cout << "init finished at: " + << std::chrono::system_clock::now().time_since_epoch() + / std::chrono::microseconds(1) << "\n"; + + initialize_ = true; + return true; +} + +int Worker::Execute(const std::string& request, std::string* output) { + if (!initialize_) return -1; + sgx_status_t retval; + size_t output_size; + + std::cout << "exec started at: " + << std::chrono::system_clock::now().time_since_epoch() + / std::chrono::microseconds(1) << "\n"; + enc_run(eid_, &retval, + request.data(), request.size(), &output_size); + if (retval != SGX_SUCCESS) { + printf("Failed to run task execution : error %d - %#x.\n", + retval, retval); + *output = "failed on task execution " + std::to_string(retval); + return -1; + } + + + uint8_t* output_buf = (uint8_t*) malloc(output_size); + if (output_buf == NULL) { + printf("malloc failed for output buffer %ld\n", output_size); + return -1; + } + + enc_get_output(eid_, &retval, output_buf, output_size); + if (retval != SGX_SUCCESS) { + printf("Failed to get encrypted result : error %d - %#x.\n", + retval, retval); + *output = "failed on encrypted result " + std::to_string(retval); + return -1; + } + *output = std::string((char*) output_buf, output_size); + free(output_buf); + + { + std::lock_guard lg(debug_log_mutex); + std::cout << "result done at: " + << std::chrono::system_clock::now().time_since_epoch() + / std::chrono::microseconds(1) << "\n"; + } + + return 0; +} + +void Worker::Close() { + if (closed_) return; + closed_ = true; + initialize_ = false; + sgx_status_t ret = enc_clear_exec_context(eid_); + printf("returned status from close %d\n", ret); + assert(ret == SGX_SUCCESS); + ret = sgx_destroy_enclave(eid_); + assert(ret == SGX_SUCCESS); +} + +bool Worker::Handle(uint64_t handle_id, const std::string& request, + std::string* output_encoded) { + + auto msg_prefix = "[id-" + std::to_string(handle_id) + "]"; + +#ifndef NDEBUG + { + std::lock_guard lg(debug_log_mutex); + std::cout << msg_prefix << " request handle start at: " + << std::chrono::system_clock::now().time_since_epoch() + / std::chrono::microseconds(1) << "\n"; + } +#endif // NDEBUG + + std::string output; + auto ret = Execute(request, &output); + +#ifndef NDEBUG + { + std::lock_guard lg(debug_log_mutex); + + std::cout << msg_prefix << "done at: " + << std::chrono::system_clock::now().time_since_epoch() + / std::chrono::microseconds(1) << "\n"; + } +#endif // NDEBUG + + const char* hex_result = hexstring(output.data(), output.size()); + if (ret == 0) { + output_encoded->append(hex_result, strlen(hex_result)); + } else *output_encoded = output; + + return ret; +} diff --git a/sdk/example_containers/sgx-task/worker/untrusted/worker.h b/sdk/example_containers/sgx-task/worker/untrusted/worker.h new file mode 100644 index 0000000..b3d75fe --- /dev/null +++ b/sdk/example_containers/sgx-task/worker/untrusted/worker.h @@ -0,0 +1,47 @@ +#ifndef MECA_SGX_WORKER_U_WORKER_H_ +#define MECA_SGX_WORKER_U_WORKER_H_ + +#include "sgx_urts.h" + +#include + +class Worker { + public: + Worker(const std::string& enclave_file_name) + : initialize_(false), closed_(false), enclave_file_name_(std::move(enclave_file_name)), + eid_(0) {} + + ~Worker() { Close(); } + + // delete copy and move constructors and assigment operators + Worker(const Worker&) = delete; + Worker& operator=(const Worker&) = delete; + Worker(Worker&&) = delete; + Worker& operator=(Worker&&) = delete; + + bool Initialize(); + + std::string GetKeyAndReport(std::string* key); + + bool Handle(uint64_t handle_id, const std::string& sample_request, + std::string* output); + + /** + * @brief execute the inference request in the worker managed enclave. + * + * @param request : user request + * @return int : 0 for success; -1 for failure + */ + int Execute(const std::string& request, std::string* output); + + + void Close(); + + private: + bool initialize_; + bool closed_; + const std::string enclave_file_name_; + sgx_enclave_id_t eid_; +}; + +#endif // MECA_SGX_WORKER_U_WORKER_H_