Repository navigation
crypto.createPrivateKey no longer throws TypeError for missing passphrase #48881
Description
Activity
Can reproduce with v19.x and v20.x. Seems like a backwards incompatible change in openssl 3.0.
Node's
PasswordCallbackreturns -1 when no passphrase is provided and that's the right return value according to openssl's documentation, but openssl then turns it into that "interrupted or cancelled" error.Returning 0 makes it return a "ui lib" error, although I'd hardly call that an improvement.
Probably needs to be reported upstream because I don't think this is something that node controls.
One minor weird thing is that our password callback is called twice for your test case:
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1 * frame #0: 0x000000010022ce90 node`node::crypto::PasswordCallback(char*, int, int, void*) frame #1: 0x0000000100f6ff2d node`ui_read + 141 frame #2: 0x0000000100f6eb62 node`UI_process + 290 frame #3: 0x0000000100f11e65 node`ossl_pw_get_passphrase + 1221 frame #4: 0x0000000100f12040 node`ossl_pw_pem_password + 112 frame #5: 0x0000000100f6ff2d node`ui_read + 141 frame #6: 0x0000000100f6eb62 node`UI_process + 290 frame #7: 0x0000000100f11e65 node`ossl_pw_get_passphrase + 1221 frame #8: 0x0000000100fbd43c node`epki2pki_decode + 284 frame #9: 0x0000000100eb142c node`decoder_process + 924 frame #10: 0x0000000100fbe2ec node`pem2der_decode + 1164 frame #11: 0x0000000100eb142c node`decoder_process + 924 frame #12: 0x0000000100eb0f68 node`OSSL_DECODER_from_bio + 296 frame #13: 0x0000000100f2d19c node`pem_read_bio_key + 332 frame #14: 0x0000000100f2d97a node`PEM_read_bio_PrivateKey + 26 frame #15: 0x000000010023a945 node`node::crypto::(anonymous namespace)::ParsePrivateKey(std::__1::unique_ptr<evp_pkey_st, node::FunctionDeleter<evp_pkey_st, &(EVP_PKEY_free)> >*, node::crypto::PrivateKeyEncodingConfig const&, char const*, unsigned long) + 101 frame #16: 0x000000010023a720 node`node::crypto::ManagedEVPPKey::GetPrivateKeyFromJs(v8::FunctionCallbackInfo<v8::Value> const&, unsigned int*, bool) + 464 frame #17: 0x000000010023bf2d node`node::crypto::KeyObjectHandle::Init(v8::FunctionCallbackInfo<v8::Value> const&) + 461 frame #18: 0x00000001002ff958 node`v8::internal::MaybeHandle<v8::internal::Object> v8::internal::(anonymous namespace)::HandleApiCallHelper<false>(v8::internal::Isolate*, v8: :internal::Handle<v8::internal::HeapObject>, v8::internal::Handle<v8::internal::FunctionTemplateInfo>, v8::internal::Handle<v8::internal::Object>, unsigned long*, int) + 856 frame #19: 0x00000001002fef1a node`v8::internal::Builtin_HandleApiCall(int, unsigned long*, v8::internal::Isolate*) + 186Vs.
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1 * frame #0: 0x000000010022ce90 node`node::crypto::PasswordCallback(char*, int, int, void*) frame #1: 0x0000000100f6ff2d node`ui_read + 141 frame #2: 0x0000000100f6eb62 node`UI_process + 290 frame #3: 0x0000000100f11e65 node`ossl_pw_get_passphrase + 1221 frame #4: 0x0000000100f12040 node`ossl_pw_pem_password + 112 frame #5: 0x0000000100f2d53b node`pem_read_bio_key + 1259 frame #6: 0x0000000100f2d97a node`PEM_read_bio_PrivateKey + 26 frame #7: 0x000000010023a945 node`node::crypto::(anonymous namespace)::ParsePrivateKey(std::__1::unique_ptr<evp_pkey_st, node::FunctionDeleter<evp_pkey_st, &(EVP_PKEY_free)> >*, node::crypto::PrivateKeyEncodingConfig const&, char const*, unsigned long) + 101 frame #8: 0x000000010023a720 node`node::crypto::ManagedEVPPKey::GetPrivateKeyFromJs(v8::FunctionCallbackInfo<v8::Value> const&, unsigned int*, bool) + 464 frame #9: 0x000000010023bf2d node`node::crypto::KeyObjectHandle::Init(v8::FunctionCallbackInfo<v8::Value> const&) + 461 frame #10: 0x00000001002ff958 node`v8::internal::MaybeHandle<v8::internal::Object> v8::internal::(anonymous namespace)::HandleApiCallHelper<false>(v8::internal::Isolate*, v8: :internal::Handle<v8::internal::HeapObject>, v8::internal::Handle<v8::internal::FunctionTemplateInfo>, v8::internal::Handle<v8::internal::Object>, unsigned long*, int) + 856 frame #11: 0x00000001002fef1a node`v8::internal::Builtin_HandleApiCall(int, unsigned long*, v8::internal::Isolate*) + 186Note how the call stack diverges starting from pem_read_bio_key.
Reacted by Dmytro Shchehlov- addedconfirmed-bugIssues and PRs for confirmed bugs.Issues and PRs for confirmed bugs.opensslIssues and PRs related to the OpenSSL dependency.Issues and PRs related to the OpenSSL dependency.
on Jul 22, 2023 - addedcryptoIssues and PRs related to the crypto subsystem.Issues and PRs related to the crypto subsystem.
on Jul 31, 2023 I remember trying to fix this in #42400 but i discontinued it
Given that 17.x is no longer supported and this behavior has been around for a while now, closing this in inactionable.
Version
v17.9.1
Platform
Darwin mattw-2.local 21.6.0 Darwin Kernel Version 21.6.0: Thu Jun 8 23:56:13 PDT 2023; root:xnu-8020.240.18.701.6~1/RELEASE_ARM64_T6000 arm64
Subsystem
crypto
What steps will reproduce the bug?
Private key generated with:
openssl genrsa -aes256 -out key.pem -passout pass:password 2048How often does it reproduce? Is there a required condition?
This issue seems to happen for any encrypted key with a missing passphrase.
What is the expected behavior? Why is that the expected behavior?
Expected
createPrivateKeyto throwTypeError: Passphrase required for encrypted key(ERR_MISSING_PASSPHRASE).This is the error thrown by Node 16.14.2 when running that code:
What do you see instead?
createPrivateKeythrowsERR_OSSL_CRYPTO_INTERRUPTED_OR_CANCELLEDinstead ofERR_MISSING_PASSPHRASE.Same thing happens with Node.js v18.17.0.
Additional information
The code path that should be throwing the
TypeErrorstill exists: https://github.com/nodejs/node/blob/v18.17.0/src/crypto/crypto_keys.cc#L828C26-L828C49, so it seems like maybe the OpenSSL upgrade changed whichParseKeyResultgets returned.