deno_crypto/
decrypt.rs

1// Copyright 2018-2025 the Deno authors. MIT license.
2
3use aes::cipher::BlockDecryptMut;
4use aes::cipher::KeyIvInit;
5use aes::cipher::block_padding::Pkcs7;
6use aes_gcm::AeadInPlace;
7use aes_gcm::KeyInit;
8use aes_gcm::Nonce;
9use aes_gcm::aead::generic_array::ArrayLength;
10use aes_gcm::aead::generic_array::typenum::U12;
11use aes_gcm::aead::generic_array::typenum::U16;
12use aes_gcm::aes::Aes128;
13use aes_gcm::aes::Aes192;
14use aes_gcm::aes::Aes256;
15use ctr::Ctr32BE;
16use ctr::Ctr64BE;
17use ctr::Ctr128BE;
18use ctr::cipher::StreamCipher;
19use deno_core::JsBuffer;
20use deno_core::ToJsBuffer;
21use deno_core::op2;
22use deno_core::unsync::spawn_blocking;
23use rsa::pkcs1::DecodeRsaPrivateKey;
24use serde::Deserialize;
25use sha1::Sha1;
26use sha2::Sha256;
27use sha2::Sha384;
28use sha2::Sha512;
29
30use crate::shared::*;
31
32#[derive(Deserialize)]
33#[serde(rename_all = "camelCase")]
34pub struct DecryptOptions {
35  key: V8RawKeyData,
36  #[serde(flatten)]
37  algorithm: DecryptAlgorithm,
38}
39
40#[derive(Deserialize)]
41#[serde(rename_all = "camelCase", tag = "algorithm")]
42pub enum DecryptAlgorithm {
43  #[serde(rename = "RSA-OAEP")]
44  RsaOaep {
45    hash: ShaHash,
46    #[serde(with = "serde_bytes")]
47    label: Vec<u8>,
48  },
49  #[serde(rename = "AES-CBC", rename_all = "camelCase")]
50  AesCbc {
51    #[serde(with = "serde_bytes")]
52    iv: Vec<u8>,
53    length: usize,
54  },
55  #[serde(rename = "AES-CTR", rename_all = "camelCase")]
56  AesCtr {
57    #[serde(with = "serde_bytes")]
58    counter: Vec<u8>,
59    ctr_length: usize,
60    key_length: usize,
61  },
62  #[serde(rename = "AES-GCM", rename_all = "camelCase")]
63  AesGcm {
64    #[serde(with = "serde_bytes")]
65    iv: Vec<u8>,
66    #[serde(with = "serde_bytes")]
67    additional_data: Option<Vec<u8>>,
68    length: usize,
69    tag_length: usize,
70  },
71}
72
73#[derive(Debug, thiserror::Error, deno_error::JsError)]
74pub enum DecryptError {
75  #[class(inherit)]
76  #[error(transparent)]
77  General(
78    #[from]
79    #[inherit]
80    SharedError,
81  ),
82  #[class(generic)]
83  #[error(transparent)]
84  Pkcs1(#[from] rsa::pkcs1::Error),
85  #[class("DOMExceptionOperationError")]
86  #[error("Decryption failed")]
87  Failed,
88  #[class(type)]
89  #[error("invalid length")]
90  InvalidLength,
91  #[class(type)]
92  #[error("invalid counter length. Currently supported 32/64/128 bits")]
93  InvalidCounterLength,
94  #[class(type)]
95  #[error("tag length not equal to 128")]
96  InvalidTagLength,
97  #[class("DOMExceptionOperationError")]
98  #[error("invalid key or iv")]
99  InvalidKeyOrIv,
100  #[class("DOMExceptionOperationError")]
101  #[error("tried to decrypt too much data")]
102  TooMuchData,
103  #[class(type)]
104  #[error("iv length not equal to 12 or 16")]
105  InvalidIvLength,
106  #[class("DOMExceptionOperationError")]
107  #[error("{0}")]
108  Rsa(rsa::Error),
109}
110
111#[op2(async)]
112#[serde]
113pub async fn op_crypto_decrypt(
114  #[serde] opts: DecryptOptions,
115  #[buffer] data: JsBuffer,
116) -> Result<ToJsBuffer, DecryptError> {
117  let key = opts.key;
118  let fun = move || match opts.algorithm {
119    DecryptAlgorithm::RsaOaep { hash, label } => {
120      decrypt_rsa_oaep(key, hash, label, &data)
121    }
122    DecryptAlgorithm::AesCbc { iv, length } => {
123      decrypt_aes_cbc(key, length, iv, &data)
124    }
125    DecryptAlgorithm::AesCtr {
126      counter,
127      ctr_length,
128      key_length,
129    } => decrypt_aes_ctr(key, key_length, &counter, ctr_length, &data),
130    DecryptAlgorithm::AesGcm {
131      iv,
132      additional_data,
133      length,
134      tag_length,
135    } => decrypt_aes_gcm(key, length, tag_length, iv, additional_data, &data),
136  };
137  let buf = spawn_blocking(fun).await.unwrap()?;
138  Ok(buf.into())
139}
140
141fn decrypt_rsa_oaep(
142  key: V8RawKeyData,
143  hash: ShaHash,
144  label: Vec<u8>,
145  data: &[u8],
146) -> Result<Vec<u8>, DecryptError> {
147  let key = key.as_rsa_private_key()?;
148
149  let private_key = rsa::RsaPrivateKey::from_pkcs1_der(key)?;
150  let label = Some(String::from_utf8_lossy(&label).to_string());
151
152  let padding = match hash {
153    ShaHash::Sha1 => rsa::Oaep {
154      digest: Box::<Sha1>::default(),
155      mgf_digest: Box::<Sha1>::default(),
156      label,
157    },
158    ShaHash::Sha256 => rsa::Oaep {
159      digest: Box::<Sha256>::default(),
160      mgf_digest: Box::<Sha256>::default(),
161      label,
162    },
163    ShaHash::Sha384 => rsa::Oaep {
164      digest: Box::<Sha384>::default(),
165      mgf_digest: Box::<Sha384>::default(),
166      label,
167    },
168    ShaHash::Sha512 => rsa::Oaep {
169      digest: Box::<Sha512>::default(),
170      mgf_digest: Box::<Sha512>::default(),
171      label,
172    },
173  };
174
175  private_key
176    .decrypt(padding, data)
177    .map_err(DecryptError::Rsa)
178}
179
180fn decrypt_aes_cbc(
181  key: V8RawKeyData,
182  length: usize,
183  iv: Vec<u8>,
184  data: &[u8],
185) -> Result<Vec<u8>, DecryptError> {
186  let key = key.as_secret_key()?;
187
188  // 2.
189  let plaintext = match length {
190    128 => {
191      // Section 10.3 Step 2 of RFC 2315 https://www.rfc-editor.org/rfc/rfc2315
192      type Aes128CbcDec = cbc::Decryptor<aes::Aes128>;
193      let cipher = Aes128CbcDec::new_from_slices(key, &iv)
194        .map_err(|_| DecryptError::InvalidKeyOrIv)?;
195
196      cipher
197        .decrypt_padded_vec_mut::<Pkcs7>(data)
198        .map_err(|_| DecryptError::Failed)?
199    }
200    192 => {
201      // Section 10.3 Step 2 of RFC 2315 https://www.rfc-editor.org/rfc/rfc2315
202      type Aes192CbcDec = cbc::Decryptor<aes::Aes192>;
203      let cipher = Aes192CbcDec::new_from_slices(key, &iv)
204        .map_err(|_| DecryptError::InvalidKeyOrIv)?;
205
206      cipher
207        .decrypt_padded_vec_mut::<Pkcs7>(data)
208        .map_err(|_| DecryptError::Failed)?
209    }
210    256 => {
211      // Section 10.3 Step 2 of RFC 2315 https://www.rfc-editor.org/rfc/rfc2315
212      type Aes256CbcDec = cbc::Decryptor<aes::Aes256>;
213      let cipher = Aes256CbcDec::new_from_slices(key, &iv)
214        .map_err(|_| DecryptError::InvalidKeyOrIv)?;
215
216      cipher
217        .decrypt_padded_vec_mut::<Pkcs7>(data)
218        .map_err(|_| DecryptError::Failed)?
219    }
220    _ => unreachable!(),
221  };
222
223  // 6.
224  Ok(plaintext)
225}
226
227fn decrypt_aes_ctr_gen<B>(
228  key: &[u8],
229  counter: &[u8],
230  data: &[u8],
231) -> Result<Vec<u8>, DecryptError>
232where
233  B: KeyIvInit + StreamCipher,
234{
235  let mut cipher = B::new(key.into(), counter.into());
236
237  let mut plaintext = data.to_vec();
238  cipher
239    .try_apply_keystream(&mut plaintext)
240    .map_err(|_| DecryptError::TooMuchData)?;
241
242  Ok(plaintext)
243}
244
245fn decrypt_aes_gcm_gen<N: ArrayLength<u8>>(
246  key: &[u8],
247  tag: &aes_gcm::Tag,
248  nonce: &[u8],
249  length: usize,
250  additional_data: Vec<u8>,
251  plaintext: &mut [u8],
252) -> Result<(), DecryptError> {
253  let nonce = Nonce::from_slice(nonce);
254  match length {
255    128 => {
256      let cipher = aes_gcm::AesGcm::<Aes128, N>::new_from_slice(key)
257        .map_err(|_| DecryptError::Failed)?;
258      cipher
259        .decrypt_in_place_detached(
260          nonce,
261          additional_data.as_slice(),
262          plaintext,
263          tag,
264        )
265        .map_err(|_| DecryptError::Failed)?
266    }
267    192 => {
268      let cipher = aes_gcm::AesGcm::<Aes192, N>::new_from_slice(key)
269        .map_err(|_| DecryptError::Failed)?;
270      cipher
271        .decrypt_in_place_detached(
272          nonce,
273          additional_data.as_slice(),
274          plaintext,
275          tag,
276        )
277        .map_err(|_| DecryptError::Failed)?
278    }
279    256 => {
280      let cipher = aes_gcm::AesGcm::<Aes256, N>::new_from_slice(key)
281        .map_err(|_| DecryptError::Failed)?;
282      cipher
283        .decrypt_in_place_detached(
284          nonce,
285          additional_data.as_slice(),
286          plaintext,
287          tag,
288        )
289        .map_err(|_| DecryptError::Failed)?
290    }
291    _ => return Err(DecryptError::InvalidLength),
292  };
293
294  Ok(())
295}
296
297fn decrypt_aes_ctr(
298  key: V8RawKeyData,
299  key_length: usize,
300  counter: &[u8],
301  ctr_length: usize,
302  data: &[u8],
303) -> Result<Vec<u8>, DecryptError> {
304  let key = key.as_secret_key()?;
305
306  match ctr_length {
307    32 => match key_length {
308      128 => decrypt_aes_ctr_gen::<Ctr32BE<aes::Aes128>>(key, counter, data),
309      192 => decrypt_aes_ctr_gen::<Ctr32BE<aes::Aes192>>(key, counter, data),
310      256 => decrypt_aes_ctr_gen::<Ctr32BE<aes::Aes256>>(key, counter, data),
311      _ => Err(DecryptError::InvalidLength),
312    },
313    64 => match key_length {
314      128 => decrypt_aes_ctr_gen::<Ctr64BE<aes::Aes128>>(key, counter, data),
315      192 => decrypt_aes_ctr_gen::<Ctr64BE<aes::Aes192>>(key, counter, data),
316      256 => decrypt_aes_ctr_gen::<Ctr64BE<aes::Aes256>>(key, counter, data),
317      _ => Err(DecryptError::InvalidLength),
318    },
319    128 => match key_length {
320      128 => decrypt_aes_ctr_gen::<Ctr128BE<aes::Aes128>>(key, counter, data),
321      192 => decrypt_aes_ctr_gen::<Ctr128BE<aes::Aes192>>(key, counter, data),
322      256 => decrypt_aes_ctr_gen::<Ctr128BE<aes::Aes256>>(key, counter, data),
323      _ => Err(DecryptError::InvalidLength),
324    },
325    _ => Err(DecryptError::InvalidCounterLength),
326  }
327}
328
329fn decrypt_aes_gcm(
330  key: V8RawKeyData,
331  length: usize,
332  tag_length: usize,
333  iv: Vec<u8>,
334  additional_data: Option<Vec<u8>>,
335  data: &[u8],
336) -> Result<Vec<u8>, DecryptError> {
337  let key = key.as_secret_key()?;
338  let additional_data = additional_data.unwrap_or_default();
339
340  // The `aes_gcm` crate only supports 128 bits tag length.
341  //
342  // Note that encryption won't fail, it instead truncates the tag
343  // to the specified tag length as specified in the spec.
344  if tag_length != 128 {
345    return Err(DecryptError::InvalidTagLength);
346  }
347
348  let sep = data.len() - (tag_length / 8);
349  let tag = &data[sep..];
350
351  // The actual ciphertext, called plaintext because it is reused in place.
352  let mut plaintext = data[..sep].to_vec();
353
354  // Fixed 96-bit or 128-bit nonce
355  match iv.len() {
356    12 => decrypt_aes_gcm_gen::<U12>(
357      key,
358      tag.into(),
359      &iv,
360      length,
361      additional_data,
362      &mut plaintext,
363    )?,
364    16 => decrypt_aes_gcm_gen::<U16>(
365      key,
366      tag.into(),
367      &iv,
368      length,
369      additional_data,
370      &mut plaintext,
371    )?,
372    _ => return Err(DecryptError::InvalidIvLength),
373  }
374
375  Ok(plaintext)
376}