From 43ff71306260dc6aacc16a15740216abdeff1cb6 Mon Sep 17 00:00:00 2001 From: Bruno Vavala Date: Tue, 17 Dec 2024 01:15:42 +0000 Subject: [PATCH] first commit with attestation API from PDO's pull request #501 Signed-off-by: Bruno Vavala --- .gitmodules | 6 + CMakeLists.txt | 219 ++++++++++++ CMakeVariables.txt | 7 + README.md | 104 ++++++ attestation/attestation.cpp | 171 ++++++++++ attestation/dcap.cpp | 136 ++++++++ attestation/dcap.h | 12 + attestation/epid.cpp | 167 ++++++++++ attestation/epid.h | 22 ++ common/.gitignore | 2 + common/CMakeLists.txt | 22 ++ common/README.md | 3 + common/_nologging/CMakeLists.txt | 9 + common/_nologging/README.md | 4 + common/_nologging/ocalls/logging.edl | 7 + common/_nologging/trusted/logging.h | 17 + common/_nologging/untrusted/logging.h | 18 + common/_yeslogging/CMakeLists.txt | 47 +++ common/_yeslogging/CMakeVariables.txt | 29 ++ common/_yeslogging/include/log-defines.h | 85 +++++ common/_yeslogging/ocalls/logging.c | 17 + common/_yeslogging/ocalls/logging.edl | 12 + common/_yeslogging/test/CMakeLists.txt | 29 ++ common/_yeslogging/test/untrusted/test.cpp | 38 +++ common/_yeslogging/trusted/logging.c | 40 +++ common/_yeslogging/trusted/logging.h | 25 ++ common/_yeslogging/untrusted/logging.c | 51 +++ common/_yeslogging/untrusted/logging.h | 49 +++ common/base64/base64.cpp | 122 +++++++ common/base64/base64.h | 14 + common/crypto/sha256.cpp | 22 ++ common/crypto/sha256.h | 8 + .../crypto/verify_dcap_direct/CMakeLists.txt | 41 +++ .../get_dcap_certificate.cpp | 14 + .../verify_dcap_direct/get_dcap_certificate.h | 7 + common/crypto/verify_ias_report/.gitignore | 1 + .../crypto/verify_ias_report/CMakeLists.txt | 39 +++ .../verify_ias_report/verify-report.cpp | 311 ++++++++++++++++++ .../crypto/verify_ias_report/verify-report.h | 70 ++++ common/crypto/verify_ita_token/CMakeLists.txt | 46 +++ .../crypto/verify_ita_token/verify-token.cpp | 112 +++++++ common/crypto/verify_ita_token/verify-token.h | 16 + common/error.h | 46 +++ common/jwt-cpp | 1 + common/jwt-cpp.patch | 224 +++++++++++++ common/logging | 1 + common/nlohmann/json | 1 + common/scripts/common_utils.sh | 85 +++++ common/sgx-support/clocale | 59 ++++ common/types/hex_string.cpp | 156 +++++++++ common/types/hex_string.h | 50 +++ common/types/types.cpp | 85 +++++ common/types/types.h | 76 +++++ conversion/attestation_to_evidence.sh | 98 ++++++ conversion/dcap-direct/a2e.sh | 25 ++ .../b64attestation2b64collateral.cpp | 101 ++++++ conversion/dcap/a2e.sh | 53 +++ conversion/define_to_variable.sh | 18 + conversion/enclave_to_mrenclave.sh | 29 ++ conversion/epid/a2e.sh | 59 ++++ conversion/simulated/a2e.sh | 16 + conversion/tag_to_variable.sh | 27 ++ docker/Dockerfile | 146 ++++++++ docker/Makefile | 9 + docker/container/README.md | 3 + docker/container/setup.sh | 71 ++++ evidence/verify-dcap-direct-evidence.cpp | 269 +++++++++++++++ evidence/verify-dcap-direct-evidence.h | 7 + evidence/verify-dcap-evidence.cpp | 104 ++++++ evidence/verify-dcap-evidence.h | 7 + evidence/verify-evidence.cpp | 88 +++++ evidence/verify-ias-evidence.cpp | 240 ++++++++++++++ evidence/verify-ias-evidence.h | 7 + include/attestation.h | 17 + include/attestation_tags.h | 34 ++ include/verify-evidence.h | 24 ++ ocalls/attestation-ocalls.c | 99 ++++++ ocalls/attestation-ocalls.edl | 25 ++ test/CMakeLists.txt | 150 +++++++++ test/attested_evidence_test.sh | 247 ++++++++++++++ test/common/test-defines.h | 17 + test/common/test-utils.cpp | 55 ++++ test/common/test-utils.h | 12 + test/get_attestation_app/app/main.cpp | 64 ++++ .../enclave/CMakeLists.txt | 74 +++++ .../enclave/CMakeVariables.txt | 100 ++++++ test/get_attestation_app/enclave/test.cpp | 27 ++ test/get_attestation_app/enclave/test.h | 15 + .../enclave/test_enclave.config.xml | 18 + .../enclave/test_enclave.edl | 21 ++ .../enclave/test_enclave.lds | 15 + .../enclave/CMakeLists.txt | 78 +++++ .../enclave/CMakeVariables.txt | 99 ++++++ test/verify_evidence_app/enclave/test.cpp | 27 ++ .../enclave/test_verify_enclave.config.xml | 18 + .../enclave/test_verify_enclave.edl | 20 ++ .../enclave/test_verify_enclave.lds | 15 + test/verify_evidence_app/main.cpp | 19 ++ test/verify_evidence_app/test-enclave.cpp | 112 +++++++ test/verify_evidence_app/test.cpp | 80 +++++ test/verify_evidence_app/test.h | 9 + usage.md | 52 +++ 102 files changed, 5875 insertions(+) create mode 100644 .gitmodules create mode 100644 CMakeLists.txt create mode 100644 CMakeVariables.txt create mode 100644 README.md create mode 100644 attestation/attestation.cpp create mode 100644 attestation/dcap.cpp create mode 100644 attestation/dcap.h create mode 100644 attestation/epid.cpp create mode 100644 attestation/epid.h create mode 100644 common/.gitignore create mode 100644 common/CMakeLists.txt create mode 100644 common/README.md create mode 100644 common/_nologging/CMakeLists.txt create mode 100644 common/_nologging/README.md create mode 100644 common/_nologging/ocalls/logging.edl create mode 100644 common/_nologging/trusted/logging.h create mode 100644 common/_nologging/untrusted/logging.h create mode 100644 common/_yeslogging/CMakeLists.txt create mode 100644 common/_yeslogging/CMakeVariables.txt create mode 100644 common/_yeslogging/include/log-defines.h create mode 100644 common/_yeslogging/ocalls/logging.c create mode 100644 common/_yeslogging/ocalls/logging.edl create mode 100644 common/_yeslogging/test/CMakeLists.txt create mode 100644 common/_yeslogging/test/untrusted/test.cpp create mode 100644 common/_yeslogging/trusted/logging.c create mode 100644 common/_yeslogging/trusted/logging.h create mode 100644 common/_yeslogging/untrusted/logging.c create mode 100644 common/_yeslogging/untrusted/logging.h create mode 100644 common/base64/base64.cpp create mode 100644 common/base64/base64.h create mode 100644 common/crypto/sha256.cpp create mode 100644 common/crypto/sha256.h create mode 100644 common/crypto/verify_dcap_direct/CMakeLists.txt create mode 100644 common/crypto/verify_dcap_direct/get_dcap_certificate.cpp create mode 100644 common/crypto/verify_dcap_direct/get_dcap_certificate.h create mode 100644 common/crypto/verify_ias_report/.gitignore create mode 100644 common/crypto/verify_ias_report/CMakeLists.txt create mode 100644 common/crypto/verify_ias_report/verify-report.cpp create mode 100644 common/crypto/verify_ias_report/verify-report.h create mode 100644 common/crypto/verify_ita_token/CMakeLists.txt create mode 100644 common/crypto/verify_ita_token/verify-token.cpp create mode 100644 common/crypto/verify_ita_token/verify-token.h create mode 100644 common/error.h create mode 160000 common/jwt-cpp create mode 100644 common/jwt-cpp.patch create mode 120000 common/logging create mode 160000 common/nlohmann/json create mode 100644 common/scripts/common_utils.sh create mode 100644 common/sgx-support/clocale create mode 100644 common/types/hex_string.cpp create mode 100644 common/types/hex_string.h create mode 100644 common/types/types.cpp create mode 100644 common/types/types.h create mode 100755 conversion/attestation_to_evidence.sh create mode 100644 conversion/dcap-direct/a2e.sh create mode 100644 conversion/dcap-direct/b64attestation2b64collateral.cpp create mode 100755 conversion/dcap/a2e.sh create mode 100755 conversion/define_to_variable.sh create mode 100755 conversion/enclave_to_mrenclave.sh create mode 100755 conversion/epid/a2e.sh create mode 100755 conversion/simulated/a2e.sh create mode 100644 conversion/tag_to_variable.sh create mode 100644 docker/Dockerfile create mode 100644 docker/Makefile create mode 100644 docker/container/README.md create mode 100755 docker/container/setup.sh create mode 100644 evidence/verify-dcap-direct-evidence.cpp create mode 100644 evidence/verify-dcap-direct-evidence.h create mode 100644 evidence/verify-dcap-evidence.cpp create mode 100644 evidence/verify-dcap-evidence.h create mode 100644 evidence/verify-evidence.cpp create mode 100644 evidence/verify-ias-evidence.cpp create mode 100644 evidence/verify-ias-evidence.h create mode 100644 include/attestation.h create mode 100644 include/attestation_tags.h create mode 100644 include/verify-evidence.h create mode 100644 ocalls/attestation-ocalls.c create mode 100644 ocalls/attestation-ocalls.edl create mode 100644 test/CMakeLists.txt create mode 100755 test/attested_evidence_test.sh create mode 100644 test/common/test-defines.h create mode 100644 test/common/test-utils.cpp create mode 100644 test/common/test-utils.h create mode 100644 test/get_attestation_app/app/main.cpp create mode 100644 test/get_attestation_app/enclave/CMakeLists.txt create mode 100644 test/get_attestation_app/enclave/CMakeVariables.txt create mode 100644 test/get_attestation_app/enclave/test.cpp create mode 100644 test/get_attestation_app/enclave/test.h create mode 100644 test/get_attestation_app/enclave/test_enclave.config.xml create mode 100644 test/get_attestation_app/enclave/test_enclave.edl create mode 100644 test/get_attestation_app/enclave/test_enclave.lds create mode 100644 test/verify_evidence_app/enclave/CMakeLists.txt create mode 100644 test/verify_evidence_app/enclave/CMakeVariables.txt create mode 100644 test/verify_evidence_app/enclave/test.cpp create mode 100644 test/verify_evidence_app/enclave/test_verify_enclave.config.xml create mode 100644 test/verify_evidence_app/enclave/test_verify_enclave.edl create mode 100644 test/verify_evidence_app/enclave/test_verify_enclave.lds create mode 100644 test/verify_evidence_app/main.cpp create mode 100644 test/verify_evidence_app/test-enclave.cpp create mode 100644 test/verify_evidence_app/test.cpp create mode 100644 test/verify_evidence_app/test.h create mode 100644 usage.md diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..fc458e8 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,6 @@ +[submodule "common/jwt-cpp"] + path = common/jwt-cpp + url = https://github.com/Thalhammer/jwt-cpp +[submodule "common/nlohmann/json"] + path = common/nlohmann/json + url = https://github.com/nlohmann/json diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..16ed4a4 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,219 @@ +# Copyright 2023 Intel Corporation +# +# SPDX-License-Identifier: Apache-2.0 + +cmake_minimum_required(VERSION 3.13) + +PROJECT(ATTESTATION-API) + +set(CMAKE_CXX_STANDARD 14) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_CXX_EXTENSIONS OFF) + +INCLUDE(CMakeVariables.txt) + +IF (NOT DEFINED ENV{DCAP_PRIMITIVES}) + MESSAGE(FATAL_ERROR "DCAP_PRIMITIVES variable with source repo path not defined") +ENDIF() + +################################################################################################### +# First run cmake in common +################################################################################################### +# This patches the jwt repo for sgx use +ADD_SUBDIRECTORY(common) + +################################################################################################### +# Set up trusted certificates in headers +################################################################################################### + +# This creates the necessary headers which include the trusted certificates +# and returns sets CERTIFICATE_INCLUDE_PATH to find them +ADD_SUBDIRECTORY(common/crypto/verify_ias_report) +LIST(APPEND CERTIFICATE_INCLUDE_PATHS "${CERTIFICATE_INCLUDE_PATH}") + +ADD_SUBDIRECTORY(common/crypto/verify_ita_token) +LIST(APPEND CERTIFICATE_INCLUDE_PATHS "${CERTIFICATE_INCLUDE_PATH}") + +ADD_SUBDIRECTORY(common/crypto/verify_dcap_direct) +LIST(APPEND CERTIFICATE_INCLUDE_PATHS "${CERTIFICATE_INCLUDE_PATH}") + +################################################################################################### +# Logging +################################################################################################### + +# Prepare the logging libs. +# Note: by default, no trusted logging and untrusted logging is printf. +# This returns the variables: LOGGING_UNTRUSTED_INCLUDE_PATH, LOGGING_TRUSTED_INCLUDE_PATH +ADD_SUBDIRECTORY(common/logging) + +################################################################################################### +# Project files and headers +################################################################################################### + +FILE(GLOB PROJECT_HEADERS + "include/*.h" + "${CERTIFICATE_INCLUDE_PATH}/*.h" + ) + +FILE(GLOB PROJECT_SOURCES + "evidence/*.cpp" + "common/base64/base64.cpp" + "common/types/*.cpp" + "common/crypto/*.cpp" + "common/crypto/verify_ias_report/*.cpp" + "common/crypto/verify_ita_token/*.cpp" + "common/crypto/verify_dcap_direct/*.cpp" + ) + +FILE(GLOB PROJECT_OCALLS + "ocalls/*.c" + ) + +FILE(GLOB PROJECT_TRUSTED_SOURCES + "attestation/*.cpp" + $ENV{DCAP_PRIMITIVES}/QuoteVerification/QVL/Src/AttestationLibrary/src/*.cpp + $ENV{DCAP_PRIMITIVES}/QuoteVerification/QVL/Src/AttestationLibrary/src/OpensslHelpers/*.cpp + $ENV{DCAP_PRIMITIVES}/QuoteVerification/QVL/Src/AttestationLibrary/src/PckParser/*.cpp + $ENV{DCAP_PRIMITIVES}/QuoteVerification/QVL/Src/AttestationLibrary/src/CertVerification/*.cpp + $ENV{DCAP_PRIMITIVES}/QuoteVerification/QVL/Src/AttestationLibrary/src/QuoteVerification/*.cpp + $ENV{DCAP_PRIMITIVES}/QuoteVerification/QVL/Src/AttestationLibrary/src/Verifiers/*.cpp + $ENV{DCAP_PRIMITIVES}/QuoteVerification/QVL/Src/AttestationLibrary/src/Verifiers/Checks/*.cpp + $ENV{DCAP_PRIMITIVES}/QuoteVerification/QVL/Src/AttestationLibrary/src/Utils/*.cpp + $ENV{DCAP_PRIMITIVES}/QuoteVerification/QVL/Src/AttestationLibrary/include/SgxEcdsaAttestation/*.h + ) + +SET(DCAP_QG_PATH "$ENV{DCAP_PRIMITIVES}/QuoteGeneration") +SET(DCAP_QV_PATH "$ENV{DCAP_PRIMITIVES}/QuoteVerification") + +################################################################################################### +# Tools +################################################################################################### +SET(B64ATTESTATION_TO_B64COLLATERAL "b64attestation_to_b64collateral") +ADD_EXECUTABLE(${B64ATTESTATION_TO_B64COLLATERAL} + conversion/dcap-direct/b64attestation2b64collateral.cpp + common/base64/base64.cpp) +ADD_CUSTOM_COMMAND(TARGET ${B64ATTESTATION_TO_B64COLLATERAL} + POST_BUILD + COMMAND mkdir -p ${CMAKE_CURRENT_BINARY_DIR}/conversion/dcap-direct/ && + mv $ ${CMAKE_CURRENT_BINARY_DIR}/conversion/dcap-direct/) + +TARGET_INCLUDE_DIRECTORIES(${B64ATTESTATION_TO_B64COLLATERAL} PRIVATE common) + +# newer DCAP (1.22) libs need the qal (older, 1.19, don't) +SET(DCAP_LINK_LIBS ${DCAP_QV_PATH}/appraisal/qal/libdcap_qal.a) + +TARGET_LINK_LIBRARIES(${B64ATTESTATION_TO_B64COLLATERAL} + #${DCAP_QV_PATH}/dcap_quoteverify/linux/libsgx_dcap_quoteverify.a + #${DCAP_QG_PATH}/build/linux/libdcap_quoteprov.a + #${DCAP_QG_PATH}/build/linux/libsgx_default_qcnl_wrapper.a + #sgx_dcap_quoteverify dcap_quoteprov sgx_default_qcnl_wrapper + sgx_dcap_quoteverify + # sgx_dcap_ql dcap_quoteprov necessary for the callbacks + sgx_dcap_ql + dcap_quoteprov + ) + +################################################################################################### +# Untrusted one-attestation library +################################################################################################### + +SET(U_OA_LIB_STATIC ${U_ONE_ATTESTATION_LIB_NAME}_Static) + +ADD_LIBRARY(${U_OA_LIB_STATIC} STATIC ${PROJECT_HEADERS} ${PROJECT_SOURCES} ${PROJECT_OCALLS}) + +TARGET_INCLUDE_DIRECTORIES(${U_OA_LIB_STATIC} PRIVATE "$ENV{SGX_SDK}/include") +TARGET_INCLUDE_DIRECTORIES(${U_OA_LIB_STATIC} PRIVATE "include") +TARGET_INCLUDE_DIRECTORIES(${U_OA_LIB_STATIC} BEFORE PRIVATE "common") +TARGET_INCLUDE_DIRECTORIES(${U_OA_LIB_STATIC} PRIVATE "common/nlohmann/json/include") +TARGET_INCLUDE_DIRECTORIES(${U_OA_LIB_STATIC} PRIVATE "common/jwt-cpp/include") +TARGET_INCLUDE_DIRECTORIES(${U_OA_LIB_STATIC} PRIVATE ${CERTIFICATE_INCLUDE_PATHS}) +TARGET_INCLUDE_DIRECTORIES(${U_OA_LIB_STATIC} PRIVATE ${LOGGING_UNTRUSTED_INCLUDE_PATH}) + +TARGET_INCLUDE_DIRECTORIES(${U_OA_LIB_STATIC} PRIVATE "$ENV{DCAP_PRIMITIVES}/QuoteVerification/QVL/Src/Build/Release/dist/include/") +TARGET_INCLUDE_DIRECTORIES(${U_OA_LIB_STATIC} PRIVATE "$ENV{DCAP_PRIMITIVES}") +TARGET_INCLUDE_DIRECTORIES(${U_OA_LIB_STATIC} PRIVATE "$ENV{DCAP_PRIMITIVES}/QuoteVerification/QVL/Src/AttestationLibrary/src") +TARGET_INCLUDE_DIRECTORIES(${U_OA_LIB_STATIC} PRIVATE "$ENV{DCAP_PRIMITIVES}/QuoteVerification/QVL/Src/AttestationCommons/include") + +TARGET_COMPILE_OPTIONS(${U_OA_LIB_STATIC} PRIVATE -fvisibility=hidden) +TARGET_COMPILE_OPTIONS(${U_OA_LIB_STATIC} PRIVATE -fpie) +TARGET_COMPILE_OPTIONS(${U_OA_LIB_STATIC} PRIVATE -fstack-protector) + +SET(QVL_LIB_PATH "$ENV{DCAP_PRIMITIVES}/QuoteVerification/QVL/Src/Build/Release/dist/lib") + +add_custom_target(combined_u_static_target ALL + COMMAND mkdir -p oals_objs lqvs_objs lacs_objs laps_objs + COMMAND ar -x --output oals_objs $ + COMMAND ar -x --output lqvs_objs ${QVL_LIB_PATH}/libQuoteVerificationStatic.a + COMMAND ar -x --output lacs_objs ${QVL_LIB_PATH}/libAttestationCommonsStatic.a + COMMAND ar -x --output laps_objs ${QVL_LIB_PATH}/libAttestationParsersStatic.a + COMMAND ar -qcs lib${U_ONE_ATTESTATION_LIB_NAME}.a oals_objs/*.o lqvs_objs/*.o lacs_objs/*.o laps_objs/*.o + WORKING_DIRECTORY ${CMAKE_BINARY_DIR} + DEPENDS ${U_OA_LIB_STATIC} + ) +ADD_LIBRARY(${U_ONE_ATTESTATION_LIB_NAME} STATIC IMPORTED) +ADD_DEPENDENCIES(${U_ONE_ATTESTATION_LIB_NAME} combined_u_static_target) +SET_TARGET_PROPERTIES(${U_ONE_ATTESTATION_LIB_NAME} + PROPERTIES + IMPORTED_LOCATION lib${U_ONE_ATTESTATION_LIB_NAME}.a + ) + +################################################################################################### +# Trusted one-attestation library +################################################################################################### + +SET(T_OA_LIB_STATIC ${T_ONE_ATTESTATION_LIB_NAME}_Static) +ADD_LIBRARY(${T_OA_LIB_STATIC} STATIC + ${PROJECT_HEADERS} ${PROJECT_TRUSTED_HEADERS} ${PROJECT_SOURCES} ${PROJECT_TRUSTED_SOURCES}) + +#dependency in common to ensure the jwt lib is patched for building the trusted lib in SGX +ADD_DEPENDENCIES(${T_OA_LIB_STATIC} patch_jwt) + +TARGET_INCLUDE_DIRECTORIES(${T_OA_LIB_STATIC} PRIVATE "common/sgx-support") # for clocale, before sgxsdk includes +TARGET_INCLUDE_DIRECTORIES(${T_OA_LIB_STATIC} PRIVATE "$ENV{SGX_SDK}/include") +TARGET_INCLUDE_DIRECTORIES(${T_OA_LIB_STATIC} PRIVATE "$ENV{SGX_SDK}/include/libcxx") +TARGET_INCLUDE_DIRECTORIES(${T_OA_LIB_STATIC} PRIVATE "$ENV{SGX_SDK}/include/tlibc") +TARGET_INCLUDE_DIRECTORIES(${T_OA_LIB_STATIC} PRIVATE "$ENV{SGX_SSL}/include") +TARGET_INCLUDE_DIRECTORIES(${T_OA_LIB_STATIC} PRIVATE "include") +TARGET_INCLUDE_DIRECTORIES(${T_OA_LIB_STATIC} BEFORE PRIVATE "common") +TARGET_INCLUDE_DIRECTORIES(${T_OA_LIB_STATIC} PRIVATE "common/nlohmann/json/include") +TARGET_INCLUDE_DIRECTORIES(${T_OA_LIB_STATIC} PRIVATE "common/jwt-cpp/include") +TARGET_INCLUDE_DIRECTORIES(${T_OA_LIB_STATIC} PRIVATE ${CERTIFICATE_INCLUDE_PATHS}) +TARGET_INCLUDE_DIRECTORIES(${T_OA_LIB_STATIC} PRIVATE ${LOGGING_TRUSTED_INCLUDE_PATH}) + +TARGET_INCLUDE_DIRECTORIES(${T_OA_LIB_STATIC} PRIVATE "$ENV{DCAP_PRIMITIVES}/QuoteVerification/QVL/Src/Build/Release/dist/include/") +TARGET_INCLUDE_DIRECTORIES(${T_OA_LIB_STATIC} PRIVATE "$ENV{DCAP_PRIMITIVES}") +TARGET_INCLUDE_DIRECTORIES(${T_OA_LIB_STATIC} PRIVATE "$ENV{DCAP_PRIMITIVES}/QuoteVerification/QVL/Src/AttestationLibrary/src") +TARGET_INCLUDE_DIRECTORIES(${T_OA_LIB_STATIC} PRIVATE "$ENV{DCAP_PRIMITIVES}/QuoteVerification/QVL/Src/AttestationCommons/include") +TARGET_INCLUDE_DIRECTORIES(${T_OA_LIB_STATIC} PRIVATE "$ENV{DCAP_PRIMITIVES}/QuoteVerification/QVL/Src/ThirdParty/rapidjson/include") + +TARGET_COMPILE_OPTIONS(${T_OA_LIB_STATIC} PRIVATE -nostdinc++) +TARGET_COMPILE_OPTIONS(${T_OA_LIB_STATIC} PRIVATE -fvisibility=hidden) +TARGET_COMPILE_OPTIONS(${T_OA_LIB_STATIC} PRIVATE -fpie) +TARGET_COMPILE_OPTIONS(${T_OA_LIB_STATIC} PRIVATE -fstack-protector) + +#remove time-related code from jwt tool (as dcap primitives do) +TARGET_COMPILE_OPTIONS(${T_OA_LIB_STATIC} PRIVATE -DSGX_JWT) + +add_custom_target(combined_t_static_target ALL + COMMAND mkdir -p toals_objs lacse_objs lapse_objs + COMMAND ar -x --output toals_objs $ + COMMAND ar -x --output lacse_objs ${QVL_LIB_PATH}/libAttestationCommonsStaticEnclave.a + COMMAND ar -x --output lapse_objs ${QVL_LIB_PATH}/libAttestationParsersStaticEnclave.a + COMMAND ar -qcs lib${T_ONE_ATTESTATION_LIB_NAME}.a toals_objs/*.o lacse_objs/*.o lapse_objs/*.o + WORKING_DIRECTORY ${CMAKE_BINARY_DIR} + DEPENDS ${U_OA_LIB_STATIC} + ) +ADD_LIBRARY(${T_ONE_ATTESTATION_LIB_NAME} STATIC IMPORTED) +ADD_DEPENDENCIES(${T_ONE_ATTESTATION_LIB_NAME} combined_t_static_target) +SET_TARGET_PROPERTIES(${T_ONE_ATTESTATION_LIB_NAME} + PROPERTIES + IMPORTED_LOCATION lib${T_ONE_ATTESTATION_LIB_NAME}.a + ) + + +################################################################################################### +# Local Tests +################################################################################################### +ENABLE_TESTING() +ADD_SUBDIRECTORY (test) + diff --git a/CMakeVariables.txt b/CMakeVariables.txt new file mode 100644 index 0000000..093af70 --- /dev/null +++ b/CMakeVariables.txt @@ -0,0 +1,7 @@ +# Copyright 2023 Intel Corporation +# +# SPDX-License-Identifier: Apache-2.0 + +SET(U_ONE_ATTESTATION_LIB_NAME u-one-attestation) +SET(T_ONE_ATTESTATION_LIB_NAME t-one-attestation) + diff --git a/README.md b/README.md new file mode 100644 index 0000000..adcbc33 --- /dev/null +++ b/README.md @@ -0,0 +1,104 @@ +# Attestation API + +This library implements a single API for generating and verifying hardware-based attestation (e.g., Intel SGX EPID/DCAP attestations). +The library implementation originally started as part of the Fabric Private Chaincode project, and later spun off and extended. + +The library works with Intel SGX EPID attestations, verified through Intel Attestation Service (IAS), and DCAP attestations, verified through Intel Trust Authority (ITA). + +## Build + +Build the docker image to create an environment with all necessaty dependencies. +```bash +cd docker +make +``` + +### Simulation mode +Start the dev container: +```bash +docker run -v :/project -e SGX_MODE=SIM -it oaa-dev +``` + +Simulation mode is the default mode. It does not use actual Intel SGX operations, and tests are conducted on simulated-type attestations. + +```bash +source /opt/intel/sgxsdk/environment +mkdir build +cd build +cmake .. +make +``` + +### Hardware mode +Start the dev container, here is an example: +```bash +docker run --network host -v :/project -e SGX_MODE=HW -it oaa-dev +``` +The container is attached to the host network to download the root certificates. +Also, enabling the HW mode makes the generated artifacts use actual Intel SGX operations -- and tests are conducted on real attestations. + +DCAP support with ITA is built by default, but requires the URL to the ITA CA root certificates (in JWK format). If the URL is not specified, the build will succeed but it won't be able to verify DCAP attestations. +```bash +export ITA_ROOT_CACERT_URL= +``` + +EPID support and DCAP support for direct verificaiton (without ITA) are built by default, and require an internet connection to download the Intel CA root certificates. + +```bash +source /opt/intel/sgxsdk/environment +mkdir build +cd build +cmake .. +make +``` + +#### Collateral (for usage and testing) +Using (or testing) SGX attestation requires some collateral. The folder to such collateral must be provided as: +```bash +export COLLATERAL_FOLDER= +``` + +EPID collateral files: +* `spid_type.txt`, a text file containing the SPID type ("epid-linkable" or "epid-unlinkable") +* `spid.txt`, a text file containing the SPID (a 32-byte long hex string) +* `api_key.txt`, a text file containing the API key to use IAS (a 32-byte long hex string) + +DCAP collateral files: +* `ita_api_key.txt`, a text file containing the API key to use ITA +* `attestation_type.txt`, a text file containing the attestation type ("dcap-sgx") + + +#### Testing + +To run the tests in HW mode, the collateral (see above) and the SGX devices are necessary. +Assuming a default system configuration, you may want to start the dev docker image as follows: +```bash +docker run --network host --device /dev/sgx_enclave:/dev/sgx_enclave --device /dev/sgx_provision:/dev/sgx_provision -v /var/run/aesmd:/var/run/aesmd -v :/project -v :/collateral -e SGX_MODE=HW -e COLLATERAL_FOLDER=/collateral -it oaa-dev +``` + +To test everything (simulated attestations in SIM mode, and all supported attestations in HW mode for platforms that support both EPID and DCAP): +```bash +make test +``` + +To test everything except DCAP with ITA: +```bash +SKIP_TEST_DCAP= make test +``` + +To test everything except DCAP with direct verification: +```bash +SKIP_TEST_DCAP_DIRECT= make test +``` +For SGX platform that do not support DCAP or FLC, both DCAP tests must be disabled. + + +To test everything except EPID (for SGX platforms that do not support EPID): +```bash +SKIP_TEST_EPID= make test +``` + +If tests fail, you may want to output some logs with: +```bash +env CTEST_OUTPUT_ON_FAILURE=1 make test +``` diff --git a/attestation/attestation.cpp b/attestation/attestation.cpp new file mode 100644 index 0000000..8e38edd --- /dev/null +++ b/attestation/attestation.cpp @@ -0,0 +1,171 @@ +/* + * Copyright 2020 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include "attestation.h" +#include "attestation_tags.h" +#include "base64/base64.h" +#include "error.h" +#include "logging.h" +#include "types/types.h" +#include "epid.h" +#include "dcap.h" + +// < JSON include +#define JSON_HAS_CPP_11 +#define JSON_NO_IO 1 +#include +#include "nlohmann/json.hpp" +using json = nlohmann::json; +// JSON include > + +/********************************************************************************************************************** + * Attestation APIs + * *******************************************************************************************************************/ + +typedef struct +{ + epid_state_t epid; + //dcap_state_t dcap_state +} state_u; + +typedef struct +{ + bool initialized; + std::string attestation_type; + state_u state; +} attestation_state_t; + +attestation_state_t g_attestation_state = {0}; + +bool init_attestation(uint8_t* params, uint32_t params_length) +{ + if(params == NULL) + { + LOG_ERROR("bad attestation init params"); + return false; + } + + // open json + bool ret = false; + json root; + std::string params_string((char*)params, params_length); + CATCH(ret, root = json::parse(params_string)); + COND2LOGERR(!ret, "invalid attestation params"); + + { + CATCH(ret, g_attestation_state.attestation_type = root[ATTESTATION_TYPE_TAG].template get()); + COND2LOGERR(!ret, "invalid attestation type field"); + + // check for simulated type + if (g_attestation_state.attestation_type.compare(SIMULATED_TYPE_TAG) == 0) + { + // terminate init successfully + goto init_success; + } + + //check for epid type + if (g_attestation_state.attestation_type.compare(0, strlen(EPID_PREFIX_TAG), EPID_PREFIX_TAG) == 0) + { + COND2ERR(!init_epid(params, params_length, &g_attestation_state.state.epid)); + goto init_success; + } + + //check for dcap type + if (g_attestation_state.attestation_type.compare(0, strlen(DCAP_PREFIX_TAG), DCAP_PREFIX_TAG) == 0) + { + COND2ERR(!init_dcap(params, params_length)); + goto init_success; + } + } + +init_success: + g_attestation_state.initialized = true; + return true; + +err: + return false; +} + +bool get_attestation(uint8_t* statement, + uint32_t statement_length, + uint8_t* attestation, + uint32_t attestation_max_length, + uint32_t* attestation_length) +{ + std::string b64attestation; + + COND2LOGERR(!g_attestation_state.initialized, "attestation not initialized"); + COND2LOGERR(statement == NULL, "bad input statement"); + COND2LOGERR(attestation == NULL, "bad input attestation buffer"); + COND2LOGERR(attestation_length == NULL || attestation_max_length == 0, + "bad input attestation buffer size"); + + // attestation type: simulated + if (g_attestation_state.attestation_type.compare(SIMULATED_TYPE_TAG) == 0) + { + std::string zero("0"); + b64attestation = base64_encode((const unsigned char*)zero.c_str(), zero.length()); + } + + // attestation type: epid + if (g_attestation_state.attestation_type.compare(0, strlen(EPID_PREFIX_TAG), EPID_PREFIX_TAG) == 0) + { + COND2ERR( + !get_epid_attestation( + statement, + statement_length, + &g_attestation_state.state.epid, + b64attestation)); + } + + // attestation type: dcap + if (g_attestation_state.attestation_type.compare(0, strlen(DCAP_PREFIX_TAG), DCAP_PREFIX_TAG) == 0) + { + COND2ERR( + !get_dcap_attestation( + statement, + statement_length, + b64attestation)); + } + + // Got the base64 attestation + + // package the output in json format + { + bool ret; + size_t serialization_size = 0; + std::string serialized_json; + json root; + + root[ATTESTATION_TYPE_TAG] = g_attestation_state.attestation_type; + root[ATTESTATION_TAG] = b64attestation; + + CATCH(ret, serialized_json = root.dump()); + COND2LOGERR(!ret, "error serializing attestation json"); + + //serialization_size = json_serialization_size(root_value); + serialization_size = serialized_json.length(); + COND2LOGERR( + serialization_size > attestation_max_length, + "not enough space for b64 conversion: serialization len %d buf len %d", + serialization_size, + attestation_max_length); + + std::strncpy((char*)attestation, serialized_json.c_str(), serialization_size); + + *attestation_length = serialization_size; + + LOG_DEBUG("attestation json: %s", serialized_json.c_str()); + LOG_DEBUG("attestation json length: %d", serialization_size); + } + + return true; + +err: + return false; +} diff --git a/attestation/dcap.cpp b/attestation/dcap.cpp new file mode 100644 index 0000000..46daa2f --- /dev/null +++ b/attestation/dcap.cpp @@ -0,0 +1,136 @@ +/* + * Copyright 2023 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include "sgx_quote.h" +#include "sgx_utils.h" +#include "types/types.h" +#include "crypto/sha256.h" +#include "base64/base64.h" +#include "attestation_tags.h" +#include "logging.h" +#include "error.h" + +// < JSON include +#define JSON_HAS_CPP_11 +#define JSON_NO_IO 1 +#include +#include "nlohmann/json.hpp" +using json = nlohmann::json; +// JSON include > + +/********************************************************************************************************************** + * C prototype declarations for the ocalls + * *******************************************************************************************************************/ + +#ifdef __cplusplus +extern "C" { +#endif + +sgx_status_t ocall_init_quote( + uint8_t* target, uint32_t target_len, uint8_t* egid, uint32_t egid_len, uint32_t* sgxret); +sgx_status_t ocall_get_quote(uint8_t* spid, + uint32_t spid_len, + uint8_t* sig_rl, + uint32_t sig_rl_len, + uint32_t sign_type, + uint8_t* report, + uint32_t report_len, + uint8_t* quote, + uint32_t max_quote_len, + uint32_t* actual_quote_len, + uint32_t* sgxret); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + + +/********************************************************************************************************************** + * DCAP Attestation APIs + * *******************************************************************************************************************/ + +bool init_dcap(uint8_t* params, uint32_t params_length) +{ + if(params == NULL) + { + LOG_ERROR("bad params"); + return false; + } + + // open json + bool ret = false; + json root; + std::string params_string((char*)params, params_length); + CATCH(ret, root = json::parse(params_string)); + COND2LOGERR(!ret, "invalid attestation params"); + + { // set attestation type + std::string attestation_type; + CATCH(ret, attestation_type = root[ATTESTATION_TYPE_TAG].template get()); + COND2LOGERR(!ret, "invalid attestation type field"); + + COND2LOGERR( + 0 != attestation_type.compare(DCAP_SGX_TYPE_TAG) && + 0 != attestation_type.compare(DCAP_DIRECT_SGX_TYPE_TAG), + "invalid attestation type: %s", attestation_type.c_str()); + } + + return true; + +err: + return false; +} + +bool get_dcap_attestation(uint8_t* statement, + uint32_t statement_length, + std::string& b64attestation) +{ + uint32_t ret; + sgx_report_t report; + sgx_target_info_t qe_target_info = {0}; + sgx_report_data_t report_data = {0}; + uint32_t attestation_length_max = (1<<13); // 8K + uint32_t attestation_length; + uint8_t attestation[attestation_length_max]; + + ByteArray ba_statement(statement, statement + statement_length); + ByteArray rd; + COND2ERR(false == SHA256(ba_statement, rd)); + + ocall_init_quote((uint8_t*)&qe_target_info, sizeof(qe_target_info), NULL, 0, &ret); + COND2LOGERR(ret != SGX_SUCCESS, "error ocall_init_quote: %d", ret); + + COND2LOGERR(rd.size() > sizeof(sgx_report_data_t), + "report data too long: %d, needed %d", rd.size(), sizeof(sgx_report_data_t)); + memcpy(&report_data, rd.data(), rd.size()); + + ret = sgx_create_report(&qe_target_info, &report_data, &report); + COND2LOGERR(SGX_SUCCESS != ret, "error sgx_create_report: %d", ret); + + ocall_get_quote( + NULL, + 0, + NULL, + 0, + 0, + (uint8_t*)&report, + sizeof(report), + attestation, + attestation_length_max, + &attestation_length, + &ret); + COND2LOGERR(ret != SGX_SUCCESS, "error ocall_get_quote: %d", ret); + COND2LOGERR(attestation_length == 0, "error get quote"); + + // convert to base64 (accepted by ITA) + b64attestation = base64_encode((const unsigned char*)attestation, attestation_length); + + return true; + +err: + return false; +} diff --git a/attestation/dcap.h b/attestation/dcap.h new file mode 100644 index 0000000..2841b00 --- /dev/null +++ b/attestation/dcap.h @@ -0,0 +1,12 @@ +/* + * Copyright 2023 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +bool init_dcap(uint8_t* params, uint32_t params_length); + +bool get_dcap_attestation(uint8_t* statement, + uint32_t statement_length, + std::string& b64attestation); + diff --git a/attestation/epid.cpp b/attestation/epid.cpp new file mode 100644 index 0000000..e71ad02 --- /dev/null +++ b/attestation/epid.cpp @@ -0,0 +1,167 @@ +/* + * Copyright 2023 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include "sgx_quote.h" +#include "sgx_utils.h" +#include "error.h" +#include "logging.h" +#include "attestation_tags.h" +#include "types/types.h" +#include "crypto/sha256.h" +#include "base64/base64.h" +#include "epid.h" + +// < JSON include +#define JSON_HAS_CPP_11 +#define JSON_NO_IO 1 +#include +#include "nlohmann/json.hpp" +using json = nlohmann::json; +// JSON include > + +/********************************************************************************************************************** + * C prototype declarations for the ocalls + * *******************************************************************************************************************/ + +#ifdef __cplusplus +extern "C" { +#endif + +sgx_status_t ocall_init_quote( + uint8_t* target, uint32_t target_len, uint8_t* egid, uint32_t egid_len, uint32_t* sgxret); +sgx_status_t ocall_get_quote(uint8_t* spid, + uint32_t spid_len, + uint8_t* sig_rl, + uint32_t sig_rl_len, + uint32_t sign_type, + uint8_t* report, + uint32_t report_len, + uint8_t* quote, + uint32_t max_quote_len, + uint32_t* actual_quote_len, + uint32_t* sgxret); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + + +/********************************************************************************************************************** + * EPID Attestation APIs + * *******************************************************************************************************************/ + +bool init_epid(uint8_t* params, uint32_t params_length, epid_state_t* state) +{ + if(params == NULL || state == NULL) + { + LOG_ERROR("bad params"); + return false; + } + + // open json + bool ret = false; + json root; + std::string params_string((char*)params, params_length); + CATCH(ret, root = json::parse(params_string)); + COND2LOGERR(!ret, "invalid attestation params"); + + { // set attestation type + std::string attestation_type; + CATCH(ret, attestation_type = root[ATTESTATION_TYPE_TAG].template get()); + COND2LOGERR(!ret, "invalid attestation type field"); + + state->sign_type = -1; + if (attestation_type.compare(EPID_LINKABLE_TYPE_TAG) == 0) + { + state->sign_type = SGX_LINKABLE_SIGNATURE; + } + + if (attestation_type.compare(EPID_UNLINKABLE_TYPE_TAG) == 0) + { + state->sign_type = SGX_UNLINKABLE_SIGNATURE; + } + + COND2LOGERR(state->sign_type == -1, "wrong attestation type"); + } + + { // set SPID + HexEncodedString hex_spid; + CATCH(ret, hex_spid = root[SPID_TAG].template get()); + COND2LOGERR(!ret, "invalid spid"); + COND2LOGERR(hex_spid.length() != sizeof(sgx_spid_t) * 2, "wrong spid length"); + + // translate hex spid to binary + ByteArray ba = HexEncodedStringToByteArray(hex_spid); + memcpy(state->spid.id, ba.data(), ba.size()); + } + + { // set sig_rl + std::string sig_rl_str; + CATCH(ret, sig_rl_str = root[SIG_RL_TAG].template get()); + COND2LOGERR(!ret, "invalid sig_rl"); + state->sig_rl = ByteArray(sig_rl_str.begin(), sig_rl_str.end()); + } + + return true; + +err: + return false; +} + +bool get_epid_attestation(uint8_t* statement, + uint32_t statement_length, + epid_state_t* state, + std::string& b64attestation) +{ + sgx_report_t report; + sgx_report_data_t report_data = {0}; + sgx_target_info_t qe_target_info = {0}; + sgx_epid_group_id_t egid = {0}; + uint32_t ret; + uint32_t attestation_length_max = (1<<12); // 4K + uint32_t attestation_length; + uint8_t attestation[attestation_length_max]; + + ByteArray ba_statement(statement, statement + statement_length); + ByteArray rd; + COND2ERR(false == SHA256(ba_statement, rd)); + + ocall_init_quote((uint8_t*)&qe_target_info, sizeof(qe_target_info), (uint8_t*)&egid, sizeof(egid), &ret); + COND2LOGERR(ret != SGX_SUCCESS, "error ocall_init_quote: %d", ret); + + + COND2LOGERR(rd.size() > sizeof(sgx_report_data_t), + "report data too long: %d, needed %d", rd.size(), sizeof(sgx_report_data_t)); + memcpy(&report_data, rd.data(), rd.size()); + + ret = sgx_create_report(&qe_target_info, &report_data, &report); + COND2LOGERR(SGX_SUCCESS != ret, "error sgx_create_report: %d", ret); + + ocall_get_quote( + (uint8_t*)&state->spid, + (uint32_t)sizeof(sgx_spid_t), + state->sig_rl.data(), + state->sig_rl.size(), + state->sign_type, + (uint8_t*)&report, + sizeof(report), + attestation, + attestation_length_max, + &attestation_length, + &ret); + COND2LOGERR(ret != SGX_SUCCESS, "error ocall_get_quote: %d", ret); + COND2LOGERR(attestation_length == 0, "error get quote"); + + // convert to base64 (accepted by IAS) + b64attestation = base64_encode((const unsigned char*)attestation, attestation_length); + + return true; + +err: + return false; +} diff --git a/attestation/epid.h b/attestation/epid.h new file mode 100644 index 0000000..5cc9ce8 --- /dev/null +++ b/attestation/epid.h @@ -0,0 +1,22 @@ +/* + * Copyright 2023 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "sgx_quote.h" +#include "sgx_utils.h" + +typedef struct +{ + sgx_spid_t spid; + ByteArray sig_rl; + uint32_t sign_type; +} epid_state_t; + +bool init_epid(uint8_t* params, uint32_t params_length, epid_state_t* state); + +bool get_epid_attestation(uint8_t* statement, + uint32_t statement_length, + epid_state_t* state, + std::string& b64attestation); diff --git a/common/.gitignore b/common/.gitignore new file mode 100644 index 0000000..7209299 --- /dev/null +++ b/common/.gitignore @@ -0,0 +1,2 @@ +.jwt-cpp-patched + diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt new file mode 100644 index 0000000..1e31d3a --- /dev/null +++ b/common/CMakeLists.txt @@ -0,0 +1,22 @@ +# Copyright 2024 Intel Corporation +# +# SPDX-License-Identifier: Apache-2.0 + + +SET(JWT_PATCHED_FILENAME .jwt-cpp-patched) +SET(JWT_PATCHED_FILEPATH ${CMAKE_CURRENT_SOURCE_DIR}/${JWT_PATCHED_FILENAME} PARENT_SCOPE) + +ADD_CUSTOM_COMMAND( + OUTPUT ${JWT_PATCHED_FILENAME} + COMMAND [ ! -f ${JWT_PATCHED_FILENAME} ] && cd jwt-cpp && patch -f -p1 < ../jwt-cpp.patch && touch ../${JWT_PATCHED_FILENAME} || true + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + ) + +ADD_CUSTOM_TARGET(patch_jwt DEPENDS ${JWT_PATCHED_FILENAME}) + +# TODO: devise better strategy for clean up; usually the build folder is removed so the clean is not called +ADD_CUSTOM_TARGET(clean_patch_jwt + COMMAND [ -f ${JWT_PATCHED_FILENAME} ] && cd jwt-cpp && patch -f -p1 -R < ../jwt-cpp.patch && rm ../${JWT_PATCHED_FILENAME} + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + ) + diff --git a/common/README.md b/common/README.md new file mode 100644 index 0000000..ed7549d --- /dev/null +++ b/common/README.md @@ -0,0 +1,3 @@ +The attestation API makes use of the jwt-cpp module. +The module must be patched to work in SGX. +The [patch](https://github.com/intel/SGXDataCenterAttestationPrimitives/blob/dcap_1.22_reproducible/external/0001-Add-a-macro-to-disable-time-support-in-jwt-for-SGX.patch) was borrowed directly from the DCAP primitives and integrated in the cmake build process. diff --git a/common/_nologging/CMakeLists.txt b/common/_nologging/CMakeLists.txt new file mode 100644 index 0000000..7ea9183 --- /dev/null +++ b/common/_nologging/CMakeLists.txt @@ -0,0 +1,9 @@ +# Copyright 2023 Intel Corporation +# +# SPDX-License-Identifier: Apache-2.0 + +SET(LOGGING_TRUSTED_INCLUDE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/trusted PARENT_SCOPE) +SET(LOGGING_UNTRUSTED_INCLUDE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/untrusted PARENT_SCOPE) +SET(LOGGING_UNTRUSTED_LIB "" PARENT_SCOPE) +SET(LOGGING_TRUSTED_LIB "" PARENT_SCOPE) + diff --git a/common/_nologging/README.md b/common/_nologging/README.md new file mode 100644 index 0000000..dca2d76 --- /dev/null +++ b/common/_nologging/README.md @@ -0,0 +1,4 @@ +This package provides the `LOG_*` defines inside and outside of the enclave for logging purposes. +Inside the enclave, the package disables logging, i.e., no enclave-edge APIs are produced +and all function calls are empty. +Outside of the enclave, the package uses the standard I/O library. diff --git a/common/_nologging/ocalls/logging.edl b/common/_nologging/ocalls/logging.edl new file mode 100644 index 0000000..e99af1f --- /dev/null +++ b/common/_nologging/ocalls/logging.edl @@ -0,0 +1,7 @@ +/* + * Copyright 2023 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +enclave {}; diff --git a/common/_nologging/trusted/logging.h b/common/_nologging/trusted/logging.h new file mode 100644 index 0000000..dbe654e --- /dev/null +++ b/common/_nologging/trusted/logging.h @@ -0,0 +1,17 @@ +/* + * Copyright 2023 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef logging_h +#define logging_h + +#define LOG_DEBUG(fmt, ...) +#define LOG_INFO(fmt, ...) +#define LOG_WARNING(fmt, ...) +#define LOG_ERROR(fmt, ...) +#define LOG(fmt, ...) + +#endif + diff --git a/common/_nologging/untrusted/logging.h b/common/_nologging/untrusted/logging.h new file mode 100644 index 0000000..39b05f6 --- /dev/null +++ b/common/_nologging/untrusted/logging.h @@ -0,0 +1,18 @@ +/* + * Copyright 2023 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef logging_h +#define logging_h + +#include + +#define LOG_DEBUG(fmt, ...) printf("DEBUG: %s-%d: " fmt "\n", __FILE__, __LINE__, ##__VA_ARGS__) +#define LOG_INFO(fmt, ...) printf("INFO: %s-%d: " fmt "\n", __FILE__, __LINE__, ##__VA_ARGS__) +#define LOG_WARNING(fmt, ...) printf("WARNING: %s-%d: " fmt "\n", __FILE__, __LINE__, ##__VA_ARGS__) +#define LOG_ERROR(fmt, ...) printf("ERROR: %s-%d: " fmt "\n", __FILE__, __LINE__, ##__VA_ARGS__) +#define LOG(fmt, ...) printf(fmt, ##__VA_ARGS__) + +#endif // logging_h diff --git a/common/_yeslogging/CMakeLists.txt b/common/_yeslogging/CMakeLists.txt new file mode 100644 index 0000000..f85f85e --- /dev/null +++ b/common/_yeslogging/CMakeLists.txt @@ -0,0 +1,47 @@ +# Copyright 2020 Intel Corporation +# +# SPDX-License-Identifier: Apache-2.0 + +CMAKE_MINIMUM_REQUIRED(VERSION 3.2 FATAL_ERROR) + +PROJECT(Logging) + +INCLUDE(CMakeVariables.txt) + +FILE(GLOB PROJECT_OCALLS + "ocalls/*.c" + ) + +FILE(GLOB PROJECT_TRUSTED_SOURCES + "trusted/*.c" + ) + +FILE(GLOB PROJECT_UNTRUSTED_SOURCES + "untrusted/*.c" + ) + +################################################################################################### +# Untrusted logging library +################################################################################################### +ADD_LIBRARY(${U_LOGGING_LIB_NAME} STATIC ${PROJECT_UNTRUSTED_SOURCES} ${PROJECT_OCALLS}) + +TARGET_INCLUDE_DIRECTORIES(${U_LOGGING_LIB_NAME} PRIVATE "untrusted/") +TARGET_INCLUDE_DIRECTORIES(${U_LOGGING_LIB_NAME} PRIVATE "../../common") # path for error.h + +# fPIC necessary for `g_log_callback` +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fPIC") + +################################################################################################### +# Trusted logging library +################################################################################################### +ADD_LIBRARY(${T_LOGGING_LIB_NAME} STATIC ${PROJECT_TRUSTED_SOURCES}) + +TARGET_INCLUDE_DIRECTORIES(${T_LOGGING_LIB_NAME} PRIVATE "trusted/") +TARGET_INCLUDE_DIRECTORIES(${T_LOGGING_LIB_NAME} PRIVATE "../../common") # for error.h +TARGET_INCLUDE_DIRECTORIES(${T_LOGGING_LIB_NAME} PUBLIC "${SGX_SDK}/include/tlibc") + +################################################################################################### +# Test +################################################################################################### +enable_testing() +ADD_SUBDIRECTORY(test) diff --git a/common/_yeslogging/CMakeVariables.txt b/common/_yeslogging/CMakeVariables.txt new file mode 100644 index 0000000..5a2c13f --- /dev/null +++ b/common/_yeslogging/CMakeVariables.txt @@ -0,0 +1,29 @@ +# Copyright 2020 Intel Corporation +# +# SPDX-License-Identifier: Apache-2.0 + +include_guard() + +SET(U_LOGGING_LIB_NAME ulogging) +SET(T_LOGGING_LIB_NAME tlogging) + +# variables useful for a parent cmake using this module +SET(LOGGING_TRUSTED_INCLUDE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/trusted PARENT_SCOPE) +SET(LOGGING_UNTRUSTED_INCLUDE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/untrusted PARENT_SCOPE) +SET(LOGGING_UNTRUSTED_LIB ${U_LOGGING_LIB_NAME} PARENT_SCOPE) +SET(LOGGING_TRUSTED_LIB ${T_LOGGING_LIB_NAME} PARENT_SCOPE) + +# set LOGGING_FLAGS based on SGX_BUILD +if ("$ENV{SGX_BUILD}" STREQUAL "DEBUG") + set(LOGGING_FLAGS "-DDO_DEBUG=true") +elseif("$ENV{SGX_BUILD}" STREQUAL "PRERELEASE") + set(LOGGING_FLAGS "-DDO_DEBUG=false") +elseif("$ENV{SGX_BUILD}" STREQUAL "RELEASE") + set(LOGGING_FLAGS "-DDO_DEBUG=false") +else() + set(LOGGING_FLAGS "-DDO_DEBUG=false") +endif() + +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${LOGGING_FLAGS}") +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${LOGGING_FLAGS}") + diff --git a/common/_yeslogging/include/log-defines.h b/common/_yeslogging/include/log-defines.h new file mode 100644 index 0000000..5a2a7f1 --- /dev/null +++ b/common/_yeslogging/include/log-defines.h @@ -0,0 +1,85 @@ +/* + * Copyright IBM Corp. All Rights Reserved. + * Copyright 2020 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef LOG_DEFINES +#define LOG_DEFINES + +#ifndef TAG +#define TAG "" +#endif + +#define LOC_FMT " (%s:%d) " + +#define NRM "\x1B[0m" +#define CYN "\x1B[36m" +#define YEL "\x1B[33m" +#define RED "\x1B[31m" + +/* + * Note: `DO_DEBUG` is set to `false` by default, so no `LOG_DEBUG` is displayed. + * At compile time, this behaviour can be changed by defining `-DDO_DEBUG=true` before the header is + * included. In SGX deployments, such define should be set "only" when the `SGX_BUILD` environment + * variable is set to `DEBUG`. Finally, notice that `DO_INFO`, `DO_WARNING` and `DO_ERROR` are set + * to `true` by default. So, unless they are explictly disabled at compile time, the respective logs + * will be displayed. + */ + +#ifndef DO_DEBUG +#define DO_DEBUG true +#endif + +#ifndef DO_INFO +#define DO_INFO true +#endif + +#ifndef DO_WARNING +#define DO_WARNING true +#endif + +#ifndef DO_ERROR +#define DO_ERROR true +#endif + +#ifdef __cplusplus +extern "C" { +#endif +int loggingf(const char* fmt, ...); +#ifdef __cplusplus +} +#endif + +#if DO_DEBUG == true +#define LOG_DEBUG(fmt, ...) \ + loggingf(CYN "DEBUG " LOC_FMT TAG YEL fmt NRM "\n", __FILE__, __LINE__, ##__VA_ARGS__) +#else // DO_DEBUG +#define LOG_DEBUG(fmt, ...) +#endif // DO_DEBUG + +#if DO_INFO == true +#define LOG_INFO(fmt, ...) \ + loggingf(CYN "INFO " LOC_FMT TAG NRM fmt "\n", __FILE__, __LINE__, ##__VA_ARGS__) +#else // DO_INFO +#define LOG_INFO(fmt, ...) +#endif // DO_INFO + +#if DO_WARNING == true +#define LOG_WARNING(fmt, ...) \ + loggingf(CYN "WARNING " LOC_FMT TAG RED fmt NRM "\n", __FILE__, __LINE__, ##__VA_ARGS__) +#else // DO_WARNING +#define LOG_WARNING(fmt, ...) +#endif // DO_WARNING + +#if DO_ERROR == true +#define LOG_ERROR(fmt, ...) \ + loggingf(CYN "ERROR " LOC_FMT TAG RED fmt NRM "\n", __FILE__, __LINE__, ##__VA_ARGS__) +#else // DO_ERROR +#define LOG_ERROR(fmt, ...) +#endif // DO_ERROR + +#define ERROR_LOG_STRING "error log - omitted" + +#endif // LOG_DEFINES diff --git a/common/_yeslogging/ocalls/logging.c b/common/_yeslogging/ocalls/logging.c new file mode 100644 index 0000000..714c309 --- /dev/null +++ b/common/_yeslogging/ocalls/logging.c @@ -0,0 +1,17 @@ +/* + * Copyright 2020 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "logging.h" +#include "error.h" + +int ocall_log(const char* str) +{ + COND2ERR(str == NULL); + return loggingf(str); + +err: + return 0; +} diff --git a/common/_yeslogging/ocalls/logging.edl b/common/_yeslogging/ocalls/logging.edl new file mode 100644 index 0000000..fd347f5 --- /dev/null +++ b/common/_yeslogging/ocalls/logging.edl @@ -0,0 +1,12 @@ +/* + * Copyright IBM Corp. All Rights Reserved. + * Copyright 2020 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +enclave { + untrusted { + int ocall_log([in, string] const char *str); + }; +}; diff --git a/common/_yeslogging/test/CMakeLists.txt b/common/_yeslogging/test/CMakeLists.txt new file mode 100644 index 0000000..1185d00 --- /dev/null +++ b/common/_yeslogging/test/CMakeLists.txt @@ -0,0 +1,29 @@ +# Copyright 2020 Intel Corporation +# +# SPDX-License-Identifier: Apache-2.0 + + +################################################################################################## +## LOG test app: +## this app tests the untrusted log library +################################################################################################## +SET(TEST_LOG_APP test_log_app) +PROJECT(${TEST_LOG_APP} CXX) +ADD_EXECUTABLE(${TEST_LOG_APP} + untrusted/test.cpp + ) + +TARGET_INCLUDE_DIRECTORIES(${TEST_LOG_APP} PRIVATE "../untrusted") # for logging.h +TARGET_INCLUDE_DIRECTORIES(${TEST_LOG_APP} PRIVATE "../../../common") # for error.h +TARGET_LINK_LIBRARIES(${TEST_LOG_APP} ${U_LOGGING_LIB_NAME}) + +# Register this application as a test +add_test( + NAME ${TEST_LOG_APP} + COMMAND ./${TEST_LOG_APP} + ) + +IF(DEFINED TEST_TARGET) + ADD_DEPENDENCIES(${TEST_TARGET} ${TEST_LOG_APP}) +endif() + diff --git a/common/_yeslogging/test/untrusted/test.cpp b/common/_yeslogging/test/untrusted/test.cpp new file mode 100644 index 0000000..d5c8bc1 --- /dev/null +++ b/common/_yeslogging/test/untrusted/test.cpp @@ -0,0 +1,38 @@ +/* + * Copyright 2020 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include "error.h" +#include "logging.h" + +int main() +{ + int r; + + LOG_INFO("IF YOU READ THIS, IT'S GOOD - 1"); // this should be displayed + + r = loggingf("IF YOU READ THIS, IT'S GOOD - 2"); // this should be displayed + COND2ERR(r == 0); + + r = logging_set_callback(&puts); + COND2ERR(r == 0); + + r = loggingf("HOPEFULLY YOU READ THIS! -3"); // this succeeds, should be displayed + COND2ERR(r == 0); + + LOG_DEBUG("If you read this, log DEBUG is enabled"); + LOG_INFO("If you read this, log INFO is enabled"); + LOG_WARNING("If you read this, log WARNING is enabled"); + LOG_ERROR("If you read this, log ERROR is enabled"); + + puts("Test successful"); + return 0; + +err: + + puts("test log failed"); + return -1; +} diff --git a/common/_yeslogging/trusted/logging.c b/common/_yeslogging/trusted/logging.c new file mode 100644 index 0000000..9ed3f6a --- /dev/null +++ b/common/_yeslogging/trusted/logging.c @@ -0,0 +1,40 @@ +/* + * Copyright IBM Corp. All Rights Reserved. + * Copyright 2020 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "logging.h" +#include //for BUFSIZ +#include //for va_list +#include +#include "error.h" + +int ocall_log(int* retval, const char* str); + +int loggingf(const char* fmt, ...) +{ + char buf[BUFSIZ] = {'\0'}; + va_list ap; + va_start(ap, fmt); + int n = vsnprintf(buf, BUFSIZ, fmt, ap); + va_end(ap); + + char* pbuf; + if (n >= 0 || n < BUFSIZ) + pbuf = buf; + else + pbuf = ERROR_LOG_STRING; + + int sgxstatus, ret; + sgxstatus = ocall_log(&ret, buf); + COND2ERR(sgxstatus != 0); + + COND2ERR(pbuf != buf); // if outputted error log, fail + + return ret; + +err: + return 0; +} diff --git a/common/_yeslogging/trusted/logging.h b/common/_yeslogging/trusted/logging.h new file mode 100644 index 0000000..73faa36 --- /dev/null +++ b/common/_yeslogging/trusted/logging.h @@ -0,0 +1,25 @@ +/* + * Copyright IBM Corp. All Rights Reserved. + * Copyright 2020 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef logging_h +#define logging_h + +#undef TAG +#define TAG "[Enclave] " + +#include "../include/log-defines.h" + +/* + * `loggingf` forwards the input string to the ocall function. + * Returns a boolean as integer: + * 0 false/error + * >0 true/success + * + * The function prototype is in "../include/log-defines.h" + */ + +#endif diff --git a/common/_yeslogging/untrusted/logging.c b/common/_yeslogging/untrusted/logging.c new file mode 100644 index 0000000..68b7ad6 --- /dev/null +++ b/common/_yeslogging/untrusted/logging.c @@ -0,0 +1,51 @@ +/* + * Copyright 2020 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "logging.h" +#include +#include "error.h" + +static log_callback_f g_log_callback = puts; + +bool logging_set_callback(log_callback_f log_callback) +{ + COND2ERR(log_callback == NULL); + + g_log_callback = log_callback; + return true; + +err: + return false; +} + +int loggingf(const char* fmt, ...) +{ + COND2ERR(g_log_callback == NULL); + + char buf[BUFSIZ] = {'\0'}; + va_list ap; + int n; + + va_start(ap, fmt); + n = vsnprintf(buf, BUFSIZ, fmt, ap); + va_end(ap); + + char* pbuf; + if (n >= 0 || n < BUFSIZ) + pbuf = buf; + else + pbuf = ERROR_LOG_STRING; + + n = g_log_callback(pbuf); + COND2ERR(n < 0); + + COND2ERR(pbuf != buf); // if outputted error log, fail + + return 1; + +err: + return 0; +} diff --git a/common/_yeslogging/untrusted/logging.h b/common/_yeslogging/untrusted/logging.h new file mode 100644 index 0000000..ba75f6e --- /dev/null +++ b/common/_yeslogging/untrusted/logging.h @@ -0,0 +1,49 @@ +/* + * Copyright IBM Corp. All Rights Reserved. + * Copyright 2020 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef logging_h +#define logging_h + +#include +#include // for printf +#include "../include/log-defines.h" + +/* + * log_callback_f is the function type that the user must pass as a callback, + * through `logging_set_callback`. + * The expected return value roughly follows the standard definitions of `printf` and `puts`: + * - negative int for error + * - non-negative for success + */ +typedef int (*log_callback_f)(const char* str); + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * `logging_set_callback` lets a user set the callback logging function. + * By default, no callback function is set. + * So the `loggingf` function below fails if no callback is initialized. + */ +bool logging_set_callback(log_callback_f log_callback); + +/* + * `loggingf` forwards the input string to the initialized callback function. + * By default, no callback function is initialized, so `loggingf` returns error. + * Returns a boolean as integer: + * 0 false/error + * >0 true/success + * + * The function prototype is in "../include/log-defines.h" + */ + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif // logging_h diff --git a/common/base64/base64.cpp b/common/base64/base64.cpp new file mode 100644 index 0000000..08bb628 --- /dev/null +++ b/common/base64/base64.cpp @@ -0,0 +1,122 @@ +/* + base64.cpp and base64.h + + base64 encoding and decoding with C++. + + Version: 1.01.00 + + Copyright (C) 2004-2017 RenĂ© Nyffenegger + + This source code is provided 'as-is', without any express or implied + warranty. In no event will the author be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this source code must not be misrepresented; you must not + claim that you wrote the original source code. If you use this source code + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original source code. + + 3. This notice may not be removed or altered from any source distribution. + + RenĂ© Nyffenegger rene.nyffenegger@adp-gmbh.ch + +*/ + +#include "base64.h" +#include + +static const std::string base64_chars = + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz" + "0123456789+/"; + + +static inline bool is_base64(unsigned char c) { + return (isalnum(c) || (c == '+') || (c == '/')); +} + +__attribute__((weak)) std::string base64_encode(unsigned char const* bytes_to_encode, unsigned int in_len) { + std::string ret; + int i = 0; + int j = 0; + unsigned char char_array_3[3]; + unsigned char char_array_4[4]; + + while (in_len--) { + char_array_3[i++] = *(bytes_to_encode++); + if (i == 3) { + char_array_4[0] = (char_array_3[0] & 0xfc) >> 2; + char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4); + char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6); + char_array_4[3] = char_array_3[2] & 0x3f; + + for(i = 0; (i <4) ; i++) + ret += base64_chars[char_array_4[i]]; + i = 0; + } + } + + if (i) + { + for(j = i; j < 3; j++) + char_array_3[j] = '\0'; + + char_array_4[0] = ( char_array_3[0] & 0xfc) >> 2; + char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4); + char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6); + + for (j = 0; (j < i + 1); j++) + ret += base64_chars[char_array_4[j]]; + + while((i++ < 3)) + ret += '='; + + } + + return ret; + +} + +__attribute__((weak)) std::string base64_decode(std::string const& encoded_string) { + int in_len = encoded_string.size(); + int i = 0; + int j = 0; + int in_ = 0; + unsigned char char_array_4[4], char_array_3[3]; + std::string ret; + + while (in_len-- && ( encoded_string[in_] != '=') && is_base64(encoded_string[in_])) { + char_array_4[i++] = encoded_string[in_]; in_++; + if (i ==4) { + for (i = 0; i <4; i++) + char_array_4[i] = base64_chars.find(char_array_4[i]); + + char_array_3[0] = ( char_array_4[0] << 2 ) + ((char_array_4[1] & 0x30) >> 4); + char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2); + char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3]; + + for (i = 0; (i < 3); i++) + ret += char_array_3[i]; + i = 0; + } + } + + if (i) { + for (j = 0; j < i; j++) + char_array_4[j] = base64_chars.find(char_array_4[j]); + + char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4); + char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2); + + for (j = 0; (j < i - 1); j++) ret += char_array_3[j]; + } + + return ret; +} diff --git a/common/base64/base64.h b/common/base64/base64.h new file mode 100644 index 0000000..dd1134c --- /dev/null +++ b/common/base64/base64.h @@ -0,0 +1,14 @@ +// +// base64 encoding and decoding with C++. +// Version: 1.01.00 +// + +#ifndef BASE64_H_C0CE2A47_D10E_42C9_A27C_C883944E704A +#define BASE64_H_C0CE2A47_D10E_42C9_A27C_C883944E704A + +#include + +std::string base64_encode(unsigned char const* , unsigned int len); +std::string base64_decode(std::string const& s); + +#endif /* BASE64_H_C0CE2A47_D10E_42C9_A27C_C883944E704A */ diff --git a/common/crypto/sha256.cpp b/common/crypto/sha256.cpp new file mode 100644 index 0000000..0ecb4b7 --- /dev/null +++ b/common/crypto/sha256.cpp @@ -0,0 +1,22 @@ +/* + * Copyright 2023 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "types/types.h" +#include + +bool SHA256(const ByteArray& message, ByteArray& hash) +{ + SHA256_CTX c; + hash.resize(32); + SHA256_Init(&c); + SHA256_Update(&c, message.data(), message.size()); + SHA256_Final(hash.data(),&c); + return true; + +err: + return false; +} + diff --git a/common/crypto/sha256.h b/common/crypto/sha256.h new file mode 100644 index 0000000..b63a3b6 --- /dev/null +++ b/common/crypto/sha256.h @@ -0,0 +1,8 @@ +/* + * Copyright 2023 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +bool SHA256(const ByteArray& message, ByteArray& hash); + diff --git a/common/crypto/verify_dcap_direct/CMakeLists.txt b/common/crypto/verify_dcap_direct/CMakeLists.txt new file mode 100644 index 0000000..e398243 --- /dev/null +++ b/common/crypto/verify_dcap_direct/CMakeLists.txt @@ -0,0 +1,41 @@ +# Copyright 2024 Intel Corporation +# +# SPDX-License-Identifier: Apache-2.0 + +SET(DCAP_ROOT_CACERT_FILE DCAP_RootCA.pem) +SET(DCAP_CERTIFICATES_H_FILE dcap-certificates.h) + +SET(CERTIFICATE_INCLUDE_PATH ${CMAKE_BINARY_DIR} PARENT_SCOPE) + +SET(DCAP_ROOT_CACERT_URL "https://certificates.trustedservices.intel.com/Intel_SGX_Provisioning_Certification_RootCA.pem") + +FILE(DOWNLOAD + "https://certificates.trustedservices.intel.com/Intel_SGX_Provisioning_Certification_RootCA.pem" + ${CMAKE_BINARY_DIR}/${DCAP_ROOT_CACERT_FILE} + TLS_VERIFY ON + SHOW_PROGRESS + ) + +FILE(READ + ${CMAKE_BINARY_DIR}/${DCAP_ROOT_CACERT_FILE} + INTEL_ROOT_CA_PEM + ) + +FILE(WRITE ${CMAKE_BINARY_DIR}/${DCAP_CERTIFICATES_H_FILE} + " +#ifndef DCAP_CERTIFICATES_H +#define DCAP_CERTIFICATES_H + +/* + * Copyright 2024 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ +#pragma once +extern const char dcap_ca_cert_pem[] = +R\"MLT(${INTEL_ROOT_CA_PEM} +)MLT\" +; + +#endif +") diff --git a/common/crypto/verify_dcap_direct/get_dcap_certificate.cpp b/common/crypto/verify_dcap_direct/get_dcap_certificate.cpp new file mode 100644 index 0000000..e3fde2f --- /dev/null +++ b/common/crypto/verify_dcap_direct/get_dcap_certificate.cpp @@ -0,0 +1,14 @@ +/* + * Copyright 2024 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include "dcap-certificates.h" + +bool get_dcap_certificate(std::string& certificate) +{ + certificate = std::string(dcap_ca_cert_pem); + return true; +} diff --git a/common/crypto/verify_dcap_direct/get_dcap_certificate.h b/common/crypto/verify_dcap_direct/get_dcap_certificate.h new file mode 100644 index 0000000..30087ba --- /dev/null +++ b/common/crypto/verify_dcap_direct/get_dcap_certificate.h @@ -0,0 +1,7 @@ +/* + * Copyright 2024 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +bool get_dcap_certificate(std::string& certificate); diff --git a/common/crypto/verify_ias_report/.gitignore b/common/crypto/verify_ias_report/.gitignore new file mode 100644 index 0000000..7979a60 --- /dev/null +++ b/common/crypto/verify_ias_report/.gitignore @@ -0,0 +1 @@ +ias-certificates.cpp diff --git a/common/crypto/verify_ias_report/CMakeLists.txt b/common/crypto/verify_ias_report/CMakeLists.txt new file mode 100644 index 0000000..77b0c26 --- /dev/null +++ b/common/crypto/verify_ias_report/CMakeLists.txt @@ -0,0 +1,39 @@ +# Copyright 2023 Intel Corporation +# +# SPDX-License-Identifier: Apache-2.0 + +SET(INTEL_ROOT_CACERT_FILE Intel_SGX_Attestation_RootCA.pem) +SET(IAS_CERTIFICATES_H_FILE ias-certificates.h) + +SET(CERTIFICATE_INCLUDE_PATH ${CMAKE_BINARY_DIR} PARENT_SCOPE) + +FILE(DOWNLOAD + https://certificates.trustedservices.intel.com/${INTEL_ROOT_CACERT_FILE} + ${CMAKE_BINARY_DIR}/${INTEL_ROOT_CACERT_FILE} + TLS_VERIFY ON + SHOW_PROGRESS + ) + +FILE(READ + ${CMAKE_BINARY_DIR}/${INTEL_ROOT_CACERT_FILE} + INTEL_ROOT_CA_PEM + ) + +FILE(WRITE ${CMAKE_BINARY_DIR}/${IAS_CERTIFICATES_H_FILE} + " +#ifndef IAS_CERTIFICATES_H +#define IAS_CERTIFICATES_H + +/* + * Copyright 2023 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ +#pragma once +extern const char ias_report_signing_ca_cert_pem[] = +R\"MLT(${INTEL_ROOT_CA_PEM} +)MLT\" +; + +#endif +") diff --git a/common/crypto/verify_ias_report/verify-report.cpp b/common/crypto/verify_ias_report/verify-report.cpp new file mode 100644 index 0000000..a391cb9 --- /dev/null +++ b/common/crypto/verify_ias_report/verify-report.cpp @@ -0,0 +1,311 @@ +/* Copyright 2018 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. + */ + +#include "verify-report.h" + +#include +#include +#include +#include +#include + +#include "ias-certificates.h" +#include "error.h" +#include "logging.h" + +// < JSON include +#define JSON_HAS_CPP_11 +#define JSON_NO_IO 1 +#include +#include "nlohmann/json.hpp" +using json = nlohmann::json; +// JSON include > + +//<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< +//########### INTERNAL FUNCTIONS ######################################### +//######################################################################## + +/* EVP_DecodeBlock pads its output with \0 if the output length is not + a multiple of 3. Check if the base64 string is padded at the end + and adjust the output length. */ +static int EVP_DecodeBlock_wrapper(unsigned char* out, + int out_len, + const unsigned char* in, + int in_len) +{ + /* Use a temporary output buffer. We do not want to disturb the + original output buffer with extraneous \0 bytes. */ + unsigned char buf[in_len]; + + int ret = EVP_DecodeBlock(buf, in, in_len); + COND2ERR(ret == -1); + if (in[in_len - 1] == '=' && in[in_len - 2] == '=') + { + ret -= 2; + } + else if (in[in_len - 1] == '=') + { + ret -= 1; + } + + memcpy(out, buf, out_len); + return ret; + +err: + return -1; +} + +//######################################################################## +//########### INTERNAL FUNCTIONS ######################################### +//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> + +#define IAS_QUOTE_STATUS_JSON_STRING "isvEnclaveQuoteStatus" + +struct qss +{ + const char* s; + size_t l; +}; + +#define MAKE_QSS_ITEM(x) \ + { \ + x, sizeof(x) - 1 \ + } +#define INIT_QS_ARRAY_ITEM(x, y) [x] = MAKE_QSS_ITEM(y) + +const struct qss quote_status[QS_NUMBER] = { + INIT_QS_ARRAY_ITEM(QS_INVALID, "INVALID"), + INIT_QS_ARRAY_ITEM(QS_OK, "OK"), + INIT_QS_ARRAY_ITEM(QS_GROUP_OUT_OF_DATE, "GROUP_OUT_OF_DATE"), + INIT_QS_ARRAY_ITEM(QS_CONFIGURATION_NEEDED, "CONFIGURATION_NEEDED"), + INIT_QS_ARRAY_ITEM(QS_SW_HARDENING_NEEDED, "SW_HARDENING_NEEDED"), + INIT_QS_ARRAY_ITEM(QS_CONFIGURATION_AND_SW_HARDENING_NEEDED, + "CONFIGURATION_AND_SW_HARDENING_NEEDED")}; + +quote_status_e get_quote_status(const char* ias_report, unsigned int ias_report_len) +{ + bool ret = false; + json root; + std::string ias_quote_status_str; + int i; + + CATCH(ret, root = json::parse(ias_report)); + COND2LOGERR(!ret, "invalid ias report json"); + + CATCH(ret, ias_quote_status_str = root[IAS_QUOTE_STATUS_JSON_STRING].template get()); + COND2LOGERR(!ret, "invalid ias quote status field"); + + for (i = 1; i < QS_NUMBER; i++) + { + if (0 == ias_quote_status_str.compare(quote_status[i].s)) + { + return (quote_status_e)i; + } + } + +err: + return QS_INVALID; +} + +int get_quote_from_report(const uint8_t* report, const int report_len, sgx_quote_t* quote) +{ + // Move report into \0 terminated buffer such that we can work + // with str* functions. + int buf_len = report_len + 1; + char buf[buf_len]; + char* p_begin = NULL; + char* p_end = NULL; + int ret = -1; + int quote_base64_len = 0; + uint8_t* quote_bin = NULL; + uint32_t quote_bin_len = 0; + + memcpy(buf, report, buf_len); + buf[report_len] = '\0'; + + const int json_string_max_len = 64; + const char json_string[json_string_max_len] = "\"isvEnclaveQuoteBody\":\""; + p_begin = strstr(buf, json_string); + COND2ERR(p_begin == NULL); + p_begin += strnlen(json_string, json_string_max_len); + p_end = strchr(p_begin, '"'); + COND2ERR(p_end == NULL); + + quote_base64_len = p_end - p_begin; + quote_bin = (uint8_t*)malloc(quote_base64_len); + quote_bin_len = quote_base64_len; + + ret = EVP_DecodeBlock(quote_bin, (unsigned char*)p_begin, quote_base64_len); + COND2ERR(ret == -1); + + quote_bin_len = ret; + COND2ERR(quote_bin_len > sizeof(sgx_quote_t)); + memset(quote, 0, sizeof(sgx_quote_t)); + memcpy(quote, quote_bin, sizeof(sgx_quote_t)); + free(quote_bin); + + // success + return 0; + +err: + return -1; +} + +verify_status_t verify_ias_report_signature(const char* ias_attestation_signing_cert_pem, + const char* ias_report, + unsigned int ias_report_len, + char* ias_signature, + unsigned int ias_signature_len) +{ + X509* crt = NULL; + int ret = -1; + int ias_signature_decoded_len = 2048; + unsigned char ias_signature_decoded[ias_signature_decoded_len]; + EVP_PKEY* key = NULL; + EVP_MD_CTX* ctx = NULL; + + BIO* crt_bio = BIO_new_mem_buf((void*)ias_attestation_signing_cert_pem, -1); + COND2ERR(crt_bio == NULL); + + crt = PEM_read_bio_X509(crt_bio, NULL, 0, NULL); + COND2ERR(crt == NULL); + + key = X509_get_pubkey(crt); + COND2ERR(key == NULL); + + ctx = EVP_MD_CTX_create(); + ret = EVP_VerifyInit_ex(ctx, EVP_sha256(), NULL); + COND2ERR(ret != 1); + + ret = EVP_VerifyUpdate(ctx, ias_report, ias_report_len); + COND2ERR(ret != 1); + + ret = EVP_DecodeBlock_wrapper(ias_signature_decoded, + ias_signature_decoded_len, + (unsigned char*)ias_signature, + ias_signature_len); + COND2ERR(ret == -1); + + ret = EVP_VerifyFinal(ctx, (unsigned char*)ias_signature_decoded, ret, key); + + EVP_MD_CTX_destroy(ctx); + EVP_PKEY_free(key); + X509_free(crt); + BIO_free(crt_bio); + + COND2ERR(ret != 1); // 1 == correct signature + + return VERIFY_SUCCESS; /* success */ + +err: + return VERIFY_FAILURE; +} + +verify_status_t verify_ias_certificate_chain(const char* cert_pem, const time_t untrusted_time) +{ + /* Using the IAS CA certificate as a root of trust. */ + /* Checking that cert is signed by CA. */ + + X509* cacrt = NULL; + X509* crt = NULL; + BIO* crt_bio = NULL; + BIO* cacrt_bio = NULL; + X509_STORE* s = NULL; + X509_STORE_CTX* ctx = NULL; + X509_VERIFY_PARAM* param; + int rc = -1; + + COND2ERR(cert_pem == NULL); + + crt_bio = BIO_new_mem_buf((void*)cert_pem, -1); + crt = PEM_read_bio_X509(crt_bio, NULL, 0, NULL); + COND2ERR(crt == NULL); + + cacrt_bio = BIO_new_mem_buf((void*)ias_report_signing_ca_cert_pem, -1); + cacrt = PEM_read_bio_X509(cacrt_bio, NULL, 0, NULL); + // the correct CA certificate is hard-coded, so this must never fail + assert(cacrt != NULL); + + //initialize store/context + s = X509_STORE_new(); + COND2ERR(s == NULL); + rc = X509_STORE_add_cert(s, cacrt); + COND2ERR(rc != 1); + ctx = X509_STORE_CTX_new(); + COND2ERR(ctx == NULL); + rc = X509_STORE_CTX_init(ctx, s, crt, NULL); + COND2ERR(rc != 1); + + //initialize time + param = X509_STORE_CTX_get0_param(ctx); + COND2ERR(param == NULL); + X509_VERIFY_PARAM_set_time(param, untrusted_time); + + rc = X509_verify_cert(ctx); + // check value after free + + X509_STORE_CTX_free(ctx); + X509_STORE_free(s); + X509_free(crt); + X509_free(cacrt); + BIO_free(crt_bio); + BIO_free(cacrt_bio); + + COND2ERR(rc <= 0); + + return VERIFY_SUCCESS; + +err: + return VERIFY_FAILURE; +} + +/** + * Check if isvEnclaveQuoteStatus is "OK" + * (cf. https://software.intel.com/sites/default/files/managed/7e/3b/ias-api-spec.pdf, + * pg. 24). + * + * @return 0 if verified successfully, 1 otherwise. + */ +verify_status_t verify_enclave_quote_status(const char* ias_report, + unsigned int ias_report_len, + unsigned int quote_status_flags) +{ + quote_status_e qs; + + qs = get_quote_status(ias_report, ias_report_len); + + switch (qs) + { + case QS_INVALID: + COND2ERR(1); + + case QS_OK: + return VERIFY_SUCCESS; + + case QS_GROUP_OUT_OF_DATE: + case QS_CONFIGURATION_NEEDED: + case QS_SW_HARDENING_NEEDED: + case QS_CONFIGURATION_AND_SW_HARDENING_NEEDED: + COND2ERR(0 == (quote_status_flags & (1 << qs))); + return VERIFY_SUCCESS; + + default: + COND2ERR(1); + } + +err: + // quote not ok + return VERIFY_FAILURE; +} diff --git a/common/crypto/verify_ias_report/verify-report.h b/common/crypto/verify_ias_report/verify-report.h new file mode 100644 index 0000000..a9fdc8e --- /dev/null +++ b/common/crypto/verify_ias_report/verify-report.h @@ -0,0 +1,70 @@ +/* Copyright 2018 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. + */ + +#ifndef VERIFY_REPORT_H +#define VERIFY_REPORT_H + +#include +#include + +typedef enum +{ + VERIFY_SUCCESS, + VERIFY_FAILURE +} verify_status_t; + +typedef enum +{ + QS_INVALID, + QS_OK, + QS_GROUP_OUT_OF_DATE, + QS_CONFIGURATION_NEEDED, + QS_SW_HARDENING_NEEDED, + QS_CONFIGURATION_AND_SW_HARDENING_NEEDED, + QS_NUMBER +} quote_status_e; + +#define QSF_ACCEPT_GROUP_OUT_OF_DATE (1 << QS_GROUP_OUT_OF_DATE) +#define QSF_ACCEPT_CONFIGURATION_NEEDED (1 << QS_CONFIGURATION_NEEDED) +#define QSF_ACCEPT_SW_HARDENING_NEEDED (1 << QS_SW_HARDENING_NEEDED) +#define QSF_ACCEPT_CONFIGURATION_AND_SW_HARDENING_NEEDED \ + (1 << QS_CONFIGURATION_AND_SW_HARDENING_NEEDED) +#define QSF_ACCEPT_ALL UINT_MAX +#define QSF_REJECT_ALL (0) + +#ifdef __cplusplus +extern "C" { +#endif + +int get_quote_from_report(const uint8_t* report, const int report_len, sgx_quote_t* quote); +verify_status_t verify_enclave_quote_status(const char* ias_report, + unsigned int ias_report_len, + unsigned int quote_status_flags); +verify_status_t verify_ias_certificate_chain(const char* cert_pem, + const time_t untrusted_time); +verify_status_t verify_ias_report_signature(const char* ias_attestation_signing_cert_pem, + const char* ias_report, + unsigned int ias_report_len, + char* ias_signature, + unsigned int ias_signature_len); + +quote_status_e get_quote_status(const char* ias_report, unsigned int ias_report_len); +#ifdef __cplusplus +} +#endif + + +#endif + diff --git a/common/crypto/verify_ita_token/CMakeLists.txt b/common/crypto/verify_ita_token/CMakeLists.txt new file mode 100644 index 0000000..49457cb --- /dev/null +++ b/common/crypto/verify_ita_token/CMakeLists.txt @@ -0,0 +1,46 @@ +# Copyright 2023 Intel Corporation +# +# SPDX-License-Identifier: Apache-2.0 + +SET(ITA_ROOT_CACERT_FILE ITA_RootCA.jwk) +SET(ITA_CERTIFICATES_H_FILE ita-certificates.h) + +SET(CERTIFICATE_INCLUDE_PATH ${CMAKE_BINARY_DIR} PARENT_SCOPE) + +IF (NOT DEFINED ENV{ITA_ROOT_CACERT_URL}) + MESSAGE(WARNING "No ITA certs url in ITA_ROOT_CACERT_URL: an empty root cert will be used (DCAP verifications will fail)") + FILE(TOUCH + ${CMAKE_BINARY_DIR}/${ITA_ROOT_CACERT_FILE} + ) +ELSE() + FILE(DOWNLOAD + $ENV{ITA_ROOT_CACERT_URL} + ${CMAKE_BINARY_DIR}/${ITA_ROOT_CACERT_FILE} + TLS_VERIFY ON + SHOW_PROGRESS + ) +ENDIF() + +FILE(READ + ${CMAKE_BINARY_DIR}/${ITA_ROOT_CACERT_FILE} + INTEL_ROOT_CA_JWT + ) + +FILE(WRITE ${CMAKE_BINARY_DIR}/${ITA_CERTIFICATES_H_FILE} + " +#ifndef ITA_CERTIFICATES_H +#define ITA_CERTIFICATES_H + +/* + * Copyright 2023 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ +#pragma once +extern const char ita_token_signing_ca_cert_jwt[] = +R\"MLT(${INTEL_ROOT_CA_JWT} +)MLT\" +; + +#endif +") diff --git a/common/crypto/verify_ita_token/verify-token.cpp b/common/crypto/verify_ita_token/verify-token.cpp new file mode 100644 index 0000000..539b9e1 --- /dev/null +++ b/common/crypto/verify_ita_token/verify-token.cpp @@ -0,0 +1,112 @@ +/* + * Copyright 2023 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "types/types.h" +#include "verify-token.h" +#include "ita-certificates.h" +#include "logging.h" +#include "error.h" + +#define JSON_HAS_CPP_11 +#define JSON_NO_IO 1 +#include +#include "nlohmann/json/single_include/nlohmann/json.hpp" + +#define JWT_DISABLE_PICOJSON +#include "jwt-cpp/include/jwt-cpp/traits/nlohmann-json/traits.h" +#include "jwt-cpp/include/jwt-cpp/jwt.h" + +bool get_ita_certificate(std::string& certificate) +{ + certificate = std::string(ita_token_signing_ca_cert_jwt); + return true; +} + +verify_status_t verify_ita_token_signature(const std::string token) +{ + + std::string cert; + std::string t(token); + get_ita_certificate(cert); + + COND2LOGERR(cert.length() <= 1, "Unable to verify ITA tokens: ITA root cert not provided"); + + try + { + auto decoded_token = jwt::decode(t); + //auto iat = decoded_token.get_issued_at().time_since_epoch().count(); + //auto exp = decoded_token.get_expires_at().time_since_epoch().count(); + //auto now = std::chrono::system_clock::time_point::clock::now().time_since_epoch().count(); + LOG_DEBUG("token decoded"); + //LOG_DEBUG("iat: %ld", iat); + //LOG_DEBUG("exp: %ld", exp); + //LOG_DEBUG("now: %ld", now); + LOG_DEBUG("keyid: %s", decoded_token.get_key_id().c_str()); + + jwt::jwks jwkeys = jwt::parse_jwks(cert); + auto jwkey = jwkeys.get_jwk(decoded_token.get_key_id()); + auto x5cert = jwkey.get_x5c_key_value(); + auto pemkey = jwt::helper::convert_base64_der_to_pem(x5cert); + LOG_DEBUG("pem: %s", pemkey.c_str()); + + auto verifier = jwt::verify().allow_algorithm(jwt::algorithm::ps384(pemkey, "", "", "")); + verifier.verify(decoded_token); + LOG_DEBUG("token verified"); + } + catch (const std::exception &exc) + { + LOG_ERROR("Exception: %s", exc.what()); + if(std::string(exc.what()).compare("token expired") == 0) + { + bool b; + int leeway = 30; // 30 seconds leeway + auto decoded_token = jwt::decode(t); + jwt::jwks jwkeys = jwt::parse_jwks(cert); + auto jwkey = jwkeys.get_jwk(decoded_token.get_key_id()); + auto x5cert = jwkey.get_x5c_key_value(); + auto pemkey = jwt::helper::convert_base64_der_to_pem(x5cert); + auto verifier = jwt::verify().allow_algorithm(jwt::algorithm::ps384(pemkey, "", "", "")); + verifier.leeway(leeway); + CATCH(b, verifier.verify(decoded_token)); + COND2LOGERR(!b, "failed token verification"); + + LOG_DEBUG("token verified, leeway: %d", leeway); + goto ok; + } + goto err; + } + +ok: + return VERIFY_SUCCESS; + + +err: + return VERIFY_FAILURE; +} + + +bool get_token_payload(const std::string token, std::string& payload) +{ + try + { + auto decoded_token = jwt::decode(token); + std::string header = decoded_token.get_header(); + LOG_INFO("%s", header.c_str()); + payload = decoded_token.get_payload(); + LOG_INFO("%s", payload.c_str()); + } + catch (const std::exception &exc) + { + LOG_ERROR("Exception: %s", exc.what()); + goto err; + } + + return true; + +err: + return false; +} + diff --git a/common/crypto/verify_ita_token/verify-token.h b/common/crypto/verify_ita_token/verify-token.h new file mode 100644 index 0000000..1f91854 --- /dev/null +++ b/common/crypto/verify_ita_token/verify-token.h @@ -0,0 +1,16 @@ +/* + * Copyright 2023 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +typedef enum +{ + VERIFY_SUCCESS, + VERIFY_FAILURE +} verify_status_t; + +bool get_ita_certificate(std::string& certificate); +verify_status_t verify_ita_token_signature(std::string token); +bool get_token_payload(std::string token, std::string& payload); + diff --git a/common/error.h b/common/error.h new file mode 100644 index 0000000..bba2c42 --- /dev/null +++ b/common/error.h @@ -0,0 +1,46 @@ +/* + * Copyright 2020 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef COND2ERR +#define COND2ERR(b) \ + do \ + { \ + if (b) \ + { \ + LOG_DEBUG("error at %s:%d\n", __FILE__, __LINE__); \ + goto err; \ + } \ + } while (0) +#endif //COND2ERR + +#ifndef COND2LOGERR +#define COND2LOGERR(b, fmt, ...) \ + do \ + { \ + if (b) \ + { \ + LOG_ERROR("error at %s:%d: " fmt "\n", __FILE__, __LINE__, ##__VA_ARGS__); \ + goto err; \ + } \ + } while (0) +#endif //COND2LOGERR + +#ifndef CATCH +#define CATCH(b, expr) \ + do \ + { \ + try \ + { \ + expr; \ + b = true; \ + } \ + catch (const std::exception &exc) \ + { \ + LOG_ERROR("exception at %s:%d: %s\n", __FILE__, __LINE__, exc.what()); \ + b = false; \ + } \ + } while (0); +#endif //CATCH diff --git a/common/jwt-cpp b/common/jwt-cpp new file mode 160000 index 0000000..4a537e9 --- /dev/null +++ b/common/jwt-cpp @@ -0,0 +1 @@ +Subproject commit 4a537e969891dde542ad8b1a4a214955a83be29f diff --git a/common/jwt-cpp.patch b/common/jwt-cpp.patch new file mode 100644 index 0000000..da8da3a --- /dev/null +++ b/common/jwt-cpp.patch @@ -0,0 +1,224 @@ +diff --git a/include/jwt-cpp/jwt.h b/include/jwt-cpp/jwt.h +index 211305e..3b00e2b 100644 +--- a/include/jwt-cpp/jwt.h ++++ b/include/jwt-cpp/jwt.h +@@ -72,11 +72,12 @@ + * JWS (JSON Web Signature) from [RFC7515](https://tools.ietf.org/html/rfc7515) + */ + namespace jwt { ++#ifndef SGX_JWT + /** + * Default system time point in UTC + */ + using date = std::chrono::system_clock::time_point; +- ++#endif + /** + * \brief Everything related to error codes issued by the library + */ +@@ -2148,8 +2149,10 @@ namespace jwt { + ~basic_claim() = default; + + JWT_CLAIM_EXPLICIT basic_claim(typename json_traits::string_type s) : val(std::move(s)) {} ++#ifndef SGX_JWT + JWT_CLAIM_EXPLICIT basic_claim(const date& d) + : val(typename json_traits::integer_type(std::chrono::system_clock::to_time_t(d))) {} ++#endif + JWT_CLAIM_EXPLICIT basic_claim(typename json_traits::array_type a) : val(std::move(a)) {} + JWT_CLAIM_EXPLICIT basic_claim(typename json_traits::value_type v) : val(std::move(v)) {} + JWT_CLAIM_EXPLICIT basic_claim(const set_t& s) : val(typename json_traits::array_type(s.begin(), s.end())) {} +@@ -2188,13 +2191,14 @@ namespace jwt { + */ + typename json_traits::string_type as_string() const { return json_traits::as_string(val); } + ++#ifndef SGX_JWT + /** + * Get the contained JSON value as a date + * \return content as date + * \throw std::bad_cast Content was not a date + */ + date as_date() const { return std::chrono::system_clock::from_time_t(as_int()); } +- ++#endif + /** + * Get the contained JSON value as an array + * \return content as array +@@ -2402,6 +2406,7 @@ namespace jwt { + + return aud.as_set(); + } ++#ifndef SGX_JWT + /** + * Get expires claim + * \return expires as a date in utc +@@ -2423,6 +2428,7 @@ namespace jwt { + * \throw std::bad_cast Claim was present but not a date (Should not happen in a valid token) + */ + date get_issued_at() const { return get_payload_claim("iat").as_date(); } ++#endif + /** + * Get id claim + * \return id as string +@@ -2780,6 +2786,7 @@ namespace jwt { + builder& set_audience(typename json_traits::string_type aud) { + return set_payload_claim("aud", typename json_traits::value_type(aud)); + } ++#ifndef SGX_JWT + /** + * Set expires at claim + * \param d Expires time +@@ -2798,6 +2805,7 @@ namespace jwt { + * \return *this to allow for method chaining + */ + builder& set_issued_at(const date& d) { return set_payload_claim("iat", basic_claim(d)); } ++#endif + /** + * Set id claim + * \param str ID to set +@@ -2898,10 +2906,15 @@ namespace jwt { + */ + template + struct verify_context { ++#ifndef SGX_JWT + verify_context(date ctime, const decoded_jwt& j, size_t l) + : current_time(ctime), jwt(j), default_leeway(l) {} + // Current time, retrieved from the verifiers clock and cached for performance and consistency + date current_time; ++#else ++ verify_context(const decoded_jwt& j, size_t l) ++ : jwt(j), default_leeway(l) {} ++#endif + // The jwt passed to the verifier + const decoded_jwt& jwt; + // The configured default leeway for this verification +@@ -2969,6 +2982,7 @@ namespace jwt { + } + }; + ++#ifndef SGX_JWT + /** + * Checks that the current time is before the time specified in the given + * claim. This is identical to how the "exp" check works. +@@ -3002,7 +3016,7 @@ namespace jwt { + } + } + }; +- ++#endif + /** + * Checks if the given set is a subset of the set inside the token. + * If the token value is a string it is traited as a set of a single element. +@@ -3053,11 +3067,16 @@ namespace jwt { + } + + static std::string to_lower_unicode(const std::string& str, const std::locale& loc) { ++#ifndef SGX_JWT + std::wstring_convert, wchar_t> conv; + auto wide = conv.from_bytes(str); + auto& f = std::use_facet>(loc); + f.tolower(&wide[0], &wide[0] + wide.size()); + return conv.to_bytes(wide); ++#else ++ // TODO - don't lowercase the string as locale support inside enclave is limited ++ return str; ++#endif + } + }; + } // namespace verify_ops +@@ -3066,7 +3085,11 @@ namespace jwt { + * Verifier class used to check if a decoded token contains all claims required by your application and has a valid + * signature. + */ ++#ifndef SGX_JWT + template ++#else ++ template ++#endif + class verifier { + public: + using basic_claim_t = basic_claim; +@@ -3099,8 +3122,10 @@ namespace jwt { + std::unordered_map claims; + /// Leeway time for exp, nbf and iat + size_t default_leeway = 0; ++#ifndef SGX_JWT + /// Instance of clock type + Clock clock; ++#endif + /// Supported algorithms + std::unordered_map> algs; + +@@ -3109,6 +3134,7 @@ namespace jwt { + * Constructor for building a new verifier instance + * \param c Clock instance + */ ++#ifndef SGX_JWT + explicit verifier(Clock c) : clock(c) { + claims["exp"] = [](const verify_ops::verify_context& ctx, std::error_code& ec) { + if (!ctx.jwt.has_expires_at()) return; +@@ -3132,6 +3158,7 @@ namespace jwt { + } + }; + } ++#endif + + /** + * Set default leeway to use. +@@ -3142,6 +3169,7 @@ namespace jwt { + default_leeway = leeway; + return *this; + } ++#ifndef SGX_JWT + /** + * Set leeway for expires at. + * If not specified the default leeway will be used. +@@ -3172,7 +3200,7 @@ namespace jwt { + claims["iat"] = verify_ops::date_after_claim{leeway}; + return *this; + } +- ++#endif + /** + * Set an type to check for. + * +@@ -3294,8 +3322,11 @@ namespace jwt { + } + algs.at(algo)->verify(data, sig, ec); + if (ec) return; +- ++#ifndef SGX_JWT + verify_ops::verify_context ctx{clock.now(), jwt, default_leeway}; ++#else ++ verify_ops::verify_context ctx{jwt, default_leeway}; ++#endif + for (auto& c : claims) { + ctx.claim_key = c.first; + c.second(ctx, ec); +@@ -3569,7 +3600,7 @@ namespace jwt { + }); + } + }; +- ++#ifndef SGX_JWT + /** + * Create a verifier using the given clock + * \param c Clock instance to use +@@ -3596,7 +3627,17 @@ namespace jwt { + verifier verify(default_clock c = {}) { + return verifier(c); + } +- ++#else ++ /** ++ * Create a verifier using the given clock ++ * \param c Clock instance to use ++ * \return verifier instance ++ */ ++ template ++ verifier verify() { ++ return verifier(); ++ } ++#endif + /** + * Return a builder instance to create a new token + */ diff --git a/common/logging b/common/logging new file mode 120000 index 0000000..897f0d0 --- /dev/null +++ b/common/logging @@ -0,0 +1 @@ +_nologging \ No newline at end of file diff --git a/common/nlohmann/json b/common/nlohmann/json new file mode 160000 index 0000000..3780b41 --- /dev/null +++ b/common/nlohmann/json @@ -0,0 +1 @@ +Subproject commit 3780b41dd070436f3f55327b0a88f27a52e2dfa8 diff --git a/common/scripts/common_utils.sh b/common/scripts/common_utils.sh new file mode 100644 index 0000000..9742e52 --- /dev/null +++ b/common/scripts/common_utils.sh @@ -0,0 +1,85 @@ +# Copyright 2020 Intel Corporation +# +# SPDX-License-Identifier: Apache-2.0 + +if [[ -z "$TERM" ]] || [[ "$TERM" == 'dumb' ]]; then + # to avoid 'tput: No value for $TERM and no -T specified' errors .. + cred= + cgrn= + cblu= + cmag= + cwht= + cbld= + bred= + bgrn= + bblu= + bwht= + crst= +else + cred=$(tput setaf 1) + cgrn=$(tput setaf 2) + cblu=$(tput setaf 4) + cmag=$(tput setaf 5) + cwht=$(tput setaf 7) + cbld=$(tput bold) + bred=$(tput setab 1) + bgrn=$(tput setab 2) + bblu=$(tput setab 4) + bwht=$(tput setab 7) + crst=$(tput sgr0) +fi + +function recho () { + echo "${cbld}${cred}"$@"${crst}" >&2 +} + +function becho () { + echo "${cbld}${cblu}"$@"${crst}" >&2 +} + +function gecho () { + echo "${cbld}${cgrn}"$@"${crst}" >&2 +} + +# Common reporting functions: say, yell & die +#----------------------------------------- +# they all write to stderr. if you want normal progres for stdout, just use echo +function say () { + echo "$(basename $0): $*" >&2; +} + +function yell () { + becho "$(basename $0): $*" >&2; +} + +function die() { + recho "$(basename $0): $*" >&2 + exit 111 +} + +function para() { + echo -e "\n" +} + +# Common functions to run commands +#----------------------------------------- +try() { + "$@" || die "test failed: $*" +} + +try_fail() { + recho "failure expected next" + (! "$@") || die "rev-test failed: $*" +} + +# Variant of try which stores commands stdout and stderr (or only stdout) in variable RESPONSE +try_r() { + say "$@" + export RESPONSE=$("$@" 2>&1) RESPONSE_TYPE="out+err" || die "test failed: $*" +} + +try_out_r() { + say "$@" + export RESPONSE=$("$@") RESPONSE_TYPE="out" || die "test failed: $*" +} + diff --git a/common/sgx-support/clocale b/common/sgx-support/clocale new file mode 100644 index 0000000..3edcc8e --- /dev/null +++ b/common/sgx-support/clocale @@ -0,0 +1,59 @@ +/* + * Copyright 2023 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/*********** + +This is support code for SGX, mainly to keep the nlohmann json library unmodified. + +************/ + +#ifndef CLOCALE +#define CLOCALE + +typedef struct { + char *decimal_point; + char *thousands_sep; + char *grouping; + char *int_curr_symbol; + char *currency_symbol; + char *mon_decimal_point; + char *mon_thousands_sep; + char *mon_grouping; + char *positive_sign; + char *negative_sign; + char int_frac_digits; + char frac_digits; + char p_cs_precedes; + char p_sep_by_space; + char n_cs_precedes; + char n_sep_by_space; + char p_sign_posn; + char n_sign_posn; +} lconv; + +namespace std +{ + + using lconv = lconv; + static lconv l = + { + NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,0,0,0,0,0,0,0 + }; + + //char* setlocale(int category, const char* locale); + static lconv* localeconv() + { + return &l; + } + +} // std + +static lconv* localeconv() +{ + return std::localeconv(); +} + +#endif //CLOCALE diff --git a/common/types/hex_string.cpp b/common/types/hex_string.cpp new file mode 100644 index 0000000..78eff57 --- /dev/null +++ b/common/types/hex_string.cpp @@ -0,0 +1,156 @@ +/* Copyright 2018 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. + */ + +#include +#include +#include "hex_string.h" +#include "error.h" +#include "logging.h" + +// XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +static inline uint8_t HexToNibble(char hex, bool& ok) +{ + hex = toupper(hex); + + if (hex >= 'A' && hex <= 'F') { // A-F case + ok = true; + return 10 + (hex - 'A'); + } + if (hex >= '0' && hex <= '9') { // 0-9 case + ok = true; + return hex - '0'; + } + + LOG_ERROR("Hex digit is not valid: %c", hex); + ok = false; + return 0; +} // HexToNibble + +// XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +static inline uint8_t HexToByte(const char* inHexDigits, bool& ok) +{ + bool b1, b2; + uint8_t c1 = HexToNibble(inHexDigits[0], b1); + uint8_t c2 = HexToNibble(inHexDigits[1], b2); + COND2ERR(b1 == false || b2 == false); + + ok = true; + return (c1 << 4) | c2; + +err: + LOG_ERROR("HexToNibble: %c %c -> %u %u", inHexDigits[0], inHexDigits[1], c1, c2); + ok = false; + return 0; +} // HexToByte + +// XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +std::vector HexStringToBinary( + const std::string& inHexString + ) +{ + // Create a buffer to hold the binary data and use the array + // implementation to do the actual conversion + std::vector binaryData(inHexString.length() / 2); + COND2ERR(false == HexStringToBinary(&binaryData[0], binaryData.size(), inHexString)); + return binaryData; + +err: + LOG_ERROR("HexStringToBinary: str(%ld) %s to vect(%ld)", inHexString.length(), inHexString.c_str(), binaryData.size()); + binaryData.resize(0); + return binaryData; +} // HexStringToBinary + +// XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +bool HexStringToBinary( + uint8_t* outBinaryData, + size_t inBinaryDataLength, + const std::string& inHexString + ) +{ + // Verify that the hex string is an even length + COND2LOGERR((inHexString.length() % 2) != 0, "Hex encoded string is not an even length"); + + { + const char* pHex = inHexString.c_str(); + size_t len = inHexString.length(); + bool b; + inBinaryDataLength = std::min(inBinaryDataLength, len / 2); + size_t pos = 0; + size_t opos = 0; + while (pos < len && opos < inBinaryDataLength) { + outBinaryData[opos++] = HexToByte(&pHex[pos], b); + COND2ERR(b == false); + pos += 2; + } + } + + return true; + +err: + LOG_ERROR("HexStringToBinary: str(%ld) %s", inHexString.length(), inHexString.c_str()); + return false; +} // HexStringToBinary + +// XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +std::string BinaryToHexString( + const std::vector& inBinaryData + ) +{ + return BinaryToHexString(&inBinaryData[0], inBinaryData.size()); +} // BinaryToHexString + +// XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +std::string BinaryToHexString( + const uint8_t* inBinaryData, + size_t inBinaryDataLength + ) +{ + static const char *hexDigits = "0123456789ABCDEF"; + + // Create a string and give a hint to its final size (twice the size + // of the input binary data) + std::string hexString; + hexString.reserve(inBinaryDataLength * 2); + + // Run through the binary data and convert to a hex string + std::for_each( + inBinaryData, + inBinaryData + inBinaryDataLength, + [&hexString](uint8_t inputByte) { + hexString.push_back(hexDigits[inputByte >> 4]); + hexString.push_back(hexDigits[inputByte & 0x0F]); + }); + + return hexString; +} // BinaryToHexString + +// XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +bool IsHex(const uint8_t* inBinaryData, size_t inBinaryDataLength) +{ + for(int i=0; i +#include +#include + +// This macro calculates the length of the actual data portion of the +// hex-string encoding of a buffer with x bytes PLUS the additional byte +// needed for the string terminator. +#define HEX_STRING_SIZE(x) (static_cast(((x) * 2))) + +// Convert a hex string (i.e., a string of characters with values +// between '0'-'9', 'A'-'F') to an array of bytes +std::vector HexStringToBinary( + const std::string& inHexString + ); + +bool HexStringToBinary( + uint8_t* outBinaryData, + size_t inBinaryDataLength, + const std::string& inHexString + ); + +// Convert an array of bytes (represented as either a std::vector of +// bytes or a raw array) to a hex string. +std::string BinaryToHexString( + const std::vector& inBinaryData + ); +std::string BinaryToHexString( + const uint8_t* inBinaryData, + size_t inBinaryDataLength + ); + +bool IsHex(const uint8_t* inBinaryData, size_t inBinaryDataLength); +bool IsHex(const std::string s); + diff --git a/common/types/types.cpp b/common/types/types.cpp new file mode 100644 index 0000000..df66fdd --- /dev/null +++ b/common/types/types.cpp @@ -0,0 +1,85 @@ +/* Copyright 2018 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. + */ + +#include +#include +#include + +#include "types/types.h" +#include "base64/base64.h" +#include "types/hex_string.h" + +// XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +// Simple conversion from ByteArray to std::string +// XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +std::string ByteArrayToString(const ByteArray& inArray) +{ + std::string outString; + std::transform(inArray.begin(), inArray.end(), std::back_inserter(outString), + [](unsigned char c) -> char { return (char)c; }); + + return outString; +} + +// XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +// Conversion from byte array to string array +// XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +void ByteArrayToStringArray(const ByteArray& inByteArray, StringArray& outStringArray) +{ + outStringArray.resize(0); + std::transform(inByteArray.begin(), inByteArray.end(), std::back_inserter(outStringArray), + [](unsigned char c) -> char { return (char)c; }); +} + +StringArray ByteArrayToStringArray(const ByteArray& inByteArray) +{ + StringArray outStringArray(0); + ByteArrayToStringArray(inByteArray, outStringArray); + return outStringArray; +} + +// XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +// Simple conversion from ByteArray to Base64EncodedString +// XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +Base64EncodedString ByteArrayToBase64EncodedString(const ByteArray& buf) +{ + return base64_encode(buf.data(), buf.size()); +} // ByteArrayToBase64EncodedString + +// XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +// Simple conversion from Base64EncodedString to ByteArray +ByteArray Base64EncodedStringToByteArray(const Base64EncodedString& encoded) +{ + std::string s = base64_decode(encoded); + ByteArray b; + std::transform(s.begin(), s.end(), std::back_inserter(b), + [](unsigned char c) -> char { return (uint8_t)c; }); + return b; +} // Base64EncodedStringToByteArray + +// XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +// Simple conversion from ByteArray to HexEncodedString +HexEncodedString ByteArrayToHexEncodedString(const ByteArray& buf) +{ + return BinaryToHexString(buf); +} // ByteArrayToHexEncodedString + +// XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +// Simple conversion from HexEncodedString to ByteArray +// throws ValueError +ByteArray HexEncodedStringToByteArray(const HexEncodedString& encoded) +{ + return HexStringToBinary(encoded); +} // HexEncodedStringToByteArray diff --git a/common/types/types.h b/common/types/types.h new file mode 100644 index 0000000..168d695 --- /dev/null +++ b/common/types/types.h @@ -0,0 +1,76 @@ +/* Copyright 2018 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. + */ + +#pragma once + +#include +#include +#include +#include + +//*** For binary unformatted data***// +typedef std::vector ByteArray; + +//*** For vector containing printable characters ***// +class StringArray : public std::vector +{ +public: + StringArray(const std::string& value) + { + assign(value); + }; + + StringArray(const size_t size) + { + resize(size); + std::vector::assign(size, '\0'); + }; + + void assign(const std::string& value) + { + std::vector::assign(value.begin(), value.end()); + } + + std::string str() + { + return std::string(this->data()); + }; +}; /* class StringArray */ + +//*** For printable base64 encoded string***// +typedef std::string Base64EncodedString; + +//*** For printable hex encoded string***// +typedef std::string HexEncodedString; + +// Simple conversion from ByteArray to String +std::string ByteArrayToString(const ByteArray& inArray); + +// Conversion from byte array to string array +void ByteArrayToStringArray(const ByteArray& inArray, StringArray& outStringArray); +StringArray ByteArrayToStringArray(const ByteArray& inArray); + +// Simple conversion from ByteArray to Base64EncodedString +Base64EncodedString ByteArrayToBase64EncodedString(const ByteArray& buf); + +// Simple conversion from Base64EncodedString to ByteArray +ByteArray Base64EncodedStringToByteArray(const Base64EncodedString& encoded); + +// Simple conversion from ByteArray to HexEncodedString +HexEncodedString ByteArrayToHexEncodedString(const ByteArray& buf); + +// Simple conversion from HexEncodedString to ByteArray +// throws ValuenError +ByteArray HexEncodedStringToByteArray(const HexEncodedString& encoded); diff --git a/conversion/attestation_to_evidence.sh b/conversion/attestation_to_evidence.sh new file mode 100755 index 0000000..14abd22 --- /dev/null +++ b/conversion/attestation_to_evidence.sh @@ -0,0 +1,98 @@ +#!/bin/bash + +# Copyright 2020 Intel Corporation +# +# SPDX-License-Identifier: Apache-2.0 + +set -e + +if [[ -z "${OAA_PATH}" ]]; then + echo "OAA_PATH not set" + exit -1 +fi + +CUR_SCRIPT_PATH="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" + +. ${OAA_PATH}/common/scripts/common_utils.sh +. ${CUR_SCRIPT_PATH}/tag_to_variable.sh +. ${CUR_SCRIPT_PATH}/simulated/a2e.sh +. ${CUR_SCRIPT_PATH}/epid/a2e.sh +. ${CUR_SCRIPT_PATH}/dcap/a2e.sh +. ${CUR_SCRIPT_PATH}/dcap-direct/a2e.sh + +tag_to_variable "ATTESTATION_TYPE_TAG" +tag_to_variable "ATTESTATION_TAG" +tag_to_variable "EVIDENCE_TAG" +tag_to_variable "SIMULATED_TYPE_TAG" +tag_to_variable "EPID_LINKABLE_TYPE_TAG" +tag_to_variable "EPID_UNLINKABLE_TYPE_TAG" +tag_to_variable "DCAP_SGX_TYPE_TAG" +tag_to_variable "DCAP_DIRECT_SGX_TYPE_TAG" + +########################################################### +# attestation_to_evidence +# input: attestation as parameter +# output: EVIDENCE variable +# +# This is the main function for a2e conversion. +########################################################### +function attestation_to_evidence() { + if [[ -z "$1" ]]; then + die "no argument provided" + fi + + say "Input Attestation: $1" + + ATTESTATION_TYPE=$(echo $1 | jq ".$ATTESTATION_TYPE_TAG" -r) + ATTESTATION=$(echo $1 | jq ".$ATTESTATION_TAG" -r) + + case "$ATTESTATION_TYPE" in + $SIMULATED_TYPE_TAG) + simulated_to_evidence "$ATTESTATION" + EVIDENCE=$SIMULATED_EVIDENCE + ;; + + $EPID_LINKABLE_TYPE_TAG) + ;& + $EPID_UNLINKABLE_TYPE_TAG) + b64quote_to_iasresponse "$ATTESTATION" + iasresponse_to_evidence "$IAS_RESPONSE" + EVIDENCE=$IAS_EVIDENCE + ;; + $DCAP_SGX_TYPE_TAG) + b64quote_to_itaresponse "$ATTESTATION" + itaresponse_to_evidence "$ITA_RESPONSE" + EVIDENCE=$ITA_EVIDENCE + ;; + $DCAP_DIRECT_SGX_TYPE_TAG) + b64quote_to_evidence "$ATTESTATION" + EVIDENCE=$DCAP_EVIDENCE + ;; + *) + die "error attestation type $ATTESTATION_TYPE" + ;; + esac + + #package evidence + EVIDENCE=$(jq -c -n --arg attestation_type "$ATTESTATION_TYPE" --arg evidence "$EVIDENCE" '{'\"$ATTESTATION_TYPE_TAG\"': $attestation_type, '\"$EVIDENCE_TAG\"': $evidence}') + + say "Output Evidence: $EVIDENCE" +} + +########################################################### +# Main (if script is directly called rather than included in other script) +# +# - expects attestation is sole command-line parameter +# - on success, return evidence on stdout +# Note: evidence is terminated with newline, depending on use-case +# this might have to be trimmed by consumer +# +########################################################### +(return 0 2>/dev/null) && sourced=1 || sourced=0 +if [ $sourced -eq "0" ]; then # i'm directly executed and not sourced in other program + function say() { # suppress normal output ... + : + } + attestation_to_evidence $1 + echo "${EVIDENCE}" +fi diff --git a/conversion/dcap-direct/a2e.sh b/conversion/dcap-direct/a2e.sh new file mode 100644 index 0000000..c7f0906 --- /dev/null +++ b/conversion/dcap-direct/a2e.sh @@ -0,0 +1,25 @@ +#!/bin/bash + +# Copyright 2024 Intel Corporation +# +# SPDX-License-Identifier: Apache-2.0 + +set -ex + +CUR_SCRIPT_PATH="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" +B64A_2_B64C=${CUR_SCRIPT_PATH}/b64attestation_to_b64collateral + +. ${CUR_SCRIPT_PATH}/../tag_to_variable.sh + + + +function b64quote_to_evidence() { + QUOTE=$1 + COLLATERAL=$(${B64A_2_B64C} $QUOTE) + UNTRUSTED_TIME_T=$(date +%s) + tag_to_variable "ATTESTATION_TAG" + tag_to_variable "COLLATERAL_TAG" + tag_to_variable "UNTRUSTED_TIME_T_TAG" + + DCAP_EVIDENCE=$(jq -c -n --arg attestation "$QUOTE" --arg collateral "$COLLATERAL" --arg untrusted_time_t "$UNTRUSTED_TIME_T" '{'\"$ATTESTATION_TAG\"': $attestation, '\"$COLLATERAL_TAG\"': $collateral, '\"$UNTRUSTED_TIME_T_TAG\"': $untrusted_time_t}') +} diff --git a/conversion/dcap-direct/b64attestation2b64collateral.cpp b/conversion/dcap-direct/b64attestation2b64collateral.cpp new file mode 100644 index 0000000..0e513d7 --- /dev/null +++ b/conversion/dcap-direct/b64attestation2b64collateral.cpp @@ -0,0 +1,101 @@ +/* + * Copyright 2024 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include "types/types.h" +#include "base64/base64.h" +#include "sgx_ql_lib_common.h" +#include "sgx_dcap_quoteverify.h" + +uint8_t* serialize_collateral(sgx_ql_qve_collateral_t* c) +{ + uint32_t collateral_size = sizeof(sgx_ql_qve_collateral_t) + c->pck_crl_issuer_chain_size + c->root_ca_crl_size + c->pck_crl_size + c->tcb_info_issuer_chain_size + c->tcb_info_size + c->qe_identity_issuer_chain_size + c->qe_identity_size; + fprintf(stderr, "calculated collateral size: %d\n", collateral_size); + + uint8_t* porig = (uint8_t*)malloc(collateral_size); + uint8_t* p = porig; + if(p == NULL) return NULL; + memset(p, '\0', collateral_size); + + // NOTICE / WARNING: here we copy the entire data structure with "meaningless" pointers + // pointers will have to be adjusted at deserialization time + memcpy(p, (uint8_t*)c, sizeof(sgx_ql_qve_collateral_t)); + p += sizeof(sgx_ql_qve_collateral_t); + + memcpy(p, c->pck_crl_issuer_chain, c->pck_crl_issuer_chain_size); + p += c->pck_crl_issuer_chain_size; + + memcpy(p, c->root_ca_crl, c->root_ca_crl_size); + p += c->root_ca_crl_size; + + memcpy(p, c->pck_crl, c->pck_crl_size); + p += c->pck_crl_size; + + memcpy(p, c->tcb_info_issuer_chain, c->tcb_info_issuer_chain_size); + p += c->tcb_info_issuer_chain_size; + + memcpy(p, c->tcb_info, c->tcb_info_size); + p += c->tcb_info_size; + + memcpy(p, c->qe_identity_issuer_chain, c->qe_identity_issuer_chain_size); + p += c->qe_identity_issuer_chain_size; + + memcpy(p, c->qe_identity, c->qe_identity_size); + + return porig; +} + + +int main(int argc, char** argv) +{ + if(argc != 2) + { + printf("Usage: %s \n", argv[0]); + return -1; + } + + std::string s = base64_decode(argv[1]); + ByteArray attestation; + std::transform(s.begin(), s.end(), std::back_inserter(attestation), + [](unsigned char c) -> char { return (uint8_t)c; }); + + + uint8_t* p_collateral=NULL; + uint32_t collateral_size=0; + quote3_error_t ret = tee_qv_get_collateral(attestation.data(), attestation.size(), &p_collateral, &collateral_size); + if(ret != SGX_QL_SUCCESS) + { + printf("error getting collateral: %x\n", ret); + return -1; + } + + uint8_t* serialized_collateral = serialize_collateral((sgx_ql_qve_collateral_t*)p_collateral); + if(serialized_collateral == NULL) + { + printf("error allocating collateral"); + return -1; + } + std::string b64collateral = base64_encode((const unsigned char*)serialized_collateral, collateral_size); + puts(b64collateral.c_str()); + + fprintf(stderr, "[DEBUG] collateral major version %hu minor version %hu\n", + ((sgx_ql_qve_collateral_t*)p_collateral)->major_version, + ((sgx_ql_qve_collateral_t*)p_collateral)->minor_version); + fprintf(stderr, "[DEBUG] collateral size: %d\n", collateral_size); + fprintf(stderr, "[DEBUG] b64collateral size: %ld\n", b64collateral.length()); + + ret = tee_qv_free_collateral(p_collateral); + if(ret != SGX_QL_SUCCESS) + { + printf("error freeing collateral: %x\n", ret); + return -1; + } + free(serialized_collateral); + + return 0; +} diff --git a/conversion/dcap/a2e.sh b/conversion/dcap/a2e.sh new file mode 100755 index 0000000..8f88c78 --- /dev/null +++ b/conversion/dcap/a2e.sh @@ -0,0 +1,53 @@ +#!/bin/bash + +# Copyright 2023 Intel Corporation +# +# SPDX-License-Identifier: Apache-2.0 + +set -ex + +CUR_SCRIPT_PATH="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" +. ${CUR_SCRIPT_PATH}/../tag_to_variable.sh + +function check_collateral_folder() +{ + if [[ -z "${COLLATERAL_FOLDER}" ]]; then + echo "COLLATERAL_FOLDER not set for DCAP conversion" + exit -1 + fi +} + +########################################################### +# b64quote_to_iasresponse +# input: quote as parameter +# output: ITA_RESPONSE variable +########################################################### +function b64quote_to_itaresponse() { + check_collateral_folder + + tag_to_variable "QUOTE_TAG" + + #get api key + API_KEY_FILEPATH="${COLLATERAL_FOLDER}/ita_api_key.txt" + test -f ${API_KEY_FILEPATH} || die "no api key file ${API_KEY_FILEPATH}" + API_KEY=$(cat $API_KEY_FILEPATH) + + #get verification report + QUOTE=$1 + # contact IAS to get the verification report + ITA_RESPONSE=$(curl -s -H "Accept: application/json" -H "Content-Type: application/json" -H "x-api-key:$API_KEY" -X POST -d '{"quote":"'$QUOTE'"}' https://api-poc-user1.project-amber-smas.com/appraisal/v1/attest ) +} + +########################################################### +# itaresponse_to_evidence +# input: ita response as parameter +# output: ITA_EVIDENCE variable +########################################################### +function itaresponse_to_evidence() { + tag_to_variable "ITA_TOKEN_TAG" + ITA_RESPONSE="$1" + ITA_TOKEN=$(echo $ITA_RESPONSE | jq ".token" -r) + JSON_ITA_RESPONSE=$(jq -c -n --arg tok "$ITA_TOKEN" '{'\"$ITA_TOKEN_TAG\"': $tok}') + #set output + ITA_EVIDENCE=$JSON_ITA_RESPONSE +} diff --git a/conversion/define_to_variable.sh b/conversion/define_to_variable.sh new file mode 100755 index 0000000..5f7bc12 --- /dev/null +++ b/conversion/define_to_variable.sh @@ -0,0 +1,18 @@ +# Copyright 2020 Intel Corporation +# +# SPDX-License-Identifier: Apache-2.0 + +set -e + +########################################################### +# define_to_variable +# input: file with C #define, #define string as parameters +# output: variable named #define string +########################################################### +function define_to_variable() { + if [[ ! -f $1 ]]; then + echo "no file $1 to extract define" + exit -1 + fi + printf -v $2 "$(awk '/.*#define.* '$2' / { print $3 }' < $1 | sed 's/"//g')" +} diff --git a/conversion/enclave_to_mrenclave.sh b/conversion/enclave_to_mrenclave.sh new file mode 100755 index 0000000..1746d26 --- /dev/null +++ b/conversion/enclave_to_mrenclave.sh @@ -0,0 +1,29 @@ +#!/bin/bash + +# Copyright 2020 Intel Corporation +# +# SPDX-License-Identifier: Apache-2.0 + +set -e + +########################################################### +# enclave_to_mrenclave +# input: non-signed enclave file path, enclave configuration file as parameters +# output: MRENCLAVE variable +########################################################### +function enclave_to_mrenclave() { + if [[ ! -f $1 ]]; then + echo "missing enclave file path" + exit -1 + fi + if [[ ! -f $2 ]]; then + echo "missing enclave configuration file path" + exit -1 + fi + + TMP1=$(mktemp) + TMP2=$(mktemp) + sgx_sign gendata -enclave $1 -config $2 -out $TMP1 + dd if=$TMP1 bs=1 skip=188 of=$TMP2 count=32 + MRENCLAVE=$(hex -c $TMP2) +} diff --git a/conversion/epid/a2e.sh b/conversion/epid/a2e.sh new file mode 100755 index 0000000..aa91906 --- /dev/null +++ b/conversion/epid/a2e.sh @@ -0,0 +1,59 @@ +#!/bin/bash + +# Copyright 2020 Intel Corporation +# +# SPDX-License-Identifier: Apache-2.0 + +set -ex + +function check_collateral_folder() +{ + if [[ -z "${COLLATERAL_FOLDER}" ]]; then + echo "COLLATERAL_FOLDER not set for EPID conversion" + exit -1 + fi +} + +########################################################### +# b64quote_to_iasresponse +# input: quote as parameter +# output: IAS_RESPONSE variable +########################################################### +function b64quote_to_iasresponse() { + check_collateral_folder + + #get api key + API_KEY_FILEPATH="${COLLATERAL_FOLDER}/api_key.txt" + test -f ${API_KEY_FILEPATH} || die "no api key file ${API_KEY_FILEPATH}" + API_KEY=$(cat $API_KEY_FILEPATH) + + #get verification report + QUOTE=$1 + # contact IAS to get the verification report + IAS_RESPONSE=$(curl -s -H "Content-Type: application/json" -H "Ocp-Apim-Subscription-Key:$API_KEY" -X POST -d '{"isvEnclaveQuote":"'$QUOTE'"}' https://api.trustedservices.intel.com/sgx/dev/attestation/v4/report -i) + # check status (as there may be multiple header, we rather check presence of relevant fields) + echo "$IAS_RESPONSE" | grep "X-IASReport-Signature" >/dev/null || die "IAS Response error" + echo "$IAS_RESPONSE" | grep "X-IASReport-Signing-Certificate" >/dev/null || die "IAS Response error" +} + +########################################################### +# iasresponse_to_evidence +# input: ias response as parameter +# output: IAS_EVIDENCE variable +########################################################### +function iasresponse_to_evidence() { + IAS_RESPONSE="$1" + UNTRUSTED_TIME_T=$(date +%s) + tag_to_variable "IAS_SIGNATURE_TAG" + tag_to_variable "IAS_CERTIFICATES_TAG" + tag_to_variable "IAS_REPORT_TAG" + tag_to_variable "UNTRUSTED_TIME_T_TAG" + + #encode relevant info in json format + IAS_SIGNATURE=$(echo "$IAS_RESPONSE" | grep -Po 'X-IASReport-Signature: \K[^ ]+' | tr -d '\r') + IAS_CERTIFICATES=$(echo "$IAS_RESPONSE" | grep -Po 'X-IASReport-Signing-Certificate: \K[^ ]+') + IAS_REPORT=$(echo "$IAS_RESPONSE" | grep -Po '{"id":[^ ]+') + JSON_IAS_RESPONSE=$(jq -c -n --arg sig "$IAS_SIGNATURE" --arg cer "$IAS_CERTIFICATES" --arg rep "$IAS_REPORT" --arg untrusted_time_t "$UNTRUSTED_TIME_T" '{'\"$IAS_SIGNATURE_TAG\"': $sig, '\"$IAS_CERTIFICATES_TAG\"': $cer, '\"$IAS_REPORT_TAG\"': $rep, '\"$UNTRUSTED_TIME_T_TAG\"': $untrusted_time_t}') + #set output + IAS_EVIDENCE=$JSON_IAS_RESPONSE +} diff --git a/conversion/simulated/a2e.sh b/conversion/simulated/a2e.sh new file mode 100755 index 0000000..6a87870 --- /dev/null +++ b/conversion/simulated/a2e.sh @@ -0,0 +1,16 @@ +#!/bin/bash + +# Copyright 2020 Intel Corporation +# +# SPDX-License-Identifier: Apache-2.0 + +set -e + +########################################################### +# simulated_to_evidence +# input: evidence as parameter +# output: SIMULATED_EVIDENCE variable +########################################################### +function simulated_to_evidence() { + SIMULATED_EVIDENCE=$1 +} diff --git a/conversion/tag_to_variable.sh b/conversion/tag_to_variable.sh new file mode 100644 index 0000000..d843eee --- /dev/null +++ b/conversion/tag_to_variable.sh @@ -0,0 +1,27 @@ +#!/bin/bash + +# Copyright 2024 Intel Corporation +# +# SPDX-License-Identifier: Apache-2.0 + +set -e + +if [[ -z "${OAA_PATH}" ]]; then + echo "OAA_PATH not set" + exit -1 +fi + +CUR_SCRIPT_PATH="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" + +. ${CUR_SCRIPT_PATH}/define_to_variable.sh + +########################################################### +# get_tag_make_variable +# input: tag string (e.g, "TAG_X") as parameter +# output: tag string variable (e.g., TAG_X) +########################################################### +function tag_to_variable() { + TAGS_PATH="${OAA_PATH}/include/attestation_tags.h" + define_to_variable "$TAGS_PATH" "$1" +} + diff --git a/docker/Dockerfile b/docker/Dockerfile new file mode 100644 index 0000000..3b30a45 --- /dev/null +++ b/docker/Dockerfile @@ -0,0 +1,146 @@ +# ------------------------------------------------------------------------------ +# 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. +# ------------------------------------------------------------------------------ + +FROM ubuntu:22.04 +ARG UBUNTU_VERSION=22.04 +ARG UBUNTU_NAME=jammy + +ENV TERM=screen-256color + +RUN apt-get update \ + && apt-get install -y -q \ + build-essential \ + debhelper \ + git \ + libcurl4-openssl-dev \ + pkgconf \ + python-is-python3 \ + wget \ + zip + +RUN echo "deb [arch=amd64] https://download.01.org/intel-sgx/sgx_repo/ubuntu ${UBUNTU_NAME} main" | tee /etc/apt/sources.list.d/intel-sgx.list +RUN cat /etc/apt/sources.list.d/intel-sgx.list +RUN wget -qO - https://download.01.org/intel-sgx/sgx_repo/ubuntu/intel-sgx-deb.key | apt-key add + +ENV DEBIAN_FRONTEND="noninteractive" +RUN apt-get update +RUN apt-get install -y -q \ + libboost-dev \ + libboost-system-dev \ + libboost-thread-dev \ + protobuf-c-compiler \ + libprotobuf-c-dev \ + protobuf-compiler + +ARG SGX=2.25 +ARG OPENSSL=3.0.14 +ARG SGXSSL=3.0_Rev4 + +RUN apt-get install -y \ + # We do not need daemons like AESMD as we run them on host (side-steps also + # issues with config of /etc/aesmd.conf like proxy ..). Without this option + # aesmd and lots of other plugsin are automatically pulled in. + # See SGX Installation notes and, in particular, linux/installer/docker/Dockerfile + # in linux-sgx git repo of sdk/psw source. + --no-install-recommends \ + libsgx-urts \ + libsgx-uae-service \ + libsgx-dcap-ql-dev \ + libsgx-dcap-quote-verify-dev + +# Install SGX SDK +WORKDIR /opt/intel +RUN SGX_SDK_BIN_REPO=https://download.01.org/intel-sgx/sgx-linux/${SGX}/distro/ubuntu${UBUNTU_VERSION}-server \ + && SGX_SDK_BIN_FILE=$(wget -P /tmp --delete-after --spider --recursive --level=1 --no-parent ${SGX_SDK_BIN_REPO} 2>&1 | perl -ne 'if (m|'${SGX_SDK_BIN_REPO}'/(sgx_linux_x64_sdk.*)|) { print "$1\n"; }') \ + && wget -q -P /tmp ${SGX_SDK_BIN_REPO}/${SGX_SDK_BIN_FILE} \ + && chmod +x /tmp/${SGX_SDK_BIN_FILE} \ + && echo -e "no\n/opt/intel" | /tmp/${SGX_SDK_BIN_FILE} \ + && rm /tmp/${SGX_SDK_BIN_FILE} + +ENV SGX_SDK=/opt/intel/sgxsdk + +# ----------------------------------------------------------------- +# LVI mitigations, needed to compile sgxssl, requires a +# recent version of binutils (>= 2.32). Ubuntu 18.04 only +# has 2.30 but Intel ships binary distro for 2.32.51.20190719 +# ----------------------------------------------------------------- +WORKDIR /opt/intel +RUN SGX_SDK_BINUTILS_REPO=https://download.01.org/intel-sgx/sgx-linux/${SGX} \ + && SGX_SDK_BINUTILS_FILE=$(wget -P /tmp --delete-after --spider --recursive --level=1 --no-parent ${SGX_SDK_BINUTILS_REPO} 2>&1 | perl -ne 'if (m|'${SGX_SDK_BINUTILS_REPO}'/(as.ld.objdump.*)|) { print "$1\n"; }') \ + && wget -q -P /tmp ${SGX_SDK_BINUTILS_REPO}/${SGX_SDK_BINUTILS_FILE} \ + && mkdir sgxsdk.extras \ + && cd sgxsdk.extras \ + && tar -zxf /tmp/${SGX_SDK_BINUTILS_FILE} \ + && rm /tmp/${SGX_SDK_BINUTILS_FILE} + +ENV PATH="/opt/intel/sgxsdk.extras/external/toolset/ubuntu${UBUNTU_VERSION}:${PATH}" + +# ----------------------------------------------------------------- +# SGXSSL +# 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 \ + && 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 + +ENV SGX_SSL="/opt/intel/sgxssl" + +# ----------------------------------------------------------------- +# SGX DCAP Primitives +# ----------------------------------------------------------------- +RUN apt-get update +RUN apt-get install -y \ + basez \ + clang \ + cmake \ + curl \ + libsgx-dcap-default-qpl \ + #libsgx-dcap-default-qpl-dev adds libdcap_quoteprov.so and /usr/include/sgx_default_quote_provider.h + libsgx-dcap-default-qpl-dev \ + jq \ + libssl-dev \ + vim + +ARG DCAP=1.19 +ENV DCAP_PRIMITIVES=/tmp/SGXDataCenterAttestationPrimitives + +RUN git clone https://github.com/intel/SGXDataCenterAttestationPrimitives.git ${DCAP_PRIMITIVES} \ + && cd ${DCAP_PRIMITIVES}/QuoteVerification \ + && git checkout DCAP_${DCAP} \ + && git submodule update --init --recursive + +RUN cd ${DCAP_PRIMITIVES}/QuoteGeneration \ + && ./download_prebuilt.sh \ + && make GEN_STATIC=1 + +RUN cd ${DCAP_PRIMITIVES}/QuoteVerification/QVL/Src \ + && ./release -DBUILD_ENCLAVE=ON -DBUILD_TESTS=OFF ; ./release -DBUILD_ENCLAVE=ON -DBUILD_ATTESTATION_APP=OFF -DBUILD_TESTS=OFF + +RUN echo '{\n\ + "pccs_url": "https://localhost:8081/sgx/certification/v4/", \n\ + "collateral_service": "https://api.trustedservices.intel.com/sgx/certification/v4/",\n\ + "use_secure_cert": false\n\ + }' > /etc/sgx_default_qcnl.conf + +WORKDIR /project diff --git a/docker/Makefile b/docker/Makefile new file mode 100644 index 0000000..cb63b2c --- /dev/null +++ b/docker/Makefile @@ -0,0 +1,9 @@ +# Copyright 2023 Intel Corporation +# +# SPDX-License-Identifier: Apache-2.0 + +DOCKER_BUILD_ARGS= + +build: + docker build ${DOCKER_BUILD_ARGS} --tag oaa-dev . + diff --git a/docker/container/README.md b/docker/container/README.md new file mode 100644 index 0000000..416f507 --- /dev/null +++ b/docker/container/README.md @@ -0,0 +1,3 @@ +This directory contains scripts to set up the attestation API dependencies in the docker container. + +The scripts are obviously meant to be run before the attestation API build and, possibly, during the docker build. diff --git a/docker/container/setup.sh b/docker/container/setup.sh new file mode 100755 index 0000000..e34eb27 --- /dev/null +++ b/docker/container/setup.sh @@ -0,0 +1,71 @@ +#!/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. + +set -e + +apt-get update + +# install quote library, necessary for dcap attestation generation +# install quote verify library, necessary for attestation conversion and verification +# (though inside the enclave we use the static libraries of DCAP) +apt-get install -y \ + --no-install-recommends \ + libsgx-dcap-ql-dev \ + libsgx-dcap-quote-verify-dev + +# ----------------------------------------------------------------- +# SGX DCAP Primitives +# ----------------------------------------------------------------- +apt-get install -y -q \ + libboost-dev \ + libboost-system-dev \ + libboost-thread-dev \ + protobuf-c-compiler \ + libprotobuf-c-dev \ + protobuf-compiler + +# Note: libsgx-dcap-default-qpl-dev adds libdcap_quoteprov.so and /usr/include/sgx_default_quote_provider.h +apt-get install -y \ + basez \ + clang \ + libsgx-dcap-default-qpl \ + libsgx-dcap-default-qpl-dev \ + jq + +export DCAP=1.22 + +git clone https://github.com/intel/SGXDataCenterAttestationPrimitives.git ${DCAP_PRIMITIVES} \ + && cd ${DCAP_PRIMITIVES}/QuoteVerification \ + && git checkout DCAP_${DCAP} \ + && git submodule update --init --recursive + +cd ${DCAP_PRIMITIVES}/QuoteGeneration \ + && ./download_prebuilt.sh \ + && make GEN_STATIC=1 + +# NOTE: below the build (./release) is run twice. Unfortunately, this is necessary because both builds fails +# when run separately in a clean environment, but succeed if they run in sequence, and produce the expected result. +# This issue has been communicated to the developers of the DCAP primitives. +cd ${DCAP_PRIMITIVES}/QuoteVerification/QVL/Src +./release -DBUILD_ENCLAVE=ON -DBUILD_TESTS=OFF || true +./release -DBUILD_ENCLAVE=ON -DBUILD_ATTESTATION_APP=OFF -DBUILD_TESTS=OFF + +# set up the qcnl to connect to the local pccs for dcap verification collateral +echo '{\n\ + "pccs_url": "https://localhost:8081/sgx/certification/v4/", \n\ + "collateral_service": "https://api.trustedservices.intel.com/sgx/certification/v4/",\n\ + "use_secure_cert": false\n\ + }' > /etc/sgx_default_qcnl.conf + diff --git a/evidence/verify-dcap-direct-evidence.cpp b/evidence/verify-dcap-direct-evidence.cpp new file mode 100644 index 0000000..bacb6a2 --- /dev/null +++ b/evidence/verify-dcap-direct-evidence.cpp @@ -0,0 +1,269 @@ +/* + * Copyright 2024 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include +#include "types/types.h" +#include "types/hex_string.h" +#include "error.h" +#include "logging.h" +#include "crypto/sha256.h" +#include "crypto/verify_dcap_direct/get_dcap_certificate.h" +#include "base64/base64.h" +#include "attestation_tags.h" + +#include "sgx_quote_3.h" +#include "SgxEcdsaAttestation/QuoteVerification.h" +#include "QuoteGeneration/quote_wrapper/common/inc/sgx_ql_lib_common.h" +#include "CertVerification/CertificateChain.h" + +// < JSON include +#define JSON_HAS_CPP_11 +#define JSON_NO_IO 1 +#include +#include "nlohmann/json.hpp" +using json = nlohmann::json; +// JSON include > + +#include "PckParser/CrlStore.h" +#include "../../AttestationCommons/include/Utils/TimeUtils.h" + +/******************************************************** + * Internal function to modify "in-place" the collateral + * data structure, so that pointers point to buffers + * appended to the structure + *******************************************************/ +void deserialize_collateral(uint8_t* p) +{ + sgx_ql_qve_collateral_t* c = (sgx_ql_qve_collateral_t*)p; + + c->pck_crl_issuer_chain = (char*)p + sizeof(sgx_ql_qve_collateral_t); + c->root_ca_crl = (char*)c->pck_crl_issuer_chain + c->pck_crl_issuer_chain_size; + c->pck_crl = (char*)c->root_ca_crl + c->root_ca_crl_size; + c->tcb_info_issuer_chain = (char*)c->pck_crl + c->pck_crl_size; + c->tcb_info = (char*)c->tcb_info_issuer_chain + c->tcb_info_issuer_chain_size; + c->qe_identity_issuer_chain = (char*)c->tcb_info + c->tcb_info_size; + c->qe_identity = (char*)c->qe_identity_issuer_chain + c->qe_identity_issuer_chain_size; +} + + +bool verify_dcap_direct_evidence(ByteArray& evidence, ByteArray& expected_statement, ByteArray& expected_code_id) +{ + ByteArray quote; + ByteArray collateral; + time_t untrusted_time; + bool b; + Status qvl_status; + sgx_ql_qve_collateral_t* p_collateral; + + { + //parse evidence + json root; + std::string evidence_str((char*)evidence.data(), evidence.size()); + LOG_DEBUG("evidence: %s", evidence_str.c_str()); + CATCH(b, root = json::parse(evidence_str)); + COND2LOGERR(!b, "bad dcap evidence json"); + + //get attestation/quote + std::string b64attestation_str; + std::string attestation_str; + CATCH(b, b64attestation_str = root[ATTESTATION_TAG].template get()); + COND2LOGERR(!b, "no attestation for dcap direct verification"); + attestation_str = base64_decode(b64attestation_str); + std::transform(attestation_str.begin(), attestation_str.end(), + std::back_inserter(quote), + [](unsigned char c) -> char { return (uint8_t)c; }); + + //get collateral + std::string b64collateral_str; + std::string collateral_str; + CATCH(b, b64collateral_str = root[COLLATERAL_TAG].template get()); + COND2LOGERR(!b, "no collateral for dcap direct verification"); + collateral_str = base64_decode(b64collateral_str); + std::transform(collateral_str.begin(), collateral_str.end(), + std::back_inserter(collateral), + [](unsigned char c) -> char { return (uint8_t)c; }); + //adjust the collateral structure pointers + deserialize_collateral(collateral.data()); + p_collateral = (sgx_ql_qve_collateral_t*)collateral.data(); + LOG_DEBUG("collateral version: %u\n", p_collateral->version); + LOG_DEBUG("collateral major %hu minor %hu\n", + p_collateral->major_version, p_collateral->minor_version); + LOG_DEBUG("collateral size %ld\n", collateral.size()); + + //get untrusted time for verification + unsigned long long timeull; + std::string time_str; + CATCH(b, time_str = root[UNTRUSTED_TIME_T_TAG].template get()); + COND2LOGERR(!b, "no untrusted-time for dcap direct verification"); + timeull = std::stoull(time_str); + COND2LOGERR(sizeof(timeull) != sizeof(time_t), "error: ull and time_t have different sizes" ); + untrusted_time = *((time_t*)(&timeull)); + LOG_DEBUG("untrusted time str: %s\n", time_str.c_str()); + LOG_DEBUG("untrusted time ull: %llu\n", timeull); + } + + //verify quote + { + ByteArray certification_data; + uint32_t certification_data_size; + uint16_t certification_data_type; + + qvl_status = sgxAttestationGetQECertificationDataSize(quote.data(), quote.size(), &certification_data_size); + COND2LOGERR(qvl_status != STATUS_OK, + "error certification data size: %x", qvl_status); + + certification_data.resize(certification_data_size); + + qvl_status = sgxAttestationGetQECertificationData( + quote.data(), quote.size(), + certification_data.size(), certification_data.data(), + &certification_data_type); + COND2LOGERR(qvl_status != STATUS_OK, + "error certification data: %x", qvl_status); + + std::string dcap_ca_certificate; + get_dcap_certificate(dcap_ca_certificate); + std::string certification_data_str(certification_data.begin(), certification_data.end()); + std::string root_ca_crl_str; + std::string pck_crl_str; + + // we check up to the last but one char (because, if hex form, it could be a space) + if(IsHex((const uint8_t*)p_collateral->root_ca_crl, p_collateral->root_ca_crl_size-1)) + { + root_ca_crl_str = + std::string(p_collateral->root_ca_crl, p_collateral->root_ca_crl_size); + } + else + { + char c = p_collateral->root_ca_crl[p_collateral->root_ca_crl_size-1]; + uint32_t size = p_collateral->root_ca_crl_size; + size -= (std::isspace(c) ? 1 : 0); + root_ca_crl_str = BinaryToHexString((const uint8_t*)p_collateral->root_ca_crl, size); + if(p_collateral->major_version == 3 && p_collateral->minor_version == 0) + LOG_WARNING("WARNING: root ca crl is not hex but collateral version is 3.0 (so it should be)\n"); + } + + // we check up to the last but one char (because, if hex form, it could be a space) + if(IsHex((const uint8_t*)p_collateral->pck_crl, p_collateral->pck_crl_size-1)) + { + pck_crl_str = + std::string(p_collateral->pck_crl, p_collateral->pck_crl_size); + } + else + { + char c = p_collateral->pck_crl[p_collateral->pck_crl_size-1]; + uint32_t size = p_collateral->pck_crl_size; + size -= (std::isspace(c) ? 1 : 0); + pck_crl_str = BinaryToHexString((const uint8_t*)p_collateral->pck_crl, p_collateral->pck_crl_size); + if(p_collateral->major_version == 3 && p_collateral->minor_version == 0) + LOG_WARNING("WARNING: pck crl is not hex but collateral version is 3.0 (so it should be)\n"); + } + + const std::array crls{{ + root_ca_crl_str.c_str(), pck_crl_str.c_str()}}; + + LOG_DEBUG("dcap_ca_certificate: %s\n", dcap_ca_certificate.c_str()); + LOG_DEBUG("Certification data type: %hu\n", certification_data_type); + LOG_DEBUG("Certification data: %s\n", certification_data_str.c_str()); + LOG_DEBUG("root_ca_crl_str (%ld): %s\n", root_ca_crl_str.length(), root_ca_crl_str.c_str()); + LOG_DEBUG("pck_crl_str(%ld): %s\n", pck_crl_str.length(), pck_crl_str.c_str()); + + { + // debug CRLS + time_t currentTime = intel::sgx::dcap::getCurrentTime((const time_t*)&untrusted_time); + intel::sgx::dcap::pckparser::CrlStore rootCaCrl; + intel::sgx::dcap::pckparser::CrlStore intermediateCrl; + COND2LOGERR(!rootCaCrl.parse(crls[0]), "error parsing rootcacrsl"); + COND2LOGERR(!intermediateCrl.parse(crls[1]), "error parsing intermediateCrl"); + LOG_DEBUG("rootCaCrl.getValidity().notBeforeTime: %llu\n", *((unsigned long long*)(&(rootCaCrl.getValidity().notBeforeTime)))); + LOG_DEBUG("rootCaCrl.getValidity().notAfterTime: %llu\n", *((unsigned long long*)(&(rootCaCrl.getValidity().notAfterTime)))); + LOG_DEBUG("currentTime: %llu\n", *((unsigned long long*)¤tTime)); + + LOG_DEBUG("intermediateCrl.getValidity().notBeforeTime: %llu\n", *((unsigned long long*)(&(intermediateCrl.getValidity().notBeforeTime)))); + LOG_DEBUG("intermediateCrl.getValidity().notAfterTime: %llu\n", *((unsigned long long*)(&(intermediateCrl.getValidity().notAfterTime)))); + } + + qvl_status = sgxAttestationVerifyPCKCertificate( + certification_data_str.c_str(), + crls.data(), + dcap_ca_certificate.c_str(), + &untrusted_time); + COND2LOGERR(qvl_status != STATUS_OK, + "error PCK certificate verification: %x", qvl_status); + + qvl_status = sgxAttestationVerifyTCBInfo( + p_collateral->tcb_info, + p_collateral->tcb_info_issuer_chain, + crls[0], + dcap_ca_certificate.c_str(), + &untrusted_time); + COND2LOGERR(qvl_status != STATUS_OK, + "error TCB info verification: %x", qvl_status); + + qvl_status = sgxAttestationVerifyEnclaveIdentity( + p_collateral->qe_identity, + p_collateral->qe_identity_issuer_chain, + crls[0], + dcap_ca_certificate.c_str(), + &untrusted_time); + COND2LOGERR(qvl_status != STATUS_OK, + "error QE identity verification: %x", qvl_status); + + intel::sgx::dcap::CertificateChain chain; + qvl_status = chain.parse((const char*)certification_data.data()); + COND2LOGERR(qvl_status != STATUS_OK, + "error parsing certification data: %x", qvl_status); + + qvl_status = sgxAttestationVerifyQuote( + quote.data(), + quote.size(), + chain.getPckCert()->getPem().c_str(), + crls[1], + p_collateral->tcb_info, + p_collateral->qe_identity); + COND2LOGERR( + //these checks are mostly the verification policy + //TODO: define and implement better strategy for verification + qvl_status != STATUS_OK && + qvl_status != STATUS_TCB_OUT_OF_DATE, + "error quote verification: %x", qvl_status); + } + + // verify code id and statement + { + //prepare code id + sgx_quote3_t* q = (sgx_quote3_t*)quote.data(); + std::string code_id = BinaryToHexString((const uint8_t*)&q->report_body.mr_enclave, sizeof(sgx_measurement_t)); + std::string expected_hex_id((char*)expected_code_id.data(), expected_code_id.size()); + LOG_DEBUG("quote version: %hu\n", q->header.version); + LOG_DEBUG("Code id in quote: %s\n", code_id.c_str()); + LOG_DEBUG("Expected code id: %s\n", expected_hex_id.c_str()); + + //prepare statements + std::string hex_report_data = BinaryToHexString((const uint8_t*)&q->report_body.report_data, sizeof(sgx_report_data_t)); + ByteArray hash; + COND2ERR(false == SHA256(expected_statement, hash)); + std::string expected_hex_report_data_str = ByteArrayToHexEncodedString(hash); + expected_hex_report_data_str.append(expected_hex_report_data_str.length(), '0'); + LOG_DEBUG("report data: %s\n", hex_report_data.c_str()); + LOG_DEBUG("expected report data: %s\n", expected_hex_report_data_str.c_str()); + + // code check + COND2LOGERR(0 != code_id.compare(expected_hex_id), "expected code id mismatch"); + + //statement check + COND2LOGERR(0 != hex_report_data.compare(expected_hex_report_data_str), "expected statement mismatch"); + } + + return true; + +err: + return false; +} diff --git a/evidence/verify-dcap-direct-evidence.h b/evidence/verify-dcap-direct-evidence.h new file mode 100644 index 0000000..03174ce --- /dev/null +++ b/evidence/verify-dcap-direct-evidence.h @@ -0,0 +1,7 @@ +/* + * Copyright 2024 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +bool verify_dcap_direct_evidence(ByteArray& evidence, ByteArray& expected_statement, ByteArray& expected_code_id); diff --git a/evidence/verify-dcap-evidence.cpp b/evidence/verify-dcap-evidence.cpp new file mode 100644 index 0000000..de73092 --- /dev/null +++ b/evidence/verify-dcap-evidence.cpp @@ -0,0 +1,104 @@ +/* + * Copyright 2023 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include "types/types.h" +#include "crypto/verify_ita_token/verify-token.h" +#include "error.h" +#include "logging.h" +#include "crypto/sha256.h" +#include "attestation_tags.h" + +// < JSON include +#define JSON_HAS_CPP_11 +#define JSON_NO_IO 1 +#include +#include "nlohmann/json.hpp" +using json = nlohmann::json; +// JSON include > + + +bool verify_dcap_evidence(ByteArray& evidence, ByteArray& expected_statement, ByteArray& expected_code_id) +{ + std::string evidence_str((char*)evidence.data(), evidence.size()); + std::string ita_token_str; + std::string ita_payload_str; + verify_status_t vs; + bool b; + + LOG_DEBUG("evidence: %s", evidence_str.c_str()); + + // get ITA token + { + //parse evidence + json root; + CATCH(b, root = json::parse(evidence_str)); + COND2LOGERR(!b, "bad dcap evidence json"); + + //get ita token + CATCH(b, ita_token_str = root[ITA_TOKEN_TAG].template get()); + COND2LOGERR(!b, "no ita token in dcap verification"); + LOG_DEBUG("ita token: %s\n", ita_token_str.c_str()); + } + + // verify ITA token signature + { + //get token payload + b = get_token_payload(ita_token_str, ita_payload_str); + COND2LOGERR(!b, "cannot get ita payload"); + + //verify ita token + vs = verify_ita_token_signature(ita_token_str); + COND2LOGERR(vs == VERIFY_FAILURE, "token verification failed"); + } + + // verify mrenclave and statement + { + json root; + CATCH(b, root = json::parse(ita_payload_str)); + COND2LOGERR(!b, "bad ita payload json"); + + //prepare external mrenclave + std::string sgx_mrenclave; + CATCH(b, sgx_mrenclave = root["sgx_mrenclave"].template get()); + COND2LOGERR(!b, "no sgx_mrenclave in ita payload"); + COND2LOGERR(sgx_mrenclave.length() == 0, "sgx_mrenclave is empty"); + std::transform(sgx_mrenclave.begin(), sgx_mrenclave.end(), sgx_mrenclave.begin(), ::toupper); + LOG_DEBUG("sgx_mrenclave: %s\n", sgx_mrenclave.c_str()); + + //prepare expected mrenclave + std::string expected_hex_id((char*)expected_code_id.data(), expected_code_id.size()); + std::transform(expected_hex_id.begin(), expected_hex_id.end(), expected_hex_id.begin(), ::toupper); + + //check mrenclaves + COND2LOGERR(0 != sgx_mrenclave.compare(expected_hex_id), + "expected code id %s mismatch %s", expected_hex_id.c_str(), sgx_mrenclave.c_str()); + + //prepare external report data + std::string sgx_report_data; + CATCH(b, sgx_report_data = root["sgx_report_data"].template get()); + COND2LOGERR(!b, "no sgx_report_data in ita payload"); + COND2LOGERR(sgx_report_data.length() == 0, "sgx_report_data is empty"); + std::transform(sgx_report_data.begin(), sgx_report_data.end(), sgx_report_data.begin(), ::toupper); + LOG_DEBUG("sgx_report_data: %s\n", sgx_report_data.c_str()); + + //prepare expected report data + ByteArray hash; + std::string expected_hex_report_data_str; + COND2ERR(false == SHA256(expected_statement, hash)); + expected_hex_report_data_str = ByteArrayToHexEncodedString(hash); + expected_hex_report_data_str.append(expected_hex_report_data_str.length(), '0'); //double length with 0s + std::transform(expected_hex_report_data_str.begin(), expected_hex_report_data_str.end(), expected_hex_report_data_str.begin(), ::toupper); + + COND2LOGERR(0 != sgx_report_data.compare(expected_hex_report_data_str), + "expected statement %s mismatch %s", expected_hex_report_data_str.c_str(), sgx_report_data.c_str()); + } + + return true; + +err: + return false; +} diff --git a/evidence/verify-dcap-evidence.h b/evidence/verify-dcap-evidence.h new file mode 100644 index 0000000..a0f628b --- /dev/null +++ b/evidence/verify-dcap-evidence.h @@ -0,0 +1,7 @@ +/* + * Copyright 2023 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +bool verify_dcap_evidence(ByteArray& evidence, ByteArray& expected_statement, ByteArray& expected_code_id); diff --git a/evidence/verify-evidence.cpp b/evidence/verify-evidence.cpp new file mode 100644 index 0000000..9187546 --- /dev/null +++ b/evidence/verify-evidence.cpp @@ -0,0 +1,88 @@ +/* + * Copyright 2020 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "verify-evidence.h" +#include +#include +#include "attestation_tags.h" +#include "error.h" +#include "logging.h" +#include "types/types.h" +#include "verify-ias-evidence.h" +#include "verify-dcap-evidence.h" +#include "verify-dcap-direct-evidence.h" + +// < JSON include +#define JSON_HAS_CPP_11 +#define JSON_NO_IO 1 +#include +#include "nlohmann/json.hpp" +using json = nlohmann::json; +// JSON include > + +bool verify_evidence(uint8_t* evidence, + uint32_t evidence_length, + uint8_t* expected_statement, + uint32_t expected_statement_length, + uint8_t* expected_code_id, + uint32_t expected_code_id_length) +{ + bool ret = false; + json root; + std::string attestation_type; + std::string evidence_field; + std::string evidence_str((char*)evidence, evidence_length); + ByteArray ba_expected_statement( + expected_statement, expected_statement + expected_statement_length); + ByteArray ba_expected_code_id(expected_code_id, expected_code_id + expected_code_id_length); + + CATCH(ret, root = json::parse(evidence_str)); + COND2LOGERR(!ret, "invalid evidence json"); + + CATCH(ret, attestation_type = root[ATTESTATION_TYPE_TAG].template get()); + COND2LOGERR(!ret, "invalid (or missing) attestation type field"); + + CATCH(ret, evidence_field = root[EVIDENCE_TAG].template get()); + COND2LOGERR(!ret, "invalid evidence field"); + + if (0 == attestation_type.compare(SIMULATED_TYPE_TAG)) + { + // nothing to check + ret = true; + } + + if (0 == attestation_type.compare(EPID_LINKABLE_TYPE_TAG) || + 0 == attestation_type.compare(EPID_UNLINKABLE_TYPE_TAG)) + { + ByteArray ba_evidence(evidence_field.begin(), evidence_field.end()); + bool b = verify_ias_evidence(ba_evidence, ba_expected_statement, ba_expected_code_id); + COND2ERR(b == false); + ret = true; + } + + if (0 == attestation_type.compare(DCAP_SGX_TYPE_TAG)) + { + ByteArray ba_evidence(evidence_field.begin(), evidence_field.end()); + bool b = verify_dcap_evidence(ba_evidence, ba_expected_statement, ba_expected_code_id); + COND2ERR(b == false); + ret = true; + } + + if (0 == attestation_type.compare(DCAP_DIRECT_SGX_TYPE_TAG)) + { + ByteArray ba_evidence(evidence_field.begin(), evidence_field.end()); + bool b = verify_dcap_direct_evidence(ba_evidence, ba_expected_statement, ba_expected_code_id); + COND2ERR(b == false); + ret = true; + } + + COND2LOGERR(ret == false, "bad attestation type: %s", attestation_type.c_str()); + + return true; + +err: + return false; +} diff --git a/evidence/verify-ias-evidence.cpp b/evidence/verify-ias-evidence.cpp new file mode 100644 index 0000000..e49fe9d --- /dev/null +++ b/evidence/verify-ias-evidence.cpp @@ -0,0 +1,240 @@ +/* + * Copyright 2023 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "error.h" +#include "logging.h" +#include "base64/base64.h" +#include "crypto/verify_ias_report/verify-report.h" +#include "types/types.h" +#include "crypto/sha256.h" +#include "attestation_tags.h" + +// < JSON include +#define JSON_HAS_CPP_11 +#define JSON_NO_IO 1 +#include +#include "nlohmann/json.hpp" +using json = nlohmann::json; +// JSON include > + +static bool unwrap_ias_evidence(const std::string& evidence_str, + std::string& ias_signature, + std::string& ias_certificates, + std::string& ias_report, + std::string& untrusted_time_str) +{ + json root; + bool ret; + + CATCH(ret, root = json::parse(evidence_str)); + COND2LOGERR(!ret, "invalid ias evidence json"); + + CATCH(ret, ias_signature = root[IAS_SIGNATURE_TAG].template get()); + COND2LOGERR(!ret, "invalid ias_signature field"); + + CATCH(ret, ias_certificates = root[IAS_CERTIFICATES_TAG].template get()); + COND2LOGERR(!ret, "invalid ias_certificates field"); + + CATCH(ret, ias_report = root[IAS_REPORT_TAG].template get()); + COND2LOGERR(!ret, "invalid ias_report field"); + + CATCH(ret, untrusted_time_str = root[UNTRUSTED_TIME_T_TAG].template get()); + COND2LOGERR(!ret, "invalid untrusted time field"); + + return true; + +err: + LOG_DEBUG("ias evidence: %s\n", evidence_str.c_str()); + return false; +} + +static void replace_all_substrings( + std::string& s, const std::string& substring, const std::string& replace_with) +{ + size_t pos = 0; + while (1) + { + pos = s.find(substring, pos); + if (pos == std::string::npos) + break; + + s.replace(pos, substring.length(), replace_with); + } +} + +static void url_decode_ias_certificate(std::string& s) +{ + replace_all_substrings(s, "%20", " "); + replace_all_substrings(s, "%0A", "\n"); + replace_all_substrings(s, "%2B", "+"); + replace_all_substrings(s, "%3D", "="); + replace_all_substrings(s, "%2F", "/"); +} + +static bool split_certificates( + std::string& ias_certificates, std::vector& ias_certificate_vector) +{ + // ias certificates should have 2 certificates "-----BEGIN CERTIFICATE----- [...] -----END + // CERTIFICATE-----\n" + std::string cert_start("-----BEGIN CERTIFICATE-----"); + std::string cert_end("-----END CERTIFICATE-----\n"); + size_t cur = 0, start = 0, end = 0; + + ias_certificate_vector.clear(); + + url_decode_ias_certificate(ias_certificates); + + while (1) + { + start = ias_certificates.find(cert_start, cur); + if (start == std::string::npos) + { + break; + } + + end = ias_certificates.find(cert_end, cur); + if (end == std::string::npos) + { + break; + } + end += cert_end.length(); + + ias_certificate_vector.push_back(ias_certificates.substr(start, end)); + cur = end; + } + + COND2LOGERR(ias_certificate_vector.size() != 2, "unexpected number of IAS certificates"); + + return true; + +err: + return false; +} + +static bool extract_hex_from_report( + const std::string& ias_report, size_t offset, size_t size, std::string& hex) +{ + std::string b64quote; + ByteArray bin_quote; + ByteArray ba; + bool ret = false; + json root; + + CATCH(ret, root = json::parse(ias_report)); + COND2LOGERR(!ret, "invalid ias_report json"); + + CATCH(ret, b64quote = root["isvEnclaveQuoteBody"].template get()); + COND2LOGERR(!ret, "invalid isvEnclaveQuoteBody field"); + + bin_quote = Base64EncodedStringToByteArray(b64quote); + COND2LOGERR(bin_quote.size() != offsetof(sgx_quote_t, signature_len), "unexpected quote size"); + ba = ByteArray(bin_quote.data() + offset, bin_quote.data() + offset + size); + hex = ByteArrayToHexEncodedString(ba); + + return true; + +err: + return false; +} + +bool verify_ias_evidence( + ByteArray& evidence, ByteArray& expected_statement, ByteArray& expected_code_id) +{ + time_t untrusted_time = 0; + std::string evidence_str((char*)evidence.data(), evidence.size()); + std::string expected_hex_id((char*)expected_code_id.data(), expected_code_id.size()); + + std::string ias_signature, ias_certificates, ias_report, untrusted_time_str; + std::vector ias_certificate_vector; + + // get evidence data + COND2ERR( + false == unwrap_ias_evidence(evidence_str, ias_signature, ias_certificates, ias_report, untrusted_time_str)); + + // split certs + COND2ERR(false == split_certificates(ias_certificates, ias_certificate_vector)); + + { + //get time + unsigned long long timeull = std::stoull(untrusted_time_str); + COND2LOGERR(sizeof(timeull) != sizeof(time_t), "error: ull and time_t have different sizes" ); + untrusted_time = *((time_t*)(&timeull)); + LOG_DEBUG("untrusted time: %llu\n", timeull); + } + + { + // verify report status + const unsigned int flags = QSF_ACCEPT_GROUP_OUT_OF_DATE | QSF_ACCEPT_CONFIGURATION_NEEDED | + QSF_ACCEPT_SW_HARDENING_NEEDED | + QSF_ACCEPT_CONFIGURATION_AND_SW_HARDENING_NEEDED; + COND2LOGERR(VERIFY_SUCCESS != + verify_enclave_quote_status(ias_report.c_str(), ias_report.length(), flags), + "invalid quote status"); + } + + { + // check root cert + const int root_certificate_index = 1; + verify_status_t v; + bool ret; + CATCH(ret, v = verify_ias_certificate_chain(ias_certificate_vector[root_certificate_index].c_str(), untrusted_time)); + COND2LOGERR(!ret, "verify root cert exception"); + COND2LOGERR(VERIFY_SUCCESS != v, "invalid root certificate"); + } + + { + // check signing cert + const int signing_certificate_index = 0; + verify_status_t v; + bool ret; + CATCH(ret, v = verify_ias_certificate_chain(ias_certificate_vector[signing_certificate_index].c_str(), untrusted_time)); + COND2LOGERR(!ret, "verify intermediate cert exception"); + COND2LOGERR(VERIFY_SUCCESS != v, "invalid intermediate certificate"); + + // check signature + COND2LOGERR( + VERIFY_SUCCESS != verify_ias_report_signature( + ias_certificate_vector[signing_certificate_index].c_str(), + ias_report.c_str(), ias_report.length(), + (char*)ias_signature.c_str(), ias_signature.length()), + "invalid report signature"); + } + + { + // check code id + std::string hex_id; + COND2ERR(false == + extract_hex_from_report(ias_report, + offsetof(sgx_quote_t, report_body) + offsetof(sgx_report_body_t, mr_enclave), + sizeof(sgx_measurement_t), hex_id)); + LOG_DEBUG("code id comparision: found '%s' (len=%ld) / expected '%s' (len=%ld)", + hex_id.c_str(), hex_id.length(), expected_hex_id.c_str(), expected_hex_id.length()); + COND2LOGERR(0 != hex_id.compare(expected_hex_id), "expected code id mismatch"); + } + + { + // check report data + std::string hex_report_data, expected_hex_report_data_str; + ByteArray hash; + COND2ERR(false == + extract_hex_from_report(ias_report, + offsetof(sgx_quote_t, report_body) + offsetof(sgx_report_body_t, report_data), + sizeof(sgx_report_data_t), hex_report_data)); + COND2ERR(false == SHA256(expected_statement, hash)); + expected_hex_report_data_str = ByteArrayToHexEncodedString(hash); + expected_hex_report_data_str.append(expected_hex_report_data_str.length(), '0'); + COND2LOGERR(0 != hex_report_data.compare(expected_hex_report_data_str), + "expected statement mismatch"); + } + + // TODO: check attributes of attestation (e.g., DEBUG flag disabled in release mode) + + return true; + +err: + return false; +} + diff --git a/evidence/verify-ias-evidence.h b/evidence/verify-ias-evidence.h new file mode 100644 index 0000000..52debdb --- /dev/null +++ b/evidence/verify-ias-evidence.h @@ -0,0 +1,7 @@ +/* + * Copyright 2023 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +bool verify_ias_evidence(ByteArray& evidence, ByteArray& expected_statement, ByteArray& expected_code_id); diff --git a/include/attestation.h b/include/attestation.h new file mode 100644 index 0000000..0cc9d0f --- /dev/null +++ b/include/attestation.h @@ -0,0 +1,17 @@ +/* + * Copyright 2020 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include + +bool init_attestation(uint8_t* params, uint32_t params_length); + +bool get_attestation(uint8_t* statement, + uint32_t statement_length, + uint8_t* attestation, + uint32_t attestation_max_length, + uint32_t* attestation_length); diff --git a/include/attestation_tags.h b/include/attestation_tags.h new file mode 100644 index 0000000..5a5ffe4 --- /dev/null +++ b/include/attestation_tags.h @@ -0,0 +1,34 @@ +/* + * Copyright 2023 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#define ATTESTATION_TYPE_TAG "attestation_type" + +#define SIMULATED_TYPE_TAG "simulated" + +#define EPID_PREFIX_TAG "epid" +#define EPID_LINKABLE_TYPE_TAG "epid-linkable" +#define EPID_UNLINKABLE_TYPE_TAG "epid-unlinkable" +#define SPID_TAG "hex_spid" +#define SIG_RL_TAG "sig_rl" +#define IAS_SIGNATURE_TAG "ias_signature" +#define IAS_CERTIFICATES_TAG "ias_certificates" +#define IAS_REPORT_TAG "ias_report" + +#define ATTESTATION_TAG "attestation" +#define QUOTE_TAG "quote" +#define EVIDENCE_TAG "evidence" + +#define DCAP_PREFIX_TAG "dcap" +#define DCAP_DIRECT_PREFIX_TAG "dcap-direct" + +#define DCAP_SGX_TYPE_TAG "dcap-sgx" +#define DCAP_DIRECT_SGX_TYPE_TAG "dcap-direct-sgx" + +#define ITA_TOKEN_TAG "ita-token" +#define COLLATERAL_TAG "collateral" +#define UNTRUSTED_TIME_T_TAG "untrusted-time-t" diff --git a/include/verify-evidence.h b/include/verify-evidence.h new file mode 100644 index 0000000..81caa4c --- /dev/null +++ b/include/verify-evidence.h @@ -0,0 +1,24 @@ +/* + * Copyright 2020 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +bool verify_evidence(uint8_t* evidence, + uint32_t evidence_length, + uint8_t* expected_statement, + uint32_t expected_statement_length, + uint8_t* expected_code_id, + uint32_t expected_code_id_length); + +#ifdef __cplusplus +} +#endif diff --git a/ocalls/attestation-ocalls.c b/ocalls/attestation-ocalls.c new file mode 100644 index 0000000..88a4eb7 --- /dev/null +++ b/ocalls/attestation-ocalls.c @@ -0,0 +1,99 @@ +/* + * Copyright 2020 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include "error.h" +#include "logging.h" +#include "sgx_quote.h" +#include "sgx_dcap_ql_wrapper.h" + +void ocall_init_quote(uint8_t* target, uint32_t target_len, uint8_t* egid, uint32_t egid_len, uint32_t* sgxret) +{ + COND2LOGERR(target == NULL, "null sgx target info"); + + if(egid != NULL) //this means: EPID + { + int ret = sgx_init_quote((sgx_target_info_t*)target, (sgx_epid_group_id_t*)egid); + *sgxret = ret; + COND2LOGERR(ret != SGX_SUCCESS, "error sgx_init_quote: %x", ret); + } + else // no egid means: DCAP + { + quote3_error_t qe3_ret; + qe3_ret = sgx_qe_get_target_info((sgx_target_info_t*)target); + *sgxret = qe3_ret; + COND2LOGERR(qe3_ret != SGX_QL_SUCCESS, "error sgx_qe_get_target_info: %x", qe3_ret); + } + return; + +err: + ; // nothing to do +} + +void ocall_get_quote(uint8_t* spid, + uint32_t spid_len, + uint8_t* sig_rl, + uint32_t sig_rl_len, + uint32_t sign_type, + uint8_t* report, + uint32_t report_len, + uint8_t* quote, + uint32_t max_quote_len, + uint32_t* actual_quote_len, + uint32_t* sgxret) +{ + if(spid != NULL) // this means: EPID + { + int ret; + uint32_t required_quote_size = 0; + ret = sgx_calc_quote_size(sig_rl, sig_rl_len, &required_quote_size); + *sgxret = ret; + COND2LOGERR(ret != SGX_SUCCESS, "error sgx_calc_quote_size: %x", ret); + COND2LOGERR( + required_quote_size > max_quote_len, + "error not enough buffer for quote: required %d max %d", + required_quote_size, max_quote_len); + + ret = sgx_get_quote( + (const sgx_report_t*)report, + (sgx_quote_sign_type_t)sign_type, + (const sgx_spid_t*)spid, // spid + NULL, // nonce + sig_rl, // sig_rl + sig_rl_len, // sig_rl_size + NULL, // p_qe_report + (sgx_quote_t*)quote, required_quote_size); + *sgxret = ret; + COND2LOGERR(ret != SGX_SUCCESS, "error sgx_get_quote: %x", ret); + *actual_quote_len = required_quote_size; + } + else // this means DCAP + { + quote3_error_t qe3_ret; + uint32_t required_quote_size = 0; + qe3_ret = sgx_qe_get_quote_size(&required_quote_size); + *sgxret = qe3_ret; + COND2LOGERR(qe3_ret != SGX_QL_SUCCESS, "error sgx_qe_get_quote_size: %x", qe3_ret); + COND2LOGERR( + required_quote_size > max_quote_len, + "error not enough buffer for quote: required %d max %d", + required_quote_size, max_quote_len); + + qe3_ret = sgx_qe_get_quote( + (const sgx_report_t*)report, + required_quote_size, + quote); + *sgxret = qe3_ret; + COND2LOGERR(qe3_ret != SGX_QL_SUCCESS, "error sgx_qe_get_quote: %x", qe3_ret); + *actual_quote_len = required_quote_size; + } + + return; + +err: + // if anything wrong, no quote + *actual_quote_len = 0; +} diff --git a/ocalls/attestation-ocalls.edl b/ocalls/attestation-ocalls.edl new file mode 100644 index 0000000..1cb40f1 --- /dev/null +++ b/ocalls/attestation-ocalls.edl @@ -0,0 +1,25 @@ +/* + * Copyright 2020 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +enclave +{ + untrusted + { + void ocall_init_quote( + [out, size=target_len] uint8_t *target, uint32_t target_len, + [out, size=egid_len] uint8_t *egid, uint32_t egid_len, + [out, count=1] uint32_t *ret); + + void ocall_get_quote( + [in, size=spid_len] uint8_t *spid, uint32_t spid_len, + [in, size=sig_rl_len] uint8_t *sig_rl, uint32_t sig_rl_len, + uint32_t sign_type, + [in, size=report_len] uint8_t *report, uint32_t report_len, + [out, size=max_quote_len] uint8_t *quote, uint32_t max_quote_len, + [out] uint32_t *actual_quote_len, + [out, count=1] uint32_t *ret); + }; +}; diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt new file mode 100644 index 0000000..3965ac0 --- /dev/null +++ b/test/CMakeLists.txt @@ -0,0 +1,150 @@ +# Copyright 2020 Intel Corporation +# +# SPDX-License-Identifier: Apache-2.0 + +# pkg_check_modules needed to set OPENSSL_LDFLAGS +find_package(PkgConfig REQUIRED) +pkg_check_modules (OPENSSL REQUIRED openssl>=1.1.0g) + +# Put test artifacts under /tests subdirectory +set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/tests) +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/tests) + +################################################################################################## +## Verify Evidence test app: +## this application tests evidence from input files +################################################################################################## + +SET(VERIFY_EVIDENCE_APP verify_evidence_app) + +ADD_EXECUTABLE(${VERIFY_EVIDENCE_APP} + ${VERIFY_EVIDENCE_APP}/main.cpp + ${VERIFY_EVIDENCE_APP}/test.cpp + common/test-utils.cpp + ) + +TARGET_INCLUDE_DIRECTORIES(${VERIFY_EVIDENCE_APP} BEFORE PRIVATE "../common") # OAA common +TARGET_INCLUDE_DIRECTORIES(${VERIFY_EVIDENCE_APP} PRIVATE "../include") +TARGET_INCLUDE_DIRECTORIES(${VERIFY_EVIDENCE_APP} PRIVATE "common") # test common +TARGET_INCLUDE_DIRECTORIES(${VERIFY_EVIDENCE_APP} PRIVATE ${LOGGING_UNTRUSTED_INCLUDE_PATH}) +TARGET_LINK_LIBRARIES(${VERIFY_EVIDENCE_APP} ${LOGGING_UNTRUSTED_LIB}) +# Link the untrusted test application against the untrusted library and openssl +TARGET_LINK_LIBRARIES(${VERIFY_EVIDENCE_APP} + # we assume that the libraries are located one level up + "-L${CMAKE_CURRENT_BINARY_DIR}/.." + "-Wl,--start-group" ${OPENSSL_LDFLAGS} "-lu-one-attestation" "-Wl,--end-group" + ) + +################################################################################################### +## Attestation app: +## this application performs a sim/hw attestation from input files and writes output on file +################################################################################################### + +SET(GET_ATTESTATION_APP "get_attestation_app") + +ADD_SUBDIRECTORY(${GET_ATTESTATION_APP}/enclave) + +INCLUDE ("${GET_ATTESTATION_APP}/enclave/CMakeVariables.txt") + +# ENCLAVE_EDL is defined in the enclave subdirectory +SGX_EDGE_UNTRUSTED(${ENCLAVE_EDL} ENCLAVE_EDGE_SOURCES) + +ADD_EXECUTABLE(${GET_ATTESTATION_APP} + ${GET_ATTESTATION_APP}/app/main.cpp + common/test-utils.cpp + ${ENCLAVE_EDGE_SOURCES}) + +# Make sure the enclave builds before the test app that links it +ADD_DEPENDENCIES(${GET_ATTESTATION_APP} test_enclave) + +TARGET_INCLUDE_DIRECTORIES(${GET_ATTESTATION_APP} PRIVATE "$ENV{SGX_SDK}/include") +TARGET_INCLUDE_DIRECTORIES(${GET_ATTESTATION_APP} PRIVATE "$ENV{SGX_SSL}/include") +TARGET_INCLUDE_DIRECTORIES(${GET_ATTESTATION_APP} BEFORE PRIVATE "../common") # OAA common +TARGET_INCLUDE_DIRECTORIES(${GET_ATTESTATION_APP} PRIVATE ${LOGGING_UNTRUSTED_INCLUDE_PATH}) +TARGET_INCLUDE_DIRECTORIES(${GET_ATTESTATION_APP} PRIVATE "common") # test common + +TARGET_LINK_LIBRARIES(${GET_ATTESTATION_APP} -Wl,-L,$ENV{SGX_SDK}/lib64) +TARGET_LINK_LIBRARIES(${GET_ATTESTATION_APP} -Wl,-L,$ENV{SGX_SSL}/lib64) +TARGET_LINK_LIBRARIES(${GET_ATTESTATION_APP} + # we assume that the libraries are located one level up + "-L${CMAKE_CURRENT_BINARY_DIR}/.." + "-lu-one-attestation" + ) +TARGET_LINK_LIBRARIES(${GET_ATTESTATION_APP} ${URTS_LIBRARY_NAME} ${UAE_SERVICE_LIBRARY_NAME}) +TARGET_LINK_LIBRARIES(${GET_ATTESTATION_APP} sgx_usgxssl ${SGX_EPID_LIB} sgx_dcap_ql) +TARGET_LINK_LIBRARIES(${GET_ATTESTATION_APP} ${LOGGING_UNTRUSTED_LIB}) + +## dcap_quoteprov necessary for dcap logging +#TARGET_LINK_LIBRARIES(${GET_ATTESTATION_APP} dcap_quoteprov) + +################################################################################################## +## Verify Evidence test enclave app: +## this application tests evidence from input files +################################################################################################## + +SET(VERIFY_EVIDENCE_APP_ENCLAVE verify_evidence_app_enclave) + +ADD_SUBDIRECTORY(${VERIFY_EVIDENCE_APP}/enclave) + +INCLUDE ("${VERIFY_EVIDENCE_APP}/enclave/CMakeVariables.txt") + +# ENCLAVE_EDL is defined in the enclave subdirectory +SGX_EDGE_UNTRUSTED(${ENCLAVE_EDL} VERIFY_ENCLAVE_EDGE_SOURCES) +MESSAGE("VERIFY_ENCLAVE_EDGE_SOURCES: ${VERIFY_ENCLAVE_EDGE_SOURCES}") + +ADD_EXECUTABLE(${VERIFY_EVIDENCE_APP_ENCLAVE} + ${VERIFY_EVIDENCE_APP}/main.cpp + ${VERIFY_EVIDENCE_APP}/test-enclave.cpp + common/test-utils.cpp + ${VERIFY_ENCLAVE_EDGE_SOURCES} + ) + +# Make sure this app builds after get_attestation_app (because it has another enclave and build variables may conflict) +ADD_DEPENDENCIES(${VERIFY_EVIDENCE_APP_ENCLAVE} ${GET_ATTESTATION_APP}) + +# Make sure the enclave builds before the test app that links it +ADD_DEPENDENCIES(${VERIFY_EVIDENCE_APP_ENCLAVE} test_verify_enclave) + +TARGET_INCLUDE_DIRECTORIES(${VERIFY_EVIDENCE_APP_ENCLAVE} PRIVATE "$ENV{SGX_SDK}/include") +TARGET_INCLUDE_DIRECTORIES(${VERIFY_EVIDENCE_APP_ENCLAVE} PRIVATE "$ENV{SGX_SSL}/include") +TARGET_INCLUDE_DIRECTORIES(${VERIFY_EVIDENCE_APP_ENCLAVE} BEFORE PRIVATE "../common") # OAA common +TARGET_INCLUDE_DIRECTORIES(${VERIFY_EVIDENCE_APP_ENCLAVE} PRIVATE ${LOGGING_UNTRUSTED_INCLUDE_PATH}) +TARGET_INCLUDE_DIRECTORIES(${VERIFY_EVIDENCE_APP_ENCLAVE} PRIVATE "common") # test common + +TARGET_INCLUDE_DIRECTORIES(${VERIFY_EVIDENCE_APP_ENCLAVE} PRIVATE "../include") + +TARGET_LINK_LIBRARIES(${VERIFY_EVIDENCE_APP_ENCLAVE} -Wl,-L,$ENV{SGX_SDK}/lib64) +TARGET_LINK_LIBRARIES(${VERIFY_EVIDENCE_APP_ENCLAVE} -Wl,-L,$ENV{SGX_SSL}/lib64) +TARGET_LINK_LIBRARIES(${VERIFY_EVIDENCE_APP_ENCLAVE} + # we assume that the libraries are located one level up + "-L${CMAKE_CURRENT_BINARY_DIR}/.." + "-lu-one-attestation" + ) +TARGET_LINK_LIBRARIES(${VERIFY_EVIDENCE_APP_ENCLAVE} ${URTS_LIBRARY_NAME} ${UAE_SERVICE_LIBRARY_NAME}) +TARGET_LINK_LIBRARIES(${VERIFY_EVIDENCE_APP_ENCLAVE} sgx_usgxssl ${SGX_EPID_LIB} sgx_dcap_ql) +TARGET_LINK_LIBRARIES(${VERIFY_EVIDENCE_APP_ENCLAVE} ${LOGGING_UNTRUSTED_LIB}) + +## dcap_quoteprov necessary for logging +#TARGET_LINK_LIBRARIES(${VERIFY_EVIDENCE_APP_ENCLAVE} dcap_quoteprov) + +################################################################################# +## Test +################################################################################# + +SET(ATTESTED_EVIDENCE_TEST_FILE "attested_evidence_test.sh") +ADD_CUSTOM_COMMAND( + TARGET ${GET_ATTESTATION_APP} + PRE_BUILD + COMMAND cp ${ATTESTED_EVIDENCE_TEST_FILE} ${CMAKE_RUNTIME_OUTPUT_DIRECTORY} + COMMAND cp ${GET_ATTESTATION_APP}/enclave/test_enclave.config.xml ${CMAKE_RUNTIME_OUTPUT_DIRECTORY} + COMMAND mkdir -p ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/../conversion && cp -r ../conversion/* ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/../conversion + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + ) + +# Register this application as a test +ADD_TEST( + NAME ${ATTESTED_EVIDENCE_TEST_FILE} + COMMAND bash -c "OAA_PATH=${CMAKE_CURRENT_SOURCE_DIR}/../ ./${ATTESTED_EVIDENCE_TEST_FILE}" + WORKING_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY} + ) + diff --git a/test/attested_evidence_test.sh b/test/attested_evidence_test.sh new file mode 100755 index 0000000..cb09429 --- /dev/null +++ b/test/attested_evidence_test.sh @@ -0,0 +1,247 @@ +# Copyright 2020 Intel Corporation +# +# SPDX-License-Identifier: Apache-2.0 + +# *** README *** +# This script is meant to run as part of the build. +# The script is transferred to the folder where other test binaries will be located, +# and it will orchestrate the test. +# Orchestration involves: preparing input file for init_attestation, +# calling get_attestation, calling attestation_to_evidence, calling verify_evidence. + +set -e + +if [[ -z "${OAA_PATH}" ]]; then + echo "OAA_PATH not set" + exit -1 +fi + +. ${OAA_PATH}/common/scripts/common_utils.sh +. ${OAA_PATH}/conversion/tag_to_variable.sh + +DEFINES_FILEPATH="${OAA_PATH}/test/common/test-defines.h" +TAGS_FILEPATH="${OAA_PATH}/include/attestation_tags.h" + + +function init_environment() +{ + . ../conversion/attestation_to_evidence.sh + . ../conversion/define_to_variable.sh + . ../conversion/enclave_to_mrenclave.sh +} + +function remove_artifacts() +{ + rm -rf *.txt +} + +function orchestrate() +{ + #get attestation + ./get_attestation_app + define_to_variable "${DEFINES_FILEPATH}" "GET_ATTESTATION_OUTPUT" + [ -f ${GET_ATTESTATION_OUTPUT} ] || die "no output from get_attestation" + + #translate attestation (note: attestation_to_evidence defines the EVIDENCE variable) + ATTESTATION=$(cat ${GET_ATTESTATION_OUTPUT}) + attestation_to_evidence "${ATTESTATION}" + + define_to_variable "${DEFINES_FILEPATH}" "EVIDENCE_FILE" + echo ${EVIDENCE} > ${EVIDENCE_FILE} + + #verify evidence + ./verify_evidence_app + + #verify evidence in enclave + ./verify_evidence_app_enclave +} + +function check_collateral_epid() +{ + if [[ -z "${COLLATERAL_FOLDER}" ]]; then + echo "COLLATERAL_FOLDER for EPID not set" + exit -1 + fi + + SPID_TYPE_FILEPATH="${COLLATERAL_FOLDER}/spid_type.txt" + test -f ${SPID_TYPE_FILEPATH} || die "no spid type file ${SPID_TYPE_FILEPATH}" + + SPID_FILEPATH="${COLLATERAL_FOLDER}/spid.txt" + test -f ${SPID_FILEPATH} || die "no spid file ${SPID_FILEPATH}" +} + +function epid_test() +{ + say "Testing EPID SGX attestations" + + #check collateral + check_collateral_epid + init_environment + + #prepare input + remove_artifacts + define_to_variable "${DEFINES_FILEPATH}" "CODE_ID_FILE" + define_to_variable "${DEFINES_FILEPATH}" "STATEMENT_FILE" + define_to_variable "${DEFINES_FILEPATH}" "STATEMENT" + define_to_variable "${DEFINES_FILEPATH}" "INIT_DATA_INPUT" + + define_to_variable "${DEFINES_FILEPATH}" "UNSIGNED_ENCLAVE_FILENAME" + enclave_to_mrenclave ${UNSIGNED_ENCLAVE_FILENAME} test_enclave.config.xml + echo -n "$MRENCLAVE" > ${CODE_ID_FILE} + echo -n ${STATEMENT} > ${STATEMENT_FILE} + + #get spid type + SPID_TYPE=$(cat $SPID_TYPE_FILEPATH) + + #get spid + SPID=$(cat $SPID_FILEPATH) + + define_to_variable "${TAGS_FILEPATH}" "SPID_TAG" + define_to_variable "${TAGS_FILEPATH}" "SIG_RL_TAG" + echo -n "{\"${ATTESTATION_TYPE_TAG}\": \"$SPID_TYPE\", \"${SPID_TAG}\": \"$SPID\", \"${SIG_RL_TAG}\":\"\"}" > ${INIT_DATA_INPUT} + + #run attestation generation/conversion/verification tests + orchestrate + + say "Test success" +} + +function check_collateral_dcap() +{ + if [[ -z "${COLLATERAL_FOLDER}" ]]; then + echo "COLLATERAL_FOLDER for DCAP not set" + exit -1 + fi + + ATTESTATION_TYPE_FILEPATH="${COLLATERAL_FOLDER}/attestation_type.txt" + test -f ${ATTESTATION_TYPE_FILEPATH} || die "no attestation type file ${ATTESTATION_TYPE_FILEPATH}" + + API_KEY_FILEPATH="${COLLATERAL_FOLDER}/ita_api_key.txt" + test -f ${API_KEY_FILEPATH} || die "no api key file ${API_KEY_FILEPATH}" +} + +function dcap_test() +{ + say "Testing DCAP SGX attestations" + + #check collateral + check_collateral_dcap + init_environment + + #prepare input + remove_artifacts + define_to_variable "${DEFINES_FILEPATH}" "CODE_ID_FILE" + define_to_variable "${DEFINES_FILEPATH}" "STATEMENT_FILE" + define_to_variable "${DEFINES_FILEPATH}" "STATEMENT" + define_to_variable "${DEFINES_FILEPATH}" "INIT_DATA_INPUT" + + define_to_variable "${DEFINES_FILEPATH}" "UNSIGNED_ENCLAVE_FILENAME" + enclave_to_mrenclave ${UNSIGNED_ENCLAVE_FILENAME} test_enclave.config.xml + echo -n "$MRENCLAVE" > ${CODE_ID_FILE} + echo -n ${STATEMENT} > ${STATEMENT_FILE} + + #get attestation type + ATTESTATION_TYPE=$(cat $ATTESTATION_TYPE_FILEPATH) + + echo -n "{\"${ATTESTATION_TYPE_TAG}\": \"$ATTESTATION_TYPE\"}" > ${INIT_DATA_INPUT} + + #run attestation generation/conversion/verification tests + orchestrate + + say "Test success" +} + +function dcap_direct_test() +{ + say "Testing DCAP-DIRECT SGX attestations" + + init_environment + + #prepare input + remove_artifacts + define_to_variable "${DEFINES_FILEPATH}" "CODE_ID_FILE" + define_to_variable "${DEFINES_FILEPATH}" "STATEMENT_FILE" + define_to_variable "${DEFINES_FILEPATH}" "STATEMENT" + define_to_variable "${DEFINES_FILEPATH}" "INIT_DATA_INPUT" + + define_to_variable "${DEFINES_FILEPATH}" "UNSIGNED_ENCLAVE_FILENAME" + enclave_to_mrenclave ${UNSIGNED_ENCLAVE_FILENAME} test_enclave.config.xml + echo -n "$MRENCLAVE" > ${CODE_ID_FILE} + echo -n ${STATEMENT} > ${STATEMENT_FILE} + + #get attestation type + tag_to_variable "DCAP_DIRECT_SGX_TYPE_TAG" + ATTESTATION_TYPE="$DCAP_DIRECT_SGX_TYPE_TAG" + + echo -n "{\"${ATTESTATION_TYPE_TAG}\": \"$ATTESTATION_TYPE\"}" > ${INIT_DATA_INPUT} + + #run attestation generation/conversion/verification tests + orchestrate + + say "Test success" +} + + +function simulated_test() +{ + say "Testing simulated attestation" + init_environment + + #prepare input + remove_artifacts + define_to_variable "${DEFINES_FILEPATH}" "CODE_ID_FILE" + define_to_variable "${DEFINES_FILEPATH}" "STATEMENT_FILE" + define_to_variable "${DEFINES_FILEPATH}" "STATEMENT" + define_to_variable "${DEFINES_FILEPATH}" "INIT_DATA_INPUT" + + define_to_variable "${TAGS_FILEPATH}" "ATTESTATION_TYPE_TAG" + define_to_variable "${TAGS_FILEPATH}" "SIMULATED_TYPE_TAG" + + echo -n "this is ignored" > ${CODE_ID_FILE} + echo -n "also ignored" > ${STATEMENT_FILE} + echo -n "{\"${ATTESTATION_TYPE_TAG}\": \"${SIMULATED_TYPE_TAG}\"}" > ${INIT_DATA_INPUT} + + #run attestation generation/conversion/verification tests + orchestrate + + say "Test success" + +} + +simulated_test + +####################################### +# hw mode test +####################################### +if [[ ${SGX_MODE} == "HW" ]]; then + + if [[ ! -z "${SKIP_TEST_EPID+x}" ]]; then + say "Skipping EPID attestation test" + else + say "Testing HW-mode EPID attestation" + init_environment + epid_test + fi + + if [[ ! -z "${SKIP_TEST_DCAP_DIRECT+x}" ]]; then + say "Skipping DCAP-DIRECT attestation test" + else + say "Testing HW-mode DCAP-DIRECT attestation" + init_environment + dcap_direct_test + fi + + if [[ ! -z "${SKIP_TEST_DCAP+x}" ]]; then + say "Skipping DCAP attestation test" + else + say "Testing HW-mode DCAP attestation" + init_environment + dcap_test + fi + +else + say "Skipping actual attestation test" +fi + +say "Test successful." +exit 0 diff --git a/test/common/test-defines.h b/test/common/test-defines.h new file mode 100644 index 0000000..31ad222 --- /dev/null +++ b/test/common/test-defines.h @@ -0,0 +1,17 @@ +/* + * Copyright 2020 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#define ENCLAVE_FILENAME "test_enclave.signed.so" +#define VERIFY_ENCLAVE_FILENAME "test_verify_enclave.signed.so" +#define UNSIGNED_ENCLAVE_FILENAME "libtest_enclave.so" +#define INIT_DATA_INPUT "init_attestation_input.txt" +#define GET_ATTESTATION_OUTPUT "get_attestation_output.txt" +#define STATEMENT "1234567890" +#define STATEMENT_FILE "statement.txt" +#define CODE_ID_FILE "code_id.txt" +#define EVIDENCE_FILE "verify_evidence_input.txt" diff --git a/test/common/test-utils.cpp b/test/common/test-utils.cpp new file mode 100644 index 0000000..7dcd702 --- /dev/null +++ b/test/common/test-utils.cpp @@ -0,0 +1,55 @@ +/* + * Copyright 2020 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "test-utils.h" +#include +#include + +#include "error.h" +#include "logging.h" + +bool load_file(const char* filename, char* buffer, uint32_t buffer_length, uint32_t* written_bytes) +{ + uint32_t file_size, bytes_read; + struct stat s; + + FILE* fp = fopen(filename, "r"); + COND2LOGERR(fp == NULL, "can't open file"); + + COND2LOGERR(0 > fstat(fileno(fp), &s), "cannot stat file"); + file_size = s.st_size; + COND2LOGERR(file_size > buffer_length, "buffer too small"); + + bytes_read = fread(buffer, 1, file_size, fp); + COND2LOGERR(bytes_read != file_size, "read bytes don't match file size"); + *written_bytes = bytes_read; + + fclose(fp); + return true; + +err: + if (fp) + fclose(fp); + + return false; +} + +bool save_file(const char* filename, const char* buffer, uint32_t buffer_length) +{ + FILE* fpo; + uint32_t bytes; + fpo = fopen(filename, "w+"); + COND2LOGERR(fpo == NULL, "can't open file"); + bytes = fwrite(buffer, sizeof(uint8_t), buffer_length, fpo); + COND2LOGERR(bytes != buffer_length, "error bytes written"); + fclose(fpo); + return true; +err: + if (fpo) + fclose(fpo); + + return false; +} diff --git a/test/common/test-utils.h b/test/common/test-utils.h new file mode 100644 index 0000000..e2897bc --- /dev/null +++ b/test/common/test-utils.h @@ -0,0 +1,12 @@ +/* + * Copyright 2020 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include + +bool load_file(const char* filename, char* buffer, uint32_t buffer_length, uint32_t* written_bytes); +bool save_file(const char* filename, const char* buffer, uint32_t buffer_length); diff --git a/test/get_attestation_app/app/main.cpp b/test/get_attestation_app/app/main.cpp new file mode 100644 index 0000000..9bb523e --- /dev/null +++ b/test/get_attestation_app/app/main.cpp @@ -0,0 +1,64 @@ +/* + * Copyright 2020 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "error.h" +#include "logging.h" +#include "sgx_eid.h" +#include "sgx_error.h" +#include "sgx_urts.h" +#include "test-defines.h" +#include "test-utils.h" +#include "test_enclave_u.h" + +int main() +{ + sgx_launch_token_t token = {0}; + int updated = 0; + sgx_enclave_id_t global_eid = 0; + sgx_status_t ret = SGX_ERROR_UNEXPECTED; + const uint32_t buffer_length = 1 << 20; + uint8_t attestation[buffer_length]; + uint32_t attestation_length = 0; + uint32_t params_length = 0; + int b; + char params_buf[buffer_length]; + //std::string params; + + ret = sgx_create_enclave(ENCLAVE_FILENAME, SGX_DEBUG_FLAG, &token, &updated, &global_eid, NULL); + if (ret != SGX_SUCCESS) + { + puts("error creating enclave"); + exit(-1); + } + + COND2LOGERR(false == load_file(INIT_DATA_INPUT, params_buf, buffer_length, ¶ms_length), + "error loading params"); + + //assuming it's a string, let's null terminate it + //params = std::string(params_buf, params_length); + params_buf[params_length] = '\0'; + + LOG_INFO("Testing init attestation\n"); + //init_att(global_eid, &b, (uint8_t*)params.c_str(), params.length()); + init_att(global_eid, &b, (uint8_t*)params_buf, params_length); + COND2LOGERR(!b, "init_attestation failed"); + + LOG_INFO("Testing get attestation\n"); + get_att(global_eid, &b, (uint8_t*)STATEMENT, strlen(STATEMENT), attestation, (1<<13), + &attestation_length); + COND2LOGERR(!b, "get_attestation failed"); + + COND2LOGERR(false == save_file(GET_ATTESTATION_OUTPUT, (char*)attestation, attestation_length), + "error saving attestation"); + sgx_destroy_enclave(global_eid); + + LOG_INFO("Test Successful\n"); + return 0; + +err: + sgx_destroy_enclave(global_eid); + return -1; +} diff --git a/test/get_attestation_app/enclave/CMakeLists.txt b/test/get_attestation_app/enclave/CMakeLists.txt new file mode 100644 index 0000000..da044b3 --- /dev/null +++ b/test/get_attestation_app/enclave/CMakeLists.txt @@ -0,0 +1,74 @@ +# Copyright 2020 Intel Corporation +# +# SPDX-License-Identifier: Apache-2.0 + +CMAKE_MINIMUM_REQUIRED(VERSION 3.2 FATAL_ERROR) + +INCLUDE ("CMakeVariables.txt") + +SET(ENCLAVE_NAME test_enclave) +PROJECT(${ENCLAVE_NAME} C CXX) + +add_definitions(-DENCLAVE_CODE) + +FILE(GLOB ENCLAVE_HEADERS *.h) +FILE(GLOB ENCLAVE_EDL *.edl) +FILE(GLOB ENCLAVE_CONFIG *.xml) +FILE(GLOB ENCLAVE_LDS *.lds) +FILE(GLOB ENCLAVE_SOURCES + *.cpp + ) + +SGX_EDGE_TRUSTED(${ENCLAVE_EDL} ENCLAVE_EDGE_SOURCES) +SET(ENCLAVE_EDL ${ENCLAVE_EDL} PARENT_SCOPE) + +ADD_LIBRARY(${ENCLAVE_NAME} SHARED ${ENCLAVE_HEADERS} ${ENCLAVE_SOURCES} ${ENCLAVE_EDGE_SOURCES} ${ENCLAVE_EDL}) + +TARGET_INCLUDE_DIRECTORIES(${ENCLAVE_NAME} PUBLIC ${SGX_SDK}/include) +TARGET_INCLUDE_DIRECTORIES(${ENCLAVE_NAME} PUBLIC ${SGX_SDK}/include/tlibc) +TARGET_INCLUDE_DIRECTORIES(${ENCLAVE_NAME} PUBLIC "../../../include") +TARGET_INCLUDE_DIRECTORIES(${ENCLAVE_NAME} PUBLIC ${LOGGING_TRUSTED_INCLUDE_PATH}) + +TARGET_COMPILE_OPTIONS(${ENCLAVE_NAME} PRIVATE $<$:-nostdinc++>) +TARGET_COMPILE_OPTIONS(${ENCLAVE_NAME} PRIVATE ${COMMON_CXX_FLAGS}) + +TARGET_LINK_DIRECTORIES(${ENCLAVE_NAME} PRIVATE ${SGX_SDK}/lib64) + +TARGET_LINK_LIBRARIES(${ENCLAVE_NAME} -Wl,--no-undefined) +TARGET_LINK_LIBRARIES(${ENCLAVE_NAME} -nostdlib) +TARGET_LINK_LIBRARIES(${ENCLAVE_NAME} -nodefaultlibs) +TARGET_LINK_LIBRARIES(${ENCLAVE_NAME} -nostartfiles) +TARGET_LINK_LIBRARIES(${ENCLAVE_NAME} -Wl,-Bstatic) +TARGET_LINK_LIBRARIES(${ENCLAVE_NAME} -Wl,-Bsymbolic) +TARGET_LINK_LIBRARIES(${ENCLAVE_NAME} -Wl,--no-undefined) +TARGET_LINK_LIBRARIES(${ENCLAVE_NAME} -Wl,-pie,-eenclave_entry) +TARGET_LINK_LIBRARIES(${ENCLAVE_NAME} -Wl,--export-dynamic) +TARGET_LINK_LIBRARIES(${ENCLAVE_NAME} -Wl,--defsym,__ImageBase=0) +TARGET_LINK_LIBRARIES(${ENCLAVE_NAME} -Wl,--version-script=${ENCLAVE_LDS}) +TARGET_LINK_LIBRARIES(${ENCLAVE_NAME} -Wl,-L,${SGX_SDK}/lib64) +TARGET_LINK_LIBRARIES(${ENCLAVE_NAME} -Wl,-L,${SGX_SSL}/lib64) +TARGET_LINK_LIBRARIES(${ENCLAVE_NAME} -Wl,--whole-archive -lsgx_tsgxssl -Wl,--no-whole-archive) +TARGET_LINK_LIBRARIES(${ENCLAVE_NAME} -Wl,--whole-archive -l${TRTS_LIBRARY_NAME} -Wl,--no-whole-archive) +TARGET_LINK_LIBRARIES(${ENCLAVE_NAME} + # we assume that the libraries are located 3 levels up + "-L${CMAKE_CURRENT_BINARY_DIR}/../../.." + -Wl,--start-group + ${LOGGING_TRUSTED_LIB} + -lt-one-attestation + sgx_tsgxssl_crypto + sgx_tstdc + sgx_tcxx + sgx_tcrypto + ${SERVICE_LIBRARY_NAME} + -Wl,--end-group + ) + +SET(ENCLAVE_SIGNING_KEY "${ENCLAVE_NAME}.sign.pem") +SGX_SIGN_ENCLAVE(${ENCLAVE_NAME} ${ENCLAVE_SIGNING_KEY} ${ENCLAVE_CONFIG}) + +ADD_CUSTOM_COMMAND( + TARGET ${ENCLAVE_NAME} + PRE_BUILD + COMMAND openssl genrsa -3 -out ${ENCLAVE_SIGNING_KEY} 3072 + ) + diff --git a/test/get_attestation_app/enclave/CMakeVariables.txt b/test/get_attestation_app/enclave/CMakeVariables.txt new file mode 100644 index 0000000..bb98f50 --- /dev/null +++ b/test/get_attestation_app/enclave/CMakeVariables.txt @@ -0,0 +1,100 @@ +# Copyright 2023 Intel Corporation +# +# SPDX-License-Identifier: Apache-2.0 + +################################################################################ +# Checks +################################################################################ + +IF (NOT DEFINED ENV{SGX_MODE}) + SET(SGX_MODE "SIM") +ELSE() + SET(SGX_MODE $ENV{SGX_MODE}) +ENDIF() + +IF (NOT DEFINED ENV{SGX_SDK}) + SET(SGX_SDK "/opt/intel/sgxsdk") +ELSE() + SET(SGX_SDK "$ENV{SGX_SDK}") +ENDIF() + +IF (NOT DEFINED ENV{SGX_SSL}) + SET(SGX_SSL "/opt/intel/sgxssl") +ELSE() + SET(SGX_SSL "$ENV{SGX_SSL}") +ENDIF() + + +SET(SGX_EDGER "${SGX_SDK}/bin/x64/sgx_edger8r") +SET(SGX_SIGN "${SGX_SDK}/bin/x64/sgx_sign") + +SET(SGX_SEARCH_PATH "${SGX_SDK}/include:${SGX_SSL}/include") +SET(SGX_ENCLAVE_INCLUDE "${SGX_SDK}/include" + "${SGX_SDK}/include/tlibc" + "${SGX_SDK}/include/libcxx") + +#ADD_COMPILE_OPTIONS($<$:-std=c++11>) + +################################################################################ +# Internal SGX Variables +################################################################################ + +IF (${SGX_MODE} STREQUAL "SIM") + #ADD_COMPILE_DEFINITIONS(SGX_SIMULATOR=1) + SET(TRTS_LIBRARY_NAME "sgx_trts_sim") + SET(URTS_LIBRARY_NAME "sgx_urts_sim") + SET(UAE_SERVICE_LIBRARY_NAME "sgx_uae_service_sim") + SET(SERVICE_LIBRARY_NAME "sgx_tservice_sim") + SET(SGX_EPID_LIB sgx_epid_sim) +ELSE() + SET(TRTS_LIBRARY_NAME "sgx_trts") + SET(URTS_LIBRARY_NAME "sgx_urts") + SET(UAE_SERVICE_LIBRARY_NAME "sgx_uae_service") + SET(SERVICE_LIBRARY_NAME "sgx_tservice") + SET(SGX_EPID_LIB sgx_epid) +ENDIF() + +################################################################################ +# Functions +################################################################################ + +FUNCTION(SGX_EDGE_TRUSTED EDL EDGE_FILES) + GET_FILENAME_COMPONENT(EDL_BASE_NAME ${EDL} NAME_WE) + GET_FILENAME_COMPONENT(EDL_DIR_NAME ${EDL} DIRECTORY) + INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR}) + + SET (EDGE_FILES_LIST "${CMAKE_CURRENT_BINARY_DIR}/${EDL_BASE_NAME}_t.h" "${CMAKE_CURRENT_BINARY_DIR}/${EDL_BASE_NAME}_t.c") + SET (${EDGE_FILES} ${EDGE_FILES_LIST} PARENT_SCOPE) + ADD_CUSTOM_COMMAND( OUTPUT ${EDGE_FILES_LIST} + COMMAND "${SGX_EDGER}" --trusted ${EDL} --search-path ${SGX_SEARCH_PATH} --search-path ${EDL_DIR_NAME} + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + DEPENDS ${EDL} + ) +ENDFUNCTION() + +FUNCTION(SGX_EDGE_UNTRUSTED EDL EDGE_FILES) + GET_FILENAME_COMPONENT(EDL_BASE_NAME ${EDL} NAME_WE) + GET_FILENAME_COMPONENT(EDL_DIR_NAME ${EDL} DIRECTORY) + INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR}) + + SET (EDGE_FILES_LIST "${CMAKE_CURRENT_BINARY_DIR}/${EDL_BASE_NAME}_u.h" "${CMAKE_CURRENT_BINARY_DIR}/${EDL_BASE_NAME}_u.c") + SET (${EDGE_FILES} ${EDGE_FILES_LIST} PARENT_SCOPE) + ADD_CUSTOM_COMMAND( OUTPUT ${EDGE_FILES_LIST} + COMMAND "${SGX_EDGER}" --untrusted ${EDL} --search-path ${SGX_SEARCH_PATH} --search-path ${EDL_DIR_NAME} + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + DEPENDS ${EDL} + ) +ENDFUNCTION() + +FUNCTION(SGX_SIGN_ENCLAVE TARGET KEY_FILE CONFIG) + SET (ENCLAVE $) + + SET (SIGNED_ENCLAVE ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/${TARGET}.signed${CMAKE_SHARED_LIBRARY_SUFFIX}) + SET (SIGNED_ENCLAVE ${SIGNED_ENCLAVE} PARENT_SCOPE) + SET (SIGNED_ENCLAVE_METADATA ${SIGNED_ENCLAVE}".meta") + ADD_CUSTOM_COMMAND( TARGET ${TARGET} + POST_BUILD + COMMAND "${SGX_SIGN}" sign -key "${KEY_FILE}" -enclave "${ENCLAVE}" -out "${SIGNED_ENCLAVE}" -dumpfile ${SIGNED_ENCLAVE_METADATA} -config "${CONFIG}" > /dev/null + ) +ENDFUNCTION() + diff --git a/test/get_attestation_app/enclave/test.cpp b/test/get_attestation_app/enclave/test.cpp new file mode 100644 index 0000000..36e990f --- /dev/null +++ b/test/get_attestation_app/enclave/test.cpp @@ -0,0 +1,27 @@ +/* + * Copyright 2020 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "test.h" +#include "attestation.h" +#include "logging.h" +#include "test_enclave_t.h" + +int init_att(uint8_t* params, uint32_t params_length) +{ + LOG_DEBUG("Testing init attestation"); + return init_attestation(params, params_length); +} + +int get_att(uint8_t* statement, + uint32_t statement_length, + uint8_t* attestation, + uint32_t attestation_max_length, + uint32_t* attestation_length) +{ + LOG_DEBUG("Testing get attestation"); + return get_attestation( + statement, statement_length, attestation, attestation_max_length, attestation_length); +} diff --git a/test/get_attestation_app/enclave/test.h b/test/get_attestation_app/enclave/test.h new file mode 100644 index 0000000..6fa11bf --- /dev/null +++ b/test/get_attestation_app/enclave/test.h @@ -0,0 +1,15 @@ +/* + * Copyright 2020 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#if defined(__cplusplus) +extern "C" { +#endif +int test(); +#if defined(__cplusplus) +} +#endif diff --git a/test/get_attestation_app/enclave/test_enclave.config.xml b/test/get_attestation_app/enclave/test_enclave.config.xml new file mode 100644 index 0000000..3938529 --- /dev/null +++ b/test/get_attestation_app/enclave/test_enclave.config.xml @@ -0,0 +1,18 @@ + + + + 0 + 0 + 0x40000 + 0x100000 + 10 + 1 + + 0 + 0 + 0xFFFFFFFF + diff --git a/test/get_attestation_app/enclave/test_enclave.edl b/test/get_attestation_app/enclave/test_enclave.edl new file mode 100644 index 0000000..5ae4e87 --- /dev/null +++ b/test/get_attestation_app/enclave/test_enclave.edl @@ -0,0 +1,21 @@ +/* + * Copyright 2020 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +enclave { + from "sgx_tstdc.edl" import *; + from "../../../ocalls/attestation-ocalls.edl" import *; + from "../../../common/logging/ocalls/logging.edl" import *; + + trusted { + public int init_att( + [in, size=params_length] uint8_t* params, uint32_t params_length); + public int get_att( + [in, size=statement_length] uint8_t* statement, uint32_t statement_length, + [out, size=attestation_max_length] uint8_t* attestation, uint32_t attestation_max_length, + [out] uint32_t* attestation_length); + }; +}; + diff --git a/test/get_attestation_app/enclave/test_enclave.lds b/test/get_attestation_app/enclave/test_enclave.lds new file mode 100644 index 0000000..829f496 --- /dev/null +++ b/test/get_attestation_app/enclave/test_enclave.lds @@ -0,0 +1,15 @@ +/* + * Copyright 2020 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +test_enclave.so +{ + global: + g_global_data_sim; + g_global_data; + test_enclave_entry; + local: + *; +}; diff --git a/test/verify_evidence_app/enclave/CMakeLists.txt b/test/verify_evidence_app/enclave/CMakeLists.txt new file mode 100644 index 0000000..c091ce3 --- /dev/null +++ b/test/verify_evidence_app/enclave/CMakeLists.txt @@ -0,0 +1,78 @@ +# Copyright 2020 Intel Corporation +# +# SPDX-License-Identifier: Apache-2.0 + +CMAKE_MINIMUM_REQUIRED(VERSION 3.2 FATAL_ERROR) + +INCLUDE ("CMakeVariables.txt") + +SET(ENCLAVE_NAME test_verify_enclave) +PROJECT(${ENCLAVE_NAME} C CXX) + +add_definitions(-DENCLAVE_CODE) + +FILE(GLOB ENCLAVE_HEADERS *.h) +FILE(GLOB ENCLAVE_EDL *.edl) +FILE(GLOB ENCLAVE_CONFIG *.xml) +FILE(GLOB ENCLAVE_LDS *.lds) +FILE(GLOB ENCLAVE_SOURCES + *.cpp + ) + +SGX_EDGE_TRUSTED(${ENCLAVE_EDL} ENCLAVE_EDGE_SOURCES) +SET(ENCLAVE_EDL ${ENCLAVE_EDL} PARENT_SCOPE) + +ADD_LIBRARY(${ENCLAVE_NAME} SHARED ${ENCLAVE_HEADERS} ${ENCLAVE_SOURCES} ${ENCLAVE_EDGE_SOURCES}) + +TARGET_INCLUDE_DIRECTORIES(${ENCLAVE_NAME} PRIVATE "../../../include") +TARGET_INCLUDE_DIRECTORIES(${ENCLAVE_NAME} PUBLIC ${SGX_SDK}/include) +TARGET_INCLUDE_DIRECTORIES(${ENCLAVE_NAME} PUBLIC ${SGX_SDK}/include/libcxx) +TARGET_INCLUDE_DIRECTORIES(${ENCLAVE_NAME} PRIVATE ${SGX_SDK}/include/tlibc) +TARGET_INCLUDE_DIRECTORIES(${ENCLAVE_NAME} PUBLIC ${LOGGING_TRUSTED_INCLUDE_PATH}) + +TARGET_INCLUDE_DIRECTORIES(${ENCLAVE_NAME} PRIVATE ${SGX_SSL}/include) + +TARGET_COMPILE_OPTIONS(${ENCLAVE_NAME} PRIVATE $<$:-nostdinc++>) +TARGET_COMPILE_OPTIONS(${ENCLAVE_NAME} PRIVATE ${COMMON_CXX_FLAGS}) +TARGET_COMPILE_OPTIONS(${ENCLAVE_NAME} PRIVATE -nostdinc) + +TARGET_LINK_DIRECTORIES(${ENCLAVE_NAME} PRIVATE ${SGX_SDK}/lib64) + +TARGET_LINK_LIBRARIES(${ENCLAVE_NAME} -Wl,--no-undefined) +TARGET_LINK_LIBRARIES(${ENCLAVE_NAME} -nostdlib) +TARGET_LINK_LIBRARIES(${ENCLAVE_NAME} -nodefaultlibs) +TARGET_LINK_LIBRARIES(${ENCLAVE_NAME} -nostartfiles) +TARGET_LINK_LIBRARIES(${ENCLAVE_NAME} -Wl,-Bstatic) +TARGET_LINK_LIBRARIES(${ENCLAVE_NAME} -Wl,-Bsymbolic) +TARGET_LINK_LIBRARIES(${ENCLAVE_NAME} -Wl,--no-undefined) +TARGET_LINK_LIBRARIES(${ENCLAVE_NAME} -Wl,-pie,-eenclave_entry) +TARGET_LINK_LIBRARIES(${ENCLAVE_NAME} -Wl,--export-dynamic) +TARGET_LINK_LIBRARIES(${ENCLAVE_NAME} -Wl,--defsym,__ImageBase=0) +TARGET_LINK_LIBRARIES(${ENCLAVE_NAME} -Wl,--version-script=${ENCLAVE_LDS}) +TARGET_LINK_LIBRARIES(${ENCLAVE_NAME} -Wl,-L,${SGX_SDK}/lib64) +TARGET_LINK_LIBRARIES(${ENCLAVE_NAME} -Wl,-L,${SGX_SSL}/lib64) +TARGET_LINK_LIBRARIES(${ENCLAVE_NAME} -Wl,--whole-archive -lsgx_tsgxssl -Wl,--no-whole-archive) +TARGET_LINK_LIBRARIES(${ENCLAVE_NAME} -Wl,--whole-archive -l${TRTS_LIBRARY_NAME} -Wl,--no-whole-archive) +TARGET_LINK_LIBRARIES(${ENCLAVE_NAME} + # we assume that the libraries are located 3 levels up + "-L${CMAKE_CURRENT_BINARY_DIR}/../../.." + -Wl,--start-group + ${LOGGING_TRUSTED_LIB} + -lt-one-attestation + sgx_tsgxssl_crypto + sgx_tstdc + sgx_tcxx + sgx_tcrypto + ${SERVICE_LIBRARY_NAME} + -Wl,--end-group + ) + +SET(ENCLAVE_SIGNING_KEY "${ENCLAVE_NAME}.sign.pem") +SGX_SIGN_ENCLAVE(${ENCLAVE_NAME} ${ENCLAVE_SIGNING_KEY} ${ENCLAVE_CONFIG}) + +ADD_CUSTOM_COMMAND( + TARGET ${ENCLAVE_NAME} + PRE_BUILD + COMMAND openssl genrsa -3 -out ${ENCLAVE_SIGNING_KEY} 3072 + ) + diff --git a/test/verify_evidence_app/enclave/CMakeVariables.txt b/test/verify_evidence_app/enclave/CMakeVariables.txt new file mode 100644 index 0000000..d101643 --- /dev/null +++ b/test/verify_evidence_app/enclave/CMakeVariables.txt @@ -0,0 +1,99 @@ +# Copyright 2023 Intel Corporation +# +# SPDX-License-Identifier: Apache-2.0 + +################################################################################ +# Checks +################################################################################ + +IF (NOT DEFINED ENV{SGX_MODE}) + SET(SGX_MODE "SIM") +ELSE() + SET(SGX_MODE $ENV{SGX_MODE}) +ENDIF() + +IF (NOT DEFINED ENV{SGX_SDK}) + SET(SGX_SDK "/opt/intel/sgxsdk") +ELSE() + SET(SGX_SDK "$ENV{SGX_SDK}") +ENDIF() + +IF (NOT DEFINED ENV{SGX_SSL}) + SET(SGX_SSL "/opt/intel/sgxssl") +ELSE() + SET(SGX_SSL "$ENV{SGX_SSL}") +ENDIF() + + +SET(SGX_EDGER "${SGX_SDK}/bin/x64/sgx_edger8r") +SET(SGX_SIGN "${SGX_SDK}/bin/x64/sgx_sign") + +SET(SGX_SEARCH_PATH "${SGX_SDK}/include:${SGX_SSL}/include") +SET(SGX_ENCLAVE_INCLUDE "${SGX_SDK}/include" + "${SGX_SDK}/include/tlibc" + "${SGX_SDK}/include/libcxx") + +#ADD_COMPILE_OPTIONS($<$:-std=c++11>) + +################################################################################ +# Internal SGX Variables +################################################################################ + +IF (${SGX_MODE} STREQUAL "SIM") + SET(TRTS_LIBRARY_NAME "sgx_trts_sim") + SET(URTS_LIBRARY_NAME "sgx_urts_sim") + SET(UAE_SERVICE_LIBRARY_NAME "sgx_uae_service_sim") + SET(SERVICE_LIBRARY_NAME "sgx_tservice_sim") + SET(SGX_EPID_LIB sgx_epid_sim) +ELSE() + SET(TRTS_LIBRARY_NAME "sgx_trts") + SET(URTS_LIBRARY_NAME "sgx_urts") + SET(UAE_SERVICE_LIBRARY_NAME "sgx_uae_service") + SET(SERVICE_LIBRARY_NAME "sgx_tservice") + SET(SGX_EPID_LIB sgx_epid) +ENDIF() + +################################################################################ +# Functions +################################################################################ + +FUNCTION(SGX_EDGE_TRUSTED EDL EDGE_FILES) + GET_FILENAME_COMPONENT(EDL_BASE_NAME ${EDL} NAME_WE) + GET_FILENAME_COMPONENT(EDL_DIR_NAME ${EDL} DIRECTORY) + INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR}) + + SET (EDGE_FILES_LIST "${CMAKE_CURRENT_BINARY_DIR}/${EDL_BASE_NAME}_t.h" "${CMAKE_CURRENT_BINARY_DIR}/${EDL_BASE_NAME}_t.c") + SET (${EDGE_FILES} ${EDGE_FILES_LIST} PARENT_SCOPE) + ADD_CUSTOM_COMMAND( OUTPUT ${EDGE_FILES_LIST} + COMMAND "${SGX_EDGER}" --trusted ${EDL} --search-path ${SGX_SEARCH_PATH} --search-path ${EDL_DIR_NAME} + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + DEPENDS ${EDL} + ) +ENDFUNCTION() + +FUNCTION(SGX_EDGE_UNTRUSTED EDL EDGE_FILES) + GET_FILENAME_COMPONENT(EDL_BASE_NAME ${EDL} NAME_WE) + GET_FILENAME_COMPONENT(EDL_DIR_NAME ${EDL} DIRECTORY) + INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR}) + + SET (EDGE_FILES_LIST "${CMAKE_CURRENT_BINARY_DIR}/${EDL_BASE_NAME}_u.h" "${CMAKE_CURRENT_BINARY_DIR}/${EDL_BASE_NAME}_u.c") + SET (${EDGE_FILES} ${EDGE_FILES_LIST} PARENT_SCOPE) + ADD_CUSTOM_COMMAND( OUTPUT ${EDGE_FILES_LIST} + COMMAND "${SGX_EDGER}" --untrusted ${EDL} --search-path ${SGX_SEARCH_PATH} --search-path ${EDL_DIR_NAME} + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + DEPENDS ${EDL} + ) +ENDFUNCTION() + +FUNCTION(SGX_SIGN_ENCLAVE TARGET KEY_FILE CONFIG) + SET (ENCLAVE $) + + SET (SIGNED_ENCLAVE ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/${TARGET}.signed${CMAKE_SHARED_LIBRARY_SUFFIX}) + SET (SIGNED_ENCLAVE ${SIGNED_ENCLAVE} PARENT_SCOPE) + SET (SIGNED_ENCLAVE_METADATA ${SIGNED_ENCLAVE}".meta") + ADD_CUSTOM_COMMAND( TARGET ${TARGET} + POST_BUILD + COMMAND "${SGX_SIGN}" sign -key "${KEY_FILE}" -enclave "${ENCLAVE}" -out "${SIGNED_ENCLAVE}" -dumpfile ${SIGNED_ENCLAVE_METADATA} -config "${CONFIG}" > /dev/null + ) +ENDFUNCTION() + diff --git a/test/verify_evidence_app/enclave/test.cpp b/test/verify_evidence_app/enclave/test.cpp new file mode 100644 index 0000000..de5d288 --- /dev/null +++ b/test/verify_evidence_app/enclave/test.cpp @@ -0,0 +1,27 @@ +/* + * Copyright 2024 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "stdbool.h" +#include "verify-evidence.h" +#include "logging.h" +#include "test_verify_enclave_t.h" + + +bool verify_ev(uint8_t* evidence, + uint32_t evidence_length, + uint8_t* expected_statement, + uint32_t expected_statement_length, + uint8_t* expected_code_id, + uint32_t expected_code_id_length) +{ + return verify_evidence( + evidence, + evidence_length, + expected_statement, + expected_statement_length, + expected_code_id, + expected_code_id_length); +} diff --git a/test/verify_evidence_app/enclave/test_verify_enclave.config.xml b/test/verify_evidence_app/enclave/test_verify_enclave.config.xml new file mode 100644 index 0000000..4912376 --- /dev/null +++ b/test/verify_evidence_app/enclave/test_verify_enclave.config.xml @@ -0,0 +1,18 @@ + + + + 0 + 0 + 0x40000 + 0x100000 + 1 + 1 + + 0 + 0 + 0xFFFFFFFF + diff --git a/test/verify_evidence_app/enclave/test_verify_enclave.edl b/test/verify_evidence_app/enclave/test_verify_enclave.edl new file mode 100644 index 0000000..d60db14 --- /dev/null +++ b/test/verify_evidence_app/enclave/test_verify_enclave.edl @@ -0,0 +1,20 @@ +/* + * Copyright 2024 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "stdbool.h" + +enclave { + from "sgx_tstdc.edl" import *; + from "../../../ocalls/attestation-ocalls.edl" import *; + from "../../../common/logging/ocalls/logging.edl" import *; + + trusted { + public bool verify_ev( + [in, size=evidence_length] uint8_t* evidence, uint32_t evidence_length, + [in, size=expected_statement_length] uint8_t* expected_statement, uint32_t expected_statement_length, + [in, size=expected_code_id_length] uint8_t* expected_code_id, uint32_t expected_code_id_length); + }; +}; diff --git a/test/verify_evidence_app/enclave/test_verify_enclave.lds b/test/verify_evidence_app/enclave/test_verify_enclave.lds new file mode 100644 index 0000000..2adb435 --- /dev/null +++ b/test/verify_evidence_app/enclave/test_verify_enclave.lds @@ -0,0 +1,15 @@ +/* + * Copyright 2020 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +test_verify_enclave.so +{ + global: + g_global_data_sim; + g_global_data; + test_verify_enclave_entry; + local: + *; +}; diff --git a/test/verify_evidence_app/main.cpp b/test/verify_evidence_app/main.cpp new file mode 100644 index 0000000..996acf2 --- /dev/null +++ b/test/verify_evidence_app/main.cpp @@ -0,0 +1,19 @@ +/* + * Copyright 2020 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "test.h" + +int main() +{ + bool b = test(); + if (b) + { + // success + return 0; + } + // error + return -1; +} diff --git a/test/verify_evidence_app/test-enclave.cpp b/test/verify_evidence_app/test-enclave.cpp new file mode 100644 index 0000000..a503798 --- /dev/null +++ b/test/verify_evidence_app/test-enclave.cpp @@ -0,0 +1,112 @@ +/* + * Copyright 2024 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "test.h" +#include +#include "error.h" +#include "logging.h" +#include "test-defines.h" +#include "test-utils.h" + +#include + +#include "attestation_tags.h" +#include "verify-evidence.h" + +#include "sgx_eid.h" +#include "sgx_error.h" +#include "sgx_urts.h" +#include "test-defines.h" +#include "test-utils.h" +#include "test_verify_enclave_u.h" + +bool test() +{ + sgx_launch_token_t token = {0}; + int updated = 0; + sgx_enclave_id_t global_eid = 0; + sgx_status_t ret = SGX_ERROR_UNEXPECTED; + + ret = sgx_create_enclave(VERIFY_ENCLAVE_FILENAME, 1, &token, &updated, &global_eid, NULL); + if (ret != SGX_SUCCESS) + { + puts("error creating enclave"); + exit(-1); + } + + uint32_t buffer_length = 1 << 20; + char buffer[buffer_length]; + uint32_t filled_size; + std::string jsonevidence; + std::string expected_statement; + std::string expected_code_id; + std::string wrong_expected_statement; + std::string wrong_expected_code_id; + + COND2LOGERR(!load_file(EVIDENCE_FILE, buffer, buffer_length, &filled_size), + "can't read input evidence " EVIDENCE_FILE); + jsonevidence = std::string(buffer, filled_size); + + COND2LOGERR(!load_file(STATEMENT_FILE, buffer, buffer_length, &filled_size), + "can't read input statement " STATEMENT_FILE); + expected_statement = std::string(buffer, filled_size); + + COND2LOGERR(!load_file(CODE_ID_FILE, buffer, buffer_length, &filled_size), + "can't read input code id " CODE_ID_FILE); + expected_code_id = std::string(buffer, filled_size); + + wrong_expected_statement = std::string("wrong statement"); + wrong_expected_code_id = + std::string("BADBADBADBAD9E317C4F7312A0D644FFC052F7645350564D43586D8102663358"); + + bool b, expected_b; + // test normal situation + expected_b = true; b = !expected_b; + ret = verify_ev(global_eid, &b, + (uint8_t*)jsonevidence.c_str(), jsonevidence.length(), + (uint8_t*)expected_statement.c_str(), expected_statement.length(), + (uint8_t*)expected_code_id.c_str(), expected_code_id.length()); + COND2LOGERR(ret != SGX_SUCCESS, "sgx error: %x", ret); + COND2LOGERR(b != expected_b, "correct evidence failed"); + + // this test succeeds for simulated attestations, and fails for real ones + // test with wrong statement + expected_b = (jsonevidence.find(SIMULATED_TYPE_TAG) == std::string::npos ? false : true); + b = !expected_b; + if (expected_b == false) + { + LOG_WARNING("next test expected to fail"); + } + ret = verify_ev(global_eid, &b, + (uint8_t*)jsonevidence.c_str(), jsonevidence.length(), + (uint8_t*)wrong_expected_statement.c_str(), wrong_expected_statement.length(), + (uint8_t*)expected_code_id.c_str(), expected_code_id.length()); + COND2LOGERR(ret != SGX_SUCCESS, "sgx error: %x", ret); + COND2LOGERR(b != expected_b, "evidence with bad statement succeeded"); + + // this test succeeds for simulated attestations, and fails for real ones + // test with wrong code id + expected_b = (jsonevidence.find(SIMULATED_TYPE_TAG) == std::string::npos ? false : true); + b = !expected_b; + if (expected_b == false) + { + LOG_WARNING("next test expected to fail"); + } + ret = verify_ev(global_eid, &b, + (uint8_t*)jsonevidence.c_str(), jsonevidence.length(), + (uint8_t*)expected_statement.c_str(), expected_statement.length(), + (uint8_t*)wrong_expected_code_id.c_str(), wrong_expected_code_id.length()); + COND2LOGERR(ret != SGX_SUCCESS, "sgx error: %x", ret); + COND2LOGERR(b != expected_b, "evidence with bad code id succeeded"); + + sgx_destroy_enclave(global_eid); + + LOG_INFO("Test Successful\n"); + return true; + +err: + return false; +} diff --git a/test/verify_evidence_app/test.cpp b/test/verify_evidence_app/test.cpp new file mode 100644 index 0000000..d2db190 --- /dev/null +++ b/test/verify_evidence_app/test.cpp @@ -0,0 +1,80 @@ +/* + * Copyright 2020 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "test.h" +#include +#include "error.h" +#include "logging.h" +#include "test-defines.h" +#include "test-utils.h" + +#include "attestation_tags.h" +#include "verify-evidence.h" + +bool test() +{ + uint32_t buffer_length = 1 << 20; + char buffer[buffer_length]; + uint32_t filled_size; + std::string jsonevidence; + std::string expected_statement; + std::string expected_code_id; + std::string wrong_expected_statement; + std::string wrong_expected_code_id; + + COND2LOGERR(!load_file(EVIDENCE_FILE, buffer, buffer_length, &filled_size), + "can't read input evidence " EVIDENCE_FILE); + jsonevidence = std::string(buffer, filled_size); + + COND2LOGERR(!load_file(STATEMENT_FILE, buffer, buffer_length, &filled_size), + "can't read input statement " STATEMENT_FILE); + expected_statement = std::string(buffer, filled_size); + + COND2LOGERR(!load_file(CODE_ID_FILE, buffer, buffer_length, &filled_size), + "can't read input code id " CODE_ID_FILE); + expected_code_id = std::string(buffer, filled_size); + + wrong_expected_statement = std::string("wrong statement"); + wrong_expected_code_id = + std::string("BADBADBADBAD9E317C4F7312A0D644FFC052F7645350564D43586D8102663358"); + + bool b, expected_b; + // test normal situation + b = verify_evidence((uint8_t*)jsonevidence.c_str(), jsonevidence.length(), + (uint8_t*)expected_statement.c_str(), expected_statement.length(), + (uint8_t*)expected_code_id.c_str(), expected_code_id.length()); + COND2LOGERR(!b, "correct evidence failed"); + + // this test succeeds for simulated attestations, and fails for real ones + // test with wrong statement + expected_b = (jsonevidence.find(SIMULATED_TYPE_TAG) == std::string::npos ? false : true); + if (expected_b == false) + { + LOG_WARNING("next test expected to fail"); + } + b = verify_evidence((uint8_t*)jsonevidence.c_str(), jsonevidence.length(), + (uint8_t*)wrong_expected_statement.c_str(), wrong_expected_statement.length(), + (uint8_t*)expected_code_id.c_str(), expected_code_id.length()); + COND2LOGERR(b != expected_b, "evidence with bad statement succeeded"); + + // this test succeeds for simulated attestations, and fails for real ones + // test with wrong code id + expected_b = (jsonevidence.find(SIMULATED_TYPE_TAG) == std::string::npos ? false : true); + if (expected_b == false) + { + LOG_WARNING("next test expected to fail"); + } + b = verify_evidence((uint8_t*)jsonevidence.c_str(), jsonevidence.length(), + (uint8_t*)expected_statement.c_str(), expected_statement.length(), + (uint8_t*)wrong_expected_code_id.c_str(), wrong_expected_code_id.length()); + COND2LOGERR(b != expected_b, "evidence with bad code id succeeded"); + + LOG_INFO("Test Successful\n"); + return true; + +err: + return false; +} diff --git a/test/verify_evidence_app/test.h b/test/verify_evidence_app/test.h new file mode 100644 index 0000000..99d5015 --- /dev/null +++ b/test/verify_evidence_app/test.h @@ -0,0 +1,9 @@ +/* + * Copyright 2020 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +bool test(); diff --git a/usage.md b/usage.md new file mode 100644 index 0000000..1f3f542 --- /dev/null +++ b/usage.md @@ -0,0 +1,52 @@ +The attestation library is available under the [attestation-dev branch](https://github.com/hyperledger-labs/private-data-objects/tree/attestation-dev). + +The library is divided in 3 main components: +1. attestation generation, inside the enclave +2. attestation conversion to evidence, outside of the enclave +3. evidence verification, inside or outside of the enclave + +![image](https://github.com/user-attachments/assets/d3042b3a-37dd-4966-9c8a-9561e93f6174) + +The figure depicts the flows for attestation generation, conversion and verification. +For additional technical details look at the `test/attested_evidence_test.sh` script in the library. +Also, for additional details about the APIs look at the headers in the `include` folder. + +**Attestation generation.** +The library exposes the `init_attestation(params)` and `attestation_blob = get_attestation(statement)` APIs. +`params` is a json blob to initialize the internal state of the library for an attestation. The `statement` is a binary string which (in SGX) is hashed to generate the "report data" to be attested. +```jsonc +// params json blob +{ + "attestation_type": "", + "hex_spid": "", // only for EPID + "sig_rl": "" // only for EPID +} +``` +`attestation` is a json blob containing the remote attestation. So notice that, for EPID and DCAP, the trusted attestation library has ocalls in untrusted space to get the remote attestation from the Intel Quoting Enclave. +```jsonc +// attestation json blob +{ + "attestation_type": "", + "attestation": "" +} +``` + +**Attestation conversion.** +The library provides a script for converting the attestation into evidence: `evidence_blob = attestation_to_evidence(attestation_blob)`. Additional sub-scripts perform the conversion based on the attestation type. For the `simulated` type, it simply copies the attestation fields into the evidence field. For EPID, the script contacts the Intel Attestation Service (IAS) for verification -- an API key is required in the collateral folder. For `dcap-sgx`, the script contacts the Intel Trust Authority (ITA) for verification through the provided url -- an API key is required in the collateral folder. For `dcap-direct-sgx`, the script uses the DCAP library to retrieve the collateral from the Intel Provisioning Certification Service (PCS) or the Intel Provisioning Certificate Caching Service (PCCS), depending on the configuration in `/etc/sgx_default_qcnl.conf`. Here, no third-party verification is performed -- the verification fully happens in the last step. +```jsonc +// evidence json blob +{ + "attestation_type": "", + "evidence": + { + "ias_report": "<>", // only for EPID + "ias_certificates": "<>", // only for EPID + "ias_signature": "<>", // only for EPID + "collateral": "", // only for DCAP + "untrusted-time-t": "" // only EPID/DCAP +} +``` + +**Attestation verification.** +The attestation library exposes the `verify_evidence(evidence_blob, statement, code_id)` API. In SGX, the code identity refers to the `mrenclave` value. The verification result is simply `true` or `false`, depending on the outcome. The API mainly verifies: the chain of trust of the evidence (i.e., none for the `simulated` type; up to the IAS root CA for EPID; up to the SGX Root CA for the `dcap-direct-sgx` type; up to the ITA root CA for the `dcap-sgx` type. The respective root CA certificates are all retrieved at build time and compiled into the library. +