mirror of
https://github.com/clearlinux/graphene.git
synced 2026-08-28 03:38:10 +00:00
[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:
@@ -0,0 +1,5 @@
|
||||
/*.tar.gz
|
||||
/OUTPUT
|
||||
/client
|
||||
/mbedtls
|
||||
/server
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -9,3 +9,4 @@ $(targets):
|
||||
$(MAKE) -C quote-dump $@
|
||||
$(MAKE) -C ias-request $@
|
||||
$(MAKE) -C verify-ias-report $@
|
||||
$(MAKE) -C ra-tls $@
|
||||
|
||||
@@ -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, "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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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**)"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;
|
||||
}
|
||||
@@ -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**)"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;
|
||||
}
|
||||
Reference in New Issue
Block a user