diff --git a/CI-Examples/python/python.manifest.template b/CI-Examples/python/python.manifest.template index dfe1c47b9c..e4515d6627 100644 --- a/CI-Examples/python/python.manifest.template +++ b/CI-Examples/python/python.manifest.template @@ -39,6 +39,10 @@ sgx.edmm_enable = {{ 'true' if env.get('EDMM', '0') == '1' else 'false' }} sgx.enclave_size = "1G" sgx.max_threads = {{ '1' if env.get('EDMM', '0') == '1' else '32' }} +sgx.kss = true +sgx.isvfamilyid = "0x00112233445566778899aabbccddeeff" +sgx.isvextprodid = "0xcafef00dcafef00df00dcafef00dcafe" + sgx.remote_attestation = "{{ ra_type }}" sgx.trusted_files = [ diff --git a/CI-Examples/python/scripts/sgx-quote.py b/CI-Examples/python/scripts/sgx-quote.py index 8a79b4f15e..759417cffe 100644 --- a/CI-Examples/python/scripts/sgx-quote.py +++ b/CI-Examples/python/scripts/sgx-quote.py @@ -21,11 +21,16 @@ quote = f.read() print(f"Extracted SGX quote with size = {len(quote)} and the following fields:") +print(f" ISVEXTPRODID: {quote[80:96].hex()}") print(f" ATTRIBUTES.FLAGS: {quote[96:104].hex()} [ Debug bit: {quote[96] & 2 > 0} ]") print(f" ATTRIBUTES.XFRM: {quote[104:112].hex()}") print(f" MRENCLAVE: {quote[112:144].hex()}") print(f" MRSIGNER: {quote[176:208].hex()}") +print(f" CONFIGID: {quote[240:272].hex()}") +print(f" {quote[272:304].hex()}") print(f" ISVPRODID: {quote[304:306].hex()}") print(f" ISVSVN: {quote[306:308].hex()}") +print(f" CONFIGSVN: {quote[308:310].hex()}") +print(f" ISVFAMILYID: {quote[352:368].hex()}") print(f" REPORTDATA: {quote[368:400].hex()}") print(f" {quote[400:432].hex()}") diff --git a/CI-Examples/python/scripts/sgx-report.py b/CI-Examples/python/scripts/sgx-report.py index 1992d294c3..2dcc44d4a1 100644 --- a/CI-Examples/python/scripts/sgx-report.py +++ b/CI-Examples/python/scripts/sgx-report.py @@ -23,11 +23,16 @@ report = f.read() print(f"Generated SGX report with size = {len(report)} and the following fields:") +print(f" ISVEXTPRODID: {report[32:48].hex()}") print(f" ATTRIBUTES.FLAGS: {report[48:56].hex()} [ Debug bit: {report[48] & 2 > 0} ]") print(f" ATTRIBUTES.XFRM: {report[56:64].hex()}") print(f" MRENCLAVE: {report[64:96].hex()}") print(f" MRSIGNER: {report[128:160].hex()}") +print(f" CONFIGID: {report[192:224].hex()}") +print(f" {report[224:256].hex()}") print(f" ISVPRODID: {report[256:258].hex()}") print(f" ISVSVN: {report[258:260].hex()}") +print(f" CONFIGSVN: {report[260:262].hex()}") +print(f" ISVFAMILYID: {report[304:320].hex()}") print(f" REPORTDATA: {report[320:352].hex()}") print(f" {report[352:384].hex()}") diff --git a/CI-Examples/ra-tls-mbedtls-with-kss/.gitignore b/CI-Examples/ra-tls-mbedtls-with-kss/.gitignore new file mode 100644 index 0000000000..f45b2e1293 --- /dev/null +++ b/CI-Examples/ra-tls-mbedtls-with-kss/.gitignore @@ -0,0 +1,7 @@ +/*.tar.gz +/OUTPUT +/client +/mbedtls +/server +/ssl/ca.* +/ssl/server.* diff --git a/CI-Examples/ra-tls-mbedtls-with-kss/Makefile b/CI-Examples/ra-tls-mbedtls-with-kss/Makefile new file mode 100644 index 0000000000..780ec8b893 --- /dev/null +++ b/CI-Examples/ra-tls-mbedtls-with-kss/Makefile @@ -0,0 +1,111 @@ +# Copyright (C) 2023 Gramine contributors +# SPDX-License-Identifier: BSD-3-Clause + +ARCH_LIBDIR ?= /lib/$(shell $(CC) -dumpmachine) + +ifeq ($(DEBUG),1) +GRAMINE_LOG_LEVEL = debug +CFLAGS += -O0 -ggdb3 +else +GRAMINE_LOG_LEVEL = error +CFLAGS += -O2 +endif + +CFLAGS += -fPIE +LDFLAGS += -pie + +.PHONY: all +all: app dcap + +.PHONY: app +app: ssl/server.crt server.manifest.sgx server.sig client + +.PHONY: dcap +dcap: client_dcap.manifest.sgx client_dcap.sig + +############################# SSL DATA DEPENDENCY ############################# + +# SSL data: key and x.509 self-signed certificate +ssl/server.crt: ssl/ca_config.conf + openssl genrsa -out ssl/ca.key 2048 + openssl req -x509 -new -nodes -key ssl/ca.key -sha256 -days 1024 -out ssl/ca.crt -config ssl/ca_config.conf + openssl genrsa -out ssl/server.key 2048 + openssl req -new -key ssl/server.key -out ssl/server.csr -config ssl/ca_config.conf + openssl x509 -req -days 360 -in ssl/server.csr -CA ssl/ca.crt -CAkey ssl/ca.key -CAcreateserial -out ssl/server.crt + +######################### CLIENT/SERVER EXECUTABLES ########################### + +CFLAGS += $(shell pkg-config --cflags mbedtls_gramine) \ + $(shell pkg-config --cflags ra_tls_gramine) + +# no need for `pkg-config --libs ra_tls_gramine` because programs use dlopen +LDFLAGS += -ldl -Wl,--enable-new-dtags $(shell pkg-config --libs mbedtls_gramine) + +server: src/server.c + $(CC) $< $(CFLAGS) $(LDFLAGS) -o $@ + +client: src/client.c + $(CC) $< $(CFLAGS) $(LDFLAGS) -o $@ + +############################### SERVER MANIFEST ############################### + +server.manifest: server.manifest.template server + gramine-manifest \ + -Dlog_level=$(GRAMINE_LOG_LEVEL) \ + -Darch_libdir=$(ARCH_LIBDIR) \ + $< > $@ + +server.manifest.sgx server.sig &: server.manifest + gramine-sgx-sign \ + --manifest $< \ + --output $<.sgx + +########################### CLIENT (DCAP) MANIFEST ############################ + +client_dcap.manifest: client.manifest.template client + gramine-manifest \ + -Dlog_level=$(GRAMINE_LOG_LEVEL) \ + -Darch_libdir=$(ARCH_LIBDIR) \ + $< >$@ + +client_dcap.manifest.sgx client_dcap.sig: sgx_sign_client_dcap + @: + +.INTERMEDIATE: sgx_sign_client_dcap +sgx_sign_client_dcap: client_dcap.manifest + gramine-sgx-sign \ + --manifest $< \ + --output $<.sgx + +############################### SGX CHECKS FOR CI ############################# + +.PHONY: check_dcap +check_dcap: app dcap + gramine-sgx server >/dev/null & SERVER_ID=$$!; \ + ../../scripts/wait_for_server 60 127.0.0.1 4433; \ + ./client dcap | tee OUTPUT; \ + ./client dcap 0 0 0 0 | tee -a OUTPUT; \ + kill -9 $$SERVER_ID + @grep -q "using default SGX-measurement verification callback" OUTPUT && echo "[ Success 1/4 ]" + @grep -q "using our own SGX-measurement verification callback" OUTPUT && echo "[ Success 2/4 ]" + @grep -q "Verifying peer X.509 certificate... ok" OUTPUT && echo "[ Success 3/4 ]" + @(exit `grep -c "failed" "OUTPUT"`) && echo "[ Success 4/4 ]" + @rm OUTPUT + +.PHONY: check_dcap_fail +check_dcap_fail: app dcap + gramine-sgx server --test-malicious-quote >/dev/null & SERVER_ID=$$!; \ + ../../scripts/wait_for_server 60 127.0.0.1 4433; \ + ./client dcap && exit 1 || echo "[ Success 1/1 ]"; \ + kill -9 $$SERVER_ID + +################################## CLEANUP #################################### + +.PHONY: clean +clean: + $(RM) -r \ + *.sig *.manifest.sgx *.manifest server client *.so *.so.* OUTPUT + +.PHONY: distclean +distclean: clean + $(RM) -r ssl/ca.* ssl/server.* diff --git a/CI-Examples/ra-tls-mbedtls-with-kss/README.md b/CI-Examples/ra-tls-mbedtls-with-kss/README.md new file mode 100644 index 0000000000..905f3f2e23 --- /dev/null +++ b/CI-Examples/ra-tls-mbedtls-with-kss/README.md @@ -0,0 +1,197 @@ +# RA-TLS Minimal Example + +This directory contains the Makefile, the template server manifest, and the +minimal server and client written against the mbedTLS library. + +The server and client are based on `ssl_server.c` and `ssl_client1.c` example +programs shipped with mbedTLS. We modified them to allow using RA-TLS flows. In +particular, the server uses a self-signed RA-TLS cert with the SGX quote +embedded in it via `ra_tls_create_key_and_crt_der()`. The client uses an RA-TLS +verification callback to verify the server RA-TLS certificate via +`ra_tls_verify_callback_der()`. + +This example uses the RA-TLS libraries `ra_tls_attest.so` for server and +`ra_tls_verify_dcap.so` for client. These libraries are installed together with +Gramine (you need `meson setup ... -Ddcap=enabled`, which is the default). The +DCAP software infrastructure must be installed and work correctly on the host. + +For more documentation about ECDSA (DCAP) remote attestation scheme, refer to +https://gramine.readthedocs.io/en/stable/attestation.html. + +## RA-TLS server + +The server is supposed to run in the SGX enclave with Gramine and RA-TLS +dlopen-loaded. If the server is started not in the SGX enclave, then it falls +back to using normal X.509 PKI flows. + +If server is run with a command-line argument ``--test-malicious-quote``, then +the server will maliciously modify the SGX quote before sending to the client. +This is useful for testing purposes. + +## RA-TLS client + +The client is supposed to run on a trusted machine (*not* in an SGX enclave). +If RA-TLS library `ra_tls_verify_dcap.so` is not requested by user via `dcap` +command-line argument respectively, the client falls back to using normal X.509 +PKI flows (specified as `native` command-line argument). + +It is also possible to run the client in an SGX enclave. This will create a +secure channel between two Gramine SGX processes, possibly running on different +machines. It can be used as an example of in-enclave remote attestation and +verification. + +If client is run without additional command-line arguments, it uses default +RA-TLS verification callback that compares `MRENCLAVE`, `MRSIGNER`, +`ISV_PROD_ID` and `ISV_SVN` against the corresonding `RA_TLS_*` environment +variables. `MRENCLAVE`, `MRSIGNER` and `ISV_PROD_ID` are expected to match +`RA_TLS_*` ones. `ISV_SVN` is expected to be equal or greater than `RA_TLS_ISV_SVN`. +To run the client with its own verification callback, execute it with four +additional command-line arguments (see the source code for details). + +Also, because this example builds and uses debug SGX enclaves (`sgx.debug` is +set to `true`), we use environment variable `RA_TLS_ALLOW_DEBUG_ENCLAVE_INSECURE=1`. +Note that in production environments, you must *not* use this option! + +Moreover, we set `RA_TLS_ALLOW_OUTDATED_TCB_INSECURE=1`, +`RA_TLS_ALLOW_HW_CONFIG_NEEDED=1` and `RA_TLS_ALLOW_SW_HARDENING_NEEDED=1` to +allow performing the tests when some of Intel's security advisories haven't been +addressed (for example, when the microcode or architectural enclaves aren't +fully up-to-date). Note that in production environments, you must carefully +analyze whether to use these options! + +## KSS features +In addition to the existing ra-tls example, this example shows the use of the following four values +provided by the Key Separation and Sharing (KSS) feature of SGX: +* ISV Extended Production ID (ISV_EXT_PROD_ID) +* ISV Family ID (ISV_FAMILY_ID) +* Config ID (CONFIG_ID) +* Config SVN (CONFIG_SVN) + +The KSS enable, ISV_EXT_PROD_ID and ISV_FAMILY_ID of the server enclave are specified +in the manifest (`sgx.kss`, `sgx.isvextprodid` and `sgx.isvfamilyid`), +while the CONFIG_ID and CONFIG_SVN are specified via command line arguments or +environment variables when executing the `gramine-sgx` command. + +These values cannot be used when running under non-SGX conditions. + +# Quick Start + +In most of the examples below, you need to tell the client what values it should +expect for `MRENCLAVE`, `MRSIGNER`, `ISV_PROD_ID` and `ISV_SVN`. One way to +obtain them is to run `gramine-sgx-sigstruct-view server.sig`. + +For all examples, we set the following environment variables: +```sh +export RA_TLS_ALLOW_DEBUG_ENCLAVE_INSECURE=1 +export RA_TLS_ALLOW_OUTDATED_TCB_INSECURE=1 +export RA_TLS_ALLOW_HW_CONFIG_NEEDED=1 +export RA_TLS_ALLOW_SW_HARDENING_NEEDED=1 +``` + +- Normal non-RA-TLS flows; without SGX and without Gramine: + +```sh +make app +./server & +./client native +# client will successfully connect to the server via normal x.509 PKI flows +kill %% +``` + +- RA-TLS flows with SGX and with Gramine, ECDSA-based (DCAP) attestation: + +```sh +make clean +make app dcap + +gramine-sgx --config-id= \ + --config-svn= \ + ./server & + +RA_TLS_MRENCLAVE= \ +RA_TLS_MRSIGNER= \ +RA_TLS_ISV_PROD_ID= \ +RA_TLS_ISV_SVN= \ +RA_TLS_ISV_EXT_PROD_ID= \ +RA_TLS_ISV_FAMILY_ID= \ +RA_TLS_CONFIG_ID= \ +RA_TLS_CONFIG_SVN= \ +./client dcap + +# client will successfully connect to the server via RA-TLS/DCAP flows +kill %% +``` + +The environment variables SGX_CONFIG_ID and SGX_CONFIG_SVN can also be used to specify +the Config ID and Config SVN. +If both command line arguments and environment variables are specified, +an error is returned if those values do not match. + +``` sh +SGX_CONFIG_ID= \ +SGX_CONFIG_SVN= \ +gramine-sgx ./server +``` + +- RA-TLS flows with SGX and with Gramine, client with its own verification callback: + +```sh +make clean +make app dcap + +gramine-sgx --config-id= \ + --config-svn= \ + ./server & + +./client dcap \ + \ + \ + \ + \ + \ + \ + \ + + +# client will successfully connect to the server via RA-TLS/DCAP flows +kill %% +``` + +Note that the Config ID must be specified as a hexadecimal string and the Config SVN as a decimal number. + +- RA-TLS flows with SGX and with Gramine, server sends malicious SGX quote: + +```sh +make clean +make app dcap + +gramine-sgx ./server --test-malicious-quote & +./client dcap + +# client will fail to verify the malicious SGX quote and will *not* connect to the server +kill %% +``` + +- RA-TLS flows with SGX and with Gramine, running DCAP client in SGX: + +```sh +make clean +make app dcap + +gramine-sgx --config-id= \ + --config-svn= \ + ./server & + +gramine-sgx ./client_dcap dcap \ + \ + \ + \ + \ + \ + \ + \ + + +# client will successfully connect to the server via RA-TLS/DCAP flows +kill %% +``` diff --git a/CI-Examples/ra-tls-mbedtls-with-kss/client.manifest.template b/CI-Examples/ra-tls-mbedtls-with-kss/client.manifest.template new file mode 100644 index 0000000000..e1dc00149d --- /dev/null +++ b/CI-Examples/ra-tls-mbedtls-with-kss/client.manifest.template @@ -0,0 +1,53 @@ +# Copyright (C) 2023 Gramine contributors +# SPDX-License-Identifier: BSD-3-Clause + +# Client manifest file + +libos.entrypoint = "/client" + +loader.log_level = "{{ log_level }}" + +loader.env.LD_LIBRARY_PATH = "/lib:/usr/local/lib:{{ arch_libdir }}:/usr{{ arch_libdir }}" +loader.env.LC_ALL = "C" + +loader.env.RA_TLS_CLIENT_INSIDE_SGX = "1" + +loader.insecure__use_cmdline_argv = true +loader.insecure__use_host_env = true + +fs.mounts = [ + { path = "/lib", uri = "file:{{ gramine.runtimedir() }}" }, + { path = "/usr/local/lib", uri = "file:/usr/local/lib" }, + { path = "{{ arch_libdir }}", uri = "file:{{ arch_libdir }}" }, + { path = "/usr{{ arch_libdir }}", uri = "file:/usr{{ arch_libdir }}" }, + { path = "/etc", uri = "file:/etc" }, + { path = "/client", uri = "file:client" }, +] + +sys.enable_extra_runtime_domain_names_conf = true + +sgx.debug = true +sgx.edmm_enable = {{ 'true' if env.get('EDMM', '0') == '1' else 'false' }} +sgx.enclave_size = "512M" +sgx.max_threads = {{ '1' if env.get('EDMM', '0') == '1' else '4' }} + +sgx.trusted_files = [ + "file:client", + "file:{{ gramine.runtimedir() }}/", + "file:/usr/local/lib/", + "file:{{ arch_libdir }}/", + "file:/usr{{ arch_libdir }}/", + "file:ssl/ca.crt", +] + +sgx.allowed_files = [ + "file:/etc/nsswitch.conf", + "file:/etc/host.conf", + "file:/etc/ethers", + "file:/etc/hosts", + "file:/etc/group", + "file:/etc/passwd", + "file:/etc/gai.conf", + "file:/etc/ssl/certs/ca-certificates.crt", + "file:/etc/sgx_default_qcnl.conf", +] diff --git a/CI-Examples/ra-tls-mbedtls-with-kss/server.manifest.template b/CI-Examples/ra-tls-mbedtls-with-kss/server.manifest.template new file mode 100644 index 0000000000..f2ad8a87fa --- /dev/null +++ b/CI-Examples/ra-tls-mbedtls-with-kss/server.manifest.template @@ -0,0 +1,50 @@ +# Copyright (C) 2023 Gramine contributors +# SPDX-License-Identifier: BSD-3-Clause + +# RA-TLS manifest file example + +libos.entrypoint = "/server" + +loader.log_level = "{{ log_level }}" + +loader.env.LD_LIBRARY_PATH = "/lib:{{ arch_libdir }}:/usr{{ arch_libdir }}" + +loader.insecure__use_cmdline_argv = true + +sys.enable_sigterm_injection = true + +fs.mounts = [ + { path = "/lib", uri = "file:{{ gramine.runtimedir() }}" }, + { path = "{{ arch_libdir }}", uri = "file:{{ arch_libdir }}" }, + { path = "/usr{{ arch_libdir }}", uri = "file:/usr{{ arch_libdir }}" }, + { path = "/etc", uri = "file:/etc" }, + { path = "/server", uri = "file:server" }, +] + +sgx.debug = true +sgx.edmm_enable = {{ 'true' if env.get('EDMM', '0') == '1' else 'false' }} + +sgx.remote_attestation = "dcap" + +sgx.kss = true +sgx.isvfamilyid = "00112233445566778899aabbccddeeff" +sgx.isvextprodid = "cafef00dcafef00df00dcafef00dcafe" + +sgx.trusted_files = [ + "file:server", + "file:{{ gramine.runtimedir() }}/", + "file:{{ arch_libdir }}/", + "file:/usr{{ arch_libdir }}/", + "file:ssl/ca.crt", + "file:ssl/server.crt", + "file:ssl/server.key", +] + +sgx.allowed_files = [ + "file:/etc/nsswitch.conf", + "file:/etc/ethers", + "file:/etc/hosts", + "file:/etc/group", + "file:/etc/passwd", + "file:/etc/gai.conf", +] diff --git a/CI-Examples/ra-tls-mbedtls-with-kss/src/client.c b/CI-Examples/ra-tls-mbedtls-with-kss/src/client.c new file mode 100644 index 0000000000..1f71d3b302 --- /dev/null +++ b/CI-Examples/ra-tls-mbedtls-with-kss/src/client.c @@ -0,0 +1,553 @@ +/* SPDX-License-Identifier: Apache-2.0 */ +/* Copyright (C) 2006-2015, ARM Limited, All Rights Reserved + * 2020, Intel Labs + */ + +/* + * SSL client demonstration program (with RA-TLS). + * This program is originally based on an mbedTLS example ssl_client1.c but uses RA-TLS flows (SGX + * Remote Attestation flows) if RA-TLS library is required by user. + * Note that this program builds against mbedTLS 3.x. + */ + +#include "mbedtls/build_info.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#define mbedtls_fprintf fprintf +#define mbedtls_printf printf + +#define MBEDTLS_EXIT_SUCCESS EXIT_SUCCESS +#define MBEDTLS_EXIT_FAILURE EXIT_FAILURE + +#include "mbedtls/ctr_drbg.h" +#include "mbedtls/debug.h" +#include "mbedtls/entropy.h" +#include "mbedtls/error.h" +#include "mbedtls/net_sockets.h" +#include "mbedtls/ssl.h" + +#include "ra_tls.h" + +/* RA-TLS: on client, only need to register ra_tls_verify_callback_extended_der() for cert + * verification. */ +int (*ra_tls_verify_callback_extended_der_f)(uint8_t* der_crt, size_t der_crt_size, + struct ra_tls_verify_callback_results* results); + +/* RA-TLS: if specified in command-line options, use our own callback to verify SGX measurements */ +void (*ra_tls_set_measurement_callback_f)(int (*f_cb)(const char* mrenclave, const char* mrsigner, + const char* isv_prod_id, const char* isv_svn, + const char* isv_ext_prod_id, const char* isv_family_id, + const char* config_id, const char* config_svn)); + +#define SERVER_PORT "4433" +#define SERVER_NAME "localhost" +#define GET_REQUEST "GET / HTTP/1.0\r\n\r\n" + +#define DEBUG_LEVEL 0 + +#define CA_CRT_PATH "ssl/ca.crt" + +static void my_debug(void* ctx, int level, const char* file, int line, const char* str) { + ((void)level); + + mbedtls_fprintf((FILE*)ctx, "%s:%04d: %s\n", file, line, str); + fflush((FILE*)ctx); +} + +static int parse_hex(const char* hex, void* buffer, size_t buffer_size) { + if (strlen(hex) != buffer_size * 2) + return -1; + + for (size_t i = 0; i < buffer_size; i++) { + if (!isxdigit(hex[i * 2]) || !isxdigit(hex[i * 2 + 1])) + return -1; + sscanf(hex + i * 2, "%02hhx", &((uint8_t*)buffer)[i]); + } + return 0; +} + + +/* expected SGX measurements in binary form */ +static char g_expected_mrenclave[32]; +static char g_expected_mrsigner[32]; +static char g_expected_isv_prod_id[2]; +static char g_expected_isv_svn[2]; +static char g_expected_isv_ext_prod_id[16]; +static char g_expected_isv_family_id[16]; +static char g_expected_config_id[64]; +static char g_expected_config_svn[2]; + +static bool g_verify_mrenclave = false; +static bool g_verify_mrsigner = false; +static bool g_verify_isv_prod_id = false; +static bool g_verify_isv_svn = false; +static bool g_verify_isv_ext_prod_id = false; +static bool g_verify_isv_family_id = false; +static bool g_verify_config_id = false; +static bool g_verify_config_svn = false; + +/* RA-TLS: our own callback to verify SGX measurements including KSS values */ +static int my_verify_measurements(const char* mrenclave, const char* mrsigner, + const char* isv_prod_id, const char* isv_svn, + const char* isv_ext_prod_id, const char* isv_family_id, + const char* config_id, const char* config_svn) { + + assert(mrenclave && mrsigner && isv_prod_id && isv_svn); + assert(isv_ext_prod_id && isv_family_id && config_id && config_svn); + + if (g_verify_mrenclave && + memcmp(mrenclave, g_expected_mrenclave, sizeof(g_expected_mrenclave))) + return -1; + + if (g_verify_mrsigner && + memcmp(mrsigner, g_expected_mrsigner, sizeof(g_expected_mrsigner))) + return -1; + + if (g_verify_isv_prod_id && + memcmp(isv_prod_id, g_expected_isv_prod_id, sizeof(g_expected_isv_prod_id))) + return -1; + + if (g_verify_isv_svn && + memcmp(isv_svn, g_expected_isv_svn, sizeof(g_expected_isv_svn))) + return -1; + + if (g_verify_isv_ext_prod_id && + memcmp(isv_ext_prod_id, g_expected_isv_ext_prod_id, sizeof(g_expected_isv_ext_prod_id))) + return -1; + + if (g_verify_isv_family_id && + memcmp(isv_family_id, g_expected_isv_family_id, sizeof(g_expected_isv_family_id))) + return -1; + + if (g_verify_config_id && + memcmp(config_id, g_expected_config_id, sizeof(g_expected_config_id))) + return -1; + + if (g_verify_config_svn && + memcmp(config_svn, g_expected_config_svn, sizeof(g_expected_config_svn))) + return -1; + + return 0; +} + +/* RA-TLS: mbedTLS-specific callback to verify the x509 certificate */ +static int my_verify_callback(void* data, mbedtls_x509_crt* crt, int depth, uint32_t* flags) { + if (depth != 0) { + /* the cert chain in RA-TLS consists of single self-signed cert, so we expect depth 0 */ + return MBEDTLS_ERR_X509_INVALID_FORMAT; + } + if (flags) { + /* mbedTLS sets flags to signal that the cert is not to be trusted (e.g., it is not + * correctly signed by a trusted CA; since RA-TLS uses self-signed certs, we don't care + * what mbedTLS thinks and ignore internal cert verification logic of mbedTLS */ + *flags = 0; + } + return ra_tls_verify_callback_extended_der_f(crt->raw.p, crt->raw.len, + (struct ra_tls_verify_callback_results*)data); +} + +static bool getenv_client_inside_sgx(void) { + char* str = getenv("RA_TLS_CLIENT_INSIDE_SGX"); + if (!str) + return false; + + return !strcmp(str, "1") || !strcmp(str, "true") || !strcmp(str, "TRUE"); +} + +int main(int argc, char** argv) { + int ret; + size_t len; + int exit_code = MBEDTLS_EXIT_FAILURE; + mbedtls_net_context server_fd; + uint32_t flags; + unsigned char buf[1024]; + const char* pers = "ssl_client1"; + bool in_sgx = getenv_client_inside_sgx(); + + char* error; + void* ra_tls_verify_lib = NULL; + ra_tls_verify_callback_extended_der_f = NULL; + ra_tls_set_measurement_callback_f = NULL; + struct ra_tls_verify_callback_results my_verify_callback_results = {0}; + + mbedtls_entropy_context entropy; + mbedtls_ctr_drbg_context ctr_drbg; + mbedtls_ssl_context ssl; + mbedtls_ssl_config conf; + mbedtls_x509_crt cacert; + +#if defined(MBEDTLS_DEBUG_C) + mbedtls_debug_set_threshold(DEBUG_LEVEL); +#endif + + mbedtls_net_init(&server_fd); + mbedtls_ssl_init(&ssl); + mbedtls_ssl_config_init(&conf); + mbedtls_ctr_drbg_init(&ctr_drbg); + mbedtls_x509_crt_init(&cacert); + mbedtls_entropy_init(&entropy); + +#if defined(MBEDTLS_USE_PSA_CRYPTO) || defined(MBEDTLS_SSL_PROTO_TLS1_3) + psa_status_t status = psa_crypto_init(); + if (status != PSA_SUCCESS) { + mbedtls_printf("Failed to initialize PSA Crypto implementation: %d\n", (int)status); + return 1; + } +#endif /* MBEDTLS_USE_PSA_CRYPTO || MBEDTLS_SSL_PROTO_TLS1_3 */ + + if (argc < 2 || + (strcmp(argv[1], "native") && strcmp(argv[1], "dcap"))) { + mbedtls_printf("USAGE: %s native|dcap [SGX measurements]\n", argv[0]); + return 1; + } + + if (!strcmp(argv[1], "dcap")) { + if (in_sgx) { + /* + * RA-TLS verification with DCAP inside SGX enclave uses dummies instead of real + * functions from libsgx_urts.so, thus we don't need to load this helper library. + */ + ra_tls_verify_lib = dlopen("libra_tls_verify_dcap_gramine.so", RTLD_LAZY); + if (!ra_tls_verify_lib) { + mbedtls_printf("%s\n", dlerror()); + mbedtls_printf("User requested RA-TLS verification with DCAP inside SGX but cannot find lib\n"); + mbedtls_printf("Please make sure that you are using client_dcap.manifest\n"); + return 1; + } + } else { + void* helper_sgx_urts_lib = dlopen("libsgx_urts.so", RTLD_NOW | RTLD_GLOBAL); + if (!helper_sgx_urts_lib) { + mbedtls_printf("%s\n", dlerror()); + mbedtls_printf("User requested RA-TLS verification with DCAP but cannot find helper" + " libsgx_urts.so lib\n"); + return 1; + } + + ra_tls_verify_lib = dlopen("libra_tls_verify_dcap.so", RTLD_LAZY); + if (!ra_tls_verify_lib) { + mbedtls_printf("%s\n", dlerror()); + mbedtls_printf("User requested RA-TLS verification with DCAP but cannot find lib\n"); + return 1; + } + } + } + + if (ra_tls_verify_lib) { + ra_tls_verify_callback_extended_der_f = dlsym(ra_tls_verify_lib, + "ra_tls_verify_callback_extended_der"); + if ((error = dlerror()) != NULL) { + mbedtls_printf("%s\n", error); + return 1; + } + + ra_tls_set_measurement_callback_f = dlsym(ra_tls_verify_lib, "ra_tls_set_measurement_including_kss_callback"); + if ((error = dlerror()) != NULL) { + mbedtls_printf("%s\n", error); + return 1; + } + } + + if (argc > 2 && ra_tls_verify_lib) { + if (argc != 10) { + mbedtls_printf("USAGE: %s %s " + " \n" + " " + " " + " (isv_prod_id, isv_svn and config_svn as decimal, others in hex; set to 0 to ignore)\n", + argv[0], argv[1]); + return 1; + } + + mbedtls_printf("[ using our own SGX-measurement verification callback" + " (via command line options) ]\n"); + + g_verify_mrenclave = true; + g_verify_mrsigner = true; + g_verify_isv_prod_id = true; + g_verify_isv_svn = true; + g_verify_isv_ext_prod_id = true; + g_verify_isv_family_id = true; + g_verify_config_id = true; + g_verify_config_svn = true; + + (*ra_tls_set_measurement_callback_f)(my_verify_measurements); + + if (!strcmp(argv[2], "0")) { + mbedtls_printf(" - ignoring MRENCLAVE\n"); + g_verify_mrenclave = false; + } else if (parse_hex(argv[2], g_expected_mrenclave, sizeof(g_expected_mrenclave)) < 0) { + mbedtls_printf("Cannot parse MRENCLAVE!\n"); + return 1; + } + + if (!strcmp(argv[3], "0")) { + mbedtls_printf(" - ignoring MRSIGNER\n"); + g_verify_mrsigner = false; + } else if (parse_hex(argv[3], g_expected_mrsigner, sizeof(g_expected_mrsigner)) < 0) { + mbedtls_printf("Cannot parse MRSIGNER!\n"); + return 1; + } + + if (!strcmp(argv[4], "0")) { + mbedtls_printf(" - ignoring ISV_PROD_ID\n"); + g_verify_isv_prod_id = false; + } else { + errno = 0; + uint16_t isv_prod_id = (uint16_t)strtoul(argv[4], NULL, 10); + if (errno) { + mbedtls_printf("Cannot parse ISV_PROD_ID!\n"); + return 1; + } + memcpy(g_expected_isv_prod_id, &isv_prod_id, sizeof(isv_prod_id)); + } + + if (!strcmp(argv[5], "0")) { + mbedtls_printf(" - ignoring ISV_SVN\n"); + g_verify_isv_svn = false; + } else { + errno = 0; + uint16_t isv_svn = (uint16_t)strtoul(argv[5], NULL, 10); + if (errno) { + mbedtls_printf("Cannot parse ISV_SVN\n"); + return 1; + } + memcpy(g_expected_isv_svn, &isv_svn, sizeof(isv_svn)); + } + + if (!strcmp(argv[6], "0")) { + mbedtls_printf(" - ignoring ISV_EXT_PROD_ID\n"); + g_verify_isv_ext_prod_id = false; + } else if (parse_hex(argv[6], g_expected_isv_ext_prod_id, sizeof(g_expected_isv_ext_prod_id)) < 0) { + mbedtls_printf("Cannot parse ISV_EXT_PROD_ID!\n"); + return 1; + } + + if (!strcmp(argv[7], "0")) { + mbedtls_printf(" - ignoring ISV_FAMILY_ID\n"); + g_verify_isv_family_id = false; + } else if (parse_hex(argv[7], g_expected_isv_family_id, sizeof(g_expected_isv_family_id)) < 0) { + mbedtls_printf("Cannot parse ISV_FAMILY_ID!\n"); + return 1; + } + + if (!strcmp(argv[8], "0")) { + mbedtls_printf(" - ignoring CONFIG_ID\n"); + g_verify_config_id = false; + } else if (parse_hex(argv[8], g_expected_config_id, sizeof(g_expected_config_id)) < 0) { + mbedtls_printf("Cannot parse CONFIG_ID!\n"); + return 1; + } + + if (!strcmp(argv[9], "0")) { + mbedtls_printf(" - ignoring CONFIG_SVN\n"); + g_verify_config_svn = false; + } else { + errno = 0; + uint16_t config_svn = (uint16_t)strtoul(argv[9], NULL, 10); + if (errno) { + mbedtls_printf("Cannot parse CONFIG_SVN\n"); + return 1; + } + memcpy(g_expected_config_svn, &config_svn, sizeof(config_svn)); + } + } else if (ra_tls_verify_lib) { + mbedtls_printf("[ using default SGX-measurement verification callback" + " (via RA_TLS_* environment variables) ]\n"); + (*ra_tls_set_measurement_callback_f)(NULL); /* just to test RA-TLS code */ + } else { + mbedtls_printf("[ using normal TLS flows ]\n"); + } + + mbedtls_printf("\n . Seeding the random number generator..."); + fflush(stdout); + + ret = mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, &entropy, + (const unsigned char*)pers, strlen(pers)); + if (ret != 0) { + mbedtls_printf(" failed\n ! mbedtls_ctr_drbg_seed returned %d\n", ret); + goto exit; + } + + mbedtls_printf(" ok\n"); + + mbedtls_printf(" . Connecting to tcp/%s/%s...", SERVER_NAME, SERVER_PORT); + fflush(stdout); + + ret = mbedtls_net_connect(&server_fd, SERVER_NAME, SERVER_PORT, MBEDTLS_NET_PROTO_TCP); + if (ret != 0) { + mbedtls_printf(" failed\n ! mbedtls_net_connect returned %d\n\n", ret); + goto exit; + } + + mbedtls_printf(" ok\n"); + + mbedtls_printf(" . Setting up the SSL/TLS structure..."); + fflush(stdout); + + ret = mbedtls_ssl_config_defaults(&conf, MBEDTLS_SSL_IS_CLIENT, MBEDTLS_SSL_TRANSPORT_STREAM, + MBEDTLS_SSL_PRESET_DEFAULT); + if (ret != 0) { + mbedtls_printf(" failed\n ! mbedtls_ssl_config_defaults returned %d\n\n", ret); + goto exit; + } + + mbedtls_printf(" ok\n"); + + mbedtls_printf(" . Loading the CA root certificate ..."); + fflush(stdout); + + ret = mbedtls_x509_crt_parse_file(&cacert, CA_CRT_PATH); + if (ret < 0) { + mbedtls_printf( " failed\n ! mbedtls_x509_crt_parse_file returned -0x%x\n\n", -ret ); + goto exit; + } + + mbedtls_ssl_conf_authmode(&conf, MBEDTLS_SSL_VERIFY_OPTIONAL); + mbedtls_ssl_conf_ca_chain(&conf, &cacert, NULL); + mbedtls_printf(" ok\n"); + + if (ra_tls_verify_lib) { + /* use RA-TLS verification callback; this will overwrite CA chain set up above */ + mbedtls_printf(" . Installing RA-TLS callback ..."); + mbedtls_ssl_conf_verify(&conf, &my_verify_callback, &my_verify_callback_results); + mbedtls_printf(" ok\n"); + } + + mbedtls_ssl_conf_rng(&conf, mbedtls_ctr_drbg_random, &ctr_drbg); + mbedtls_ssl_conf_dbg(&conf, my_debug, stdout); + + ret = mbedtls_ssl_setup(&ssl, &conf); + if (ret != 0) { + mbedtls_printf(" failed\n ! mbedtls_ssl_setup returned %d\n\n", ret); + goto exit; + } + + ret = mbedtls_ssl_set_hostname(&ssl, SERVER_NAME); + if (ret != 0) { + mbedtls_printf(" failed\n ! mbedtls_ssl_set_hostname returned %d\n\n", ret); + goto exit; + } + + mbedtls_ssl_set_bio(&ssl, &server_fd, mbedtls_net_send, mbedtls_net_recv, NULL); + + mbedtls_printf(" . Performing the SSL/TLS handshake..."); + fflush(stdout); + + while ((ret = mbedtls_ssl_handshake(&ssl)) != 0) { + if (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE) { + mbedtls_printf(" failed\n ! mbedtls_ssl_handshake returned -0x%x\n", -ret); + mbedtls_printf(" ! ra_tls_verify_callback_results:\n" + " attestation_scheme=%d, err_loc=%d, \n", + my_verify_callback_results.attestation_scheme, + my_verify_callback_results.err_loc); + switch (my_verify_callback_results.attestation_scheme) { + case RA_TLS_ATTESTATION_SCHEME_DCAP: + mbedtls_printf(" dcap.func_verify_quote_result=0x%x, " + "dcap.quote_verification_result=0x%x\n\n", + my_verify_callback_results.dcap.func_verify_quote_result, + my_verify_callback_results.dcap.quote_verification_result); + break; + default: + mbedtls_printf(" ! unknown attestation scheme!\n\n"); + break; + } + + goto exit; + } + } + + mbedtls_printf(" ok\n"); + + mbedtls_printf(" . Verifying peer X.509 certificate..."); + + flags = mbedtls_ssl_get_verify_result(&ssl); + if (flags != 0) { + char vrfy_buf[512]; + mbedtls_printf(" failed\n"); + mbedtls_x509_crt_verify_info(vrfy_buf, sizeof(vrfy_buf), " ! ", flags); + mbedtls_printf("%s\n", vrfy_buf); + + /* verification failed for whatever reason, fail loudly */ + goto exit; + } else { + mbedtls_printf(" ok\n"); + } + + mbedtls_printf(" > Write to server:"); + fflush(stdout); + + len = sprintf((char*)buf, GET_REQUEST); + + while ((ret = mbedtls_ssl_write(&ssl, buf, len)) <= 0) { + if (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE) { + mbedtls_printf(" failed\n ! mbedtls_ssl_write returned %d\n\n", ret); + goto exit; + } + } + + len = ret; + mbedtls_printf(" %lu bytes written\n\n%s", len, (char*)buf); + + mbedtls_printf(" < Read from server:"); + fflush(stdout); + + do { + len = sizeof(buf) - 1; + memset(buf, 0, sizeof(buf)); + ret = mbedtls_ssl_read(&ssl, buf, len); + + if (ret == MBEDTLS_ERR_SSL_WANT_READ || ret == MBEDTLS_ERR_SSL_WANT_WRITE) + continue; + + if (ret == MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY) + break; + + if (ret < 0) { + mbedtls_printf("failed\n ! mbedtls_ssl_read returned %d\n\n", ret); + break; + } + + if (ret == 0) { + mbedtls_printf("\n\nEOF\n\n"); + break; + } + + len = ret; + mbedtls_printf(" %lu bytes read\n\n%s", len, (char*)buf); + } while (1); + + mbedtls_ssl_close_notify(&ssl); + exit_code = MBEDTLS_EXIT_SUCCESS; +exit: +#ifdef MBEDTLS_ERROR_C + if (exit_code != MBEDTLS_EXIT_SUCCESS) { + char error_buf[100]; + mbedtls_strerror(ret, error_buf, sizeof(error_buf)); + mbedtls_printf("Last error was: %d - %s\n\n", ret, error_buf); + } +#endif + if (ra_tls_verify_lib) + dlclose(ra_tls_verify_lib); + + mbedtls_net_free(&server_fd); + + mbedtls_x509_crt_free(&cacert); + mbedtls_ssl_free(&ssl); + mbedtls_ssl_config_free(&conf); + mbedtls_ctr_drbg_free(&ctr_drbg); + mbedtls_entropy_free(&entropy); + +#if defined(MBEDTLS_USE_PSA_CRYPTO) || defined(MBEDTLS_SSL_PROTO_TLS1_3) + mbedtls_psa_crypto_free(); +#endif /* MBEDTLS_USE_PSA_CRYPTO || MBEDTLS_SSL_PROTO_TLS1_3 */ + + return exit_code; +} diff --git a/CI-Examples/ra-tls-mbedtls-with-kss/src/server.c b/CI-Examples/ra-tls-mbedtls-with-kss/src/server.c new file mode 100644 index 0000000000..67ba972225 --- /dev/null +++ b/CI-Examples/ra-tls-mbedtls-with-kss/src/server.c @@ -0,0 +1,440 @@ +/* SPDX-License-Identifier: Apache-2.0 */ +/* Copyright (C) 2006-2015, ARM Limited, All Rights Reserved + * 2020, Intel Labs + */ + +/* + * SSL server demonstration program (with RA-TLS) + * This program is originally based on an mbedTLS example ssl_server.c but uses RA-TLS flows (SGX + * Remote Attestation flows) if RA-TLS library is required by user. + * Note that this program builds against mbedTLS 3.x. + */ + +#define _GNU_SOURCE +#include "mbedtls/build_info.h" + +#include +#include +#include +#include +#include +#include + +#define mbedtls_fprintf fprintf +#define mbedtls_printf printf + +#include "mbedtls/ctr_drbg.h" +#include "mbedtls/debug.h" +#include "mbedtls/entropy.h" +#include "mbedtls/error.h" +#include "mbedtls/net_sockets.h" +#include "mbedtls/ssl.h" +#include "mbedtls/x509.h" + +#include "ra_tls.h" + +#define HTTP_RESPONSE \ + "HTTP/1.0 200 OK\r\nContent-Type: text/html\r\n\r\n" \ + "

mbed TLS Test Server

\r\n" \ + "

Successful connection using: %s

\r\n" + +#define DEBUG_LEVEL 0 + +#define MALICIOUS_STR "MALICIOUS DATA" + +#define CA_CRT_PATH "ssl/ca.crt" +#define SRV_CRT_PATH "ssl/server.crt" +#define SRV_KEY_PATH "ssl/server.key" + +static void my_debug(void* ctx, int level, const char* file, int line, const char* str) { + ((void)level); + + mbedtls_fprintf((FILE*)ctx, "%s:%04d: %s\n", file, line, str); + fflush((FILE*)ctx); +} + +static ssize_t file_read(const char* path, char* buf, size_t count) { + FILE* f = fopen(path, "r"); + if (!f) + return -errno; + + ssize_t bytes = fread(buf, 1, count, f); + if (bytes <= 0) { + int errsv = errno; + fclose(f); + return -errsv; + } + + int close_ret = fclose(f); + if (close_ret < 0) + return -errno; + + return bytes; +} + +int main(int argc, char** argv) { + int ret; + size_t len; + mbedtls_net_context listen_fd; + mbedtls_net_context client_fd; + unsigned char buf[1024]; + const char* pers = "ssl_server"; + + void* ra_tls_attest_lib; + int (*ra_tls_create_key_and_crt_der_f)(uint8_t** der_key, size_t* der_key_size, uint8_t** der_crt, + size_t* der_crt_size); + + uint8_t* der_key = NULL; + uint8_t* der_crt = NULL; + + mbedtls_entropy_context entropy; + mbedtls_ctr_drbg_context ctr_drbg; + mbedtls_ssl_context ssl; + mbedtls_ssl_config conf; + mbedtls_x509_crt srvcert; + mbedtls_pk_context pkey; + + mbedtls_net_init(&listen_fd); + mbedtls_net_init(&client_fd); + mbedtls_ssl_init(&ssl); + mbedtls_ssl_config_init(&conf); + mbedtls_x509_crt_init(&srvcert); + mbedtls_pk_init(&pkey); + mbedtls_entropy_init(&entropy); + mbedtls_ctr_drbg_init(&ctr_drbg); + +#if defined(MBEDTLS_USE_PSA_CRYPTO) || defined(MBEDTLS_SSL_PROTO_TLS1_3) + psa_status_t status = psa_crypto_init(); + if (status != PSA_SUCCESS) { + mbedtls_printf("Failed to initialize PSA Crypto implementation: %d\n", (int)status); + return 1; + } +#endif /* MBEDTLS_USE_PSA_CRYPTO || MBEDTLS_SSL_PROTO_TLS1_3 */ + +#if defined(MBEDTLS_DEBUG_C) + mbedtls_debug_set_threshold(DEBUG_LEVEL); +#endif + + char attestation_type_str[32] = {0}; + ret = file_read("/dev/attestation/attestation_type", attestation_type_str, + sizeof(attestation_type_str) - 1); + if (ret < 0 && ret != -ENOENT) { + mbedtls_printf("User requested RA-TLS attestation but cannot read SGX-specific file " + "/dev/attestation/attestation_type\n"); + return 1; + } + + if (ret == -ENOENT || !strcmp(attestation_type_str, "none")) { + ra_tls_attest_lib = NULL; + ra_tls_create_key_and_crt_der_f = NULL; + } else if (!strcmp(attestation_type_str, "dcap")) { + ra_tls_attest_lib = dlopen("libra_tls_attest.so", RTLD_LAZY); + if (!ra_tls_attest_lib) { + mbedtls_printf("User requested RA-TLS attestation but cannot find lib\n"); + return 1; + } + + char* error; + ra_tls_create_key_and_crt_der_f = dlsym(ra_tls_attest_lib, "ra_tls_create_key_and_crt_der"); + if ((error = dlerror()) != NULL) { + mbedtls_printf("%s\n", error); + return 1; + } + } else { + mbedtls_printf("Unrecognized remote attestation type: %s\n", attestation_type_str); + return 1; + } + + mbedtls_printf(" . Seeding the random number generator..."); + fflush(stdout); + + ret = mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, &entropy, + (const unsigned char*)pers, strlen(pers)); + if (ret != 0) { + mbedtls_printf(" failed\n ! mbedtls_ctr_drbg_seed returned %d\n", ret); + goto exit; + } + + mbedtls_printf(" ok\n"); + + if (ra_tls_attest_lib) { + mbedtls_printf("\n . Creating the RA-TLS server cert and key (using \"%s\" as " + "attestation type)...", attestation_type_str); + fflush(stdout); + + size_t der_key_size; + size_t der_crt_size; + + ret = (*ra_tls_create_key_and_crt_der_f)(&der_key, &der_key_size, &der_crt, &der_crt_size); + if (ret != 0) { + mbedtls_printf(" failed\n ! ra_tls_create_key_and_crt_der returned %d\n\n", ret); + goto exit; + } + + ret = mbedtls_x509_crt_parse(&srvcert, (unsigned char*)der_crt, der_crt_size); + if (ret != 0) { + mbedtls_printf(" failed\n ! mbedtls_x509_crt_parse returned %d\n\n", ret); + goto exit; + } + + ret = mbedtls_pk_parse_key(&pkey, (unsigned char*)der_key, der_key_size, /*pwd=*/NULL, 0, + mbedtls_ctr_drbg_random, &ctr_drbg); + if (ret != 0) { + mbedtls_printf(" failed\n ! mbedtls_pk_parse_key returned %d\n\n", ret); + goto exit; + } + + mbedtls_printf(" ok\n"); + + if (argc > 1) { + if (strcmp(argv[1], "--test-malicious-quote") != 0) { + mbedtls_printf("Unrecognized command-line argument `%s` (only " + "`--test-malicious-quote` is recognized)\n", argv[1]); + return 1; + } + + /* 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 both of them */ + mbedtls_printf(" . Maliciously modifying SGX quote embedded in RA-TLS cert..."); + fflush(stdout); + + uint8_t legacy_oid[] = NON_STANDARD_INTEL_SGX_QUOTE_OID; + uint8_t standard_oid[] = TCG_DICE_TAGGED_EVIDENCE_OID_RAW; + 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)); + } + + mbedtls_printf(" ok\n"); + } + } else { + mbedtls_printf("\n . Creating normal server cert and key..."); + fflush(stdout); + + ret = mbedtls_x509_crt_parse_file(&srvcert, SRV_CRT_PATH); + if (ret != 0) { + mbedtls_printf(" failed\n ! mbedtls_x509_crt_parse_file returned %d\n\n", ret); + goto exit; + } + + ret = mbedtls_x509_crt_parse_file(&srvcert, CA_CRT_PATH); + if (ret != 0) { + mbedtls_printf(" failed\n ! mbedtls_x509_crt_parse_file returned %d\n\n", ret); + goto exit; + } + + ret = mbedtls_pk_parse_keyfile(&pkey, SRV_KEY_PATH, /*password=*/NULL, + mbedtls_ctr_drbg_random, &ctr_drbg); + if (ret != 0) { + mbedtls_printf(" failed\n ! mbedtls_pk_parse_keyfile returned %d\n\n", ret); + goto exit; + } + + mbedtls_printf(" ok\n"); + } + + mbedtls_printf(" . Bind on https://localhost:4433/ ..."); + fflush(stdout); + + ret = mbedtls_net_bind(&listen_fd, NULL, "4433", MBEDTLS_NET_PROTO_TCP); + if (ret != 0) { + mbedtls_printf(" failed\n ! mbedtls_net_bind returned %d\n\n", ret); + goto exit; + } + + mbedtls_printf(" ok\n"); + + mbedtls_printf(" . Setting up the SSL data...."); + fflush(stdout); + + ret = mbedtls_ssl_config_defaults(&conf, MBEDTLS_SSL_IS_SERVER, MBEDTLS_SSL_TRANSPORT_STREAM, + MBEDTLS_SSL_PRESET_DEFAULT); + if (ret != 0) { + mbedtls_printf(" failed\n ! mbedtls_ssl_config_defaults returned %d\n\n", ret); + goto exit; + } + + mbedtls_ssl_conf_rng(&conf, mbedtls_ctr_drbg_random, &ctr_drbg); + mbedtls_ssl_conf_dbg(&conf, my_debug, stdout); + + if (!ra_tls_attest_lib) { + /* no RA-TLS attest library present, use embedded CA chain */ + mbedtls_ssl_conf_ca_chain(&conf, srvcert.next, NULL); + } + + ret = mbedtls_ssl_conf_own_cert(&conf, &srvcert, &pkey); + if (ret != 0) { + mbedtls_printf(" failed\n ! mbedtls_ssl_conf_own_cert returned %d\n\n", ret); + goto exit; + } + + ret = mbedtls_ssl_setup(&ssl, &conf); + if (ret != 0) { + mbedtls_printf(" failed\n ! mbedtls_ssl_setup returned %d\n\n", ret); + goto exit; + } + + mbedtls_printf(" ok\n"); + +reset: +#ifdef MBEDTLS_ERROR_C + if (ret != 0) { + char error_buf[100]; + mbedtls_strerror(ret, error_buf, sizeof(error_buf)); + mbedtls_printf("Last error was: %d - %s\n\n", ret, error_buf); + } +#endif + + mbedtls_net_free(&client_fd); + + mbedtls_ssl_session_reset(&ssl); + + mbedtls_printf(" . Waiting for a remote connection ..."); + fflush(stdout); + + ret = mbedtls_net_accept(&listen_fd, &client_fd, NULL, 0, NULL); + if (ret != 0) { + mbedtls_printf(" failed\n ! mbedtls_net_accept returned %d\n\n", ret); + goto exit; + } + + mbedtls_ssl_set_bio(&ssl, &client_fd, mbedtls_net_send, mbedtls_net_recv, NULL); + + mbedtls_printf(" ok\n"); + + mbedtls_printf(" . Performing the SSL/TLS handshake..."); + fflush(stdout); + + while ((ret = mbedtls_ssl_handshake(&ssl)) != 0) { + if (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE) { + mbedtls_printf(" failed\n ! mbedtls_ssl_handshake returned %d\n\n", ret); + goto reset; + } + } + + mbedtls_printf(" ok\n"); + + mbedtls_printf(" < Read from client:"); + fflush(stdout); + + do { + len = sizeof(buf) - 1; + memset(buf, 0, sizeof(buf)); + ret = mbedtls_ssl_read(&ssl, buf, len); + + if (ret == MBEDTLS_ERR_SSL_WANT_READ || ret == MBEDTLS_ERR_SSL_WANT_WRITE) + continue; + + if (ret <= 0) { + switch (ret) { + case MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY: + mbedtls_printf(" connection was closed gracefully\n"); + break; + + case MBEDTLS_ERR_NET_CONN_RESET: + mbedtls_printf(" connection was reset by peer\n"); + break; + + default: + mbedtls_printf(" mbedtls_ssl_read returned -0x%x\n", -ret); + break; + } + + break; + } + + len = ret; + mbedtls_printf(" %lu bytes read\n\n%s", len, (char*)buf); + + if (ret > 0) + break; + } while (1); + + mbedtls_printf(" > Write to client:"); + fflush(stdout); + + len = sprintf((char*)buf, HTTP_RESPONSE, mbedtls_ssl_get_ciphersuite(&ssl)); + + while ((ret = mbedtls_ssl_write(&ssl, buf, len)) <= 0) { + if (ret == MBEDTLS_ERR_NET_CONN_RESET) { + mbedtls_printf(" failed\n ! peer closed the connection\n\n"); + goto reset; + } + + if (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE) { + mbedtls_printf(" failed\n ! mbedtls_ssl_write returned %d\n\n", ret); + goto exit; + } + } + + len = ret; + mbedtls_printf(" %lu bytes written\n\n%s\n", len, (char*)buf); + + mbedtls_printf(" . Closing the connection..."); + + while ((ret = mbedtls_ssl_close_notify(&ssl)) < 0) { + if (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE) { + mbedtls_printf(" failed\n ! mbedtls_ssl_close_notify returned %d\n\n", ret); + goto reset; + } + } + + mbedtls_printf(" ok\n"); + + ret = 0; + goto reset; + +exit: +#ifdef MBEDTLS_ERROR_C + if (ret != 0) { + char error_buf[100]; + mbedtls_strerror(ret, error_buf, sizeof(error_buf)); + mbedtls_printf("Last error was: %d - %s\n\n", ret, error_buf); + } +#endif + + if (ra_tls_attest_lib) + dlclose(ra_tls_attest_lib); + + mbedtls_net_free(&client_fd); + mbedtls_net_free(&listen_fd); + + mbedtls_x509_crt_free(&srvcert); + mbedtls_pk_free(&pkey); + mbedtls_ssl_free(&ssl); + mbedtls_ssl_config_free(&conf); + mbedtls_ctr_drbg_free(&ctr_drbg); + mbedtls_entropy_free(&entropy); + +#if defined(MBEDTLS_USE_PSA_CRYPTO) || defined(MBEDTLS_SSL_PROTO_TLS1_3) + mbedtls_psa_crypto_free(); +#endif /* MBEDTLS_USE_PSA_CRYPTO || MBEDTLS_SSL_PROTO_TLS1_3 */ + + free(der_key); + free(der_crt); + + return ret; +} diff --git a/CI-Examples/ra-tls-mbedtls-with-kss/ssl/ca_config.conf b/CI-Examples/ra-tls-mbedtls-with-kss/ssl/ca_config.conf new file mode 100644 index 0000000000..a6a4bc2ed4 --- /dev/null +++ b/CI-Examples/ra-tls-mbedtls-with-kss/ssl/ca_config.conf @@ -0,0 +1,18 @@ +# Copyright (C) 2023 Gramine contributors +# SPDX-License-Identifier: BSD-3-Clause + +[ req ] +default_bits = 4096 +default_md = sha512 +default_keyfile = example.com.key +prompt = no +encrypt_key = no +distinguished_name = req_distinguished_name + +[ req_distinguished_name ] +countryName = "XX" # C= +localityName = "XXXXX" # L= +organizationName = "My Company" # O= +organizationalUnitName = "Department" # OU= +commonName = "localhost" # CN= +emailAddress = "me@example.com" # email diff --git a/pal/src/host/linux-sgx/enclave_framework.c b/pal/src/host/linux-sgx/enclave_framework.c index 72adb4f32d..614506fc2f 100644 --- a/pal/src/host/linux-sgx/enclave_framework.c +++ b/pal/src/host/linux-sgx/enclave_framework.c @@ -310,6 +310,10 @@ static void print_report(sgx_report_t* r) { log_debug(" attr.xfrm: %016lx", r->body.attributes.xfrm); log_debug(" isv_prod_id: %02x", r->body.isv_prod_id); log_debug(" isv_svn: %02x", r->body.isv_svn); + log_debug(" isv_ext_prod_id: %s", BYTES2HEX(r->body.isv_ext_prod_id)); + log_debug(" isv_family_id: %s", BYTES2HEX(r->body.isv_family_id)); + log_debug(" config_id : %s", BYTES2HEX(r->body.config_id.data)); + log_debug(" config_svn: %02x", r->body.config_svn); log_debug(" report_data: %s", BYTES2HEX(r->body.report_data.d)); log_debug(" key_id: %s", BYTES2HEX(r->key_id.id)); log_debug(" mac: %s", BYTES2HEX(r->mac)); diff --git a/pal/src/host/linux-sgx/enclave_ocalls.c b/pal/src/host/linux-sgx/enclave_ocalls.c index 71e25c324b..24c76f6b3f 100644 --- a/pal/src/host/linux-sgx/enclave_ocalls.c +++ b/pal/src/host/linux-sgx/enclave_ocalls.c @@ -1011,7 +1011,8 @@ int ocall_clone_thread(void* dynamic_tcs) { } int ocall_create_process(size_t nargs, const char** args, uintptr_t (*reserved_mem_ranges)[2], - size_t reserved_mem_ranges_len, int* out_stream_fd) { + size_t reserved_mem_ranges_len, sgx_config_id_t config_id, + sgx_config_svn_t config_svn, int* out_stream_fd) { int ret; void* urts_reserved_mem_ranges = NULL; size_t reserved_mem_ranges_size = reserved_mem_ranges_len * sizeof(*reserved_mem_ranges); @@ -1054,6 +1055,9 @@ int ocall_create_process(size_t nargs, const char** args, uintptr_t (*reserved_m COPY_VALUE_TO_UNTRUSTED(&ocall_cp_args->reserved_mem_ranges, urts_reserved_mem_ranges); COPY_VALUE_TO_UNTRUSTED(&ocall_cp_args->reserved_mem_ranges_size, reserved_mem_ranges_size); + COPY_VALUE_TO_UNTRUSTED(&ocall_cp_args->config_id, config_id); + COPY_VALUE_TO_UNTRUSTED(&ocall_cp_args->config_svn, config_svn); + do { /* FIXME: if there was an EINTR, there may be an untrusted process left over */ ret = sgx_exitless_ocall(OCALL_CREATE_PROCESS, ocall_cp_args); diff --git a/pal/src/host/linux-sgx/enclave_ocalls.h b/pal/src/host/linux-sgx/enclave_ocalls.h index 595731ef48..35ce1bffc9 100644 --- a/pal/src/host/linux-sgx/enclave_ocalls.h +++ b/pal/src/host/linux-sgx/enclave_ocalls.h @@ -85,7 +85,8 @@ int ocall_sched_getaffinity(void* tcs, unsigned long* cpu_mask, size_t cpu_mask_ int ocall_clone_thread(void* dynamic_tcs); int ocall_create_process(size_t nargs, const char** args, uintptr_t (*reserved_mem_ranges)[2], - size_t reserved_mem_ranges_len, int* out_stream_fd); + size_t reserved_mem_ranges_len, sgx_config_id_t config_id, + sgx_config_svn_t config_svn, int* out_stream_fd); int ocall_futex(uint32_t* uaddr, int op, int val, uint64_t* timeout_us); diff --git a/pal/src/host/linux-sgx/generated_offsets.c b/pal/src/host/linux-sgx/generated_offsets.c index ef9459744c..fcc4edb3ca 100644 --- a/pal/src/host/linux-sgx/generated_offsets.c +++ b/pal/src/host/linux-sgx/generated_offsets.c @@ -15,6 +15,7 @@ const struct generated_offset generated_offsets[] = { /* defines from sgx_arch.h */ DEFINE(SGX_FLAGS_DEBUG, SGX_FLAGS_DEBUG), DEFINE(SGX_FLAGS_MODE64BIT, SGX_FLAGS_MODE64BIT), + DEFINE(SGX_FLAGS_KSS, SGX_FLAGS_KSS), DEFINE(SGX_XFRM_LEGACY, SGX_XFRM_LEGACY), DEFINE(SGX_XFRM_AVX, SGX_XFRM_AVX), DEFINE(SGX_XFRM_MPX, SGX_XFRM_MPX), diff --git a/pal/src/host/linux-sgx/host_internal.h b/pal/src/host/linux-sgx/host_internal.h index 220078d0a2..c554c1fbad 100644 --- a/pal/src/host/linux-sgx/host_internal.h +++ b/pal/src/host/linux-sgx/host_internal.h @@ -43,6 +43,7 @@ struct pal_enclave { unsigned long rpc_thread_num; unsigned long ssa_frame_size; bool edmm_enabled; + bool kss_enabled; enum sgx_attestation_type attestation_type; char* libpal_uri; /* Path to the PAL binary */ diff --git a/pal/src/host/linux-sgx/host_main.c b/pal/src/host/linux-sgx/host_main.c index e402d6b12b..8f664ccb4c 100644 --- a/pal/src/host/linux-sgx/host_main.c +++ b/pal/src/host/linux-sgx/host_main.c @@ -29,6 +29,8 @@ #include "toml.h" #include "toml_utils.h" #include "topo_info.h" +#include "api.h" +#include "hex.h" const size_t g_page_size = PRESET_PAGESIZE; @@ -199,7 +201,8 @@ static int load_enclave_binary(sgx_arch_secs_t* secs, int fd, unsigned long base return ret; } -static int initialize_enclave(struct pal_enclave* enclave, const char* manifest_to_measure) { +static int initialize_enclave(struct pal_enclave* enclave, const char* manifest_to_measure, + sgx_config_id_t config_id, sgx_config_svn_t config_svn) { int ret = 0; int enclave_image = -1; sgx_sigstruct_t enclave_sigstruct; @@ -286,6 +289,11 @@ static int initialize_enclave(struct pal_enclave* enclave, const char* manifest_ memset(&enclave_secs, 0, sizeof(enclave_secs)); enclave_secs.base = enclave->baseaddr; enclave_secs.size = enclave->size; + + /* set received config_id/svn to SECS */ + memcpy(enclave_secs.config_id.data, config_id.data, SGX_CONFIGID_SIZE); + enclave_secs.config_svn = config_svn; + ret = create_enclave(&enclave_secs, &enclave_sigstruct); if (ret < 0) { log_error("Creating enclave failed: %s", unix_strerror(ret)); @@ -634,6 +642,14 @@ static int parse_loader_config(char* manifest, struct pal_enclave* enclave_info, goto out; } + ret = toml_bool_in(manifest_root, "sgx.kss", /*defaultval=*/false, + &enclave_info->kss_enabled); + if (ret < 0) { + log_error("Cannot parse 'sgx.kss'"); + ret = -EINVAL; + goto out; + } + if (!enclave_info->size || !IS_POWER_OF_2(enclave_info->size)) { log_error("Enclave size not a power of two (an SGX-imposed requirement)"); ret = -EINVAL; @@ -892,7 +908,8 @@ static int parse_loader_config(char* manifest, struct pal_enclave* enclave_info, * exits after this function's failure. */ static int load_enclave(struct pal_enclave* enclave, char* args, size_t args_size, char* env, size_t env_size, int parent_stream_fd, - void* reserved_mem_ranges, size_t reserved_mem_ranges_size) { + void* reserved_mem_ranges, size_t reserved_mem_ranges_size, + sgx_config_id_t config_id, sgx_config_svn_t config_svn) { int ret; struct timeval tv; struct pal_topo_info topo_info = {0}; @@ -972,7 +989,21 @@ static int load_enclave(struct pal_enclave* enclave, char* args, size_t args_siz } } - ret = initialize_enclave(enclave, enclave->raw_manifest_data); + if (enclave->kss_enabled) { + uint32_t cpuid_values[4]; + cpuid(INTEL_SGX_LEAF, 1, cpuid_values); + if (!(cpuid_values[0] & (1u << 7))) { + log_error("KSS feature was requested in manifest, but the platform doesn't support it"); + return -EPERM; + } + } else { + /* when sgx.kss is false, config ID and SVN must be 0-cleared */ + memset(config_id.data, 0, SGX_CONFIGID_SIZE); + config_svn = 0; + } + + ret = initialize_enclave(enclave, enclave->raw_manifest_data, + config_id, config_svn); if (ret < 0) return ret; @@ -1029,7 +1060,7 @@ static int load_enclave(struct pal_enclave* enclave, char* args, size_t args_siz noreturn static void print_usage_and_exit(const char* argv_0) { const char* self = argv_0 ?: ""; log_always("USAGE:\n" - "\tFirst process: %s init args...\n" + "\tFirst process: %s init args...\n" "\tChildren: %s child args...", self, self); log_always("This is an internal interface. Use gramine-sgx wrapper to launch applications in " @@ -1125,8 +1156,12 @@ int main(int argc, char* argv[], char* envp[]) { static_assert(THREAD_STACK_SIZE % PAGE_SIZE == 0, ""); probe_stack(THREAD_STACK_SIZE / PAGE_SIZE); - if (argc < 4) + if (argc < 6 && strcmp(argv[2], "init") == 0) { print_usage_and_exit(argv[0]); + } + if (argc < 4 && strcmp(argv[2], "child") == 0) { + print_usage_and_exit(argv[0]); + } g_host_pid = DO_SYSCALL(getpid); @@ -1151,6 +1186,8 @@ int main(int argc, char* argv[], char* envp[]) { } int parent_stream_fd = -1; + sgx_config_id_t final_config_id = {0}; + sgx_config_svn_t final_config_svn = 0; if (first_process) { g_pal_enclave.is_first_process = true; @@ -1181,7 +1218,8 @@ int main(int argc, char* argv[], char* envp[]) { } ret = sgx_init_child_process(parent_stream_fd, &g_pal_enclave.application_path, &manifest, - &reserved_mem_ranges, &reserved_mem_ranges_size); + &reserved_mem_ranges, &reserved_mem_ranges_size, + &final_config_id, &final_config_svn); if (ret < 0) return ret; } @@ -1192,14 +1230,37 @@ int main(int argc, char* argv[], char* envp[]) { * continuous we know that we are running on Linux, which does this. This * saves us creating a copy of all argv and envp strings. */ - char* args; - size_t args_size; + char* args = NULL; + size_t args_size = 0; + if (first_process) { - args = argv[3]; - args_size = argc > 3 ? (argv[argc - 1] - args) + strlen(argv[argc - 1]) + 1 : 0; + size_t total_size = strlen(argv[3]) + 1; + + for (int i = 6; i < argc; i++) { + total_size += strlen(argv[i]) + 1; + } + + args = malloc(total_size); + if (!args) + return -ENOMEM; + + char* p = args; + size_t len = strlen(argv[3]) + 1; + memcpy(p, argv[3], len); + p += len; + + for (int i = 6; i < argc; i++) { + len = strlen(argv[i]) + 1; + memcpy(p, argv[i], len); + p += len; + } + + args_size = total_size; } else { - args = argv[4]; - args_size = argc > 4 ? (argv[argc - 1] - args) + strlen(argv[argc - 1]) + 1 : 0; + if (argc > 4) { + args = argv[4]; + args_size = (argv[argc - 1] - args) + strlen(argv[argc - 1]) + 1; + } } size_t envc = 0; @@ -1209,8 +1270,27 @@ int main(int argc, char* argv[], char* envp[]) { char* env = envp[0]; size_t env_size = envc > 0 ? (envp[envc - 1] - envp[0]) + strlen(envp[envc - 1]) + 1 : 0; + /* If we're first process, parse config ID/SVN from command line args */ + if (first_process && argc >= 6) { + if (!hex2bytes(argv[4], strlen(argv[4]), final_config_id.data, SGX_CONFIGID_SIZE)) { + log_error("Parsing Config ID from argv failed"); + return -EINVAL; + } + + unsigned long parsed_value; + const char* endptr; + if (str_to_ulong(argv[5], 10, &parsed_value, &endptr) == 0 && + *endptr == '\0' && parsed_value <= UINT16_MAX) { + final_config_svn = (uint16_t)parsed_value; + } else { + log_error("Parsing Config SVN from argv failed"); + return -EINVAL; + } + } + ret = load_enclave(&g_pal_enclave, args, args_size, env, env_size, parent_stream_fd, - reserved_mem_ranges, reserved_mem_ranges_size); + reserved_mem_ranges, reserved_mem_ranges_size, + final_config_id, final_config_svn); if (ret < 0) { log_error("load_enclave() failed with error: %s", unix_strerror(ret)); return ret; diff --git a/pal/src/host/linux-sgx/host_ocalls.c b/pal/src/host/linux-sgx/host_ocalls.c index 7d36875dd6..7f0469d395 100644 --- a/pal/src/host/linux-sgx/host_ocalls.c +++ b/pal/src/host/linux-sgx/host_ocalls.c @@ -245,7 +245,8 @@ static long sgx_ocall_create_process(void* args) { return sgx_create_process(ocall_cp_args->nargs, ocall_cp_args->args, g_pal_enclave.raw_manifest_data, ocall_cp_args->reserved_mem_ranges, - ocall_cp_args->reserved_mem_ranges_size, &ocall_cp_args->stream_fd); + ocall_cp_args->reserved_mem_ranges_size, ocall_cp_args->config_id, + ocall_cp_args->config_svn, &ocall_cp_args->stream_fd); } static long sgx_ocall_futex(void* args) { diff --git a/pal/src/host/linux-sgx/host_process.c b/pal/src/host/linux-sgx/host_process.c index f0f61fa3aa..db5c7839ad 100644 --- a/pal/src/host/linux-sgx/host_process.c +++ b/pal/src/host/linux-sgx/host_process.c @@ -27,6 +27,8 @@ struct proc_args { size_t manifest_size; // manifest will follow application path on the pipe. int reserved_mem_ranges_fd; size_t reserved_mem_ranges_size; + sgx_config_id_t config_id; + sgx_config_svn_t config_svn; }; static int vfork_exec(const char** argv) { @@ -42,7 +44,7 @@ static int vfork_exec(const char** argv) { int sgx_create_process(size_t nargs, const char** args, const char* manifest, void* reserved_mem_ranges, size_t reserved_mem_ranges_size, - int* out_stream_fd) { + sgx_config_id_t config_id, sgx_config_svn_t config_svn, int* out_stream_fd) { int ret, rete; int reserved_mem_ranges_fd = -1; int fds[2] = {-1, -1}; @@ -100,8 +102,11 @@ int sgx_create_process(size_t nargs, const char** args, const char* manifest, .manifest_size = strlen(manifest), .reserved_mem_ranges_fd = reserved_mem_ranges_fd, .reserved_mem_ranges_size = reserved_mem_ranges_size, + .config_svn = config_svn }; + memcpy(proc_args.config_id.data, config_id.data, sizeof(proc_args.config_id.data)); + ret = write_all(fds[1], &proc_args, sizeof(proc_args)); if (ret < 0) { goto out; @@ -145,7 +150,8 @@ int sgx_create_process(size_t nargs, const char** args, const char* manifest, } int sgx_init_child_process(int parent_stream_fd, char** out_application_path, char** out_manifest, - void** out_reserved_mem_ranges, size_t* out_reserved_mem_ranges_size) { + void** out_reserved_mem_ranges, size_t* out_reserved_mem_ranges_size, + sgx_config_id_t* out_config_id, sgx_config_svn_t* out_config_svn) { int ret; struct proc_args proc_args; char* manifest = NULL; @@ -168,6 +174,14 @@ int sgx_init_child_process(int parent_stream_fd, char** out_application_path, ch goto out; } + if (out_config_id) { + *out_config_id = proc_args.config_id; + } + + if (out_config_svn) { + *out_config_svn = proc_args.config_svn; + } + ret = read_all(parent_stream_fd, application_path, proc_args.application_path_size); if (ret < 0) { goto out; diff --git a/pal/src/host/linux-sgx/host_process.h b/pal/src/host/linux-sgx/host_process.h index f14a69eed6..7182a33af6 100644 --- a/pal/src/host/linux-sgx/host_process.h +++ b/pal/src/host/linux-sgx/host_process.h @@ -6,10 +6,12 @@ #pragma once #include +#include "sgx_arch.h" int sgx_create_process(size_t nargs, const char** args, const char* manifest, void* reserved_mem_ranges, size_t reserved_mem_ranges_size, - int* out_stream_fd); + sgx_config_id_t config_id, sgx_config_svn_t config_svn, int* out_stream_fd); int sgx_init_child_process(int parent_stream_fd, char** out_application_path, char** out_manifest, - void** out_reserved_mem_ranges, size_t* out_reserved_mem_ranges_size); + void** out_reserved_mem_ranges, size_t* out_reserved_mem_ranges_size, + sgx_config_id_t* out_config_id, sgx_config_svn_t* out_config_svn); diff --git a/pal/src/host/linux-sgx/pal_ocall_types.h b/pal/src/host/linux-sgx/pal_ocall_types.h index d20bab3a19..5242e5e772 100644 --- a/pal/src/host/linux-sgx/pal_ocall_types.h +++ b/pal/src/host/linux-sgx/pal_ocall_types.h @@ -182,6 +182,8 @@ struct ocall_create_process { void* reserved_mem_ranges; size_t reserved_mem_ranges_size; size_t nargs; + sgx_config_id_t config_id; + sgx_config_svn_t config_svn; const char* args[]; }; diff --git a/pal/src/host/linux-sgx/pal_process.c b/pal/src/host/linux-sgx/pal_process.c index 9f060b9519..7543e31e79 100644 --- a/pal/src/host/linux-sgx/pal_process.c +++ b/pal/src/host/linux-sgx/pal_process.c @@ -132,8 +132,17 @@ int _PalProcessCreate(const char** args, uintptr_t (*reserved_mem_ranges)[2], * host OS. It serves merely as a hint to the initial memory allocator in PAL, so any malicious * host OS modifications are irrelevant (will be detected, if anything overlaps). */ + sgx_config_id_t config_id; + sgx_config_svn_t config_svn; + + memcpy(config_id.data, + g_pal_linuxsgx_state.enclave_info.config_id.data, + sizeof(config_id.data)); + + config_svn = g_pal_linuxsgx_state.enclave_info.config_svn; + int ret = ocall_create_process(nargs, args, reserved_mem_ranges, reserved_mem_ranges_len, - &stream_fd); + config_id, config_svn, &stream_fd); if (ret < 0) return unix_to_pal_error(ret); diff --git a/pal/src/host/linux-sgx/sgx_arch.h b/pal/src/host/linux-sgx/sgx_arch.h index 5dd3a69de6..3c9617cce1 100644 --- a/pal/src/host/linux-sgx/sgx_arch.h +++ b/pal/src/host/linux-sgx/sgx_arch.h @@ -66,6 +66,7 @@ typedef uint8_t sgx_isvfamily_id_t[SGX_ISV_FAMILY_ID_SIZE]; #define SGX_FLAGS_MODE64BIT 0x04ULL #define SGX_FLAGS_PROVISION_KEY 0x10ULL #define SGX_FLAGS_LICENSE_KEY 0x20ULL +#define SGX_FLAGS_KSS 0x80ULL /* EINIT must verify *all* SECS.ATTRIBUTES[63..0] bits (FLAGS bits) against * SIGSTRUCT.ATTRIBUTES[63..0]. diff --git a/python/gramine-sgx-sigstruct-view b/python/gramine-sgx-sigstruct-view index 4db1daaf93..107ca2b26a 100755 --- a/python/gramine-sgx-sigstruct-view +++ b/python/gramine-sgx-sigstruct-view @@ -28,6 +28,8 @@ def main(sigfile, verbose, output_format, output): 'mr_enclave': sig['enclave_hash'].hex(), 'isv_prod_id': sig['isv_prod_id'], 'isv_svn': sig['isv_svn'], + 'isv_ext_prod_id': sig['isv_ext_prod_id'].hex(), + 'isv_family_id': sig['isv_family_id'].hex(), 'attribute_flags': hex(sig['attribute_flags']), 'attribute_xfrms': hex(sig['attribute_xfrms']), 'misc_select': hex(sig['misc_select']), @@ -39,8 +41,9 @@ def main(sigfile, verbose, output_format, output): } if not verbose: - keys_to_remove = ('attribute_flags', 'attribute_xfrms', 'misc_select', - 'attribute_flags_mask', 'attribute_xfrm_mask', 'misc_mask', 'date') + keys_to_remove = ('isv_family_id', 'isv_ext_prod_id', 'attribute_flags', + 'attribute_xfrms', 'misc_select', 'attribute_flags_mask', + 'attribute_xfrm_mask', 'misc_mask', 'date') for key in keys_to_remove: del sig_readable[key] diff --git a/python/graminelibos/manifest.py b/python/graminelibos/manifest.py index 25399ba9d1..5b8d588443 100644 --- a/python/graminelibos/manifest.py +++ b/python/graminelibos/manifest.py @@ -305,6 +305,9 @@ def __init__(self, manifest_str): sgx.setdefault('max_threads', DEFAULT_THREAD_NUM) sgx.setdefault('isvprodid', 0) sgx.setdefault('isvsvn', 0) + sgx.setdefault('kss', False) + sgx.setdefault('isvextprodid', "00000000000000000000000000000000") + sgx.setdefault('isvfamilyid', "00000000000000000000000000000000") sgx.setdefault('remote_attestation', "none") sgx.setdefault('debug', False) sgx.setdefault('enable_stats', False) diff --git a/python/graminelibos/manifest_check.py b/python/graminelibos/manifest_check.py index 9fe5590812..ecda7f41ba 100644 --- a/python/graminelibos/manifest_check.py +++ b/python/graminelibos/manifest_check.py @@ -102,6 +102,9 @@ 'xfrm_mask': _mask64, 'misc_mask': _mask32, }, + 'kss': bool, + 'isvfamilyid': str, + 'isvextprodid': str, # TODO: validator for sha256 'trusted_files': [Any(str, {'uri': _uri, 'sha256': str})], 'use_exinfo': bool, diff --git a/python/graminelibos/sgx_sign.py b/python/graminelibos/sgx_sign.py index a328cd2863..cb4b96ef83 100644 --- a/python/graminelibos/sgx_sign.py +++ b/python/graminelibos/sgx_sign.py @@ -95,6 +95,7 @@ def collect_cpu_feature_bits(manifest_cpu_features, options_dict, val, mask, sec def get_enclave_attributes(manifest_sgx): flags_dict = { 'debug': offs.SGX_FLAGS_DEBUG, + 'kss': offs.SGX_FLAGS_KSS, } flags = collect_bits(manifest_sgx, flags_dict) if ARCHITECTURE == 'amd64': @@ -546,6 +547,10 @@ def get_tbssigstruct(manifest_path, date, libpal=SGX_LIBPAL, verbose=False): sig['attribute_xfrms'] = attribute_xfrms sig['attribute_xfrm_mask'] = xfrms_mask + if attribute_flags & offs.SGX_FLAGS_KSS: + sig['isv_ext_prod_id'] = int(manifest_sgx['isvextprodid'], 16).to_bytes(16, 'big') + sig['isv_family_id'] = int(manifest_sgx['isvfamilyid'], 16).to_bytes(16, 'big') + return sig diff --git a/python/graminelibos/sigstruct.py b/python/graminelibos/sigstruct.py index 0a2eac5afd..34c70b42bd 100644 --- a/python/graminelibos/sigstruct.py +++ b/python/graminelibos/sigstruct.py @@ -35,6 +35,8 @@ class Sigstruct: 'attribute_xfrm_mask': (offs.SGX_ARCH_SIGSTRUCT_ATTRIBUTE_MASK + 8, '&2 + exit 2 + fi + shift + continue + ;; + --config-svn=*) + CONFIG_SVN="${1#*=}" + if [ -z "$CONFIG_SVN" ]; then + echo "Error: --config-svn requires a non-empty value." >&2 + exit 2 + fi + shift + continue + ;; + *) + if [ -z "$APPLICATION" ]; then + APPLICATION="$1" + shift + continue + fi + break + ;; + esac +done + +if [ "$SGX" != "1" ]; then + if [ -n "$CONFIG_ID" ] || [ -n "$CONFIG_SVN" ]; then + echo "Error: --config-id and --config-svn are only allowed in gramine-sgx." >&2 + exit 2 fi +fi - break -done +if [ -n "$CONFIG_ID" ]; then + if [ -n "$ENV_CONFIG_ID" ] && [ "$CONFIG_ID" != "$ENV_CONFIG_ID" ]; then + echo "Error: --config-id does not match SGX_CONFIG_ID environment variable." >&2 + exit 2 + fi +else + CONFIG_ID="$ENV_CONFIG_ID" +fi + +if [ -n "$CONFIG_SVN" ]; then + if [ -n "$ENV_CONFIG_SVN" ] && [ "$CONFIG_SVN" != "$ENV_CONFIG_SVN" ]; then + echo "Error: --config-svn does not match SGX_CONFIG_SVN environment variable." >&2 + exit 2 + fi +else + CONFIG_SVN="$ENV_CONFIG_SVN" +fi if [ "$APPLICATION" == "" ]; then echo "Usage: $0 [] ..." @@ -80,5 +132,18 @@ fi CMD=("${ENVS[@]}") CMD+=("${PREFIX[@]}") -CMD+=("$PAL_CMD" "$LIBPAL_PATH" init "$APPLICATION" "$@") -exec env "${CMD[@]}" +CMD+=("$PAL_CMD" "$LIBPAL_PATH" init "$APPLICATION") + +if [ "$SGX" == "1" ]; then + if [ -z "$CONFIG_ID" ]; then + CONFIG_ID="00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + fi + if [ -z "$CONFIG_SVN" ]; then + CONFIG_SVN="0" + fi + CMD+=("$CONFIG_ID" "$CONFIG_SVN") +fi + +CMD+=("$@") + +exec env "${CMD[@]}" \ No newline at end of file diff --git a/tools/sgx/common/quote.c b/tools/sgx/common/quote.c index 4637e6fc13..4c8c712125 100644 --- a/tools/sgx/common/quote.c +++ b/tools/sgx/common/quote.c @@ -105,6 +105,8 @@ void display_quote(const void* quote_data, size_t quote_size) { int verify_quote_body(const sgx_quote_body_t* quote_body, const char* mr_signer, const char* mr_enclave, const char* isv_prod_id, const char* isv_svn, + const char* isv_ext_prod_id, const char* isv_family_id, + const char* config_id, const char* config_svn, const char* report_data, bool expected_as_str) { int ret = -1; @@ -196,6 +198,95 @@ int verify_quote_body(const sgx_quote_body_t* quote_body, const char* mr_signer, DBG("Quote: isv_svn OK\n"); } + if (isv_ext_prod_id) { + sgx_isvext_prod_id_t expected_isv_ext_prod_id; + + if (expected_as_str) { + if (parse_hex(isv_ext_prod_id, &expected_isv_ext_prod_id, sizeof(expected_isv_ext_prod_id), NULL) != 0) + goto out; + } else { + memcpy(&expected_isv_ext_prod_id, isv_ext_prod_id, sizeof(expected_isv_ext_prod_id)); + } + + if (memcmp(&report_body->isv_ext_prod_id, &expected_isv_ext_prod_id, sizeof(expected_isv_ext_prod_id)) != 0) { + ERROR("Quote: isv_ext_prod_id doesn't match the expected value\n"); + if (get_verbose()) { + ERROR("Quote isv_ext_prod_id:\n"); + HEXDUMP(report_body->isv_ext_prod_id); + ERROR("Expected isv_ext_prod_id:\n"); + HEXDUMP(expected_isv_ext_prod_id); + } + goto out; + } + + DBG("Quote: isv_ext_prod_id OK\n"); + } + + if (isv_family_id) { + sgx_isvfamily_id_t expected_isv_family_id; + + if (expected_as_str) { + if (parse_hex(isv_family_id, &expected_isv_family_id, sizeof(expected_isv_family_id), NULL) != 0) + goto out; + } else { + memcpy(&expected_isv_family_id, isv_family_id, sizeof(expected_isv_family_id)); + } + + if (memcmp(&report_body->isv_family_id, &expected_isv_family_id, sizeof(expected_isv_family_id)) != 0) { + ERROR("Quote: isv_family_id doesn't match the expected value\n"); + if (get_verbose()) { + ERROR("Quote isv_family_id:\n"); + HEXDUMP(report_body->isv_family_id); + ERROR("Expected isv_family_id:\n"); + HEXDUMP(expected_isv_family_id); + } + goto out; + } + + DBG("Quote: isv_family_id OK\n"); + } + + if (config_id) { + sgx_config_id_t expected_config_id; + + if (expected_as_str) { + if (parse_hex(config_id, &expected_config_id, sizeof(expected_config_id), NULL) != 0) + goto out; + } else { + memcpy(&expected_config_id, config_id, sizeof(expected_config_id)); + } + + if (memcmp(&report_body->config_id, &expected_config_id, sizeof(expected_config_id)) != 0) { + ERROR("Quote: config_id doesn't match the expected value\n"); + if (get_verbose()) { + ERROR("Quote config_id:\n"); + HEXDUMP(report_body->config_id); + ERROR("Expected config_id:\n"); + HEXDUMP(expected_config_id); + } + goto out; + } + + DBG("Quote: config_id OK\n"); + } + + if (config_svn) { + sgx_config_svn_t expected_config_svn; + + if (expected_as_str) { + expected_config_svn = strtoul(config_svn, NULL, 10); + } else { + memcpy(&expected_config_svn, config_svn, sizeof(expected_config_svn)); + } + + if (report_body->config_svn < expected_config_svn) { + ERROR("Quote: invalid config_svn (%u < expected %u)\n", report_body->config_svn, expected_config_svn); + goto out; + } + + DBG("Quote: config_svn OK\n"); + } + if (report_data) { sgx_report_data_t rd; @@ -221,7 +312,6 @@ int verify_quote_body(const sgx_quote_body_t* quote_body, const char* mr_signer, } ret = 0; - // TODO: KSS support (isv_ext_prod_id, config_id, config_svn, isv_family_id) out: return ret; } diff --git a/tools/sgx/common/quote.h b/tools/sgx/common/quote.h index 569f83421a..82a9635e27 100644 --- a/tools/sgx/common/quote.h +++ b/tools/sgx/common/quote.h @@ -27,18 +27,24 @@ void display_quote(const void* quote_data, size_t quote_size); * \param mr_enclave (Optional) Expected mr_enclave quote field. * \param isv_prod_id (Optional) Expected isv_prod_id quote field. * \param isv_svn (Optional) Expected isv_svn quote field. + * \param isv_ext_prod_id (Optional) Expected isv_ext_prod_id quote field. + * \param isv_family_id (Optional) Expected isv_family_id quote field. + * \param config_id (Optional) Expected config_id quote field. + * \param config_svn (Optional) Expected config_svn quote field. * \param report_data (Optional) Expected report_data quote field. * \param expected_as_str If true, then all expected SGX fields are treated as hex and decimal * strings. Otherwise, they are treated as raw bytes. * * \returns 0 on successful verification, negative value on error. * - * If \p expected_as_str is true, then \p mr_signer, \p mr_enclave and \p report_data are treated - * as hex strings, and \p isv_prod_id and a isv_svn are treated as decimal strings. This is - * convenient for command-line utilities. + * If \p expected_as_str is true, then \p mr_signer, \p mr_enclave, \p isv_ext_prod_id, \p isv_family_id, + * \p config_id and \p report_data are treated as hex strings, and \p isv_prod_id, a isv_svn and a config_svn + * are treated as decimal strings. This is convenient for command-line utilities. */ int verify_quote_body(const sgx_quote_body_t* quote_body, const char* mr_signer, const char* mr_enclave, const char* isv_prod_id, const char* isv_svn, + const char* isv_ext_prod_id, const char* isv_family_id, + const char* config_id, const char* config_svn, const char* report_data, bool expected_as_str); /*! diff --git a/tools/sgx/ra-tls/ra_tls.h b/tools/sgx/ra-tls/ra_tls.h index 3339e4a830..3522beb52e 100644 --- a/tools/sgx/ra-tls/ra_tls.h +++ b/tools/sgx/ra-tls/ra_tls.h @@ -26,10 +26,14 @@ #define RA_TLS_ALLOW_SW_HARDENING_NEEDED "RA_TLS_ALLOW_SW_HARDENING_NEEDED" #define RA_TLS_ALLOW_DEBUG_ENCLAVE_INSECURE "RA_TLS_ALLOW_DEBUG_ENCLAVE_INSECURE" -#define RA_TLS_MRSIGNER "RA_TLS_MRSIGNER" -#define RA_TLS_MRENCLAVE "RA_TLS_MRENCLAVE" -#define RA_TLS_ISV_PROD_ID "RA_TLS_ISV_PROD_ID" -#define RA_TLS_ISV_SVN "RA_TLS_ISV_SVN" +#define RA_TLS_MRSIGNER "RA_TLS_MRSIGNER" +#define RA_TLS_MRENCLAVE "RA_TLS_MRENCLAVE" +#define RA_TLS_ISV_PROD_ID "RA_TLS_ISV_PROD_ID" +#define RA_TLS_ISV_SVN "RA_TLS_ISV_SVN" +#define RA_TLS_ISV_EXT_PROD_ID "RA_TLS_ISV_EXT_PROD_ID" +#define RA_TLS_ISV_FAMILY_ID "RA_TLS_ISV_FAMILY_ID" +#define RA_TLS_CONFIG_ID "RA_TLS_CONFIG_ID" +#define RA_TLS_CONFIG_SVN "RA_TLS_CONFIG_SVN" #define RA_TLS_CERT_TIMESTAMP_NOT_BEFORE "RA_TLS_CERT_TIMESTAMP_NOT_BEFORE" #define RA_TLS_CERT_TIMESTAMP_NOT_AFTER "RA_TLS_CERT_TIMESTAMP_NOT_AFTER" @@ -87,6 +91,30 @@ typedef int (*verify_measurements_cb_t)(const char* mrenclave, const char* mrsig */ void ra_tls_set_measurement_callback(verify_measurements_cb_t f_cb); +typedef int (*verify_measurements_with_kss_cb_t)(const char* mrenclave, const char* mrsigner, + const char* isv_prod_id, const char* isv_svn, + const char* isv_ext_prod_id, const char* isv_family_id, + const char* config_id, const char* config_svn); + +/*! + * \brief Callback for user-specific verification of measurements including KSS values in SGX quote. + * + * \param f_cb Callback for user-specific verification; RA-TLS passes pointers to MRENCLAVE, MRSIGNER, + * ISV_PROD_ID, ISV_SVN, ISV_EXT_PROD_ID, ISV_FAMILY_ID, CONFIG_ID, CONFIG_SVN measurements in SGX quote. + * Use NULL to revert to default behavior of RA-TLS. + * + * \returns 0 on success, specific error code (negative int) otherwise. + * + * If this callback is registered before RA-TLS session, then RA-TLS verification will invoke this + * callback to allow for user-specific checks on SGX measurements reported in the SGX quote. If no + * callback is registered (or registered as NULL), then RA-TLS defaults to verifying SGX + * measurements against `RA_TLS_*` environment variables (if any). + */ +void ra_tls_set_measurement_including_kss_callback(int (*f_cb)(const char* mrenclave, const char* mrsigner, + const char* isv_prod_id, const char* isv_svn, + const char* isv_ext_prod_id, const char* isv_family_id, + const char* config_id, const char* config_svn)); + /*! * \brief Generic verification callback for ECDSA-based (DCAP) quote * verification (DER format). Deprecated in favor of the diff --git a/tools/sgx/ra-tls/ra_tls.map b/tools/sgx/ra-tls/ra_tls.map index 552b52b0fc..d5758d6151 100644 --- a/tools/sgx/ra-tls/ra_tls.map +++ b/tools/sgx/ra-tls/ra_tls.map @@ -1,5 +1,5 @@ RA_TLS { - global: ra_tls_set_measurement_callback; ra_tls_verify_callback_der; ra_tls_verify_callback_extended_der; ra_tls_create_key_and_crt_der; + global: ra_tls_set_measurement_callback; ra_tls_set_measurement_including_kss_callback; ra_tls_verify_callback_der; ra_tls_verify_callback_extended_der; ra_tls_create_key_and_crt_der; local: *; }; diff --git a/tools/sgx/ra-tls/ra_tls_verify_common.c b/tools/sgx/ra-tls/ra_tls_verify_common.c index 106e4da815..7501f4aacd 100644 --- a/tools/sgx/ra-tls/ra_tls_verify_common.c +++ b/tools/sgx/ra-tls/ra_tls_verify_common.c @@ -28,6 +28,7 @@ #include "ra_tls_common.h" verify_measurements_cb_t g_verify_measurements_cb = NULL; +verify_measurements_with_kss_cb_t g_verify_measurements_including_kss_cb = NULL; static bool getenv_critical(const char* name, const char** out_value) { const char* value = getenv(name); @@ -44,19 +45,45 @@ static bool getenv_critical(const char* name, const char** out_value) { return true; } +static bool getenv_optional(const char* name, const char** out_value) { + const char* value = getenv(name); + if (!value) { + return false; + } + + if (strcmp(value, "any") == 0) { + value = NULL; + } + + *out_value = value; + return true; +} + static int getenv_enclave_measurements(sgx_measurement_t* mrsigner, bool* validate_mrsigner, sgx_measurement_t* mrenclave, bool* validate_mrenclave, sgx_prod_id_t* isv_prod_id, bool* validate_isv_prod_id, - sgx_isv_svn_t* isv_svn, bool* validate_isv_svn) { + sgx_isv_svn_t* isv_svn, bool* validate_isv_svn, + sgx_isvext_prod_id_t* isv_ext_prod_id, bool* validate_isv_ext_prod_id, + sgx_isvfamily_id_t* isv_family_id, bool* validate_isv_family_id, + sgx_config_id_t* config_id, bool* validate_config_id, + sgx_config_svn_t* config_svn, bool* validate_config_svn) { *validate_mrsigner = false; *validate_mrenclave = false; *validate_isv_prod_id = false; *validate_isv_svn = false; + *validate_isv_ext_prod_id = false; + *validate_isv_family_id = false; + *validate_config_id = false; + *validate_config_svn = false; const char* mrsigner_hex; const char* mrenclave_hex; const char* isv_prod_id_dec; const char* isv_svn_dec; + const char* isv_ext_prod_id_hex; + const char* isv_family_id_hex; + const char* config_id_hex; + const char* config_svn_dec; /* any of the below variables may be NULL (and then not used in validation) */ if (!getenv_critical(RA_TLS_MRSIGNER, &mrsigner_hex)) @@ -95,6 +122,40 @@ static int getenv_enclave_measurements(sgx_measurement_t* mrsigner, bool* valida *validate_isv_svn = true; } + if (!getenv_optional(RA_TLS_ISV_EXT_PROD_ID, &isv_ext_prod_id_hex)) + *validate_isv_ext_prod_id = false; + else if (isv_ext_prod_id_hex) { + if (parse_hex(isv_ext_prod_id_hex, isv_ext_prod_id, sizeof(*isv_ext_prod_id), NULL) != 0) + return MBEDTLS_ERR_X509_BAD_INPUT_DATA; + *validate_isv_ext_prod_id = true; + } + + if (!getenv_optional(RA_TLS_ISV_FAMILY_ID, &isv_family_id_hex)) + *validate_isv_family_id = false; + else if (isv_family_id_hex) { + if (parse_hex(isv_family_id_hex, isv_family_id, sizeof(*isv_family_id), NULL) != 0) + return MBEDTLS_ERR_X509_BAD_INPUT_DATA; + *validate_isv_family_id = true; + } + + if (!getenv_optional(RA_TLS_CONFIG_ID, &config_id_hex)) + *validate_config_id = false; + else if (config_id_hex) { + if (parse_hex(config_id_hex, config_id, sizeof(*config_id), NULL) != 0) + return MBEDTLS_ERR_X509_BAD_INPUT_DATA; + *validate_config_id = true; + } + + if (!getenv_optional(RA_TLS_CONFIG_SVN, &config_svn_dec)) + *validate_config_svn = false; + else if (config_svn_dec) { + errno = 0; + *config_svn = strtoul(config_svn_dec, NULL, 10); + if (errno) + return MBEDTLS_ERR_X509_BAD_INPUT_DATA; + *validate_config_svn = true; + } + if (!*validate_mrsigner && !*validate_mrenclave) { INFO("WARNING: Neither " RA_TLS_MRSIGNER " nor " RA_TLS_MRENCLAVE " are specified. " "This will accept any enclave and provides no security whatsoever.\n"); @@ -553,6 +614,13 @@ void ra_tls_set_measurement_callback(int (*f_cb)(const char* mrenclave, const ch g_verify_measurements_cb = f_cb; } +void ra_tls_set_measurement_including_kss_callback(int (*f_cb)(const char* mrenclave, const char* mrsigner, + const char* isv_prod_id, const char* isv_svn, + const char* isv_ext_prod_id, const char* isv_family_id, + const char* config_id, const char* config_svn)) { + g_verify_measurements_including_kss_cb = f_cb; +} + int ra_tls_verify_callback_der(uint8_t* der_crt, size_t der_crt_size) { INFO("WARNING: The ra_tls_verify_callback_der() API is deprecated in favor of the " "ra_tls_verify_callback_extended_der() version of API.\n"); @@ -591,16 +659,28 @@ int verify_quote_body_against_envvar_measurements(const sgx_quote_body_t* quote_ sgx_measurement_t expected_mrenclave; sgx_prod_id_t expected_isv_prod_id; sgx_isv_svn_t expected_isv_svn; - - bool validate_mrsigner = false; - bool validate_mrenclave = false; - bool validate_isv_prod_id = false; - bool validate_isv_svn = false; + sgx_isvext_prod_id_t expected_isv_ext_prod_id; + sgx_isvfamily_id_t expected_isv_family_id; + sgx_config_id_t expected_config_id; + sgx_config_svn_t expected_config_svn; + + bool validate_mrsigner = false; + bool validate_mrenclave = false; + bool validate_isv_prod_id = false; + bool validate_isv_svn = false; + bool validate_isv_ext_prod_id = false; + bool validate_isv_family_id = false; + bool validate_config_id = false; + bool validate_config_svn = false; ret = getenv_enclave_measurements(&expected_mrsigner, &validate_mrsigner, &expected_mrenclave, &validate_mrenclave, &expected_isv_prod_id, &validate_isv_prod_id, - &expected_isv_svn, &validate_isv_svn); + &expected_isv_svn, &validate_isv_svn, + &expected_isv_ext_prod_id, &validate_isv_ext_prod_id, + &expected_isv_family_id, &validate_isv_family_id, + &expected_config_id, &validate_config_id, + &expected_config_svn, &validate_config_svn); if (ret < 0) return ret; @@ -608,6 +688,10 @@ int verify_quote_body_against_envvar_measurements(const sgx_quote_body_t* quote_ validate_mrenclave ? (char*)&expected_mrenclave : NULL, validate_isv_prod_id ? (char*)&expected_isv_prod_id : NULL, validate_isv_svn ? (char*)&expected_isv_svn : NULL, + validate_isv_ext_prod_id ? (char*)&expected_isv_ext_prod_id : NULL, + validate_isv_family_id ? (char*)&expected_isv_family_id : NULL, + validate_config_id ? (char*)&expected_config_id : NULL, + validate_config_svn ? (char*)&expected_config_svn : NULL, /*report_data=*/NULL, /*expected_as_str=*/false); if (ret < 0) return MBEDTLS_ERR_X509_CERT_VERIFY_FAILED; diff --git a/tools/sgx/ra-tls/ra_tls_verify_dcap.c b/tools/sgx/ra-tls/ra_tls_verify_dcap.c index b6102ef411..b54f80131b 100644 --- a/tools/sgx/ra-tls/ra_tls_verify_dcap.c +++ b/tools/sgx/ra-tls/ra_tls_verify_dcap.c @@ -32,6 +32,7 @@ #include "ra_tls_common.h" extern verify_measurements_cb_t g_verify_measurements_cb; +extern verify_measurements_with_kss_cb_t g_verify_measurements_including_kss_cb; /* we cannot include libsgx_dcap_verify headers because they conflict with Gramine SGX headers, * so we declare the used types and functions below */ @@ -244,12 +245,25 @@ int ra_tls_verify_callback(void* data, mbedtls_x509_crt* crt, int depth, uint32_ results->err_loc = AT_VERIFY_ENCLAVE_MEASUREMENTS; /* verify other relevant enclave information from the SGX quote */ - if (g_verify_measurements_cb) { + if (g_verify_measurements_cb != NULL && g_verify_measurements_including_kss_cb != NULL) { + ERROR("Only a single callback can be set.\n"); + ret = -1; + } else if (g_verify_measurements_cb) { /* use user-supplied callback to verify measurements */ ret = g_verify_measurements_cb((const char*)"e_body->report_body.mr_enclave, (const char*)"e_body->report_body.mr_signer, (const char*)"e_body->report_body.isv_prod_id, (const char*)"e_body->report_body.isv_svn); + } else if (g_verify_measurements_including_kss_cb) { + /* use user-supplied callback to verify measurements */ + ret = g_verify_measurements_including_kss_cb((const char*)"e_body->report_body.mr_enclave, + (const char*)"e_body->report_body.mr_signer, + (const char*)"e_body->report_body.isv_prod_id, + (const char*)"e_body->report_body.isv_svn, + (const char*)"e_body->report_body.isv_ext_prod_id, + (const char*)"e_body->report_body.isv_family_id, + (const char*)"e_body->report_body.config_id, + (const char*)"e_body->report_body.config_svn); } else { /* use default logic to verify measurements */ ret = verify_quote_body_against_envvar_measurements(quote_body);