/* Copyright (C) 2019, Texas A&M University.
This file is part of Graphene Library OS.
Graphene Library OS is free software: you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
as published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
Graphene Library OS is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program. If not, see . */
#include
#include
#include
#include
#include
#include
#include
#include "quote/generated-cacert.h"
/*
* Graphene's simple remote attestation feature:
*
* This feature is for verifying the SGX hardware platforms for executing applications
* in Graphene, to a remote trusted entity. The whole remote attestation process requires
* interaction with the Intel Attestation Service (IAS) and Intel PSW enclave for
* generating the remote and local quotes. Once the platform is fully verified, Graphene
* will continue to initialize the library OS and the application; otherwise the execution
* should terminate.
*
* Graphene's remote attestation process is meant to be transparent to the application;
* that is, no change is required to the source code or binary of the application. The
* remote attestation feature is enabled if "sgx.ra_client_spid" and "sgx.ra_client_key"
* are specified in the manifest. To obtain the SPID and the subscription key, register in
* the Intel API Portal: https://api.portal.trustedservices.intel.com/EPID-attestation.
*
* The remote attestation process contains four steps:
*
* (1) Initialization:
*
* +-------------------+ +-----------+ +---------+
* | Intel PSW Enclave | target info (PSW) | Untrusted | target info (PSW) | Enclave |
* | (AESMD) |------------------->| PAL |-------------------->| PAL |
* +-------------------+ +-----------+ +---------+
*
* Before the enclave is created, Graphene contacts the AESMD to retrieve the target info
* of the Intel PSW enclave. The target info is used for generating local report later.
*
* (2) OCALL + Local attestation:
*
* +---------+ +-----------+ +-------------------+
* | Enclave | OCALL(GET_ATTESTATION) | Untrusted | Report (PAL->PSW) | Intel PSW Enclave |
* | PAL |----------------------->| PAL |------------------>| (AESMD) |
* +---------+ +-----------+ +-------------------+
*
* The enclave PAL uses ENCLU[EREPORT] to generate a local report for the PSW enclave to
* verify that the two enclaves are on the same platform. The enclave PAL then issues an
* OCALL(GET_ATTESTATION), along with the report, the SPID, and the subscription key.
* The report is given to the PSW enclave to generate a local quote. The PSW enclave will
* verify the report, decide whether to trust the Graphene enclave, and then sign the
* local quote with a PSW-only attestation key.
*
* (3) Contact the IAS for platform report:
*
* The local quote from the PSW enclave needs to be verified by the IAS. Different from the
* Intel SDK model, Graphene does not rely on a third party to contact the IAS.
* Graphene contacts the IAS as part of its remote attestation process.
*
* +-----------+ +--------------+ +---------------+
* | Untrusted | fork + execve | HTTPS client | HTTPS (quote, SPID, key) | Intel Attest. |
* | PAL |-------------->| (CURL) |--------------------------->| Service |
* +-----------+ +--------------+ +---------------+
*
* Graphene now uses a commodity HTTPS client (CURL) to contact the IAS. This is not fully
* compliant to the TOS of the IAS, because the SPID and the key is not protected from
* the untrusted host. The verification of the SGX platform does not require secrecy of the
* SPID/key, because the SPID/key is only used by the IAS to identify the clients. Even if
* an attacker has obtained the SPID/key, the attacker cannot tamper the quote and the IAS
* attestation report.
*
* (4) Checking the IAS report:
*
* +---------------+ +-----------+ HTTPS resp, Certs, +---------+
* | Intel Attest. | HTTPS resp, Certs | Untrusted | Report (PSW->PAL) | Enclave |
* | Service |------------------>| PAL |-------------------->| PAL |
* +---------------+ +-----------+ +---------+
*
* Finally, Graphene returns the HTTPS response and a certificate chain from the IAS
* back to the enclave PAL, along with the local report from the PSW enclave. Graphene
* then verifies the attestation result based on the following criteria:
*
* - The HTTPS response needs to be signed by the certificate chain, including the first
* certificate to generate the signature, and all the following certificates to sign
* the previous certificates.
* - The last certificate in the chain will be signed by a known IAS root CA, hard-coded
* in the enclave PAL.
* - The report from the PSW enclave needs to be verified. This will establish the mutual
* trust between the enclave PAL and the PSW enclave.
* - The HTTPS response from the IAS needs to contain the same quote generated from the
* PSW enclave, the same mr_enclave, attributes, and 64-byte report data.
* - The HTTPS response needs to have an acceptable status, which is "OK" by default, or
* "GROUP_OUT_OF_DATE" if "sgx.ra_accept_group_out_of_date = 1" is in the manifest, or
* "CONFIGURATION_NEEDED" if "sgx.ra_accept_configuration_needed = 1" is in the manifest.
* If you obtain a status besides OK, please see the SECURITY ADVISORIES in README.md.
*/
/*
* Perform the initial attestation procedure if "sgx.ra_client.spid" is specified in the manifest
* file.
*/
int init_trusted_platform(void) {
char spid_hex[sizeof(sgx_spid_t) * 2 + 1];
ssize_t len =
get_config(pal_state.root_config, "sgx.ra_client_spid", spid_hex, sizeof(spid_hex));
if (len <= 0) {
SGX_DBG(DBG_E,
"*** No client info specified in the manifest. "
"Graphene will not perform remote attestation ***\n");
return 0;
}
if (len != sizeof(sgx_spid_t) * 2) {
SGX_DBG(DBG_E, "Malformed sgx.ra_client_spid value in the manifest: %s\n", spid_hex);
return -PAL_ERROR_INVAL;
}
sgx_spid_t spid;
for (ssize_t i = 0; i < len; i++) {
int8_t val = hex2dec(spid_hex[i]);
if (val < 0) {
SGX_DBG(DBG_E, "Malformed sgx.ra_client_spid value in the manifest: %s\n", spid_hex);
return -PAL_ERROR_INVAL;
}
spid[i / 2] = spid[i / 2] * 16 + (uint8_t)val;
}
char subkey[CONFIG_MAX];
len = get_config(pal_state.root_config, "sgx.ra_client_key", subkey, sizeof(subkey));
if (len <= 0) {
SGX_DBG(DBG_E, "No sgx.ra_client_key in the manifest\n");
return -PAL_ERROR_INVAL;
}
char buf[2];
len = get_config(pal_state.root_config, "sgx.ra_client_linkable", buf, sizeof(buf));
bool linkable = (len == 1 && buf[0] == '1');
len = get_config(pal_state.root_config, "sgx.ra_accept_group_out_of_date", buf, sizeof(buf));
bool accept_group_out_of_date = (len == 1 && buf[0] == '1');
len = get_config(pal_state.root_config, "sgx.ra_accept_configuration_needed", buf, sizeof(buf));
bool accept_configuration_needed = (len == 1 && buf[0] == '1');
sgx_quote_nonce_t nonce;
int ret = _DkRandomBitsRead(&nonce, sizeof(nonce));
if (ret < 0)
return ret;
char* status;
char* timestamp;
ret = sgx_verify_platform(&spid, subkey, &nonce, (sgx_report_data_t*)&pal_enclave_state,
linkable, accept_group_out_of_date, accept_configuration_needed,
/*ret_attestation=*/NULL, &status, ×tamp);
if (ret < 0)
return ret;
// If the attestation is successful, update the control block
__pal_control.attestation_status = status;
__pal_control.attestation_timestamp = timestamp;
return ret;
}
/*
* A simple function to parse a X509 certificate for only the certificate body, the signature,
* and the public key.
*
* TODO: Currently no verification of the X509 certificate.
*
* @cert: The certificate to parse (DER format).
* @cert_len: The length of cert.
* @body: The certificate body (the signed part).
* @body_len: The length of body.
* @sig: The certificate signature.
* @sig_len: The length of sig.
* @pubkey: The RSA public key from the certificate.
*/
static int parse_x509(uint8_t* cert, size_t cert_len, uint8_t** body, size_t* body_len,
uint8_t** sig, size_t* sig_len, LIB_RSA_KEY* pubkey) {
uint8_t* ptr = cert;
uint8_t* end = cert + cert_len;
enum asn1_tag tag;
bool is_cons;
uint8_t* buf;
size_t buf_len;
int ret;
// X509Certificate := SEQUENCE {
// Body CertificateBody,
// SignatureAlgorithm AlgorithmDescriptor,
// Signature BIT STRING }
ret = lib_ASN1GetSerial(&ptr, end, &tag, &is_cons, &buf, &buf_len);
if (ret < 0)
return ret;
if (tag != ASN1_SEQUENCE || !is_cons)
return -PAL_ERROR_INVAL;
uint8_t* cert_signed = ptr = buf;
uint8_t* cert_body;
uint8_t* cert_sig;
size_t cert_body_len, cert_sig_len;
end = buf + buf_len;
ret = lib_ASN1GetSerial(&ptr, end, &tag, &is_cons, &cert_body, &cert_body_len);
if (ret < 0)
return ret;
if (tag != ASN1_SEQUENCE || !is_cons)
return -PAL_ERROR_INVAL;
size_t cert_signed_len = ptr - cert_signed;
// Skip SignatureAlgorithm
ret = lib_ASN1GetSerial(&ptr, end, &tag, &is_cons, &buf, &buf_len);
if (ret < 0)
return ret;
ret = lib_ASN1GetBitstring(&ptr, end, &cert_sig, &cert_sig_len);
if (ret < 0)
return ret;
// CertficateBody := SEQUENCE {
// Version CONSTANT,
// SerialNumber INTEGER,
// Signature AlgorithmDiscriptor,
// Issuer Name,
// Validity ValidityTime,
// Subject Name,
// SubjectPublicKeyInfo PublicKeyInfo,
// (optional fields) }
ptr = cert_body;
end = cert_body + cert_body_len;
// Skip Version, SerialNumber, Signature, Issuer, Validity, and Subject
for (int i = 0; i < 6; i++) {
ret = lib_ASN1GetSerial(&ptr, end, &tag, &is_cons, &buf, &buf_len);
if (ret < 0)
return ret;
}
// Get SubjectPublicKeyInfo
ret = lib_ASN1GetSerial(&ptr, end, &tag, &is_cons, &buf, &buf_len);
if (ret < 0)
return ret;
if (tag != ASN1_SEQUENCE || !is_cons)
return -PAL_ERROR_INVAL;
// PublickKeyInfo := SEQUENCE {
// PublicKeyAlgorithm AlgorithmDescriptor,
// PublicKey BIT STRING }
ptr = buf;
end = buf + buf_len;
// Skip PublicKeyAlgorithm
ret = lib_ASN1GetSerial(&ptr, end, &tag, &is_cons, &buf, &buf_len);
if (ret < 0)
return ret;
// Get PublicKey
uint8_t* pkey_bits;
size_t pkey_bits_len;
ret = lib_ASN1GetBitstring(&ptr, end, &pkey_bits, &pkey_bits_len);
if (ret < 0)
return ret;
// RSAPublicKey := SEQUENCE {
// Modulus Integer,
// PublicExponent Integer }
ptr = pkey_bits;
end = pkey_bits + pkey_bits_len;
ret = lib_ASN1GetSerial(&ptr, end, &tag, &is_cons, &buf, &buf_len);
if (ret < 0)
return ret;
uint8_t* mod;
uint8_t* exp;
size_t mod_len, exp_len;
ptr = buf;
end = buf + buf_len;
ret = lib_ASN1GetLargeNumberLength(&ptr, end, &mod_len);
if (ret < 0)
return ret;
mod = ptr;
ptr += mod_len;
ret = lib_ASN1GetLargeNumberLength(&ptr, end, &exp_len);
if (ret < 0)
return ret;
exp = ptr;
ptr += exp_len;
*body = malloc(cert_signed_len);
*body_len = cert_signed_len;
memcpy(*body, cert_signed, cert_signed_len);
*sig = malloc(cert_sig_len);
*sig_len = cert_sig_len;
memcpy(*sig, cert_sig, cert_sig_len);
ret = lib_RSAInitKey(pubkey);
if (ret < 0)
return ret;
ret = lib_RSAImportPublicKey(pubkey, exp, exp_len, mod, mod_len);
if (ret < 0)
return ret;
return 0;
}
/*
* Same as parse_x509(), but parse the certificate in PEM format.
*
* @cert: The starting address for parsing the certificate.
* @cert_end: Returns the end of certificate after parsing.
* @body: The certificate body (the signed part).
* @body_len: The length of body.
* @sig: The certificate signature.
* @sig_len: The length of sig.
* @pubkey: The RSA public key from the certificate.
*/
static int parse_x509_pem(char* cert, char** cert_end, uint8_t** body, size_t* body_len,
uint8_t** sig, size_t* sig_len, LIB_RSA_KEY* pubkey) {
int ret;
char* start = strchr(cert, '-');
if (!start) {
// No more certificate
*cert_end = NULL;
return 0;
}
if (!strstartswith_static(start, "-----BEGIN CERTIFICATE-----"))
return -PAL_ERROR_INVAL;
start += static_strlen("-----BEGIN CERTIFICATE-----");
char* end = strchr(start, '-');
if (!strstartswith_static(end, "-----END CERTIFICATE-----"))
return -PAL_ERROR_INVAL;
size_t cert_der_len;
ret = lib_Base64Decode(start, end - start, NULL, &cert_der_len);
if (ret < 0)
return ret;
uint8_t* cert_der = __alloca(cert_der_len);
ret = lib_Base64Decode(start, end - start, cert_der, &cert_der_len);
if (ret < 0)
return ret;
ret = parse_x509(cert_der, cert_der_len, body, body_len, sig, sig_len, pubkey);
if (ret < 0)
return ret;
*cert_end = end + static_strlen("-----END CERTIFICATE-----");
return 0;
}
/*
* Perform the remote attestation to verify the current SGX platform.
*
* The remote attestation procedure verifies two primary properties: (1) The current execution runs
* in an SGX enclave; and (2) The enclave is created on a genuine, up-to-date Intel CPU. This
* procedure requires interaction with the Intel PSW quoting enclave (AESMD) and the Intel
* Attestation Service (IAS). The quoting enclave verifies a local attestation report from the
* target enclave, and then generates a quoting enclave (QE) report and a platform quote signed by
* the platform's attestation key. The IAS then verifies the platform quote and issues a remote
* attestation report, signed by a certificate chain attached to the report.
*
* TODO: currently no verification of the correctness of the IAS certificate
*
* @spid: The SPID registered for the Intel Attestation Service (IAS).
* @subkey: SPID subscription key.
* @nonce: A 16-byte nonce to be included in the quote.
* @report_data: A 64-byte bytestring to be included in the local report and the quote.
* @linkable: Specify whether the SPID is linkable.
* @accept_group_out_of_date: Specify whether to accept GROUP_OUT_OF_DATE from IAS
* @accept_configuration_needed: Specify whether to accept CONFIGURATION_NEEDED from IAS
* @ret_attestation: Returns the retrieved attestation data.
* @ret_ias_status: Returns a pointer to the attestation status (as a string) from the IAS.
* @ret_ias_timestamp: Returns a pointer to the timestamp (as a string) from the IAS.
* Timestamp format: %Y-%m-%dT%H:%M:%S.%f (Ex: 2019-08-01T12:30:00.123456)
*/
int sgx_verify_platform(sgx_spid_t* spid, const char* subkey, sgx_quote_nonce_t* nonce,
sgx_report_data_t* report_data, bool linkable,
bool accept_group_out_of_date, bool accept_configuration_needed,
sgx_attestation_t* ret_attestation, char** ret_ias_status,
char** ret_ias_timestamp) {
SGX_DBG(DBG_S, "Request quote:\n");
SGX_DBG(DBG_S, " spid: %s\n", ALLOCA_BYTES2HEXSTR(*spid));
SGX_DBG(DBG_S, " type: %s\n", linkable ? "linkable" : "unlinkable");
SGX_DBG(DBG_S, " nonce: %s\n", ALLOCA_BYTES2HEXSTR(*nonce));
__sgx_mem_aligned sgx_report_t report;
__sgx_mem_aligned sgx_target_info_t targetinfo = pal_sec.aesm_targetinfo;
int ret = sgx_report(&targetinfo, report_data, &report);
if (ret) {
SGX_DBG(DBG_E, "Failed to get report for attestation\n");
return -PAL_ERROR_DENIED;
}
sgx_attestation_t attestation;
ret = ocall_get_attestation(spid, subkey, linkable, &report, nonce, &attestation);
if (ret < 0) {
SGX_DBG(DBG_E, "Failed to get attestation\n");
return ret;
}
// First, verify the report from the quoting enclave
ret = sgx_verify_report(&attestation.qe_report);
if (ret < 0) {
SGX_DBG(DBG_E, "Failed to verify QE report, ret = %d\n", ret);
goto failed;
}
// Verify the IAS response against the certificate chain
uint8_t* data_to_verify = (uint8_t*)attestation.ias_report;
uint8_t* data_sig = attestation.ias_sig;
size_t data_len = attestation.ias_report_len;
size_t data_sig_len = attestation.ias_sig_len;
// Attach the IAS signing chain with the hard-coded CA certificate
const char* ca_cert = IAS_CA_CERT;
size_t len1 = strlen(attestation.ias_certs);
size_t len2 = static_strlen(IAS_CA_CERT);
char* certs = malloc(len1 + len2 + 1);
memcpy(certs, attestation.ias_certs, len1);
memcpy(certs + len1, ca_cert, len2);
certs[len1 + len2] = 0;
free(attestation.ias_certs);
attestation.ias_certs = certs;
attestation.ias_certs_len = len1 + len2 + 1;
// There can be multiple certificates in the chain. We need to use the public key from the
// *first* certificate to verify the IAS response. For each certificate except the last one, we
// need to use the public key from the *next* certificate to verify the certificate body. The
// last certificate will be verified by the CA certificate (hard-coded in the binary)
for (char* cert_start = attestation.ias_certs;
cert_start < attestation.ias_certs + attestation.ias_certs_len && *cert_start;) {
// Generate the message digest first (without RSA)
LIB_SHA256_CONTEXT ctx;
uint8_t hash[32];
if ((ret = lib_SHA256Init(&ctx)) < 0)
goto failed;
if ((ret = lib_SHA256Update(&ctx, data_to_verify, data_len)) < 0)
goto failed;
if ((ret = lib_SHA256Final(&ctx, hash)) < 0)
goto failed;
// Use the public key to verify the last signature
uint8_t* cert_body;
uint8_t* cert_sig;
size_t cert_body_len;
size_t cert_sig_len;
LIB_RSA_KEY cert_key;
ret = parse_x509_pem(cert_start, &cert_start, &cert_body, &cert_body_len, &cert_sig,
&cert_sig_len, &cert_key);
if (ret < 0) {
SGX_DBG(DBG_E, "Failed to parse IAS certificate, rv = %d\n", ret);
goto failed;
}
ret = lib_RSAVerifySHA256(&cert_key, hash, sizeof(hash), data_sig, data_sig_len);
if (ret < 0) {
SGX_DBG(DBG_E,
"Failed to verify the report against the IAS certificates, rv = %d\n",
ret);
lib_RSAFreeKey(&cert_key);
goto failed;
}
lib_RSAFreeKey(&cert_key);
data_to_verify = cert_body;
data_sig = cert_sig;
data_len = cert_body_len;
data_sig_len = cert_sig_len;
}
// Parse the IAS report in JSON format
char* ias_status = NULL;
char* ias_timestamp = NULL;
sgx_quote_nonce_t* ias_nonce = NULL;
sgx_quote_t* ias_quote = NULL;
char* start = attestation.ias_report;
if (start[0] == '{')
start++;
char* end = strchr(start, ',');
while (end) {
char* next_start = end + 1;
// Retrieve the key and value separated by the colon (:)
char* delim = strchr(start, ':');
if (!delim)
break;
char* key = start;
char* val = delim + 1;
size_t klen = delim - start;
size_t vlen = end - val;
// Remove quotation marks (") around the key and value if there are any
if (key[0] == '"') {
key++;
klen--;
}
if (key[klen - 1] == '"')
klen--;
if (val[0] == '"') {
val++;
vlen--;
}
if (val[vlen - 1] == '"')
vlen--;
// Scan the fields in the IAS report.
if (!memcmp(key, "isvEnclaveQuoteStatus", klen)) {
// Parse "isvEnclaveQuoteStatus":"OK"|"GROUP_OUT_OF_DATE"|...
ias_status = __alloca(vlen + 1);
memcpy(ias_status, val, vlen);
ias_status[vlen] = 0;
} else if (!memcmp(key, "nonce", klen)) {
// Parse "nonce":"{Hex representation of the initial nonce}"
if (vlen != sizeof(sgx_quote_nonce_t) * 2) {
SGX_DBG(DBG_E, "Malformed nonce in the IAS report\n");
goto failed;
}
ias_nonce = __alloca(sizeof(sgx_quote_nonce_t));
for (size_t i = 0; i < sizeof(sgx_quote_nonce_t); i++) {
int8_t hi = hex2dec(val[i * 2]);
int8_t lo = hex2dec(val[i * 2 + 1]);
if (hi < 0 || lo < 0) {
SGX_DBG(DBG_E, "Malformed nonce in the IAS report\n");
goto failed;
}
((uint8_t*)ias_nonce)[i] = (uint8_t)hi * 16 + (uint8_t)lo;
}
} else if (!memcmp(key, "timestamp", klen)) {
// Parse "timestamp":"{IAS timestamp (format: %Y-%m-%dT%H:%M:%S.%f)}"
ias_timestamp = __alloca(vlen + 1);
memcpy(ias_timestamp, val, vlen);
ias_timestamp[vlen] = 0;
} else if (!memcmp(key, "isvEnclaveQuoteBody", klen)) {
// Parse "isvEnclaveQuoteBody":"{Quote body (in Base64 format)}"
size_t ias_quote_len;
ret = lib_Base64Decode(val, vlen, NULL, &ias_quote_len);
if (ret < 0) {
SGX_DBG(DBG_E, "Malformed quote in the IAS report\n");
goto failed;
}
ias_quote = __alloca(ias_quote_len);
ret = lib_Base64Decode(val, vlen, (uint8_t*)ias_quote, &ias_quote_len);
if (ret < 0) {
SGX_DBG(DBG_E, "Malformed quote in the IAS report\n");
goto failed;
}
}
start = next_start;
end = strchr(start, ',') ?: strchr(start, '}');
}
if (!ias_status || !ias_nonce || !ias_timestamp || !ias_quote) {
SGX_DBG(DBG_E, "Missing important field(s) in the IAS report\n");
goto failed;
}
SGX_DBG(DBG_S, "Quote:\n");
SGX_DBG(DBG_S, " version: %04x\n", ias_quote->body.version);
SGX_DBG(DBG_S, " sigtype: %04x\n", ias_quote->body.sigtype);
SGX_DBG(DBG_S, " gid: %08x\n", ias_quote->body.gid);
SGX_DBG(DBG_S, " isvsvn qe: %08x\n", ias_quote->body.isvsvn_qe);
SGX_DBG(DBG_S, " isvsvn pce: %08x\n", ias_quote->body.isvsvn_pce);
SGX_DBG(DBG_S, "IAS report: %s\n", attestation.ias_report);
SGX_DBG(DBG_S, " status: %s\n", ias_status);
SGX_DBG(DBG_S, " timestamp: %s\n", ias_timestamp);
// Only accept status to be "OK" or "GROUP_OUT_OF_DATE" / "CONFIGURATION_NEEDED" (if
// accept_out_of_date / accept_configuration_needed is true)
if (strcmp_static(ias_status, "OK") &&
(!accept_group_out_of_date || strcmp_static(ias_status, "GROUP_OUT_OF_DATE")) &&
(!accept_configuration_needed || strcmp_static(ias_status, "CONFIGURATION_NEEDED"))) {
SGX_DBG(DBG_E, "IAS returned invalid status: %s\n", ias_status);
goto failed;
}
// Check if the nonce matches the IAS report
if (memcmp(ias_nonce, nonce, sizeof(sgx_quote_nonce_t))) {
SGX_DBG(DBG_E, "IAS returned the wrong nonce\n");
goto failed;
}
// Check if the quote matches the IAS report
if (memcmp(&ias_quote->body, &attestation.quote->body, sizeof(sgx_quote_body_t)) ||
memcmp(&ias_quote->report_body, &report.body, sizeof(sgx_report_body_t))) {
SGX_DBG(DBG_E, "IAS returned the wrong quote\n");
goto failed;
}
// Check if the quote has the right enclave report
if (memcmp(&attestation.quote->report_body, &report.body, sizeof(sgx_report_body_t))) {
SGX_DBG(DBG_E, "The returned quote contains the wrong enclave report\n");
goto failed;
}
// Succeeded!!!
if (ret_ias_status) {
size_t len = strlen(ias_status) + 1;
*ret_ias_status = malloc(len);
memcpy(*ret_ias_status, ias_status, len);
}
if (ret_ias_timestamp) {
size_t len = strlen(ias_timestamp) + 1;
*ret_ias_timestamp = malloc(len);
memcpy(*ret_ias_timestamp, ias_timestamp, len);
}
if (ret_attestation) {
memcpy(ret_attestation, &attestation, sizeof(sgx_attestation_t));
return 0;
}
ret = 0;
free_attestation:
if (attestation.quote)
free(attestation.quote);
if (attestation.ias_report)
free(attestation.ias_report);
if (attestation.ias_sig)
free(attestation.ias_sig);
if (attestation.ias_certs)
free(attestation.ias_certs);
return ret;
failed:
ret = -PAL_ERROR_DENIED;
goto free_attestation;
}