mirror of
https://github.com/intel/linux-sgx
synced 2026-06-08 14:49:32 +00:00
8a22317709
Upgraded to OpenSSL 3.0.10. Added interoperable RA-TLS support which follows CCC design. Enhanced Protect File System performance and added additional dependency `libsgx_pthread.a`. Added the Constant Time instruction Decoder (CTD) into the default AEX-Notify mitigation handler in order to prevent the introduction of any additional subtle sidechannel leakages within the default handler. Added Mistletoe 3 mitigations to the IPP Cryptography Library to the AES-ECB, AESGCM, and AES-CMAC algorithms. These have been incorporated transparently into the `sgx_tcrypto` library. Resigned all Intel® SGX Architecture Enclaves. Upgraded Intel SGX Quote Verification Enclave to integrate OpenSSL/SgxSSL 3.0.10. Added Attestation Library support for Intel(R) TDX Migration TD. Added Rust wrapper for low-level Quote Generation APIs. Enabled `SE_TRACE` log in release binary. Updated Rust QVL wrapper to use native Rust structure for quote verification collateral. Added a limitation in the DCAP QVL to only allow the user to set the QvE load policy once. Fixed bugs. Signed-off-by: Li, Xun <xun.li@intel.com>
1027 lines
29 KiB
C++
1027 lines
29 KiB
C++
/**
|
|
*
|
|
* MIT License
|
|
*
|
|
* Copyright (c) Open Enclave SDK contributors.
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
* of this software and associated documentation files (the "Software"), to deal
|
|
* in the Software without restriction, including without limitation the rights
|
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
* copies of the Software, and to permit persons to whom the Software is
|
|
* furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included in all
|
|
* copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
* SOFTWARE
|
|
*
|
|
*/
|
|
|
|
#include <openssl/bio.h>
|
|
#include <openssl/err.h>
|
|
#include <openssl/pem.h>
|
|
#include <openssl/x509.h>
|
|
#include <openssl/asn1.h>
|
|
#include <openssl/ec.h>
|
|
#include <openssl/rsa.h>
|
|
#include <string.h>
|
|
#include <sgx_error.h>
|
|
#include "cert_header.h"
|
|
#include <openssl/sha.h>
|
|
#include "cbor.h"
|
|
#include "sgx_quote_4.h"
|
|
#include "sgx_quote_5.h"
|
|
|
|
typedef struct _cert
|
|
{
|
|
uint64_t magic;
|
|
X509* x509;
|
|
} cert_t;
|
|
|
|
|
|
static void _cert_init(cert_t* impl, X509* x509)
|
|
{
|
|
impl->magic = SGX_CERT_MAGIC;
|
|
impl->x509 = x509;
|
|
}
|
|
|
|
static bool _cert_is_valid(const cert_t* impl)
|
|
{
|
|
return impl && (impl->magic == SGX_CERT_MAGIC) && impl->x509;
|
|
}
|
|
|
|
static void _cert_clear(cert_t* impl)
|
|
{
|
|
if (impl)
|
|
{
|
|
impl->magic = 0;
|
|
impl->x509 = NULL;
|
|
}
|
|
}
|
|
|
|
typedef struct _cert_chain
|
|
{
|
|
uint64_t magic;
|
|
STACK_OF(X509) * sk;
|
|
} cert_chain_t;
|
|
|
|
static bool _cert_chain_is_valid(const cert_chain_t* impl)
|
|
{
|
|
return impl && (impl->magic == SGX_CERT_CHAIN_MAGIC) && impl->sk;
|
|
}
|
|
|
|
/* Clone the certificate to clear any verification state */
|
|
static X509* _clone_x509(X509* x509)
|
|
{
|
|
X509* ret = NULL;
|
|
BIO* out = NULL;
|
|
BIO* in = NULL;
|
|
BUF_MEM* mem;
|
|
|
|
if (!x509)
|
|
goto done;
|
|
|
|
if (!(out = BIO_new(BIO_s_mem())))
|
|
goto done;
|
|
|
|
if (!PEM_write_bio_X509(out, x509))
|
|
goto done;
|
|
|
|
if (!BIO_get_mem_ptr(out, &mem))
|
|
goto done;
|
|
|
|
if (mem->length > INT_MAX)
|
|
goto done;
|
|
|
|
if (!(in = BIO_new_mem_buf(mem->data, (int)mem->length)))
|
|
goto done;
|
|
|
|
ret = PEM_read_bio_X509(in, NULL, 0, NULL);
|
|
|
|
done:
|
|
|
|
if (out)
|
|
BIO_free(out);
|
|
|
|
if (in)
|
|
BIO_free(in);
|
|
|
|
return ret;
|
|
}
|
|
|
|
#if OPENSSL_VERSION_NUMBER < 0x10100000L
|
|
/* Needed because some versions of OpenSSL do not support X509_up_ref() */
|
|
static int X509_up_ref(X509* x509)
|
|
{
|
|
if (!x509)
|
|
return 0;
|
|
|
|
CRYPTO_add(&x509->references, 1, CRYPTO_LOCK_X509);
|
|
return 1;
|
|
}
|
|
|
|
static const STACK_OF(X509_EXTENSION) * X509_get0_extensions(const X509* x)
|
|
{
|
|
if (!x->cert_info)
|
|
{
|
|
return NULL;
|
|
}
|
|
return x->cert_info->extensions;
|
|
}
|
|
|
|
#endif
|
|
|
|
static STACK_OF(X509) * _clone_chain(STACK_OF(X509) * chain)
|
|
{
|
|
STACK_OF(X509)* sk = NULL;
|
|
int n = sk_X509_num(chain);
|
|
|
|
if (!(sk = sk_X509_new(NULL)))
|
|
return NULL;
|
|
|
|
for (int i = 0; i < n; i++)
|
|
{
|
|
X509* x509;
|
|
|
|
if (!(x509 = sk_X509_value(chain, (int)i)))
|
|
return NULL;
|
|
|
|
if (!(x509 = _clone_x509(x509)))
|
|
return NULL;
|
|
|
|
if (!sk_X509_push(sk, x509))
|
|
return NULL;
|
|
}
|
|
|
|
return sk;
|
|
}
|
|
|
|
static sgx_status_t _verify_cert(
|
|
X509* cert,
|
|
STACK_OF(X509) * chain_,
|
|
const sgx_crl_t* const* crls,
|
|
size_t num_crls)
|
|
{
|
|
sgx_status_t result = SGX_ERROR_UNEXPECTED;
|
|
X509_STORE_CTX* ctx = NULL;
|
|
X509_STORE* store = NULL;
|
|
X509* x509 = NULL;
|
|
STACK_OF(X509)* chain = NULL;
|
|
|
|
/* Clone the certificate to clear any cached verification state */
|
|
if (!(x509 = _clone_x509(cert)))
|
|
goto end;
|
|
|
|
/* Clone the chain to clear any cached verification state */
|
|
if (chain_ && !(chain = _clone_chain(chain_)))
|
|
goto end;
|
|
|
|
/* Create a store for the verification */
|
|
if (!(store = X509_STORE_new()))
|
|
goto end;
|
|
|
|
/* Create a context for verification */
|
|
if (!(ctx = X509_STORE_CTX_new()))
|
|
goto end;
|
|
|
|
/* Initialize the context that will be used to verify the certificate */
|
|
if (!X509_STORE_CTX_init(ctx, store, NULL, NULL))
|
|
goto end;
|
|
|
|
/* Create a store with CRLs if needed */
|
|
if (crls && num_crls)
|
|
{
|
|
X509_VERIFY_PARAM* verify_param = NULL;
|
|
|
|
for (size_t i = 0; i < num_crls; i++)
|
|
{
|
|
crl_t* crl_impl = (crl_t*)crls[i];
|
|
|
|
/* X509_STORE_add_crl manages its own addition refcount */
|
|
if (!X509_STORE_add_crl(store, crl_impl->crl))
|
|
goto end;
|
|
}
|
|
|
|
/* Get the verify parameter (must not be null) */
|
|
if (!(verify_param = X509_STORE_CTX_get0_param(ctx)))
|
|
goto end;
|
|
|
|
X509_VERIFY_PARAM_set_flags(verify_param, X509_V_FLAG_CRL_CHECK);
|
|
X509_VERIFY_PARAM_set_flags(verify_param, X509_V_FLAG_CRL_CHECK_ALL);
|
|
}
|
|
|
|
/* Inject the certificate into the verification context */
|
|
X509_STORE_CTX_set_cert(ctx, x509);
|
|
|
|
/* Set the CA chain into the verification context */
|
|
if (chain)
|
|
X509_STORE_CTX_trusted_stack(ctx, chain);
|
|
else
|
|
X509_STORE_add_cert(store, x509);
|
|
|
|
/* Finally verify the certificate */
|
|
if (!X509_verify_cert(ctx))
|
|
{
|
|
int errorno = X509_STORE_CTX_get_error(ctx);
|
|
if (errorno != X509_V_OK)
|
|
goto end;
|
|
}
|
|
|
|
result = SGX_SUCCESS;
|
|
|
|
end:
|
|
if (x509)
|
|
X509_free(x509);
|
|
|
|
if (chain)
|
|
sk_X509_pop_free(chain, X509_free);
|
|
|
|
if (store)
|
|
X509_STORE_free(store);
|
|
|
|
if (ctx)
|
|
X509_STORE_CTX_free(ctx);
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
sgx_status_t sgx_read_cert_in_der(
|
|
sgx_cert_t* cert,
|
|
const void* der_data,
|
|
size_t der_size)
|
|
{
|
|
sgx_status_t result = SGX_ERROR_UNEXPECTED;
|
|
cert_t* impl = (cert_t*)cert;
|
|
X509* x509 = NULL;
|
|
unsigned char* p = NULL;
|
|
|
|
/* Zero-initialize the implementation */
|
|
if (impl)
|
|
impl->magic = 0;
|
|
|
|
/* Check parameters */
|
|
if (!der_data || !der_size || der_size > INT_MAX || !cert)
|
|
return SGX_ERROR_INVALID_PARAMETER;
|
|
|
|
/* Initialize OpenSSL (if not already initialized) */
|
|
//sgxssl_crypto_initialize();
|
|
|
|
p = (unsigned char*)der_data;
|
|
|
|
/* Convert the PEM BIO into a certificate object */
|
|
if (!(x509 = d2i_X509(NULL, (const unsigned char**)&p, (int)der_size)))
|
|
goto end;
|
|
|
|
_cert_init(impl, x509);
|
|
x509 = NULL;
|
|
|
|
result = SGX_SUCCESS;
|
|
|
|
end:
|
|
|
|
X509_free(x509);
|
|
|
|
return result;
|
|
}
|
|
|
|
sgx_status_t sgx_cert_free(sgx_cert_t* cert)
|
|
{
|
|
sgx_status_t result = SGX_ERROR_UNEXPECTED;
|
|
cert_t* impl = (cert_t*)cert;
|
|
|
|
/* Check parameters */
|
|
if (!_cert_is_valid(impl))
|
|
goto end;
|
|
|
|
/* Free the certificate */
|
|
X509_free(impl->x509);
|
|
_cert_clear(impl);
|
|
|
|
result = SGX_SUCCESS;
|
|
|
|
end:
|
|
return result;
|
|
}
|
|
|
|
sgx_status_t sgx_cert_verify(
|
|
sgx_cert_t* cert,
|
|
sgx_cert_chain_t* chain,
|
|
const sgx_crl_t* const* crls,
|
|
size_t num_crls)
|
|
{
|
|
sgx_status_t result = SGX_ERROR_UNEXPECTED;
|
|
cert_t* cert_impl = (cert_t*)cert;
|
|
cert_chain_t* chain_impl = (cert_chain_t*)chain;
|
|
|
|
/* Check for invalid cert parameter */
|
|
if (!_cert_is_valid(cert_impl))
|
|
return SGX_ERROR_INVALID_PARAMETER;
|
|
|
|
/* Check for invalid chain parameter */
|
|
if (chain && !_cert_chain_is_valid(chain_impl))
|
|
return SGX_ERROR_INVALID_PARAMETER;
|
|
|
|
/* Verify the certificate */
|
|
_verify_cert(
|
|
cert_impl->x509,
|
|
(chain_impl != NULL ? chain_impl->sk : NULL),
|
|
crls,
|
|
num_crls);
|
|
|
|
result = SGX_SUCCESS;
|
|
|
|
return result;
|
|
}
|
|
|
|
static sgx_status_t compare_cert_pubkey_against_cbor_claim_hash(
|
|
const uint8_t* pem_pub_key,
|
|
size_t pem_pub_key_len,
|
|
cbor_item_t* cbor_hash_entry)
|
|
{
|
|
uint8_t pk_der[PUB_KEY_MAX_SIZE] = {0};
|
|
size_t pk_der_size = 0;
|
|
unsigned char *p_sha = NULL;
|
|
sgx_status_t ret = SGX_ERROR_UNEXPECTED;
|
|
cbor_item_t* cbor_hash_alg_id = NULL;
|
|
cbor_item_t* cbor_hash_value = NULL;
|
|
|
|
if (PEM2DER_PublicKey_converter(pem_pub_key, pem_pub_key_len, pk_der, &pk_der_size))
|
|
goto out;
|
|
|
|
if (!cbor_isa_array(cbor_hash_entry) || !cbor_array_is_definite(cbor_hash_entry)
|
|
|| cbor_array_size(cbor_hash_entry) != 2) {
|
|
return SGX_ERROR_TLS_X509_INVALID_EXTENSION;
|
|
}
|
|
|
|
cbor_hash_alg_id = cbor_array_get(cbor_hash_entry, /*index=*/0);
|
|
if (!cbor_hash_alg_id || !cbor_isa_uint(cbor_hash_alg_id)) {
|
|
ret = SGX_ERROR_TLS_X509_INVALID_EXTENSION;
|
|
goto out;
|
|
}
|
|
|
|
cbor_hash_value = cbor_array_get(cbor_hash_entry, /*index=*/1);
|
|
if (!cbor_hash_value || !cbor_isa_bytestring(cbor_hash_value)
|
|
|| !cbor_bytestring_is_definite(cbor_hash_value)) {
|
|
ret = SGX_ERROR_TLS_X509_INVALID_EXTENSION;
|
|
goto out;
|
|
}
|
|
|
|
uint8_t sha[SHA512_DIGEST_LENGTH]; /* enough to hold SHA-256, -384, or -512 */
|
|
size_t sha_size;
|
|
size_t temp_size;
|
|
|
|
uint64_t hash_alg_id;
|
|
switch (cbor_int_get_width(cbor_hash_alg_id)) {
|
|
case CBOR_INT_8: hash_alg_id = cbor_get_uint8(cbor_hash_alg_id); break;
|
|
case CBOR_INT_16: hash_alg_id = cbor_get_uint16(cbor_hash_alg_id); break;
|
|
case CBOR_INT_32: hash_alg_id = cbor_get_uint32(cbor_hash_alg_id); break;
|
|
case CBOR_INT_64: hash_alg_id = cbor_get_uint64(cbor_hash_alg_id); break;
|
|
default: ret = SGX_ERROR_TLS_X509_INVALID_EXTENSION; goto out;
|
|
}
|
|
|
|
switch (hash_alg_id) {
|
|
case IANA_NAMED_INFO_HASH_ALG_REGISTRY_SHA256:
|
|
sha_size = SHA256_DIGEST_LENGTH;
|
|
break;
|
|
case IANA_NAMED_INFO_HASH_ALG_REGISTRY_SHA384:
|
|
sha_size = SHA384_DIGEST_LENGTH;
|
|
break;
|
|
case IANA_NAMED_INFO_HASH_ALG_REGISTRY_SHA512:
|
|
sha_size = SHA512_DIGEST_LENGTH;
|
|
break;
|
|
default:
|
|
ret = SGX_ERROR_TLS_X509_INVALID_EXTENSION;
|
|
goto out;
|
|
}
|
|
|
|
temp_size = cbor_bytestring_length(cbor_hash_value);
|
|
if (temp_size != sha_size) {
|
|
ret = SGX_ERROR_TLS_X509_INVALID_EXTENSION;
|
|
goto out;
|
|
}
|
|
|
|
switch (hash_alg_id) {
|
|
case IANA_NAMED_INFO_HASH_ALG_REGISTRY_RESERVED:
|
|
case IANA_NAMED_INFO_HASH_ALG_REGISTRY_SHA256:
|
|
p_sha = SHA256(pk_der, pk_der_size, sha);
|
|
break;
|
|
case IANA_NAMED_INFO_HASH_ALG_REGISTRY_SHA384:
|
|
p_sha = SHA384(pk_der, pk_der_size, sha);
|
|
break;
|
|
case IANA_NAMED_INFO_HASH_ALG_REGISTRY_SHA512:
|
|
p_sha = SHA512(pk_der, pk_der_size, sha);
|
|
break;
|
|
}
|
|
|
|
if (p_sha == NULL)
|
|
{
|
|
ret = SGX_ERROR_UNEXPECTED;
|
|
goto out;
|
|
}
|
|
|
|
if (memcmp(cbor_bytestring_handle(cbor_hash_value), sha, sha_size)) {
|
|
ret = SGX_ERROR_INVALID_SIGNATURE;
|
|
goto out;
|
|
}
|
|
|
|
ret = SGX_SUCCESS;
|
|
out:
|
|
if (cbor_hash_alg_id) cbor_decref(&cbor_hash_alg_id);
|
|
if (cbor_hash_value) cbor_decref(&cbor_hash_value);
|
|
return ret;
|
|
}
|
|
|
|
|
|
sgx_status_t extract_cbor_evidence_and_compare_hash(
|
|
const uint8_t* cbor_evidence_buf,
|
|
size_t evidence_buf_size,
|
|
uint8_t* pem_pub_key,
|
|
size_t pem_pub_key_len,
|
|
uint8_t* out_quote,
|
|
uint32_t* out_quote_size)
|
|
{
|
|
/* for description of evidence format, see ttls.c:generate_cbor_evidence() */
|
|
cbor_item_t* cbor_tagged_evidence = NULL;
|
|
cbor_item_t* cbor_evidence = NULL;
|
|
cbor_item_t* cbor_quote = NULL;
|
|
cbor_item_t* cbor_claims = NULL; /* serialized CBOR map of claims (as bytestring) */
|
|
cbor_item_t* cbor_claims_map = NULL;
|
|
cbor_item_t* cbor_hash_entry = NULL;
|
|
uint8_t* quote = NULL;
|
|
sgx_status_t ret = SGX_SUCCESS;
|
|
|
|
struct cbor_pair* claims_pairs = NULL;
|
|
uint8_t* claims_buf = NULL;
|
|
size_t claims_buf_size = 0;
|
|
size_t quote_size = 0;
|
|
|
|
if (evidence_buf_size == 0) return SGX_ERROR_UNEXPECTED;
|
|
|
|
struct cbor_load_result cbor_result;
|
|
cbor_tagged_evidence = cbor_load(cbor_evidence_buf, evidence_buf_size, &cbor_result);
|
|
if (cbor_result.error.code != CBOR_ERR_NONE) {
|
|
ret = (cbor_result.error.code == CBOR_ERR_MEMERROR) ?
|
|
SGX_ERROR_OUT_OF_MEMORY : SGX_ERROR_UNEXPECTED;
|
|
goto out;
|
|
}
|
|
if (!cbor_isa_tag(cbor_tagged_evidence)
|
|
|| cbor_tag_value(cbor_tagged_evidence) != TCG_DICE_TAGGED_EVIDENCE_TEE_QUOTE_CBOR_TAG)
|
|
{
|
|
ret = SGX_ERROR_TLS_X509_INVALID_EXTENSION;
|
|
goto out;
|
|
}
|
|
|
|
cbor_evidence = cbor_tag_item(cbor_tagged_evidence);
|
|
if (!cbor_evidence || !cbor_isa_array(cbor_evidence)
|
|
|| !cbor_array_is_definite(cbor_evidence)) {
|
|
ret = SGX_ERROR_TLS_X509_INVALID_EXTENSION;
|
|
goto out;
|
|
}
|
|
|
|
if (cbor_array_size(cbor_evidence) != 2) {
|
|
ret = SGX_ERROR_TLS_X509_INVALID_EXTENSION;
|
|
goto out;
|
|
}
|
|
|
|
cbor_quote = cbor_array_get(cbor_evidence, /*index=*/0);
|
|
if (!cbor_quote || !cbor_isa_bytestring(cbor_quote) || !cbor_bytestring_is_definite(cbor_quote)
|
|
|| cbor_bytestring_length(cbor_quote) == 0) {
|
|
ret = SGX_ERROR_TLS_X509_INVALID_EXTENSION;
|
|
goto out;
|
|
}
|
|
|
|
quote_size = cbor_bytestring_length(cbor_quote);
|
|
if (quote_size < QUOTE_MIN_SIZE) {
|
|
ret = SGX_ERROR_TLS_X509_INVALID_EXTENSION;
|
|
goto out;
|
|
}
|
|
quote = (uint8_t*)malloc(quote_size);
|
|
if (!quote) {
|
|
ret = SGX_ERROR_OUT_OF_MEMORY;
|
|
goto out;
|
|
}
|
|
memcpy(quote, cbor_bytestring_handle(cbor_quote), quote_size);
|
|
|
|
cbor_claims = cbor_array_get(cbor_evidence, /*index=*/1);
|
|
if (!cbor_claims || !cbor_isa_bytestring(cbor_claims)
|
|
|| !cbor_bytestring_is_definite(cbor_claims)
|
|
|| cbor_bytestring_length(cbor_claims) == 0) {
|
|
ret = SGX_ERROR_TLS_X509_INVALID_EXTENSION;
|
|
goto out;
|
|
}
|
|
|
|
/* claims object is borrowed, no need to free separately */
|
|
claims_buf = cbor_bytestring_handle(cbor_claims);
|
|
claims_buf_size = cbor_bytestring_length(cbor_claims);
|
|
assert(claims_buf && claims_buf_size);
|
|
|
|
/* verify that TEE quote corresponds to the attached serialized claims */
|
|
ret = sgx_tls_compare_quote_hash(quote, claims_buf, claims_buf_size);
|
|
if (ret != SGX_SUCCESS)
|
|
{
|
|
goto out;
|
|
}
|
|
/* parse and verify CBOR claims */
|
|
cbor_claims_map = cbor_load(claims_buf, claims_buf_size, &cbor_result);
|
|
if (cbor_result.error.code != CBOR_ERR_NONE) {
|
|
ret = (cbor_result.error.code == CBOR_ERR_MEMERROR) ?
|
|
SGX_ERROR_OUT_OF_MEMORY : SGX_ERROR_TLS_X509_INVALID_EXTENSION;
|
|
goto out;
|
|
}
|
|
|
|
if (!cbor_isa_map(cbor_claims_map) || !cbor_map_is_definite(cbor_claims_map)
|
|
|| cbor_map_size(cbor_claims_map) < 1) {
|
|
ret = SGX_ERROR_TLS_X509_INVALID_EXTENSION;
|
|
goto out;
|
|
}
|
|
|
|
claims_pairs = cbor_map_handle(cbor_claims_map);
|
|
for (size_t i = 0; i < cbor_map_size(cbor_claims_map); i++) {
|
|
if (!claims_pairs[i].key || !cbor_isa_string(claims_pairs[i].key)
|
|
|| !cbor_string_is_definite(claims_pairs[i].key)
|
|
|| cbor_string_length(claims_pairs[i].key) == 0) {
|
|
ret = SGX_ERROR_TLS_X509_INVALID_EXTENSION;
|
|
goto out;
|
|
}
|
|
|
|
if (strncmp((char*)cbor_string_handle(claims_pairs[i].key), "pubkey-hash",
|
|
cbor_string_length(claims_pairs[i].key)) == 0) {
|
|
/* claim { "pubkey-hash" : serialized CBOR array hash-entry (as CBOR bstr) } */
|
|
if (!claims_pairs[i].value || !cbor_isa_bytestring(claims_pairs[i].value)
|
|
|| !cbor_bytestring_is_definite(claims_pairs[i].value)
|
|
|| cbor_bytestring_length(claims_pairs[i].value) == 0) {
|
|
ret = SGX_ERROR_TLS_X509_INVALID_EXTENSION;
|
|
goto out;
|
|
}
|
|
|
|
uint8_t* hash_entry_buf = cbor_bytestring_handle(claims_pairs[i].value);
|
|
size_t hash_entry_buf_size = cbor_bytestring_length(claims_pairs[i].value);
|
|
|
|
cbor_hash_entry = cbor_load(hash_entry_buf, hash_entry_buf_size, &cbor_result);
|
|
if (cbor_result.error.code != CBOR_ERR_NONE) {
|
|
ret = (cbor_result.error.code == CBOR_ERR_MEMERROR) ? SGX_ERROR_OUT_OF_EPC
|
|
: SGX_ERROR_TLS_X509_INVALID_EXTENSION;
|
|
goto out;
|
|
}
|
|
|
|
ret = compare_cert_pubkey_against_cbor_claim_hash(pem_pub_key, pem_pub_key_len, cbor_hash_entry);
|
|
if (ret != SGX_SUCCESS)
|
|
{
|
|
goto out;
|
|
}
|
|
}
|
|
}
|
|
|
|
memcpy(out_quote, quote, quote_size);
|
|
*out_quote_size = (uint32_t)quote_size;
|
|
ret = SGX_SUCCESS;
|
|
|
|
out:
|
|
SGX_TLS_SAFE_FREE(quote);
|
|
if (cbor_hash_entry)
|
|
cbor_decref(&cbor_hash_entry);
|
|
if (cbor_claims_map)
|
|
cbor_decref(&cbor_claims_map);
|
|
if (cbor_claims)
|
|
cbor_decref(&cbor_claims);
|
|
if (cbor_quote)
|
|
cbor_decref(&cbor_quote);
|
|
if (cbor_evidence)
|
|
cbor_decref(&cbor_evidence);
|
|
if (cbor_tagged_evidence)
|
|
cbor_decref(&cbor_tagged_evidence);
|
|
return ret;
|
|
}
|
|
|
|
sgx_status_t sgx_cert_get_public_key(
|
|
const sgx_cert_t* cert,
|
|
sgx_public_key_t* public_key)
|
|
{
|
|
sgx_status_t result = SGX_ERROR_UNEXPECTED;
|
|
const cert_t* impl = (const cert_t*)cert;
|
|
EVP_PKEY* pkey = NULL;
|
|
|
|
if (public_key)
|
|
memset(public_key, 0, sizeof(sgx_public_key_t));
|
|
|
|
if (!_cert_is_valid(impl) || !public_key) {
|
|
result = SGX_ERROR_INVALID_PARAMETER;
|
|
goto done;
|
|
}
|
|
|
|
if (!(pkey = X509_get_pubkey(impl->x509))) {
|
|
result = SGX_ERROR_UNEXPECTED;
|
|
goto done;
|
|
}
|
|
|
|
public_key->pkey = pkey;
|
|
pkey = NULL;
|
|
|
|
result = SGX_SUCCESS;
|
|
|
|
done:
|
|
|
|
if (pkey)
|
|
{
|
|
EVP_PKEY_free(pkey);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
sgx_status_t sgx_public_key_write_pem(
|
|
const sgx_public_key_t* key,
|
|
uint8_t* data,
|
|
size_t* size)
|
|
{
|
|
sgx_status_t result = SGX_ERROR_UNEXPECTED;
|
|
BIO* bio = NULL;
|
|
const sgx_public_key_t* impl = (const sgx_public_key_t*)key;
|
|
const char null_terminator = '\0';
|
|
|
|
/* If buffer is null, then size must be zero */
|
|
if (!key || (!data && *size != 0)) {
|
|
result = SGX_ERROR_INVALID_PARAMETER;
|
|
goto done;
|
|
}
|
|
|
|
/* Create memory BIO object to write key to */
|
|
if (!(bio = BIO_new(BIO_s_mem()))) {
|
|
result = SGX_ERROR_UNEXPECTED;
|
|
goto done;
|
|
}
|
|
|
|
/* Write key to BIO */
|
|
if (!PEM_write_bio_PUBKEY(bio, impl->pkey)) {
|
|
result = SGX_ERROR_UNEXPECTED;
|
|
goto done;
|
|
}
|
|
|
|
/* Write a NULL terminator onto BIO */
|
|
if (BIO_write(bio, &null_terminator, sizeof(null_terminator)) <= 0) {
|
|
result = SGX_ERROR_UNEXPECTED;
|
|
goto done;
|
|
}
|
|
|
|
/* Copy the BIO onto caller's memory */
|
|
{
|
|
BUF_MEM* mem;
|
|
|
|
if (!BIO_get_mem_ptr(bio, &mem)) {
|
|
result = SGX_ERROR_UNEXPECTED;
|
|
goto done;
|
|
}
|
|
|
|
/* If buffer is too small */
|
|
if (*size < mem->length)
|
|
{
|
|
*size = mem->length;
|
|
|
|
result = SGX_ERROR_OUT_OF_MEMORY;
|
|
goto done;
|
|
}
|
|
|
|
/* Copy result to output buffer */
|
|
memcpy(data, mem->data, mem->length);
|
|
*size = mem->length;
|
|
}
|
|
|
|
result = SGX_SUCCESS;
|
|
|
|
done:
|
|
|
|
if (bio)
|
|
BIO_free(bio);
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
sgx_status_t sgx_cert_find_extension(
|
|
const sgx_cert_t* cert,
|
|
const char* oid,
|
|
uint8_t* data,
|
|
uint32_t* size)
|
|
{
|
|
sgx_status_t result = SGX_ERROR_UNEXPECTED;
|
|
const cert_t* impl = (const cert_t*)cert;
|
|
const STACK_OF(X509_EXTENSION) * extensions;
|
|
int num_extensions;
|
|
|
|
/* Reject invalid parameters */
|
|
if (!_cert_is_valid(impl) || !oid || !size) {
|
|
result = SGX_ERROR_INVALID_PARAMETER;
|
|
goto done;
|
|
}
|
|
|
|
/* Set a pointer to the stack of extensions (possibly NULL) */
|
|
if (!(extensions = X509_get0_extensions(impl->x509)))
|
|
goto done;
|
|
|
|
/* Get the number of extensions (possibly zero) */
|
|
num_extensions = sk_X509_EXTENSION_num(extensions);
|
|
|
|
/* Find the certificate with this OID */
|
|
for (int i = 0; i < num_extensions; i++)
|
|
{
|
|
X509_EXTENSION* ext;
|
|
ASN1_OBJECT* obj;
|
|
sgx_oid_string_t ext_oid;
|
|
|
|
/* Get the i-th extension from the stack */
|
|
if (!(ext = sk_X509_EXTENSION_value(extensions, i)))
|
|
goto done;
|
|
|
|
/* Get the OID */
|
|
if (!(obj = X509_EXTENSION_get_object(ext)))
|
|
goto done;
|
|
|
|
/* Get the string name of the OID */
|
|
if (!OBJ_obj2txt(ext_oid.buf, sizeof(ext_oid.buf), obj, 1))
|
|
goto done;
|
|
|
|
/* If found then get the data */
|
|
if (strcmp(ext_oid.buf, oid) == 0)
|
|
{
|
|
ASN1_OCTET_STRING* str;
|
|
|
|
/* Get the data from the extension */
|
|
if (!(str = X509_EXTENSION_get_data(ext)))
|
|
goto done;
|
|
|
|
if (data)
|
|
{
|
|
memcpy(data, str->data, (size_t)str->length);
|
|
*size = (size_t)str->length;
|
|
result = SGX_SUCCESS;
|
|
goto done;
|
|
}
|
|
}
|
|
}
|
|
|
|
done:
|
|
return result;
|
|
}
|
|
|
|
sgx_status_t sgx_get_pubkey_from_cert(
|
|
const sgx_cert_t* cert,
|
|
uint8_t* pem_data,
|
|
size_t* pem_size)
|
|
{
|
|
sgx_status_t result = SGX_ERROR_UNEXPECTED;
|
|
sgx_public_key_t public_key;
|
|
|
|
if (SGX_SUCCESS != sgx_cert_get_public_key(cert, &public_key)) {
|
|
goto done;
|
|
}
|
|
|
|
if (SGX_SUCCESS != sgx_public_key_write_pem(&public_key, pem_data, pem_size)) {
|
|
goto done;
|
|
}
|
|
|
|
result = SGX_SUCCESS;
|
|
|
|
done:
|
|
|
|
if (public_key.pkey) {
|
|
EVP_PKEY_free(public_key.pkey);
|
|
public_key.pkey = NULL;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/* a common function to compare hash from target_buf with quote */
|
|
/* possible in_buf could be public_key for legacy or claims buf for interoperable ra-tls*/
|
|
sgx_status_t sgx_tls_compare_quote_hash(uint8_t *p_quote,
|
|
uint8_t* in_buf, size_t in_buf_len)
|
|
{
|
|
size_t report_data_size = 0;
|
|
uint32_t quote_type = 0;
|
|
uint8_t *p_report_data = NULL;
|
|
uint8_t *hash_in_buf = NULL; // buf to store hash by target_buf
|
|
unsigned char *p_sha = NULL;
|
|
sgx_status_t ret = SGX_SUCCESS;
|
|
|
|
if (p_quote == NULL) return SGX_ERROR_UNEXPECTED;
|
|
|
|
quote_type = *(uint32_t *)(p_quote + 4 * sizeof(uint8_t));
|
|
|
|
// get hash of cert pub key
|
|
report_data_size = (quote_type == 0x81) ? SGX_REPORT2_DATA_SIZE : SGX_REPORT_DATA_SIZE;
|
|
hash_in_buf = (uint8_t*)malloc(report_data_size);
|
|
if (!hash_in_buf) {
|
|
ret = SGX_ERROR_OUT_OF_MEMORY;
|
|
goto done;
|
|
}
|
|
if (quote_type == 0x81) {
|
|
uint16_t _version = 0;
|
|
memcpy((void*)&_version, p_quote, sizeof(_version));
|
|
|
|
if (_version == 5)
|
|
{
|
|
p_report_data = (uint8_t*)&(((sgx_report2_body_v1_5_t*)&(((sgx_quote5_t*)p_quote)->body))->report_data);
|
|
} else
|
|
{
|
|
p_report_data = (uint8_t*)(&((sgx_quote4_t *)p_quote)->report_body.report_data);
|
|
}
|
|
|
|
p_sha = SHA384(in_buf, in_buf_len,
|
|
reinterpret_cast<unsigned char *>(hash_in_buf));
|
|
if (p_sha == NULL ||
|
|
memcmp(p_sha, hash_in_buf, SHA384_DIGEST_LENGTH) != 0) {
|
|
ret = SGX_ERROR_UNEXPECTED;
|
|
goto done;
|
|
}
|
|
if (memcmp(p_report_data, hash_in_buf, SHA384_DIGEST_LENGTH) != 0) {
|
|
ret = SGX_ERROR_INVALID_SIGNATURE;
|
|
goto done;
|
|
}
|
|
} else if (quote_type == 0x00)
|
|
{
|
|
p_report_data = (uint8_t*)(&((sgx_quote3_t *)p_quote)->report_body.report_data);
|
|
p_sha = SHA256(in_buf, in_buf_len, reinterpret_cast<unsigned char *>(hash_in_buf));
|
|
if (p_sha == NULL ||
|
|
memcmp(p_sha, hash_in_buf, SHA256_DIGEST_LENGTH) != 0) {
|
|
ret = SGX_ERROR_UNEXPECTED;
|
|
goto done;
|
|
}
|
|
// compare hash, only compare the first 32 bytes
|
|
if (memcmp(p_report_data, hash_in_buf, SHA256_DIGEST_LENGTH) != 0) {
|
|
ret = SGX_ERROR_INVALID_SIGNATURE;
|
|
goto done;
|
|
}
|
|
}
|
|
else {
|
|
ret = SGX_ERROR_UNEXPECTED;
|
|
goto done;
|
|
}
|
|
|
|
done:
|
|
SGX_TLS_SAFE_FREE(hash_in_buf);
|
|
return ret;
|
|
}
|
|
|
|
// support functions
|
|
static char b64revtb[256] = {
|
|
-3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*0-15*/
|
|
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*16-31*/
|
|
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, /*32-47*/
|
|
52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -2, -1, -1, /*48-63*/
|
|
-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, /*64-79*/
|
|
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, /*80-95*/
|
|
-1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, /*96-111*/
|
|
41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1, /*112-127*/
|
|
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*128-143*/
|
|
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*144-159*/
|
|
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*160-175*/
|
|
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*176-191*/
|
|
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*192-207*/
|
|
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*208-223*/
|
|
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*224-239*/
|
|
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 /*240-255*/
|
|
};
|
|
|
|
static unsigned int raw_base64_decode(uint8_t *in,
|
|
uint8_t* out, int strict, int *err) {
|
|
unsigned int result = 0;
|
|
int x = 0;
|
|
unsigned char buf[3] = {0, 0, 0};
|
|
unsigned char *p = in, pad = 0;
|
|
|
|
*err = 0;
|
|
while (!pad) {
|
|
switch ((x = b64revtb[*p++])) {
|
|
case -3: /* NULL TERMINATOR */
|
|
if (((p - 1) - in) % 4) *err = 1;
|
|
return result;
|
|
case -2: /* PADDING CHARACTER. INVALID HERE */
|
|
if (((p - 1) - in) % 4 < 2) {
|
|
*err = 1;
|
|
return result;
|
|
} else if (((p - 1) - in) % 4 == 2) {
|
|
/* Make sure there's appropriate padding */
|
|
if (*p != '=') {
|
|
*err = 1;
|
|
return result;
|
|
}
|
|
buf[2] = 0;
|
|
pad = 2;
|
|
result++;
|
|
break;
|
|
} else {
|
|
pad = 1;
|
|
result += 2;
|
|
break;
|
|
}
|
|
return result;
|
|
case -1:
|
|
if (strict) {
|
|
*err = 2;
|
|
return result;
|
|
}
|
|
break;
|
|
default:
|
|
switch (((p - 1) - in) % 4) {
|
|
case 0:
|
|
buf[0] = (unsigned char)(x << 2);
|
|
break;
|
|
case 1:
|
|
buf[0] |= (unsigned char)(x >> 4);
|
|
buf[1] = (unsigned char)(x << 4);
|
|
break;
|
|
case 2:
|
|
buf[1] |= (unsigned char)(x >> 2);
|
|
buf[2] = (unsigned char)(x << 6);
|
|
break;
|
|
case 3:
|
|
buf[2] |= (unsigned char)x;
|
|
result += 3;
|
|
for (x = 0; x < 3 - pad; x++) *out++ = buf[x];
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
for (x = 0; x < 3 - pad; x++) *out++ = buf[x];
|
|
return result;
|
|
}
|
|
|
|
void PEM_strip_header_and_footer(
|
|
uint8_t *pem,
|
|
size_t pem_len,
|
|
uint8_t *stripped_pem,
|
|
size_t *real_pem_len
|
|
)
|
|
{
|
|
int i = 0;
|
|
int j = 0;
|
|
int real_begin = 0;
|
|
int real_end = 0;
|
|
for (i = 0; i < (int)pem_len; i++)
|
|
{
|
|
if (pem[i] == '\n' || pem[i] == '\r') break;
|
|
}
|
|
real_begin = i+1; // the character right after \n
|
|
|
|
// do not search \n from the exact end,
|
|
// which may contain one '\n' that we don't want
|
|
// to strip the footer "---- END Public Key -----"
|
|
for (i = (int)pem_len - 5; i >= 0; i--)
|
|
{
|
|
if (pem[i] == '\n' || pem[i] == '\r') break;
|
|
}
|
|
|
|
real_end = i;
|
|
|
|
// remove carriage return if any
|
|
for (i = real_begin, j = 0; i < real_end; i++)
|
|
{
|
|
if (pem[i] != '\n' && pem[i] != '\r')
|
|
{
|
|
stripped_pem[j] = pem[i];
|
|
j++;
|
|
}
|
|
}
|
|
*real_pem_len = j;
|
|
}
|
|
|
|
int PEM2DER_PublicKey_converter(const uint8_t *pem_pub, size_t pem_len, uint8_t *der, size_t *der_len)
|
|
{
|
|
uint8_t *stripped_pk_pem = NULL;
|
|
size_t stripped_len = 0;
|
|
|
|
int temp_len = 0;
|
|
int errorcode = 0;
|
|
|
|
if (pem_pub == NULL || pem_len == 0)
|
|
return 1;
|
|
|
|
stripped_pk_pem = (uint8_t*)malloc(pem_len);
|
|
if (stripped_pk_pem == NULL) return 1;
|
|
memset(stripped_pk_pem, 0x00, pem_len);
|
|
PEM_strip_header_and_footer((uint8_t*)pem_pub, pem_len, stripped_pk_pem, &stripped_len);
|
|
|
|
if (stripped_len <= 0 || stripped_len > pem_len)
|
|
{
|
|
free(stripped_pk_pem);
|
|
return 1;
|
|
}
|
|
temp_len = raw_base64_decode(stripped_pk_pem, der, 0, &errorcode);
|
|
free(stripped_pk_pem);
|
|
if (!errorcode)
|
|
{
|
|
*der_len = temp_len;
|
|
}
|
|
|
|
return errorcode;
|
|
}
|