diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index 1dc74929..83fa10c8 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -1,4 +1,4 @@ -FROM mcr.microsoft.com/ccf/app/dev:4.0.7-virtual +FROM mcr.microsoft.com/ccf/app/dev:4.0.12-virtual # Dependency of the virtual build of attested-fetch. RUN apt-get update && apt-get install -y libcurl4-openssl-dev diff --git a/.github/workflows/build-test-virtual.yml b/.github/workflows/build-test-virtual.yml index 6477c1e3..0d0fd01a 100644 --- a/.github/workflows/build-test-virtual.yml +++ b/.github/workflows/build-test-virtual.yml @@ -7,7 +7,7 @@ jobs: run: shell: bash container: - image: mcr.microsoft.com/ccf/app/dev:4.0.7-virtual + image: mcr.microsoft.com/ccf/app/dev:4.0.12-virtual env: # Helps to distinguish between CI and local builds. SCITT_CI: 1 diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 9828edec..beea3bd3 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -17,7 +17,7 @@ jobs: name: Analyze runs-on: ubuntu-latest - container: mcr.microsoft.com/ccf/app/dev:4.0.7-virtual + container: mcr.microsoft.com/ccf/app/dev:4.0.12-virtual permissions: actions: read diff --git a/.pipelines/azure-pipelines.yml b/.pipelines/azure-pipelines.yml index 530706e4..507d12b7 100644 --- a/.pipelines/azure-pipelines.yml +++ b/.pipelines/azure-pipelines.yml @@ -9,7 +9,7 @@ trigger: parameters: - name: CCF_VERSION type: string - default: 4.0.7 + default: 4.0.12 resources: containers: diff --git a/DEVELOPMENT.md b/DEVELOPMENT.md index f35d0194..4f40b216 100644 --- a/DEVELOPMENT.md +++ b/DEVELOPMENT.md @@ -46,10 +46,10 @@ Follow the steps below to setup your development environment, replacing ` -e clang_version=<11|15> + wget https://github.com/microsoft/CCF/archive/refs/tags/ccf-4.0.12.tar.gz + tar xvzf ccf-4.0.12.tar.gz + cd CCF-ccf-4.0.12/getting_started/setup_vm/ + ./run.sh app-dev.yml -e ccf_ver=4.0.12 -e platform= -e clang_version=<11|15> ``` ## Building diff --git a/app/CMakeLists.txt b/app/CMakeLists.txt index 444264fc..d42df3dd 100644 --- a/app/CMakeLists.txt +++ b/app/CMakeLists.txt @@ -30,9 +30,9 @@ if (CCF_UNSAFE) message(FATAL_ERROR "The unsafe CCF variant is only available on SGX builds.") endif() - find_package(ccf_sgx_unsafe 4.0.7 REQUIRED) + find_package(ccf_sgx_unsafe 4.0.12 REQUIRED) else() - find_package(ccf_${COMPILE_TARGET} 4.0.7 REQUIRED) + find_package(ccf_${COMPILE_TARGET} 4.0.12 REQUIRED) endif() if (ENABLE_PREFIX_TREE) diff --git a/app/src/openssl_wrappers.h b/app/src/openssl_wrappers.h index 59146c31..51cb013f 100644 --- a/app/src/openssl_wrappers.h +++ b/app/src/openssl_wrappers.h @@ -9,11 +9,13 @@ #include #include #include +#include #include #include #include #include #include +#include #include #include #include @@ -21,6 +23,10 @@ // Note: This file was extended from: // https://github.com/microsoft/CCF/blob/main/src/crypto/openssl/openssl_wrappers.h +#if defined(OPENSSL_VERSION_MAJOR) && OPENSSL_VERSION_MAJOR >= 3 +# include +#endif + namespace scitt { namespace OpenSSL @@ -49,7 +55,7 @@ namespace scitt } } - /// Throws if rc is 1 and has error + /// Throws if rc is not 1 and has error inline void CHECK1(int rc) { unsigned long ec = ERR_get_error(); @@ -80,6 +86,26 @@ namespace scitt } } + // Throws if values are not equal + inline void CHECKEQUAL(int expect, int actual) + { + if (expect != actual) + { + unsigned long ec = ERR_get_error(); + throw std::runtime_error( + fmt::format("OpenSSL error: {}", error_string(ec))); + } + } + + // Throws if value is not positive + inline void CHECKPOSITIVE(int val) + { + if (val <= 0) + { + throw std::runtime_error("OpenSSL error: expected positive value"); + } + } + /* * Unique pointer wrappers for SSL objects, with SSL' specific constructors * and destructors. Some objects need special functionality, others are just @@ -177,6 +203,12 @@ namespace scitt Unique_SSL_OBJECT( PEM_read_bio_PUBKEY(mem, NULL, NULL, NULL), EVP_PKEY_free) {} + +#if defined(OPENSSL_VERSION_MAJOR) && OPENSSL_VERSION_MAJOR >= 3 + Unique_PKEY(EVP_PKEY* pkey) : + Unique_SSL_OBJECT(EVP_PKEY_dup(pkey), EVP_PKEY_free) + {} +#endif }; struct Unique_EVP_PKEY_CTX @@ -189,6 +221,14 @@ namespace scitt Unique_SSL_OBJECT( EVP_PKEY_CTX_new_id(EVP_PKEY_EC, NULL), EVP_PKEY_CTX_free) {} + +#if defined(OPENSSL_VERSION_MAJOR) && OPENSSL_VERSION_MAJOR >= 3 + Unique_EVP_PKEY_CTX(const std::string& name) : + Unique_SSL_OBJECT( + EVP_PKEY_CTX_new_from_name(NULL, name.c_str(), NULL), + EVP_PKEY_CTX_free) + {} +#endif }; struct Unique_X509_REQ @@ -287,6 +327,8 @@ namespace scitt struct Unique_BIGNUM : public Unique_SSL_OBJECT { using Unique_SSL_OBJECT::Unique_SSL_OBJECT; + + Unique_BIGNUM(const BIGNUM* n) : Unique_BIGNUM(BN_dup(n), BN_free) {} }; struct Unique_X509_TIME @@ -330,25 +372,33 @@ namespace scitt Unique_SSL_OBJECT( EC_POINT_new(group), EC_POINT_free, /*check_null=*/true) {} + Unique_EC_POINT(EC_POINT* point) : + Unique_SSL_OBJECT(point, EC_POINT_free, /*check_null=*/true) + {} }; +#if !(defined(OPENSSL_VERSION_MAJOR) && OPENSSL_VERSION_MAJOR >= 3) struct Unique_EC_KEY : public Unique_SSL_OBJECT { Unique_EC_KEY(int nid) : Unique_SSL_OBJECT( EC_KEY_new_by_curve_name(nid), EC_KEY_free, /*check_null=*/true) {} + Unique_EC_KEY(EC_KEY* key) : + Unique_SSL_OBJECT(key, EC_KEY_free, /*check_null=*/true) + {} }; - struct Unique_EVP_ENCODE_CTX : public Unique_SSL_OBJECT< - EVP_ENCODE_CTX, - EVP_ENCODE_CTX_new, - EVP_ENCODE_CTX_free> + struct Unique_RSA : public Unique_SSL_OBJECT { using Unique_SSL_OBJECT::Unique_SSL_OBJECT; }; +#endif - struct Unique_RSA : public Unique_SSL_OBJECT + struct Unique_EVP_ENCODE_CTX : public Unique_SSL_OBJECT< + EVP_ENCODE_CTX, + EVP_ENCODE_CTX_new, + EVP_ENCODE_CTX_free> { using Unique_SSL_OBJECT::Unique_SSL_OBJECT; }; diff --git a/app/src/public_key.h b/app/src/public_key.h index 03abec50..0d9c71d5 100644 --- a/app/src/public_key.h +++ b/app/src/public_key.h @@ -7,6 +7,12 @@ #include +#if defined(OPENSSL_VERSION_MAJOR) && OPENSSL_VERSION_MAJOR >= 3 +# include +# include +# include +#endif + namespace scitt { class PublicKey @@ -20,6 +26,43 @@ namespace scitt cose_alg(cose_alg) {} +#if defined(OPENSSL_VERSION_MAJOR) && OPENSSL_VERSION_MAJOR >= 3 + PublicKey( + std::vector& n_raw, + std::vector& e_raw, + std::optional cose_alg) : + cose_alg(cose_alg) + { + OSSL_PARAM params[3]; + params[0] = OSSL_PARAM_construct_BN( + OSSL_PKEY_PARAM_RSA_N, n_raw.data(), n_raw.size()); + params[1] = OSSL_PARAM_construct_BN( + OSSL_PKEY_PARAM_RSA_E, e_raw.data(), e_raw.size()); + params[2] = OSSL_PARAM_construct_end(); + + OpenSSL::Unique_EVP_PKEY_CTX pctx("RSA"); + OpenSSL::CHECK1(EVP_PKEY_fromdata_init(pctx)); + OpenSSL::CHECK1( + EVP_PKEY_fromdata(pctx, (EVP_PKEY**)&key, EVP_PKEY_PUBLIC_KEY, params)); + } + + PublicKey( + std::vector& buf, int nid, std::optional cose_alg) : + cose_alg(cose_alg) + { + OSSL_PARAM params[3]; + params[0] = OSSL_PARAM_construct_utf8_string( + OSSL_PKEY_PARAM_GROUP_NAME, (char*)OSSL_EC_curve_nid2name(nid), 0); + params[1] = OSSL_PARAM_construct_octet_string( + OSSL_PKEY_PARAM_PUB_KEY, buf.data(), buf.size()); + params[2] = OSSL_PARAM_construct_end(); + + OpenSSL::Unique_EVP_PKEY_CTX pctx("EC"); + OpenSSL::CHECK1(EVP_PKEY_fromdata_init(pctx)); + OpenSSL::CHECK1( + EVP_PKEY_fromdata(pctx, (EVP_PKEY**)&key, EVP_PKEY_PUBLIC_KEY, params)); + } +#else PublicKey( const OpenSSL::Unique_RSA& rsa_key, std::optional cose_alg) : cose_alg(cose_alg) @@ -39,6 +82,7 @@ namespace scitt throw std::runtime_error("EC key could not be set"); } } +#endif PublicKey( int ossl_type, diff --git a/app/src/verifier.h b/app/src/verifier.h index 43ad5061..393c759b 100644 --- a/app/src/verifier.h +++ b/app/src/verifier.h @@ -14,6 +14,12 @@ #include #include +#if defined(OPENSSL_VERSION_MAJOR) && OPENSSL_VERSION_MAJOR >= 3 +# include +# include +# include +#endif + namespace scitt::verifier { struct VerificationError : public std::runtime_error @@ -538,6 +544,17 @@ namespace scitt::verifier throw VerificationError("JWK e could not be parsed"); } +#if defined(OPENSSL_VERSION_MAJOR) && OPENSSL_VERSION_MAJOR >= 3 + std::pair, std::vector> r( + BN_num_bytes(n_bn), BN_num_bytes(e_bn)); + + OpenSSL::CHECKPOSITIVE( + BN_bn2nativepad(n_bn, r.first.data(), r.first.size())); + OpenSSL::CHECKPOSITIVE( + BN_bn2nativepad(e_bn, r.second.data(), r.second.size())); + + return PublicKey(r.first, r.second, cose_alg); +#else OpenSSL::Unique_RSA rsa; if (!RSA_set0_key(rsa, n_bn, e_bn, nullptr)) { @@ -548,6 +565,7 @@ namespace scitt::verifier (void)e_bn.release(); return PublicKey(rsa, cose_alg); +#endif } if (jwk.kty == "OKP" && jwk.crv == "Ed25519" && jwk.x.has_value()) @@ -590,6 +608,26 @@ namespace scitt::verifier { throw VerificationError("JWK EC Key has no valid supported curve"); } + +#if defined(OPENSSL_VERSION_MAJOR) && OPENSSL_VERSION_MAJOR >= 3 + OpenSSL::Unique_BN_CTX bn_ctx; + OpenSSL::Unique_EC_GROUP group(nid); + OpenSSL::Unique_EC_POINT p(group); + OpenSSL::CHECK1( + EC_POINT_set_affine_coordinates(group, p, x_bn, y_bn, bn_ctx)); + size_t buf_size = EC_POINT_point2oct( + group, p, POINT_CONVERSION_UNCOMPRESSED, nullptr, 0, bn_ctx); + std::vector buf(buf_size); + OpenSSL::CHECKPOSITIVE(EC_POINT_point2oct( + group, + p, + POINT_CONVERSION_UNCOMPRESSED, + buf.data(), + buf.size(), + bn_ctx)); + + return PublicKey(buf, nid, cose_alg); +#else auto ec_key = OpenSSL::Unique_EC_KEY(nid); if (!EC_KEY_set_public_key_affine_coordinates(ec_key, x_bn, y_bn)) { @@ -597,6 +635,7 @@ namespace scitt::verifier } return PublicKey(ec_key, cose_alg); +#endif } throw VerificationError("JWK has no valid supported key"); diff --git a/app/unit-tests/cbor_test.cpp b/app/unit-tests/cbor_test.cpp index 4df332f4..11409f58 100644 --- a/app/unit-tests/cbor_test.cpp +++ b/app/unit-tests/cbor_test.cpp @@ -3,6 +3,8 @@ #include "cbor.h" +#include "cbor_test_utils.h" + #include #include #include @@ -13,6 +15,7 @@ namespace { RC_GTEST_PROP(CborHasher, hash_text, ()) { + crypto::openssl_sha256_init(); auto text = *rc::gen::arbitrary(); cbor::hasher hasher; @@ -24,10 +27,12 @@ namespace auto h2 = crypto::Sha256Hash(encoder.finish()); RC_ASSERT(h1 == h2); + crypto::openssl_sha256_shutdown(); } RC_GTEST_PROP(Cbor, hash_bytes, ()) { + crypto::openssl_sha256_init(); auto bytes = *rc::gen::arbitrary>(); cbor::hasher hasher; @@ -39,10 +44,12 @@ namespace auto h2 = crypto::Sha256Hash(encoder.finish()); RC_ASSERT(h1 == h2); + crypto::openssl_sha256_shutdown(); } RC_GTEST_PROP(Cbor, hash_array, ()) { + crypto::openssl_sha256_init(); auto fields = *rc::gen::arbitrary>>(); cbor::hasher hasher; @@ -63,5 +70,6 @@ namespace auto h2 = crypto::Sha256Hash(encoder.finish()); RC_ASSERT(h1 == h2); + crypto::openssl_sha256_shutdown(); } } diff --git a/app/unit-tests/cbor_test_utils.h b/app/unit-tests/cbor_test_utils.h new file mode 100644 index 00000000..a383db8a --- /dev/null +++ b/app/unit-tests/cbor_test_utils.h @@ -0,0 +1,21 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#pragma once + +// These are functions implemented in CCF (see +// https://github.com/microsoft/CCF/blob/8f06c5fa00e5dae51e5b489ef9d312aa7f2496e4/src/crypto/openssl/hash.cpp#L43-L81). +// They are used to create and destroy a thread-local cache of contexts for +// OpenSSL operations. Calling these functions before using ccf::crypto objects +// (e.g., crypto::Sha256Hash) is not required in the context of a CCF +// application (CCF already takes care of that). However, calling them when +// running unit tests (or, in general, outside the context of the CCF +// application) is needed, otherwise an exception will be thrown. Since the two +// functions are not currently exposed in the CCF library, we define the headers +// here as a simple workaround to be able to call them and let the unit tests +// run successfully. +namespace crypto +{ + void openssl_sha256_init(); + void openssl_sha256_shutdown(); +} diff --git a/docker/enclave.Dockerfile b/docker/enclave.Dockerfile index 1c378fa7..f16208e4 100644 --- a/docker/enclave.Dockerfile +++ b/docker/enclave.Dockerfile @@ -1,4 +1,4 @@ -ARG CCF_VERSION=4.0.7 +ARG CCF_VERSION=4.0.12 FROM mcr.microsoft.com/ccf/app/dev:${CCF_VERSION}-sgx as builder ARG CCF_VERSION ARG SCITT_VERSION_OVERRIDE diff --git a/docker/virtual.Dockerfile b/docker/virtual.Dockerfile index b4fd9bc6..3062e863 100644 --- a/docker/virtual.Dockerfile +++ b/docker/virtual.Dockerfile @@ -1,4 +1,4 @@ -ARG CCF_VERSION=4.0.7 +ARG CCF_VERSION=4.0.12 FROM mcr.microsoft.com/ccf/app/dev:${CCF_VERSION}-virtual as builder ARG CCF_VERSION ARG SCITT_VERSION_OVERRIDE diff --git a/docs/reproducibility.md b/docs/reproducibility.md index a46e06ab..c85a3d5e 100644 --- a/docs/reproducibility.md +++ b/docs/reproducibility.md @@ -26,7 +26,7 @@ do a docker build locally but inside of the development version of CCF image: - Run a build inside of the CCF docker image: ``` - CCF_VERSION="4.0.7" + CCF_VERSION="4.0.12" docker run -it --rm \ -w /__w/1/s -v $(pwd):/__w/1/s \ -v /var/run/docker.sock:/var/run/docker.sock \ diff --git a/pyscitt/setup.py b/pyscitt/setup.py index e235f5f9..4751b54b 100644 --- a/pyscitt/setup.py +++ b/pyscitt/setup.py @@ -25,7 +25,7 @@ }, python_requires=">=3.8", install_requires=[ - "ccf==4.0.10", # We temporarily bump this to 4.0.10 instead of 4.0.7 (current CCF version) so that we can upgrade cryptography to a 41.* version, which fixes several security vulnerabilities. + "ccf==4.0.12", "cryptography==41.*", # needs to match ccf "httpx", "cbor2==5.4.*", diff --git a/test/requirements.txt b/test/requirements.txt index 3ba979b6..0ffd95f6 100644 --- a/test/requirements.txt +++ b/test/requirements.txt @@ -3,5 +3,5 @@ httpx pytest loguru aiotools -ccf==4.0.10 # We temporarily bump this to 4.0.10 instead of 4.0.7 (current CCF version) so that we can upgrade cryptography to a 41.* version, which fixes several security vulnerabilities. +ccf==4.0.12 cryptography==41.*