src: fix error handling on async crypto operations
authorRafaelGSS <rafael.nunu@hotmail.com>
Mon, 12 May 2025 15:33:54 +0000 (12:33 -0300)
committerBastien Roucariès <rouca@debian.org>
Mon, 6 Apr 2026 14:18:52 +0000 (16:18 +0200)
Fixes: https://hackerone.com/reports/2817648
Co-Authored-By: Filip Skokan <panva.ip@gmail.com>
Co-Authored-By: Tobias Nießen <tniessen@tnie.de>
Backport-PR-URL: https://github.com/nodejs-private/node-private/pull/688
CVE-ID: CVE-2025-23166
PR-URL: https://github.com/nodejs-private/node-private/pull/710

origin: backport, https://github.com/nodejs/node/commit/6c57465920cf1b981a63031e71b1e4a73bf9beaa

Gbp-Pq: Name CVE-2025-23166.patch

20 files changed:
src/crypto/crypto_dh.cc
src/crypto/crypto_dh.h
src/crypto/crypto_ec.cc
src/crypto/crypto_ec.h
src/crypto/crypto_hash.cc
src/crypto/crypto_hash.h
src/crypto/crypto_hkdf.cc
src/crypto/crypto_hkdf.h
src/crypto/crypto_hmac.cc
src/crypto/crypto_hmac.h
src/crypto/crypto_pbkdf2.cc
src/crypto/crypto_pbkdf2.h
src/crypto/crypto_random.cc
src/crypto/crypto_random.h
src/crypto/crypto_scrypt.cc
src/crypto/crypto_scrypt.h
src/crypto/crypto_sig.cc
src/crypto/crypto_sig.h
src/crypto/crypto_util.h
test/parallel/test-crypto-async-sign-verify.js

index dd69323b80076d7333b80453c9cc9ef5b680ce27..cf9cbc97394b121155d294783eb59c1cf8c5db2e 100644 (file)
@@ -705,10 +705,10 @@ Maybe<bool> DHBitsTraits::EncodeOutput(
   return Just(!result->IsEmpty());
 }
 
