Repository: denoland/deno
Pull request: fix(ext/node): make `setAutoPadding(false)` a no-op for GCM ciphers

## Summary

- GCM is a stream cipher mode that doesn't use block padding. In Node.js, `setAutoPadding()` is a no-op for GCM modes, but Deno was throwing `setAutoPadding(false) not supported for Aes128Gcm/Aes256Gcm yet` when called on GCM deciphers.
- This broke npm packages like `ssh2` that set auto-padding to false by default.
- Merges the `auto_pad=false` arms into the existing `auto_pad=true` arms (using `_` wildcard) for both `Aes128Gcm` and `Aes256Gcm` in `Decipher::final()`, and removes the now-unused error variants.

Closes #29425
Closes #28589

Changed files:
filename: ext/node_crypto/cipher.rs
patch: @@ -505,12 +505,6 @@ pub enum DecipherError {
   #[error("Failed to authenticate data")]
   DataAuthenticationFailed,
   #[class(type)]
-  #[error("setAutoPadding(false) not supported for Aes128Gcm yet")]
-  SetAutoPaddingFalseAes128GcmUnsupported,
-  #[class(type)]
-  #[error("setAutoPadding(false) not supported for Aes256Gcm yet")]
-  SetAutoPaddingFalseAes256GcmUnsupported,
-  #[class(type)]
   #[error("Unknown cipher {0}")]
   UnknownCipher(String),
 }
@@ -806,7 +800,7 @@ impl Decipher {
         );
         Ok(())
       }
-      (Aes128Gcm(decipher, auth_tag_length), true) => {
+      (Aes128Gcm(decipher, auth_tag_length), _) => {
         let tag = decipher.finish();
         let tag_slice = tag.as_slice();
         let truncated_tag = if let Some(len) = auth_tag_length {
@@ -820,10 +814,7 @@ impl Decipher {
           Err(DecipherError::DataAuthenticationFailed)
         }
       }
-      (Aes128Gcm(..), false) => {
-        Err(DecipherError::SetAutoPaddingFalseAes128GcmUnsupported)
-      }
-      (Aes256Gcm(decipher, auth_tag_length), true) => {
+      (Aes256Gcm(decipher, auth_tag_length), _) => {
         let tag = decipher.finish();
         let tag_slice = tag.as_slice();
         let truncated_tag = if let Some(len) = auth_tag_length {
@@ -837,9 +828,6 @@ impl Decipher {
           Err(DecipherError::DataAuthenticationFailed)
         }
       }
-      (Aes256Gcm(..), false) => {
-        Err(DecipherError::SetAutoPaddingFalseAes256GcmUnsupported)
-      }
       (Aes256Cbc(decryptor), true) => {
         assert_block_len!(input.len(), 16);
         let _ = (*decryptor)

filename: tests/unit_node/crypto/crypto_cipher_gcm_test.ts
patch: @@ -146,6 +146,39 @@ Deno.test({
   },
 });
 
+Deno.test({
+  name: "aes-128-gcm and aes-256-gcm with setAutoPadding(false)",
+  fn() {
+    for (const bits of ["128", "256"] as const) {
+      const algo = `aes-${bits}-gcm` as const;
+      const keyLen = bits === "128" ? 16 : 32;
+      const key = Buffer.alloc(keyLen, 0xaa);
+      const iv = Buffer.alloc(12, 0xbb);
+      const plaintext = "Hello, GCM with setAutoPadding(false)!";
+
+      // Encrypt
+      const cipher = crypto.createCipheriv(algo, key, iv);
+      cipher.setAutoPadding(false);
+      const encrypted = Buffer.concat([
+        cipher.update(plaintext, "utf8"),
+        cipher.final(),
+      ]);
+      const authTag = cipher.getAuthTag();
+
+      // Decrypt
+      const decipher = crypto.createDecipheriv(algo, key, iv);
+      decipher.setAutoPadding(false);
+      decipher.setAuthTag(authTag);
+      const decrypted = Buffer.concat([
+        decipher.update(encrypted),
+        decipher.final(),
+      ]);
+
+      assertEquals(decrypted.toString("utf8"), plaintext);
+    }
+  },
+});
+
 Deno.test({
   name: "aes gcm with invalid key length",
   fn() {