Commit 686b3464db7 for nodejs

commit 686b3464db7f4cffb2bd9173145225f399b27bb2
Author: Filip Skokan <panva.ip@gmail.com>
Date:   Wed Sep 23 14:38:13 2026 +0200

    crypto: verify empty KMAC outputs

    Compare empty KMAC outputs with empty signatures as equal, as required
    by the verification algorithm. Skip CRYPTO_memcmp for zero-length
    buffers while retaining the constant-time comparison for other MACs.

    Signed-off-by: Filip Skokan <panva.ip@gmail.com>
    Assisted-by: Codex
    PR-URL: https://github.com/nodejs/node/pull/66237
    Reviewed-By: James M Snell <jasnell@gmail.com>
    Reviewed-By: Aviv Keller <me@aviv.sh>

diff --git a/src/crypto/crypto_kmac.cc b/src/crypto/crypto_kmac.cc
index f7a994b9e12..40b39990737 100644
--- a/src/crypto/crypto_kmac.cc
+++ b/src/crypto/crypto_kmac.cc
@@ -191,9 +191,10 @@ MaybeLocal<Value> KmacTraits::EncodeOutput(Environment* env,
     case SignConfiguration::Mode::Verify:
       return Boolean::New(
           env->isolate(),
-          out->size() > 0 && out->size() == params.signature.size() &&
-              CRYPTO_memcmp(
-                  out->data(), params.signature.data(), out->size()) == 0);
+          out->size() == params.signature.size() &&
+              (out->size() == 0 ||
+               CRYPTO_memcmp(
+                   out->data(), params.signature.data(), out->size()) == 0));
   }
   UNREACHABLE();
 }
diff --git a/test/parallel/test-webcrypto-kmac-empty-output.js b/test/parallel/test-webcrypto-kmac-empty-output.js
new file mode 100644
index 00000000000..fe88f7ab769
--- /dev/null
+++ b/test/parallel/test-webcrypto-kmac-empty-output.js
@@ -0,0 +1,42 @@
+'use strict';
+
+const common = require('../common');
+
+if (!common.hasCrypto)
+  common.skip('missing crypto');
+
+const { hasFIPS, hasOpenSSL } = require('../common/crypto');
+
+if (!hasOpenSSL(3))
+  common.skip('requires OpenSSL >= 3');
+if (hasFIPS())
+  common.skip('empty KMAC output is not supported in FIPS mode');
+
+const assert = require('assert');
+const { subtle } = globalThis.crypto;
+
+(async () => {
+  const data = new Uint8Array([1, 2, 3]);
+
+  for (const name of ['KMAC128', 'KMAC256']) {
+    const key = await subtle.importKey(
+      'raw-secret', new Uint8Array(32), name, false, ['sign', 'verify']);
+    const otherKey = await subtle.importKey(
+      'raw-secret', new Uint8Array(32).fill(1), name, false, ['verify']);
+    const algorithm = { name, outputLength: 0 };
+    const signature = await subtle.sign(algorithm, key, data);
+
+    assert.strictEqual(signature.byteLength, 0);
+    assert.strictEqual(await subtle.verify(algorithm, key, signature, data), true);
+    assert.strictEqual(await subtle.verify(algorithm, key, new Uint8Array([0]), data), false);
+    assert.strictEqual(await subtle.verify(
+      { name, outputLength: 256 }, key, signature, data), false);
+
+    // Empty MACs compare equal even when the inputs differ.
+    assert.strictEqual(await subtle.verify(
+      algorithm, key, signature, new Uint8Array([4, 5, 6])), true);
+    assert.strictEqual(await subtle.verify(algorithm, otherKey, signature, data), true);
+    assert.strictEqual(await subtle.verify(
+      { ...algorithm, customization: new Uint8Array([1]) }, key, signature, data), true);
+  }
+})().then(common.mustCall());