Repository: denoland/deno
Pull request: fix(ext/node): validate input lengths in `Cipheriv` and `Decipheriv`

This PR adds the validation of key and init vector lengths for `createDecipheriv` and `createCipheriv` with 'aes-256-cbc' algorithm.

Currently we don't check the lengths of the inputs and that causes panics at `key.into()` or `iv.into()` calls [here](https://github.com/denoland/deno/blob/1521adf5ed640832755e362abc64b32afd7dcc7d/ext/node/ops/crypto/cipher.rs#L349) (that causes #25279). This PR adds the check of these lengths and throws appropriate errors similar to what Node.js throws.

addresses the first part of #25279

Changed files:
filename: ext/node/ops/crypto/cipher.rs
patch: @@ -4,6 +4,7 @@ use aes::cipher::block_padding::Pkcs7;
 use aes::cipher::BlockDecryptMut;
 use aes::cipher::BlockEncryptMut;
 use aes::cipher::KeyIvInit;
+use deno_core::error::range_error;
 use deno_core::error::type_error;
 use deno_core::error::AnyError;
 use deno_core::Resource;
@@ -157,6 +158,13 @@ impl Cipher {
         Aes256Gcm(Box::new(cipher))
       }
       "aes256" | "aes-256-cbc" => {
+        if key.len() != 32 {
+          return Err(range_error("Invalid key length"));
+        }
+        if iv.len() != 16 {
+          return Err(type_error("Invalid initialization vector"));
+        }
+
         Aes256Cbc(Box::new(cbc::Encryptor::new(key.into(), iv.into())))
       }
       _ => return Err(type_error(format!("Unknown cipher {algorithm_name}"))),
@@ -346,6 +354,13 @@ impl Decipher {
         Aes256Gcm(Box::new(decipher))
       }
       "aes256" | "aes-256-cbc" => {
+        if key.len() != 32 {
+          return Err(range_error("Invalid key length"));
+        }
+        if iv.len() != 16 {
+          return Err(type_error("Invalid initialization vector"));
+        }
+
         Aes256Cbc(Box::new(cbc::Decryptor::new(key.into(), iv.into())))
       }
       _ => return Err(type_error(format!("Unknown cipher {algorithm_name}"))),

filename: ext/node/ops/crypto/mod.rs
patch: @@ -220,13 +220,9 @@ pub fn op_node_create_cipheriv(
   #[string] algorithm: &str,
   #[buffer] key: &[u8],
   #[buffer] iv: &[u8],
-) -> u32 {
-  state.resource_table.add(
-    match cipher::CipherContext::new(algorithm, key, iv) {
-      Ok(context) => context,
-      Err(_) => return 0,
-    },
-  )
+) -> Result<u32, AnyError> {
+  let context = cipher::CipherContext::new(algorithm, key, iv)?;
+  Ok(state.resource_table.add(context))
 }
 
 #[op2(fast)]
@@ -292,13 +288,9 @@ pub fn op_node_create_decipheriv(
   #[string] algorithm: &str,
   #[buffer] key: &[u8],
   #[buffer] iv: &[u8],
-) -> u32 {
-  state.resource_table.add(
-    match cipher::DecipherContext::new(algorithm, key, iv) {
-      Ok(context) => context,
-      Err(_) => return 0,
-    },
-  )
+) -> Result<u32, AnyError> {
+  let context = cipher::DecipherContext::new(algorithm, key, iv)?;
+  Ok(state.resource_table.add(context))
 }
 
 #[op2(fast)]

filename: tests/unit_node/crypto/crypto_cipher_test.ts
patch: @@ -245,6 +245,44 @@ Deno.test({
   },
 });
 
+Deno.test({
+  name: "createCipheriv - invalid inputs",
+  fn() {
+    assertThrows(
+      () =>
+        crypto.createCipheriv("aes256", new Uint8Array(31), new Uint8Array(16)),
+      RangeError,
+      "Invalid key length",
+    );
+    assertThrows(
+      () =>
+        crypto.createCipheriv(
+          "aes-256-cbc",
+          new Uint8Array(31),
+          new Uint8Array(16),
+        ),
+      RangeError,
+      "Invalid key length",
+    );
+    assertThrows(
+      () =>
+        crypto.createCipheriv("aes256", new Uint8Array(32), new Uint8Array(15)),
+      TypeError,
+      "Invalid initialization vector",
+    );
+    assertThrows(
+      () =>
+        crypto.createCipheriv(
+          "aes-256-cbc",
+          new Uint8Array(32),
+          new Uint8Array(15),
+        ),
+      TypeError,
+      "Invalid initialization vector",
+    );
+  },
+});
+
 Deno.test({
   name: "createDecipheriv - invalid algorithm",
   fn() {
@@ -257,6 +295,52 @@ Deno.test({
   },
 });
 
+Deno.test({
+  name: "createDecipheriv - invalid inputs",
+  fn() {
+    assertThrows(
+      () =>
+        crypto.createDecipheriv(
+          "aes256",
+          new Uint8Array(31),
+          new Uint8Array(16),
+        ),
+      RangeError,
+      "Invalid key length",
+    );
+    assertThrows(
+      () =>
+        crypto.createDecipheriv(
+          "aes-256-cbc",
+          new Uint8Array(31),
+          new Uint8Array(16),
+        ),
+      RangeError,
+      "Invalid key length",
+    );
+    assertThrows(
+      () =>
+        crypto.createDecipheriv(
+          "aes256",
+          new Uint8Array(32),
+          new Uint8Array(15),
+        ),
+      TypeError,
+      "Invalid initialization vector",
+    );
+    assertThrows(
+      () =>
+        crypto.createDecipheriv(
+          "aes-256-cbc",
+          new Uint8Array(32),
+          new Uint8Array(15),
+        ),
+      TypeError,
+      "Invalid initialization vector",
+    );
+  },
+});
+
 Deno.test({
   name: "getCiphers",
   fn() {