diff --git a/Examples/ra-tls-mbedtls/.gitignore b/Examples/ra-tls-mbedtls/.gitignore new file mode 100644 index 00000000..2b225a2c --- /dev/null +++ b/Examples/ra-tls-mbedtls/.gitignore @@ -0,0 +1,5 @@ +/*.tar.gz +/OUTPUT +/client +/mbedtls +/server diff --git a/Examples/ra-tls-mbedtls/Makefile b/Examples/ra-tls-mbedtls/Makefile new file mode 100644 index 00000000..8d2f79b8 --- /dev/null +++ b/Examples/ra-tls-mbedtls/Makefile @@ -0,0 +1,187 @@ +# Build mbedTLS client/server example with RA-TLS as follows: +# +# - make -- build SGX version with apps built in release mode and no logs +# - make DEBUG=1 -- build SGX version with apps built in debug mode and with logs +# +# Any of these invocations clones mbedTLS' git repository (version 2.21.0) and +# builds it in default configuration. Also, server and client programs are +# built. See README for details. +# +# Use `make clean` to remove Graphene-generated files and `make distclean` to +# additionally remove the cloned mbedTLS git repository. + +# Relative path to Graphene root +GRAPHENEDIR ?= ../.. +GRAPHENEKEY ?= $(GRAPHENEDIR)/Pal/src/host/Linux-SGX/signer/enclave-key.pem + +# Specify your SPID and linkable/unlinkable attestation policy (only for EPID); +# these variables will not be used if this example is built for ECDSA/DCAP +RA_CLIENT_SPID ?= AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +RA_CLIENT_LINKABLE ?= 0 + +ifeq ($(DEBUG),1) +GRAPHENEDEBUG=inline +CFLAGS += -O0 -ggdb3 +else +GRAPHENEDEBUG=none +CFLAGS += -O2 +endif + +.PHONY: all +all: app epid # by default, only build EPID because it doesn't rely on additional (DCAP) libs + +.PHONY: app +app: mbedtls/CMakeLists.txt server.manifest.sgx client pal_loader + +.PHONY: epid +epid: libra_tls_attest.so libra_tls_verify_epid.so + +.PHONY: dcap +dcap: libra_tls_attest.so libra_tls_verify_dcap.so + +############################# MBEDTLS DEPENDENCY ############################## + +MBEDTLS_VERSION ?= 2.21.0 +MBEDTLS_SRC ?= mbedtls-$(MBEDTLS_VERSION).tar.gz +MBEDTLS_URI ?= https://github.com/ARMmbed/mbedtls/archive/ +MBEDTLS_CHECKSUM ?= 320e930b7596ade650ae4fc9ba94b510d05e3a7d63520e121d8fdc7a21602db9 + +# mbedTLS uses a submodule mbedcrypto, need to download it and move under mbedtls/crypto +MBEDCRYPTO_VERSION ?= 3.1.0 +MBEDCRYPTO_SRC ?= mbedcrypto-$(MBEDCRYPTO_VERSION).tar.gz +MBEDCRYPTO_URI ?= https://github.com/ARMmbed/mbed-crypto/archive/ +MBEDCRYPTO_CHECKSUM ?= 7e171df03560031bc712489930831e70ae4b70ff521a609c6361f36bd5f8b76b + +ifeq ($(DEBUG),1) +MBED_BUILD_TYPE=Debug +else +MBED_BUILD_TYPE=Release +endif + +$(MBEDTLS_SRC): + $(GRAPHENEDIR)/Scripts/download --output $@ --url $(MBEDTLS_URI)/$(MBEDTLS_SRC) \ + --sha256 $(MBEDTLS_CHECKSUM) + +$(MBEDCRYPTO_SRC): + $(GRAPHENEDIR)/Scripts/download --output $@ --url $(MBEDCRYPTO_URI)/$(MBEDCRYPTO_SRC) \ + --sha256 $(MBEDCRYPTO_CHECKSUM) + +mbedtls/CMakeLists.txt: $(MBEDTLS_SRC) $(MBEDCRYPTO_SRC) + tar -mxzf $(MBEDTLS_SRC) + tar -mxzf $(MBEDCRYPTO_SRC) + mv mbedtls-mbedtls-$(MBEDTLS_VERSION) mbedtls + $(RM) -r mbedtls/crypto + mv mbed-crypto-mbedcrypto-$(MBEDCRYPTO_VERSION) mbedtls + mv mbedtls/mbed-crypto-mbedcrypto-$(MBEDCRYPTO_VERSION) mbedtls/crypto + mkdir mbedtls/install + cd mbedtls && ./scripts/config.pl set MBEDTLS_CMAC_C && make SHARED=1 DESTDIR=install install . + +######################### CLIENT/SERVER EXECUTABLES ########################### + +CFLAGS += -I./mbedtls/install/include -I./mbedtls/crypto/include +LFLAGS += -Wl,-rpath,. -L. -lmbedcrypto -lmbedtls -lmbedx509 + +server: src/server.c libmbedcrypto.so libmbedtls.so libmbedx509.so + $(CC) $< $(CFLAGS) $(LFLAGS) -o $@ + +client: src/client.c libmbedcrypto.so libmbedtls.so libmbedx509.so + $(CC) $< $(CFLAGS) $(LFLAGS) -o $@ + +########################### COPIES FOR CONVENIENCE ############################ + +pal_loader: + ln -s $(GRAPHENEDIR)/Runtime/$@ . + +libmbedcrypto.so: mbedtls/CMakeLists.txt + cp mbedtls/install/lib/$@.* . + ln -s $@.* $@ + +libmbedtls.so: mbedtls/CMakeLists.txt + cp mbedtls/install/lib/$@.* . + ln -s $@.* $@ + +libmbedx509.so: mbedtls/CMakeLists.txt + cp mbedtls/install/lib/$@.* . + ln -s $@.* $@ + +libsgx_util.so: + cp $(GRAPHENEDIR)/Pal/src/host/Linux-SGX/tools/common/$@ . + +libra_tls_attest.so: + cp $(GRAPHENEDIR)/Pal/src/host/Linux-SGX/tools/ra-tls/$@ . + +libra_tls_verify_epid.so: libmbedcrypto.so libmbedx509.so libsgx_util.so + cp $(GRAPHENEDIR)/Pal/src/host/Linux-SGX/tools/ra-tls/$@ . + +libra_tls_verify_dcap.so: libmbedcrypto.so libmbedx509.so libsgx_util.so + cp $(GRAPHENEDIR)/Pal/src/host/Linux-SGX/tools/ra-tls/$@ . + +############################### SERVER MANIFEST ############################### + +server.manifest: server.manifest.template + sed -e 's|$$(GRAPHENEDIR)|'"$(GRAPHENEDIR)"'|g' \ + -e 's|$$(GRAPHENEDEBUG)|'"$(GRAPHENEDEBUG)"'|g' \ + -e 's|$$(RA_CLIENT_SPID)|'"$(RA_CLIENT_SPID)"'|g' \ + -e 's|$$(RA_CLIENT_LINKABLE)|'"$(RA_CLIENT_LINKABLE)"'|g' \ + $< > $@ + +server.manifest.sgx: server.manifest server libra_tls_attest.so + $(GRAPHENEDIR)/Pal/src/host/Linux-SGX/signer/pal-sgx-sign \ + -libpal $(GRAPHENEDIR)/Runtime/libpal-Linux-SGX.so \ + -key $(GRAPHENEKEY) \ + -manifest $< -output $@ \ + -exec server + $(GRAPHENEDIR)/Pal/src/host/Linux-SGX/signer/pal-sgx-get-token \ + -output server.token -sig server.sig + +############################### SGX CHECKS FOR CI ############################# + +.PHONY: check_epid +check_epid: app epid + SGX=1 ./pal_loader server >/dev/null & SERVER_ID=$$!; \ + sleep 30; \ + LD_PRELOAD="libra_tls_verify_epid.so" ./client > OUTPUT; \ + LD_PRELOAD="libra_tls_verify_epid.so" ./client 0 0 0 0 >> 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_epid_fail +check_epid_fail: app epid + SGX=1 ./pal_loader server dummy-option >/dev/null & SERVER_ID=$$!; \ + sleep 30; \ + LD_PRELOAD="libra_tls_verify_epid.so" ./client && exit 1 || echo "[ Success 1/1 ]"; \ + kill -9 $$SERVER_ID + +.PHONY: check_dcap +check_dcap: app dcap + SGX=1 ./pal_loader server >/dev/null & SERVER_ID=$$!; \ + sleep 30; \ + LD_PRELOAD="libsgx_urts.so libra_tls_verify_dcap.so" ./client > OUTPUT; \ + LD_PRELOAD="libsgx_urts.so libra_tls_verify_dcap.so" ./client 0 0 0 0 >> 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 + SGX=1 ./pal_loader server dummy-option >/dev/null & SERVER_ID=$$!; \ + sleep 30; \ + LD_PRELOAD="libsgx_urts.so libra_tls_verify_dcap.so" ./client && exit 1 || echo "[ Success 1/1 ]"; \ + kill -9 $$SERVER_ID + +################################## CLEANUP #################################### + +.PHONY: clean +clean: + $(RM) *.token *.sig *.manifest.sgx *.manifest pal_loader server client *.so *.so.* OUTPUT + +.PHONY: distclean +distclean: clean + $(RM) -r mbedtls *.tar.gz diff --git a/Examples/ra-tls-mbedtls/README.md b/Examples/ra-tls-mbedtls/README.md new file mode 100644 index 00000000..7ec7e336 --- /dev/null +++ b/Examples/ra-tls-mbedtls/README.md @@ -0,0 +1,126 @@ +# RA-TLS Minimal Example + +This directory contains the Makefile, the template server manifest, and the minimal server and +client written against the mbedTLS 2.21.0 library. This was tested on a machine with SGX v1 and +Ubuntu 18.04. + +The server and client are based on `ssl_server.c` and `ssl_client.c` example programs shipped with +mbedTLS. We modified them to allow using RA-TLS flows if the programs detect this library is +preloaded via the `LD_PRELOAD` trick. 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()`. The client uses an RA-TLS +verification callback to verify the server RA-TLS certificate via `ra_tls_verify_callback()`. + +This example uses the RA-TLS libraries `ra_tls_attest.so` for server and `ra_tls_verify_epid.so`/ +`ra_tls_verify_dcap.so` for client. These libraries are found under +`Pal/src/host/Linux-SGX/tools/ra-tls`. Additionally, mbedTLS libraries are required to correctly +run RA-TLS, the client, and the server. For ECDSA/DCAP attestation, the DCAP software +infrastructure must be installed and working correctly on the host. + +The current example works with both EPID (IAS) and ECDSA (DCAP) remote attestation schemes. For +more documentation, refer to `Pal/src/host/Linux-SGX/tools/README.rst`. + + +## RA-TLS server + +The server is supposed to run in the SGX enclave with Graphene and RA-TLS preloaded. If RA-TLS +library `ra_tls_attest.so` is not preloaded, the server falls back to using normal X.509 PKI flows. + +If server is run with any command-line options (the only important thing is to have at least one +option), 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_epid.so` or `ra_tls_verify_dcap.so` is not preloaded, the client falls back to using +normal X.509 PKI flows. + +If client is run without command-line options, it uses default RA-TLS verification callback that +compares `MRENCLAVE`, `MRSIGNER`, `ISV_PROD_ID` and `ISV_SVN` against the corresonding `RA_TLS_*` +environment variables. To run the client with its own verification callback, execute it with four +command-line options (see the source code for details). + + +# Quick Start + +- Normal non-RA-TLS flows; without SGX and without Graphene: + +```sh +make app +./server & +./client +# client will successfully connect to the server via normal x.509 PKI flows +kill %% +``` + +- RA-TLS flows with SGX and with Graphene, EPID-based (IAS) attestation: + +```sh +# replace dummy values with your SPID, linkable setting, API key, MRENCLAVE, etc! +make clean +RA_CLIENT_SPID=12345678901234567890123456789012 RA_CLIENT_LINKABLE=0 make app epid + +SGX=1 ./pal_loader ./server & + +RA_TLS_EPID_API_KEY=12345678901234567890123456789012 \ +RA_TLS_ALLOW_OUTDATED_TCB_INSECURE=1 \ +RA_TLS_MRENCLAVE=1234567890123456789012345678901234567890123456789012345678901234 \ +RA_TLS_MRSIGNER=1234567890123456789012345678901234567890123456789012345678901234 \ +RA_TLS_ISV_PROD_ID=0 RA_TLS_ISV_SVN=0 \ +LD_PRELOAD="$PWD/libra_tls_verify_epid.so" ./client + +# client will successfully connect to the server via RA-TLS/EPID flows +kill %% +``` + +- RA-TLS flows with SGX and with Graphene, ECDSA-based (DCAP) attestation: + +```sh +# replace dummy values with your MRENCLAVE, MRSIGNER, etc! +make clean +make app dcap + +SGX=1 ./pal_loader ./server & + +RA_TLS_ALLOW_OUTDATED_TCB_INSECURE=1 \ +RA_TLS_MRENCLAVE=1234567890123456789012345678901234567890123456789012345678901234 \ +RA_TLS_MRSIGNER=1234567890123456789012345678901234567890123456789012345678901234 \ +RA_TLS_ISV_PROD_ID=0 RA_TLS_ISV_SVN=0 \ +LD_PRELOAD="libsgx_urts.so $PWD/libra_tls_verify_dcap.so" \ +./client + +# client will successfully connect to the server via RA-TLS/DCAP flows +kill %% +``` + +- RA-TLS flows with SGX and with Graphene, client with its own verification callback: + +```sh +# replace dummy values with your MRENCLAVE, MRSIGNER, etc! +make clean +make app dcap + +SGX=1 ./pal_loader ./server & + +# arguments are: MRENCLAVE in hex, MRSIGNER in hex, ISV_PROD_ID as dec, ISV_SVN as dec +LD_PRELOAD="libsgx_urts.so $PWD/libra_tls_verify_dcap.so" ./client \ + 1234567890123456789012345678901234567890123456789012345678901234 \ + 1234567890123456789012345678901234567890123456789012345678901234 \ + 0 0 + +# client will successfully connect to the server via RA-TLS/DCAP flows +kill %% +``` + +- RA-TLS flows with SGX and with Graphene, server sends malicious SGX quote: + +```sh +make clean +make app dcap + +SGX=1 ./pal_loader ./server dummy-option & +LD_PRELOAD="libsgx_urts.so $PWD/libra_tls_verify_dcap.so" ./client + +# client will fail to verify the malicious SGX quote and will *not* connect to the server +kill %% +``` diff --git a/Examples/ra-tls-mbedtls/server.manifest.template b/Examples/ra-tls-mbedtls/server.manifest.template new file mode 100644 index 00000000..0832d0ee --- /dev/null +++ b/Examples/ra-tls-mbedtls/server.manifest.template @@ -0,0 +1,53 @@ +# RA-TLS manifest file example +# +# This manifest was prepared and tested on Ubuntu 18.04. + +loader.exec = file:server +loader.preload = file:$(GRAPHENEDIR)/Runtime/libsysdb.so +loader.debug_type = $(GRAPHENEDEBUG) + +loader.env.LD_LIBRARY_PATH = /lib:/lib/x86_64-linux-gnu + +# RA-TLS library is preloaded (without it, server uses normal X.509 PKI flows) +loader.env.LD_PRELOAD = libra_tls_attest.so + +# Request remote attestation functionality from Graphene +sgx.remote_attestation = 1 + +# Specify your SPID and linkable/unlinkable attestation policy +sgx.ra_client_spid = $(RA_CLIENT_SPID) +sgx.ra_client_linkable = $(RA_CLIENT_LINKABLE) + +fs.mount.lib.type = chroot +fs.mount.lib.path = /lib +fs.mount.lib.uri = file:$(GRAPHENEDIR)/Runtime + +fs.mount.lib2.type = chroot +fs.mount.lib2.path = /lib/x86_64-linux-gnu +fs.mount.lib2.uri = file:/lib/x86_64-linux-gnu + +fs.mount.etc.type = chroot +fs.mount.etc.path = /etc +fs.mount.etc.uri = file:/etc + +sgx.trusted_files.ld = file:$(GRAPHENEDIR)/Runtime/ld-linux-x86-64.so.2 +sgx.trusted_files.libc = file:$(GRAPHENEDIR)/Runtime/libc.so.6 +sgx.trusted_files.libm = file:$(GRAPHENEDIR)/Runtime/libm.so.6 + +sgx.trusted_files.libnsscompat = file:/lib/x86_64-linux-gnu/libnss_compat.so.2 +sgx.trusted_files.libnssfiles = file:/lib/x86_64-linux-gnu/libnss_files.so.2 +sgx.trusted_files.libnssnis = file:/lib/x86_64-linux-gnu/libnss_nis.so.2 +sgx.trusted_files.libnsl = file:/lib/x86_64-linux-gnu/libnsl.so.1 + +sgx.trusted_files.libmbedcrypto = file:libmbedcrypto.so.4 +sgx.trusted_files.libmbedtls = file:libmbedtls.so.13 +sgx.trusted_files.libmbedx509 = file:libmbedx509.so.1 + +sgx.trusted_files.libratlsattest = file:libra_tls_attest.so + +sgx.allowed_files.nsswitch = file:/etc/nsswitch.conf +sgx.allowed_files.ethers = file:/etc/ethers +sgx.allowed_files.hosts = file:/etc/hosts +sgx.allowed_files.group = file:/etc/group +sgx.allowed_files.passwd = file:/etc/passwd +sgx.allowed_files.gaiconf = file:/etc/gai.conf diff --git a/Examples/ra-tls-mbedtls/src/client.c b/Examples/ra-tls-mbedtls/src/client.c new file mode 100644 index 00000000..70f7bd8e --- /dev/null +++ b/Examples/ra-tls-mbedtls/src/client.c @@ -0,0 +1,376 @@ +/* + * SSL client demonstration program (with RA-TLS). + * This program is heavily based on an mbedTLS 2.21.0 example ssl_client.c + * but uses RA-TLS flows (SGX Remote Attestation flows) if RA-TLS library + * is preloaded. + * + * Copyright (C) 2006-2015, ARM Limited, All Rights Reserved + * 2020, Intel Labs + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "mbedtls/config.h" + +#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/certs.h" +#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" + +/* RA-TLS: on client, only need to register ra_tls_verify_callback() for cert verification */ +__attribute__((weak)) +int ra_tls_verify_callback(void* data, mbedtls_x509_crt* crt, int depth, uint32_t* flags); + +/* RA-TLS: if specified in command-line options, use our own callback to verify SGX measurements */ +__attribute__((weak)) +void ra_tls_set_measurement_callback(int (*f_cb)(const char* mrenclave, const char* mrsigner, + const char* isv_prod_id, const char* isv_svn)); + +#define SERVER_PORT "4433" +#define SERVER_NAME "localhost" +#define GET_REQUEST "GET / HTTP/1.0\r\n\r\n" + +#define DEBUG_LEVEL 0 + +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 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; + +/* RA-TLS: our own callback to verify SGX measurements */ +static int my_verify_measurements(const char* mrenclave, const char* mrsigner, + const char* isv_prod_id, const char* isv_svn) { + assert(mrenclave && mrsigner && isv_prod_id && isv_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; + + return 0; +} + +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"; + + 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 (argc > 1 && ra_tls_set_measurement_callback) { + if (argc != 5) { + mbedtls_printf("USAGE: %s " + " \n" + " (first two in hex, last two as decimal; set to 0 to ignore)\n", + argv[0]); + 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; + + ra_tls_set_measurement_callback(my_verify_measurements); + + if (!strcmp(argv[1], "0")) { + mbedtls_printf(" - ignoring MRENCLAVE\n"); + g_verify_mrenclave = false; + } else if (parse_hex(argv[1], g_expected_mrenclave, sizeof(g_expected_mrenclave)) < 0) { + mbedtls_printf("Cannot parse MRENCLAVE!\n"); + return 1; + } + + if (!strcmp(argv[2], "0")) { + mbedtls_printf(" - ignoring MRSIGNER\n"); + g_verify_mrsigner = false; + } else if (parse_hex(argv[2], g_expected_mrsigner, sizeof(g_expected_mrsigner)) < 0) { + mbedtls_printf("Cannot parse MRSIGNER!\n"); + return 1; + } + + if (!strcmp(argv[3], "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[3], 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[4], "0")) { + mbedtls_printf(" - ignoring ISV_SVN\n"); + g_verify_isv_svn = false; + } else { + errno = 0; + uint16_t isv_svn = (uint16_t)strtoul(argv[4], NULL, 10); + if (errno) { + mbedtls_printf("Cannot parse ISV_SVN\n"); + return 1; + } + memcpy(g_expected_isv_svn, &isv_svn, sizeof(isv_svn)); + } + } else if (ra_tls_set_measurement_callback) { + mbedtls_printf("[ using default SGX-measurement verification callback" + " (via RA_TLS_* environment variables) ]\n"); + ra_tls_set_measurement_callback(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(&cacert, (const unsigned char*)mbedtls_test_cas_pem, + mbedtls_test_cas_pem_len); + if (ret < 0) { + mbedtls_printf( " failed\n ! mbedtls_x509_crt_parse 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_callback) { + /* RA-TLS verify library is present, use RA-TLS verification callback; this + * will overwrite CA chain set up above */ + mbedtls_printf(" . Installing RA-TLS callback ..."); + mbedtls_ssl_conf_verify(&conf, ra_tls_verify_callback, NULL); + 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\n", -ret); + 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 + + 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); + + return exit_code; +} diff --git a/Examples/ra-tls-mbedtls/src/server.c b/Examples/ra-tls-mbedtls/src/server.c new file mode 100644 index 00000000..ef144d37 --- /dev/null +++ b/Examples/ra-tls-mbedtls/src/server.c @@ -0,0 +1,339 @@ +/* + * SSL server demonstration program (with RA-TLS) + * This program is heavily based on an mbedTLS 2.21.0 example ssl_server.c + * but uses RA-TLS flows (SGX Remote Attestation flows) if RA-TLS library + * is preloaded. + * + * Copyright (C) 2006-2015, ARM Limited, All Rights Reserved + * 2020, Intel Labs + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define _GNU_SOURCE +#include "mbedtls/config.h" + +#include +#include +#include +#include + +#define mbedtls_fprintf fprintf +#define mbedtls_printf printf + +#include "mbedtls/certs.h" +#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" + +/* RA-TLS: on server, only need ra_tls_create_key_and_crt() to create keypair and X.509 cert */ +__attribute__((weak)) +int ra_tls_create_key_and_crt(mbedtls_pk_context* key, mbedtls_x509_crt* crt); + +#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" + +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); +} + +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"; + + 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_DEBUG_C) + mbedtls_debug_set_threshold(DEBUG_LEVEL); +#endif + + if (ra_tls_create_key_and_crt) { + /* RA-TLS attest library is present, use RA-TLS generated cert and key */ + mbedtls_printf("\n . Creating the RA-TLS server cert and key..."); + fflush(stdout); + + ret = ra_tls_create_key_and_crt(&pkey, &srvcert); + if (ret != 0) { + mbedtls_printf(" failed\n ! ra_tls_create_key_and_crt returned %d\n\n", ret); + goto exit; + } + + mbedtls_printf(" ok\n"); + + if (argc > 1) { + /* user asks to maliciously modify the embedded SGX quote (for testing purposes) */ + 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; + } + + 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 { + /* no RA-TLS attest library present, use embedded test certificate */ + mbedtls_printf("\n . Creating normal server cert and key..."); + fflush(stdout); + + ret = mbedtls_x509_crt_parse(&srvcert, (const unsigned char*)mbedtls_test_srv_crt, + mbedtls_test_srv_crt_len); + if (ret != 0) { + mbedtls_printf(" failed\n ! mbedtls_x509_crt_parse returned %d\n\n", ret); + goto exit; + } + + ret = mbedtls_x509_crt_parse(&srvcert, (const unsigned char*)mbedtls_test_cas_pem, + mbedtls_test_cas_pem_len); + if (ret != 0) { + mbedtls_printf(" failed\n ! mbedtls_x509_crt_parse returned %d\n\n", ret); + goto exit; + } + + ret = mbedtls_pk_parse_key(&pkey, (const unsigned char*)mbedtls_test_srv_key, + mbedtls_test_srv_key_len, NULL, 0); + if (ret != 0) { + mbedtls_printf(" failed\n ! mbedtls_pk_parse_key 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(" . 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(" . 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_create_key_and_crt) { + /* 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 + + 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); + + return ret; +} diff --git a/Jenkinsfiles/Linux-SGX-18.04-apps b/Jenkinsfiles/Linux-SGX-18.04-apps index 115526be..9be183f0 100644 --- a/Jenkinsfiles/Linux-SGX-18.04-apps +++ b/Jenkinsfiles/Linux-SGX-18.04-apps @@ -145,6 +145,25 @@ pipeline { make SGX=1 check ''' } + timeout(time: 5, unit: 'MINUTES') { + sh ''' + cd Examples/ra-tls-mbedtls + if [ "${ra_client_spid}" != "" ] && [ "${ra_client_key}" != "" ]; \ + then \ + make SGX=1 check_epid RA_CLIENT_SPID=${ra_client_spid} \ + RA_TLS_EPID_API_KEY=${ra_client_key} \ + RA_CLIENT_LINKABLE=0 \ + RA_TLS_ALLOW_OUTDATED_TCB_INSECURE=1; \ + make SGX=1 check_epid_fail RA_CLIENT_SPID=${ra_client_spid} \ + RA_TLS_EPID_API_KEY=${ra_client_key} \ + RA_CLIENT_LINKABLE=0 \ + RA_TLS_ALLOW_OUTDATED_TCB_INSECURE=1; \ + else \ + echo "Failure: no ra_client_spid and/or ra_client_key!"; \ + exit 1; \ + fi + ''' + } sh ''' ./Scripts/gitignore-test ''' @@ -165,6 +184,7 @@ pipeline { make -C Examples/nginx SGX=1 distclean make -C Examples/apache SGX=1 distclean make -C Examples/blender SGX=1 distclean + make -C Examples/ra-tls-mbedtls SGX=1 distclean # Currently used release of LTP contains broken symlinks under # utils/ffsb-6.0-rc2/ (config.guess and config.sub); without explicit diff --git a/Pal/src/host/Linux-SGX/tools/Makefile b/Pal/src/host/Linux-SGX/tools/Makefile index 52403121..5d06826d 100644 --- a/Pal/src/host/Linux-SGX/tools/Makefile +++ b/Pal/src/host/Linux-SGX/tools/Makefile @@ -9,3 +9,4 @@ $(targets): $(MAKE) -C quote-dump $@ $(MAKE) -C ias-request $@ $(MAKE) -C verify-ias-report $@ + $(MAKE) -C ra-tls $@ diff --git a/Pal/src/host/Linux-SGX/tools/README.rst b/Pal/src/host/Linux-SGX/tools/README.rst index d810f8a7..63644945 100644 --- a/Pal/src/host/Linux-SGX/tools/README.rst +++ b/Pal/src/host/Linux-SGX/tools/README.rst @@ -158,3 +158,98 @@ Example report verification with all options enabled:: Quote: isv_prod_id OK Quote: isv_svn OK Quote: report_data OK + + +RA-TLS Libraries +---------------- + +RA-TLS integrates Intel SGX remote attestation into the TLS connection setup. Conceptually, it +extends the standard X.509 certificate with SGX-related information. The additional information +allows the receiver (verifier) of the certificate to verify that it is indeed communicating with +an SGX enclave (attester). RA-TLS is shipped as three libraries: ``ra_tls_attest.so``, EPID-based +``ra_tls_verify_epid.so`` and ECDSA-based (DCAP) ``ra_tls_verify_dcap.so``. + +``ra_tls_attest.so`` +^^^^^^^^^^^^^^^^^^^^ + +This library creates the self-signed RA-TLS certificate. It must be loaded into the SGX enclave. +This library relies on the pseudo-FS ``/dev/attestation`` to retrieve the SGX quote and embed it +into the RA-TLS certificate. Typically linked into server applications. Not thread-safe. + +The library expects the following information in the manifest for EPID-based attestation: + +- ``sgx.ra_client_spid`` -- client SPID for EPID remote attestation. +- ``sgx.ra_client_linkable`` -- client linkable/unlinkable attestation policy. + +For ECDSA-based (DCAP) attestation, the library expects instead: + +- ``sgx.attestation = 1`` -- DCAP remote attestation is enabled. + +The library uses the following environment variables if available: + +- ``RA_TLS_CERT_TIMESTAMP_NOT_BEFORE`` -- the generated RA-TLS certificate uses this + timestamp-not-before value, in the format "20010101000000" (this is also the default value if + environment variable is not available). +- ``RA_TLS_CERT_TIMESTAMP_NOT_AFTER`` -- the generated RA-TLS certificate uses this + timestamp-not-after value, in the format "20301231235959" (this is also the default value if + environment variable is not available). + +``ra_tls_verify_epid.so`` +^^^^^^^^^^^^^^^^^^^^^^^^^ + +This library contains the verification callback that should be registered with the TLS library +during verification of the TLS certificate. It verifies the RA-TLS certificate and the SGX quote by +sending it to the Intel Attestation Service (IAS) and retrieving the attestation report from IAS. +Typically linked into client applications. Not thread-safe. + +The library uses the following SGX-specific environment variables, representing SGX measurements, +if available: + +- ``RA_TLS_MRSIGNER`` (optional) -- verify that the server enclave has this ``MRSIGNER``. This is a + hex string. +- ``RA_TLS_MRENCLAVE`` (optional) -- verify that the server enclave has this ``MRENCLAVE``. This is + a hex string. +- ``RA_TLS_ISV_PROD_ID`` (optional) -- verify that the server enclave has this ``ISV_PROD_ID``. + This is a decimal string. +- ``RA_TLS_ISV_SVN`` (optional) -- verify that the server enclave has this ``ISV_SVN``. This is a + decimal string. + +The four SGX measurements above may be also verified via a user-specified callback with the +signature ``int (*callback)(char* mrenclave, char* mrsigner, char* isv_prod_id, char* isv_svn)``. +This callback must be registered via ``ra_tls_set_measurement_callback()``. The measurements from +the received SGX quote are passed as four arguments. It is up to the user to implement the correct +verification of SGX measurements in this callback (e.g., by comparing against expected values stored +in a central database). + +The library also uses the following SGX-specific environment variable: + +- ``RA_TLS_ALLOW_OUTDATED_TCB_INSECURE`` (optional) -- whether to allow outdated TCB as returned in + the IAS attestation report or returned by the DCAP verification library. Values ``1/true/TRUE`` + mean "allow outdated TCB". Note that allowing outdated TCB is **insecure** and should be used + only for debugging and testing. Outdated TCB is not allowed by default. + +The library uses the following EPID-specific environment variables if available: + +- ``RA_TLS_EPID_API_KEY`` (mandatory) -- client API key for EPID remote attestation. +- ``RA_TLS_IAS_REPORT_URL`` (optional) -- URL for IAS "verify attestation evidence" API endpoint. + If not specified, the default hard-coded URL for IAS is used. +- ``RA_TLS_IAS_SIGRL_URL`` (optional) -- URL for IAS "Retrieve SigRL" API endpoint. If not + specified, the default hard-coded URL for IAS is used. +- ``RA_TLS_IAS_PUB_KEY_PEM`` (optional) -- public key of IAS. If not specified, the default + hard-coded public key is used. + +``ra_tls_verify_dcap.so`` +^^^^^^^^^^^^^^^^^^^^^^^^^ + +Similarly to ``ra_tls_verify_epid.so``, this library contains the verification callback that +should be registered with the TLS library during verification of the TLS certificate. Verifies +the RA-TLS certificate and the SGX quote by forwarding it to DCAP verification library +(``libsgx_dcap_quoteverify.so``) and checking the result. Typically linked into client +applications. Not thread-safe. + +The library uses the same SGX-specific environment variables as ``ra_tls_verify_epid.so`` and +ignores the EPID-specific environment variables. Similarly to the EPID version, instead of using +environment variables, the four SGX measurements may be verified via a user-specified callback +registered via ``ra_tls_set_measurement_callback()``. + +The library expects all the DCAP infrastructure to be installed and working correctly on the host. diff --git a/Pal/src/host/Linux-SGX/tools/common/attestation.c b/Pal/src/host/Linux-SGX/tools/common/attestation.c index 4040845d..df0b1dbf 100644 --- a/Pal/src/host/Linux-SGX/tools/common/attestation.c +++ b/Pal/src/host/Linux-SGX/tools/common/attestation.c @@ -118,11 +118,11 @@ void display_quote(const void* quote_data, size_t quote_size) { } } -int verify_ias_report(const uint8_t* ias_report, size_t ias_report_size, uint8_t* ias_sig_b64, - size_t ias_sig_b64_size, bool allow_outdated_tcb, const char* nonce, - const char* mrsigner, const char* mrenclave, const char* isv_prod_id, - const char* isv_svn, const char* report_data, const char* ias_pub_key_pem, - bool expected_as_str) { +int verify_ias_report_extract_quote(const uint8_t* ias_report, size_t ias_report_size, + uint8_t* ias_sig_b64, size_t ias_sig_b64_size, + bool allow_outdated_tcb, const char* nonce, + const char* ias_pub_key_pem, uint8_t** out_quote, + size_t* out_quote_size) { mbedtls_pk_context ias_pub_key; int ret = -1; uint8_t* ias_sig = NULL; @@ -290,15 +290,42 @@ int verify_ias_report(const uint8_t* ias_report, size_t ias_report_size, uint8_t } DBG("IAS report: quote decoded, size %zu bytes\n", quote_size); + *out_quote = report_quote; + *out_quote_size = quote_size; + ret = 0; +out: + if (ret) { + free(report_quote); + } + if (json) + cJSON_Delete(json); + mbedtls_pk_free(&ias_pub_key); + free(ias_sig); + return ret ? -1 : 0; +} + +int verify_ias_report(const uint8_t* ias_report, size_t ias_report_size, uint8_t* ias_sig_b64, + size_t ias_sig_b64_size, bool allow_outdated_tcb, const char* nonce, + const char* mrsigner, const char* mrenclave, const char* isv_prod_id, + const char* isv_svn, const char* report_data, const char* ias_pub_key_pem, + bool expected_as_str) { + int ret; + + uint8_t* report_quote = NULL; + size_t quote_size = 0; + + ret = verify_ias_report_extract_quote(ias_report, ias_report_size, ias_sig_b64, + ias_sig_b64_size, allow_outdated_tcb, nonce, + ias_pub_key_pem, &report_quote, "e_size); + if (ret) { + goto out; + } + ret = verify_quote(report_quote, quote_size, mrsigner, mrenclave, isv_prod_id, isv_svn, report_data, expected_as_str); out: - if (json) - cJSON_Delete(json); - mbedtls_pk_free(&ias_pub_key); free(report_quote); - free(ias_sig); return ret ? -1 : 0; } diff --git a/Pal/src/host/Linux-SGX/tools/common/attestation.h b/Pal/src/host/Linux-SGX/tools/common/attestation.h index a2c9f68e..02a38d3f 100644 --- a/Pal/src/host/Linux-SGX/tools/common/attestation.h +++ b/Pal/src/host/Linux-SGX/tools/common/attestation.h @@ -69,6 +69,29 @@ int verify_ias_report(const uint8_t* ias_report, size_t ias_report_size, uint8_t const char* isv_svn, const char* report_data, const char* ias_pub_key_pem, bool expected_as_str); +/*! + * \brief Same as verify_ias_report() but instead of verifying the quote contained in IAS report, + * this function allocates enough memory to hold the quote and passes it to the user. + * + * \param[in] ias_report IAS attestation verification report. + * \param[in] ias_report_size Size of \a ias_report in bytes. + * \param[in] ias_sig_b64 IAS report signature (base64-encoded as returned by IAS). + * \param[in] ias_sig_b64_size Size of \a ias_sig_b64 in bytes. + * \param[in] allow_outdated_tcb Treat IAS status GROUP_OUT_OF_DATE as OK. + * \param[in] nonce (Optional) Nonce that's expected in the report. + * \param[in] ias_pub_key_pem (Optional) IAS public RSA key (PEM format, NULL-terminated). + * If not specified, a hardcoded Intel's key is used. + * \param[out] out_quote Buffer with quote. User is responsible for freeing it. + * \param[out] out_quote_size Size of \a out_quote in bytes. + * + * \return 0 on successful verification, negative value on error. + */ +int verify_ias_report_extract_quote(const uint8_t* ias_report, size_t ias_report_size, + uint8_t* ias_sig_b64, size_t ias_sig_b64_size, + bool allow_outdated_tcb, const char* nonce, + const char* ias_pub_key_pem, uint8_t** out_quote, + size_t* out_quote_size); + /*! * \brief Verify that the provided SGX quote contains expected values. * diff --git a/Pal/src/host/Linux-SGX/tools/ra-tls/Makefile b/Pal/src/host/Linux-SGX/tools/ra-tls/Makefile new file mode 100644 index 00000000..58643d1d --- /dev/null +++ b/Pal/src/host/Linux-SGX/tools/ra-tls/Makefile @@ -0,0 +1,34 @@ +include ../../../../../../Scripts/Makefile.configs + +CFLAGS += -I../.. \ + -I../common \ + -I../../../../../lib/crypto/mbedtls/install/include \ + -I../../../../../lib/crypto/mbedtls/crypto/include \ + -fPIC -fvisibility=hidden + +LDFLAGS += -L../../../../../lib/crypto/mbedtls/install/lib -Wl,-rpath,. + +.PHONY: all +all: epid # by default, only build EPID because it doesn't rely on additional (DCAP) libs + +.PHONY: epid +epid: libra_tls_attest.so libra_tls_verify_epid.so + +.PHONY: dcap +dcap: libra_tls_attest.so libra_tls_verify_dcap.so + +libra_tls_attest.so: ra_tls_attest.o + $(CC) $^ $(LDFLAGS) -l:libmbedx509.a -l:libmbedcrypto.a -shared -o $@ + +libra_tls_verify_epid.so: ra_tls_verify_common.o ra_tls_verify_epid.o + $(CC) $^ $(LDFLAGS) -L../common -lsgx_util -lmbedcrypto -lmbedx509 -shared -o $@ + +libra_tls_verify_dcap.so: ra_tls_verify_common.o ra_tls_verify_dcap.o + $(CC) $^ $(LDFLAGS) -L../common -lsgx_util -lmbedcrypto -lmbedx509 -lsgx_dcap_quoteverify -shared -o $@ + +.PHONY: clean +clean: + $(RM) *.o *.so + +.PHONY: distclean +distclean: clean diff --git a/Pal/src/host/Linux-SGX/tools/ra-tls/ra_tls.h b/Pal/src/host/Linux-SGX/tools/ra-tls/ra_tls.h new file mode 100644 index 00000000..d03423e8 --- /dev/null +++ b/Pal/src/host/Linux-SGX/tools/ra-tls/ra_tls.h @@ -0,0 +1,149 @@ +/* Copyright (C) 2018-2020 Intel Labs + This file is part of Graphene Library OS. + Graphene Library OS is free software: you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public License + as published by the Free Software Foundation, either version 3 of the + License, or (at your option) any later version. + Graphene Library OS is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + You should have received a copy of the GNU Lesser General Public License + along with this program. If not, see . */ + +#include +#include + +#include "sgx_arch.h" +#include "sgx_attest.h" + +#define RA_TLS_EPID_API_KEY "RA_TLS_EPID_API_KEY" + +#define RA_TLS_ALLOW_OUTDATED_TCB_INSECURE "RA_TLS_ALLOW_OUTDATED_TCB_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_IAS_PUB_KEY_PEM "RA_TLS_IAS_PUB_KEY_PEM" +#define RA_TLS_IAS_REPORT_URL "RA_TLS_IAS_REPORT_URL" +#define RA_TLS_IAS_SIGRL_URL "RA_TLS_IAS_SIGRL_URL" + +#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" + +#define SHA256_DIGEST_SIZE 32 +#define RSA_PUB_3072_KEY_LEN 3072 +#define RSA_PUB_3072_KEY_DER_LEN 422 +#define RSA_PUB_EXPONENT 65537 +#define PUB_KEY_SIZE_MAX 512 +#define IAS_REQUEST_NONCE_LEN 32 + +#define OID(N) {0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF8, 0x4D, 0x8A, 0x39, (N)} +static const uint8_t quote_oid[] = OID(0x06); +static const size_t quote_oid_len = sizeof(quote_oid); +#define QUOTE_MAX_SIZE 8192 + +typedef int (*verify_measurements_cb_t)(const char* mrenclave, const char* mrsigner, + const char* isv_prod_id, const char* isv_svn); + +/* internally used functions, not exported */ +__attribute__ ((visibility("hidden"))) +int getenv_allow_outdated_tcb(bool* allow_outdated_tcb); + +__attribute__ ((visibility("hidden"))) +int find_oid(const uint8_t* exts, size_t exts_len, const uint8_t* oid, size_t oid_len, + uint8_t** val, size_t* len); + +__attribute__ ((visibility("hidden"))) +int cmp_crt_pk_against_quote_report_data(mbedtls_x509_crt* crt, sgx_quote_t* quote); + +__attribute__ ((visibility("hidden"))) +int verify_quote_against_envvar_measurements(const void* quote, size_t quote_size); + +/*! + * \brief Callback for user-specific verification of measurements in SGX quote. + * + * 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). + * + * \param[in] f_cb Callback for user-specific verification; RA-TLS passes pointers to MRENCLAVE, + * MRSIGNER, ISV_PROD_ID, ISV_SVN measurements in SGX quote. Use NULL to revert to + * default behavior of RA-TLS. + * + * \return 0 on success, specific error code (negative int) otherwise. + */ +__attribute__ ((visibility("default"))) __attribute__((weak)) +void ra_tls_set_measurement_callback(verify_measurements_cb_t f_cb); + +/*! + * \brief mbedTLS-suitable verification callback for EPID-based (IAS) or ECDSA-based (DCAP) + * quote verification. + * + * This callback must be registered via mbedtls_ssl_conf_verify(). All parameters required for + * the SGX quote, IAS attestation report verification, and/or DCAP quote verification must be + * passed in the corresponding RA-TLS environment variables. + * + * \param[in] data Unused (required due to mbedTLS callback function signature). + * \param[in] crt Self-signed RA-TLS certificate with SGX quote embedded. + * \param[in] depth Unused (required due to mbedTLS callback function signature). + * \param[in] flags Unused (required due to mbedTLS callback function signature). + * + * \return 0 on success, specific mbedTLS error code (negative int) otherwise. + */ +__attribute__ ((visibility("default"))) __attribute__((weak)) +int ra_tls_verify_callback(void* data, mbedtls_x509_crt* crt, int depth, uint32_t* flags); + +/*! + * \brief Generic verification callback for EPID-based (IAS) or ECDSA-based (DCAP) quote + * verification. + * + * This function must be called from a non-mbedTLS verification callback, e.g., from a user-defined + * OpenSSL callback for SSL_CTX_set_cert_verify_callback(). All parameters required for the SGX + * quote, IAS attestation report verification, and/or DCAP quote verification must be passed in the + * corresponding RA-TLS environment variables. + * + * \param[in] der_crt Self-signed RA-TLS certificate with SGX quote embedded in DER format. + * \param[in] der_crt_size Size of the RA-TLS certificate. + * + * \return 0 on success, specific mbedTLS error code (negative int) otherwise. + */ +__attribute__ ((visibility("default"))) __attribute__((weak)) +int ra_tls_verify_callback_der(uint8_t* der_crt, size_t der_crt_size); + +/*! + * \brief mbedTLS-suitable function to generate a key and a corresponding RA-TLS certificate. + * + * The function first generates a random RSA keypair with PKCS#1 v1.5 encoding. Then it calculates + * the SHA256 hash over the generated public key and retrieves an SGX quote with report_data equal + * to the calculated hash (this ties the generated certificate key to the SGX quote). Finally, it + * generates the X.509 self-signed certificate with this key and the SGX quote embedded. + * + * \param[out] key Populated with a generated RSA keypair. + * \param[out] crt Populated with a self-signed RA-TLS certificate with SGX quote embedded. + * + * \return 0 on success, specific mbedTLS error code (negative int) otherwise. + */ +__attribute__ ((visibility("default"))) __attribute__((weak)) +int ra_tls_create_key_and_crt(mbedtls_pk_context* key, mbedtls_x509_crt* crt); + +/*! + * \brief Generic function to generate a key and a corresponding RA-TLS certificate (DER format). + * + * The function behaves the same as ra_tls_create_key_and_crt() but generates key and certificate + * in the DER format. The function allocates memory for key and certificate; user is expected to + * free them after use. + * + * \param[out] der_key Pointer to buffer populated with generated RSA keypair in DER format. + * \param[out] der_key_size Pointer to size of generated RSA keypair. + * \param[out] der_crt Pointer to buffer populated with self-signed RA-TLS certificate. + * \param[out] der_crt_size Pointer to size of self-signed RA-TLS certificate. + * + * \return 0 on success, specific mbedTLS error code (negative int) otherwise. + */ +__attribute__ ((visibility("default"))) __attribute__((weak)) +int ra_tls_create_key_and_crt_der(uint8_t** der_key, size_t* der_key_size, uint8_t** der_crt, + size_t* der_crt_size); diff --git a/Pal/src/host/Linux-SGX/tools/ra-tls/ra_tls_attest.c b/Pal/src/host/Linux-SGX/tools/ra-tls/ra_tls_attest.c new file mode 100644 index 00000000..079fc7c0 --- /dev/null +++ b/Pal/src/host/Linux-SGX/tools/ra-tls/ra_tls_attest.c @@ -0,0 +1,344 @@ +/* Copyright (C) 2018-2020 Intel Labs + This file is part of Graphene Library OS. + Graphene Library OS is free software: you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public License + as published by the Free Software Foundation, either version 3 of the + License, or (at your option) any later version. + Graphene Library OS is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + You should have received a copy of the GNU Lesser General Public License + along with this program. If not, see . */ + +/*! + * \file + * + * This file contains the implementation of server-side attestation for TLS libraries. It contains + * functions to create a self-signed RA-TLS certificate with an SGX quote embedded in it. It works + * with both EPID-based (quote v2) and ECDSA-based (quote v3 or DCAP) SGX quotes (in fact, it is + * agnostic to the format of the SGX quote). + * + * This file is part of the RA-TLS attestation library which is typically linked into server + * applications. This library is *not* thread-safe. + */ + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "ra_tls.h" +#include "sgx_arch.h" + +#define CERT_SUBJECT_NAME_VALUES "CN=RATLS,O=GrapheneDevelopers,C=US" +#define CERT_TIMESTAMP_NOT_BEFORE_DEFAULT "20010101000000" +#define CERT_TIMESTAMP_NOT_AFTER_DEFAULT "20301231235959" + +static ssize_t rw_file(const char* path, uint8_t* buf, size_t len, bool do_write) { + ssize_t bytes = 0; + ssize_t ret = 0; + + int fd = open(path, do_write ? O_WRONLY : O_RDONLY); + if (fd < 0) + return fd; + + while ((ssize_t)len > bytes) { + if (do_write) + ret = write(fd, buf + bytes, len - bytes); + else + ret = read(fd, buf + bytes, len - bytes); + + if (ret > 0) { + bytes += ret; + } else if (ret == 0) { + /* end of file */ + break; + } else { + if (ret < 0 && (errno == EAGAIN || errno == EINTR)) + continue; + break; + } + } + + close(fd); + return ret < 0 ? ret : bytes; +} + +/*! given public key \p pk, generate an RA-TLS certificate \p writecrt with \p quote embedded */ +static int generate_x509(mbedtls_pk_context* pk, const uint8_t* quote, size_t quote_size, + mbedtls_x509write_cert* writecrt) { + int ret; + char* cert_timestamp_not_before = NULL; + char* cert_timestamp_not_after = NULL; + + mbedtls_mpi serial; + mbedtls_mpi_init(&serial); + + mbedtls_x509write_crt_init(writecrt); + mbedtls_x509write_crt_set_md_alg(writecrt, MBEDTLS_MD_SHA256); + + /* generated certificate is self-signed, so declares itself both a subject and an issuer */ + mbedtls_x509write_crt_set_subject_key(writecrt, pk); + mbedtls_x509write_crt_set_issuer_key(writecrt, pk); + + /* set (dummy) subject names for both subject and issuer */ + ret = mbedtls_x509write_crt_set_subject_name(writecrt, CERT_SUBJECT_NAME_VALUES); + if (ret < 0) + goto out; + + ret = mbedtls_x509write_crt_set_issuer_name(writecrt, CERT_SUBJECT_NAME_VALUES); + if (ret < 0) + goto out; + + /* set a serial number (dummy "1") for the generated certificate */ + ret = mbedtls_mpi_read_string(&serial, 10, "1"); + if (ret < 0) + goto out; + + ret = mbedtls_x509write_crt_set_serial(writecrt, &serial); + if (ret < 0) + goto out; + + cert_timestamp_not_before = strdup(getenv(RA_TLS_CERT_TIMESTAMP_NOT_BEFORE) ? : + CERT_TIMESTAMP_NOT_BEFORE_DEFAULT); + if (!cert_timestamp_not_before) { + ret = MBEDTLS_ERR_X509_ALLOC_FAILED; + goto out; + } + + cert_timestamp_not_after = strdup(getenv(RA_TLS_CERT_TIMESTAMP_NOT_AFTER) ? : + CERT_TIMESTAMP_NOT_AFTER_DEFAULT); + if (!cert_timestamp_not_after) { + ret = MBEDTLS_ERR_X509_ALLOC_FAILED; + goto out; + } + + ret = mbedtls_x509write_crt_set_validity(writecrt, cert_timestamp_not_before, + cert_timestamp_not_after); + if (ret < 0) + goto out; + + ret = mbedtls_x509write_crt_set_basic_constraints(writecrt, /*is_ca=*/0, /*max_pathlen=*/-1); + if (ret < 0) + goto out; + + ret = mbedtls_x509write_crt_set_subject_key_identifier(writecrt); + if (ret < 0) + goto out; + + ret = mbedtls_x509write_crt_set_authority_key_identifier(writecrt); + if (ret < 0) + goto out; + + /* finally, embed the quote into the generated certificate (as X.509 extension) */ + ret = mbedtls_x509write_crt_set_extension(writecrt, (const char*)quote_oid, quote_oid_len, + /*critical=*/0, quote, quote_size); + if (ret < 0) + goto out; + + ret = 0; +out: + free(cert_timestamp_not_before); + free(cert_timestamp_not_after); + mbedtls_mpi_free(&serial); + return ret; +} + +/*! calculate sha256 over public key \p pk and copy it into \p sha */ +static int sha256_over_pk(mbedtls_pk_context* pk, uint8_t* sha) { + 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(pk, pk_der, PUB_KEY_SIZE_MAX); + if (pk_der_size_byte != RSA_PUB_3072_KEY_DER_LEN) + return MBEDTLS_ERR_PK_INVALID_PUBKEY; + + /* 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_ret(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) { + sgx_report_data_t user_report_data = {0}; + int ret = sha256_over_pk(pk, user_report_data.d); + if (ret < 0) + return ret; + + 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)) + return MBEDTLS_ERR_X509_FILE_IO_ERROR; + + uint8_t* quote = malloc(QUOTE_MAX_SIZE); + if (!quote) + return MBEDTLS_ERR_X509_ALLOC_FAILED; + + ssize_t quote_size = rw_file("/dev/attestation/quote", quote, QUOTE_MAX_SIZE, + /*do_write=*/false); + if (quote_size < 0) { + free(quote); + return MBEDTLS_ERR_X509_FILE_IO_ERROR; + } + + ret = generate_x509(pk, quote, quote_size, writecrt); + + free(quote); + return ret; +} + +static int create_key_and_crt(mbedtls_pk_context* key, mbedtls_x509_crt* crt, + uint8_t** crt_der, size_t* crt_der_size) { + int ret; + + if (!key || !crt) + return MBEDTLS_ERR_X509_FATAL_ERROR; + + mbedtls_ctr_drbg_context ctr_drbg; + mbedtls_ctr_drbg_init(&ctr_drbg); + + mbedtls_entropy_context entropy; + mbedtls_entropy_init(&entropy); + + mbedtls_x509write_cert writecrt; + mbedtls_x509write_crt_init(&writecrt); + + uint8_t* crt_der_buf = NULL; + uint8_t* output_buf = NULL; + size_t output_buf_size = 16 * 1024; /* enough for any X.509 certificate */ + + output_buf = malloc(output_buf_size); + if (!output_buf) { + ret = MBEDTLS_ERR_X509_ALLOC_FAILED; + goto out; + } + + ret = mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, &entropy, /*custom=*/NULL, + /*customlen=*/0); + if (ret < 0) + goto out; + + ret = mbedtls_pk_setup(key, mbedtls_pk_info_from_type(MBEDTLS_PK_RSA)); + if (ret < 0) + goto out; + + mbedtls_rsa_init((mbedtls_rsa_context*)key->pk_ctx, MBEDTLS_RSA_PKCS_V15, /*hash_id=*/0); + + ret = mbedtls_rsa_gen_key((mbedtls_rsa_context*)key->pk_ctx, mbedtls_ctr_drbg_random, + &ctr_drbg, RSA_PUB_3072_KEY_LEN, RSA_PUB_EXPONENT); + if (ret < 0) + goto out; + + ret = create_x509(key, &writecrt); + if (ret < 0) + goto out; + + int size = mbedtls_x509write_crt_der(&writecrt, output_buf, output_buf_size, + mbedtls_ctr_drbg_random, &ctr_drbg); + if (size < 0) { + ret = size; + goto out; + } + + if (crt_der && crt_der_size) { + crt_der_buf = malloc(size); + if (!crt_der_buf) { + ret = MBEDTLS_ERR_X509_ALLOC_FAILED; + goto out; + } + + /* note that mbedtls_x509write_crt_der() wrote data at the end of the output_buf */ + memcpy(crt_der_buf, output_buf + output_buf_size - size, size); + *crt_der = crt_der_buf; + *crt_der_size = size; + } + + if (crt) { + ret = mbedtls_x509_crt_parse_der(crt, output_buf + output_buf_size - size, size); + if (ret < 0) + goto out; + } + + ret = 0; +out: + if (ret < 0) { + free(crt_der_buf); + } + mbedtls_x509write_crt_free(&writecrt); + mbedtls_entropy_free(&entropy); + mbedtls_ctr_drbg_free(&ctr_drbg); + free(output_buf); + return ret; +} + +int ra_tls_create_key_and_crt(mbedtls_pk_context* key, mbedtls_x509_crt* crt) { + return create_key_and_crt(key, crt, NULL, NULL); +} + +int ra_tls_create_key_and_crt_der(uint8_t** der_key, size_t* der_key_size, + uint8_t** der_crt, size_t* der_crt_size) { + int ret; + + if (!der_key || !der_key_size || !der_crt || !der_crt_size) + return -EINVAL; + + mbedtls_pk_context key; + mbedtls_pk_init(&key); + + uint8_t* der_key_buf = NULL; + uint8_t* output_buf = NULL; + size_t output_buf_size = 1024; /* enough for any public key */ + + output_buf = malloc(output_buf_size); + if (!output_buf) { + ret = MBEDTLS_ERR_X509_ALLOC_FAILED; + goto out; + } + + ret = create_key_and_crt(&key, NULL, der_crt, der_crt_size); + if (ret < 0) { + goto out; + } + + /* populate der_key; note that der_crt was already populated */ + int size = mbedtls_pk_write_key_der(&key, output_buf, sizeof(output_buf)); + if (size < 0) { + ret = size; + goto out; + } + + der_key_buf = malloc(size); + if (!der_key_buf) { + ret = MBEDTLS_ERR_X509_ALLOC_FAILED; + goto out; + } + + /* note that mbedtls_pk_write_key_der() wrote data at the end of the output_buf */ + memcpy(der_key_buf, output_buf + sizeof(output_buf) - size, size); + *der_key = der_key_buf; + *der_key_size = size; + + ret = 0; +out: + if (ret < 0) { + free(der_key_buf); + } + mbedtls_pk_free(&key); + free(output_buf); + return ret; +} diff --git a/Pal/src/host/Linux-SGX/tools/ra-tls/ra_tls_verify_common.c b/Pal/src/host/Linux-SGX/tools/ra-tls/ra_tls_verify_common.c new file mode 100644 index 00000000..3201a476 --- /dev/null +++ b/Pal/src/host/Linux-SGX/tools/ra-tls/ra_tls_verify_common.c @@ -0,0 +1,243 @@ +/* Copyright (C) 2018-2020 Intel Labs + This file is part of Graphene Library OS. + Graphene Library OS is free software: you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public License + as published by the Free Software Foundation, either version 3 of the + License, or (at your option) any later version. + Graphene Library OS is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + You should have received a copy of the GNU Lesser General Public License + along with this program. If not, see . */ + +/*! + * \file + * + * This file contains the common code of verification callbacks for TLS libraries. All functions + * here have hidden visibility (not accessible from outside the shared library). + */ + +#define _GNU_SOURCE +#include +#include +#include +#include + +#include +#include +#include + +#include "attestation.h" +#include "ra_tls.h" +#include "util.h" + +verify_measurements_cb_t g_verify_measurements_cb = NULL; + +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) { + *validate_mrsigner = false; + *validate_mrenclave = false; + *validate_isv_prod_id = false; + *validate_isv_svn = false; + + const char* mrsigner_hex; + const char* mrenclave_hex; + const char* isv_prod_id_dec; + const char* isv_svn_dec; + + /* any of the below variables may be NULL (and then not used in validation) */ + mrsigner_hex = getenv(RA_TLS_MRSIGNER); + if (mrsigner_hex) { + if (parse_hex(mrsigner_hex, mrsigner, sizeof(*mrsigner)) != 0) + return MBEDTLS_ERR_X509_BAD_INPUT_DATA; + *validate_mrsigner = true; + } + + mrenclave_hex = getenv(RA_TLS_MRENCLAVE); + if (mrenclave_hex) { + if (parse_hex(mrenclave_hex, mrenclave, sizeof(*mrenclave)) != 0) + return MBEDTLS_ERR_X509_BAD_INPUT_DATA; + *validate_mrenclave = true; + } + + isv_prod_id_dec = getenv(RA_TLS_ISV_PROD_ID); + if (isv_prod_id_dec) { + errno = 0; + *isv_prod_id = strtoul(isv_prod_id_dec, NULL, 10); + if (errno) + return MBEDTLS_ERR_X509_BAD_INPUT_DATA; + *validate_isv_prod_id = true; + } + + isv_svn_dec = getenv(RA_TLS_ISV_SVN); + if (isv_svn_dec) { + errno = 0; + *isv_svn = strtoul(isv_svn_dec, NULL, 10); + if (errno) + return MBEDTLS_ERR_X509_BAD_INPUT_DATA; + *validate_isv_svn = true; + } + + return 0; +} + +int getenv_allow_outdated_tcb(bool* allow_outdated_tcb) { + *allow_outdated_tcb = false; + + char* str = getenv(RA_TLS_ALLOW_OUTDATED_TCB_INSECURE); + if (!str) + return 0; + + if (!strcmp(str, "1") || !strcmp(str, "true") || !strcmp(str, "TRUE")) + *allow_outdated_tcb = true; + + return 0; +} + +/*! searches for specific \p oid among \p exts and returns pointer to its value in \p val */ +int find_oid(const uint8_t* exts, size_t exts_len, const uint8_t* oid, size_t oid_len, + uint8_t** val, size_t* len) { + /* TODO: searching with memmem is not robust (what if some extension contains exactly these + * chars?), but mbedTLS has nothing generic enough for our purposes; this is still + * secure because this func is used for extracting the SGX quote which is verified + * later, but may lead to unexpected failures (hardly possible in real world though) */ + uint8_t* p = memmem(exts, exts_len, oid, oid_len); + if (!p) + return MBEDTLS_ERR_X509_INVALID_EXTENSIONS; + + const uint8_t* exts_end = exts + exts_len; + + /* move pointer past OID string and to the OID value */ + p += oid_len; + + if (p >= exts_end) + return MBEDTLS_ERR_X509_INVALID_EXTENSIONS; + + if (*p == 0x01) { + /* some TLS libs generate a BOOLEAN for the criticality of the extension before the + * extension value itself; check its value and skip it */ + p++; + if (p >= exts_end || *p++ != 0x01) { + /* BOOLEAN length must be 0x01 */ + return MBEDTLS_ERR_X509_INVALID_EXTENSIONS; + } + if (p >= exts_end || *p++ != 0x00) { + /* BOOLEAN value must be 0x00 (non-critical extension) */ + return MBEDTLS_ERR_X509_INVALID_EXTENSIONS; + } + } + + /* now comes the octet string */ + if (p >= exts_end || *p++ != 0x04) { + /* tag for octet string must be 0x04 */ + return MBEDTLS_ERR_X509_INVALID_EXTENSIONS; + } + if (p >= exts_end || *p++ != 0x82) { + /* length of octet string must be 0x82 (encoded in two bytes) */ + return MBEDTLS_ERR_X509_INVALID_EXTENSIONS; + } + + if (p + 2 >= exts_end) + return MBEDTLS_ERR_X509_INVALID_EXTENSIONS; + + *len = *p++; + *len <<= 8; + *len += *p++; + + *val = p; + + if (*len > QUOTE_MAX_SIZE || *val + *len > exts_end) + return MBEDTLS_ERR_X509_INVALID_EXTENSIONS; + + 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) { + 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, PUB_KEY_SIZE_MAX); + if (pk_der_size_byte != RSA_PUB_3072_KEY_DER_LEN) + return MBEDTLS_ERR_PK_INVALID_PUBKEY; + + /* 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_ret(pk_der, pk_der_size_byte, sha, /*is224=*/0); +} + +/*! compares if report_data from \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 sha[SHA256_DIGEST_SIZE]; + ret = sha256_over_crt_pk(crt, sha); + if (ret < 0) + return ret; + + ret = memcmp(quote->report_body.report_data.d, sha, SHA256_DIGEST_SIZE); + if (ret) + return MBEDTLS_ERR_X509_SIG_MISMATCH; + + return 0; +} + +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; +} + +int ra_tls_verify_callback_der(uint8_t* der_crt, size_t der_crt_size) { + int ret; + mbedtls_x509_crt crt; + mbedtls_x509_crt_init(&crt); + + ret = mbedtls_x509_crt_parse(&crt, der_crt, der_crt_size); + if (ret < 0) + goto out; + + ret = ra_tls_verify_callback(/*unused data=*/NULL, &crt, /*depth=*/0, /*flags=*/NULL); + if (ret < 0) + goto out; + + ret = 0; +out: + mbedtls_x509_crt_free(&crt); + return ret; +} + +int verify_quote_against_envvar_measurements(const void* quote, size_t quote_size) { + int ret; + + sgx_measurement_t expected_mrsigner; + 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; + + 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); + if (ret < 0) + return MBEDTLS_ERR_X509_BAD_INPUT_DATA; + + ret = verify_quote(quote, quote_size, + validate_mrsigner ? (char*)&expected_mrsigner : NULL, + validate_mrenclave ? (char*)&expected_mrenclave : NULL, + validate_isv_prod_id ? (char*)&expected_isv_prod_id : NULL, + validate_isv_svn ? (char*)&expected_isv_svn : NULL, + /*report_data=*/NULL, /*expected_as_str=*/false); + if (ret < 0) + return MBEDTLS_ERR_X509_CERT_VERIFY_FAILED; + + return 0; +} diff --git a/Pal/src/host/Linux-SGX/tools/ra-tls/ra_tls_verify_dcap.c b/Pal/src/host/Linux-SGX/tools/ra-tls/ra_tls_verify_dcap.c new file mode 100644 index 00000000..e484c802 --- /dev/null +++ b/Pal/src/host/Linux-SGX/tools/ra-tls/ra_tls_verify_dcap.c @@ -0,0 +1,201 @@ +/* Copyright (C) 2018-2020 Intel Labs + This file is part of Graphene Library OS. + Graphene Library OS is free software: you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public License + as published by the Free Software Foundation, either version 3 of the + License, or (at your option) any later version. + Graphene Library OS is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + You should have received a copy of the GNU Lesser General Public License + along with this program. If not, see . */ + +/*! + * \file + * + * This file contains the implementation of verification callbacks for TLS libraries. The callbacks + * verify the correctness of a self-signed RA-TLS certificate with an SGX quote embedded in it. The + * callbacks call into the `libsgx_dcap_quoteverify` DCAP library for ECDSA-based verification. A + * callback ra_tls_verify_callback() can be used directly in mbedTLS, and a more generic version + * ra_tls_verify_callback_der() should be used for other TLS libraries. + * + * This file is part of the RA-TLS verification library which is typically linked into client + * applications. This library is *not* thread-safe. + */ + +#define _GNU_SOURCE +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "attestation.h" +#include "ra_tls.h" +#include "sgx_arch.h" +#include "sgx_attest.h" +#include "util.h" + +extern verify_measurements_cb_t g_verify_measurements_cb; + +/* we cannot include libsgx_dcap_verify headers because they conflict with Graphene SGX headers, + * so we declare the used types and functions below */ + +/* QL stands for Quoting Library; QV stands for Quote Verification */ +#define SGX_QL_QV_MK_ERROR(x) (0x0000A000|(x)) +typedef enum _sgx_ql_qv_result_t { + /* quote verification passed and is at the latest TCB level */ + SGX_QL_QV_RESULT_OK = 0x0000, + /* quote verification passed and the platform is patched to the latest TCB level but additional + * configuration of the SGX platform may be needed */ + SGX_QL_QV_RESULT_CONFIG_NEEDED = SGX_QL_QV_MK_ERROR(0x0001), + /* quote is good but TCB level of the platform is out of date; platform needs patching to be at + * the latest TCB level */ + SGX_QL_QV_RESULT_OUT_OF_DATE = SGX_QL_QV_MK_ERROR(0x0002), + /* quote is good but the TCB level of the platform is out of date and additional configuration + * of the SGX platform at its current patching level may be needed; platform needs patching to + * be at the latest TCB level */ + SGX_QL_QV_RESULT_OUT_OF_DATE_CONFIG_NEEDED = SGX_QL_QV_MK_ERROR(0x0003), + /* signature over the application report is invalid */ + SGX_QL_QV_RESULT_INVALID_SIGNATURE = SGX_QL_QV_MK_ERROR(0x0004), + /* attestation key or platform has been revoked */ + SGX_QL_QV_RESULT_REVOKED = SGX_QL_QV_MK_ERROR(0x0005), + /* quote verification failed due to an error in one of the input */ + SGX_QL_QV_RESULT_UNSPECIFIED = SGX_QL_QV_MK_ERROR(0x0006), + /* TCB level of the platform is up to date, but SGX SW hardening is needed */ + SGX_QL_QV_RESULT_SW_HARDENING_NEEDED = SGX_QL_QV_MK_ERROR(0x0007), + /* TCB level of the platform is up to date, but additional configuration of the platform at its + * current patching level may be needed; moreover, SGX SW hardening is also needed */ + SGX_QL_QV_RESULT_CONFIG_AND_SW_HARDENING_NEEDED = SGX_QL_QV_MK_ERROR(0x0008), +} sgx_ql_qv_result_t; + +int sgx_qv_get_quote_supplemental_data_size(uint32_t* p_data_size); +int sgx_qv_verify_quote(const uint8_t* p_quote, uint32_t quote_size, void* p_quote_collateral, + const time_t expiration_check_date, + uint32_t* p_collateral_expiration_status, + sgx_ql_qv_result_t* p_quote_verification_result, void* p_qve_report_info, + uint32_t supplemental_data_size, uint8_t* p_supplemental_data); + + +int ra_tls_verify_callback(void* data, mbedtls_x509_crt* crt, int depth, uint32_t* flags) { + (void)data; + + int ret; + + uint8_t* supplemental_data = NULL; + uint32_t supplemental_data_size = 0; + + 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; + } + + /* extract SGX quote from "quote" OID extension from crt */ + sgx_quote_t* quote; + size_t quote_size; + ret = find_oid(crt->v3_ext.p, crt->v3_ext.len, quote_oid, quote_oid_len, (uint8_t**)"e, + "e_size); + if (ret < 0) + goto out; + + if (quote_size < sizeof(*quote)) { + ret = MBEDTLS_ERR_X509_INVALID_EXTENSIONS; + goto out; + } + + /* compare public key's hash from cert against quote's report_data */ + ret = cmp_crt_pk_against_quote_report_data(crt, quote); + if (ret < 0) + goto out; + + /* prepare user-supplied verification parameter "allow outdated TCB" */ + bool allow_outdated_tcb; + ret = getenv_allow_outdated_tcb(&allow_outdated_tcb); + if (ret < 0) { + ret = MBEDTLS_ERR_X509_BAD_INPUT_DATA; + goto out; + } + + /* call into libsgx_dcap_quoteverify to verify ECDSA/based SGX quote */ + ret = sgx_qv_get_quote_supplemental_data_size(&supplemental_data_size); + if (ret) { + ret = MBEDTLS_ERR_X509_FATAL_ERROR; + goto out; + } + + supplemental_data = (uint8_t*)malloc(supplemental_data_size); + if (!supplemental_data) { + ret = MBEDTLS_ERR_X509_ALLOC_FAILED; + goto out; + } + + time_t current_time = time(NULL); + if (current_time == ((time_t)-1)) { + ret = MBEDTLS_ERR_X509_FATAL_ERROR; + goto out; + } + + uint32_t collateral_expiration_status = 1; + sgx_ql_qv_result_t verification_result = SGX_QL_QV_RESULT_UNSPECIFIED; + + ret = sgx_qv_verify_quote((uint8_t*)quote, (uint32_t)quote_size, /*p_quote_collateral=*/NULL, + current_time, &collateral_expiration_status, &verification_result, + /*p_qve_report_info=*/NULL, supplemental_data_size, + supplemental_data); + if (ret) { + ret = MBEDTLS_ERR_X509_CERT_VERIFY_FAILED; + goto out; + } + + switch (verification_result) { + case SGX_QL_QV_RESULT_OK: + ret = 0; + break; + case SGX_QL_QV_RESULT_CONFIG_NEEDED: + case SGX_QL_QV_RESULT_OUT_OF_DATE: + case SGX_QL_QV_RESULT_OUT_OF_DATE_CONFIG_NEEDED: + case SGX_QL_QV_RESULT_SW_HARDENING_NEEDED: + case SGX_QL_QV_RESULT_CONFIG_AND_SW_HARDENING_NEEDED: + ret = allow_outdated_tcb ? 0 : MBEDTLS_ERR_X509_CERT_VERIFY_FAILED; + break; + case SGX_QL_QV_RESULT_INVALID_SIGNATURE: + case SGX_QL_QV_RESULT_REVOKED: + case SGX_QL_QV_RESULT_UNSPECIFIED: + default: + ret = MBEDTLS_ERR_X509_CERT_VERIFY_FAILED; + break; + } + + /* verify all measurements from the SGX quote */ + if (g_verify_measurements_cb) { + /* use user-supplied callback to verify measurements */ + ret = g_verify_measurements_cb((const char*)"e->report_body.mr_enclave, + (const char*)"e->report_body.mr_signer, + (const char*)"e->report_body.isv_prod_id, + (const char*)"e->report_body.isv_svn); + } else { + /* use default logic to verify measurements */ + ret = verify_quote_against_envvar_measurements(quote, quote_size); + } + if (ret < 0) { + ret = MBEDTLS_ERR_X509_CERT_VERIFY_FAILED; + goto out; + } + + ret = 0; +out: + free(supplemental_data); + return ret; +} diff --git a/Pal/src/host/Linux-SGX/tools/ra-tls/ra_tls_verify_epid.c b/Pal/src/host/Linux-SGX/tools/ra-tls/ra_tls_verify_epid.c new file mode 100644 index 00000000..697184c2 --- /dev/null +++ b/Pal/src/host/Linux-SGX/tools/ra-tls/ra_tls_verify_epid.c @@ -0,0 +1,281 @@ +/* Copyright (C) 2018-2020 Intel Labs + This file is part of Graphene Library OS. + Graphene Library OS is free software: you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public License + as published by the Free Software Foundation, either version 3 of the + License, or (at your option) any later version. + Graphene Library OS is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + You should have received a copy of the GNU Lesser General Public License + along with this program. If not, see . */ + +/*! + * \file + * + * This file contains the implementation of verification callbacks for TLS libraries. The callbacks + * verify the correctness of a self-signed RA-TLS certificate with an SGX quote embedded in it. The + * callbacks access Intel Attestation Service (IAS) for EPID-based attestation as part of the + * verification process. A callback ra_tls_verify_callback() can be used directly in mbedTLS, and + * a more generic version ra_tls_verify_callback_der() should be used for other TLS libraries. + * + * This file is part of the RA-TLS verification library which is typically linked into client + * applications. This library is *not* thread-safe. + */ + +#define _GNU_SOURCE +#include +#include +#include +#include + +#include +#include +#include + +#include "attestation.h" +#include "ias.h" +#include "ra_tls.h" +#include "sgx_arch.h" +#include "sgx_attest.h" +#include "util.h" + +extern verify_measurements_cb_t g_verify_measurements_cb; + +/** Default base URL for IAS API endpoints. Remove "/dev" for production environment. */ +#define IAS_URL_BASE "https://api.trustedservices.intel.com/sgx/dev" + +/** Default URL for IAS "verify attestation evidence" API endpoint. */ +#define IAS_URL_REPORT IAS_URL_BASE "/attestation/v3/report" + +/** Default URL for IAS "Retrieve SigRL" API endpoint. EPID group id is added at the end. */ +#define IAS_URL_SIGRL IAS_URL_BASE "/attestation/v3/sigrl" + +static char* g_api_key = NULL; +static char* g_report_url = NULL; +static char* g_sigrl_url = NULL; + +static int init_from_env(char** ptr, const char* env_name, char* default_val) { + assert(ptr == &g_api_key || ptr == &g_report_url || ptr == &g_sigrl_url); + + if (*ptr) { + /* already initialized */ + return 0; + } + + char* env_val = getenv(env_name); + if (!env_val) { + if (!default_val) + return MBEDTLS_ERR_X509_BAD_INPUT_DATA; + + *ptr = default_val; + return 0; + } + + size_t env_val_size = strlen(env_val) + 1; + *ptr = malloc(env_val_size); + if (!*ptr) + return MBEDTLS_ERR_X509_ALLOC_FAILED; + + memcpy(*ptr, env_val, env_val_size); + return 0; + +} + +static int generate_nonce(char* buf, size_t size) { + if (size != IAS_REQUEST_NONCE_LEN + 1) { + return MBEDTLS_ERR_X509_BAD_INPUT_DATA; + } + + char random_data[IAS_REQUEST_NONCE_LEN / 2]; + + FILE* f = fopen("/dev/urandom", "rb"); + if (!f) + return MBEDTLS_ERR_X509_FILE_IO_ERROR; + + size_t nmemb = fread(&random_data, sizeof(random_data), 1, f); + if (nmemb != 1) { + fclose(f); + return MBEDTLS_ERR_X509_FILE_IO_ERROR; + } + + fclose(f); + + if (hexdump_mem_to_buffer(&random_data, sizeof(random_data), buf, size) < 0) { + return MBEDTLS_ERR_X509_BAD_INPUT_DATA; + } + return 0; +} + +static int getenv_ias_pub_key_pem(char** ias_pub_key_pem) { + char* tmp = getenv(RA_TLS_IAS_PUB_KEY_PEM); + if (!tmp) { + /* return as NULL, and then a hard-coded public key of IAS is used */ + *ias_pub_key_pem = NULL; + return 0; + } + + tmp = strdup(tmp); + if (!tmp) + return MBEDTLS_ERR_X509_ALLOC_FAILED; + + *ias_pub_key_pem = tmp; + return 0; +} + +int ra_tls_verify_callback(void* data, mbedtls_x509_crt* crt, int depth, uint32_t* flags) { + (void)data; + + int ret; + struct ias_context_t* ias = NULL; + char* ias_pub_key_pem = NULL; + + char* report_data = NULL; + char* sig_data = NULL; + char* cert_data = NULL; + char* advisory_data = NULL; + + size_t report_data_size = 0; + size_t sig_data_size = 0; + size_t cert_data_size = 0; + size_t advisory_data_size = 0; + + uint8_t* quote_from_ias = NULL; + size_t quote_from_ias_size = 0; + + 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; + } + + ret = init_from_env(&g_api_key, RA_TLS_EPID_API_KEY, /*default_val=*/NULL); + if (ret < 0) + goto out; + + ret = init_from_env(&g_report_url, RA_TLS_IAS_REPORT_URL, IAS_URL_REPORT); + if (ret < 0) + goto out; + + ret = init_from_env(&g_sigrl_url, RA_TLS_IAS_SIGRL_URL, IAS_URL_SIGRL); + if (ret < 0) + goto out; + + /* extract SGX quote from "quote" OID extension from crt */ + sgx_quote_t* quote; + size_t quote_size; + ret = find_oid(crt->v3_ext.p, crt->v3_ext.len, quote_oid, quote_oid_len, (uint8_t**)"e, + "e_size); + if (ret < 0) + goto out; + + if (quote_size < sizeof(*quote)) { + ret = MBEDTLS_ERR_X509_INVALID_EXTENSIONS; + goto out; + } + + /* compare public key's hash from cert against quote's report_data */ + ret = cmp_crt_pk_against_quote_report_data(crt, quote); + if (ret < 0) + goto out; + + /* initialize the IAS context, send the quote to the IAS and receive IAS attestation report */ + ias = ias_init(g_api_key, g_report_url, g_sigrl_url); + if (!ias) { + ret = MBEDTLS_ERR_X509_FATAL_ERROR; + goto out; + } + + char nonce[IAS_REQUEST_NONCE_LEN + 1]; + ret = generate_nonce(nonce, sizeof(nonce)); + if (ret < 0) + goto out; + + ret = ias_verify_quote_raw(ias, quote, quote_size, nonce, &report_data, &report_data_size, + &sig_data, &sig_data_size, &cert_data, &cert_data_size, + &advisory_data, &advisory_data_size); + if (ret < 0) { + ret = MBEDTLS_ERR_X509_FATAL_ERROR; + goto out; + } + + if (!report_data || !report_data_size || !sig_data || !sig_data_size) { + /* received IAS attestation report doesn't contain report and/or signature */ + ret = MBEDTLS_ERR_X509_INVALID_FORMAT; + goto out; + } + + /* verify_ias_report() expects report_data and sig_data without the ending '\0' */ + assert(report_data[report_data_size - 1] == '\0'); + report_data_size--; + assert(sig_data[sig_data_size - 1] == '\0'); + sig_data_size--; + + /* TODO: obtain cert revocation lists via ias_get_sigrl(); currently they are not used during + * IAS attestation report verification, so we don't obtain them */ + + /* verify the received IAS attestation report */ + bool allow_outdated_tcb; + ret = getenv_allow_outdated_tcb(&allow_outdated_tcb); + if (ret < 0) { + ret = MBEDTLS_ERR_X509_BAD_INPUT_DATA; + goto out; + } + + ret = getenv_ias_pub_key_pem(&ias_pub_key_pem); + if (ret < 0) + goto out; + + ret = verify_ias_report_extract_quote((uint8_t*)report_data, report_data_size, + (uint8_t*)sig_data, sig_data_size, + allow_outdated_tcb, nonce, + ias_pub_key_pem, "e_from_ias, "e_from_ias_size); + if (ret < 0) { + ret = MBEDTLS_ERR_X509_CERT_VERIFY_FAILED; + goto out; + } + + /* verify all measurements from the SGX quote (extracted from IAS report) */ + if (g_verify_measurements_cb) { + /* use user-supplied callback to verify measurements */ + size_t min_quote_size = offsetof(sgx_quote_t, signature_len); + if (quote_from_ias_size < min_quote_size || quote_from_ias_size > QUOTE_MAX_SIZE) { + ret = MBEDTLS_ERR_X509_CERT_VERIFY_FAILED; + goto out; + } + + sgx_quote_t* q = (sgx_quote_t*)quote_from_ias; + ret = g_verify_measurements_cb((const char*)&q->report_body.mr_enclave, + (const char*)&q->report_body.mr_signer, + (const char*)&q->report_body.isv_prod_id, + (const char*)&q->report_body.isv_svn); + } else { + /* use default logic to verify measurements */ + ret = verify_quote_against_envvar_measurements(quote_from_ias, quote_from_ias_size); + } + if (ret < 0) { + ret = MBEDTLS_ERR_X509_CERT_VERIFY_FAILED; + goto out; + } + + ret = 0; +out: + if (ias) + ias_cleanup(ias); + + free(ias_pub_key_pem); + free(quote_from_ias); + free(report_data); + free(sig_data); + free(cert_data); + free(advisory_data); + + return ret; +}