From a8a9ed9ad736e9d2ee55656b9f500948362659cf Mon Sep 17 00:00:00 2001 From: Ulises Gascon Date: Fri, 23 Oct 2026 11:35:08 +0200 Subject: [PATCH] tls: test the SPKI blocklist with a fixture key Replace the test-only hooks on the crypto binding, and the process-wide list and mutex they required on every handshake, with a fixture CA whose public key is a permanent entry on the compiled-in blocklist. The test now exercises the real list through full handshakes, without --expose-internals, and the production path is a comparison against a constant array. The new fixtures provide two chains that contain the blocked key: a server certificate issued by blocked-ca, and one issued by ca1 that reuses the blocked key itself. The private key is checked in like every other fixture key, so blocking it costs nothing. It is pinned in the fixtures Makefile, since its digest is compiled in. Refs: https://github.com/nodejs/node/pull/66871#discussion_r4325672378 Assisted-by: Claude, Codex Signed-off-by: Ulises Gascon --- src/crypto/crypto_context.cc | 62 ++------ src/crypto/crypto_context.h | 9 -- test/fixtures/keys/Makefile | 78 +++++++++- test/fixtures/keys/blocked-ca-cert.pem | 26 ++++ test/fixtures/keys/blocked-ca-cert.srl | 1 + test/fixtures/keys/blocked-ca-csr.pem | 18 +++ test/fixtures/keys/blocked-ca-key.pem | 28 ++++ test/fixtures/keys/blocked-ca-leaf-cert.pem | 48 +++++++ test/fixtures/keys/blocked-ca-leaf-csr.pem | 18 +++ test/fixtures/keys/blocked-ca-leaf-key.pem | 28 ++++ test/fixtures/keys/blocked-ca-leaf.cnf | 18 +++ test/fixtures/keys/blocked-ca.cnf | 23 +++ test/fixtures/keys/blocked-key-leaf-cert.pem | 21 +++ test/fixtures/keys/blocked-key-leaf-csr.pem | 18 +++ test/fixtures/keys/blocked-key-leaf.cnf | 18 +++ test/fixtures/keys/ca1-cert.srl | 2 +- test/parallel/test-tls-spki-blocklist.js | 144 +++++++------------ 17 files changed, 406 insertions(+), 154 deletions(-) create mode 100644 test/fixtures/keys/blocked-ca-cert.pem create mode 100644 test/fixtures/keys/blocked-ca-cert.srl create mode 100644 test/fixtures/keys/blocked-ca-csr.pem create mode 100644 test/fixtures/keys/blocked-ca-key.pem create mode 100644 test/fixtures/keys/blocked-ca-leaf-cert.pem create mode 100644 test/fixtures/keys/blocked-ca-leaf-csr.pem create mode 100644 test/fixtures/keys/blocked-ca-leaf-key.pem create mode 100644 test/fixtures/keys/blocked-ca-leaf.cnf create mode 100644 test/fixtures/keys/blocked-ca.cnf create mode 100644 test/fixtures/keys/blocked-key-leaf-cert.pem create mode 100644 test/fixtures/keys/blocked-key-leaf-csr.pem create mode 100644 test/fixtures/keys/blocked-key-leaf.cnf diff --git a/src/crypto/crypto_context.cc b/src/crypto/crypto_context.cc index 13edcb588864..be2416c33c03 100644 --- a/src/crypto/crypto_context.cc +++ b/src/crypto/crypto_context.cc @@ -10,7 +10,6 @@ #include "ncrypto.h" #include "node.h" #include "node_buffer.h" -#include "node_mutex.h" #include "node_options.h" #include "util.h" #include "v8.h" @@ -32,7 +31,6 @@ #endif #include -#include #include namespace node { @@ -1437,29 +1435,18 @@ constexpr SpkiHash kBlockedSPKIs[] = { {0xe0, 0x25, 0x4c, 0x7f, 0xcd, 0xc2, 0x65, 0xe2, 0xf0, 0xf8, 0xa4, 0x28, 0xda, 0xbb, 0x75, 0xab, 0x4a, 0xe0, 0x39, 0xb4, 0x87, 0x2d, 0x62, 0xf5, 0xee, 0x57, 0xf0, 0x87, 0x7d, 0x5a, 0x8a, 0x41}, + // Test fixture, not a real CA: test/fixtures/keys/blocked-ca-key.pem. + // Its private key is public, so blocking it costs nothing and lets + // test/parallel/test-tls-spki-blocklist.js exercise the real list. The + // key is pinned in the fixtures Makefile; rotating it means updating + // this entry too. + // a6cbb923961eada55c02f104681aeabe52ea1508ece2078aec11d00a5778ce71 + // C=US, ST=CA, L=SF, O=Joyent, OU=Node.js, CN=blocked-ca + {0x41, 0xc2, 0xd1, 0xc4, 0x24, 0x68, 0x46, 0x6a, 0x54, 0x24, 0x83, + 0x69, 0xf6, 0x65, 0x9a, 0x95, 0x52, 0xc5, 0xa4, 0x16, 0xf8, 0xd5, + 0xfc, 0xd8, 0xdd, 0x55, 0xa8, 0x75, 0xd7, 0x20, 0xaa, 0xc2}, }; -// Extra entries registered by the test-only hooks on the crypto binding. -// The list is process-wide and TLS handshakes may run on worker threads, so -// every access goes through the mutex. -Mutex& BlockedSPKIsForTestingMutex() { - static Mutex mutex; - return mutex; -} - -std::vector& BlockedSPKIsForTesting() { - static std::vector list; - return list; -} - -bool IsBlockedSPKIForTesting(const SpkiHash& digest) { - Mutex::ScopedLock lock(BlockedSPKIsForTestingMutex()); - for (const auto& blocked : BlockedSPKIsForTesting()) { - if (digest == blocked) return true; - } - return false; -} - enum class SpkiCheck { kAllowed, kBlocked, kError }; // Hashes the DER-encoded SubjectPublicKeyInfo of `cert` and looks it up in @@ -1483,7 +1470,6 @@ SpkiCheck CheckSPKI(X509* cert) { for (const auto& blocked : kBlockedSPKIs) { if (digest == blocked) return SpkiCheck::kBlocked; } - if (IsBlockedSPKIForTesting(digest)) return SpkiCheck::kBlocked; return SpkiCheck::kAllowed; } @@ -1603,26 +1589,6 @@ void InstallCertVerifyCallback(SSL_CTX* ctx) { SSL_CTX_set_cert_verify_callback(ctx, CertVerifyCallback, nullptr); } -void SecureContext::AddBlockedSPKIForTesting( - const FunctionCallbackInfo& args) { - Environment* env = Environment::GetCurrent(args); - THROW_AND_RETURN_IF_NOT_BUFFER(env, args[0], "SPKI hash"); - ArrayBufferOrViewContents hash(args[0]); - if (hash.size() != kSpkiHashLength) { - return THROW_ERR_OUT_OF_RANGE(env, "SPKI hash must be 32 bytes"); - } - SpkiHash entry; - memcpy(entry.data(), hash.data(), entry.size()); - Mutex::ScopedLock lock(BlockedSPKIsForTestingMutex()); - BlockedSPKIsForTesting().push_back(entry); -} - -void SecureContext::ClearBlockedSPKIsForTesting( - const FunctionCallbackInfo& args) { - Mutex::ScopedLock lock(BlockedSPKIsForTestingMutex()); - BlockedSPKIsForTesting().clear(); -} - void SecureContext::Initialize(Environment* env, Local target) { Local context = env->context(); SetConstructorFunction(context, @@ -1650,12 +1616,6 @@ void SecureContext::Initialize(Environment* env, Local target) { target, "getCertificateCompressionAlgorithms", GetCertificateCompressionAlgorithms); - SetMethod( - context, target, "addBlockedSPKIForTesting", AddBlockedSPKIForTesting); - SetMethod(context, - target, - "clearBlockedSPKIsForTesting", - ClearBlockedSPKIsForTesting); } void SecureContext::RegisterExternalReferences( @@ -1703,8 +1663,6 @@ void SecureContext::RegisterExternalReferences( registry->Register(GetUserRootCertificates); registry->Register(StartLoadingCertificatesOffThread); registry->Register(GetCertificateCompressionAlgorithms); - registry->Register(AddBlockedSPKIForTesting); - registry->Register(ClearBlockedSPKIsForTesting); } SecureContext* SecureContext::Create(Environment* env) { diff --git a/src/crypto/crypto_context.h b/src/crypto/crypto_context.h index c2da04bbbb55..c5f77f2da01e 100644 --- a/src/crypto/crypto_context.h +++ b/src/crypto/crypto_context.h @@ -160,15 +160,6 @@ class SecureContext final : public BaseObject { template static void GetCertificate(const v8::FunctionCallbackInfo& args); - // Test-only hooks for the SPKI blocklist that every SSL_CTX applies - // during certificate verification. Reachable through - // internalBinding('crypto') with --expose-internals. The argument is the - // SHA-256 of a DER-encoded SubjectPublicKeyInfo. - static void AddBlockedSPKIForTesting( - const v8::FunctionCallbackInfo& args); - static void ClearBlockedSPKIsForTesting( - const v8::FunctionCallbackInfo& args); - static int TicketKeyCallback(SSL* ssl, unsigned char* name, unsigned char* iv, diff --git a/test/fixtures/keys/Makefile b/test/fixtures/keys/Makefile index c7f444bf69e1..480b111463f8 100644 --- a/test/fixtures/keys/Makefile +++ b/test/fixtures/keys/Makefile @@ -12,6 +12,9 @@ all: \ ca4-cert.pem \ ca5-cert.pem \ ca6-cert.pem \ + blocked-ca-cert.pem \ + blocked-ca-leaf-cert.pem \ + blocked-key-leaf-cert.pem \ cert-without-key-fips.pfx \ agent1-cert.pem \ agent1-fips.pfx \ @@ -225,6 +228,79 @@ ca3-cert.pem: ca3-csr.pem ca3-key.pem ca3.cnf ca1-cert.pem ca1-key.pem -CAcreateserial \ -out ca3-cert.pem +# +# blocked-ca is a subordinate CA issued by ca1 whose public key is on the +# compiled-in SPKI blocklist (src/crypto/crypto_context.cc). Any chain that +# contains this key must be rejected, so it is used to test the blocklist. +# The certificates can be regenerated freely; the key cannot (see below). +# blocked-ca-leaf is a server cert issued by blocked-ca; blocked-key-leaf is +# a server cert issued by ca1 that reuses the blocked key itself. +# ('password' is used for the CA password.) +# +# The SHA-256 of this key's SubjectPublicKeyInfo is compiled into +# src/crypto/crypto_context.cc, so the key is pinned: it is kept across +# `make clean` and never regenerated here. Rotating it means updating the +# digest and fingerprint in that file as well. +blocked-ca-key.pem: + @echo "$@ is pinned (see src/crypto/crypto_context.cc); restore it with: git checkout -- $@" >&2 + @exit 1 + +blocked-ca-csr.pem: blocked-ca.cnf blocked-ca-key.pem + openssl req -new \ + -extensions v3_ca \ + -config blocked-ca.cnf \ + -key blocked-ca-key.pem \ + -out blocked-ca-csr.pem + +blocked-ca-cert.pem: blocked-ca-csr.pem blocked-ca-key.pem blocked-ca.cnf ca1-cert.pem ca1-key.pem + openssl x509 -req \ + -extfile blocked-ca.cnf \ + -extensions v3_ca \ + -days 99999 \ + -passin "pass:password" \ + -in blocked-ca-csr.pem \ + -CA ca1-cert.pem \ + -CAkey ca1-key.pem \ + -CAcreateserial \ + -out blocked-ca-cert.pem + +blocked-ca-leaf-key.pem: + openssl genrsa -out blocked-ca-leaf-key.pem 2048 + +blocked-ca-leaf-csr.pem: blocked-ca-leaf.cnf blocked-ca-leaf-key.pem + openssl req -new \ + -config blocked-ca-leaf.cnf \ + -key blocked-ca-leaf-key.pem \ + -out blocked-ca-leaf-csr.pem + +blocked-ca-leaf-cert.pem: blocked-ca-leaf-csr.pem blocked-ca-cert.pem blocked-ca-key.pem + openssl x509 -req \ + -days 99999 \ + -in blocked-ca-leaf-csr.pem \ + -CA blocked-ca-cert.pem \ + -CAkey blocked-ca-key.pem \ + -CAcreateserial \ + -extfile blocked-ca-leaf.cnf \ + -out blocked-ca-leaf-cert.pem + cat blocked-ca-cert.pem >> blocked-ca-leaf-cert.pem + +blocked-key-leaf-csr.pem: blocked-key-leaf.cnf blocked-ca-key.pem + openssl req -new \ + -config blocked-key-leaf.cnf \ + -key blocked-ca-key.pem \ + -out blocked-key-leaf-csr.pem + +blocked-key-leaf-cert.pem: blocked-key-leaf-csr.pem ca1-cert.pem ca1-key.pem + openssl x509 -req \ + -days 99999 \ + -passin "pass:password" \ + -in blocked-key-leaf-csr.pem \ + -CA ca1-cert.pem \ + -CAkey ca1-key.pem \ + -CAcreateserial \ + -extfile blocked-key-leaf.cnf \ + -out blocked-key-leaf-cert.pem + # # Create Subordinate Certificate Authority: ca4 issued by ca2 # ('password' is used for the CA password.) @@ -1312,7 +1388,7 @@ non-canonical-ip-dns-key.pem: openssl ecparam -name prime256v1 -genkey -noout -out non-canonical-ip-dns-key.pem clean: - rm -f *.pfx *.pem *.srl ca2-database.txt ca2-crl-agent3-database.txt* ca2-crl-agent3.cnf ca2-serial fake-startcom-root-serial *.print *.old fake-startcom-root-issued-certs/*.pem + rm -f *.pfx $(filter-out blocked-ca-key.pem,$(wildcard *.pem)) *.srl ca2-database.txt ca2-crl-agent3-database.txt* ca2-crl-agent3.cnf ca2-serial fake-startcom-root-serial *.print *.old fake-startcom-root-issued-certs/*.pem @> fake-startcom-root-database.txt test: agent1-verify agent2-verify agent3-verify agent4-verify agent5-verify agent6-verify agent7-verify agent8-verify agent10-verify ec10-verify diff --git a/test/fixtures/keys/blocked-ca-cert.pem b/test/fixtures/keys/blocked-ca-cert.pem new file mode 100644 index 000000000000..1d46cd5010fd --- /dev/null +++ b/test/fixtures/keys/blocked-ca-cert.pem @@ -0,0 +1,26 @@ +-----BEGIN CERTIFICATE----- +MIIEZjCCA06gAwIBAgIUFH02wcL3Qgben6tfIibXitsApCswDQYJKoZIhvcNAQEL +BQAwejELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkNBMQswCQYDVQQHDAJTRjEPMA0G +A1UECgwGSm95ZW50MRAwDgYDVQQLDAdOb2RlLmpzMQwwCgYDVQQDDANjYTExIDAe +BgkqhkiG9w0BCQEWEXJ5QHRpbnljbG91ZHMub3JnMCAXDTI2MTAyMzA5MzE1M1oY +DzIzMDAwODA3MDkzMTUzWjCBhjELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkNBMQsw +CQYDVQQHDAJTRjEPMA0GA1UECgwGSm95ZW50MRAwDgYDVQQLDAdOb2RlLmpzMRMw +EQYDVQQDDApibG9ja2VkLWNhMSUwIwYJKoZIhvcNAQkBFhZibG9ja2VkLWNhQGV4 +YW1wbGUub3JnMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA3JSHZeuE +OSZ77MDkjTgJRhQJtvLruDJdA2hVfT47aST8cXqfHAR+tQsotBcIc4b69l1QxIts +SYkmiQbbnObIj1pAbmZ3blFy+13wNxfnLJR2Jh51vZyUTQMX+KIlrrzg9qpfOGi1 +D1zJvCSs5pyxpsGZ1GZxiFVTIGqZ4PZApyTjobqsivLV5FYiRgGESHXziRPJBeKY +D9MGKslA8p1qY79iqyaoY580xkiFNXeXsmguy96+KdEuGlfdpT+mPsCRWEU7Rt9E +wXsqUAPcZ0/22u62IJvBapMf3lvbTo3M6vraQmFEwsqlxeaWDhx6621VjHzsTHp9 +8Ko2VExaIYoSNQIDAQABo4HUMIHRMAwGA1UdEwQFMAMBAf8wHQYDVR0OBBYEFBO3 +GH448bBtSUSGJcz0UV8t0M2AMIGhBgNVHSMEgZkwgZahfqR8MHoxCzAJBgNVBAYT +AlVTMQswCQYDVQQIDAJDQTELMAkGA1UEBwwCU0YxDzANBgNVBAoMBkpveWVudDEQ +MA4GA1UECwwHTm9kZS5qczEMMAoGA1UEAwwDY2ExMSAwHgYJKoZIhvcNAQkBFhFy +eUB0aW55Y2xvdWRzLm9yZ4IUSrFsjf1qfQ0t/KvfnEsOksatAikwDQYJKoZIhvcN +AQELBQADggEBAKl9HrTXud8wHFRGTrrJBMOP5jZO50Sgf6/QSg3gVxLbTnraZDUq +PD3MKON95r/4ksa139/SIcTPSU6Nr4uJw4iVtfMgXr5/cUOeJN+l6z9Ju0bN1Fiu +xLXNgm7fHcNS2SkNxFKDCK7D11nhgkwUgs/JPQWD2DLmfoVScScQNXuFmgOYxUdI +Lfis9fdnAlTwFvu87FxFhKNjTD+gbo16KUz+uhevOHz+b2MZNgqhNklePP/mlQhx +kWzDz9B6ofhn3SD/q/idtQuEntVM7qV60ZXj27ELS58q7DnbwCJ/Kivl+cjKLmSJ +3EbbetSB6+9PA5MJjAMgInyfSAHw+S1IFqk= +-----END CERTIFICATE----- diff --git a/test/fixtures/keys/blocked-ca-cert.srl b/test/fixtures/keys/blocked-ca-cert.srl new file mode 100644 index 000000000000..419e72c54a66 --- /dev/null +++ b/test/fixtures/keys/blocked-ca-cert.srl @@ -0,0 +1 @@ +5A1AB9CDD00373643B19FADBFB08C51AE1EEFBC6 diff --git a/test/fixtures/keys/blocked-ca-csr.pem b/test/fixtures/keys/blocked-ca-csr.pem new file mode 100644 index 000000000000..d2e327349044 --- /dev/null +++ b/test/fixtures/keys/blocked-ca-csr.pem @@ -0,0 +1,18 @@ +-----BEGIN CERTIFICATE REQUEST----- +MIIC8TCCAdkCAQAwgYYxCzAJBgNVBAYTAlVTMQswCQYDVQQIDAJDQTELMAkGA1UE +BwwCU0YxDzANBgNVBAoMBkpveWVudDEQMA4GA1UECwwHTm9kZS5qczETMBEGA1UE +AwwKYmxvY2tlZC1jYTElMCMGCSqGSIb3DQEJARYWYmxvY2tlZC1jYUBleGFtcGxl +Lm9yZzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANyUh2XrhDkme+zA +5I04CUYUCbby67gyXQNoVX0+O2kk/HF6nxwEfrULKLQXCHOG+vZdUMSLbEmJJokG +25zmyI9aQG5md25Rcvtd8DcX5yyUdiYedb2clE0DF/iiJa684PaqXzhotQ9cybwk +rOacsabBmdRmcYhVUyBqmeD2QKck46G6rIry1eRWIkYBhEh184kTyQXimA/TBirJ +QPKdamO/YqsmqGOfNMZIhTV3l7JoLsvevinRLhpX3aU/pj7AkVhFO0bfRMF7KlAD +3GdP9trutiCbwWqTH95b206NzOr62kJhRMLKpcXmlg4ceuttVYx87Ex6ffCqNlRM +WiGKEjUCAwEAAaAlMCMGCSqGSIb3DQEJBzEWDBRBIGNoYWxsZW5nZSBwYXNzd29y +ZDANBgkqhkiG9w0BAQsFAAOCAQEAq+O48whfKxxS8PmRRoyrnNMpFAjwLW6QXNc0 +5VPTFU66mPxrG4zNTA0Jr0Z8P1SRAmMADFcWWIRW/qWg81nSRJAn1YbUyMj8Pq0O +WUIItJsHMobCLEe/dDyIw30mPCjTqaYc7ZQ1joTnejQmhlza02FTzePKs1HApQlt +NLkOheK4dx3C92YJFL5pd9pw1tsBBXeKt7X7hJ69ykvNPqmzN7gam4D+g+SA4dSL +BR5fbW8cBTKzgydDRp9kV8SPyjE8Rt1LZFeRelRe5ow/iNlf0zZvO1/PfFv0gJZa +Lp1gx0E5QaDhmlnGFToNxIgDtVAAqwWpFvBOwLWiOZBIBpm2Fw== +-----END CERTIFICATE REQUEST----- diff --git a/test/fixtures/keys/blocked-ca-key.pem b/test/fixtures/keys/blocked-ca-key.pem new file mode 100644 index 000000000000..69016e2784dd --- /dev/null +++ b/test/fixtures/keys/blocked-ca-key.pem @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDclIdl64Q5Jnvs +wOSNOAlGFAm28uu4Ml0DaFV9PjtpJPxxep8cBH61Cyi0Fwhzhvr2XVDEi2xJiSaJ +Btuc5siPWkBuZnduUXL7XfA3F+cslHYmHnW9nJRNAxf4oiWuvOD2ql84aLUPXMm8 +JKzmnLGmwZnUZnGIVVMgapng9kCnJOOhuqyK8tXkViJGAYRIdfOJE8kF4pgP0wYq +yUDynWpjv2KrJqhjnzTGSIU1d5eyaC7L3r4p0S4aV92lP6Y+wJFYRTtG30TBeypQ +A9xnT/ba7rYgm8Fqkx/eW9tOjczq+tpCYUTCyqXF5pYOHHrrbVWMfOxMen3wqjZU +TFohihI1AgMBAAECggEAEafi4ZXVLtTlgPkcojAcz+rtLyNpGkSPvOTWSPDZBQzE +lxm7UB0M/lVPFAEu+D5S6rLj8uN9DrLHbprpq7iuT4koFJ4ni6FB3JxzjJlhc+hL +P7ha8MsudK6dddWWwhKUyDGxbvy0l270J9bgrxHWR3SCKWXsyv6XaYYW3JKhcK9l +bVli1AVRk8JapNkkHfUxp0AxUJRbixlts760lXgArp74XLohleL0tw9lOoE+PWVY +1dvO9X+sOu3a1t/EK3cz9gXYoU1GgR3sy59Y6lweMBnarbdHDqMhWojV/MQGyD7e +WA+XS4Z/4kc4fGoCdFFLM9wznPevDfOEv4oOnS7vWQKBgQDt7vmYhVEUQ2QXtYaX +MwN+UBoNinrcxXvzqbzTMD7A1ahxZm3e3aNjD7kILYI43EIfdUBYnkY3GhIMuz5B ++66BhsesGFAL9n9mDSbou/Ykr5sQ9VsqGeEjTGbXSujGVrQDGnSzIfKDRx71SlYA +CnknFHn+DWqJQonrWNm46S29/QKBgQDtVDworobAkD8dmWi8sT4c60qLVp/7Pyp9 +W8fun4JXS1JlRZ/+N0C1qephD3/q/MVP4WDnUUR8BDvStWRxtSmJ8e2sTyB23IGx +tt6JOS2nQxppcd6+PQ5FQHYxI20GE/IwpiPRr8mbyi9hDEUgRm+7xyjpYeE9U6Hv +skRxpsV+mQKBgG6HENNVOvDazWhmHvul6v2GYFPh/GkKJCZwtxKz2rLhjboABxH8 +i6W87AtseI3JY74RTa7c92kpZzEa2jT0oX/yCFddg/HKtCvH+Ecs6Y7pNHOQRqUs +GHW5TBy+45sfM8gtRgDgyDNLmon/2itysg0BahomSVMx4be+MoW/bExRAoGBAJZf +2iJYuw8GFse1bVSp5VZDi58QCxwR4OU/I0ZCET8ofZOncwGLltfESrqnLIEDGi5L ++q92obMGp7URtAwMIr7B6LtWA5bsjszAtu4xqYiijvii6uSVIRamCn2GGc1RFBLZ +Ijb5b//jMvQF50LU/nT3cye1GhdWh3Ot+4obhmV5AoGAG29zxE7brtbHx05P8iZ5 +tsQyPWx69uYZ6MU84eb5hdLJyo1jddTTolhedO0oIpyIPCna0HWkNmNXJzzof1He +WfnKQZxbFQuiigP/u8/UlV8GF+rL9Vu5Wac156CsAqWdK0YokV5xOL/Dg0xzXigS +/HOGgpgUjvkKU40z37qs0MI= +-----END PRIVATE KEY----- diff --git a/test/fixtures/keys/blocked-ca-leaf-cert.pem b/test/fixtures/keys/blocked-ca-leaf-cert.pem new file mode 100644 index 000000000000..d1f1643e105b --- /dev/null +++ b/test/fixtures/keys/blocked-ca-leaf-cert.pem @@ -0,0 +1,48 @@ +-----BEGIN CERTIFICATE----- +MIIDljCCAn4CFFoauc3QA3NkOxn62/sIxRrh7vvGMA0GCSqGSIb3DQEBCwUAMIGG +MQswCQYDVQQGEwJVUzELMAkGA1UECAwCQ0ExCzAJBgNVBAcMAlNGMQ8wDQYDVQQK +DAZKb3llbnQxEDAOBgNVBAsMB05vZGUuanMxEzARBgNVBAMMCmJsb2NrZWQtY2Ex +JTAjBgkqhkiG9w0BCQEWFmJsb2NrZWQtY2FAZXhhbXBsZS5vcmcwIBcNMjYxMDIz +MDkzMTU0WhgPMjMwMDA4MDcwOTMxNTRaMIGFMQswCQYDVQQGEwJVUzELMAkGA1UE +CAwCQ0ExCzAJBgNVBAcMAlNGMQ8wDQYDVQQKDAZKb3llbnQxEDAOBgNVBAsMB05v +ZGUuanMxEjAQBgNVBAMMCWxvY2FsaG9zdDElMCMGCSqGSIb3DQEJARYWYmxvY2tl +ZC1jYUBleGFtcGxlLm9yZzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB +ANi/4BRZJCWbt6/0u6N1JAEHZtjQHJIkLsla2hIIqRzidIwZIvatkQYF2E2IAxjM +E5fxVrZPCS29lyMnt+cgm/29OuLdHEf5wbZuXtXR01VrAY0Sd5YumFvpUDPxnB15 +NKKwClnx/op64ePglSbwgLqQpH94iiOsrp721n4aAGuxOwA1P4u64dT3ry4fclPw +UIlq51EyQqNJ/1CtHXqGgdbXuEAUNy1iBb0EM9IVSAieDaPwlwuVG6k1e8cqGkXC +gdtL29XLoX47rd58s9C9hI5u9e+BNLWtLIrJYtbGqQgF05BZbk+yH9pyeracQEXy +ZyV1cJ0iVLpWByu7PNvjuisCAwEAATANBgkqhkiG9w0BAQsFAAOCAQEA1xqSl5FF +23kKf+7gbPydD3/gIuZlsFAGPa9CaTrIs/hDZAX086KEadroSzWmNvlsGhNxCUug +A5u5zDXir6SWHomnLggkA9S+TDBcF5IQp0DrGEZ3MpOAsbi6a+Qwofnc+jwwjBui +FMYCJaVh5tQeY1MVxRbYL7hDiWe74k01WOczVCe726bvkUcryTa+jCR8h8jX1DRr +xvgnU1KV7futG/afwM8if/XdyNKbPgtkP8HFgHEQLHXPMwju5ft73vW33TuGzIIW +HNACy9z8Swqik8V2k7Mo0d0l5sEWtoablbAo53qDE1dW5wSVOxpTlDhOlxxrS0/T +8nb7gMePTFNW+Q== +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIEZjCCA06gAwIBAgIUFH02wcL3Qgben6tfIibXitsApCswDQYJKoZIhvcNAQEL +BQAwejELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkNBMQswCQYDVQQHDAJTRjEPMA0G +A1UECgwGSm95ZW50MRAwDgYDVQQLDAdOb2RlLmpzMQwwCgYDVQQDDANjYTExIDAe +BgkqhkiG9w0BCQEWEXJ5QHRpbnljbG91ZHMub3JnMCAXDTI2MTAyMzA5MzE1M1oY +DzIzMDAwODA3MDkzMTUzWjCBhjELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkNBMQsw +CQYDVQQHDAJTRjEPMA0GA1UECgwGSm95ZW50MRAwDgYDVQQLDAdOb2RlLmpzMRMw +EQYDVQQDDApibG9ja2VkLWNhMSUwIwYJKoZIhvcNAQkBFhZibG9ja2VkLWNhQGV4 +YW1wbGUub3JnMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA3JSHZeuE +OSZ77MDkjTgJRhQJtvLruDJdA2hVfT47aST8cXqfHAR+tQsotBcIc4b69l1QxIts +SYkmiQbbnObIj1pAbmZ3blFy+13wNxfnLJR2Jh51vZyUTQMX+KIlrrzg9qpfOGi1 +D1zJvCSs5pyxpsGZ1GZxiFVTIGqZ4PZApyTjobqsivLV5FYiRgGESHXziRPJBeKY +D9MGKslA8p1qY79iqyaoY580xkiFNXeXsmguy96+KdEuGlfdpT+mPsCRWEU7Rt9E +wXsqUAPcZ0/22u62IJvBapMf3lvbTo3M6vraQmFEwsqlxeaWDhx6621VjHzsTHp9 +8Ko2VExaIYoSNQIDAQABo4HUMIHRMAwGA1UdEwQFMAMBAf8wHQYDVR0OBBYEFBO3 +GH448bBtSUSGJcz0UV8t0M2AMIGhBgNVHSMEgZkwgZahfqR8MHoxCzAJBgNVBAYT +AlVTMQswCQYDVQQIDAJDQTELMAkGA1UEBwwCU0YxDzANBgNVBAoMBkpveWVudDEQ +MA4GA1UECwwHTm9kZS5qczEMMAoGA1UEAwwDY2ExMSAwHgYJKoZIhvcNAQkBFhFy +eUB0aW55Y2xvdWRzLm9yZ4IUSrFsjf1qfQ0t/KvfnEsOksatAikwDQYJKoZIhvcN +AQELBQADggEBAKl9HrTXud8wHFRGTrrJBMOP5jZO50Sgf6/QSg3gVxLbTnraZDUq +PD3MKON95r/4ksa139/SIcTPSU6Nr4uJw4iVtfMgXr5/cUOeJN+l6z9Ju0bN1Fiu +xLXNgm7fHcNS2SkNxFKDCK7D11nhgkwUgs/JPQWD2DLmfoVScScQNXuFmgOYxUdI +Lfis9fdnAlTwFvu87FxFhKNjTD+gbo16KUz+uhevOHz+b2MZNgqhNklePP/mlQhx +kWzDz9B6ofhn3SD/q/idtQuEntVM7qV60ZXj27ELS58q7DnbwCJ/Kivl+cjKLmSJ +3EbbetSB6+9PA5MJjAMgInyfSAHw+S1IFqk= +-----END CERTIFICATE----- diff --git a/test/fixtures/keys/blocked-ca-leaf-csr.pem b/test/fixtures/keys/blocked-ca-leaf-csr.pem new file mode 100644 index 000000000000..18bf31610af0 --- /dev/null +++ b/test/fixtures/keys/blocked-ca-leaf-csr.pem @@ -0,0 +1,18 @@ +-----BEGIN CERTIFICATE REQUEST----- +MIIC8DCCAdgCAQAwgYUxCzAJBgNVBAYTAlVTMQswCQYDVQQIDAJDQTELMAkGA1UE +BwwCU0YxDzANBgNVBAoMBkpveWVudDEQMA4GA1UECwwHTm9kZS5qczESMBAGA1UE +AwwJbG9jYWxob3N0MSUwIwYJKoZIhvcNAQkBFhZibG9ja2VkLWNhQGV4YW1wbGUu +b3JnMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2L/gFFkkJZu3r/S7 +o3UkAQdm2NAckiQuyVraEgipHOJ0jBki9q2RBgXYTYgDGMwTl/FWtk8JLb2XIye3 +5yCb/b064t0cR/nBtm5e1dHTVWsBjRJ3li6YW+lQM/GcHXk0orAKWfH+inrh4+CV +JvCAupCkf3iKI6yunvbWfhoAa7E7ADU/i7rh1PevLh9yU/BQiWrnUTJCo0n/UK0d +eoaB1te4QBQ3LWIFvQQz0hVICJ4No/CXC5UbqTV7xyoaRcKB20vb1cuhfjut3nyz +0L2Ejm7174E0ta0sisli1sapCAXTkFluT7If2nJ6tpxARfJnJXVwnSJUulYHK7s8 +2+O6KwIDAQABoCUwIwYJKoZIhvcNAQkHMRYMFEEgY2hhbGxlbmdlIHBhc3N3b3Jk +MA0GCSqGSIb3DQEBCwUAA4IBAQBEdD8hTSajIPZBRkE6me4vxHiGHsRZGGohdE5k +UEzTU+TVH0V6c1E48Lh0nhLhzghvxdU9aBghoySDYfYWQTR4fN+l245yGWZCuRSp +0fSlehzN+batpgtRqNuI/OUzV0iyLmHpTmjKBcYCCAPb33LsYEIUYOtECU7OMVVS +Tz/AOsy+35TFqCV8OB8573en448Wcfiwh2f3IyG4zWAXaWyvGqxyCpbnGxDa8cNR +/QPFLtqQMNUBDTxBSp2A6To8LM9YAJP0HEOaQnCgZOPH0h7bMY9NnLz9GT208Veh +erTeSPFs2vazs34u78KwsFO9qVQQj9io3H1x4np14PhfYKQs +-----END CERTIFICATE REQUEST----- diff --git a/test/fixtures/keys/blocked-ca-leaf-key.pem b/test/fixtures/keys/blocked-ca-leaf-key.pem new file mode 100644 index 000000000000..b896545644bf --- /dev/null +++ b/test/fixtures/keys/blocked-ca-leaf-key.pem @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQDYv+AUWSQlm7ev +9LujdSQBB2bY0BySJC7JWtoSCKkc4nSMGSL2rZEGBdhNiAMYzBOX8Va2TwktvZcj +J7fnIJv9vTri3RxH+cG2bl7V0dNVawGNEneWLphb6VAz8ZwdeTSisApZ8f6KeuHj +4JUm8IC6kKR/eIojrK6e9tZ+GgBrsTsANT+LuuHU968uH3JT8FCJaudRMkKjSf9Q +rR16hoHW17hAFDctYgW9BDPSFUgIng2j8JcLlRupNXvHKhpFwoHbS9vVy6F+O63e +fLPQvYSObvXvgTS1rSyKyWLWxqkIBdOQWW5Psh/acnq2nEBF8mcldXCdIlS6Vgcr +uzzb47orAgMBAAECggEABLKv/DDscCY+ZoFy+bmFxMHw0D0+iUpgezvNYzut0rLz +3L+Euv0LGirRQ+jxTD3MJOx1uTDgXY3w8zGeslNKHkdWOemZ/dNjh/0/b/oCzySp +HXIrhmVPJxrZAq83d4oTgn03VDjDECRWthY9lWD8IU8WSa8Gq3xIMqjeAFC29p6Q +FFxZxrkEHkmmzXchAvDqW9Dv5uudfRVUHZuC2rV3tNNbPfVf3vLERQDNJ0H6gtHB +tEFJUn3CCc/LtRgfmPRHt2pmMwH5Eo7vkpkT5V5uqjlnm4Gnt/5RD0OAkzpy5wBI +7q/cMhA/GU4oe65pi9qpVLClg+uSwasEooAtUUoULQKBgQD2viYNK4/3a1W0mGTW +OHdtMlkJAdsK+hyh1Bzx23mQNkXvCnwOYtsTXc8/hQdNGLfaMZEI/3cLAui7cWUb +oG0twJUl3rL79uwnJUYKWxewedV36F6zjxS9F1qfGaQEczH28n3hTxlD1akOresk +79fBqihMdShw1n2ahZxkax1jXwKBgQDg4ae6ERbZmPtCvb/uLyERWBkJrZd7TAmO +VlWEKK3k3naPkqf1jQxE3PyEjhbxVPQS0z4FNi8XH/oeKJCyZkdrxIxtzfm5hvLe +2BgGpFrxEUDD4OGV2V2VpIfvIT4DNHeaHwV5AwpeIH59RhMx0MuWXr2miDJbVMrx +jnSpf+6ItQKBgFx+RRdUyB/wbW1IXyxqeGJHqFaXCnxf65HCSKNlKXmkzqxf2sct +yX/F/6WJAYamCG1TfVHJKd/H1GRHiqZDignHkmFizOdk1XuLHmlNYmD2Gs5bHfzj +WtvTQFY5r2E3qXgvyH5IP2lNU5U8HtR20AM5/OdEV8eMsALXqRt/h8GzAoGACYPi +ye+xLNHZR9Cpj1V1K7uzi4JJ15C3Esu6YJYsDXQQRjDpZG1saHmFQcKVW+7BSQ6h +IC1TvsG+nooO3U2QlhHB8190wpHgMuen1JgdtPeK6v7X7LQ+zncgvVCFxDLzIJVR +cPZc+PCdCYXuh/wSsTEfCy3WG6Ex2Riy/P0JxcUCgYAOqILp97U+ML5H1P+8d/Ct +jNaODIFhiSpFKSXsFO7tcZe6IOYbUlE0E+ROuUiyLgp1IH4NwbYvaqI7VBYr5+oN +WMrlC3wrACrENc8tFxpkzYYfWvM6g7IS7QjlFWz5Hw/O9nXlFiZrZ9Kcay3mx2YD +bv5b3RsxMU1/cjdDGDNolA== +-----END PRIVATE KEY----- diff --git a/test/fixtures/keys/blocked-ca-leaf.cnf b/test/fixtures/keys/blocked-ca-leaf.cnf new file mode 100644 index 000000000000..e2aef8f5c7d6 --- /dev/null +++ b/test/fixtures/keys/blocked-ca-leaf.cnf @@ -0,0 +1,18 @@ +[ req ] +default_bits = 2048 +days = 9999 +distinguished_name = req_distinguished_name +attributes = req_attributes +prompt = no + +[ req_distinguished_name ] +C = US +ST = CA +L = SF +O = Joyent +OU = Node.js +CN = localhost +emailAddress = blocked-ca@example.org + +[ req_attributes ] +challengePassword = A challenge password diff --git a/test/fixtures/keys/blocked-ca.cnf b/test/fixtures/keys/blocked-ca.cnf new file mode 100644 index 000000000000..b989e3cc0316 --- /dev/null +++ b/test/fixtures/keys/blocked-ca.cnf @@ -0,0 +1,23 @@ +[ req ] +default_bits = 2048 +days = 9999 +distinguished_name = req_distinguished_name +attributes = req_attributes +prompt = no +output_password = password +x509_extensions = v3_ca + +[ req_distinguished_name ] +C = US +ST = CA +L = SF +O = Joyent +OU = Node.js +CN = blocked-ca +emailAddress = blocked-ca@example.org + +[ req_attributes ] +challengePassword = A challenge password + +[ v3_ca ] +basicConstraints = CA:TRUE diff --git a/test/fixtures/keys/blocked-key-leaf-cert.pem b/test/fixtures/keys/blocked-key-leaf-cert.pem new file mode 100644 index 000000000000..37254c452311 --- /dev/null +++ b/test/fixtures/keys/blocked-key-leaf-cert.pem @@ -0,0 +1,21 @@ +-----BEGIN CERTIFICATE----- +MIIDiTCCAnECFBR9NsHC90IG3p+rXyIm14rbAKQsMA0GCSqGSIb3DQEBCwUAMHox +CzAJBgNVBAYTAlVTMQswCQYDVQQIDAJDQTELMAkGA1UEBwwCU0YxDzANBgNVBAoM +BkpveWVudDEQMA4GA1UECwwHTm9kZS5qczEMMAoGA1UEAwwDY2ExMSAwHgYJKoZI +hvcNAQkBFhFyeUB0aW55Y2xvdWRzLm9yZzAgFw0yNjEwMjMwOTMxNTRaGA8yMzAw +MDgwNzA5MzE1NFowgYUxCzAJBgNVBAYTAlVTMQswCQYDVQQIDAJDQTELMAkGA1UE +BwwCU0YxDzANBgNVBAoMBkpveWVudDEQMA4GA1UECwwHTm9kZS5qczESMBAGA1UE +AwwJbG9jYWxob3N0MSUwIwYJKoZIhvcNAQkBFhZibG9ja2VkLWNhQGV4YW1wbGUu +b3JnMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA3JSHZeuEOSZ77MDk +jTgJRhQJtvLruDJdA2hVfT47aST8cXqfHAR+tQsotBcIc4b69l1QxItsSYkmiQbb +nObIj1pAbmZ3blFy+13wNxfnLJR2Jh51vZyUTQMX+KIlrrzg9qpfOGi1D1zJvCSs +5pyxpsGZ1GZxiFVTIGqZ4PZApyTjobqsivLV5FYiRgGESHXziRPJBeKYD9MGKslA +8p1qY79iqyaoY580xkiFNXeXsmguy96+KdEuGlfdpT+mPsCRWEU7Rt9EwXsqUAPc +Z0/22u62IJvBapMf3lvbTo3M6vraQmFEwsqlxeaWDhx6621VjHzsTHp98Ko2VExa +IYoSNQIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQCFpF2VptDf+Xbmm5MmFS/edORK +lbZvL17Gw7bVJ3IlqvLc34BPmrLf4t/nh0tnA+OaMREh4YLJ5qZPXI2xpMt2hNef +eMPk7a5OyV3ZdGfyUCRRvv16iLJB/N8jl4WWT6I0+qPtVPUeEdJVr9Hkrko5sX+H +WtiA2EhOgCcmPDvwFbc+t8VVvfBJLKuAwimmMmKu9AnLc5rvnX4S0MFlJG4iCULj +o+Kq+Xai2KtyoHcNbRgRSHl/HTN8nwjPo5KCN1rCFuwTGogJZkKTeiVmEs800uza +gpFk85KpDPkMZrYBsFgz5sgVHaMCQCjMlpIUVoOJnXW7sX+IjWEwNCahuvuM +-----END CERTIFICATE----- diff --git a/test/fixtures/keys/blocked-key-leaf-csr.pem b/test/fixtures/keys/blocked-key-leaf-csr.pem new file mode 100644 index 000000000000..86733c16e26e --- /dev/null +++ b/test/fixtures/keys/blocked-key-leaf-csr.pem @@ -0,0 +1,18 @@ +-----BEGIN CERTIFICATE REQUEST----- +MIIC8DCCAdgCAQAwgYUxCzAJBgNVBAYTAlVTMQswCQYDVQQIDAJDQTELMAkGA1UE +BwwCU0YxDzANBgNVBAoMBkpveWVudDEQMA4GA1UECwwHTm9kZS5qczESMBAGA1UE +AwwJbG9jYWxob3N0MSUwIwYJKoZIhvcNAQkBFhZibG9ja2VkLWNhQGV4YW1wbGUu +b3JnMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA3JSHZeuEOSZ77MDk +jTgJRhQJtvLruDJdA2hVfT47aST8cXqfHAR+tQsotBcIc4b69l1QxItsSYkmiQbb +nObIj1pAbmZ3blFy+13wNxfnLJR2Jh51vZyUTQMX+KIlrrzg9qpfOGi1D1zJvCSs +5pyxpsGZ1GZxiFVTIGqZ4PZApyTjobqsivLV5FYiRgGESHXziRPJBeKYD9MGKslA +8p1qY79iqyaoY580xkiFNXeXsmguy96+KdEuGlfdpT+mPsCRWEU7Rt9EwXsqUAPc +Z0/22u62IJvBapMf3lvbTo3M6vraQmFEwsqlxeaWDhx6621VjHzsTHp98Ko2VExa +IYoSNQIDAQABoCUwIwYJKoZIhvcNAQkHMRYMFEEgY2hhbGxlbmdlIHBhc3N3b3Jk +MA0GCSqGSIb3DQEBCwUAA4IBAQDYknP02irc6qk8bmUVng5TwmGgqC+tyKtrZJI3 ++o4R5wvB6m/UZBHgdhyTGeSsBYszJADHSmmWNzsoPiPw1keArwJ6bKH9dF+G8ve5 +oBdRGsYD4drsVkmOTSoILm4Rexg9/LnOehJ4zJCCsUdXXOLPAIpa/hZ3wWtVeYLi +rblRu6bWa0lWdI6fFfz1ZmssdqI4kSmGhy22oUJViA4IMi37y5qTMtEGBJ6f5PNF +FVu4FeOoNbmCocLGsWIGKymLrtm9jdCXfHp7I83vb76Xkaf3JzJH6GpaocKTDrpp +lFc86ZfcR9/z9r+r+YM+2/DkPa0dGULOCaCIIi0o8hLrpj8i +-----END CERTIFICATE REQUEST----- diff --git a/test/fixtures/keys/blocked-key-leaf.cnf b/test/fixtures/keys/blocked-key-leaf.cnf new file mode 100644 index 000000000000..e2aef8f5c7d6 --- /dev/null +++ b/test/fixtures/keys/blocked-key-leaf.cnf @@ -0,0 +1,18 @@ +[ req ] +default_bits = 2048 +days = 9999 +distinguished_name = req_distinguished_name +attributes = req_attributes +prompt = no + +[ req_distinguished_name ] +C = US +ST = CA +L = SF +O = Joyent +OU = Node.js +CN = localhost +emailAddress = blocked-ca@example.org + +[ req_attributes ] +challengePassword = A challenge password diff --git a/test/fixtures/keys/ca1-cert.srl b/test/fixtures/keys/ca1-cert.srl index f4de097137cc..973a463cf816 100644 --- a/test/fixtures/keys/ca1-cert.srl +++ b/test/fixtures/keys/ca1-cert.srl @@ -1 +1 @@ -147D36C1C2F74206DE9FAB5F2226D78ADB00A428 +147D36C1C2F74206DE9FAB5F2226D78ADB00A42C diff --git a/test/parallel/test-tls-spki-blocklist.js b/test/parallel/test-tls-spki-blocklist.js index a7d83e096a77..c822a9d818db 100644 --- a/test/parallel/test-tls-spki-blocklist.js +++ b/test/parallel/test-tls-spki-blocklist.js @@ -1,4 +1,3 @@ -// Flags: --expose-internals 'use strict'; const common = require('../common'); if (!common.hasCrypto) @@ -12,98 +11,81 @@ if (!common.hasCrypto) // is surfaced through socket.authorizationError and honours rejectUnauthorized // on both the client and the server side. // -// The fixtures give a chain of agent6 -> ca3 (intermediate) -> ca1 (root); -// agent6-cert.pem already bundles ca3. agent1 is signed directly by ca1 and -// serves as an unaffected server certificate for the client-certificate -// cases. The test blocks the SPKI of ca3 through the test-only hooks on the -// crypto binding. +// The public key of the blocked-ca fixture is a permanent entry on the +// blocklist. The fixtures provide two chains that contain it: +// blocked-ca-leaf -> blocked-ca (intermediate) -> ca1 (root), and +// blocked-key-leaf -> ca1, where the leaf reuses the blocked key itself. +// blocked-ca-leaf-cert.pem already bundles blocked-ca. The agent6 -> ca3 -> +// ca1 chain and agent1 (signed directly by ca1) are unaffected. const assert = require('assert'); -const { createHash, X509Certificate } = require('crypto'); const { once } = require('events'); const { describe, it, beforeEach, afterEach } = require('node:test'); const tls = require('tls'); const fixtures = require('../common/fixtures'); -const { internalBinding } = require('internal/test/binding'); - -const { - addBlockedSPKIForTesting, - clearBlockedSPKIsForTesting, -} = internalBinding('crypto'); - -function spkiSha256(pem) { - const spki = new X509Certificate(pem).publicKey.export({ - type: 'spki', - format: 'der', - }); - return createHash('sha256').update(spki).digest(); -} const rootCert = fixtures.readKey('ca1-cert.pem'); -const intermediateCert = fixtures.readKey('ca3-cert.pem'); -const leafKey = fixtures.readKey('agent6-key.pem'); -const leafChain = fixtures.readKey('agent6-cert.pem'); -const unaffectedKey = fixtures.readKey('agent1-key.pem'); -const unaffectedCert = fixtures.readKey('agent1-cert.pem'); -// ca2 is unrelated to the agent6 chain; appended to the server's certificate -// it is sent to the peer but never becomes part of the verified chain. -const unrelatedCert = fixtures.readKey('ca2-cert.pem'); - -const intermediateSpkiHash = spkiSha256(intermediateCert); -const leafSpkiHash = spkiSha256(leafChain); -const unrelatedSpkiHash = spkiSha256(unrelatedCert); +const blockedCaCert = fixtures.readKey('blocked-ca-cert.pem'); +const blockedCaLeafKey = fixtures.readKey('blocked-ca-leaf-key.pem'); +const blockedCaLeafChain = fixtures.readKey('blocked-ca-leaf-cert.pem'); +const blockedKey = fixtures.readKey('blocked-ca-key.pem'); +const blockedKeyLeafCert = fixtures.readKey('blocked-key-leaf-cert.pem'); +const unaffectedKey = fixtures.readKey('agent6-key.pem'); +const unaffectedChain = fixtures.readKey('agent6-cert.pem'); +const serverKey = fixtures.readKey('agent1-key.pem'); +const serverCert = fixtures.readKey('agent1-cert.pem'); describe('SPKI blocklist on the server certificate', function() { let server; let client; - let opts; - beforeEach(async function() { - server = tls.createServer({ - key: leafKey, - cert: leafChain, - }, (socket) => socket.resume()); + function listen(serverOpts) { + server = tls.createServer(serverOpts, (socket) => socket.resume()); server.listen(0); - await once(server, 'listening'); + return once(server, 'listening'); + } - opts = { + function connect(clientOpts) { + client = tls.connect({ port: server.address().port, ca: rootCert, checkServerIdentity() {}, - }; - }); + ...clientOpts, + }); + return client; + } - afterEach(async function() { - clearBlockedSPKIsForTesting(); + afterEach(function() { client?.destroy(); server?.close(); }); - it('accepts the chain when nothing is blocked', async function() { - client = tls.connect(opts); + it('accepts a chain that contains no blocked key', async function() { + await listen({ key: unaffectedKey, cert: unaffectedChain }); + connect(); await once(client, 'secureConnect'); assert.strictEqual(client.authorized, true); }); it('rejects a chain through a blocked intermediate', async function() { - addBlockedSPKIForTesting(intermediateSpkiHash); + await listen({ key: blockedCaLeafKey, cert: blockedCaLeafChain }); await assert.rejects(async () => { - client = tls.connect(opts); + connect(); await once(client, 'secureConnect'); }, { code: 'CERT_REVOKED' }); }); it('rejects a chain whose leaf key is blocked', async function() { - addBlockedSPKIForTesting(leafSpkiHash); + await listen({ key: blockedKey, cert: blockedKeyLeafCert }); await assert.rejects(async () => { - client = tls.connect(opts); + connect(); await once(client, 'secureConnect'); }, { code: 'CERT_REVOKED' }); }); it('reports CERT_REVOKED without rejectUnauthorized', async function() { - addBlockedSPKIForTesting(intermediateSpkiHash); - client = tls.connect({ ...opts, rejectUnauthorized: false }); + await listen({ key: blockedCaLeafKey, cert: blockedCaLeafChain }); + connect({ rejectUnauthorized: false }); await once(client, 'secureConnect'); assert.strictEqual(client.authorized, false); assert.strictEqual(client.authorizationError, 'CERT_REVOKED'); @@ -113,52 +95,36 @@ describe('SPKI blocklist on the server certificate', function() { // With an empty trust store the chain cannot reach a root, which would // normally be reported as UNABLE_TO_GET_ISSUER_CERT_LOCALLY. The // blocked key must win over that error. - addBlockedSPKIForTesting(intermediateSpkiHash); + await listen({ key: blockedCaLeafKey, cert: blockedCaLeafChain }); await assert.rejects(async () => { - client = tls.connect({ ...opts, ca: [] }); + connect({ ca: [] }); await once(client, 'secureConnect'); }, { code: 'CERT_REVOKED' }); }); - - it('accepts the chain again once the blocklist is cleared', async function() { - addBlockedSPKIForTesting(intermediateSpkiHash); - clearBlockedSPKIsForTesting(); - client = tls.connect(opts); - await once(client, 'secureConnect'); - assert.strictEqual(client.authorized, true); - }); - - it('validates the hook arguments', function() { - assert.throws(() => addBlockedSPKIForTesting('not a buffer'), { - code: 'ERR_INVALID_ARG_TYPE', - }); - assert.throws(() => addBlockedSPKIForTesting(Buffer.alloc(31)), { - code: 'ERR_OUT_OF_RANGE', - }); - }); }); describe('SPKI blocklist on certificates outside the verified chain', function() { let server; let client; + // blocked-ca is unrelated to the agent6 chain; appended to the server's + // certificate it is sent to the peer but never becomes part of the + // verified chain. beforeEach(async function() { server = tls.createServer({ - key: leafKey, - cert: `${leafChain}\n${unrelatedCert}`, + key: unaffectedKey, + cert: `${unaffectedChain}\n${blockedCaCert}`, }, (socket) => socket.resume()); server.listen(0); await once(server, 'listening'); }); - afterEach(async function() { - clearBlockedSPKIsForTesting(); + afterEach(function() { client?.destroy(); server?.close(); }); it('ignores an unused peer-sent certificate when the chain verifies', async function() { - addBlockedSPKIForTesting(unrelatedSpkiHash); client = tls.connect({ port: server.address().port, ca: rootCert, @@ -171,7 +137,6 @@ describe('SPKI blocklist on certificates outside the verified chain', function() it('checks every peer-sent certificate when the chain does not verify', async function() { // With an empty trust store the chain is incomplete, so the fallback // scan of everything the peer sent applies and finds the blocked key. - addBlockedSPKIForTesting(unrelatedSpkiHash); await assert.rejects(async () => { client = tls.connect({ port: server.address().port, @@ -189,8 +154,8 @@ describe('SPKI blocklist on the client certificate', function() { function listen(serverOpts) { server = tls.createServer({ - key: unaffectedKey, - cert: unaffectedCert, + key: serverKey, + cert: serverCert, ca: rootCert, requestCert: true, ...serverOpts, @@ -199,50 +164,47 @@ describe('SPKI blocklist on the client certificate', function() { return once(server, 'listening'); } - function connect() { + function connect(clientOpts) { client = tls.connect({ port: server.address().port, - key: leafKey, - cert: leafChain, ca: rootCert, checkServerIdentity() {}, + ...clientOpts, }); return client; } - afterEach(async function() { - clearBlockedSPKIsForTesting(); + afterEach(function() { client?.destroy(); server?.close(); }); - it('accepts the client chain when nothing is blocked', async function() { + it('accepts a client chain that contains no blocked key', async function() { await listen({ rejectUnauthorized: true }); const connection = once(server, 'secureConnection'); - connect(); + connect({ key: unaffectedKey, cert: unaffectedChain }); const [socket] = await connection; assert.strictEqual(socket.authorized, true); }); it('reports CERT_REVOKED without rejectUnauthorized', async function() { - addBlockedSPKIForTesting(intermediateSpkiHash); await listen({ rejectUnauthorized: false }); const connection = once(server, 'secureConnection'); - connect(); + connect({ key: blockedCaLeafKey, cert: blockedCaLeafChain }); const [socket] = await connection; assert.strictEqual(socket.authorized, false); assert.strictEqual(socket.authorizationError, 'CERT_REVOKED'); }); it('rejects the client chain with rejectUnauthorized', async function() { - addBlockedSPKIForTesting(intermediateSpkiHash); await listen({ rejectUnauthorized: true }); server.on('secureConnection', common.mustNotCall()); const clientError = once(server, 'tlsClientError'); // The client may see either a clean close or a reset depending on // timing, so accept both and only wait for it to go away. const clientClosed = new Promise((resolve) => { - connect().on('error', () => {}).once('close', resolve); + connect({ key: blockedCaLeafKey, cert: blockedCaLeafChain }) + .on('error', () => {}).once('close', resolve); }); // The server destroys the socket during 'secure', which surfaces as a // reset rather than a handshake failure; the reason is kept on the socket.