[Pal/Linux-SGX] tools: add RA-TLS utility 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: creates self-signed RA-TLS X.509 certificate with
  SGX quote embedded; typically linked into server apps.
- ra_tls_verify_epid.so: verifies RA-TLS certificate by sending SGX
  quote to IAS and verifying attestation report from IAS; typically
  linked into client apps.
- ra_tls_verify_dcap.so: verifies RA-TLS certificate by providing SGX
  quote v3 to the libsgx_dcap_quoteverify library and relying on its
  assessment of the quote; typically linked into client apps.

This commit also adds an RA-TLS example using simple mbedTLS server
and client. The server generates the RA-TLS certificate, and the client
connects to the server, verifies this certificate, and performs a
dummy HTTP request. This example is added to Jenkins.
This commit is contained in:
Dmitrii Kuvaiskii
2020-04-09 23:39:08 +00:00
parent 933bb794e7
commit 91e882c130
17 changed files with 2513 additions and 9 deletions
+5
View File
@@ -0,0 +1,5 @@
/*.tar.gz
/OUTPUT
/client
/mbedtls
/server
+187
View File
@@ -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
+126
View File
@@ -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 %%
```
@@ -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
+376
View File
@@ -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 <assert.h>
#include <ctype.h>
#include <errno.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#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 <expected mrenclave> <expected mrsigner>"
" <expected isv_prod_id> <expected isv_svn>\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;
}
+339
View File
@@ -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 <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#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" \
"<h2>mbed TLS Test Server</h2>\r\n" \
"<p>Successful connection using: %s</p>\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;
}
+20
View File
@@ -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
+1
View File
@@ -9,3 +9,4 @@ $(targets):
$(MAKE) -C quote-dump $@
$(MAKE) -C ias-request $@
$(MAKE) -C verify-ias-report $@
$(MAKE) -C ra-tls $@
+95
View File
@@ -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.
@@ -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, &quote_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;
}
@@ -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.
*
@@ -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
@@ -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 <http://www.gnu.org/licenses/>. */
#include <mbedtls/x509_crt.h>
#include <stdint.h>
#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);
@@ -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 <http://www.gnu.org/licenses/>. */
/*!
* \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 <errno.h>
#include <fcntl.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <mbedtls/ctr_drbg.h>
#include <mbedtls/entropy.h>
#include <mbedtls/pk.h>
#include <mbedtls/rsa.h>
#include <mbedtls/sha256.h>
#include <mbedtls/x509_crt.h>
#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;
}
@@ -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 <http://www.gnu.org/licenses/>. */
/*!
* \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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <mbedtls/pk.h>
#include <mbedtls/sha256.h>
#include <mbedtls/x509_crt.h>
#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;
}
@@ -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 <http://www.gnu.org/licenses/>. */
/*!
* \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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include <mbedtls/pk.h>
#include <mbedtls/sha256.h>
#include <mbedtls/x509_crt.h>
#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**)&quote,
&quote_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*)&quote->report_body.mr_enclave,
(const char*)&quote->report_body.mr_signer,
(const char*)&quote->report_body.isv_prod_id,
(const char*)&quote->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;
}
@@ -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 <http://www.gnu.org/licenses/>. */
/*!
* \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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <mbedtls/pk.h>
#include <mbedtls/sha256.h>
#include <mbedtls/x509_crt.h>
#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**)&quote,
&quote_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, &quote_from_ias, &quote_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;
}