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.
## 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.

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