From a2cd8aa883e374e463ab3f9d34856fc0a128890d Mon Sep 17 00:00:00 2001 From: Filip Skokan Date: Thu, 27 Aug 2026 00:59:23 +0200 Subject: [PATCH] crypto,https,tls: end OpenSSL engine support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit OpenSSL removed support for engines in version 4. Remove the Node.js APIs, constants, native bindings, and implementation that depend on ENGINE. Keep recognizing the former TLS and HTTPS engine options so their use fails explicitly instead of appearing to work while being ignored. Move DEP0183 directly from Documentation-only to End-of-Life after reverting its unreleased Runtime deprecation. Signed-off-by: Filip Skokan Assisted-by: Codex PR-URL: https://github.com/nodejs/node/pull/64777 Fixes: https://github.com/nodejs/node/issues/56733 Refs: https://github.com/nodejs/node/pull/64211 Refs: https://github.com/nodejs/node/pull/63966 Refs: https://github.com/nodejs/node/pull/53329 Refs: https://github.com/nodejs/node/pull/48950 Refs: https://github.com/nodejs/node/issues/45465 Refs: https://github.com/nodejs/node/issues/42827 Refs: https://github.com/nodejs/node/issues/47076 Refs: https://github.com/nodejs/node/pull/62862 Refs: https://github.com/nodejs/node/pull/38512 Reviewed-By: Yagiz Nizipli Reviewed-By: James M Snell Reviewed-By: Richard Lau Reviewed-By: Gürgün Dayıoğlu --- deps/ncrypto/engine.cc | 105 ----------------- deps/ncrypto/ncrypto.gyp | 35 ------ deps/ncrypto/ncrypto.h | 43 ------- deps/ncrypto/unofficial.gni | 6 +- doc/api/crypto.md | 89 -------------- doc/api/deprecations.md | 18 +-- doc/api/errors.md | 24 ++-- doc/api/https.md | 11 +- doc/api/permissions.md | 2 - doc/api/tls.md | 19 ++- lib/crypto.js | 2 - lib/https.js | 18 +-- lib/internal/crypto/util.js | 27 ----- lib/internal/errors.js | 3 +- lib/internal/tls/secure-context.js | 62 +++------- lib/internal/tls/wrap.js | 17 +-- src/crypto/crypto_context.cc | 109 ------------------ src/crypto/crypto_context.h | 11 -- src/crypto/crypto_util.cc | 37 ------ src/crypto/crypto_util.h | 1 - src/node_constants.cc | 67 ----------- src/node_errors.h | 1 - .../openssl-client-cert-engine/binding.gyp | 23 ---- .../addons/openssl-client-cert-engine/test.js | 65 ----------- .../openssl-client-cert-engine/testengine.cc | 106 ----------------- test/addons/openssl-key-engine/binding.gyp | 23 ---- test/addons/openssl-key-engine/test.js | 67 ----------- .../openssl-key-engine/testkeyengine.cc | 79 ------------- test/addons/openssl-test-engine/binding.gyp | 33 ------ test/addons/openssl-test-engine/test.js | 69 ----------- .../openssl-test-engine/testsetengine.cc | 44 ------- test/doctool/test-doc-api-json.mjs | 2 +- test/parallel/test-crypto-dep0183.js | 96 +++++++++++++++ test/parallel/test-https-agent-getname.js | 11 +- ...t-tls-clientcertengine-invalid-arg-type.js | 15 --- .../test-tls-clientcertengine-unsupported.js | 30 ----- test/parallel/test-tls-error-stack.js | 21 ---- .../test-tls-keyengine-invalid-arg-type.js | 24 ---- .../test-tls-keyengine-unsupported.js | 35 ------ typings/internalBinding/constants.d.ts | 11 -- typings/internalBinding/crypto.d.ts | 3 - 41 files changed, 174 insertions(+), 1290 deletions(-) delete mode 100644 deps/ncrypto/engine.cc delete mode 100644 test/addons/openssl-client-cert-engine/binding.gyp delete mode 100644 test/addons/openssl-client-cert-engine/test.js delete mode 100644 test/addons/openssl-client-cert-engine/testengine.cc delete mode 100644 test/addons/openssl-key-engine/binding.gyp delete mode 100644 test/addons/openssl-key-engine/test.js delete mode 100644 test/addons/openssl-key-engine/testkeyengine.cc delete mode 100644 test/addons/openssl-test-engine/binding.gyp delete mode 100644 test/addons/openssl-test-engine/test.js delete mode 100644 test/addons/openssl-test-engine/testsetengine.cc create mode 100644 test/parallel/test-crypto-dep0183.js delete mode 100644 test/parallel/test-tls-clientcertengine-invalid-arg-type.js delete mode 100644 test/parallel/test-tls-clientcertengine-unsupported.js delete mode 100644 test/parallel/test-tls-error-stack.js delete mode 100644 test/parallel/test-tls-keyengine-invalid-arg-type.js delete mode 100644 test/parallel/test-tls-keyengine-unsupported.js diff --git a/deps/ncrypto/engine.cc b/deps/ncrypto/engine.cc deleted file mode 100644 index 6b9514b8565..00000000000 --- a/deps/ncrypto/engine.cc +++ /dev/null @@ -1,105 +0,0 @@ -#include "ncrypto.h" - -#if !defined(OPENSSL_NO_ENGINE) && defined(NCRYPTO_ENGINE_COMPAT) && \ - NCRYPTO_ENGINE_COMPAT -#include -#endif - -namespace ncrypto { - -// ============================================================================ -// Engine - -#ifndef OPENSSL_NO_ENGINE -EnginePointer::EnginePointer(void* engine_, bool finish_on_exit_) - : engine(engine_), finish_on_exit(finish_on_exit_) {} - -EnginePointer::EnginePointer(EnginePointer&& other) noexcept - : engine(other.engine), finish_on_exit(other.finish_on_exit) { - other.release(); -} - -EnginePointer::~EnginePointer() { - reset(); -} - -EnginePointer& EnginePointer::operator=(EnginePointer&& other) noexcept { - if (this == &other) return *this; - this->~EnginePointer(); - return *new (this) EnginePointer(std::move(other)); -} - -void EnginePointer::reset(void* engine_, bool finish_on_exit_) { - if (engine != nullptr) { - ENGINE* current = static_cast(engine); - if (finish_on_exit) { - // This also does the equivalent of ENGINE_free. - ENGINE_finish(current); - } else { - ENGINE_free(current); - } - } - engine = engine_; - finish_on_exit = finish_on_exit_; -} - -void* EnginePointer::release() { - void* ret = engine; - engine = nullptr; - finish_on_exit = false; - return ret; -} - -EnginePointer EnginePointer::getEngineByName(const char* name, - CryptoErrorList* errors) { - MarkPopErrorOnReturn mark_pop_error_on_return(errors); - EnginePointer engine(ENGINE_by_id(name)); - if (!engine) { - // Engine not found, try loading dynamically. - engine = EnginePointer(ENGINE_by_id("dynamic")); - if (engine) { - ENGINE* current = static_cast(engine.engine); - if (!ENGINE_ctrl_cmd_string(current, "SO_PATH", name, 0) || - !ENGINE_ctrl_cmd_string(current, "LOAD", nullptr, 0)) { - engine.reset(); - } - } - } - return engine; -} - -bool EnginePointer::setAsDefault(uint32_t flags, CryptoErrorList* errors) { - if (engine == nullptr) return false; - ClearErrorOnReturn clear_error_on_return(errors); - return ENGINE_set_default(static_cast(engine), flags) != 0; -} - -bool EnginePointer::init(bool finish_on_exit) { - if (engine == nullptr) return false; - if (finish_on_exit) setFinishOnExit(); - return ENGINE_init(static_cast(engine)) == 1; -} - -EVPKeyPointer EnginePointer::loadPrivateKey(const char* key_name) { - if (engine == nullptr) return EVPKeyPointer(); - return EVPKeyPointer(ENGINE_load_private_key( - static_cast(engine), key_name, nullptr, nullptr)); -} - -bool EnginePointer::setClientCertEngine(SSL_CTX* ctx) { - if (engine == nullptr || ctx == nullptr) return false; - return SSL_CTX_set_client_cert_engine(ctx, static_cast(engine)) == 1; -} - -void EnginePointer::initEnginesOnce() { - static bool initialized = false; - if (!initialized) { - ENGINE_load_builtin_engines(); - ENGINE_register_all_complete(); - initialized = true; - } -} - -#endif // OPENSSL_NO_ENGINE - -} // namespace ncrypto diff --git a/deps/ncrypto/ncrypto.gyp b/deps/ncrypto/ncrypto.gyp index 40f7760ed77..69eccf349f1 100644 --- a/deps/ncrypto/ncrypto.gyp +++ b/deps/ncrypto/ncrypto.gyp @@ -5,10 +5,6 @@ 'ncrypto.cc', 'ncrypto.h', ], - 'ncrypto_engine_sources': [ - 'engine.cc', - 'ncrypto.h', - ], # OpenSSL may be configured with --api below 3.0. Keep 3.0 deprecation # checks where supported without exceeding its configured API level. 'ncrypto_openssl3_api_compat': @@ -17,11 +13,6 @@ 'OPENSSL_API_COMPAT=<(ncrypto_openssl3_api_compat)', 'OPENSSL_NO_DEPRECATED', ], - 'ncrypto_engine_defines': [ - 'OPENSSL_API_COMPAT=<(ncrypto_openssl3_api_compat)', - 'OPENSSL_SUPPRESS_DEPRECATED', - 'NCRYPTO_ENGINE_COMPAT=1', - ], }, 'targets': [ { @@ -46,9 +37,6 @@ 'conditions': [ ['openssl_is_boringssl=="false"', { 'defines': [ '<@(ncrypto_strict_defines)' ], - 'dependencies': [ - 'ncrypto_engine', - ], }], ['node_shared_openssl=="false"', { 'dependencies': [ @@ -58,27 +46,4 @@ ] }, ], - 'conditions': [ - ['openssl_is_boringssl=="false"', { - 'targets': [ - { - 'target_name': 'ncrypto_engine', - 'type': 'static_library', - 'include_dirs': ['.'], - 'defines': [ - 'NCRYPTO_BSSL_LIBDECREPIT_MISSING=<(ncrypto_bssl_libdecrepit_missing)', - '<@(ncrypto_engine_defines)', - ], - 'sources': [ '<@(ncrypto_engine_sources)' ], - 'conditions': [ - ['node_shared_openssl=="false"', { - 'dependencies': [ - '../openssl/openssl.gyp:openssl' - ] - }], - ] - }, - ], - }], - ], } diff --git a/deps/ncrypto/ncrypto.h b/deps/ncrypto/ncrypto.h index 2a84f4b2073..e45807ec179 100644 --- a/deps/ncrypto/ncrypto.h +++ b/deps/ncrypto/ncrypto.h @@ -23,11 +23,6 @@ #include #include #include -#if defined(NCRYPTO_ENGINE_COMPAT) && NCRYPTO_ENGINE_COMPAT && \ - !defined(OPENSSL_NO_ENGINE) -#include -#endif // NCRYPTO_ENGINE_COMPAT && !OPENSSL_NO_ENGINE - #ifndef OPENSSL_VERSION_PREREQ #define OPENSSL_VERSION_PREREQ(maj, min) \ (OPENSSL_VERSION_NUMBER >= (((maj) << 28) | ((min) << 20))) @@ -2109,44 +2104,6 @@ class MacCache final { }; #endif -#ifndef OPENSSL_NO_ENGINE -class EnginePointer final { - public: - EnginePointer() = default; - - explicit EnginePointer(void* engine_, bool finish_on_exit = false); - EnginePointer(EnginePointer&& other) noexcept; - EnginePointer& operator=(EnginePointer&& other) noexcept; - NCRYPTO_DISALLOW_COPY(EnginePointer) - ~EnginePointer(); - - inline operator bool() const { return engine != nullptr; } - inline void setFinishOnExit() { finish_on_exit = true; } - - void reset(void* engine_ = nullptr, bool finish_on_exit_ = false); - - bool setAsDefault(uint32_t flags, CryptoErrorList* errors = nullptr); - bool init(bool finish_on_exit = false); - EVPKeyPointer loadPrivateKey(const char* key_name); - bool setClientCertEngine(SSL_CTX* ctx); - - void* release(); - - // Retrieve an OpenSSL Engine instance by name. If the name does not - // identify a valid named engine, the returned EnginePointer will be - // empty. - static EnginePointer getEngineByName(const char* name, - CryptoErrorList* errors = nullptr); - - // Call once when initializing OpenSSL at startup for the process. - static void initEnginesOnce(); - - private: - void* engine = nullptr; - bool finish_on_exit = false; -}; -#endif // !OPENSSL_NO_ENGINE - // ============================================================================ // FIPS bool isFipsEnabled(); diff --git a/deps/ncrypto/unofficial.gni b/deps/ncrypto/unofficial.gni index dad4fbbf16f..7cb27d22b9b 100644 --- a/deps/ncrypto/unofficial.gni +++ b/deps/ncrypto/unofficial.gni @@ -26,11 +26,7 @@ template("ncrypto_gn_build") { source_set(target_name) { forward_variables_from(invoker, "*") public_configs = [ ":ncrypto_config" ] - defines = [ - "NCRYPTO_ENGINE_COMPAT=1", - "OPENSSL_SUPPRESS_DEPRECATED", - ] - sources = gypi_values.ncrypto_sources + gypi_values.ncrypto_engine_sources + sources = gypi_values.ncrypto_sources deps = [ "$node_openssl_path" ] } } diff --git a/doc/api/crypto.md b/doc/api/crypto.md index 63b85c8ca70..76999281502 100644 --- a/doc/api/crypto.md +++ b/doc/api/crypto.md @@ -6601,42 +6601,6 @@ added: v15.6.0 * `utilization` {number} The calculated ratio of `used` to `total` allocated bytes. -### `crypto.setEngine(engine[, flags])` - - - -* `engine` {string} -* `flags` {crypto.constants} **Default:** `crypto.constants.ENGINE_METHOD_ALL` - -Load and set the `engine` for some or all OpenSSL functions (selected by flags). -Support for custom engines in OpenSSL is deprecated from OpenSSL 3. - -`engine` could be either an id or a path to the engine's shared library. - -The optional `flags` argument uses `ENGINE_METHOD_ALL` by default. The `flags` -is a bit field taking one of or a mix of the following flags (defined in -`crypto.constants`): - -* `crypto.constants.ENGINE_METHOD_RSA` -* `crypto.constants.ENGINE_METHOD_DSA` -* `crypto.constants.ENGINE_METHOD_DH` -* `crypto.constants.ENGINE_METHOD_RAND` -* `crypto.constants.ENGINE_METHOD_EC` -* `crypto.constants.ENGINE_METHOD_CIPHERS` -* `crypto.constants.ENGINE_METHOD_DIGESTS` -* `crypto.constants.ENGINE_METHOD_PKEY_METHS` -* `crypto.constants.ENGINE_METHOD_PKEY_ASN1_METHS` -* `crypto.constants.ENGINE_METHOD_ALL` -* `crypto.constants.ENGINE_METHOD_NONE` - ### `crypto.setFips(bool)` -Type: Documentation-only +Type: End-of-Life -OpenSSL 3 has deprecated support for custom engines with a recommendation to -switch to its new provider model. The `clientCertEngine` option for -`https.request()`, [`tls.createSecureContext()`][], and [`tls.createServer()`][]; -the `privateKeyEngine` and `privateKeyIdentifier` for [`tls.createSecureContext()`][]; -and [`crypto.setEngine()`][] all depend on this functionality from OpenSSL. +The `crypto.setEngine()` API and the `crypto.constants.ENGINE_METHOD_*` +constants have been removed. The `clientCertEngine` option for +[`https.request()`][], [`tls.createSecureContext()`][], and +[`tls.createServer()`][] and the `privateKeyEngine` and `privateKeyIdentifier` +options for [`tls.createSecureContext()`][] now throw +`ERR_CRYPTO_CUSTOM_ENGINE_NOT_SUPPORTED` when used. There is no direct +replacement API in Node.js. OpenSSL's provider model replaces engines upstream. ### DEP0184: Instantiating `node:zlib` classes without `new` @@ -4887,7 +4892,6 @@ Type: Documentation-only [`crypto.pbkdf2()`]: crypto.md#cryptopbkdf2password-salt-iterations-keylen-digest-callback [`crypto.randomBytes()`]: crypto.md#cryptorandombytessize-callback [`crypto.scrypt()`]: crypto.md#cryptoscryptpassword-salt-keylen-options-callback -[`crypto.setEngine()`]: crypto.md#cryptosetengineengine-flags [`decipher.final()`]: crypto.md#decipherfinaloutputencoding [`decipher.setAuthTag()`]: crypto.md#deciphersetauthtagbuffer-encoding [`dirent.parentPath`]: fs.md#direntparentpath diff --git a/doc/api/errors.md b/doc/api/errors.md index 1d812bd0b33..834be04dcbb 100644 --- a/doc/api/errors.md +++ b/doc/api/errors.md @@ -911,9 +911,8 @@ Argon2 is not supported by the current version of OpenSSL being used. ### `ERR_CRYPTO_CUSTOM_ENGINE_NOT_SUPPORTED` -An OpenSSL engine was requested (for example, through the `clientCertEngine` or -`privateKeyEngine` TLS options) that is not supported by the version of OpenSSL -being used, likely due to the compile-time flag `OPENSSL_NO_ENGINE`. +An OpenSSL engine-based TLS or HTTPS option was used after support for custom +engines reached End-of-Life in Node.js. @@ -930,13 +929,6 @@ An invalid value for the `key` argument has been passed to the `crypto.ECDH()` class `computeSecret()` method. It means that the public key lies outside of the elliptic curve. - - -### `ERR_CRYPTO_ENGINE_UNKNOWN` - -An invalid crypto engine identifier was passed to -[`require('node:crypto').setEngine()`][]. - ### `ERR_CRYPTO_FIPS_FORCED` @@ -3945,6 +3937,17 @@ removed: v15.0.0 The native call from `process.cpuUsage` could not be processed. + + +### `ERR_CRYPTO_ENGINE_UNKNOWN` + + + +An invalid crypto engine identifier was passed to +`require('node:crypto').setEngine()`. + ### `ERR_CRYPTO_HASH_DIGEST_NO_UTF16` @@ -4840,7 +4843,6 @@ An error occurred trying to allocate memory. This should never happen. [`process.send()`]: process.md#processsendmessage-sendhandle-options-callback [`process.setUncaughtExceptionCaptureCallback()`]: process.md#processsetuncaughtexceptioncapturecallbackfn [`readable._read()`]: stream.md#readable_readsize -[`require('node:crypto').setEngine()`]: crypto.md#cryptosetengineengine-flags [`require()`]: modules.md#requireid [`server.close()`]: net.md#serverclosecallback [`server.listen()`]: net.md#serverlisten diff --git a/doc/api/https.md b/doc/api/https.md index 74b759556c4..c4a95ee6749 100644 --- a/doc/api/https.md +++ b/doc/api/https.md @@ -427,6 +427,10 @@ a `timeout` of 5 seconds.