From d59590e98ac9e09c8127c48ef67b5a832599613e Mon Sep 17 00:00:00 2001 From: Amaury Chamayou Date: Thu, 22 Aug 2024 11:27:15 +0000 Subject: [PATCH 1/4] Initial removal --- CMakeLists.txt | 5 ++--- tests/code_update.py | 7 +++---- tests/infra/utils.py | 23 ++--------------------- tests/lts_compatibility.py | 2 -- 4 files changed, 7 insertions(+), 30 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 75eb5d2fd658..36e9d081387e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1162,9 +1162,8 @@ if(BUILD_TESTS) add_e2e_test( NAME code_update_test - PYTHON_SCRIPT ${CMAKE_SOURCE_DIR}/tests/code_update.py - ADDITIONAL_ARGS --oe-binary ${OE_BINDIR} --js-app-bundle - ${CMAKE_SOURCE_DIR}/samples/apps/logging/js + PYTHON_SCRIPT ${CMAKE_SOURCE_DIR}/tests/code_update.py --js-app-bundle + ${CMAKE_SOURCE_DIR}/samples/apps/logging/js ) if(BUILD_TPCC) diff --git a/tests/code_update.py b/tests/code_update.py index 3a40f29957eb..64264d457eea 100644 --- a/tests/code_update.py +++ b/tests/code_update.py @@ -342,7 +342,7 @@ def test_add_node_with_bad_code(network, args): ) new_code_id = infra.utils.get_code_id( - args.enclave_type, args.enclave_platform, args.oe_binary, replacement_package + args.enclave_type, args.enclave_platform, replacement_package ) LOG.info(f"Adding a node with unsupported code id {new_code_id}") @@ -378,10 +378,10 @@ def test_update_all_nodes(network, args): primary, _ = network.find_nodes() first_code_id = infra.utils.get_code_id( - args.enclave_type, args.enclave_platform, args.oe_binary, args.package + args.enclave_type, args.enclave_platform, args.package ) new_code_id = infra.utils.get_code_id( - args.enclave_type, args.enclave_platform, args.oe_binary, replacement_package + args.enclave_type, args.enclave_platform, replacement_package ) if args.enclave_platform == "virtual": @@ -458,7 +458,6 @@ def test_proposal_invalidation(network, args): temp_code_id = infra.utils.get_code_id( args.enclave_type, args.enclave_platform, - args.oe_binary, get_replacement_package(args), ) network.consortium.add_new_code(primary, temp_code_id) diff --git a/tests/infra/utils.py b/tests/infra/utils.py index db2b5c58fc96..163faa35a9bb 100644 --- a/tests/infra/utils.py +++ b/tests/infra/utils.py @@ -2,30 +2,11 @@ # Licensed under the Apache 2.0 License. import infra.path import hashlib -import os -import subprocess -def get_code_id( - enclave_type, enclave_platform, oe_binary_dir, package, library_dir="." -): +def get_code_id(enclave_type, enclave_platform, package, library_dir="."): lib_path = infra.path.build_lib_path( package, enclave_type, enclave_platform, library_dir ) - if enclave_platform == "sgx": - res = subprocess.run( - [os.path.join(oe_binary_dir, "oesign"), "dump", "-e", lib_path], - capture_output=True, - check=True, - ) - lines = [ - line - for line in res.stdout.decode().split(os.linesep) - if line.startswith("mrenclave=") - ] - - return lines[0].split("=")[1] - else: - # Virtual and SNP - return hashlib.sha256(lib_path.encode()).hexdigest() + return hashlib.sha256(lib_path.encode()).hexdigest() diff --git a/tests/lts_compatibility.py b/tests/lts_compatibility.py index a34b56ecddf8..ef863cddb2d7 100644 --- a/tests/lts_compatibility.py +++ b/tests/lts_compatibility.py @@ -237,7 +237,6 @@ def run_code_upgrade_from( new_code_id = infra.utils.get_code_id( args.enclave_type, args.enclave_platform, - args.oe_binary, args.package, library_dir=to_library_dir, ) @@ -293,7 +292,6 @@ def run_code_upgrade_from( old_code_id = infra.utils.get_code_id( args.enclave_type, args.enclave_platform, - args.oe_binary, args.package, library_dir=from_library_dir, ) From 3478a49f3c5eeb970328a3f7df16929279d5a5b1 Mon Sep 17 00:00:00 2001 From: Amaury Chamayou Date: Thu, 22 Aug 2024 11:38:32 +0000 Subject: [PATCH 2/4] Old nodes can be elected after removal --- tests/code_update.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/code_update.py b/tests/code_update.py index 64264d457eea..885c9418a3aa 100644 --- a/tests/code_update.py +++ b/tests/code_update.py @@ -433,8 +433,6 @@ def test_update_all_nodes(network, args): if node.node_id == primary.node_id: new_primary, _ = network.wait_for_new_primary(primary) primary = new_primary - # See https://github.com/microsoft/CCF/issues/1713 - check_can_progress(new_primary) node.stop() LOG.info("Check the network is still functional") From ff0cdd38f7019790a3ad5e809beac0ac94de8121 Mon Sep 17 00:00:00 2001 From: Amaury Chamayou Date: Tue, 17 Sep 2024 13:09:16 +0000 Subject: [PATCH 3/4] Remove test that requires SGX-specific tool, and re-order sequence --- tests/code_update.py | 51 ++++++-------------------------------------- 1 file changed, 6 insertions(+), 45 deletions(-) diff --git a/tests/code_update.py b/tests/code_update.py index 885c9418a3aa..ac5f98cd4a8c 100644 --- a/tests/code_update.py +++ b/tests/code_update.py @@ -20,38 +20,6 @@ VIRTUAL_CODE_ID = "0" * 64 -@reqs.description("Verify node evidence") -def test_verify_quotes(network, args): - if args.enclave_platform == "virtual": - LOG.warning("Skipping quote test with virtual enclave") - return network - elif snp.IS_SNP: - LOG.warning( - "Skipping quote test until there is a separate utility to verify SNP quotes" - ) - return network - - LOG.info("Check the network is stable") - primary, _ = network.find_primary() - check_can_progress(primary) - - for node in network.get_joined_nodes(): - LOG.info(f"Verifying quote for node {node.node_id}") - cafile = os.path.join(network.common_dir, "service_cert.pem") - assert ( - infra.proc.ccall( - "verify_quote.sh", - f"https://{node.get_public_rpc_host()}:{node.get_public_rpc_port()}", - "--cacert", - f"{cafile}", - log_output=True, - ).returncode - == 0 - ), f"Quote verification for node {node.node_id} failed" - - return network - - @reqs.description("Test the SNP measurements table") @reqs.snp_only() def test_snp_measurements_tables(network, args): @@ -331,10 +299,6 @@ def test_add_node_with_no_uvm_endorsements(network, args): @reqs.description("Node with bad code fails to join") def test_add_node_with_bad_code(network, args): - if args.enclave_platform != "sgx": - LOG.warning("Skipping test_add_node_with_bad_code with non-sgx enclave") - return network - replacement_package = ( "samples/apps/logging/liblogging" if args.package == "libjs_generic" @@ -481,7 +445,11 @@ def run(args): ) as network: network.start_and_open(args) - test_verify_quotes(network, args) + test_add_node_with_bad_code(network, args) + return + # NB: Assumes the current nodes are still using args.package, so must run before test_proposal_invalidation + test_proposal_invalidation(network, args) + if snp.IS_SNP: test_snp_measurements_tables(network, args) test_add_node_with_no_uvm_endorsements(network, args) @@ -490,16 +458,9 @@ def run(args): test_add_node_remove_trusted_security_policy(network, args) test_start_node_with_mismatched_host_data(network, args) test_add_node_with_bad_host_data(network, args) - test_add_node_with_bad_code(network, args) - # NB: Assumes the current nodes are still using args.package, so must run before test_proposal_invalidation - test_proposal_invalidation(network, args) - - if not snp.IS_SNP: + else: test_update_all_nodes(network, args) - # Run again at the end to confirm current nodes are acceptable - test_verify_quotes(network, args) - if __name__ == "__main__": args = infra.e2e_args.cli_args() From 393343355a54bd47e71fe9356605e5b78e632500 Mon Sep 17 00:00:00 2001 From: Amaury Chamayou Date: Mon, 23 Sep 2024 10:40:37 +0000 Subject: [PATCH 4/4] .so digest --- include/ccf/pal/attestation.h | 15 +++++++++++++-- src/ds/files.h | 16 ++++++++-------- tests/infra/remote.py | 11 +++++++++++ 3 files changed, 32 insertions(+), 10 deletions(-) diff --git a/include/ccf/pal/attestation.h b/include/ccf/pal/attestation.h index 2cc25bee6939..e4d9addec1d0 100644 --- a/include/ccf/pal/attestation.h +++ b/include/ccf/pal/attestation.h @@ -10,6 +10,7 @@ #include "ccf/ds/quote_info.h" #include "ccf/pal/measurement.h" #include "ccf/pal/snp_ioctl.h" +#include "ds/files.h" #include #include @@ -200,9 +201,19 @@ namespace ccf::pal RetrieveEndorsementCallback endorsement_cb, const snp::EndorsementsServers& endorsements_servers = {}) { + // Once we have merged cchost + .so, we can take the + // digest of argv[0] instead of using a files. + std::vector so_digest; + if (files::exists("VIRTUAL_ENCLAVE_DIGEST")) + { + so_digest = files::slurp("VIRTUAL_ENCLAVE_DIGEST"); + } + std::string so_digest_str = std::string(so_digest.begin(), so_digest.end()); + endorsement_cb( { .format = QuoteFormat::insecure_virtual, + .quote = ds::from_hex(so_digest_str), }, {}); } @@ -246,8 +257,8 @@ namespace ccf::pal throw std::logic_error( "Cannot verify virtual attestation report if node is SEV-SNP"); } - // For now, virtual resembles SGX (mostly for historical reasons) - measurement = SgxAttestationMeasurement(); + // For historical reasons, virtual resembles SGX + measurement = SgxAttestationMeasurement(quote_info.quote); report_data = SgxAttestationReportData(); } else if (quote_info.format == QuoteFormat::amd_sev_snp_v1) diff --git a/src/ds/files.h b/src/ds/files.h index 7b3e058535d7..932289e2e249 100644 --- a/src/ds/files.h +++ b/src/ds/files.h @@ -26,7 +26,7 @@ namespace files * @param file file to check * @return true if the file exists. */ - bool exists(const std::string& file) + static inline bool exists(const std::string& file) { std::ifstream f(file.c_str()); return f.good(); @@ -40,7 +40,7 @@ namespace files * exist. If true, an empty vector is returned. If false, the process exits * @return vector the file contents as bytes. */ - std::vector slurp(const std::string& file, bool optional = false) + static inline std::vector slurp(const std::string& file, bool optional = false) { std::ifstream f(file, std::ios::binary | std::ios::ate); @@ -79,13 +79,13 @@ namespace files * exist. If true, an empty vector is returned. If false, the process exits * @return std::string the file contents as a string. */ - std::string slurp_string(const std::string& file, bool optional = false) + static inline std::string slurp_string(const std::string& file, bool optional = false) { auto v = slurp(file, optional); return {v.begin(), v.end()}; } - std::optional try_slurp_string(const std::string& file) + static inline std::optional try_slurp_string(const std::string& file) { if (!fs::exists(file)) { @@ -103,7 +103,7 @@ namespace files * exits * @return nlohmann::json JSON object containing the parsed file */ - nlohmann::json slurp_json(const std::string& file, bool optional = false) + static inline nlohmann::json slurp_json(const std::string& file, bool optional = false) { auto v = slurp(file, optional); if (v.size() == 0) @@ -118,7 +118,7 @@ namespace files * @param data vector to write * @param file the path */ - void dump(const std::vector& data, const std::string& file) + static inline void dump(const std::vector& data, const std::string& file) { using namespace std; ofstream f(file, ios::binary | ios::trunc); @@ -133,12 +133,12 @@ namespace files * @param data string to write * @param file the path */ - void dump(const std::string& data, const std::string& file) + static inline void dump(const std::string& data, const std::string& file) { return dump(std::vector(data.begin(), data.end()), file); } - void rename(const fs::path& src, const fs::path& dst) + static inline void rename(const fs::path& src, const fs::path& dst) { std::error_code ec; fs::rename(src, dst, ec); diff --git a/tests/infra/remote.py b/tests/infra/remote.py index 0a6fe11e9ef3..e8d902eba34f 100644 --- a/tests/infra/remote.py +++ b/tests/infra/remote.py @@ -2,6 +2,8 @@ # Licensed under the Apache 2.0 License. import os import time +import glob +import hashlib from enum import Enum, auto import paramiko import subprocess @@ -423,6 +425,15 @@ def _setup_files(self, use_links: bool): dst_path = os.path.join(self.root, os.path.basename(path)) self.cp(path, dst_path) + vencs = glob.glob(os.path.join(self.root, "*.virtual.so")) + for venc in vencs: + with open(venc, "rb") as f: + digest = hashlib.sha256(f.read()).hexdigest() + with open( + os.path.join(self.root, "VIRTUAL_ENCLAVE_DIGEST"), "w" + ) as f: + f.write(digest) + def get( self, src_path,