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.