diff --git a/CHANGELOG.md b/CHANGELOG.md index 57a1c30590c..a3b308cc9b1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,14 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). +## [6.0.0-dev7] + +[6.0.0-dev7]: https://github.com/microsoft/CCF/releases/tag/6.0.0-dev7 + +### Fixed + +- Services upgrading from 4.x to 5.x may accidentally change their service's subject name, resulting in cryptographic errors when verifying anything endorsed by the old subject name. The subject name field is now correctly populated and retained across joins, renewals, and disaster recoveries. + ## [6.0.0-dev6] [6.0.0-dev6]: https://github.com/microsoft/CCF/releases/tag/6.0.0-dev6 diff --git a/python/pyproject.toml b/python/pyproject.toml index 81e6f2ad6af..fca4ea8611a 100644 --- a/python/pyproject.toml +++ b/python/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "ccf" -version = "6.0.0-dev6" +version = "6.0.0-dev7" authors = [ { name="CCF Team", email="CCF-Sec@microsoft.com" }, ] diff --git a/src/node/identity.h b/src/node/identity.h index 6aad7ca03b9..de43503deb2 100644 --- a/src/node/identity.h +++ b/src/node/identity.h @@ -3,6 +3,7 @@ #pragma once #include "ccf/crypto/curve.h" +#include "ccf/crypto/verifier.h" #include "ccf/node/cose_signatures_config.h" #include "crypto/certs.h" #include "crypto/openssl/key_pair.h" @@ -91,7 +92,8 @@ namespace ccf } ReplicatedNetworkIdentity(const NetworkIdentity& other) : - NetworkIdentity(other.subject_name, other.cose_signatures_config) + NetworkIdentity( + ccf::crypto::get_subject_name(other.cert), other.cose_signatures_config) { if (type != other.type) { diff --git a/tests/lts_compatibility.py b/tests/lts_compatibility.py index a34b56ecddf..dd6b3c8d947 100644 --- a/tests/lts_compatibility.py +++ b/tests/lts_compatibility.py @@ -18,6 +18,8 @@ from governance import test_all_nodes_cert_renewal, test_service_cert_renewal from infra.snp import IS_SNP from distutils.dir_util import copy_tree +from cryptography import x509 +from cryptography.hazmat.backends import default_backend from loguru import logger as LOG @@ -98,6 +100,7 @@ def test_new_service( binary_dir, library_dir, version, + expected_subject_name=None, ): if IS_SNP: LOG.info( @@ -149,6 +152,15 @@ def test_new_service( test_all_nodes_cert_renewal(network, args, valid_from=valid_from) test_service_cert_renewal(network, args, valid_from=valid_from) + if expected_subject_name: + LOG.info(f"Confirming subject name == {expected_subject_name}") + with primary.client() as c: + r = c.get("/node/network") + assert r.status_code == 200, r + cert_pem = r.body.json()["service_certificate"] + cert = x509.load_pem_x509_certificate(cert_pem.encode(), default_backend()) + assert cert.subject.rfc4514_string() == expected_subject_name, cert + LOG.info("Apply transactions to new nodes only") issue_activity_on_live_service(network, args) test_random_receipts(network, args, lts=True, log_capture=[]) @@ -206,6 +218,8 @@ def run_code_upgrade_from( set_js_args(args, from_install_path, to_install_path) + service_subject_name = "CN=LTS custom service name" + jwt_issuer = infra.jwt_issuer.JwtIssuer( "https://localhost", refresh_interval=args.jwt_key_refresh_interval_s ) @@ -225,7 +239,10 @@ def run_code_upgrade_from( kwargs["reconfiguration_type"] = "OneTransaction" network.start_and_open( - args, node_container_image=from_container_image, **kwargs + args, + node_container_image=from_container_image, + service_subject_name=service_subject_name, + **kwargs, ) old_nodes = network.get_joined_nodes() @@ -287,6 +304,26 @@ def run_code_upgrade_from( version == expected_version ), f"For node {node.local_node_id}, expect version {expected_version}, got {version}" + # Verify that either custom service_subject_name was applied, + # or that a default name is used + primary, _ = network.find_primary() + with primary.client() as c: + r = c.get("/node/network") + assert r.status_code == 200, r + cert_pem = r.body.json()["service_certificate"] + cert = x509.load_pem_x509_certificate( + cert_pem.encode(), default_backend() + ) + version = primary.version or args.ccf_version + if not infra.node.version_after(version, "ccf-5.0.0-dev14"): + service_subject_name = cert.subject.rfc4514_string() + LOG.info( + f"Custom subject name not supported on {version}, so falling back to default {service_subject_name}" + ) + else: + LOG.info(f"Custom subject name should be supported on {version}") + assert cert.subject.rfc4514_string() == service_subject_name, cert + LOG.info("Apply transactions to hybrid network, with primary as old node") issue_activity_on_live_service(network, args) @@ -371,6 +408,7 @@ def run_code_upgrade_from( to_binary_dir, to_library_dir, to_version, + service_subject_name, ) network.get_latest_ledger_public_state()