Skip to content

Commit b78fbba

Browse files
authored
[release/5.x] Cherry pick: Extend pathlen for CAs (#6662) (#6667)
1 parent 31764f4 commit b78fbba

File tree

4 files changed

+49
-2
lines changed

4 files changed

+49
-2
lines changed

CHANGELOG.md

+4
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
1111

1212
- Added OpenAPI support for `std::unordered_set`.
1313

14+
### Changed
15+
16+
- Service certificates and endorsements used for historical receipts now have a pathlen constraint of 1 instead of 0, reflecting the fact that there can be a single intermediate in endorsement chains. Historically the value had been 0, which happened to work because of a quirk in OpenSSL when Issuer and Subject match on an element in the chain.
17+
1418
### Fixed
1519

1620
- 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.

src/crypto/openssl/key_pair.cpp

+3-1
Original file line numberDiff line numberDiff line change
@@ -382,7 +382,9 @@ namespace ccf::crypto
382382
std::string constraints = "critical,CA:FALSE";
383383
if (ca)
384384
{
385-
constraints = "critical,CA:TRUE,pathlen:0";
385+
// 1 to allow for intermediate CAs with a different subject name,
386+
// which can occur in service endorsements of some services.
387+
constraints = "critical,CA:TRUE,pathlen:1";
386388
}
387389

388390
// Add basic constraints

src/crypto/test/crypto.cpp

+41
Original file line numberDiff line numberDiff line change
@@ -1292,3 +1292,44 @@ TEST_CASE("COSE sign & verify")
12921292

12931293
REQUIRE(cose_verifier->verify_detached(cose_sign, {}));
12941294
}
1295+
1296+
TEST_CASE("Sign and verify a chain with an intermediate and different subjects")
1297+
{
1298+
auto root_kp = ccf::crypto::make_key_pair(CurveID::SECP384R1);
1299+
auto root_cert = generate_self_signed_cert(root_kp, "CN=root");
1300+
1301+
auto intermediate_kp = ccf::crypto::make_key_pair(CurveID::SECP384R1);
1302+
auto intermediate_csr = intermediate_kp->create_csr("CN=intermediate", {});
1303+
1304+
std::string valid_from = "20210311000000Z";
1305+
std::string valid_to = "20230611235959Z";
1306+
auto intermediate_cert =
1307+
root_kp->sign_csr(root_cert, intermediate_csr, valid_from, valid_to, true);
1308+
1309+
auto leaf_kp = ccf::crypto::make_key_pair(CurveID::SECP384R1);
1310+
auto leaf_csr = leaf_kp->create_csr("CN=leaf", {});
1311+
auto leaf_cert = intermediate_kp->sign_csr(
1312+
intermediate_cert, leaf_csr, valid_from, valid_to, true);
1313+
1314+
auto verifier = ccf::crypto::make_verifier(leaf_cert.raw());
1315+
auto rc = verifier->verify_certificate(
1316+
{&root_cert}, {&intermediate_cert}, true /* ignore time */
1317+
);
1318+
1319+
// Failed with pathlen: 0
1320+
REQUIRE(rc);
1321+
1322+
// Missing intermediate
1323+
rc = verifier->verify_certificate(
1324+
{&root_cert}, {}, true /* ignore time */
1325+
);
1326+
1327+
REQUIRE(!rc);
1328+
1329+
// Invalid root
1330+
rc = verifier->verify_certificate(
1331+
{&leaf_cert}, {}, true /* ignore time */
1332+
);
1333+
1334+
REQUIRE(!rc);
1335+
}

tests/e2e_logging.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -1797,7 +1797,7 @@ def test_basic_constraints(network, args):
17971797
)
17981798
assert basic_constraints.critical is True
17991799
assert basic_constraints.value.ca is True
1800-
assert basic_constraints.value.path_length == 0
1800+
assert basic_constraints.value.path_length == 1
18011801

18021802
node_pem = primary.get_tls_certificate_pem()
18031803
node_cert = load_pem_x509_certificate(node_pem.encode(), default_backend())

0 commit comments

Comments
 (0)