diff --git a/demos/README.md b/demos/README.md index 78a2daab..5bb0f951 100644 --- a/demos/README.md +++ b/demos/README.md @@ -2,10 +2,23 @@ This directory contains sample projects that demonstrate how Occlum can be used to build and run user applications. +## Toolchain demos + +This set of demos shows how the Occlum toolchain can be used with different build tools. + * `hello_c/`: A sample C project built with Makefile/CMake. * `hello_cc/`: A sample C++ project built with Makefile/CMake. * `hello_bazel/`: A sample C++ project built with [Bazel](https://bazel.build). + +## Application demos + +This set of demos shows how real-world apps can be easily run inside SGX enclaves with Occlum. + * `https_server/`: A HTTPS file server based on [Mongoose Embedded Web Server Library](https://github.com/cesanta/mongoose). * `openvino/` A benchmark of [OpenVINO Inference Engine](https://docs.openvinotoolkit.org/2019_R3/_docs_IE_DG_inference_engine_intro.html). * `tensorflow_lite/`: A demo and benchmark of [Tensorflow Lite](https://www.tensorflow.org/lite) inference engine. * `xgboost/`: A demo of [XGBoost](https://xgboost.readthedocs.io/en/latest/). + +## SGX capability demos + +* `remote_attestation/`: This project demonstrates how an app running upon Occlum can perform SGX remote attestation. diff --git a/demos/remote_attestation/.gitignore b/demos/remote_attestation/.gitignore new file mode 100644 index 00000000..06cfff8d --- /dev/null +++ b/demos/remote_attestation/.gitignore @@ -0,0 +1,3 @@ +build +deps +occlum_workspace diff --git a/demos/remote_attestation/CMakeLists.txt b/demos/remote_attestation/CMakeLists.txt new file mode 100644 index 00000000..7da155ae --- /dev/null +++ b/demos/remote_attestation/CMakeLists.txt @@ -0,0 +1,42 @@ +CMAKE_MINIMUM_REQUIRED(VERSION 3.5) +SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") + +PROJECT(REMOTE_ATTESTATION LANGUAGES CXX VERSION 0.0.1) +MESSAGE(STATUS "BINARY dir " ${CMAKE_CURRENT_BINARY_DIR}) +MESSAGE(STATUS "SOURCE dir " ${CMAKE_CURRENT_SOURCE_DIR}) + +SET(SGXSDK_INSTALL_DIR /opt/intel/sgxsdk) +SET(OCCLUM_INSTALL_DIR /usr/local/occlum/x86_64-linux-musl) + +FILE(GLOB LIB_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/lib/src/*.cpp) + +SET(RALIB occlumra) +ADD_LIBRARY(${RALIB} ${LIB_SRCS}) +TARGET_INCLUDE_DIRECTORIES( + ${RALIB} PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR}/lib/include + ${SGXSDK_INSTALL_DIR}/include + ${OCCLUM_INSTALL_DIR}/include + ${CMAKE_CURRENT_SOURCE_DIR}/deps/cppcodec + ${CMAKE_CURRENT_SOURCE_DIR}/deps/rapidjson/include +) +TARGET_LINK_LIBRARIES(${RALIB} + -L${OCCLUM_INSTALL_DIR}/lib -lcurl +) + +SET(DEMOAPP remote_attestation_demo) +FILE(GLOB APP_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/app/*.cpp) +ADD_EXECUTABLE(${DEMOAPP} ${APP_SRCS}) +TARGET_INCLUDE_DIRECTORIES( + ${DEMOAPP} PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR}/app + ${CMAKE_CURRENT_SOURCE_DIR}/lib/include + ${SGXSDK_INSTALL_DIR}/include + ${OCCLUM_INSTALL_DIR}/include +) +TARGET_LINK_LIBRARIES(${DEMOAPP} + -L${CMAKE_CURRENT_BINARY_DIR} -l${RALIB} + -L${OCCLUM_INSTALL_DIR}/lib -lcurl + -Wl,-rpath=${CMAKE_CURRENT_BINARY_DIR}:${OCCLUM_INSTALL_DIR}/lib +) +ADD_DEPENDENCIES(${DEMOAPP} ${RALIB}) diff --git a/demos/remote_attestation/README.md b/demos/remote_attestation/README.md new file mode 100644 index 00000000..97d46a81 --- /dev/null +++ b/demos/remote_attestation/README.md @@ -0,0 +1,18 @@ +# SGX Remote Attestation Demo + +This project demonstrates how to do remote attestation on Occlum. + +In a nutshell, Occlum provides SGX capabilities to user apps through ioctls on a special device (`/dev/sgx`). To hide the low-level details of ioctls from user apps, a user-friendly, remote attestation library is provided in this demo. + +**Prerequisites.** This demo needs to access Intel Attestation Service (IAS). To do this, a developer needs to contact Intel to obtain a Service Provider ID (SPID) and the associated Service Provider certificate. The certificate and key files should be put into `conf/certs`, and configure the SPID and paths of the certificate and key files in `conf/ra_config.example.json`. + +**Step 1.** Build this demo +``` +download_and_build.sh +``` + +**Step 2.** Run this demo on Occlum +``` +run_on_occlum.sh +``` + diff --git a/demos/remote_attestation/app/ra_config.h b/demos/remote_attestation/app/ra_config.h new file mode 100644 index 00000000..504cc72f --- /dev/null +++ b/demos/remote_attestation/app/ra_config.h @@ -0,0 +1,26 @@ +#ifndef REMOTE_ATTESTATION_RA_CONFIG_H_ +#define REMOTE_ATTESTATION_RA_CONFIG_H_ + +#include + +#include "sofaenclave/common/error.h" +#include "sofaenclave/common/log.h" +#include "sofaenclave/ra_conf.h" + +constexpr char kConfValueEnable[] = "enable"; +constexpr char kConfValueDisable[] = "disable"; +constexpr char kConfValueTrue[] = "true"; +constexpr char kConfValueFalse[] = "false"; + +constexpr char kRaConf[] = "ra_config.json"; + +constexpr char kConfIasServer[] = "ias_url"; +constexpr char kConfIasCert[] = "ias_sp_cert_file"; +constexpr char kConfIasKey[] = "ias_sp_key_file"; +constexpr char kConfSPID[] = "enclave_spid"; + +#define RA_CONF_STR(name) SofaeConfGetStr(kRaConf, name) +#define RA_CONF_INT(name, value) SofaeConfGetInt(kRaConf, name, value) +#define RA_CONF_ARRARY(name, value) SofaeConfGetStrArray(kRaConf, name, value) + +#endif // REMOTE_ATTESTATION_RA_CONFIG_H_ diff --git a/demos/remote_attestation/app/ra_main.cpp b/demos/remote_attestation/app/ra_main.cpp new file mode 100644 index 00000000..ae4e89b2 --- /dev/null +++ b/demos/remote_attestation/app/ra_main.cpp @@ -0,0 +1,66 @@ +#include +#include + +#include "./ra_config.h" +#include "sofaenclave/ra_manager.h" + +static uint8_t Hex2Dec(const char hex) { + if (('0' <= hex) && (hex <= '9')) { + return hex - '0'; + } + else if (('a' <= hex) && (hex <= 'f')) { + return hex - 'a' + 10; + } + else if (('A' <= hex) && (hex <= 'F')) { + return hex - 'A' + 10; + } + else { + // Otherwise return zero for none HEX charactor + return 0; + } +} + +static std::vector HexStr2Bytes(const uint8_t* str) { + int len = strlen(RCAST(const char *, str)) / 2; + std::vector dst(len); + + for (int i = 0; i < len; i++) { + dst[i] = (Hex2Dec(str[i * 2] & 0xFF) << 4) + + (Hex2Dec(str[i * 2 + 1] & 0xFF)); + } + return dst; +} + +int main() { + printf("Remote attestation testing ...\n"); + + std::string endpoint = RA_CONF_STR(kConfIasServer); + std::string cert = RA_CONF_STR(kConfIasCert); + std::string key = RA_CONF_STR(kConfIasKey); + std::string spid_str = RA_CONF_STR(kConfSPID); + + if (spid_str.empty()) { + printf("Please specify the right SPID in configuration file!\n"); + return -1; + } + + SofaeServerCfg ias_server = {endpoint, cert, key}; + SofaeEnclaveQuote quote = {0}; + SofaeQuoteArgs quote_args = {0}; + quote_args.quote_type = SGX_LINKABLE_SIGNATURE; + quote_args.quote.as_buf = RCAST(uint8_t *, "e); + quote_args.quote_buf_len = sizeof(SofaeEnclaveQuote); + + std::vector spid_vec = HexStr2Bytes( + RCAST(const uint8_t *, spid_str.c_str())); + std::memcpy(RCAST(void *, "e_args.spid.id), + RCAST(const void *, spid_vec.data()), sizeof(quote_args.spid)); + sofaenclave::occlum::IasReport ias_report; + int ret = GetQuoteAndFetchIasReport(ias_server, "e_args, &ias_report); + if (ret) { + printf("Fail to get quote or fetch report, erro code is %x!\n", ret); + } else { + printf("Test getting quote and fetching report successfully!\n"); + } + return ret; +} diff --git a/demos/remote_attestation/conf/ra_config.example.json b/demos/remote_attestation/conf/ra_config.example.json new file mode 100644 index 00000000..44dee9fb --- /dev/null +++ b/demos/remote_attestation/conf/ra_config.example.json @@ -0,0 +1,6 @@ +{ + "ias_url": "", + "ias_sp_cert_file": "/etc/certs/sp_client.crt", + "ias_sp_key_file": "/etc/certs/sp_client.key", + "enclave_spid": "" +} diff --git a/demos/remote_attestation/download_and_build.sh b/demos/remote_attestation/download_and_build.sh new file mode 100755 index 00000000..a0963189 --- /dev/null +++ b/demos/remote_attestation/download_and_build.sh @@ -0,0 +1,104 @@ +#!/usr/bin/env bash + +export CC=occlum-gcc +export CXX=occlum-g++ + +THISDIR="$(dirname $(readlink -f $0))" +INSTALLDIR="/usr/local/occlum/x86_64-linux-musl" +DEPSDIR="$THISDIR/deps" + +mkdir -p $DEPSDIR || exit 1 + +# Download OpenSSL 1.1.1 +OPENSSLDIR="${DEPSDIR}/openssl" +if [ ! -d "$OPENSSLDIR" ] ; then + echo "Downloading openssl ..." + cd "$DEPSDIR" && \ + wget https://github.com/openssl/openssl/archive/OpenSSL_1_1_1.tar.gz && \ + tar -xvf OpenSSL_1_1_1.tar.gz && \ + mv openssl-OpenSSL_1_1_1 openssl && \ + echo "Download openssl successfully" || exit 1 +else + echo "The openssl code is already existent" +fi + +# Download curl 7.29.0 +CURLDIR="${DEPSDIR}/curl" +if [ ! -d "$CURLDIR" ] ; then + echo "Downloading curl ..." + cd "$DEPSDIR" && \ + wget https://github.com/curl/curl/archive/curl-7_29_0.tar.gz && \ + tar -xvf curl-7_29_0.tar.gz && \ + mv curl-curl-7_29_0 curl && \ + echo "Download curl successfully" || exit 1 +else + echo "The openssl code is already existent" +fi + +# Download other dependencies +CPPCODECDIR="${DEPSDIR}/cppcodec" +if [ ! -d "$CPPCODECDIR" ] ; then + echo "Downloading cppcodec ..." + cd "$DEPSDIR" && \ + wget -O cppcodec-v0.2.tar.gz \ + https://github.com/tplgy/cppcodec/archive/v0.2.tar.gz && \ + tar -xvf cppcodec-v0.2.tar.gz && \ + mv cppcodec-0.2 cppcodec && \ + echo "Download cppcodec successfully" || exit 1 +else + echo "The cppcodec code is already existent" +fi +RAPIDJSONDIR="${DEPSDIR}/rapidjson" +if [ ! -d "$RAPIDJSONDIR" ] ; then + echo "Downloading rapidjson ..." + cd "$DEPSDIR" && \ + wget -O rapidjson-v1.1.0.tar.gz \ + https://github.com/Tencent/rapidjson/archive/v1.1.0.tar.gz && \ + tar -xvf rapidjson-v1.1.0.tar.gz && \ + mv rapidjson-1.1.0 rapidjson && \ + echo "Download cppcodec successfully" || exit 1 +else + echo "The cppcodec code is already existent" +fi + + +# Build and install openssl +if [ ! -f "$INSTALLDIR/lib/libcrypto.so" ] ; then + echo "Building openssl ..." + cd "$OPENSSLDIR" && \ + CC=occlum-gcc ./config \ + --prefix=$INSTALLDIR \ + --openssldir=/usr/local/occlum/ssl \ + --with-rand-seed=rdcpu \ + no-zlib no-async no-tests && \ + make -j${nproc} && \ + sudo make install && \ + echo "Build openssl successfully" || exit 1 +else + echo "The openssl library is aleady existent" +fi + +# Build and install libcurl +if [ ! -f "$INSTALLDIR/lib/libcurl.so" ] ; then + cd "$CURLDIR" + if [ ! -f ./configure ] ; then + echo "Building configure file ..." + ./buildconf || exit 1 + fi + echo "Building curl ..." + CC=occlum-gcc CXX=occlum-g++ ./configure \ + --prefix=$INSTALLDIR \ + --with-ssl=$INSTALLDIR && \ + make -j${nproc} && \ + sudo make install && \ + echo "Build curl successfully" || exit 1 +else + echo "The curl library is aleady existent" +fi + +# Build the remote attestation library and demo application +echo "Build demo source code" +cd "$THISDIR" && rm -rf ./build && mkdir -p build +cd build && \ +cmake -DCMAKE_CXX_COMPILER=occlum-g++ ../ && \ +make -j$(nproc) diff --git a/demos/remote_attestation/lib/include/sofaenclave/common/error.h b/demos/remote_attestation/lib/include/sofaenclave/common/error.h new file mode 100644 index 00000000..d0a6cddb --- /dev/null +++ b/demos/remote_attestation/lib/include/sofaenclave/common/error.h @@ -0,0 +1,39 @@ +#ifndef REMOTE_ATTESTATION_LIB_INCLUDE_SOFAENCLAVE_COMMON_ERROR_H_ +#define REMOTE_ATTESTATION_LIB_INCLUDE_SOFAENCLAVE_COMMON_ERROR_H_ + +/* We want to use SofaeErrorCode to include the error code from Intel SDK and + * also the SOFAEnclave code, so we use the same unsigned int type here.*/ +typedef int SofaeErrorCode; + +#define SOFAE_MK_ERROR(x) (0xFFFF0000&((x) << 16)) + +#define SOFAE_SUCCESS (0x00000000) + +#define SOFAE_ERROR_GENERIC SOFAE_MK_ERROR(0x0001) +#define SOFAE_ERROR_PARAMETERS SOFAE_MK_ERROR(0x0002) +#define SOFAE_ERROR_MALLOC SOFAE_MK_ERROR(0x0003) +#define SOFAE_ERROR_ENCLAVE_NOTINITIALIZED SOFAE_MK_ERROR(0x0004) +#define SOFAE_ERROR_REPORT_DATA_SIZE SOFAE_MK_ERROR(0x0005) +#define SOFAE_ERROR_PARSE_CONFIGURATIONS SOFAE_MK_ERROR(0x0006) +#define SOFAE_ERROR_PARSE_COMMANDLINE SOFAE_MK_ERROR(0x0007) + +#define SOFAE_ERROR_FILE_OPEN SOFAE_MK_ERROR(0x0101) +#define SOFAE_ERROR_FILE_READ SOFAE_MK_ERROR(0x0102) +#define SOFAE_ERROR_FILE_WRITE SOFAE_MK_ERROR(0x0103) + +#define SOFAE_ERROR_CONF_LOAD SOFAE_MK_ERROR(0x0201) +#define SOFAE_ERROR_CONF_NOTEXIST SOFAE_MK_ERROR(0x0202) + +#define SOFAE_ERROR_IAS_CLIENT_INIT SOFAE_MK_ERROR(0x0501) +#define SOFAE_ERROR_IAS_CLIENT_CONNECT SOFAE_MK_ERROR(0x0502) +#define SOFAE_ERROR_IAS_CLIENT_GETSIGRL SOFAE_MK_ERROR(0x0503) +#define SOFAE_ERROR_IAS_CLIENT_GETREPORT SOFAE_MK_ERROR(0x0504) +#define SOFAE_ERROR_IAS_CLIENT_UNESCAPE SOFAE_MK_ERROR(0x0505) +#define SOFAE_ERROR_IAS_LOAD_CACHED_REPORT SOFAE_MK_ERROR(0x0506) + +#define SOFAE_ERROR_SDK_UNEXPECTED SOFAE_MK_ERROR(0x0FFF) + +#define SOFAE_ERROR_CODE(rc) (rc) +#define SOFAE_ERROR_MERGE(ecallcode, retcode) ((ecallcode) | (retcode)) + +#endif // REMOTE_ATTESTATION_LIB_INCLUDE_SOFAENCLAVE_COMMON_ERROR_H_ diff --git a/demos/remote_attestation/lib/include/sofaenclave/common/log.h b/demos/remote_attestation/lib/include/sofaenclave/common/log.h new file mode 100644 index 00000000..728acf4c --- /dev/null +++ b/demos/remote_attestation/lib/include/sofaenclave/common/log.h @@ -0,0 +1,22 @@ +#ifndef REMOTE_ATTESTATION_LIB_INCLUDE_SOFAENCLAVE_COMMON_LOG_H_ +#define REMOTE_ATTESTATION_LIB_INCLUDE_SOFAENCLAVE_COMMON_LOG_H_ + +#include + +extern "C" int printf(const char *fmt, ...); + +#ifdef DEBUG +#define SOFAE_LOG_DEBUG(fmt, ...) \ + printf("[DEBUG][%s:%d] " fmt "\n", __FILE__, __LINE__, ##__VA_ARGS__) +#else +#define SOFAE_LOG_DEBUG(fmt, ...) +#endif + +#define SOFAE_LOG_INFO(fmt, ...) \ + printf("[INFO][%s:%d] " fmt "\n", __FILE__, __LINE__, ##__VA_ARGS__) +#define SOFAE_LOG_WARN(fmt, ...) \ + printf("[WARN][%s:%d] " fmt "\n", __FILE__, __LINE__, ##__VA_ARGS__) +#define SOFAE_LOG_ERROR(fmt, ...) \ + printf("[ERROR][%s:%d] " fmt "\n", __FILE__, __LINE__, ##__VA_ARGS__) + +#endif // REMOTE_ATTESTATION_LIB_INCLUDE_SOFAENCLAVE_COMMON_LOG_H_ diff --git a/demos/remote_attestation/lib/include/sofaenclave/common/type.h b/demos/remote_attestation/lib/include/sofaenclave/common/type.h new file mode 100644 index 00000000..df54e0c4 --- /dev/null +++ b/demos/remote_attestation/lib/include/sofaenclave/common/type.h @@ -0,0 +1,58 @@ +#ifndef REMOTE_ATTESTATION_LIB_INCLUDE_SOFAENCLAVE_COMMON_TYPE_H_ +#define REMOTE_ATTESTATION_LIB_INCLUDE_SOFAENCLAVE_COMMON_TYPE_H_ + +#include + +#include "./sgx_quote.h" +#include "./sgx_report.h" +#include "./sgx_tseal.h" + +#define RCAST(t, v) reinterpret_cast((v)) +#define SCAST(t, v) static_cast((v)) +#define CCAST(t, v) const_cast((v)) + +#define SOFAE_UNREFERENCED_PARAMETER(p) static_cast((p)) + +typedef uint8_t SofaeEnclaveQuote[4096]; + +/** + * report_data Input report data which will be included in quote data. + * The first 32 bytes should be the SHA256 hash value of + * the public key which is used in the RA work flow. + * nonce Nonce value to avoid replay attack. All zero to ignore it. + * spid The service provider ID, please use you real SPID, + * otherwise, IAS will return bad request when quote report. + * quote_type Maybe SGX_UNLINKABLE_SIGNATURE or SGX_LINKABLE_SIGNATURE + * quote type. + * sigrl_ptr The SigRL data buffer + * sigrl_len The total length of SigRL data + * quote Output quote structure data in binary format. + */ +typedef struct { + sgx_report_data_t report_data; // input + sgx_quote_sign_type_t quote_type; // input + sgx_spid_t spid; // input + sgx_quote_nonce_t nonce; // input + const uint8_t* sigrl_ptr; // input (optional) + uint32_t sigrl_len; // input (optional) + uint32_t quote_buf_len; // input + union { + uint8_t* as_buf; + sgx_quote_t* as_quote; + } quote; // output +} SofaeQuoteArgs; + +/** + * endpoint http://xxx.xxx.xxx.xxx: for HTTP IAS proxy server + * or https://xxx.xxx.xxx.xxx: for IAS server. Key and + * certificate must be provoided for HTTPS IAS server. + * cert Service provider certificate file path + * key Service provider private key file path + */ +typedef struct { + std::string endpoint; + std::string cert; + std::string key; +} SofaeServerCfg; + +#endif // REMOTE_ATTESTATION_LIB_INCLUDE_SOFAENCLAVE_COMMON_TYPE_H_ diff --git a/demos/remote_attestation/lib/include/sofaenclave/ra_conf.h b/demos/remote_attestation/lib/include/sofaenclave/ra_conf.h new file mode 100644 index 00000000..6cd12aa8 --- /dev/null +++ b/demos/remote_attestation/lib/include/sofaenclave/ra_conf.h @@ -0,0 +1,23 @@ +#ifndef REMOTE_ATTESTATION_LIB_INCLUDE_SOFAENCLAVE_RA_CONF_H_ +#define REMOTE_ATTESTATION_LIB_INCLUDE_SOFAENCLAVE_RA_CONF_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +std::string SofaeConfGetStr(const std::string& conf_file, const char* name, + const std::string& default_value = ""); +SofaeErrorCode SofaeConfGetStrArray(const std::string& conf_file, + const char* name, + std::vector* values); +SofaeErrorCode SofaeConfGetInt(const std::string& conf_file, const char* name, + int* value); + +#ifdef __cplusplus +} +#endif + +#endif // REMOTE_ATTESTATION_LIB_INCLUDE_SOFAENCLAVE_RA_CONF_H_ diff --git a/demos/remote_attestation/lib/include/sofaenclave/ra_device.h b/demos/remote_attestation/lib/include/sofaenclave/ra_device.h new file mode 100644 index 00000000..a82013ae --- /dev/null +++ b/demos/remote_attestation/lib/include/sofaenclave/ra_device.h @@ -0,0 +1,27 @@ +#ifndef REMOTE_ATTESTATION_LIB_INCLUDE_RA_DEVICE_H_ +#define REMOTE_ATTESTATION_LIB_INCLUDE_RA_DEVICE_H_ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "sofaenclave/common/error.h" +#include "sofaenclave/common/type.h" + +namespace sofaenclave { +namespace occlum { + +SofaeErrorCode SgxDeviceGetGroupID(sgx_epid_group_id_t* gid); +SofaeErrorCode SgxDeviceGetQuote(SofaeQuoteArgs* quote_args); + +} // namespace occlum +} // namespace sofaenclave + +#endif // REMOTE_ATTESTATION_LIB_INCLUDE_RA_DEVICE_H_ diff --git a/demos/remote_attestation/lib/include/sofaenclave/ra_ias.h b/demos/remote_attestation/lib/include/sofaenclave/ra_ias.h new file mode 100644 index 00000000..7cc60cc9 --- /dev/null +++ b/demos/remote_attestation/lib/include/sofaenclave/ra_ias.h @@ -0,0 +1,46 @@ +#ifndef REMOTE_ATTESTATION_LIB_INCLUDE_RA_IAS_H_ +#define REMOTE_ATTESTATION_LIB_INCLUDE_RA_IAS_H_ + +#include +#include + +#include "./sgx_uae_service.h" +#include "./sgx_urts.h" +#include "./sgx_utils.h" + +#include "curl/curl.h" + +#include "sofaenclave/common/error.h" +#include "sofaenclave/common/type.h" +#include "sofaenclave/ra_report.h" + +namespace sofaenclave { +namespace occlum { + +class RaIasClient { + public: + /// Connect to the HTTP IAS proxy server + explicit RaIasClient(const std::string& url); + + /// Connect to the HTTPS IAS + explicit RaIasClient(const SofaeServerCfg& ias_server); + + ~RaIasClient(); + + SofaeErrorCode GetSigRL(const sgx_epid_group_id_t* gid, std::string* sigrl); + SofaeErrorCode FetchReport(const std::string& quote, IasReport* ias_report); + + private: + void InitIasConnection(const std::string& url); + + CURL* curl_ = NULL; + curl_slist* headers_ = NULL; + std::string server_endpoint_; + + static std::mutex init_mutex_; +}; + +} // namespace occlum +} // namespace sofaenclave + +#endif // REMOTE_ATTESTATION_LIB_INCLUDE_RA_IAS_H_ diff --git a/demos/remote_attestation/lib/include/sofaenclave/ra_json.h b/demos/remote_attestation/lib/include/sofaenclave/ra_json.h new file mode 100644 index 00000000..31a79997 --- /dev/null +++ b/demos/remote_attestation/lib/include/sofaenclave/ra_json.h @@ -0,0 +1,67 @@ +#ifndef REMOTE_ATTESTATION_LIB_INCLUDE_SOFAENCLAVE_RA_JSON_H_ +#define REMOTE_ATTESTATION_LIB_INCLUDE_SOFAENCLAVE_RA_JSON_H_ + +#include +#include +#include +#include + +#include "rapidjson/document.h" + +#include "sofaenclave/common/error.h" +#include "sofaenclave/common/log.h" +#include "sofaenclave/common/type.h" + +namespace sofaenclave { +namespace occlum { + +typedef std::shared_ptr SofaeJsonDocPtr; +typedef std::map SofaeJsonConfMap; + +class JsonConfig { + public: + // Gets the singleton UnitTest object. + static JsonConfig* GetInstance(); + + // To support both rapidjson::Document and rapidjson::Value + template + static bool CheckString(const T& conf, const char* name); + template + static bool CheckArray(const T& conf, const char* name); + template + static bool CheckInt(const T& conf, const char* name); + template + static bool CheckObj(const T& conf, const char* name); + template + static std::string GetStr(const T& conf, const char* name, + const std::string& default_val = ""); + template + static SofaeErrorCode GetStrArray(const T& conf, const char* name, + std::vector* values); + template + static SofaeErrorCode GetInt(const T& conf, const char* name, int* value); + + // Load configuration files and then parse and get value(s) + std::string ConfGetStr(const std::string& conf_file, const char* name, + const std::string& default_val = ""); + SofaeErrorCode ConfGetStrArray(const std::string& conf_file, const char* name, + std::vector* values); + SofaeErrorCode ConfGetInt(const std::string& conf_file, const char* name, + int* value); + + private: + // Hide construction functions + JsonConfig() {} + JsonConfig(const JsonConfig&); + void operator=(JsonConfig const&); + + std::string GetConfigFilename(const std::string& filename); + SofaeErrorCode LoadConfiguration(const std::string& filename); + + SofaeJsonConfMap cfgs_; +}; + +} // namespace occlum +} // namespace sofaenclave + +#endif // REMOTE_ATTESTATION_LIB_INCLUDE_SOFAENCLAVE_RA_JSON_H_ diff --git a/demos/remote_attestation/lib/include/sofaenclave/ra_manager.h b/demos/remote_attestation/lib/include/sofaenclave/ra_manager.h new file mode 100644 index 00000000..f1d8c7c4 --- /dev/null +++ b/demos/remote_attestation/lib/include/sofaenclave/ra_manager.h @@ -0,0 +1,91 @@ +#ifndef REMOTE_ATTESTATION_LIB_INCLUDE_RA_MANAGER_H_ +#define REMOTE_ATTESTATION_LIB_INCLUDE_RA_MANAGER_H_ + +#include + +#include "./sgx_quote.h" +#include "./sgx_report.h" +#include "./sgx_tseal.h" +#include "./sgx_urts.h" + +#include "sofaenclave/common/error.h" +#include "sofaenclave/common/type.h" + +#include "./ra_report.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Initialization for getting enclave quote + * @param gid return GID for getting SigRL from attestation server + * @return Function run successfully or failed + * @retval 0 on success + * @retval Others when failed + */ +SofaeErrorCode InitializeQuote(sgx_epid_group_id_t* gid); + +/** + * @brief Get enclave quote for remote attestation + * @param quote_args All the input parameters required by get quote function. + * The output buffer is also in this structure. Please + * refer to the description of it in type.h header file. + * @return Function run successfully or failed + * @retval 0 on success + * @retval Others when failed + */ +SofaeErrorCode GetQuote(SofaeQuoteArgs* quote_args); + +/** + * @brief Fetch IAS report after ge. + * @param ias_server Specify the IAS server address, certificate and key. + * If HTTP proxy server is used, certificate and key are + * optional. + * @param gid input GID for getting SigRL from attestation server + * @param sigrl The string including the response from IAS + * @return Function run successfully or failed + * @retval 0 on success + * @retval Others when failed + */ +SofaeErrorCode FetchIasSigRL(const SofaeServerCfg& ias_server, + sgx_epid_group_id_t* gid, + std::string* sigrl); + +/** + * @brief Fetch IAS report after get quote by GetQuote() function. + * @param ias_server Specify the IAS server address, certificate and key. + * If HTTP proxy server is used, certificate and key are + * optional. + * @param quote The input quote data returned by GetQuote() function + * @param ias_report The output IAS report strings wrapped by IasReport + * @return Function run successfully or failed + * @retval 0 on success + * @retval Others when failed + */ +SofaeErrorCode FetchIasReport(const SofaeServerCfg& ias_server, + sgx_quote_t* quote, + SofaeIasReport* ias_report); + +/** + * @brief All together to initialize quote, get quote and then fetch IAS report. + * @param ias_server Specify the IAS server address, certificate and key. + * If HTTP proxy server is used, certificate and key are + * optional. + * @param quote_args All the input parameters required by get quote function. + * The output buffer is also in this structure. Please + * refer to the description of it in type.h header file. + * @param ias_report The output IAS report strings wrapped by IasReport + * @return Function run successfully or failed + * @retval 0 on success + * @retval Others when failed + */ +SofaeErrorCode GetQuoteAndFetchIasReport(const SofaeServerCfg& ias_server, + SofaeQuoteArgs* quote_args, + SofaeIasReport* ias_report); + +#ifdef __cplusplus +} +#endif + +#endif // REMOTE_ATTESTATION_LIB_INCLUDE_RA_MANAGER_H_ diff --git a/demos/remote_attestation/lib/include/sofaenclave/ra_report.h b/demos/remote_attestation/lib/include/sofaenclave/ra_report.h new file mode 100644 index 00000000..4cc6fd3c --- /dev/null +++ b/demos/remote_attestation/lib/include/sofaenclave/ra_report.h @@ -0,0 +1,42 @@ +#ifndef REMOTE_ATTESTATION_LIB_INCLUDE_RA_REPORT_H_ +#define REMOTE_ATTESTATION_LIB_INCLUDE_RA_REPORT_H_ + +#include + +namespace sofaenclave { +namespace occlum { + +#define DECLARE_IAS_REPORT_CLASS_MEMBER(x) \ + public: \ + std::string& x(); \ + const std::string& x() const; \ + void set_##x(const std::string& value); \ + void set_##x(const char* value); \ + void set_##x(const char* value, size_t size); \ + \ + private: \ + std::string x##_ + +class IasReport { + public: + IasReport() {} + ~IasReport() {} + + DECLARE_IAS_REPORT_CLASS_MEMBER(b64_signature); + DECLARE_IAS_REPORT_CLASS_MEMBER(signing_cert); + DECLARE_IAS_REPORT_CLASS_MEMBER(advisory_url); + DECLARE_IAS_REPORT_CLASS_MEMBER(advisory_ids); + DECLARE_IAS_REPORT_CLASS_MEMBER(response_body); + DECLARE_IAS_REPORT_CLASS_MEMBER(epid_pseudonym); + DECLARE_IAS_REPORT_CLASS_MEMBER(quote_status); + DECLARE_IAS_REPORT_CLASS_MEMBER(b16_platform_info_blob); + DECLARE_IAS_REPORT_CLASS_MEMBER(b64_quote_body); +}; + +} // namespace occlum +} // namespace sofaenclave + +// For coding convenience +using SofaeIasReport = sofaenclave::occlum::IasReport; + +#endif // REMOTE_ATTESTATION_LIB_INCLUDE_RA_REPORT_H_ diff --git a/demos/remote_attestation/lib/src/ra_device.cpp b/demos/remote_attestation/lib/src/ra_device.cpp new file mode 100644 index 00000000..9804d651 --- /dev/null +++ b/demos/remote_attestation/lib/src/ra_device.cpp @@ -0,0 +1,67 @@ +#include + +#include "sofaenclave/common/error.h" +#include "sofaenclave/common/log.h" +#include "sofaenclave/ra_device.h" + +namespace sofaenclave { +namespace occlum { + +#define SGXIOC_GET_EPID_GROUP_ID _IOR('s', 1, sgx_epid_group_id_t) +#define SGXIOC_GEN_QUOTE _IOWR('s', 2, SofaeQuoteArgs) + +constexpr char kSgxDeviceName[] = "/dev/sgx"; + +SofaeErrorCode SgxDeviceGetGroupID(sgx_epid_group_id_t* gid) { + int sgx_fd; + if ((sgx_fd = open(kSgxDeviceName, O_RDONLY)) < 0) { + SOFAE_LOG_ERROR("Fail to open %s", kSgxDeviceName); + return SOFAE_ERROR_FILE_OPEN; + } + + SofaeErrorCode ret = SOFAE_SUCCESS; + if (ioctl(sgx_fd, SGXIOC_GET_EPID_GROUP_ID, gid) < 0) { + SOFAE_LOG_ERROR("Fail to get group id from %s", kSgxDeviceName); + ret = SOFAE_ERROR_SDK_UNEXPECTED; + } + + close(sgx_fd); + return ret; +} + +SofaeErrorCode SgxDeviceGetQuote(SofaeQuoteArgs* quote_args) { + int sgx_fd; + if ((sgx_fd = open(kSgxDeviceName, O_RDONLY)) < 0) { + SOFAE_LOG_ERROR("Fail to open %s", kSgxDeviceName); + return SOFAE_ERROR_FILE_OPEN; + } + + SofaeErrorCode ret = SOFAE_SUCCESS; + int count = 3; + while (count--) { + if (ioctl(sgx_fd, SGXIOC_GEN_QUOTE, quote_args) == 0) { + uint32_t signature_len = quote_args->quote.as_quote->signature_len; + SOFAE_LOG_DEBUG("SgxDeviceGetQuote length=%ld", signature_len); + if (signature_len == 0) { + SOFAE_LOG_ERROR("Invalid quote from %s", kSgxDeviceName); + ret = SOFAE_ERROR_SDK_UNEXPECTED; + } + break; + } + else if (errno != EAGAIN) { + SOFAE_LOG_ERROR("Fail to get quote from %s", kSgxDeviceName); + ret = SOFAE_ERROR_SDK_UNEXPECTED; + break; + } + else { + SOFAE_LOG_WARN("/dev/sgx is temporarily busy. Try again after 1s."); + sleep(1); + } + } + + close(sgx_fd); + return ret; +} + +} // namespace occlum +} // namespace sofaenclave diff --git a/demos/remote_attestation/lib/src/ra_ias.cpp b/demos/remote_attestation/lib/src/ra_ias.cpp new file mode 100644 index 00000000..284c7bd5 --- /dev/null +++ b/demos/remote_attestation/lib/src/ra_ias.cpp @@ -0,0 +1,293 @@ +#include +#include +#include + +#include "sofaenclave/common/error.h" +#include "sofaenclave/common/log.h" +#include "sofaenclave/common/type.h" +#include "sofaenclave/ra_json.h" +#include "sofaenclave/ra_ias.h" + +// use cppcodec/base64 +#include "cppcodec/base64_rfc4648.hpp" +using base64 = cppcodec::base64_rfc4648; + +namespace sofaenclave { +namespace occlum { + +constexpr char kStrEpidPseudonym[] = "epidPseudonym"; +constexpr char kStrQuoteStatus[] = "isvEnclaveQuoteStatus"; +constexpr char kStrPlatform[] = "platformInfoBlob"; +constexpr char kStrQuoteBody[] = "isvEnclaveQuoteBody"; +constexpr char kStrHeaderSig[] = "x-iasreport-signature:"; +constexpr char kStrHeaderCA[] = "x-iasreport-signing-certificate:"; +constexpr char kStrHeaderAdvisoryURL[] = "advisory-url:"; +constexpr char kStrHeaderAdvisoryIDs[] = "advisory-ids:"; + +typedef struct { + std::string b64_sigrl; +} SofaeIasSigrl; + +static std::string GetHeaderValue(const char *header, const char *name) { + std::string header_str = header; + std::string ending("\n\r"); + + // Name: value\r\n + std::size_t pos_start = header_str.find_first_of(" "); + std::size_t pos_end = header_str.find_first_of("\r\n"); + if ((pos_start != std::string::npos) && (pos_end != std::string::npos)) { + return header_str.substr(pos_start + 1, pos_end - pos_start - 1); + } else { + return std::string(""); + } +} + +static size_t ParseSigrlResponseBody(const void* contents, size_t size, + size_t nmemb, void* response) { + size_t content_length = size * nmemb; + SofaeIasSigrl *sigrl = RCAST(SofaeIasSigrl *, response); + + if (content_length == 0) { + sigrl->b64_sigrl.clear(); + SOFAE_LOG_DEBUG("GetSigRL: Empty"); + } else { + sigrl->b64_sigrl.assign(RCAST(const char *, contents), content_length); + SOFAE_LOG_DEBUG("GetSigRL: %s", sigrl->b64_sigrl.c_str()); + } + return content_length; +} + +static size_t ParseSigrlResponseHeader(const void* contents, size_t size, + size_t nmemb, void* response) { + size_t len = size * nmemb; + const char *header = RCAST(const char *, contents); + + SOFAE_LOG_DEBUG("IAS Get SigRL %s", header); + return len; +} + +static size_t ParseReportResponseBody(const void* contents, size_t size, + size_t nmemb, void* response) { + const char *body = RCAST(const char *, contents); + size_t content_length = size * nmemb; + IasReport *report = RCAST(IasReport *, response); + + report->set_response_body(body, content_length); + + rapidjson::Document doc; + if (doc.Parse(body).HasParseError()) { + SOFAE_LOG_ERROR("Fail to parse report response body"); + } else { + report->set_epid_pseudonym(JsonConfig::GetStr(doc, kStrEpidPseudonym)); + report->set_quote_status(JsonConfig::GetStr(doc, kStrQuoteStatus)); + report->set_b16_platform_info_blob(JsonConfig::GetStr(doc, kStrPlatform)); + report->set_b64_quote_body(JsonConfig::GetStr(doc, kStrQuoteBody)); + } + + return content_length; +} + +static size_t ParseReportResponseHeader(const void* contents, size_t size, + size_t nmemb, void* response) { + size_t len = size * nmemb; + const char *header = RCAST(const char *, contents); + IasReport *report = RCAST(IasReport *, response); + + if (strncmp(header, kStrHeaderSig, strlen(kStrHeaderSig)) == 0) { + report->set_b64_signature(GetHeaderValue(header, kStrHeaderSig)); + } else if (strncmp(header, kStrHeaderCA, strlen(kStrHeaderCA)) == 0) { + report->set_signing_cert(GetHeaderValue(header, kStrHeaderCA)); + } else if (strncmp(header, kStrHeaderAdvisoryURL, + strlen(kStrHeaderAdvisoryURL)) == 0) { + report->set_advisory_url(GetHeaderValue(header, kStrHeaderAdvisoryURL)); + } else if (strncmp(header, kStrHeaderAdvisoryIDs, + strlen(kStrHeaderAdvisoryIDs)) == 0) { + report->set_advisory_ids(GetHeaderValue(header, kStrHeaderAdvisoryIDs)); + } + return len; +} + +std::mutex RaIasClient::init_mutex_; + +void RaIasClient::InitIasConnection(const std::string& endpoint) { + if (endpoint.empty()) { + curl_ = NULL; + return; + } + + // curl_global_init is not multithreads safe function. It's suggested to + // call it in main thread. Here we just add lock to make sure safety, but + // don't consider the performance, as multithreads is not common usecase. + { + std::lock_guard lock(init_mutex_); + curl_global_init(CURL_GLOBAL_ALL); + } + + curl_ = curl_easy_init(); + if (!curl_) { + return; + } + +#ifdef DEBUG + /* set libcurl verbose */ + curl_easy_setopt(curl_, CURLOPT_VERBOSE, 1L); +#endif + + /* set the common header */ + headers_ = curl_slist_append(NULL, "Accept: application/json"); + headers_ = curl_slist_append(headers_, "Content-Type: application/json"); + curl_easy_setopt(curl_, CURLOPT_HTTPHEADER, headers_); + curl_easy_setopt(curl_, CURLOPT_USERAGENT, "sgx-sp/1.0"); + + /* set commom option */ + curl_easy_setopt(curl_, CURLOPT_FORBID_REUSE, 1L); + curl_easy_setopt(curl_, CURLOPT_NOSIGNAL, 1L); + curl_easy_setopt(curl_, CURLOPT_TIMEOUT, 60L); + curl_easy_setopt(curl_, CURLOPT_CONNECTTIMEOUT, 10L); + + server_endpoint_ = endpoint; +} + +RaIasClient::RaIasClient(const std::string& endpoint) { + InitIasConnection(endpoint); +} + +RaIasClient::RaIasClient(const SofaeServerCfg& ias_server) { + // Configure the other normal settings firstly. + InitIasConnection(ias_server.endpoint); + + // Check the HTTPS server addr and set the cert/key settings + if (curl_ && (ias_server.endpoint.find("https://") != std::string::npos)) { + const char *ias_cert_key_type = "PEM"; + SOFAE_LOG_DEBUG("IAS cert: %s", ias_server.cert.c_str()); + SOFAE_LOG_DEBUG("IAS key: %s", ias_server.key.c_str()); + + curl_easy_setopt(curl_, CURLOPT_SSL_VERIFYPEER, 0L); + curl_easy_setopt(curl_, CURLOPT_SSL_VERIFYHOST, 0L); + curl_easy_setopt(curl_, CURLOPT_SSLCERT, ias_server.cert.c_str()); + curl_easy_setopt(curl_, CURLOPT_SSLKEY, ias_server.key.c_str()); + curl_easy_setopt(curl_, CURLOPT_SSLCERTTYPE, ias_cert_key_type); + curl_easy_setopt(curl_, CURLOPT_SSLKEYTYPE, ias_cert_key_type); + } +} + +RaIasClient::~RaIasClient() { + if (headers_) { + curl_slist_free_all(headers_); + } + if (curl_) { + curl_easy_cleanup(curl_); + } + + // add lock for multi-threads safety + { + std::lock_guard lock(init_mutex_); + curl_global_cleanup(); + } +} + +SofaeErrorCode RaIasClient::GetSigRL(const sgx_epid_group_id_t *gid, + std::string *sigrl) { + if (!curl_) { + SOFAE_LOG_ERROR("IAS client is not initialized"); + return SOFAE_ERROR_IAS_CLIENT_INIT; + } + + /* Set the URL */ + std::string url = server_endpoint_ + "/attestation/sgx/v3/sigrl/"; + std::vector tmp_gid_vec(sizeof(sgx_epid_group_id_t) * 2, 0); + snprintf(tmp_gid_vec.data(), tmp_gid_vec.size(), "%02X%02X%02X%02X", gid[3], + gid[2], gid[1], gid[0]); + url += std::string(tmp_gid_vec.data()); + SOFAE_LOG_DEBUG("URL: %s", url.c_str()); + curl_easy_setopt(curl_, CURLOPT_URL, url.c_str()); + + /* Set the sigrl request header and body handler function and data */ + SofaeIasSigrl ias_sigrl; + curl_easy_setopt(curl_, CURLOPT_WRITEFUNCTION, ParseSigrlResponseBody); + curl_easy_setopt(curl_, CURLOPT_HEADERFUNCTION, ParseSigrlResponseHeader); + curl_easy_setopt(curl_, CURLOPT_WRITEDATA, RCAST(void *, &ias_sigrl)); + curl_easy_setopt(curl_, CURLOPT_WRITEHEADER, RCAST(void *, &ias_sigrl)); + + CURLcode rc = curl_easy_perform(curl_); + if (rc != CURLE_OK) { + SOFAE_LOG_ERROR("Fail to connect server: %s\n", curl_easy_strerror(rc)); + return SOFAE_ERROR_IAS_CLIENT_CONNECT; + } + + if (!ias_sigrl.b64_sigrl.empty()) { + std::vector sigrl_vec; + try { + sigrl_vec = base64::decode(ias_sigrl.b64_sigrl); + } catch (std::exception& e) { + SOFAE_LOG_ERROR("Cannot decode base64 sigrl: %s", e.what()); + return SOFAE_ERROR_IAS_CLIENT_GETSIGRL; + } + sigrl->assign(RCAST(const char *, sigrl_vec.data()), sigrl_vec.size()); + } + return SOFAE_SUCCESS; +} + +SofaeErrorCode RaIasClient::FetchReport(const std::string& quote, + IasReport *ias_report) { + /* should not be empty is not to use cache */ + if (quote.empty()) { + SOFAE_LOG_ERROR("Invalid base64 quote value"); + return SOFAE_ERROR_PARAMETERS; + } + + if (!curl_) { + SOFAE_LOG_ERROR("IAS client is not initialized!"); + return SOFAE_ERROR_IAS_CLIENT_INIT; + } + + /* Set the report url */ + std::string url = server_endpoint_ + "/attestation/sgx/v3/report"; + SOFAE_LOG_DEBUG("URL: %s", url.c_str()); + curl_easy_setopt(curl_, CURLOPT_URL, url.c_str()); + + /* Set the post data */ + SOFAE_LOG_DEBUG("Quote length: %ld", quote.length()); + std::string b64_quote = base64::encode(RCAST(const char *, quote.c_str()), + SCAST(size_t, quote.length())); + SOFAE_LOG_DEBUG("QUTEO[%lu]: %s", b64_quote.length(), b64_quote.c_str()); + std::string post_data = "{\"isvEnclaveQuote\": \""; + post_data += b64_quote; + post_data += "\"}"; + curl_easy_setopt(curl_, CURLOPT_POSTFIELDS, post_data.c_str()); + + /* Set the report request header and body handler function and data */ + curl_easy_setopt(curl_, CURLOPT_WRITEFUNCTION, ParseReportResponseBody); + curl_easy_setopt(curl_, CURLOPT_HEADERFUNCTION, ParseReportResponseHeader); + curl_easy_setopt(curl_, CURLOPT_WRITEDATA, RCAST(void *, ias_report)); + curl_easy_setopt(curl_, CURLOPT_WRITEHEADER, RCAST(void *, ias_report)); + + CURLcode rc = curl_easy_perform(curl_); + if (rc != CURLE_OK) { + SOFAE_LOG_ERROR("Fail to connect server: %s\n", curl_easy_strerror(rc)); + return SOFAE_ERROR_IAS_CLIENT_CONNECT; + } + + /* deal with the escaped certificates */ + std::string signing_cert = ias_report->signing_cert(); + if (!signing_cert.empty()) { + int unescape_len = 0; + char *p_unescape = curl_easy_unescape(curl_, signing_cert.data(), + signing_cert.length(), &unescape_len); + if (p_unescape && unescape_len) { + ias_report->set_signing_cert(p_unescape, unescape_len); + curl_free(p_unescape); + } else { + SOFAE_LOG_ERROR("Fail to convert the escaped certificate in response."); + return SOFAE_ERROR_IAS_CLIENT_UNESCAPE; + } + } else { + SOFAE_LOG_ERROR("Fail to get quote report from IAS"); + return SOFAE_ERROR_IAS_CLIENT_GETREPORT; + } + + return SOFAE_SUCCESS; +} + +} // namespace occlum +} // namespace sofaenclave diff --git a/demos/remote_attestation/lib/src/ra_json.cpp b/demos/remote_attestation/lib/src/ra_json.cpp new file mode 100644 index 00000000..bdfabee2 --- /dev/null +++ b/demos/remote_attestation/lib/src/ra_json.cpp @@ -0,0 +1,251 @@ +#include +#include +#include +#include + +#include "sofaenclave/common/error.h" +#include "sofaenclave/common/log.h" +#include "sofaenclave/ra_json.h" + +namespace sofaenclave { +namespace occlum { + +static SofaeErrorCode FsReadString(const std::string& filename, + std::string* str) { + std::ifstream ifs(filename, std::ios::binary | std::ios::in); + if (!ifs) { + SOFAE_LOG_ERROR("Fail to open file \"%s\"\n", filename.c_str()); + return SOFAE_ERROR_FILE_OPEN; + } + + ifs.seekg(0, std::ios::end); + int length = ifs.tellg(); + ifs.seekg(0, std::ios::beg); + + std::vector buf(length); + ifs.read(buf.data(), length); + if (ifs.fail()) { + SOFAE_LOG_ERROR("Fail to read file \"%s\"\n", filename.c_str()); + return SOFAE_ERROR_FILE_READ; + } + + str->assign(buf.data(), length); + return SOFAE_SUCCESS; +} + +static bool FsFileExists(const std::string& filename) { + std::ifstream ifs(filename, std::ios::binary | std::ios::in); + return ifs.good(); +} + +JsonConfig* JsonConfig::GetInstance() { + static JsonConfig instance; + return &instance; +} + +template +bool JsonConfig::CheckString(const T& conf, const char* name) { + if (!conf.HasMember(name) || !conf[name].IsString()) { + SOFAE_LOG_ERROR("%s is missed or not string in config file", name); + return false; + } + return true; +} + +template +bool JsonConfig::CheckArray(const T& conf, const char* name) { + if (!conf.HasMember(name) || !conf[name].IsArray()) { + SOFAE_LOG_ERROR("%s is missed or not array in config file", name); + return false; + } + return true; +} + +template +bool JsonConfig::CheckInt(const T& conf, const char* name) { + if (!conf.HasMember(name) || !conf[name].IsInt()) { + SOFAE_LOG_ERROR("%s is missed or not integer in config file", name); + return false; + } + return true; +} + +template +bool JsonConfig::CheckObj(const T& conf, const char* name) { + if (!conf.HasMember(name) || !conf[name].IsObject()) { + SOFAE_LOG_ERROR("%s is missed or not object in config file", name); + return false; + } + return true; +} + +template +std::string JsonConfig::GetStr(const T& conf, const char* name, + const std::string& default_val) { + if (CheckString(conf, name)) { + std::string value = conf[name].GetString(); + SOFAE_LOG_DEBUG("%s=%s", name, value.c_str()); + return value; + } else { + SOFAE_LOG_DEBUG("Not string type, %s=%s[default]", name, default_val); + return default_val; + } +} + +template +SofaeErrorCode JsonConfig::GetStrArray(const T& conf, const char* name, + std::vector* values) { + if (CheckArray(conf, name)) { + const rapidjson::Value& val_array = conf[name]; + size_t count = val_array.Size(); + for (size_t i = 0; i < count; i++) { + if (val_array[i].IsString()) { + std::string val_str = val_array[i].GetString(); + SOFAE_LOG_DEBUG("%s[%ld]=%s", name, i, val_str.c_str()); + values->push_back(val_str); + } else { + SOFAE_LOG_ERROR("Invalid string type in Array"); + return SOFAE_ERROR_PARSE_CONFIGURATIONS; + } + } + } else { + SOFAE_LOG_DEBUG("Invalid Array type"); + return SOFAE_ERROR_PARSE_CONFIGURATIONS; + } + return SOFAE_SUCCESS; +} + +template +SofaeErrorCode JsonConfig::GetInt(const T& conf, const char* name, int* value) { + if (!CheckInt(conf, name)) { + SOFAE_LOG_ERROR("Not integer type: %s", name); + return SOFAE_ERROR_PARSE_CONFIGURATIONS; + } + + *value = conf[name].GetInt(); + SOFAE_LOG_DEBUG("%s=%d", name, *value); + return SOFAE_SUCCESS; +} + +std::string JsonConfig::GetConfigFilename(const std::string& filename) { + // First priority, the absolute path filename or file in current directory + if (FsFileExists(filename)) { + SOFAE_LOG_DEBUG("Configuration file: %s", filename.c_str()); + return filename; + } + + // Finally, try to find configuration file in /etc directory + std::string etcpath = "/etc/"; + etcpath += filename; + if (FsFileExists(etcpath)) { + SOFAE_LOG_DEBUG("Configuration file: %s", etcpath.c_str()); + return etcpath; + } + + // If cannot find configuration file, return empty string + SOFAE_LOG_ERROR("Cannot find configuration file: %s", filename.c_str()); + return ""; +} + +SofaeErrorCode JsonConfig::LoadConfiguration(const std::string& filename) { + if (filename.empty()) { + SOFAE_LOG_ERROR("Empty configuration file name"); + return SOFAE_ERROR_CONF_NOTEXIST; + } + + std::string config_file = GetConfigFilename(filename); + if (config_file.empty()) { + SOFAE_LOG_ERROR("Fail to find configuration file"); + return SOFAE_ERROR_CONF_NOTEXIST; + } + + std::string config_str; + if (FsReadString(config_file, &config_str) != SOFAE_SUCCESS) { + SOFAE_LOG_ERROR("Fail to read configuration file"); + return SOFAE_ERROR_PARSE_CONFIGURATIONS; + } + + SofaeJsonDocPtr doc(new rapidjson::Document); + if (doc.get()->Parse(config_str.data()).HasParseError()) { + SOFAE_LOG_ERROR("Fail to parse json configration file"); + return SOFAE_ERROR_PARSE_CONFIGURATIONS; + } + + cfgs_.emplace(filename, doc); + SOFAE_LOG_DEBUG("Load configuration file %s successfully", filename.c_str()); + return SOFAE_SUCCESS; +} + +std::string JsonConfig::ConfGetStr(const std::string& conf_file, + const char* name, + const std::string& default_val) { + SOFAE_LOG_DEBUG("Get %s from %s", name, conf_file.c_str()); + + if (cfgs_.find(conf_file) == cfgs_.end()) { + if (LoadConfiguration(conf_file) != SOFAE_SUCCESS) { + SOFAE_LOG_DEBUG("Load config failed, %s=%s[default]", name, default_val); + return default_val; + } + } + + return GetStr(*cfgs_[conf_file].get(), name, default_val); +} + +SofaeErrorCode JsonConfig::ConfGetStrArray(const std::string& conf_file, + const char* name, + std::vector* values) { + SOFAE_LOG_DEBUG("Get %s from %s", name, conf_file.c_str()); + + if (cfgs_.find(conf_file) == cfgs_.end()) { + if (LoadConfiguration(conf_file) != SOFAE_SUCCESS) { + SOFAE_LOG_DEBUG("Fail to load configuration file"); + return SOFAE_ERROR_PARSE_CONFIGURATIONS; + } + } + + return GetStrArray(*cfgs_[conf_file].get(), name, values); +} + +SofaeErrorCode JsonConfig::ConfGetInt(const std::string& conf_file, + const char* name, int* value) { + SOFAE_LOG_DEBUG("Get %s from %s", name, conf_file.c_str()); + + if (cfgs_.find(conf_file) == cfgs_.end()) { + if (LoadConfiguration(conf_file) != SOFAE_SUCCESS) { + SOFAE_LOG_ERROR("Fail to load configuration file"); + return SOFAE_ERROR_PARSE_CONFIGURATIONS; + } + } + + return GetInt(*cfgs_[conf_file].get(), name, value); +} + +} // namespace occlum +} // namespace sofaenclave + +#ifdef __cplusplus +extern "C" { +#endif + +std::string SofaeConfGetStr(const std::string& conf_file, const char* name, + const std::string& default_val) { + return sofaenclave::occlum::JsonConfig::GetInstance()->ConfGetStr( + conf_file, name, default_val); +} + +SofaeErrorCode SofaeConfGetStrArray(const std::string& conf_file, + const char* name, + std::vector* values) { + return sofaenclave::occlum::JsonConfig::GetInstance()->ConfGetStrArray( + conf_file, name, values); +} + +SofaeErrorCode SofaeConfGetInt(const std::string& conf_file, const char* name, + int* value) { + return sofaenclave::occlum::JsonConfig::GetInstance()->ConfGetInt( + conf_file, name, value); +} + +#ifdef __cplusplus +} +#endif diff --git a/demos/remote_attestation/lib/src/ra_manager.cpp b/demos/remote_attestation/lib/src/ra_manager.cpp new file mode 100644 index 00000000..500d91b1 --- /dev/null +++ b/demos/remote_attestation/lib/src/ra_manager.cpp @@ -0,0 +1,88 @@ +#include +#include +#include +#include + +#include "./sgx_quote.h" +#include "./sgx_tseal.h" + +#include "sofaenclave/common/error.h" +#include "sofaenclave/common/log.h" +#include "sofaenclave/common/type.h" +#include "sofaenclave/ra_device.h" +#include "sofaenclave/ra_ias.h" +#include "sofaenclave/ra_manager.h" + +// use cppcodec/base64 +#include "cppcodec/base64_rfc4648.hpp" +using base64 = cppcodec::base64_rfc4648; + +#ifdef __cplusplus +extern "C" { +#endif + +SofaeErrorCode InitializeQuote(sgx_epid_group_id_t* gid) { + return sofaenclave::occlum::SgxDeviceGetGroupID(gid); +} + +SofaeErrorCode GetQuote(SofaeQuoteArgs* quote_args) { + if (!quote_args->quote.as_buf || (quote_args->quote_buf_len == 0)) { + SOFAE_LOG_ERROR("Invalid quote buffer or len"); + return SOFAE_ERROR_PARAMETERS; + } + + return sofaenclave::occlum::SgxDeviceGetQuote(quote_args); +} + +SofaeErrorCode FetchIasSigRL(const SofaeServerCfg& ias_server, + sgx_epid_group_id_t* gid, + std::string* sigrl) { + sofaenclave::occlum::RaIasClient ias_client(ias_server); + return ias_client.GetSigRL(gid, sigrl); +} + +SofaeErrorCode FetchIasReport(const SofaeServerCfg& ias_server, + sgx_quote_t* quote, + SofaeIasReport* ias_report) { + sofaenclave::occlum::RaIasClient ias_client(ias_server); + std::string quote_str(RCAST(char*, quote), + sizeof(sgx_quote_t) + quote->signature_len); + return ias_client.FetchReport(quote_str, ias_report); +} + +SofaeErrorCode GetQuoteAndFetchIasReport(const SofaeServerCfg& ias_server, + SofaeQuoteArgs* quote_args, + SofaeIasReport* ias_report) { + // Initialize the quote firstly + sgx_epid_group_id_t gid = {0}; + SofaeErrorCode ret = InitializeQuote(&gid); + if (ret != SOFAE_SUCCESS) { + return ret; + } + + // If there is no SigRL, try to fetch it. + if (!quote_args->sigrl_ptr || (quote_args->sigrl_len == 0)) { + std::string sigrl_str; + ret = FetchIasSigRL(ias_server, &gid, &sigrl_str); + if (ret != SOFAE_SUCCESS) { + return ret; + } + if (!sigrl_str.empty()) { + quote_args->sigrl_ptr = RCAST(const uint8_t *, sigrl_str.data()); + quote_args->sigrl_len = sigrl_str.length(); + } + } + + // Get the quote, assuming quote buffer is allocated out of this function + ret = GetQuote(quote_args); + if (ret != SOFAE_SUCCESS) { + return ret; + } + + // Fetch the IAS report based on the quote output buffer + return FetchIasReport(ias_server, quote_args->quote.as_quote, ias_report); +} + +#ifdef __cplusplus +} +#endif diff --git a/demos/remote_attestation/lib/src/ra_report.cpp b/demos/remote_attestation/lib/src/ra_report.cpp new file mode 100644 index 00000000..62f67b51 --- /dev/null +++ b/demos/remote_attestation/lib/src/ra_report.cpp @@ -0,0 +1,26 @@ +#include + +#include "sofaenclave/ra_report.h" + +namespace sofaenclave { +namespace occlum { + +#define IMPLEMENT_IAS_REPORT_CLASS_MEMBER(x); \ + std::string& IasReport::x() { return x##_; } \ + const std::string& IasReport::x() const { return x##_; } \ + void IasReport::set_##x(const std::string& value) { x##_ = value; } \ + void IasReport::set_##x(const char* value) { x##_ = value; } \ + void IasReport::set_##x(const char* value, size_t size) { x##_ = value; } + +IMPLEMENT_IAS_REPORT_CLASS_MEMBER(b64_signature); +IMPLEMENT_IAS_REPORT_CLASS_MEMBER(signing_cert); +IMPLEMENT_IAS_REPORT_CLASS_MEMBER(advisory_url); +IMPLEMENT_IAS_REPORT_CLASS_MEMBER(advisory_ids); +IMPLEMENT_IAS_REPORT_CLASS_MEMBER(response_body); +IMPLEMENT_IAS_REPORT_CLASS_MEMBER(epid_pseudonym); +IMPLEMENT_IAS_REPORT_CLASS_MEMBER(quote_status); +IMPLEMENT_IAS_REPORT_CLASS_MEMBER(b16_platform_info_blob); +IMPLEMENT_IAS_REPORT_CLASS_MEMBER(b64_quote_body); + +} // namespace occlum +} // namespace sofaenclave diff --git a/demos/remote_attestation/run_on_occlum.sh b/demos/remote_attestation/run_on_occlum.sh new file mode 100755 index 00000000..d247cb51 --- /dev/null +++ b/demos/remote_attestation/run_on_occlum.sh @@ -0,0 +1,26 @@ +#!/usr/bin/env bash + +THISDIR="$(dirname $(readlink -f $0))" +DEMPOAPP="remote_attestation_demo" + +# 1. Init Occlum Workspace +rm -rf $THISDIR/occlum_workspace && \ +mkdir -p $THISDIR/occlum_workspace && \ +cd occlum_workspace && +occlum init || exit 1 + +# 2. Copy files into Occlum Workspace and Build +mkdir -p image/etc +mkdir -p image/etc/certs +cp /etc/resolv.conf image/etc +cp /etc/hosts image/etc +cp $THISDIR/conf/ra_config.example.json image/etc/ra_config.json +cp $THISDIR/conf/certs/* image/etc/certs +cp $THISDIR/build/$DEMPOAPP image/bin +cp /usr/local/occlum/x86_64-linux-musl/lib/libssl.so.1.1 image/lib +cp /usr/local/occlum/x86_64-linux-musl/lib/libcrypto.so.1.1 image/lib +cp /usr/local/occlum/x86_64-linux-musl/lib/libcurl.so.4 image/lib +occlum build + +# 3. Run application +occlum run /bin/$DEMPOAPP