Commit d0cbb419865 for nodejs

commit d0cbb41986550917851446aaf14a6e75672a7f5b
Author: Filip Skokan <panva.ip@gmail.com>
Date:   Tue Sep 22 22:14:41 2026 +0200

    crypto: allow short AES-GCM IVs

    Allow nonempty GCM IVs shorter than the default length and let the
    underlying cipher enforce its supported range.

    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_aes.cc b/src/crypto/crypto_aes.cc
index e46a84188f0..ea869a8eef1 100644
--- a/src/crypto/crypto_aes.cc
+++ b/src/crypto/crypto_aes.cc
@@ -573,8 +573,13 @@ Maybe<void> AESCipherTraits::AdditionalConfig(
     UseDefaultIV(params);
   }

-  // For OCB mode, allow variable IV lengths (1-15 bytes)
-  if (params->cipher.isOcbMode()) {
+  if (params->cipher.isGcmMode()) {
+    if (params->iv.size() == 0) {
+      THROW_ERR_CRYPTO_INVALID_IV(env);
+      return Nothing<void>();
+    }
+  } else if (params->cipher.isOcbMode()) {
+    // For OCB mode, allow variable IV lengths (1-15 bytes).
     if (params->iv.size() == 0 || params->iv.size() > 15) {
       THROW_ERR_CRYPTO_INVALID_IV(env);
       return Nothing<void>();
diff --git a/test/parallel/test-webcrypto-aes-gcm-iv-length.js b/test/parallel/test-webcrypto-aes-gcm-iv-length.js
new file mode 100644
index 00000000000..a4243bd0930
--- /dev/null
+++ b/test/parallel/test-webcrypto-aes-gcm-iv-length.js
@@ -0,0 +1,42 @@
+'use strict';
+
+const common = require('../common');
+
+if (!common.hasCrypto)
+  common.skip('missing crypto');
+
+const assert = require('assert');
+const { createCipheriv } = require('crypto');
+const { subtle } = globalThis.crypto;
+
+(async () => {
+  const plaintext = Buffer.from('AES-GCM with a variable-length IV');
+  const additionalData = Buffer.from('additional data');
+
+  for (const length of [128, 192, 256]) {
+    const keyBytes = Buffer.alloc(length / 8);
+    const key = await subtle.importKey(
+      'raw', keyBytes, 'AES-GCM', false, ['encrypt', 'decrypt']);
+
+    for (const ivLength of [1, 8, 11, 12, 16, 128]) {
+      const iv = Buffer.alloc(ivLength, 1);
+      const algorithm = { name: 'AES-GCM', iv, additionalData };
+      const cipher = createCipheriv(`aes-${length}-gcm`, keyBytes, iv);
+      cipher.setAAD(additionalData);
+      const expected = Buffer.concat([
+        cipher.update(plaintext), cipher.final(), cipher.getAuthTag(),
+      ]);
+
+      assert.deepStrictEqual(
+        Buffer.from(await subtle.encrypt(algorithm, key, plaintext)), expected);
+      assert.deepStrictEqual(
+        Buffer.from(await subtle.decrypt(algorithm, key, expected)), plaintext);
+    }
+
+    const algorithm = { name: 'AES-GCM', iv: new Uint8Array(0) };
+    await assert.rejects(subtle.encrypt(algorithm, key, plaintext),
+                         { name: 'OperationError' });
+    await assert.rejects(subtle.decrypt(algorithm, key, new Uint8Array(16)),
+                         { name: 'OperationError' });
+  }
+})().then(common.mustCall());