Add SGX remote attestaion demo

This demo consists of
1. An user-friendly library for getting Intel Attestation Service (IAS) report.
2. A application that uses this library to get IAS report.

This demo requires obtaining service provider certificate/ID from Intel.
This commit is contained in:
Junxian Xiao 2019-11-28 21:07:22 +08:00 committed by Tate, Hongliang Tian
parent b9fa937504
commit 2052447950
23 changed files with 1444 additions and 0 deletions

@ -2,10 +2,23 @@
This directory contains sample projects that demonstrate how Occlum can be used to build and run user applications. 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_c/`: A sample C project built with Makefile/CMake.
* `hello_cc/`: 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). * `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). * `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). * `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. * `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/). * `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.

3
demos/remote_attestation/.gitignore vendored Normal file

@ -0,0 +1,3 @@
build
deps
occlum_workspace

@ -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})

@ -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
```

@ -0,0 +1,26 @@
#ifndef REMOTE_ATTESTATION_RA_CONFIG_H_
#define REMOTE_ATTESTATION_RA_CONFIG_H_
#include <string>
#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_

@ -0,0 +1,66 @@
#include <cstring>
#include <string>
#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<uint8_t> HexStr2Bytes(const uint8_t* str) {
int len = strlen(RCAST(const char *, str)) / 2;
std::vector<uint8_t> 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 *, &quote);
quote_args.quote_buf_len = sizeof(SofaeEnclaveQuote);
std::vector<uint8_t> spid_vec = HexStr2Bytes(
RCAST(const uint8_t *, spid_str.c_str()));
std::memcpy(RCAST(void *, &quote_args.spid.id),
RCAST(const void *, spid_vec.data()), sizeof(quote_args.spid));
sofaenclave::occlum::IasReport ias_report;
int ret = GetQuoteAndFetchIasReport(ias_server, &quote_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;
}

@ -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": ""
}

@ -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)

@ -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_

@ -0,0 +1,22 @@
#ifndef REMOTE_ATTESTATION_LIB_INCLUDE_SOFAENCLAVE_COMMON_LOG_H_
#define REMOTE_ATTESTATION_LIB_INCLUDE_SOFAENCLAVE_COMMON_LOG_H_
#include <string>
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_

@ -0,0 +1,58 @@
#ifndef REMOTE_ATTESTATION_LIB_INCLUDE_SOFAENCLAVE_COMMON_TYPE_H_
#define REMOTE_ATTESTATION_LIB_INCLUDE_SOFAENCLAVE_COMMON_TYPE_H_
#include <string>
#include "./sgx_quote.h"
#include "./sgx_report.h"
#include "./sgx_tseal.h"
#define RCAST(t, v) reinterpret_cast<t>((v))
#define SCAST(t, v) static_cast<t>((v))
#define CCAST(t, v) const_cast<t>((v))
#define SOFAE_UNREFERENCED_PARAMETER(p) static_cast<void>((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:<port> for HTTP IAS proxy server
* or https://xxx.xxx.xxx.xxx:<port> 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_

@ -0,0 +1,23 @@
#ifndef REMOTE_ATTESTATION_LIB_INCLUDE_SOFAENCLAVE_RA_CONF_H_
#define REMOTE_ATTESTATION_LIB_INCLUDE_SOFAENCLAVE_RA_CONF_H_
#include <string>
#include <vector>
#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<std::string>* 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_

@ -0,0 +1,27 @@
#ifndef REMOTE_ATTESTATION_LIB_INCLUDE_RA_DEVICE_H_
#define REMOTE_ATTESTATION_LIB_INCLUDE_RA_DEVICE_H_
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <termios.h>
#include <unistd.h>
#include <string>
#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_

@ -0,0 +1,46 @@
#ifndef REMOTE_ATTESTATION_LIB_INCLUDE_RA_IAS_H_
#define REMOTE_ATTESTATION_LIB_INCLUDE_RA_IAS_H_
#include <mutex>
#include <string>
#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_

@ -0,0 +1,67 @@
#ifndef REMOTE_ATTESTATION_LIB_INCLUDE_SOFAENCLAVE_RA_JSON_H_
#define REMOTE_ATTESTATION_LIB_INCLUDE_SOFAENCLAVE_RA_JSON_H_
#include <map>
#include <memory>
#include <string>
#include <vector>
#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<rapidjson::Document> SofaeJsonDocPtr;
typedef std::map<std::string, SofaeJsonDocPtr> SofaeJsonConfMap;
class JsonConfig {
public:
// Gets the singleton UnitTest object.
static JsonConfig* GetInstance();
// To support both rapidjson::Document and rapidjson::Value
template <typename T>
static bool CheckString(const T& conf, const char* name);
template <typename T>
static bool CheckArray(const T& conf, const char* name);
template <typename T>
static bool CheckInt(const T& conf, const char* name);
template <typename T>
static bool CheckObj(const T& conf, const char* name);
template <typename T>
static std::string GetStr(const T& conf, const char* name,
const std::string& default_val = "");
template <typename T>
static SofaeErrorCode GetStrArray(const T& conf, const char* name,
std::vector<std::string>* values);
template <typename T>
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<std::string>* 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_

@ -0,0 +1,91 @@
#ifndef REMOTE_ATTESTATION_LIB_INCLUDE_RA_MANAGER_H_
#define REMOTE_ATTESTATION_LIB_INCLUDE_RA_MANAGER_H_
#include <string>
#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_

@ -0,0 +1,42 @@
#ifndef REMOTE_ATTESTATION_LIB_INCLUDE_RA_REPORT_H_
#define REMOTE_ATTESTATION_LIB_INCLUDE_RA_REPORT_H_
#include <string>
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_

@ -0,0 +1,67 @@
#include <string>
#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

@ -0,0 +1,293 @@
#include <cstring>
#include <string>
#include <vector>
#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<std::mutex> 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<std::mutex> 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<char> 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<uint8_t> 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

@ -0,0 +1,251 @@
#include <fstream>
#include <iostream>
#include <string>
#include <vector>
#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<char> 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 <typename T>
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 <typename T>
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 <typename T>
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 <typename T>
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 <typename T>
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 <typename T>
SofaeErrorCode JsonConfig::GetStrArray(const T& conf, const char* name,
std::vector<std::string>* 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 <typename T>
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<std::string>* 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<std::string>* 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

@ -0,0 +1,88 @@
#include <algorithm>
#include <cstring>
#include <string>
#include <vector>
#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

@ -0,0 +1,26 @@
#include <string>
#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

@ -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