diff --git a/bin/lib/common_service.sh b/bin/lib/common_service.sh index bceb4937..37e78efd 100644 --- a/bin/lib/common_service.sh +++ b/bin/lib/common_service.sh @@ -125,7 +125,7 @@ service_start() { ${F_LOGLEVEL} 2> $EFILE > $OFILE & echo $! > ${F_LOGDIR}/${IDENTITY}.pid else - ${F_SERVICE_CMD} --identity ${IDENTITY} --config ${IDENTITY}.toml enclave.toml --config-dir ${F_CONFDIR} \ + ${F_SERVICE_CMD} --identity ${IDENTITY} --config ${IDENTITY}.toml --config-dir ${F_CONFDIR} \ ${F_LEDGERURL} ${F_LOGLEVEL} 2> $EFILE > $OFILE & echo $! > ${F_LOGDIR}/${IDENTITY}.pid fi diff --git a/build/Makefile b/build/Makefile index 2f354557..335028ee 100644 --- a/build/Makefile +++ b/build/Makefile @@ -21,8 +21,8 @@ ifndef PDO_INSTALL_ROOT $(error Incomplete configuration, PDO_INSTALL_ROOT is not defined) endif -ifndef PDO_ENCLAVE_CODE_SIGN_PEM -$(error Incomplete configuration, PDO_ENCLAVE_CODE_SIGN_PEM is not defined) +ifndef PDO_SGX_KEY_ROOT +$(error Incomplete configuration, PDO_SGX_KEY_ROOT is not defined) endif ifndef SGX_MODE @@ -43,7 +43,6 @@ SRCDIR ?= $(abspath $(SCRIPTDIR)/..) BUILD = $(abspath $(SCRIPTDIR)/__tools__/build.sh) VERIFY_PRE_BUILD = $(abspath $(SCRIPTDIR)/__tools__/verify-pre-build.sh) -VERIFY_PRE_CONF = $(abspath $(SCRIPTDIR)/__tools__/verify-pre-conf.sh) CLEAN = $(abspath $(SCRIPTDIR)/__tools__/clean.sh) TESTSCRIPT = $(abspath $(SCRIPTDIR)/__tools__/run-tests.sh) BENCHMARKSCRIPT = $(abspath $(SCRIPTDIR)/__tools__/run-benchmarks.sh) @@ -95,9 +94,6 @@ $(DSTDIR) : verify-pre-build : $(VERIFY_PRE_BUILD) -verify-pre-config : - $(VERIFY_PRE_CONF) - build : $(PYTHON_DIR) . $(abspath $(DSTDIR)/bin/activate) && $(BUILD) @@ -106,16 +102,16 @@ verified-build : verify-pre-build rebuild : clean-build build $(CONDITIONAL_REGISTER_TARGET) -system-keys : ${PDO_ENCLAVE_CODE_SIGN_PEM} +system-keys : ${PDO_SGX_KEY_ROOT}/enclave_code_sign.pem -${PDO_ENCLAVE_CODE_SIGN_PEM} : - openssl genrsa -3 -out ${PDO_ENCLAVE_CODE_SIGN_PEM} 3072 +${PDO_SGX_KEY_ROOT}/enclave_code_sign.pem : + openssl genrsa -3 -out ${PDO_SGX_KEY_ROOT}/enclave_code_sign.pem 3072 # SERVICES_COUNT is the number of services of each type to create # First value is the number of eservices, then pservices, then # sservices, 5 of each is the default SERVICES_COUNT ?= 5 5 5 -SERVICES_CONF_TEMPLATES = $(addprefix $(SCRIPTDIR)/template/, eservice.toml pservice.toml enclave.toml) +SERVICES_CONF_TEMPLATES = $(addprefix $(SCRIPTDIR)/template/, eservice.toml pservice.toml) SERVICES_CONF_TARGET = $(DSTDIR)/opt/pdo/.services_configured $(SERVICES_CONF_TARGET) : $(PYTHON_DIR) $(SERVICE_CONF_TEMPLATES) @@ -125,18 +121,18 @@ $(SERVICES_CONF_TARGET) : $(PYTHON_DIR) $(SERVICE_CONF_TEMPLATES) config-services : $(SERVICES_CONF_TARGET) -verified-config : verify-pre-config +verified-config : ${MAKE} config config : config-services config-client -force-config : verify-pre-config +force-config : - rm -f $(SERVICES_CONF_TARGET) $(CLIENT_CONF_TARGET) ${MAKE} config ifeq ($(SGX_MODE),HW) register : $(PYTHON_DIR) - @ echo registering enclave and IAS public key on the ledger + @ echo Register the enclave registration policy on the ledger . $(abspath $(DSTDIR)/bin/activate) && $(SRCDIR)/eservice/bin/register-with-ledger.sh else @@ -171,6 +167,6 @@ benchmark : $(PYTHON_DIR) .PHONY : all environment register system-keys .PHONY : build rebuild verified-build verify-pre-build .PHONY : clean clean-build clean-install -.PHONY : config config-services force-config verified-config verify-pre-config +.PHONY : config config-services force-config verified-config .PHONY : client build-client config-client .PHONY : benchmark test diff --git a/build/__tools__/verify-pre-build.sh b/build/__tools__/verify-pre-build.sh index 52c33fae..538a6896 100755 --- a/build/__tools__/verify-pre-build.sh +++ b/build/__tools__/verify-pre-build.sh @@ -41,7 +41,6 @@ yell --------------- CONFIG AND ENVIRONMENT PRE-BUILD CHECK --------------- : "${PDO_INSTALL_ROOT:-$(warn Missing environment variable PDO_INSTALL_ROOT)}" : "${PDO_HOME:-$(warn Missing environment variable PDO_HOME)}" -: "${PDO_ENCLAVE_CODE_SIGN_PEM:-$(warn Missing environment variable PDO_ENCLAVE_CODE_SIGN_PEM)}" ([ ! -z "${SGX_SSL}" ] && [ -f ${SGX_SSL}/include/openssl/err.h ] ) || warn "Missing or invalid environment variable SGX_SSL" ([ ! -z "${SGX_SDK}" ] && [ -f ${SGX_SDK}/include/sgx.h ] ) || warn "Missing or invalid environment variable SGX_SDK" : "${SGX_MODE:-$(warn Missing environment variable SGX_MODE, set it to HW or SIM)}" @@ -59,8 +58,4 @@ if [ ! -d "${PDO_INSTALL_ROOT}" ]; then warn "PDO_INSTALL_ROOT directory does not exist" fi -if [ ! -f "${PDO_ENCLAVE_CODE_SIGN_PEM}" ]; then - warn "PDO_ENCLAVE_CODE_SIGN_PEM file does not exist" -fi - exit $F_VERIFIED diff --git a/build/cmake/SGX.cmake b/build/cmake/SGX.cmake index f9b3665e..d69b522e 100644 --- a/build/cmake/SGX.cmake +++ b/build/cmake/SGX.cmake @@ -16,10 +16,10 @@ # Environment Variables ################################################################################ -IF (NOT DEFINED ENV{PDO_ENCLAVE_CODE_SIGN_PEM}) - MESSAGE(FATAL_ERROR "PDO_ENCLAVE_CODE_SIGN_PEM not defined") +IF (NOT DEFINED ENV{PDO_SGX_KEY_ROOT}) + MESSAGE(FATAL_ERROR "PDO_SGX_KEY_ROOT not defined") ENDIF() -SET(PDO_ENCLAVE_CODE_SIGN_PEM "$ENV{PDO_ENCLAVE_CODE_SIGN_PEM}") +SET(PDO_SGX_KEY_ROOT "$ENV{PDO_SGX_KEY_ROOT}") IF (NOT DEFINED ENV{SGX_MODE}) MESSAGE(FATAL_ERROR "SGX_MODE not defined") diff --git a/build/template/eservice.toml b/build/template/eservice.toml index 5a40516a..1dba95df 100644 --- a/build/template/eservice.toml +++ b/build/template/eservice.toml @@ -72,3 +72,20 @@ DataPath = "${data}" # BaseName is the root of the name used to store data # about the enclave. A 'enc' extension will be added BaseName = "${identity}" + +# -------------------------------------------------- +# EnclaveModule -- configuration of the SGX contract enclave +# -------------------------------------------------- +[EnclaveModule] + +# Number of available enclave workers to service requests +NumberOfEnclaves = '7' + +# ias_url is the URL of the Intel Attestation Service (IAS) server. The +# example server is for debug enclaves only, +# the production url is without the trailing '/dev' +ias_url = 'https://api.trustedservices.intel.com/sgx/dev' + +# sgx key root folder configuration +sgx_key_root = "${sgx_key_root}" + diff --git a/build/template/pservice.toml b/build/template/pservice.toml index f22a351f..5e4b204b 100644 --- a/build/template/pservice.toml +++ b/build/template/pservice.toml @@ -68,3 +68,20 @@ DataPath = "${data}" # BaseName is the root of the name used to store data # about the enclave. A 'enc' extension will be added BaseName = "${identity}" + +# -------------------------------------------------- +# EnclaveModule -- configuration of the SGX contract enclave +# -------------------------------------------------- +[EnclaveModule] + +# Number of available enclave workers to service requests +NumberOfEnclaves = '7' + +# ias_url is the URL of the Intel Attestation Service (IAS) server. The +# example server is for debug enclaves only, +# the production url is without the trailing '/dev' +ias_url = 'https://api.trustedservices.intel.com/sgx/dev' + +# sgx key root folder configuration +sgx_key_root = "${sgx_key_root}" + diff --git a/common/tests/crypto/trusted/enclave/CMakeLists.txt b/common/tests/crypto/trusted/enclave/CMakeLists.txt index d368fb0c..c22237c4 100644 --- a/common/tests/crypto/trusted/enclave/CMakeLists.txt +++ b/common/tests/crypto/trusted/enclave/CMakeLists.txt @@ -41,4 +41,4 @@ TARGET_LINK_LIBRARIES(${PROJECT_NAME} ${COMMON_TRUSTED_LIBS}) TARGET_LINK_LIBRARIES(${PROJECT_NAME} -Wl,--end-group) SGX_PREPARE_TRUSTED_LINK(${PROJECT_NAME}) -SGX_SIGN_ENCLAVE(${PROJECT_NAME} ${PDO_ENCLAVE_CODE_SIGN_PEM} ${ENCLAVE_CONFIG}) +SGX_SIGN_ENCLAVE(${PROJECT_NAME} ${PDO_SGX_KEY_ROOT}/enclave_code_sign.pem ${ENCLAVE_CONFIG}) diff --git a/docker/Makefile b/docker/Makefile index 9137b352..71418e18 100644 --- a/docker/Makefile +++ b/docker/Makefile @@ -50,7 +50,9 @@ DOCKER_BUILDARGS += --build-arg UID=$(PDO_USER_UID) DOCKER_BUILDARGS += --build-arg GID=$(PDO_GROUP_UID) DOCKER_ARGS = $(DOCKER_BUILDARGS) -IMAGES=base client services_base services ccf_base ccf +IAS_CERTIFICATES=$(DOCKER_DIR)/repository/common/crypto/verify_ias_report/ias-certificates.txt + +IMAGES=base client services_base services services_sgx ccf_base ccf # for the most part this is just used to force rebuild when the # PDO repository has changed @@ -71,6 +73,13 @@ build_% : repository --tag pdo_$*:$(PDO_VERSION) \ --file '$(DOCKER_DIR)'/pdo_$*.dockerfile . +sgx_build_services : $(IAS_CERTIFICATES) repository build_services_base + docker build $(DOCKER_ARGS) \ + --build-arg PDO_VERSION=$(PDO_VERSION) \ + --build-arg SGX_MODE=HW \ + --tag pdo_services_sgx:$(PDO_VERSION) \ + --file $(DOCKER_DIR)/pdo_services.dockerfile . + # docker build dependencies build_client: build_base build_services: build_services_base @@ -104,6 +113,11 @@ stop_services : stop_client : - docker rm -f client_container +$(IAS_CERTIFICATES) : repository + # the script prepares the certificates from the source repo + # and moves only the necessary artifacts to the destination repo (absolute path required) + $(DOCKER_DIR)/tools/prepare_ias_certificates.sh "$(PDO_SOURCE_ROOT)" $(DOCKER_DIR)/$< + # ----------------------------------------------------------------- # We need a repository with the source for the branch we are going # to build. In theory this could just be a copy of the local source @@ -112,8 +126,14 @@ stop_client : # performance requirements are relatively low. # ----------------------------------------------------------------- repository : + # clone the repo git clone --single-branch --branch $(PDO_BRANCH) --recurse-submodules '$(PDO_REPO)' repository + # Prepare enclave signing key (if any, this goes in the repo itself). + # This is effective only in HW mode builds. + # PDO_SGX_KEY_ROOT is an optional parameter (either set in environment or empty) + $(DOCKER_DIR)/tools/copy_enclave_signing_key.sh "$(PDO_SOURCE_ROOT)" repository "${PDO_SGX_KEY_ROOT}" + clean_repository : rm -rf repository @@ -130,12 +150,35 @@ TEST_FILES += -f services_base.yaml TEST_FILES += -f ccf_base.yaml TEST_FILES += -f test.yaml +TEST_SGX_FILES = ${TEST_FILES} +TEST_SGX_FILES += -f test-sgx.yaml + +SGX_DEVICE_PATH=$(shell if [ -e "/dev/isgx" ]; \ + then echo "/dev/isgx"; \ + elif [ -e "/dev/sgx/enclave" ]; \ + then echo "/dev/sgx/enclave"; \ + else echo "ERROR: NO SGX DEVICE FOUND"; \ + fi) + +DOCKER_COMPOSE_SGX := env SGX_DEVICE_PATH=${SGX_DEVICE_PATH} docker-compose + build_test : repository build_services build_ccf build_client test : clean_config clean_repository build_test stop_all PDO_VERSION=$(PDO_VERSION) docker-compose $(TEST_FILES) up --abort-on-container-exit PDO_VERSION=$(PDO_VERSION) docker-compose $(TEST_FILES) down +sgx_build_test : repository sgx_build_services build_ccf build_client + +sgx_keys : + # Prepare sgx keys. + # PDO_SGX_KEY_ROOT is an optional parameter (either set in environment or empty) + $(DOCKER_DIR)/tools/copy_sgx_keys.sh "$(PDO_SOURCE_ROOT)" "$(DOCKER_DIR)" "${PDO_SGX_KEY_ROOT}" + +sgx_test : clean_config clean_repository sgx_build_test stop_all sgx_keys + PDO_VERSION=$(PDO_VERSION) $(DOCKER_COMPOSE_SGX) $(TEST_SGX_FILES) up --abort-on-container-exit + PDO_VERSION=$(PDO_VERSION) $(DOCKER_COMPOSE_SGX) $(TEST_SGX_FILES) down + # ----------------------------------------------------------------- # Cleaning is a bit interesting because the containers don't go away # unless they are told to very nicely. Until they go away they hold onto @@ -151,12 +194,16 @@ clean_images : $(addprefix clean_,$(IMAGES)) clean_config : rm -f '$(DOCKER_DIR)'/xfer/ccf/keys/*.pem '$(DOCKER_DIR)'/xfer/ccf/etc/*.toml rm -f '$(DOCKER_DIR)'/xfer/services/keys/*.pem '$(DOCKER_DIR)'/xfer/services/etc/*.toml + rm -f '$(DOCKER_DIR)'/xfer/services/keys/sgx/*.pem '$(DOCKER_DIR)'/xfer/services/keys/sgx/*.txt rm -f '$(DOCKER_DIR)'/xfer/services/etc/site.psh + # clean the artifacts of the prepare_ias_certificates target + rm -f $(IAS_CERTIFICATES) clean : clean_images clean_config clean_repository .PHONY: clean clean_images clean_config clean_repository .PHONY: build_test test +.PHONY: sgx_build_test sgx_test sgx_keys .PHONY: run_ccf run_client run_services # ----------------------------------------------------------------- diff --git a/docker/README.md b/docker/README.md index dc532f52..b8c21df6 100644 --- a/docker/README.md +++ b/docker/README.md @@ -79,6 +79,17 @@ as services in detached mode. The last for the client will run an interactive shell in the client container. See below for information on how to use the client container. +### Build for SGX ### + +For the contract enclave to run in SGX hardware mode, the services +image must be built using the following target: +```bash + make sgx_build_services +``` +This will create the `pdo_service_sgx` image. Inside the image, +the `SGX_MODE=HW` environment variable further indicates that the +service were built to run in SGX. + ## Pattern: Local Development in a Container ## @@ -246,6 +257,30 @@ with the PDO tool `pdo-configure-services`. --name ${USER}_services_container pdo_services --mode copy ``` +#### PDO Services Deployment Using SGX #### + +There are a few _additional_ considerations when using the services with SGX. + +Before starting the container, make sure that the SGX collateral is available +as described [here](../docs/install). + +Also, recall that the attestation policy on the ledger has to be set once by the +first eservice of a ledger consortium member. Hence, the first service container +that is deputed to perform such registration must be instructed to do so. +```bash + docker run -v $(SCRIPT_DIR)/xfer/:/project/pdo/xfer --network host \ + -v :/var/run/aesmd --device=:/dev/sgx/enclave \ + --name ${USER}_services_container pdo_services_sgx --register +``` +This updated command allows to trigger the registration step right before +starting the services. The policy registration must happen before enclaves are +registered (or any enclave registration will fail). + +Finally, the _same_ SGX collateral must be made available to all service containers. +At enclave registration time, this will allow the eservice to generate the right +quote (and attestation verification report) that meets the attestation policy +originally registered with the PDO Transaction Processor. + ### PDO Client Deployment ### The client image creates an interactive environment for connecting diff --git a/docker/pdo_services.dockerfile b/docker/pdo_services.dockerfile index 1bb4a139..692e6427 100644 --- a/docker/pdo_services.dockerfile +++ b/docker/pdo_services.dockerfile @@ -27,6 +27,9 @@ FROM pdo_services_base:${PDO_VERSION} # ----------------------------------------------------------------- ARG REBUILD=0 +ARG SGX_MODE=SIM +ENV SGX_MODE $SGX_MODE + ARG PDO_DEBUG_BUILD=0 ENV PDO_DEBUG_BUILD=${PDO_DEBUG_BUILD} diff --git a/docker/pdo_services_base.dockerfile b/docker/pdo_services_base.dockerfile index ef813eee..a039fe6f 100644 --- a/docker/pdo_services_base.dockerfile +++ b/docker/pdo_services_base.dockerfile @@ -24,9 +24,6 @@ ARG SGX=2.22 ARG OPENSSL=3.0.12 ARG SGXSSL=3.0_Rev1 -ARG SGX_MODE=SIM -ENV SGX_MODE $SGX_MODE - RUN echo "deb [arch=amd64] https://download.01.org/intel-sgx/sgx_repo/ubuntu ${UBUNTU_NAME} main" >> /etc/apt/sources.list \ && wget -qO - https://download.01.org/intel-sgx/sgx_repo/ubuntu/intel-sgx-deb.key | apt-key add - \ && apt-get update \ @@ -71,17 +68,16 @@ ENV PATH="/opt/intel/sgxsdk.extras/external/toolset/ubuntu${UBUNTU_VERSION}:${PA # ----------------------------------------------------------------- # SGXSSL -# Note that we build sgxssl with SIM mode; the SGX_MODE only changes -# the mode for running tests and we do not want the tests run in HW -# mode +# Note that the SGX_MODE variable only determines the mode for +# running tests. We do not want the tests to run in HW mode here. +# This allows us to keep this image mode-agnostic. # ----------------------------------------------------------------- WORKDIR /tmp RUN . /opt/intel/sgxsdk/environment \ && git clone --depth 1 --branch ${SGXSSL} 'https://github.com/intel/intel-sgx-ssl.git' \ && wget -q -P /tmp/intel-sgx-ssl/openssl_source https://www.openssl.org/source/openssl-${OPENSSL}.tar.gz \ && cd /tmp/intel-sgx-ssl/Linux \ - && if [ $SGX_MODE = SIM ] ; then SKIP_INTELCPU_CHECK=TRUE ; else SKIP_INTELCPU_CHECK=FALSE ; fi \ - && bash -c "make SKIP_INTELCPU_CHECK=$SKIP_INTELCPU_CHECK SGX_MODE=$SGX_MODE NO_THREADS=1 DESTDIR=/opt/intel/sgxssl VERBOSE=0 all &> /dev/null" \ + && bash -c "make SKIP_INTELCPU_CHECK=TRUE SGX_MODE=SIM NO_THREADS=1 DESTDIR=/opt/intel/sgxssl VERBOSE=0 all &> /dev/null" \ && make install \ && make clean \ && rm -rf /tmp/intel-sgx-ssl diff --git a/docker/test-sgx.yaml b/docker/test-sgx.yaml new file mode 100644 index 00000000..5b1a7d53 --- /dev/null +++ b/docker/test-sgx.yaml @@ -0,0 +1,33 @@ +# Copyright 2024 Intel Corporation +# +# 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. +# ------------------------------------------------------------------------------ +version: "3.4" + +services: + ccf_container: + environment: + - SGX_MODE=HW + + services_container: + environment: + # the PDO_FORCE_IAS_PROXY is a small hack that is used to force IAS connections + # through a proxy when one such proxy must be used. + # If the variable is defined in the host environment, it is propagated to the containers. + - PDO_FORCE_IAS_PROXY=${PDO_FORCE_IAS_PROXY:-false} + image: pdo_services_sgx:${PDO_VERSION:-latest} + volumes: + - /var/run/aesmd:/var/run/aesmd + devices: + - ${SGX_DEVICE_PATH:-/dev/isgx}:${SGX_DEVICE_PATH:-/dev/isgx} + diff --git a/docker/tools/copy_enclave_signing_key.sh b/docker/tools/copy_enclave_signing_key.sh new file mode 100755 index 00000000..ef6bc7bc --- /dev/null +++ b/docker/tools/copy_enclave_signing_key.sh @@ -0,0 +1,58 @@ +#!/bin/bash + +# Copyright 2024 Intel Corporation +# +# 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. + + +# This script copies the enclave signing key on the host (if any) for the docker container build. +# environment.sh is not imported, as this script is meant to run on the host. +# Note: the key (if any) is copied into the default folder for HW mode builds; +# so for SIM mode builds, this will have no effect. + +if [ $# != 2 ] && [ $# != 3 ]; then + echo "$(basename $0 ' [ ${PDO_SGX_KEY_ROOT} ]')" + echo "PDO source and dest paths are required, PDO_SGX_KEY_ROOT is optional" + exit 1 +fi + +PDO_SOURCE_ROOT=$1 +PDO_DEST_ROOT=$2 +PDO_SGX_KEY_ROOT=$3 + +source ${PDO_SOURCE_ROOT}/bin/lib/common.sh + +# If an enclave signing key is available on the host, copy that under build/keys in the repo +# Note: on the host, the key must be in ${PDO_SGX_KEY_ROOT}/enclave_code_sign.pem, +# and the env variable must be defined. +# Note: in the docker container, the host key (or a new key) will be placed on the same path, +# but the PDO_SGX_KEY_ROOT default value is defined in docker/tools/environment.sh + +KEY_REL_PATH="build/keys/sgx_mode_hw/enclave_code_sign.pem" + +if [ ! -z "${PDO_SGX_KEY_ROOT}" ] && [ -e "${PDO_SGX_KEY_ROOT}/enclave_code_sign.pem" ]; then + yell "Enclave signing key: using host-provided key: ${PDO_SGX_KEY_ROOT}/enclave_code_sign.pem" + yell "Enclave signing key: copying it to ${PDO_DEST_ROOT}/${KEY_REL_PATH}" + try cp ${PDO_SGX_KEY_ROOT}/enclave_code_sign.pem ${PDO_DEST_ROOT}/${KEY_REL_PATH} +else + yell "Enclave signing key: none available, now checking default path ${PDO_SOURCE_ROOT}/${KEY_REL_PATH}" + if [ -e "${PDO_SOURCE_ROOT}/${KEY_REL_PATH}" ]; then + yell "Enclave signing key: key available, copying it to ${PDO_DEST_ROOT}/${KEY_REL_PATH}" + try cp ${PDO_SOURCE_ROOT}/${KEY_REL_PATH} ${PDO_DEST_ROOT}/${KEY_REL_PATH} + else + yell "Enclave signing key: no default key, a new one will be generated" + fi +fi + +exit 0 + diff --git a/docker/tools/copy_sgx_keys.sh b/docker/tools/copy_sgx_keys.sh new file mode 100755 index 00000000..4b6914b6 --- /dev/null +++ b/docker/tools/copy_sgx_keys.sh @@ -0,0 +1,70 @@ +#!/bin/bash + +# Copyright 2024 Intel Corporation +# +# 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. + + +# This script prepares the sgx keys on the host for the docker container build. +# environment.sh is not imported, as this script is meant to run on the host. + +if [ $# != 2 ] && [ $# != 3 ]; then + echo "$(basename $0 '${PDO_SOURCE_ROOT} ${DOCKER_DIR} [ ${PDO_SGX_KEY_ROOT} ]')" + echo "PDO_SOURCE_ROOT and DOCKER_DIR are required, PDO_SGX_KEY_ROOT is optional" + exit 1 +fi + +PDO_SOURCE_ROOT=$1 +DOCKER_DIR=$2 +PDO_SGX_KEY_ROOT=$3 + +source ${PDO_SOURCE_ROOT}/bin/lib/common.sh + +# check for sgx keys in PDO_SGX_KEY_ROOT and copy that in xfer +# or, copy anything in the default folder to xfer + +if [ ! -z "${PDO_SGX_KEY_ROOT}" ]; then + # PDO_SGX_KEY_ROOT is set + yell "SGX keys: checking for source SGX keys in ${PDO_SGX_KEY_ROOT}" + if [ ! -f ${PDO_SGX_KEY_ROOT}/sgx_spid_api_key.txt ] || + [ ! -f ${PDO_SGX_KEY_ROOT}/sgx_spid.txt ] || + [ ! -f ${PDO_SGX_KEY_ROOT}/sgx_ias_key.pem ]; then + die "SGX keys: missing - check PDO_SGX_KEY_ROOT and SGX keys in it" + fi + + yell "SGX keys: found ... copying them to docker" + try cp ${PDO_SGX_KEY_ROOT}/* ${DOCKER_DIR}/xfer/services/keys/sgx/ + +else + yell "SGX keys: PDO_SGX_KEY_ROOT undefined" + yell "SGX keys: copying default folder ${PDO_SOURCE_ROOT}/build/keys/sgx_mode_hw/ to docker" + # copy anything in the default folder, and ignore errors if no keys exist + ls ${PDO_SOURCE_ROOT}/build/keys/sgx_mode_hw/* + cp ${PDO_SOURCE_ROOT}/build/keys/sgx_mode_hw/* ${DOCKER_DIR}/xfer/services/keys/sgx/ 2>/dev/null + echo $? + ls ${DOCKER_DIR}/xfer/services/keys/sgx/ +fi + +# test sgx keys availability in xfer +# this succeeds if it was copied above, or if it was already in place +yell "SGX keys: checking for SGX keys in docker" +if [ ! -f ${DOCKER_DIR}/xfer/services/keys/sgx/sgx_spid_api_key.txt ] || + [ ! -f ${DOCKER_DIR}/xfer/services/keys/sgx/sgx_spid.txt ] || + [ ! -f ${DOCKER_DIR}/xfer/services/keys/sgx/sgx_ias_key.pem ]; then + yell "SGX keys: not found in docker -- set PDO_SGX_KEY_ROOT and check sgx keys" + exit 1 +fi +yell "SGX keys: docker-ready" + +exit 0 + diff --git a/docker/tools/environment.sh b/docker/tools/environment.sh index c0850b12..f6407b12 100755 --- a/docker/tools/environment.sh +++ b/docker/tools/environment.sh @@ -44,23 +44,7 @@ fi export XFER_DIR=${XFER_DIR:-/project/pdo/xfer} -# if the container is running HW mode, then we will grab the -# SGX keys from the xfer directory; we know that the default -# keys must be overridden -if [ ${SGX_MODE} == "HW" ]; then - export PDO_SGX_KEY_ROOT=${XFER_DIR}/services/keys/sgx -else - export PDO_SGX_KEY_ROOT=${PDO_SOURCE_ROOT}/build/keys/sgx_mode_${SGX_MODE,,} -fi - -# this variable is needed for the build for signing the -# eservice and pservice enclaves -export PDO_ENCLAVE_CODE_SIGN_PEM=${PDO_SGX_KEY_ROOT}/enclave_code_sign.pem - -# these are only used for configuration and registration -# they are not used at build or run time -export PDO_SPID="$(cat ${PDO_SGX_KEY_ROOT}/sgx_spid.txt)" -export PDO_SPID_API_KEY="$(cat ${PDO_SGX_KEY_ROOT}/sgx_spid_api_key.txt)" +export PDO_SGX_KEY_ROOT=${PDO_SOURCE_ROOT}/build/keys/sgx_mode_${SGX_MODE,,} # set up the ccf directories, ccf_base is where the ccf # core is installed, ccf_pdo_dir is where the pdo tp diff --git a/docker/tools/prepare_ias_certificates.sh b/docker/tools/prepare_ias_certificates.sh new file mode 100755 index 00000000..4d67af06 --- /dev/null +++ b/docker/tools/prepare_ias_certificates.sh @@ -0,0 +1,51 @@ +#!/bin/bash + +# Copyright 2024 Intel Corporation +# +# 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. + + +# This script prepares the IAS certificate that is necessary for build in HW mode. +# The certificate is downloaded in the repo before the docker build, thus becoming +# part of the "repository". The docker build will then clone the repository in the +# container. As the certificate will be avialable, the build inside docker will not +# attempt to retrieve it. + +if [ $# != 2 ] ; then + echo "$(basename $0 '$ /dev/null + # ----------------------------------------------------------------- yell configure services for host $PDO_HOSTNAME and ledger $PDO_LEDGER_URL # ----------------------------------------------------------------- @@ -56,7 +62,7 @@ yell check for registration # ----------------------------------------------------------------- # this probably requires additional CCF keys, need to test this if [ "$SGX_MODE" == "HW" ]; then - if [ ! -f ${XFER}/ccf/keys/memberccf_privk.pem ] ; then + if [ ! -f ${XFER_DIR}/ccf/keys/memberccf_privk.pem ] ; then die unable to locate CCF policies keys fi diff --git a/docker/tools/start_services.sh b/docker/tools/start_services.sh index 11f21926..b7bf6853 100755 --- a/docker/tools/start_services.sh +++ b/docker/tools/start_services.sh @@ -82,6 +82,16 @@ check_pdo_runtime_env export no_proxy=$PDO_HOSTNAME,$PDO_LEDGER_ADDRESS,$no_proxy export NO_PROXY=$PDO_HOSTNAME,$PDO_LEDGER_ADDRESS,$NO_PROXY +# ----------------------------------------------------------------- +yell copy sgx keys +# ----------------------------------------------------------------- +# copy any keys in the SGX directory, ignore any errors if no keys exist +cp ${XFER_DIR}/services/keys/sgx/* ${PDO_SGX_KEY_ROOT} 2>/dev/null + +# ----------------------------------------------------------------- +yell Register with ledger: ${F_REGISTER} +# ----------------------------------------------------------------- + # ----------------------------------------------------------------- # Handle the configuration of the services # ----------------------------------------------------------------- @@ -115,7 +125,7 @@ try cp ${XFER_DIR}/ccf/keys/networkcert.pem ${PDO_LEDGER_KEY_ROOT}/ yell register the enclave if necessary # ----------------------------------------------------------------- if [ "${F_REGISTER,,}" == 'yes' ]; then - if [ ! -f ${XFER}/ccf/keys/memberccf_privk.pem ] ; then + if [ ! -f ${XFER_DIR}/ccf/keys/memberccf_privk.pem ] ; then die unable to locate CCF policies keys fi diff --git a/docs/install.md b/docs/install.md index 3943125b..969a6a57 100644 --- a/docs/install.md +++ b/docs/install.md @@ -115,13 +115,19 @@ to create the client authentication key. The key will be available from your profile page. Now organize your data as follows under the `${PDO_SGX_KEY_ROOT}` folder -(the default folder is `${PDO_SOURCE_ROOT}/build/keys/sgx_mode_${SGX_MODE,,}`, +(the default folder is `${PDO_SOURCE_ROOT}/build/keys/sgx_mode_hw`, or you can define yours with `export PDO_SGX_KEY_ROOT=`): -* save your SPID in `${PDO_SGX_KEY_ROOT}/sgx_spid_api_key.txt` +* save your SPID in `${PDO_SGX_KEY_ROOT}/sgx_spid.txt` * save your API key in `${PDO_SGX_KEY_ROOT}/sgx_spid_api_key.txt` * save the IAS root CA certificate in `${PDO_SGX_KEY_ROOT}/sgx_ias_key.pem` (`wget https://certificates.trustedservices.intel.com/Intel_SGX_Attestation_RootCA.pem -O ${PDO_SGX_KEY_ROOT}/sgx_ias_key.pem`) +#### (optional) Set the path to an existing enclave code signing key + +At build time, an enclave code signing key is required to sign the contract enclave. +If one such key is available, it can be used by organizing it as follows: +* save the enclave code signing key in `${PDO_SGX_KEY_ROOT}/enclave_code_sign.pem` + #### Install the SGX Kernel Driver (Hardware Support) SGX can run in either simulation or hardware mode. No kernel driver is @@ -178,7 +184,9 @@ To validate that your SGX HW installation & and corresponding PDO configuration is working properly, the easiest way is to install docker as discussed below and then run ```bash - make SGX_MODE=HW -C docker test +. build/common-config.sh + +make -C docker sgx_test ``` This will build PDO and automatically execute the tests described in the Section [Validate the Installation](usage.md#validating) in HW mode. diff --git a/docs/usage.md b/docs/usage.md index a97353b3..855dee4e 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -41,10 +41,6 @@ configuration file: | `sservice` | state storage service associated with an enclave service | [`sservice.toml`](../build/opt/pdo/templates/sservice.toml) | | `pdo-shell` | the PDO client shell for creating contracts and invoking methods | [`pcontract.toml`](../build/opt/pdo/templates/pcontract.toml) | -For simplicity in installation, the file `enclave.toml` in the -`${PDO_HOME}/etc` directory contains the configuration for the accessing -the Intel Attestation Service. - In addition, most provided `pdo-shell` scripts use the service configuration information found in [`${PDO_HOME}/etc/site.psh`](../build/opt/pdo/templates/site.psh). @@ -62,6 +58,18 @@ Using PDO requires a running instance of a ledger. Documentation for building, installing and running [Microsoft CCF](../ledgers/ccf/README.md) is available. +PDO provides a script to start and configure the ledger (see `ledgers/ccf/scripts/start_ccf_network.sh`). +If `"${SGX_MODE}" == "SIM"`, the script configures the PDO TP to skip attestation checks. +If `"${SGX_MODE}" == "HW"`, the script configures the PDO TP to enforce attestation checks. +In this case, a policy must be registered (see [README](../ledgers/ccf/README.md)). + +# Register the attestation policy with the ledger + +The registration of the attestation policy is required if `"${SGX_MODE}" == "HW"`, and irrelevant otherwise. +PDO provides the `private-data-objects/eservice/bin/register-with-ledger.sh` script to automate this procedure. +The script is meant to be use by a member of the ledger consortium, at the eservice side, +once the contract enclave's MRENCLAVE is available (i.e., once enclave build is completed). + # Validate the Installation The easiest way to validate that your installation is correct is to run diff --git a/eservice/bin/register-with-ledger.sh b/eservice/bin/register-with-ledger.sh index fdd85526..d2432057 100755 --- a/eservice/bin/register-with-ledger.sh +++ b/eservice/bin/register-with-ledger.sh @@ -21,10 +21,11 @@ ETCDIR=${DSTDIR}/opt/pdo/etc/ ESERVICE_IDENTITY=eservice1 ESERVICE_TOML=${ESERVICE_IDENTITY}.toml -ENCLAVE_TOML=enclave.toml -PDO_IAS_SIGNING_CERT_PATH=${PDO_SGX_KEY_ROOT}/ias_signing.cert -PDO_IAS_KEY_PEM=${PDO_SGX_KEY_ROOT}/sgx_ias_key.pem +SGX_KEY_ROOT=${PDO_SGX_KEY_ROOT:-${SRCDIR}/build/keys/sgx_mode_${SGX_MODE,,}} + +IAS_SIGNING_CERT_PATH=${SGX_KEY_ROOT}/ias_signing.cert +IAS_KEY_PEM=${SGX_KEY_ROOT}/sgx_ias_key.pem eservice_enclave_info_file=$(mktemp /tmp/pdo-test.XXXXXXXXX) @@ -39,41 +40,35 @@ function cleanup { trap cleanup EXIT -#Set SPID to parameter if passed -SPID=$PDO_SPID -if (( "$#" == 1 )) ; then - SPID=$1 -fi - function DeriveIasPublicKey { - try test -e ${PDO_IAS_SIGNING_CERT_PATH} - try openssl x509 -pubkey -noout -in ${PDO_IAS_SIGNING_CERT_PATH} > ${PDO_IAS_KEY_PEM} + yell Derive IAS public to be registered on the ledger + try test -e ${IAS_SIGNING_CERT_PATH} + try openssl x509 -pubkey -noout -in ${IAS_SIGNING_CERT_PATH} > ${IAS_KEY_PEM} + yell IAS public derived in ${IAS_KEY_PEM} } -# Store MR_ENCLAVE & MR_BASENAME to eservice_enclave_info_file -# Note: an alternative way without any enclave invocations would be the following. -# -# if [ -z "${SPID}" -o ${#SPID} != 32 ]; then -# echo "No valid (length 32) SPID pass as argument or PDO_SPID environment variable" -# exit 1 -# fi -# perl -0777 -ne 'if (/metadata->enclave_css.body.enclave_hash.m:([a-fx0-9 \n]+)/) { $eh = $1; $eh=~s/0x| |\n//g; $eh=~tr/a-z/A-Z/; $bn="'${SPID}'"; $bn .= "0" x (64 - length $bn); print "MRENCLAVE:${eh}\nBASENAME:${bn}\n"; }' ./build/lib/libpdo-enclave.signed.so.meta > $eservice_enclave_info_file -# # Note: group id is always zero, hence the zero-padding ... -# -# This would also allow removing in eservice/pservice the code related to CreateErsatzEnclaveReport and GetEnclave Characteristics -# However, getting basename via enclave invocation & quote is somewhat cleaner than below .. function Store { - : "${SPID:?Need PDO_SPID environment variable set or passed in for valid MR_BASENAME}" try test -e ${ETCDIR}/${ESERVICE_TOML} - try test -e ${ETCDIR}/${ENCLAVE_TOML} yell Download IAS certificates and Compute the enclave information - try eservice-enclave-info \ - --spid ${SPID} \ - --save ${eservice_enclave_info_file} \ - --loglevel warn \ - --identity ${ESERVICE_IDENTITY} \ - --config ${ESERVICE_TOML} ${ENCLAVE_TOML} \ - --config-dir ${ETCDIR} + if [ "${PDO_FORCE_IAS_PROXY}" == "true" ]; then + yell PDO_FORCE_IAS_PROXY is true + NO_PROXY='' no_proxy='' try eservice-enclave-info \ + --save ${eservice_enclave_info_file} \ + --loglevel info \ + --logfile __screen__ \ + --identity ${ESERVICE_IDENTITY} \ + --config ${ESERVICE_TOML} \ + --config-dir ${ETCDIR} + else + try eservice-enclave-info \ + --save ${eservice_enclave_info_file} \ + --loglevel info \ + --logfile __screen__ \ + --identity ${ESERVICE_IDENTITY} \ + --config ${ESERVICE_TOML} \ + --config-dir ${ETCDIR} + fi + yell Enclave info are ready } # Registers MR_ENCLAVE & BASENAMES with Ledger @@ -85,13 +80,14 @@ function Register { VAR_BASENAME=$(grep -o 'BASENAME:.*' ${eservice_enclave_info_file} | cut -f2- -d:) : "${PDO_LEDGER_URL:?Registration failed! PDO_LEDGER_URL environment variable not set}" - : "PDO_IAS_KEY_PEM" "${PDO_IAS_KEY_PEM:?Registration failed! PDO_IAS_KEY_PEM environment variable not set}" + : "IAS_KEY_PEM" "${IAS_KEY_PEM:?Registration failed! PDO_IAS_KEY_PEM environment variable not set}" if [ ${PDO_LEDGER_TYPE} == "ccf" ]; then + yell Register enclave with CCF ledger: mrenclave=${VAR_MRENCLAVE} basename=${VAR_BASENAME} source ${PDO_INSTALL_ROOT}/bin/activate try ${PDO_INSTALL_ROOT}/bin/ccf_set_expected_sgx_measurements \ --logfile __screen__ --loglevel INFO --mrenclave ${VAR_MRENCLAVE} \ - --basename ${VAR_BASENAME} --ias-public-key "$(cat $PDO_IAS_KEY_PEM)" + --basename ${VAR_BASENAME} --ias-public-key "$(cat $IAS_KEY_PEM)" else die unsupported ledger ${PDO_LEDGER_TYPE} fi diff --git a/eservice/docs/test-scripts.md b/eservice/docs/test-scripts.md index a2c38bd1..fff27e28 100644 --- a/eservice/docs/test-scripts.md +++ b/eservice/docs/test-scripts.md @@ -66,10 +66,8 @@ The following configuration variables can be specified: will send all logging information to the console * ``EnclaveModule`` - * ``spid`` -- a 32-digit hex string tied to the enclave implementation * ``ias_url`` -- URL of the Intel Attestation Service (IAS) server (ignored) - * ``https_proxy`` -- proxy used to contact IAS server (ignored) - * ``spid_api_key`` -- the api key corresponding to spid (ignored) + * ``sgx_key_root`` -- folder containing the sgx keys (ignored) * ``contract`` -- the base name of the contract to use, this is expected to reference a file found in ``SourceSearchPath`` diff --git a/eservice/lib/libpdo_enclave/CMakeLists.txt b/eservice/lib/libpdo_enclave/CMakeLists.txt index 396959e3..9bea66ca 100644 --- a/eservice/lib/libpdo_enclave/CMakeLists.txt +++ b/eservice/lib/libpdo_enclave/CMakeLists.txt @@ -43,5 +43,5 @@ TARGET_LINK_LIBRARIES(${PROJECT_NAME} ${COMMON_TRUSTED_LIBS}) TARGET_LINK_LIBRARIES(${PROJECT_NAME} -Wl,--end-group) SGX_PREPARE_TRUSTED_LINK(${PROJECT_NAME}) -SGX_SIGN_ENCLAVE(${PROJECT_NAME} ${PDO_ENCLAVE_CODE_SIGN_PEM} ${PROJECT_CONFIG}) +SGX_SIGN_ENCLAVE(${PROJECT_NAME} ${PDO_SGX_KEY_ROOT}/enclave_code_sign.pem ${PROJECT_CONFIG}) SGX_DEPLOY_FILES(${PROJECT_NAME} eservice) diff --git a/eservice/pdo/eservice/pdo_enclave.py b/eservice/pdo/eservice/pdo_enclave.py index 468af67e..9228ca7a 100644 --- a/eservice/pdo/eservice/pdo_enclave.py +++ b/eservice/pdo/eservice/pdo_enclave.py @@ -16,6 +16,7 @@ import json import time import toml +from pathlib import Path from ssl import SSLError from requests.exceptions import Timeout @@ -137,7 +138,7 @@ def initialize_with_configuration(config) : enclave.SetLogger(logger) # Ensure that the required keys are in the configuration - valid_keys = set(['spid', 'ias_url', 'spid_api_key']) + valid_keys = set(['ias_url', 'sgx_key_root']) found_keys = set(config.keys()) missing_keys = valid_keys.difference(found_keys) @@ -148,20 +149,25 @@ def initialize_with_configuration(config) : '{}'.format( ', '.join(sorted(list(missing_keys))))) - num_of_enclaves = int(config.get('num_of_enclaves', 1)) + NumberOfEnclaves = int(config.get('NumberOfEnclaves', 1)) + + try: + spid = Path(os.path.join(config['sgx_key_root'], "sgx_spid.txt")).read_text().strip() + spid_api_key = Path(os.path.join(config['sgx_key_root'], "sgx_spid_api_key.txt")).read_text().strip() + except Exception as e : + raise Exception("Unable to access SGX keys: {}".format(str(e))) if not _ias: _ias = \ ias_client.IasClient( IasServer = config['ias_url'], - SpidApiKey = config['spid_api_key'], - Spid = config['spid'], - HttpsProxy = config.get('https_proxy', "")) + SpidApiKey = spid_api_key, + Spid = spid) if not _pdo: signed_enclave = __find_enclave_library(config) logger.debug("Attempting to load enclave at: %s", signed_enclave) - _pdo = enclave.pdo_enclave_info(signed_enclave, config['spid'], num_of_enclaves) + _pdo = enclave.pdo_enclave_info(signed_enclave, spid, NumberOfEnclaves) logger.info("Basename: %s", get_enclave_basename()) logger.info("MRENCLAVE: %s", get_enclave_measurement()) @@ -232,8 +238,8 @@ def get_enclave_service_info(spid, config=None) : signed_enclave = __find_enclave_library(None) logger.debug("Attempting to load enclave at: %s", signed_enclave) - num_of_enclaves = 1 - pdo = enclave.pdo_enclave_info(signed_enclave, spid, num_of_enclaves) + NumberOfEnclaves = 1 + pdo = enclave.pdo_enclave_info(signed_enclave, spid, NumberOfEnclaves) if pdo is None : raise Exception('unable to load the enclave') diff --git a/eservice/pdo/eservice/scripts/EServiceCLI.py b/eservice/pdo/eservice/scripts/EServiceCLI.py index e26d72e4..d52d7cbc 100644 --- a/eservice/pdo/eservice/scripts/EServiceCLI.py +++ b/eservice/pdo/eservice/scripts/EServiceCLI.py @@ -246,7 +246,7 @@ def Main() : config_map = pconfig.build_configuration_map() # parse out the configuration file first - conffiles = [ 'eservice.toml', 'enclave.toml' ] + conffiles = [ 'eservice.toml' ] confpaths = [ ".", "./etc", config_map['etc'] ] parser = argparse.ArgumentParser() @@ -271,6 +271,8 @@ def Main() : parser.add_argument('--enclave-save', help='Name of the directory where enclave data will be save', type=str) parser.add_argument('--enclave-path', help='Directories to search for the enclave data file', type=str, nargs = '+') + parser.add_argument('--sgx-key-root', help='Path to SGX key root folder', type = str) + options = parser.parse_args() # first process the options necessary to load the default configuration @@ -355,6 +357,18 @@ def Main() : port = config['StorageService'].get('HttpPort',7201) config['StorageService']['URL'] = "http://{0}:{1}".format(host, port) + # set up the default enclave module configuration (if necessary) + if config.get('EnclaveModule') is None : + config['EnclaveModule'] = { + 'NumberOfEnclaves' : 7, + 'ias_url' : 'https://api.trustedservices.intel.com/sgx/dev', + 'sgx_key_root' : os.environ.get('PDO_SGX_KEY_ROOT', '.') + } + + # override the enclave module configuration (if options are specified) + if options.sgx_key_root : + config['EnclaveModule']['sgx_key_root'] = options.sgx_key_root + # GO! LocalMain(config) diff --git a/eservice/pdo/eservice/scripts/EServiceEnclaveInfoCLI.py b/eservice/pdo/eservice/scripts/EServiceEnclaveInfoCLI.py index 7a6b977f..ae0f9680 100644 --- a/eservice/pdo/eservice/scripts/EServiceEnclaveInfoCLI.py +++ b/eservice/pdo/eservice/scripts/EServiceEnclaveInfoCLI.py @@ -18,6 +18,7 @@ import sys import argparse import json +from pathlib import Path import pdo.common.config as pconfig import pdo.common.logger as plogger @@ -30,15 +31,18 @@ import time + + # ----------------------------------------------------------------- # ----------------------------------------------------------------- -def GetBasename(spid, save_path, config) : +def GetBasename(save_path, config) : attempts = 0 while True : try : logger.debug('initialize the enclave') - enclave_config = {} - info = pdo_enclave_helper.get_enclave_service_info(spid, config=enclave_config) + enclave_config = config.get('EnclaveModule') + spid = Path(os.path.join(enclave_config['sgx_key_root'], "sgx_spid.txt")).read_text().strip() + info = pdo_enclave_helper.get_enclave_service_info(spid) logger.info('save MR_ENCLAVE and MR_BASENAME to %s', save_path) with open(save_path, "w") as file : @@ -71,7 +75,7 @@ def GetIasCertificates(config) : # (signup info are not relevant here) # the creation of signup info includes getting a verification report from IAS try : - enclave_config = config['EnclaveModule'] + enclave_config = config.get('EnclaveModule') pdo_enclave.initialize_with_configuration(enclave_config) except Exception as e : logger.error("unable to initialize a new enclave; %s", str(e)) @@ -86,14 +90,10 @@ def GetIasCertificates(config) : ias_certificates = pd_dict['certificates'] # dump the IAS certificates in the respective files - IasKeysPath = os.environ.get("PDO_SGX_KEY_ROOT") - - IasRootCACertificate_FilePath = os.path.join(IasKeysPath, "ias_root_ca.cert") - with open(IasRootCACertificate_FilePath, "w+") as file : + with open(os.path.join(enclave_config['sgx_key_root'], "ias_root_ca.cert"), "w+") as file : file.write("{0}".format(ias_certificates[1])) - IasAttestationVerificationCertificate_FilePathname = os.path.join(IasKeysPath, "ias_signing.cert") - with open(IasAttestationVerificationCertificate_FilePathname, "w+") as file : + with open(os.path.join(enclave_config['sgx_key_root'], "ias_signing.cert"), "w+") as file : file.write("{0}".format(ias_certificates[0])) except Exception as e : @@ -104,8 +104,8 @@ def GetIasCertificates(config) : # do a clean shutdown of enclave pdo_enclave.shutdown() -def LocalMain(config, spid, save_path) : - GetBasename(spid, save_path, config) +def LocalMain(config, save_path) : + GetBasename(save_path, config) GetIasCertificates(config) sys.exit(0) @@ -119,7 +119,7 @@ def Main() : config_map = pconfig.build_configuration_map() # parse out the configuration file first - conffiles = [ 'eservice.toml', 'enclave.toml' ] + conffiles = [ 'eservice.toml' ] confpaths = [ ".", "./etc", config_map['etc'] ] parser = argparse.ArgumentParser() @@ -127,7 +127,7 @@ def Main() : parser.add_argument('--config-dir', help='directory to search for configuration files', nargs = '+') parser.add_argument('--identity', help='Identity to use for the process', required = True, type = str) - parser.add_argument('--spid', help='SPID to generate enclave basename', type=str) + parser.add_argument('--sgx-key-root', help='Path to SGX key root folder', type = str) parser.add_argument('--save', help='Where to save MR_ENCLAVE and BASENAME', type=str) parser.add_argument('--logfile', help='Name of the log file, __screen__ for standard output', type=str) @@ -145,9 +145,6 @@ def Main() : if options.save : save_path = options.save - if options.spid : - spid = options.spid - config_map['identity'] = options.identity try : config = pconfig.parse_configuration_files(conffiles, confpaths, config_map) @@ -169,8 +166,20 @@ def Main() : sys.stdout = plogger.stream_to_logger(logging.getLogger('STDOUT'), logging.DEBUG) sys.stderr = plogger.stream_to_logger(logging.getLogger('STDERR'), logging.WARN) + # set up the default enclave module configuration (if necessary) + if config.get('EnclaveModule') is None : + config['EnclaveModule'] = { + 'NumberOfEnclaves' : 7, + 'ias_url' : 'https://api.trustedservices.intel.com/sgx/dev', + 'sgx_key_root' : os.environ.get('PDO_SGX_KEY_ROOT', '.') + } + + # override the enclave module configuration (if options are specified) + if options.sgx_key_root : + config['EnclaveModule']['sgx_key_root'] = options.sgx_key_root + # GO! - LocalMain(config, spid, save_path) + LocalMain(config, save_path) ## ----------------------------------------------------------------- ## Entry points diff --git a/eservice/pdo/eservice/utility/ias_client.py b/eservice/pdo/eservice/utility/ias_client.py index f67e205d..660c2f58 100644 --- a/eservice/pdo/eservice/utility/ias_client.py +++ b/eservice/pdo/eservice/utility/ias_client.py @@ -32,10 +32,6 @@ class IasClient(object): def __init__(self, **kwargs): logger.info("IAS settings:") - self._proxies = {} - if "HttpsProxy" in kwargs: - self._proxies["https"] = kwargs["HttpsProxy"] - logger.info("Proxy: " + self._proxies["https"]) if "Spid" in kwargs: self._spid = kwargs["Spid"] logger.info("SPID: " + self._spid) @@ -65,7 +61,7 @@ def get_signature_revocation_lists(self, url = self._ias_url + path + gid[0:8] logger.debug("Fetching SigRL from: %s", url) - result = requests.get(url, proxies=self._proxies, + result = requests.get(url, headers={'Ocp-Apim-Subscription-Key': self._spid_api_key}) if result.status_code != requests.codes.ok: logger.debug("get_signature_revocation_lists HTTP Error code : %d", @@ -90,7 +86,6 @@ def post_verify_attestation(self, quote, manifest=None, nonce=None): logger.debug("Posting attestation verification request to: %s\n", url) result = requests.post(url, json=json, - proxies=self._proxies, headers={'Ocp-Apim-Subscription-Key': self._spid_api_key}, timeout=self._timeout) logger.debug("result headers: %s\n", result.headers) diff --git a/eservice/tests/test-secrets.py b/eservice/tests/test-secrets.py index fed2b25e..ab7d45eb 100644 --- a/eservice/tests/test-secrets.py +++ b/eservice/tests/test-secrets.py @@ -60,7 +60,7 @@ def ErrorShutdown() : # ----------------------------------------------------------------- # ----------------------------------------------------------------- config_map = pconfig.build_configuration_map() -conffiles = [ 'pcontract.toml', 'enclave.toml', 'eservice1.toml' ] +conffiles = [ 'pcontract.toml', 'eservice1.toml' ] confpaths = [ ".", "./etc", config_map['etc'] ] import argparse @@ -69,6 +69,7 @@ def ErrorShutdown() : parser.add_argument('--config-dir', help='configuration file', nargs = '+') parser.add_argument('--loglevel', help='Set the logging level', default='INFO') parser.add_argument('--logfile', help='Name of the log file', default='__screen__') +parser.add_argument('--sgx-key-root', help='Path to SGX key root folder', type = str) options = parser.parse_args() config_map['identity'] = 'test-secrets' @@ -100,6 +101,18 @@ def ErrorShutdown() : logger.error('failed to initialize the block store; %s', str(e)) ErrorShutdown() +# set up the default enclave module configuration (if necessary) +if config.get('EnclaveModule') is None : + config['EnclaveModule'] = { + 'NumberOfEnclaves' : 7, + 'ias_url' : 'https://api.trustedservices.intel.com/sgx/dev', + 'sgx_key_root' : os.environ.get('PDO_SGX_KEY_ROOT', '.') + } + +# override the enclave module configuration (if options are specified) +if options.sgx_key_root : + config['EnclaveModule']['sgx_key_root'] = options.sgx_key_root + # ----------------------------------------------------------------- # ----------------------------------------------------------------- plogger.setup_loggers({'LogLevel' : options.loglevel.upper(), 'LogFile' : options.logfile}) diff --git a/pservice/lib/libpdo_enclave/CMakeLists.txt b/pservice/lib/libpdo_enclave/CMakeLists.txt index 2465eae0..75acfdd8 100644 --- a/pservice/lib/libpdo_enclave/CMakeLists.txt +++ b/pservice/lib/libpdo_enclave/CMakeLists.txt @@ -49,5 +49,5 @@ TARGET_LINK_LIBRARIES(${PROJECT_NAME} ${COMMON_TRUSTED_LIBS}) TARGET_LINK_LIBRARIES(${PROJECT_NAME} -Wl,--end-group) SGX_PREPARE_TRUSTED_LINK(${PROJECT_NAME}) -SGX_SIGN_ENCLAVE(${PROJECT_NAME} ${PDO_ENCLAVE_CODE_SIGN_PEM} ${PROJECT_CONFIG}) +SGX_SIGN_ENCLAVE(${PROJECT_NAME} ${PDO_SGX_KEY_ROOT}/enclave_code_sign.pem ${PROJECT_CONFIG}) SGX_DEPLOY_FILES(${PROJECT_NAME} pservice) diff --git a/pservice/pdo/pservice/pdo_enclave.py b/pservice/pdo/pservice/pdo_enclave.py index c0b4b57b..bdb8e945 100644 --- a/pservice/pdo/pservice/pdo_enclave.py +++ b/pservice/pdo/pservice/pdo_enclave.py @@ -16,6 +16,7 @@ import json import time import toml +from pathlib import Path from ssl import SSLError from requests.exceptions import Timeout @@ -132,7 +133,7 @@ def initialize_with_configuration(config) : enclave._SetLogger(logger) # Ensure that the required keys are in the configuration - valid_keys = set(['spid', 'ias_url', 'spid_api_key']) + valid_keys = set(['ias_url', 'sgx_key_root']) found_keys = set(config.keys()) missing_keys = valid_keys.difference(found_keys) @@ -143,18 +144,23 @@ def initialize_with_configuration(config) : '{}'.format( ', '.join(sorted(list(missing_keys))))) + try: + spid = Path(os.path.join(config['sgx_key_root'], "sgx_spid.txt")).read_text().strip() + spid_api_key = Path(os.path.join(config['sgx_key_root'], "sgx_spid_api_key.txt")).read_text().strip() + except Exception as e : + raise Exception("Unable to access SGX keys: {}".format(str(e))) + if not _ias: _ias = \ ias_client.IasClient( IasServer = config['ias_url'], - SpidApiKey = config['spid_api_key'], - Spid = config['spid'], - HttpsProxy = config.get('https_proxy', "")) + SpidApiKey = spid_api_key, + Spid = spid) if not _pdo: signed_enclave = __find_enclave_library(config) logger.debug("Attempting to load enclave at: %s", signed_enclave) - _pdo = enclave.pdo_enclave_info(signed_enclave, config['spid']) + _pdo = enclave.pdo_enclave_info(signed_enclave, spid) logger.info("Basename: %s", get_enclave_basename()) logger.info("MRENCLAVE: %s", get_enclave_measurement()) diff --git a/pservice/pdo/pservice/scripts/PServiceCLI.py b/pservice/pdo/pservice/scripts/PServiceCLI.py index 0a1340ba..74f8ede1 100644 --- a/pservice/pdo/pservice/scripts/PServiceCLI.py +++ b/pservice/pdo/pservice/scripts/PServiceCLI.py @@ -446,6 +446,8 @@ def Main() : parser.add_argument('--provisioning-path', help='Directories to search for the enclave data file', type=str, nargs='+') parser.add_argument('--provisioning-data', help='Name of the file containing enclave sealed storage', type=str) + parser.add_argument('--sgx-key-root', help='Path to SGX key root folder', type = str) + options = parser.parse_args() # first process the options necessary to load the default configuration @@ -521,6 +523,18 @@ def Main() : if options.provisioning_path : config['ProvisioningData']['SearchPath'] = options.provisioning_path + # set up the default enclave module configuration (if necessary) + if config.get('EnclaveModule') is None : + config['EnclaveModule'] = { + 'NumberOfEnclaves' : 7, + 'ias_url' : 'https://api.trustedservices.intel.com/sgx/dev', + 'sgx_key_root' : os.environ.get('PDO_SGX_KEY_ROOT', '.') + } + + # override the enclave module configuration (if options are specified) + if options.sgx_key_root : + config['EnclaveModule']['sgx_key_root'] = options.sgx_key_root + # GO! LocalMain(config) diff --git a/pservice/pdo/pservice/utility/ias_client.py b/pservice/pdo/pservice/utility/ias_client.py index 38168934..0e31a84f 100644 --- a/pservice/pdo/pservice/utility/ias_client.py +++ b/pservice/pdo/pservice/utility/ias_client.py @@ -30,10 +30,6 @@ class IasClient(object): def __init__(self, **kwargs): logger.info("IAS settings:") - self._proxies = {} - if "HttpsProxy" in kwargs: - self._proxies["https"] = kwargs["HttpsProxy"] - logger.info("Proxy: " + self._proxies["https"]) if "Spid" in kwargs: self._spid = kwargs["Spid"] logger.info("SPID: " + self._spid) @@ -63,7 +59,7 @@ def get_signature_revocation_lists(self, url = self._ias_url + path + gid[0:8] logger.debug("Fetching SigRL from: %s", url) - result = requests.get(url, proxies=self._proxies, + result = requests.get(url, headers={'Ocp-Apim-Subscription-Key': self._spid_api_key}) if result.status_code != requests.codes.ok: logger.debug("get_signature_revocation_lists HTTP Error code : %d", @@ -88,7 +84,6 @@ def post_verify_attestation(self, quote, manifest=None, nonce=None): logger.debug("Posting attestation verification request to: %s\n", url) result = requests.post(url, json=json, - proxies=self._proxies, headers={'Ocp-Apim-Subscription-Key': self._spid_api_key}, timeout=self._timeout) logger.debug("result headers: %s\n", result.headers) diff --git a/python/pdo/common/config.py b/python/pdo/common/config.py index b60c8088..fb2a24bd 100644 --- a/python/pdo/common/config.py +++ b/python/pdo/common/config.py @@ -187,8 +187,6 @@ def build_configuration_map(**kwargs) : # these are effectively required by common-config, but clients dont # need them and we should be able to set reasonable defaults - SPID = os.environ.get("PDO_SPID", "DEADBEEF00000000DEADBEEF00000000") - SPID_API_KEY = os.environ.get("PDO_SPID_API_KEY", "deadbeef00000000deadbeef00000000") SGX_MODE = os.environ.get("SGX_MODE", "SIM") ContractHost = kwargs.get('host') or os.environ.get("PDO_HOSTNAME", os.environ.get("HOSTNAME", "localhost")) @@ -208,8 +206,9 @@ def build_configuration_map(**kwargs) : ContractData = os.path.join(ContractHome, "data") Interpreter = kwargs.get('interpreter') or os.environ.get("PDO_INTERPRETER", "wawaka") LedgerKeyRoot = kwargs.get('ledger_key_root') or os.environ.get("PDO_LEDGER_KEY_ROOT", os.path.join(ContractKeys, "ledger")) - HttpsProxy = os.environ.get("https_proxy", "") EserviceKeyFormat = 'pem' + SgxKeyRoot = os.environ.get('PDO_SGX_KEY_ROOT', ContractKeys) + config_map = { 'data' : ContractData, @@ -227,10 +226,8 @@ def build_configuration_map(**kwargs) : 'ledger_host_name' : LedgerHostName, 'ledger_type': LedgerType, 'ledger_key_root' : LedgerKeyRoot, - 'proxy' : HttpsProxy, 'sgx_mode' : SGX_MODE, - 'spid' : SPID, - 'spid_api_key' : SPID_API_KEY + 'sgx_key_root': SgxKeyRoot } return config_map diff --git a/python/pdo/scripts/ConfigureCLI.py b/python/pdo/scripts/ConfigureCLI.py index ba00c737..6fc4bfd1 100644 --- a/python/pdo/scripts/ConfigureCLI.py +++ b/python/pdo/scripts/ConfigureCLI.py @@ -134,9 +134,6 @@ def configure_services() : for n in range(1, options.count[2]+1) : expand_service(options, 'pservice', 'ProvisioningService', n) - # Generate enclave configuration file - expand_helper(options, 'enclave.toml') - filename = os.path.join(options.output_directory, 'etc', 'site.toml') with open(filename, 'w') as outfile: toml.dump(site_information, outfile) diff --git a/python/pdo/test/contract.py b/python/pdo/test/contract.py index ac57ca69..e81aaf77 100644 --- a/python/pdo/test/contract.py +++ b/python/pdo/test/contract.py @@ -541,7 +541,7 @@ def Main() : config_map = pconfig.build_configuration_map() # parse out the configuration file first - conffiles = [ 'pcontract.toml', 'enclave.toml', 'eservice1.toml' ] + conffiles = [ 'pcontract.toml', 'eservice1.toml' ] confpaths = [ ".", "./etc", config_map['etc'] ] parser = argparse.ArgumentParser() diff --git a/python/pdo/test/request.py b/python/pdo/test/request.py index cab13511..268872ec 100644 --- a/python/pdo/test/request.py +++ b/python/pdo/test/request.py @@ -469,7 +469,7 @@ def Main() : config_map = pconfig.build_configuration_map() # parse out the configuration file first - conffiles = [ 'pcontract.toml', 'enclave.toml', 'eservice1.toml' ] + conffiles = [ 'pcontract.toml', 'eservice1.toml' ] confpaths = [ ".", "./etc", config_map['etc'] ] parser = argparse.ArgumentParser()