-bool DHBitsTraits::DeriveBits(
-    Environment* env,
-    const DHBitsConfig& params,
-    ByteSource* out) {
+bool DHBitsTraits::DeriveBits(Environment* env,
+                              const DHBitsConfig& params,
+                              ByteSource* out,
+                              CryptoJobMode mode) {
   *out = StatelessDiffieHellmanThreadsafe(
       params.private_key->GetAsymmetricKey(),
       params.public_key->GetAsymmetricKey());
index ec12548dbe57d56d6ef139bde0e697278309becd..f7c4b67572541292a72b3e87c855ce09e371319c 100644 (file)
@@ -131,10 +131,10 @@ struct DHBitsTraits final {
       unsigned int offset,
       DHBitsConfig* params);
 
-  static bool DeriveBits(
-      Environment* env,
-      const DHBitsConfig& params,
-      ByteSource* out_);
+  static bool DeriveBits(Environment* env,
+                         const DHBitsConfig& params,
+                         ByteSource* out_,
+                         CryptoJobMode mode);
 
   static v8::Maybe<bool> EncodeOutput(
       Environment* env,
index 3ccea99fb2bbe9ff33d8fe227081721fa1b39d9c..d7c77d0780abc3038af5032c067c035069fa9755 100644 (file)
@@ -484,7 +484,8 @@ Maybe<bool> ECDHBitsTraits::AdditionalConfig(
 
 bool ECDHBitsTraits::DeriveBits(Environment* env,
                                 const ECDHBitsConfig& params,
-                                ByteSource* out) {
+                                ByteSource* out,
+                                CryptoJobMode mode) {
   size_t len = 0;
   ManagedEVPPKey m_privkey = params.private_->GetAsymmetricKey();
   ManagedEVPPKey m_pubkey = params.public_->GetAsymmetricKey();
index 9782ce0bf35a661417f5c74a13bebe288390b00d..8c955ecdbfadb9113461309a673d14aee94b8fd7 100644 (file)
@@ -77,10 +77,10 @@ struct ECDHBitsTraits final {
       unsigned int offset,
       ECDHBitsConfig* params);
 
-  static bool DeriveBits(
-      Environment* env,
-      const ECDHBitsConfig& params,
-      ByteSource* out_);
+  static bool DeriveBits(Environment* env,
+                         const ECDHBitsConfig& params,
+                         ByteSource* out_,
+                         CryptoJobMode mode);
 
   static v8::Maybe<bool> EncodeOutput(
       Environment* env,
index 3cb39d795c732d36d2eea4fe7cafe45b27a16f2f..eb1d5152ad93e452a4a046d2256c5e9059dfb4a7 100644 (file)
@@ -282,10 +282,10 @@ Maybe<bool> HashTraits::AdditionalConfig(
   return Just(true);
 }
 
-bool HashTraits::DeriveBits(
-    Environment* env,
-    const HashConfig& params,
-    ByteSource* out) {
+bool HashTraits::DeriveBits(Environment* env,
+                            const HashConfig& params,
+                            ByteSource* out,
+                            CryptoJobMode mode) {
   EVPMDPointer ctx(EVP_MD_CTX_new());
 
   if (UNLIKELY(!ctx ||
index 2d17c3510ed21443a49e618258cce7f1fcf45359..d673694622f2e456f0661a0178e74fd982e9301d 100644 (file)
@@ -68,10 +68,10 @@ struct HashTraits final {
       unsigned int offset,
       HashConfig* params);
 
-  static bool DeriveBits(
-      Environment* env,
-      const HashConfig& params,
-      ByteSource* out);
+  static bool DeriveBits(Environment* env,
+                         const HashConfig& params,
+                         ByteSource* out,
+                         CryptoJobMode mode);
 
   static v8::Maybe<bool> EncodeOutput(
       Environment* env,
index 7663dd69374db742362b273f31510b0d38bc8bd3..896bcf20d7382a3ebb955f57ca64bc784dd83f47 100644 (file)
@@ -100,10 +100,10 @@ Maybe<bool> HKDFTraits::AdditionalConfig(
   return Just(true);
 }
 
-bool HKDFTraits::DeriveBits(
-    Environment* env,
-    const HKDFConfig& params,
-    ByteSource* out) {
+bool HKDFTraits::DeriveBits(Environment* env,
+                            const HKDFConfig& params,
+                            ByteSource* out,
+                            CryptoJobMode mode) {
   EVPKeyCtxPointer ctx =
       EVPKeyCtxPointer(EVP_PKEY_CTX_new_id(EVP_PKEY_HKDF, nullptr));
   if (!ctx || !EVP_PKEY_derive_init(ctx.get()) ||
index c4a537cef8a79234c672f53706aa4db32b30db1c..acd2b670a8c8e71220e76ee346c4673dee10ed50 100644 (file)
@@ -42,10 +42,10 @@ struct HKDFTraits final {
       unsigned int offset,
       HKDFConfig* params);
 
-  static bool DeriveBits(
-      Environment* env,
-      const HKDFConfig& params,
-      ByteSource* out);
+  static bool DeriveBits(Environment* env,
+                         const HKDFConfig& params,
+                         ByteSource* out,
+                         CryptoJobMode mode);
 
   static v8::Maybe<bool> EncodeOutput(
       Environment* env,
index a9bb00ee5939f95d89407d824a0e42a335ef56e5..8173946b12bc75ea4a44346df65d7bd3e9bb1b9e 100644 (file)
@@ -222,10 +222,10 @@ Maybe<bool> HmacTraits::AdditionalConfig(
   return Just(true);
 }
 
-bool HmacTraits::DeriveBits(
-    Environment* env,
-    const HmacConfig& params,
-    ByteSource* out) {
+bool HmacTraits::DeriveBits(Environment* env,
+                            const HmacConfig& params,
+                            ByteSource* out,
+                            CryptoJobMode mode) {
   HMACCtxPointer ctx(HMAC_CTX_new());
 
   if (!ctx ||
index c80cc36f11dddc232047ae056d962bf9249643b8..dd490f05ec2a80aa08e85e7f03ee98c642c64b3d 100644 (file)
@@ -73,10 +73,10 @@ struct HmacTraits final {
       unsigned int offset,
       HmacConfig* params);
 
-  static bool DeriveBits(
-      Environment* env,
-      const HmacConfig& params,
-      ByteSource* out);
+  static bool DeriveBits(Environment* env,
+                         const HmacConfig& params,
+                         ByteSource* out,
+                         CryptoJobMode mode);
 
   static v8::Maybe<bool> EncodeOutput(
       Environment* env,
index 963d0db6c62a456bf02395b012b11fec73e9c2f0..f6d37dadcdf707394a0a0aa5a48f4478b12c7963 100644 (file)
@@ -111,10 +111,10 @@ Maybe<bool> PBKDF2Traits::AdditionalConfig(
   return Just(true);
 }
 
-bool PBKDF2Traits::DeriveBits(
-    Environment* env,
-    const PBKDF2Config& params,
-    ByteSource* out) {
+bool PBKDF2Traits::DeriveBits(Environment* env,
+                              const PBKDF2Config& params,
+                              ByteSource* out,
+                              CryptoJobMode mode) {
   ByteSource::Builder buf(params.length);
 
   // Both pass and salt may be zero length here.
index 6fda7cd3101002561ff98736b8a7a77cc2cee998..11ffad784db59b0d49077c5951c446916c996a2f 100644 (file)
@@ -55,10 +55,10 @@ struct PBKDF2Traits final {
       unsigned int offset,
       PBKDF2Config* params);
 
-  static bool DeriveBits(
-      Environment* env,
-      const PBKDF2Config& params,
-      ByteSource* out);
+  static bool DeriveBits(Environment* env,
+                         const PBKDF2Config& params,
+                         ByteSource* out,
+                         CryptoJobMode mode);
 
   static v8::Maybe<bool> EncodeOutput(
       Environment* env,
index 9850104cd607f877a8e867e83a8d4d4ccd4a4395..527f6a8b226662def04e17fac75d1fb43ccca37b 100644 (file)
@@ -56,10 +56,10 @@ Maybe<bool> RandomBytesTraits::AdditionalConfig(
   return Just(true);
 }
 
-bool RandomBytesTraits::DeriveBits(
-    Environment* env,
-    const RandomBytesConfig& params,
-    ByteSource* unused) {
+bool RandomBytesTraits::DeriveBits(Environment* env,
+                                   const RandomBytesConfig& params,
+                                   ByteSource* unused,
+                                   CryptoJobMode mode) {
   return CSPRNG(params.buffer, params.size).is_ok();
 }
 
@@ -151,7 +151,8 @@ Maybe<bool> RandomPrimeTraits::AdditionalConfig(
 
 bool RandomPrimeTraits::DeriveBits(Environment* env,
                                    const RandomPrimeConfig& params,
-                                   ByteSource* unused) {
+                                   ByteSource* unused,
+                                   CryptoJobMode mode) {
   // BN_generate_prime_ex() calls RAND_bytes_ex() internally.
   // Make sure the CSPRNG is properly seeded.
   CHECK(CSPRNG(nullptr, 0).is_ok());
@@ -194,11 +195,10 @@ Maybe<bool> CheckPrimeTraits::AdditionalConfig(
   return Just(true);
 }
 
-bool CheckPrimeTraits::DeriveBits(
-    Environment* env,
-    const CheckPrimeConfig& params,
-    ByteSource* out) {
-
+bool CheckPrimeTraits::DeriveBits(Environment* env,
+                                  const CheckPrimeConfig& params,
+                                  ByteSource* out,
+                                  CryptoJobMode mode) {
   BignumCtxPointer ctx(BN_CTX_new());
 
   int ret = BN_is_prime_ex(
index a2807ed6ec8743be9786f781e72bb15711f48272..b673cbbfd1704fa8d0f407c47dd46ed0c53e7b4d 100644 (file)
@@ -32,10 +32,10 @@ struct RandomBytesTraits final {
       unsigned int offset,
       RandomBytesConfig* params);
 
-  static bool DeriveBits(
-      Environment* env,
-      const RandomBytesConfig& params,
-      ByteSource* out_);
+  static bool DeriveBits(Environment* env,
+                         const RandomBytesConfig& params,
+                         ByteSource* out_,
+                         CryptoJobMode mode);
 
   static v8::Maybe<bool> EncodeOutput(
       Environment* env,
@@ -72,7 +72,8 @@ struct RandomPrimeTraits final {
   static bool DeriveBits(
       Environment* env,
       const RandomPrimeConfig& params,
-      ByteSource* out_);
+      ByteSource* out_,
+      CryptoJobMode mode);
 
   static v8::Maybe<bool> EncodeOutput(
       Environment* env,
@@ -105,10 +106,10 @@ struct CheckPrimeTraits final {
       unsigned int offset,
       CheckPrimeConfig* params);
 
-  static bool DeriveBits(
-      Environment* env,
-      const CheckPrimeConfig& params,
-      ByteSource* out);
+  static bool DeriveBits(Environment* env,
+                         const CheckPrimeConfig& params,
+                         ByteSource* out,
+                         CryptoJobMode mode);
 
   static v8::Maybe<bool> EncodeOutput(
       Environment* env,
index 4dae07f13604d4ad36d73c40581faf69d823778f..99a6a0e7a9e5b1d9c3fa052b60d0dffbb437b67c 100644 (file)
@@ -114,10 +114,10 @@ Maybe<bool> ScryptTraits::AdditionalConfig(
   return Just(true);
 }
 
-bool ScryptTraits::DeriveBits(
-    Environment* env,
-    const ScryptConfig& params,
-    ByteSource* out) {
+bool ScryptTraits::DeriveBits(Environment* env,
+                              const ScryptConfig& params,
+                              ByteSource* out,
+                              CryptoJobMode mode) {
   ByteSource::Builder buf(params.length);
 
   // Both the pass and salt may be zero-length at this point
index 3d185637f44be3fa76dd9948a25bfee4a56ccb17..9ea9d75d85bf35192196ecdcc732dd6c2b16e631 100644 (file)
@@ -57,10 +57,10 @@ struct ScryptTraits final {
       unsigned int offset,
       ScryptConfig* params);
 
-  static bool DeriveBits(
-      Environment* env,
-      const ScryptConfig& params,
-      ByteSource* out);
+  static bool DeriveBits(Environment* env,
+                         const ScryptConfig& params,
+                         ByteSource* out,
+                         CryptoJobMode mode);
 
   static v8::Maybe<bool> EncodeOutput(
       Environment* env,
index 64e788dcecaca106d602310c38a4ab53c6ed7f4a..18810342420588acf8473fc1afe5c0d333f14a4d 100644 (file)
@@ -695,12 +695,13 @@ Maybe<bool> SignTraits::AdditionalConfig(
   return Just(true);
 }
 
-bool SignTraits::DeriveBits(
-    Environment* env,
-    const SignConfiguration& params,
-    ByteSource* out) {
-  ClearErrorOnReturn clear_error_on_return;
+bool SignTraits::DeriveBits(Environment* env,
+                            const SignConfiguration& params,
+                            ByteSource* out,
+                            CryptoJobMode mode) {
+  bool can_throw = mode == CryptoJobMode::kCryptoJobSync;
   EVPMDPointer context(EVP_MD_CTX_new());
+
   EVP_PKEY_CTX* ctx = nullptr;
 
   switch (params.mode) {
@@ -711,7 +712,7 @@ bool SignTraits::DeriveBits(
               params.digest,
               nullptr,
               params.key.get())) {
-        crypto::CheckThrow(env, SignBase::Error::kSignInit);
+        if (can_throw) crypto::CheckThrow(env, SignBase::Error::kSignInit);
         return false;
       }
       break;
@@ -722,7 +723,7 @@ bool SignTraits::DeriveBits(
               params.digest,
               nullptr,
               params.key.get())) {
-        crypto::CheckThrow(env, SignBase::Error::kSignInit);
+        if (can_throw) crypto::CheckThrow(env, SignBase::Error::kSignInit);
         return false;
       }
       break;
@@ -740,7 +741,7 @@ bool SignTraits::DeriveBits(
           ctx,
           padding,
           salt_length)) {
-    crypto::CheckThrow(env, SignBase::Error::kSignPrivateKey);
+    if (can_throw) crypto::CheckThrow(env, SignBase::Error::kSignPrivateKey);
     return false;
   }
 
@@ -754,7 +755,8 @@ bool SignTraits::DeriveBits(
             &len,
             params.data.data<unsigned char>(),
             params.data.size())) {
-          crypto::CheckThrow(env, SignBase::Error::kSignPrivateKey);
+          if (can_throw)
+            crypto::CheckThrow(env, SignBase::Error::kSignPrivateKey);
           return false;
         }
         ByteSource::Builder buf(len);
@@ -763,7 +765,8 @@ bool SignTraits::DeriveBits(
                             &len,
                             params.data.data<unsigned char>(),
                             params.data.size())) {
-          crypto::CheckThrow(env, SignBase::Error::kSignPrivateKey);
+          if (can_throw)
+            crypto::CheckThrow(env, SignBase::Error::kSignPrivateKey);
           return false;
         }
         *out = std::move(buf).release(len);
@@ -774,13 +777,15 @@ bool SignTraits::DeriveBits(
                 params.data.data<unsigned char>(),
                 params.data.size()) ||
             !EVP_DigestSignFinal(context.get(), nullptr, &len)) {
-          crypto::CheckThrow(env, SignBase::Error::kSignPrivateKey);
+          if (can_throw)
+            crypto::CheckThrow(env, SignBase::Error::kSignPrivateKey);
           return false;
         }
         ByteSource::Builder buf(len);
         if (!EVP_DigestSignFinal(
                 context.get(), buf.data<unsigned char>(), &len)) {
-          crypto::CheckThrow(env, SignBase::Error::kSignPrivateKey);
+          if (can_throw)
+            crypto::CheckThrow(env, SignBase::Error::kSignPrivateKey);
           return false;
         }
 
index 1a4cda42272e51cf943fa87cd822729e92961a48..6b3c8d915e6f3d9dd656c5c132ff251debeadfc7 100644 (file)
@@ -147,10 +147,10 @@ struct SignTraits final {
       unsigned int offset,
       SignConfiguration* params);
 
-  static bool DeriveBits(
-      Environment* env,
-      const SignConfiguration& params,
-      ByteSource* out);
+  static bool DeriveBits(Environment* env,
+                         const SignConfiguration& params,
+                         ByteSource* out,
+                         CryptoJobMode mode);
 
   static v8::Maybe<bool> EncodeOutput(
       Environment* env,
index bf19334cf61fa497c9325c1d2e996a16545f1b7f..e528de912973f1b2c9e34f0fa3b6156c01066d78 100644 (file)
@@ -498,9 +498,10 @@ class DeriveBitsJob final : public CryptoJob<DeriveBitsTraits> {
             std::move(params)) {}
 
   void DoThreadPoolWork() override {
+    ClearErrorOnReturn clear_error_on_return;
     if (!DeriveBitsTraits::DeriveBits(
             AsyncWrap::env(),
-            *CryptoJob<DeriveBitsTraits>::params(), &out_)) {
+            *CryptoJob<DeriveBitsTraits>::params(), &out_, this->mode())) {
       CryptoErrorStore* errors = CryptoJob<DeriveBitsTraits>::errors();
       errors->Capture();
       if (errors->Empty())
index 4e3c32fdcd23fbe3e74bd5e624b739d224689f33..5924d36e480721ccb6ea858d5fc62fb881490471 100644 (file)
@@ -141,3 +141,29 @@ test('dsa_public.pem', 'dsa_private.pem', 'sha256', false,
   })
   .catch(common.mustNotCall());
 }
+
+{
+  const untrustedKey = `-----BEGIN PUBLIC KEY-----
+MCowBQYDK2VuAyEA6pwGRbadNQAI/tYN8+/p/0/hbsdHfOEGr1ADiLVk/Gc=
+-----END PUBLIC KEY-----`;
+  const data = crypto.randomBytes(32);
+  const signature = crypto.randomBytes(16);
+
+  const expected = common.hasOpenSSL3 ?
+    /operation not supported for this keytype/ : /no default digest/;
+
+  crypto.verify(undefined, data, untrustedKey, signature, common.mustCall((err) => {
+    assert.ok(err);
+    assert.match(err.message, expected);
+  }));
+}
+
+{
+  const { privateKey } = crypto.generateKeyPairSync('rsa', {
+    modulusLength: 512
+  });
+  crypto.sign('sha512', 'message', privateKey, common.mustCall((err) => {
+    assert.ok(err);
+    assert.match(err.message, /digest too big for rsa key/);
+  }));
+}