From af400b39b0872c971bb14364294397360ea856b4 Mon Sep 17 00:00:00 2001 From: Dmitrii Kuvaiskii Date: Fri, 11 Nov 2022 06:13:49 -0800 Subject: [PATCH] DONTMERGE [tools/sgx] Interoperable RA-TLS Interoperable RA-TLS is a spec that allows different RA-TLS implementations (from different SGX frameworks, e.g. Gramine and Occlum) to interoperate and recognize each others' SGX evidence (SGX quotes and attached SGX claims). For example, Gramine app enclave can establish a TLS connection with an Occlum app enclave and verify its SGX evidence. The spec standardizes the OID extension for X.509 certs that is used for the SGX evidence. It also standardizes the format of the OID contents: a CBOR-formatted tag with an array that contains the SGX quote and a dict of related claims (with the most important dict item being the public key hash encoded as a CBOR array `hash-entry`). Current RA-TLS implementation creates X.509 certs that have both the old (legacy) OID with plain SGX quote as well as the new (standardized) OID with the CBOR-formatted SGX evidence. Thus, backward compatibility is preserved at a small cost of larger-sized certs. Signed-off-by: Dmitrii Kuvaiskii --- .ci/ubuntu18.04.dockerfile | 1 + .ci/ubuntu20.04.dockerfile | 1 + CI-Examples/ra-tls-mbedtls/src/server.c | 45 ++- Documentation/attestation.rst | 44 ++- Documentation/devel/building.rst | 2 +- Documentation/img/ratls-interoperable.svg | 1 + meson.build | 2 + subprojects/.gitignore | 1 + subprojects/libcbor-0.9.0.wrap | 7 + subprojects/packagefiles/libcbor/compile.sh | 44 +++ subprojects/packagefiles/libcbor/meson.build | 37 +++ tools/sgx/ra-tls/meson.build | 21 +- tools/sgx/ra-tls/ra_tls.h | 27 +- tools/sgx/ra-tls/ra_tls_attest.c | 325 ++++++++++++++++++- tools/sgx/ra-tls/ra_tls_verify_common.c | 293 ++++++++++++++++- tools/sgx/ra-tls/ra_tls_verify_dcap.c | 5 +- tools/sgx/ra-tls/ra_tls_verify_epid.c | 6 +- 17 files changed, 801 insertions(+), 61 deletions(-) create mode 100644 Documentation/img/ratls-interoperable.svg create mode 100644 subprojects/libcbor-0.9.0.wrap create mode 100644 subprojects/packagefiles/libcbor/compile.sh create mode 100644 subprojects/packagefiles/libcbor/meson.build diff --git a/.ci/ubuntu18.04.dockerfile b/.ci/ubuntu18.04.dockerfile index ec287b831b..4fc758d7f3 100644 --- a/.ci/ubuntu18.04.dockerfile +++ b/.ci/ubuntu18.04.dockerfile @@ -8,6 +8,7 @@ RUN apt-get update && env DEBIAN_FRONTEND=noninteractive apt-get install -y \ bison \ build-essential \ cargo \ + cmake \ curl \ flex \ gawk \ diff --git a/.ci/ubuntu20.04.dockerfile b/.ci/ubuntu20.04.dockerfile index fef644df87..2f772327df 100644 --- a/.ci/ubuntu20.04.dockerfile +++ b/.ci/ubuntu20.04.dockerfile @@ -8,6 +8,7 @@ RUN apt-get update && env DEBIAN_FRONTEND=noninteractive apt-get install -y \ build-essential \ cargo \ clang \ + cmake \ curl \ flex \ gawk \ diff --git a/CI-Examples/ra-tls-mbedtls/src/server.c b/CI-Examples/ra-tls-mbedtls/src/server.c index d5c63ae87c..c9e4a2facb 100644 --- a/CI-Examples/ra-tls-mbedtls/src/server.c +++ b/CI-Examples/ra-tls-mbedtls/src/server.c @@ -184,25 +184,42 @@ int main(int argc, char** argv) { return 1; } - /* user asks to maliciously modify the embedded SGX quote (for testing purposes) */ + /* user asks to maliciously modify the embedded SGX quote (for testing purposes); we + * have two quotes currently (with legacy OID and with standard TCG DICE OID), so we + * modify them both */ mbedtls_printf(" . Maliciously modifying SGX quote embedded in RA-TLS cert..."); fflush(stdout); - uint8_t oid[] = {0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF8, 0x4D, 0x8A, 0x39, 0x06}; - uint8_t* p = memmem(srvcert.v3_ext.p, srvcert.v3_ext.len, oid, sizeof(oid)); - if (!p) { - mbedtls_printf(" failed\n ! No embedded SGX quote found\n\n"); - goto exit; + uint8_t legacy_oid[] = {0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF8, 0x4D, 0x8A, 0x39, + 0x06}; + uint8_t standard_oid[] = { 0x06, 0x06, 0x67, 0x81, 0x05, 0x05, 0x04, 0x09 }; + struct { + uint8_t* oid; + size_t size; + size_t offset; + } oids[2] = { + { .oid = legacy_oid, .size = sizeof(legacy_oid), .offset = 5}, + { .oid = standard_oid, .size = sizeof(standard_oid), .offset = 10}, + }; + + for (size_t i = 0; i < 2; i++) { + uint8_t* p = memmem(srvcert.v3_ext.p, srvcert.v3_ext.len, oids[i].oid, + oids[i].size); + if (!p) { + mbedtls_printf(" failed\n ! No embedded SGX quote found\n\n"); + goto exit; + } + + p += oids[i].size; + p += oids[i].offset; /* jump somewhere in the middle of the SGX quote */ + if (p + sizeof(MALICIOUS_STR) > srvcert.v3_ext.p + srvcert.v3_ext.len) { + mbedtls_printf(" failed\n ! Size of embedded SGX quote is too small\n\n"); + goto exit; + } + + memcpy(p, MALICIOUS_STR, sizeof(MALICIOUS_STR)); } - p += sizeof(oid); - p += 5; /* jump somewhere in the middle of the SGX quote */ - if (p + sizeof(MALICIOUS_STR) > srvcert.v3_ext.p + srvcert.v3_ext.len) { - mbedtls_printf(" failed\n ! Size of embedded SGX quote is too small\n\n"); - goto exit; - } - - memcpy(p, MALICIOUS_STR, sizeof(MALICIOUS_STR)); mbedtls_printf(" ok\n"); } } else { diff --git a/Documentation/attestation.rst b/Documentation/attestation.rst index 8b5441bbf5..5d43a01e17 100644 --- a/Documentation/attestation.rst +++ b/Documentation/attestation.rst @@ -248,21 +248,45 @@ information (SGX quote). The additional information allows the remote user (verifier) of the certificate to verify that it is indeed communicating with an SGX enclave (attester). -.. image:: ./img/ratls.svg - :target: ./img/ratls.svg - :alt: Figure: The X.509 certificate generated by RA-TLS +.. image:: ./img/ratls-interoperable.svg + :target: ./img/ratls-interoperable.svg + :alt: Figure: The X.509 certificate generated by RA-TLS (standardized) The diagram above shows the standard X.509 certificate generated by RA-TLS (the diagram shows the DCAP based RA-TLS certificate, but the EPID based RA-TLS certificate is conceptually similar). This certificate is self-signed because the actual chain of trust is stored in the Intel SGX certificates embedded in -the SGX quote. The most important concept behind the RA-TLS certificate is that -it embeds the SGX quote (in one of the unused X.509 extension fields), which in -turn embeds the SGX report and the complete Intel SGX certificate chain. -Therefore, the RA-TLS certificate contains all the SGX-relevant information. -Also, notice how the SGX report's REPORTDATA field contains the secure hash of -the ephemeral public key generated by the enclavized application -- this is how -this RA-TLS certificate is tied to the enclavized application that generated it. +the SGX quote. + +The most important concept behind the RA-TLS certificate is that it embeds the +SGX quote (in the standardized X.509 extension field with the TCG DICE "tagged +evidence" OID), which in turn embeds the SGX report and the complete Intel SGX +certificate chain. In addition to the SGX quote, the certificate also contains +the so-called evidence claims, with the most important one being the "pubkey" +claim that contains the ephemeral public key (in DER format) generated by the +enclavized application. For reasons of standardization, the OID object and all +its sub-objects are encoded in the CBOR data format. + +Notice how the SGX report's REPORTDATA field contains the secure hash of the +complete claims buffer. This is how the RA-TLS certificate is tied to the +enclavized application that generated it (through the claims buffer that +contains the enclave-generated public key). In the end, the RA-TLS certificate +contains all the SGX-relevant information. + +.. image:: ./img/ratls.svg + :target: ./img/ratls.svg + :alt: Figure: The X.509 certificate generated by RA-TLS (legacy) + +The diagram above shows the non-standard X.509 certificate generated by RA-TLS. +This format, with a non-standard X.509 extension field (and a dummy OID), was +developed prior to the "Interoperable RA-TLS" specification, and is deprecated. +One can see that the non-standard OID directly embeds the SGX quote, doesn't +have the concept of claims and doesn't use the CBOR data format. + +Gramine generates RA-TLS certificates that contain both the new standard OID and +the legacy non-standard OID, for backward compatibility. In the future, legacy +OID will be dropped, and only the new standard OID will be put in the X.509 +certificates generated by Gramine. RA-TLS is shipped as three libraries: ``ra_tls_attest.so``, EPID based ``ra_tls_verify_epid.so`` and DCAP/ECDSA based ``ra_tls_verify_dcap.so``. diff --git a/Documentation/devel/building.rst b/Documentation/devel/building.rst index 74dc07a2af..149464846e 100644 --- a/Documentation/devel/building.rst +++ b/Documentation/devel/building.rst @@ -71,7 +71,7 @@ running, and Intel SGX SDK/PSW/DCAP must be installed. """""""""""""""""""" Run the following commands on Ubuntu to install SGX-related dependencies:: - sudo apt-get install -y libprotobuf-c-dev protobuf-c-compiler \ + sudo apt-get install -y cmake libprotobuf-c-dev protobuf-c-compiler \ protobuf-compiler python3-cryptography python3-pip python3-protobuf 2. Install Linux kernel with patched FSGSBASE diff --git a/Documentation/img/ratls-interoperable.svg b/Documentation/img/ratls-interoperable.svg new file mode 100644 index 0000000000..234746e54e --- /dev/null +++ b/Documentation/img/ratls-interoperable.svg @@ -0,0 +1 @@ +X.509 certSUBJECT = “CN=RATLS,O=…”ISSUER = “CN=RATLS,O=…”TCG DICE tagged evidence OIDCBOR tagged object = TBDCBOR array = []Claims (as CBOR bstr)pubkey” = PK (as CBOR bstr)… more claims in future …X.509 public key (PK), in DER formatRA-TLS certificate(DCAP-based,TCG DICE format)Self-signed certificateMust verify against expectedCompare CBOR-bstrclaims against this hashMust verify against Intel PCS provided attestation certificatesSGX Quote (as CBOR bstr)VERSION = 1..Intel SGX certs = SGX Report (EREPORT)REPORTDATA = sha256(claims)… measurements …Compare cert PK against this oneRegistered IANA OID: 2.23.133.5.4.9Registered IANA CBOR tag: TBDClaims is a CBOR map, serialized as CBOR bstr diff --git a/meson.build b/meson.build index 7d6e93b75f..d17f8cf121 100644 --- a/meson.build +++ b/meson.build @@ -263,6 +263,8 @@ if sgx protobuf_dep = dependency('libprotobuf-c') + libcbor_dep = subproject('libcbor-0.9.0').get_variable('libcbor_dep') + if dcap sgx_dcap_quoteverify_dep = cc.find_library('sgx_dcap_quoteverify') endif diff --git a/subprojects/.gitignore b/subprojects/.gitignore index a2c9535aaa..c5385662dd 100644 --- a/subprojects/.gitignore +++ b/subprojects/.gitignore @@ -4,6 +4,7 @@ /curl-*/ /gcc-*/ /glibc-*/ +/libcbor-*/ /mbedtls-*/ /musl-*/ /tomlc99-*/ diff --git a/subprojects/libcbor-0.9.0.wrap b/subprojects/libcbor-0.9.0.wrap new file mode 100644 index 0000000000..56a72cb502 --- /dev/null +++ b/subprojects/libcbor-0.9.0.wrap @@ -0,0 +1,7 @@ +[wrap-file] +directory = libcbor-0.9.0 +source_url = https://github.com/PJK/libcbor/archive/refs/tags/v0.9.0.tar.gz +source_fallback_url = https://packages.gramineproject.io/distfiles/libcbor-v0.9.0.tar.gz +source_filename = libcbor-0.9.0.tar.gz +source_hash = da81e4f9333e0086d4e2745183c7052f04ecc4dbcffcf910029df24f103c15d1 +patch_directory = libcbor diff --git a/subprojects/packagefiles/libcbor/compile.sh b/subprojects/packagefiles/libcbor/compile.sh new file mode 100644 index 0000000000..fc683c5e01 --- /dev/null +++ b/subprojects/packagefiles/libcbor/compile.sh @@ -0,0 +1,44 @@ +#!/bin/sh + +set -e + +log() { + echo "libcbor (static): $*" +} + +CURRENT_SOURCE_DIR="$1" +CURRENT_BUILD_DIR="$2" +PRIVATE_DIR="$3" + +BUILD_LOG=$(realpath "$CURRENT_BUILD_DIR/libcbor-build.log") +rm -f "$BUILD_LOG" + +log "see $BUILD_LOG for full build log" + +log "preparing sources..." + +rm -rf "$PRIVATE_DIR" +cp -ar "$CURRENT_SOURCE_DIR" "$PRIVATE_DIR" + +( + cd "$PRIVATE_DIR" + + log "running cmake..." + cmake \ + -DCMAKE_BUILD_TYPE=Release \ + -DCMAKE_POSITION_INDEPENDENT_CODE=ON \ + -DCMAKE_INSTALL_PREFIX="$CURRENT_BUILD_DIR" \ + . >>"$BUILD_LOG" 2>&1 + + log "running make..." + make -j"$(nproc)" >>"$BUILD_LOG" 2>&1 + make install >>"$BUILD_LOG" 2>&1 +) + +cp -ar "$CURRENT_BUILD_DIR"/include/. "$CURRENT_BUILD_DIR" +cp -ar "$CURRENT_BUILD_DIR"/lib/. "$CURRENT_BUILD_DIR" + +log "ls -la $CURRENT_BUILD_DIR" +ls -la $CURRENT_BUILD_DIR + +log "done" diff --git a/subprojects/packagefiles/libcbor/meson.build b/subprojects/packagefiles/libcbor/meson.build new file mode 100644 index 0000000000..69804926d6 --- /dev/null +++ b/subprojects/packagefiles/libcbor/meson.build @@ -0,0 +1,37 @@ +project('libcbor', 'c', version: '0.9.0') + +fs = import('fs') + +# NOTE: This is custom_target, because CMake integration in Meson doesn't work correctly with PIC +# static libraries, see https://github.com/mesonbuild/meson/issues/10764. +libcbor_lib = custom_target('libcbor', + command: [ + find_program('compile.sh'), + '@CURRENT_SOURCE_DIR@', + meson.current_build_dir(), + '@PRIVATE_DIR@', + ], + + input: 'CMakeLists.txt', + output: [ + 'libcbor.a', + 'cbor.h', + ], + + console: true, + install: false, +) + +# We can't use `include_directories('include')` because the `include/` dir is generated in the +# custom target above, but Meson checks for existence of the dir *before* running the target +libcbor_inc = include_directories('.') + +libcbor_dep = declare_dependency( + link_with: libcbor_lib[0], + # HACK: Use the generated "cbor.h" file and propagate it as part of the RA-TLS build dependency + # to enforce compile order, i.e., to make sure libcbor headers are ready before RA-TLS sources + # start compiling. + sources: libcbor_lib[1], + include_directories: libcbor_inc, + compile_args: '-Wno-strict-prototypes', +) diff --git a/tools/sgx/ra-tls/meson.build b/tools/sgx/ra-tls/meson.build index b57a459204..8b714b6ba9 100644 --- a/tools/sgx/ra-tls/meson.build +++ b/tools/sgx/ra-tls/meson.build @@ -13,6 +13,7 @@ libra_tls_attest = shared_library('ra_tls_attest', c_args: ra_tls_args, include_directories: pal_sgx_inc, # this is only for `sgx_arch.h` and `sgx_attest.h` dependencies: [ + libcbor_dep, mbedtls_static_dep, ], install: true, @@ -31,8 +32,9 @@ libra_tls_verify_epid = shared_library('ra_tls_verify_epid', c_args: ra_tls_args, include_directories: pal_sgx_inc, dependencies: [ - sgx_util_dep, + libcbor_dep, mbedtls_static_dep, + sgx_util_dep, ], install: true, install_rpath: join_paths(get_option('prefix'), get_option('libdir')), @@ -52,6 +54,7 @@ libsecret_prov_attest = shared_library('secret_prov_attest', c_args: ra_tls_args, include_directories: pal_sgx_inc, dependencies: [ + libcbor_dep, mbedtls_static_dep, sgx_util_dep, ], @@ -74,9 +77,10 @@ libsecret_prov_verify_epid = shared_library('secret_prov_verify_epid', c_args: ra_tls_args, include_directories: pal_sgx_inc, dependencies: [ - threads_dep, - sgx_util_dep, + libcbor_dep, mbedtls_static_dep, + sgx_util_dep, + threads_dep, ], install: true, install_rpath: join_paths(get_option('prefix'), get_option('libdir')), @@ -95,9 +99,10 @@ if dcap c_args: ra_tls_args, include_directories: pal_sgx_inc, dependencies: [ + libcbor_dep, + mbedtls_static_dep, sgx_dcap_quoteverify_dep, sgx_util_dep, - mbedtls_static_dep, ], install: true, install_rpath: join_paths(get_option('prefix'), get_option('libdir')), @@ -116,9 +121,10 @@ if dcap c_args: ra_tls_args, include_directories: pal_sgx_inc, dependencies: [ + libcbor_dep, + mbedtls_static_dep, sgx_dcap_quoteverify_dep, sgx_util_dep, - mbedtls_static_dep, ], install: true, install_rpath: join_paths(get_option('prefix'), get_option('libdir')), @@ -139,10 +145,11 @@ if dcap c_args: ra_tls_args, include_directories: pal_sgx_inc, dependencies: [ - threads_dep, + libcbor_dep, + mbedtls_static_dep, sgx_dcap_quoteverify_dep, sgx_util_dep, - mbedtls_static_dep, + threads_dep, ], install: true, install_rpath: join_paths(get_option('prefix'), get_option('libdir')), diff --git a/tools/sgx/ra-tls/ra_tls.h b/tools/sgx/ra-tls/ra_tls.h index 13ae00d3a3..f8af6930a4 100644 --- a/tools/sgx/ra-tls/ra_tls.h +++ b/tools/sgx/ra-tls/ra_tls.h @@ -30,11 +30,30 @@ #define PUB_KEY_SIZE_MAX 128 /* enough for the only currently supported algo (ECDSA-384) */ #define IAS_REQUEST_NONCE_LEN 32 -#define OID(N) \ - { 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF8, 0x4D, 0x8A, 0x39, (N) } -static const uint8_t g_quote_oid[] = OID(0x06); +/* below OID is actually wrong: it shouldn't have the first two bytes (0x06 0x09) because they + * represent the ASN.1 Type (6 = OBJECT IDENTIFIER) and ASN.1 Length (9 bytes); we don't modify it + * because it is non-standard anyway and we don't want to break backwards-compatibility */ +#define NON_STANDARD_INTEL_SGX_QUOTE_OID \ + { 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF8, 0x4D, 0x8A, 0x39, 0x06 } +static const uint8_t g_quote_oid[] = NON_STANDARD_INTEL_SGX_QUOTE_OID; static const size_t g_quote_oid_size = sizeof(g_quote_oid); +/* standard TCG DICE "tagged evidence" OID (2.23.133.5.4.9) */ +#define TCG_DICE_TAGGED_EVIDENCE_OID { 0x67, 0x81, 0x05, 0x05, 0x04, 0x09 } +#define TCG_DICE_TAGGED_EVIDENCE_OID_RAW { 0x06, 0x06, 0x67, 0x81, 0x05, 0x05, 0x04, 0x09 } +static const uint8_t g_evidence_oid[] = TCG_DICE_TAGGED_EVIDENCE_OID; +static const size_t g_evidence_oid_size = sizeof(g_evidence_oid); +static const uint8_t g_evidence_oid_raw[] = TCG_DICE_TAGGED_EVIDENCE_OID_RAW; +static const size_t g_evidence_oid_raw_size = sizeof(g_evidence_oid_raw); + +#define TCG_DICE_TAGGED_EVIDENCE_CBOR_TAG 0x1A75 /* FIXME: proper IANA tag once registered */ + +/* hash IDs per IANA: https://www.iana.org/assignments/named-information/named-information.xhtml */ +#define IANA_NAMED_INFO_HASH_ALG_REGISTRY_RESERVED 0 +#define IANA_NAMED_INFO_HASH_ALG_REGISTRY_SHA256 1 +#define IANA_NAMED_INFO_HASH_ALG_REGISTRY_SHA384 7 +#define IANA_NAMED_INFO_HASH_ALG_REGISTRY_SHA512 8 + typedef int (*verify_measurements_cb_t)(const char* mrenclave, const char* mrsigner, const char* isv_prod_id, const char* isv_svn); @@ -49,7 +68,7 @@ __attribute__ ((visibility("hidden"))) int cmp_crt_pk_against_quote_report_data(mbedtls_x509_crt* crt, sgx_quote_t* quote); __attribute__ ((visibility("hidden"))) -int extract_quote_and_verify_pubkey(mbedtls_x509_crt* crt, sgx_quote_t** out_quote, +int extract_quote_and_verify_claims(mbedtls_x509_crt* crt, sgx_quote_t** out_quote, size_t* out_quote_size); __attribute__ ((visibility("hidden"))) diff --git a/tools/sgx/ra-tls/ra_tls_attest.c b/tools/sgx/ra-tls/ra_tls_attest.c index 0ce47b7738..a7c3abe989 100644 --- a/tools/sgx/ra-tls/ra_tls_attest.c +++ b/tools/sgx/ra-tls/ra_tls_attest.c @@ -22,6 +22,8 @@ #include #include +#include + #include #include #include @@ -67,8 +69,10 @@ static ssize_t rw_file(const char* path, uint8_t* buf, size_t len, bool do_write return ret < 0 ? ret : bytes; } -/*! given public key \p pk, generate an RA-TLS certificate \p writecrt with \p quote embedded */ +/*! given public key \p pk, generate an RA-TLS certificate \p writecrt with \p quote (legacy format) + * and \p evidence (new standard format) embedded */ static int generate_x509(mbedtls_pk_context* pk, const uint8_t* quote, size_t quote_size, + const uint8_t* evidence, size_t evidence_size, mbedtls_x509write_cert* writecrt) { int ret; char* cert_timestamp_not_before = NULL; @@ -133,12 +137,21 @@ static int generate_x509(mbedtls_pk_context* pk, const uint8_t* quote, size_t qu if (ret < 0) goto out; - /* finally, embed the quote into the generated certificate (as X.509 extension) */ + /* embed the SGX quote into the generated certificate (as X.509 extension) in two formats: + * - legacy non-standard "SGX quote" OID (used from Gramine v1.0) + * - new standard TCG DICE "tagged evidence" OID (2.23.133.5.4.9) + */ ret = mbedtls_x509write_crt_set_extension(writecrt, (const char*)g_quote_oid, g_quote_oid_size, /*critical=*/0, quote, quote_size); if (ret < 0) goto out; + ret = mbedtls_x509write_crt_set_extension(writecrt, (const char*)g_evidence_oid, + g_evidence_oid_size, /*critical=*/0, evidence, + evidence_size); + if (ret < 0) + goto out; + ret = 0; out: free(cert_timestamp_not_before); @@ -162,8 +175,9 @@ static int sha256_over_pk(mbedtls_pk_context* pk, uint8_t* sha) { return mbedtls_sha256(pk_der, pk_der_size_byte, sha, /*is224=*/0); } -/*! given public key \p pk, generate an RA-TLS certificate \p writecrt */ -static int create_x509(mbedtls_pk_context* pk, mbedtls_x509write_cert* writecrt) { +/*! generate SGX quote with user_report_data equal to SHA256 hash over \p pk (legacy format) */ +static int generate_quote_with_pk_hash(mbedtls_pk_context* pk, uint8_t** out_quote, + size_t* out_quote_size) { sgx_report_data_t user_report_data = {0}; int ret = sha256_over_pk(pk, user_report_data.d); if (ret < 0) @@ -185,9 +199,310 @@ static int create_x509(mbedtls_pk_context* pk, mbedtls_x509write_cert* writecrt) return MBEDTLS_ERR_X509_FILE_IO_ERROR; } - ret = generate_x509(pk, quote, quote_size, writecrt); + *out_quote = quote; + *out_quote_size = (size_t)quote_size; + return 0; +} + +/*! create CBOR bstr from SHA256 hash of public key \p pk and copy it into \p out_cbor_bstr */ +static int cbor_bstr_from_pk_sha256(mbedtls_pk_context* pk, cbor_item_t** out_cbor_bstr) { + uint8_t sha256[SHA256_DIGEST_SIZE] = {0}; + int ret = sha256_over_pk(pk, sha256); + if (ret < 0) + return ret; + + cbor_item_t* cbor_bstr = cbor_build_bytestring(sha256, sizeof(sha256)); + if (!cbor_bstr) + return MBEDTLS_ERR_X509_ALLOC_FAILED; + + *out_cbor_bstr = cbor_bstr; + return 0; +} + +/*! generate hash-entry -- CBOR array with [ hash-alg-id, hash-value -- hash of pubkey ] */ +static int generate_serialized_hash_entry(mbedtls_pk_context* pk, uint8_t** out_hash_entry_buf, + size_t* out_hash_entry_buf_size) { + /* the hash-entry array as defined in Concise Software Identification Tags (CoSWID) */ + cbor_item_t* cbor_hash_entry = cbor_new_definite_array(2); + if (!cbor_hash_entry) + return MBEDTLS_ERR_X509_ALLOC_FAILED; + + /* RA-TLS always generates SHA256 hash over pubkey */ + cbor_item_t* cbor_hash_alg_id = cbor_build_uint8(IANA_NAMED_INFO_HASH_ALG_REGISTRY_SHA256); + if (!cbor_hash_alg_id) { + cbor_decref(&cbor_hash_entry); + return MBEDTLS_ERR_X509_ALLOC_FAILED; + } + + cbor_item_t* cbor_hash_value; + int ret = cbor_bstr_from_pk_sha256(pk, &cbor_hash_value); + if (ret < 0) { + cbor_decref(&cbor_hash_alg_id); + cbor_decref(&cbor_hash_entry); + return ret; + } + + int bool_ret = cbor_array_push(cbor_hash_entry, cbor_hash_alg_id); + if (!bool_ret) { + cbor_decref(&cbor_hash_value); + cbor_decref(&cbor_hash_alg_id); + cbor_decref(&cbor_hash_entry); + return MBEDTLS_ERR_X509_ALLOC_FAILED; + } + + bool_ret = cbor_array_push(cbor_hash_entry, cbor_hash_value); + if (!bool_ret) { + cbor_decref(&cbor_hash_value); + cbor_decref(&cbor_hash_alg_id); + cbor_decref(&cbor_hash_entry); + return MBEDTLS_ERR_X509_ALLOC_FAILED; + } + + /* cbor_hash_entry took ownership of hash_alg_id and hash_value cbor items */ + cbor_decref(&cbor_hash_alg_id); + cbor_decref(&cbor_hash_value); + + uint8_t* hash_entry_buf; + size_t hash_entry_buf_size; + cbor_serialize_alloc(cbor_hash_entry, &hash_entry_buf, &hash_entry_buf_size); + + cbor_decref(&cbor_hash_entry); + + if (!hash_entry_buf) + return MBEDTLS_ERR_X509_ALLOC_FAILED; + + *out_hash_entry_buf = hash_entry_buf; + *out_hash_entry_buf_size = hash_entry_buf_size; + return 0; +} + +/*! generate claims -- CBOR map with { "pubkey-hash" = } */ +static int generate_serialized_claims(mbedtls_pk_context* pk, uint8_t** out_claims_buf, + size_t* out_claims_buf_size) { + cbor_item_t* cbor_claims = cbor_new_definite_map(1); + if (!cbor_claims) + return MBEDTLS_ERR_X509_ALLOC_FAILED; + + cbor_item_t* cbor_pubkey_hash_key = cbor_build_string("pubkey-hash"); + if (!cbor_pubkey_hash_key) { + cbor_decref(&cbor_claims); + return MBEDTLS_ERR_X509_ALLOC_FAILED; + } + + uint8_t* hash_entry_buf; + size_t hash_entry_buf_size; + int ret = generate_serialized_hash_entry(pk, &hash_entry_buf, &hash_entry_buf_size); + if (ret < 0) { + cbor_decref(&cbor_pubkey_hash_key); + cbor_decref(&cbor_claims); + return ret; + } + + cbor_item_t* cbor_pubkey_hash_val = cbor_build_bytestring(hash_entry_buf, hash_entry_buf_size); + + free(hash_entry_buf); + + if (!cbor_pubkey_hash_val) { + cbor_decref(&cbor_pubkey_hash_key); + cbor_decref(&cbor_claims); + return MBEDTLS_ERR_X509_ALLOC_FAILED; + } + + struct cbor_pair cbor_pubkey_hash_pair = { .key = cbor_pubkey_hash_key, + .value = cbor_pubkey_hash_val }; + bool bool_ret = cbor_map_add(cbor_claims, cbor_pubkey_hash_pair); + if (!bool_ret) { + cbor_decref(&cbor_pubkey_hash_val); + cbor_decref(&cbor_pubkey_hash_key); + cbor_decref(&cbor_claims); + return MBEDTLS_ERR_X509_ALLOC_FAILED; + } + + uint8_t* claims_buf; + size_t claims_buf_size; + cbor_serialize_alloc(cbor_claims, &claims_buf, &claims_buf_size); + + cbor_decref(&cbor_pubkey_hash_val); + cbor_decref(&cbor_pubkey_hash_key); + cbor_decref(&cbor_claims); + + if (!claims_buf) + return MBEDTLS_ERR_X509_ALLOC_FAILED; + + *out_claims_buf = claims_buf; + *out_claims_buf_size = claims_buf_size; + return 0; +} + +/*! generate evidence -- CBOR tag with CBOR array of CBOR bstrs: [ quote, claims ] */ +static int generate_serialized_evidence(uint8_t* quote, size_t quote_size, uint8_t* claims, + size_t claims_size, uint8_t** out_evidence_buf, + size_t* out_evidence_buf_size) { + cbor_item_t* cbor_evidence = cbor_new_definite_array(2); + if (!cbor_evidence) + return MBEDTLS_ERR_X509_ALLOC_FAILED; + + cbor_item_t* cbor_quote = cbor_build_bytestring(quote, quote_size); + if (!cbor_quote) { + cbor_decref(&cbor_evidence); + return MBEDTLS_ERR_X509_ALLOC_FAILED; + } + + cbor_item_t* cbor_claims = cbor_build_bytestring(claims, claims_size); + if (!cbor_claims) { + cbor_decref(&cbor_quote); + cbor_decref(&cbor_evidence); + return MBEDTLS_ERR_X509_ALLOC_FAILED; + } + + int bool_ret = cbor_array_push(cbor_evidence, cbor_quote); + if (!bool_ret) { + cbor_decref(&cbor_claims); + cbor_decref(&cbor_quote); + cbor_decref(&cbor_evidence); + return MBEDTLS_ERR_X509_ALLOC_FAILED; + } + + bool_ret = cbor_array_push(cbor_evidence, cbor_claims); + if (!bool_ret) { + cbor_decref(&cbor_claims); + cbor_decref(&cbor_quote); + cbor_decref(&cbor_evidence); + return MBEDTLS_ERR_X509_ALLOC_FAILED; + } + + /* cbor_evidence took ownership of quote and claims cbor bstrs */ + cbor_decref(&cbor_claims); + cbor_decref(&cbor_quote); + + cbor_item_t* cbor_tagged_evidence = cbor_new_tag(TCG_DICE_TAGGED_EVIDENCE_CBOR_TAG); + if (!cbor_tagged_evidence) { + cbor_decref(&cbor_evidence); + return MBEDTLS_ERR_X509_ALLOC_FAILED; + } + + cbor_tag_set_item(cbor_tagged_evidence, cbor_evidence); + + uint8_t* evidence_buf; + size_t evidence_buf_size; + cbor_serialize_alloc(cbor_tagged_evidence, &evidence_buf, &evidence_buf_size); + cbor_decref(&cbor_evidence); + cbor_decref(&cbor_tagged_evidence); + + if (!evidence_buf) + return MBEDTLS_ERR_X509_ALLOC_FAILED; + + *out_evidence_buf = evidence_buf; + *out_evidence_buf_size = evidence_buf_size; + return 0; +} + +/*! generate SGX-quote evidence with \p pk as one of the embedded claims (standard format) */ +static int generate_evidence_with_claims(mbedtls_pk_context* pk, uint8_t** out_evidence, + size_t* out_evidence_size) { + /* + * SGX-quote evidence has the following serialized-CBOR format: + * + * CBOR object (major type 6, new CBOR tag for "ECDSA SGX Quotes") -> + * CBOR array -> + * [ + * 0: CBOR bstr (SGX quote with user_report_data = hash(serialized-cbor-map of claims)), + * 1: CBOR bstr (serialized-cbor-map of claims) + * ] + * + * where "serialized-cbor-map of claims" is as follows: + * + * CBOR map -> + * { + * "pubkey-hash" (req) : CBOR bstr (serialized-cbor-array hash-entry), + * "nonce" (opt) : CBOR bstr (arbitrary-sized nonce for per-session freshness) + * } + * + * where "serialized-cbor-array hash-entry" is as follows: + * + * CBOR array -> + * [ + * 0: CBOR uint (hash-alg-id), + * 1: CBOR bstr (hash of DER-formatted "SubjectPublicKeyInfo" field as CBOR bstr) + * ] + * + * For hash-alg-id values, see + * https://www.iana.org/assignments/named-information/named-information.xhtml + */ + uint8_t* claims = NULL; + uint8_t* quote = NULL; + uint8_t* evidence = NULL; + + size_t claims_size; + int ret = generate_serialized_claims(pk, &claims, &claims_size); + if (ret < 0) + goto out; + + sgx_report_data_t user_report_data = {0}; + ret = mbedtls_sha256(claims, claims_size, user_report_data.d, /*is224=*/0); + if (ret < 0) + goto out; + + ssize_t written = rw_file("/dev/attestation/user_report_data", user_report_data.d, + sizeof(user_report_data.d), /*do_write=*/true); + if (written != sizeof(user_report_data)) { + ret = MBEDTLS_ERR_X509_FILE_IO_ERROR; + goto out; + } + + quote = malloc(SGX_QUOTE_MAX_SIZE); + if (!quote) { + ret = MBEDTLS_ERR_X509_ALLOC_FAILED; + goto out; + } + + ssize_t quote_size = rw_file("/dev/attestation/quote", quote, SGX_QUOTE_MAX_SIZE, + /*do_write=*/false); + if (quote_size < 0) { + ret = MBEDTLS_ERR_X509_FILE_IO_ERROR; + goto out; + } + + size_t evidence_size; + ret = generate_serialized_evidence(quote, quote_size, claims, claims_size, &evidence, + &evidence_size); + if (ret < 0) + goto out; + + *out_evidence = evidence; + *out_evidence_size = (size_t)evidence_size; + ret = 0; +out: + free(quote); + free(claims); + return ret; +} + +/*! given public key \p pk, generate an RA-TLS certificate \p writecrt */ +static int create_x509(mbedtls_pk_context* pk, mbedtls_x509write_cert* writecrt) { + int ret; + + /* put both "legacy Gramine" OID with plain SGX quote as well as standardized TCG DICE "tagged + * evidence" OID with CBOR-formatted SGX quote into RA-TLS X.509 cert */ + uint8_t* quote = NULL; + uint8_t* evidence = NULL; + + /* TODO: this legacy OID with plain SGX quote should be removed at some point */ + size_t quote_size; + ret = generate_quote_with_pk_hash(pk, "e, "e_size); + if (ret < 0) + goto out; + + size_t evidence_size; + ret = generate_evidence_with_claims(pk, &evidence, &evidence_size); + if (ret < 0) + goto out; + + ret = generate_x509(pk, quote, quote_size, evidence, evidence_size, writecrt); +out: free(quote); + free(evidence); return ret; } diff --git a/tools/sgx/ra-tls/ra_tls_verify_common.c b/tools/sgx/ra-tls/ra_tls_verify_common.c index d8afd8bbd7..d999a1e036 100644 --- a/tools/sgx/ra-tls/ra_tls_verify_common.c +++ b/tools/sgx/ra-tls/ra_tls_verify_common.c @@ -13,6 +13,8 @@ #include #include +#include + #include #include #include @@ -170,32 +172,34 @@ static int find_oid(const uint8_t* exts, size_t exts_size, const uint8_t* oid, s return 0; } -/*! calculate sha256 over public key from \p crt and copy it into \p sha */ -static int sha256_over_crt_pk(mbedtls_x509_crt* crt, uint8_t* sha) { +/*! fill buffer \p pk_der with DER-formatted public key from \p crt */ +static int fill_crt_pk_der(mbedtls_x509_crt* crt, uint8_t* pk_der, size_t* pk_der_size) { mbedtls_ecp_keypair* key = mbedtls_pk_ec(crt->pk); if (key == NULL || key->MBEDTLS_PRIVATE(grp).id != MBEDTLS_ECP_DP_SECP384R1) { return MBEDTLS_ERR_PK_BAD_INPUT_DATA; } - uint8_t pk_der[PUB_KEY_SIZE_MAX] = {0}; - /* below function writes data at the end of the buffer */ - int pk_der_size_byte = mbedtls_pk_write_pubkey_der(&crt->pk, pk_der, sizeof(pk_der)); - if (pk_der_size_byte < 0) - return pk_der_size_byte; + int pk_der_size_int = mbedtls_pk_write_pubkey_der(&crt->pk, pk_der, *pk_der_size); + if (pk_der_size_int < 0) + return pk_der_size_int; /* move the data to the beginning of the buffer, to avoid pointer arithmetic later */ - memmove(pk_der, pk_der + PUB_KEY_SIZE_MAX - pk_der_size_byte, pk_der_size_byte); - - return mbedtls_sha256(pk_der, pk_der_size_byte, sha, /*is224=*/0); + memmove(pk_der, pk_der + *pk_der_size - pk_der_size_int, pk_der_size_int); + *pk_der_size = (size_t)pk_der_size_int; + return 0; } -/*! compares if report_data from \quote corresponds to sha256 of public key in \p crt */ +/*! compares if report_data from \p quote corresponds to sha256 of public key in \p crt */ int cmp_crt_pk_against_quote_report_data(mbedtls_x509_crt* crt, sgx_quote_t* quote) { - int ret; + uint8_t pk_der[PUB_KEY_SIZE_MAX] = {0}; + size_t pk_der_size = sizeof(pk_der); + int ret = fill_crt_pk_der(crt, pk_der, &pk_der_size); + if (ret < 0) + return ret; uint8_t sha[SHA256_DIGEST_SIZE]; - ret = sha256_over_crt_pk(crt, sha); + ret = mbedtls_sha256(pk_der, pk_der_size, sha, /*is224=*/0); if (ret < 0) return ret; @@ -206,10 +210,249 @@ int cmp_crt_pk_against_quote_report_data(mbedtls_x509_crt* crt, sgx_quote_t* quo return 0; } -int extract_quote_and_verify_pubkey(mbedtls_x509_crt* crt, sgx_quote_t** out_quote, - size_t* out_quote_size) { +/*! compares if CBOR array \p cbor_hash_entry from claims corresponds to public key in \p crt */ +static int cmp_crt_pk_against_cbor_claim_hash_entry(mbedtls_x509_crt* crt, + cbor_item_t* cbor_hash_entry) { + uint8_t pk_der[PUB_KEY_SIZE_MAX] = {0}; + size_t pk_der_size = sizeof(pk_der); + int ret = fill_crt_pk_der(crt, pk_der, &pk_der_size); + if (ret < 0) + return ret; + + if (!cbor_isa_array(cbor_hash_entry) || !cbor_array_is_definite(cbor_hash_entry) + || cbor_array_size(cbor_hash_entry) != 2) { + return MBEDTLS_ERR_X509_INVALID_EXTENSIONS; + } + + cbor_item_t* cbor_hash_alg_id = NULL; + cbor_item_t* cbor_hash_value = NULL; + + 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 = MBEDTLS_ERR_X509_INVALID_EXTENSIONS; + 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 = MBEDTLS_ERR_X509_INVALID_EXTENSIONS; + goto out; + } + + uint8_t hash_alg_id = cbor_get_uint8(cbor_hash_alg_id); + switch (hash_alg_id) { + /* assume that RESERVED (ID = 0) means SHA256 */ + case IANA_NAMED_INFO_HASH_ALG_REGISTRY_RESERVED: + case IANA_NAMED_INFO_HASH_ALG_REGISTRY_SHA256: { + if (cbor_bytestring_length(cbor_hash_value) != SHA256_DIGEST_SIZE) { + ret = MBEDTLS_ERR_X509_INVALID_EXTENSIONS; + goto out; + } + + uint8_t sha[SHA256_DIGEST_SIZE]; + ret = mbedtls_sha256(pk_der, pk_der_size, sha, /*is224=*/0); + if (ret < 0) + goto out; + + ret = memcmp(cbor_bytestring_handle(cbor_hash_value), sha, sizeof(sha)); + if (ret) { + ret = MBEDTLS_ERR_X509_SIG_MISMATCH; + goto out; + } + + break; + } + + /* TODO: add other recognized hash functions on a need basis */ + + default: + ret = MBEDTLS_ERR_X509_INVALID_EXTENSIONS; + goto out; + } + + ret = 0; +out: + if (cbor_hash_alg_id) + cbor_decref(&cbor_hash_alg_id); + if (cbor_hash_value) + cbor_decref(&cbor_hash_value); + return ret; +} + +static int extract_standard_quote_and_verify_claims(mbedtls_x509_crt* crt, sgx_quote_t** out_quote, + size_t* out_quote_size) { + /* for description of evidence format, see ra_tls_attest.c:generate_evidence_with_claims() */ + 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; + sgx_quote_t* quote = NULL; + + uint8_t* evidence_buf; + size_t evidence_buf_size; + int ret = find_oid(crt->v3_ext.p, crt->v3_ext.len, g_evidence_oid_raw, g_evidence_oid_raw_size, + &evidence_buf, &evidence_buf_size); + if (ret < 0) + return ret; + + struct cbor_load_result cbor_result; + cbor_tagged_evidence = cbor_load(evidence_buf, evidence_buf_size, &cbor_result); + if (cbor_result.error.code != CBOR_ERR_NONE) { + ERROR("Certificate: cannot parse 'tagged evidence' OID in CBOR format (error %d)\n", + cbor_result.error.code); + ret = (cbor_result.error.code == CBOR_ERR_MEMERROR) ? MBEDTLS_ERR_X509_ALLOC_FAILED + : MBEDTLS_ERR_X509_INVALID_EXTENSIONS; + goto out; + } + + if (!cbor_isa_tag(cbor_tagged_evidence) + || cbor_tag_value(cbor_tagged_evidence) != TCG_DICE_TAGGED_EVIDENCE_CBOR_TAG) { + ret = MBEDTLS_ERR_X509_INVALID_EXTENSIONS; + 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 = MBEDTLS_ERR_X509_INVALID_EXTENSIONS; + goto out; + } + + if (cbor_array_size(cbor_evidence) != 2) { + ret = MBEDTLS_ERR_X509_INVALID_EXTENSIONS; + 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 = MBEDTLS_ERR_X509_INVALID_EXTENSIONS; + goto out; + } + + size_t quote_size = cbor_bytestring_length(cbor_quote); + if (quote_size < sizeof(*quote)) { + ret = MBEDTLS_ERR_X509_INVALID_EXTENSIONS; + goto out; + } + quote = malloc(quote_size); + if (!quote) { + ret = MBEDTLS_ERR_X509_ALLOC_FAILED; + 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 = MBEDTLS_ERR_X509_INVALID_EXTENSIONS; + goto out; + } + + /* claims object is borrowed, no need to free separately */ + uint8_t* claims_buf = cbor_bytestring_handle(cbor_claims); + size_t claims_buf_size = cbor_bytestring_length(cbor_claims); + assert(claims_buf && claims_buf_size); + + /* verify that SGX quote corresponds to the attached serialized claims */ + uint8_t sha[SHA256_DIGEST_SIZE]; + ret = mbedtls_sha256(claims_buf, claims_buf_size, sha, /*is224=*/0); + if (ret < 0) { + ret = MBEDTLS_ERR_X509_INVALID_EXTENSIONS; + goto out; + } + + ret = memcmp(quote->body.report_body.report_data.d, sha, SHA256_DIGEST_SIZE); + if (ret) { + ret = MBEDTLS_ERR_X509_SIG_MISMATCH; + 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) { + ERROR("Certificate: cannot parse serialized CBOR map of claims (error %d)\n", + cbor_result.error.code); + ret = (cbor_result.error.code == CBOR_ERR_MEMERROR) ? MBEDTLS_ERR_X509_ALLOC_FAILED + : MBEDTLS_ERR_X509_INVALID_EXTENSIONS; + goto out; + } + + if (!cbor_isa_map(cbor_claims_map) || !cbor_map_is_definite(cbor_claims_map) + || cbor_map_size(cbor_claims_map) < 1) { + ret = MBEDTLS_ERR_X509_INVALID_EXTENSIONS; + goto out; + } + + struct cbor_pair* 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 = MBEDTLS_ERR_X509_INVALID_EXTENSIONS; + 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 = MBEDTLS_ERR_X509_INVALID_EXTENSIONS; + 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) { + ERROR("Certificate: cannot parse 'pubkey-hash' array in CBOR format (error %d)\n", + cbor_result.error.code); + ret = (cbor_result.error.code == CBOR_ERR_MEMERROR) ? MBEDTLS_ERR_X509_ALLOC_FAILED + : MBEDTLS_ERR_X509_INVALID_EXTENSIONS; + goto out; + } + + ret = cmp_crt_pk_against_cbor_claim_hash_entry(crt, cbor_hash_entry); + if (ret < 0) + goto out; + } else { + INFO("WARNING: Unrecognized claim in TCG DICE 'tagged evidence' OID, ignoring.\n"); + } + } + + *out_quote = quote; + *out_quote_size = quote_size; + ret = 0; +out: + if (ret < 0) + 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; +} + +static int extract_legacy_quote_and_verify_pubkey(mbedtls_x509_crt* crt, sgx_quote_t** out_quote, + size_t* out_quote_size) { sgx_quote_t* quote; size_t quote_size; + int ret = find_oid(crt->v3_ext.p, crt->v3_ext.len, g_quote_oid, g_quote_oid_size, (uint8_t**)"e, "e_size); if (ret < 0) @@ -223,11 +466,29 @@ int extract_quote_and_verify_pubkey(mbedtls_x509_crt* crt, sgx_quote_t** out_quo if (ret < 0) return ret; - *out_quote = quote; + /* quote returned by find_oid() is a pointer somewhere inside of the X.509 cert object; let's + * copy it into a newly allocated object to correctly track ownership */ + sgx_quote_t* allocated_quote = malloc(quote_size); + if (!allocated_quote) + return MBEDTLS_ERR_X509_ALLOC_FAILED; + memcpy(allocated_quote, quote, quote_size); + + *out_quote = allocated_quote; *out_quote_size = quote_size; return 0; } +int extract_quote_and_verify_claims(mbedtls_x509_crt* crt, sgx_quote_t** out_quote, + size_t* out_quote_size) { + int ret = extract_standard_quote_and_verify_claims(crt, out_quote, out_quote_size); + if (!ret) + return 0; + + INFO("WARNING: TCG DICE 'tagged evidence' OID was not found. Checking non-standard legacy " + "Gramine OID. This will be deprecated in the future.\n"); + return extract_legacy_quote_and_verify_pubkey(crt, out_quote, out_quote_size); +} + void ra_tls_set_measurement_callback(int (*f_cb)(const char* mrenclave, const char* mrsigner, const char* isv_prod_id, const char* isv_svn)) { g_verify_measurements_cb = f_cb; diff --git a/tools/sgx/ra-tls/ra_tls_verify_dcap.c b/tools/sgx/ra-tls/ra_tls_verify_dcap.c index 572b47c9de..85fd6e883e 100644 --- a/tools/sgx/ra-tls/ra_tls_verify_dcap.c +++ b/tools/sgx/ra-tls/ra_tls_verify_dcap.c @@ -99,6 +99,7 @@ int ra_tls_verify_callback(void* data, mbedtls_x509_crt* crt, int depth, uint32_ (void)data; int ret; + sgx_quote_t* quote = NULL; uint8_t* supplemental_data = NULL; uint32_t supplemental_data_size = 0; @@ -116,9 +117,8 @@ int ra_tls_verify_callback(void* data, mbedtls_x509_crt* crt, int depth, uint32_ } /* extract SGX quote from "quote" OID extension from crt */ - sgx_quote_t* quote; size_t quote_size; - ret = extract_quote_and_verify_pubkey(crt, "e, "e_size); + ret = extract_quote_and_verify_claims(crt, "e, "e_size); if (ret < 0) goto out; @@ -212,6 +212,7 @@ int ra_tls_verify_callback(void* data, mbedtls_x509_crt* crt, int depth, uint32_ ret = 0; out: + free(quote); free(supplemental_data); return ret; } diff --git a/tools/sgx/ra-tls/ra_tls_verify_epid.c b/tools/sgx/ra-tls/ra_tls_verify_epid.c index e3491b28c0..2f30fc8386 100644 --- a/tools/sgx/ra-tls/ra_tls_verify_epid.c +++ b/tools/sgx/ra-tls/ra_tls_verify_epid.c @@ -121,6 +121,8 @@ int ra_tls_verify_callback(void* data, mbedtls_x509_crt* crt, int depth, uint32_ (void)data; int ret; + sgx_quote_t* quote = NULL; + struct ias_context_t* ias = NULL; char* ias_pub_key_pem = NULL; @@ -160,9 +162,8 @@ int ra_tls_verify_callback(void* data, mbedtls_x509_crt* crt, int depth, uint32_ goto out; /* extract SGX quote from "quote" OID extension from crt */ - sgx_quote_t* quote; size_t quote_size; - ret = extract_quote_and_verify_pubkey(crt, "e, "e_size); + ret = extract_quote_and_verify_claims(crt, "e, "e_size); if (ret < 0) goto out; @@ -256,6 +257,7 @@ int ra_tls_verify_callback(void* data, mbedtls_x509_crt* crt, int depth, uint32_ if (ias) ias_cleanup(ias); + free(quote); free(ias_pub_key_pem); free(quote_from_ias); free(report_data);