// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. use aes::cipher::block_padding::Pkcs7; use aes::cipher::BlockEncryptMut; use aes::cipher::KeyIvInit; use aes::cipher::StreamCipher; use aes_gcm::aead::generic_array::typenum::U12; use aes_gcm::aead::generic_array::typenum::U16; use aes_gcm::aead::generic_array::ArrayLength; use aes_gcm::aes::Aes128; use aes_gcm::aes::Aes192; use aes_gcm::aes::Aes256; use aes_gcm::AeadInPlace; use aes_gcm::KeyInit; use aes_gcm::Nonce; use ctr::Ctr128BE; use ctr::Ctr32BE; use ctr::Ctr64BE; use deno_core::error::type_error; use deno_core::error::AnyError; use deno_core::op; use deno_core::task::spawn_blocking; use deno_core::JsBuffer; use deno_core::ToJsBuffer; use rand::rngs::OsRng; use rsa::pkcs1::DecodeRsaPublicKey; use rsa::PaddingScheme; use rsa::PublicKey; use serde::Deserialize; use sha1::Digest; use sha1::Sha1; use sha2::Sha256; use sha2::Sha384; use sha2::Sha512; use crate::shared::*; #[derive(Deserialize)] #[serde(rename_all = "camelCase")] pub struct EncryptOptions { key: V8RawKeyData, #[serde(flatten)] algorithm: EncryptAlgorithm, } #[derive(Deserialize)] #[serde(rename_all = "camelCase", tag = "algorithm")] pub enum EncryptAlgorithm { #[serde(rename = "RSA-OAEP")] RsaOaep { hash: ShaHash, #[serde(with = "serde_bytes")] label: Vec, }, #[serde(rename = "AES-CBC", rename_all = "camelCase")] AesCbc { #[serde(with = "serde_bytes")] iv: Vec, length: usize, }, #[serde(rename = "AES-GCM", rename_all = "camelCase")] AesGcm { #[serde(with = "serde_bytes")] iv: Vec, #[serde(with = "serde_bytes")] additional_data: Option>, length: usize, tag_length: usize, }, #[serde(rename = "AES-CTR", rename_all = "camelCase")] AesCtr { #[serde(with = "serde_bytes")] counter: Vec, ctr_length: usize, key_length: usize, }, } #[op] pub async fn op_crypto_encrypt( opts: EncryptOptions, data: JsBuffer, ) -> Result { let key = opts.key; let fun = move || match opts.algorithm { EncryptAlgorithm::RsaOaep { hash, label } => { encrypt_rsa_oaep(key, hash, label, &data) } EncryptAlgorithm::AesCbc { iv, length } => { encrypt_aes_cbc(key, length, iv, &data) } EncryptAlgorithm::AesGcm { iv, additional_data, length, tag_length, } => encrypt_aes_gcm(key, length, tag_length, iv, additional_data, &data), EncryptAlgorithm::AesCtr { counter, ctr_length, key_length, } => encrypt_aes_ctr(key, key_length, &counter, ctr_length, &data), }; let buf = spawn_blocking(fun).await.unwrap()?; Ok(buf.into()) } fn encrypt_rsa_oaep( key: V8RawKeyData, hash: ShaHash, label: Vec, data: &[u8], ) -> Result, AnyError> { let label = String::from_utf8_lossy(&label).to_string(); let public_key = key.as_rsa_public_key()?; let public_key = rsa::RsaPublicKey::from_pkcs1_der(&public_key) .map_err(|_| operation_error("failed to decode public key"))?; let mut rng = OsRng; let padding = match hash { ShaHash::Sha1 => PaddingScheme::OAEP { digest: Box::new(Sha1::new()), mgf_digest: Box::new(Sha1::new()), label: Some(label), }, ShaHash::Sha256 => PaddingScheme::OAEP { digest: Box::new(Sha256::new()), mgf_digest: Box::new(Sha256::new()), label: Some(label), }, ShaHash::Sha384 => PaddingScheme::OAEP { digest: Box::new(Sha384::new()), mgf_digest: Box::new(Sha384::new()), label: Some(label), }, ShaHash::Sha512 => PaddingScheme::OAEP { digest: Box::new(Sha512::new()), mgf_digest: Box::new(Sha512::new()), label: Some(label), }, }; let encrypted = public_key .encrypt(&mut rng, padding, data) .map_err(|_| operation_error("Encryption failed"))?; Ok(encrypted) } fn encrypt_aes_cbc( key: V8RawKeyData, length: usize, iv: Vec, data: &[u8], ) -> Result, AnyError> { let key = key.as_secret_key()?; let ciphertext = match length { 128 => { // Section 10.3 Step 2 of RFC 2315 https://www.rfc-editor.org/rfc/rfc2315 type Aes128CbcEnc = cbc::Encryptor; let cipher = Aes128CbcEnc::new_from_slices(key, &iv) .map_err(|_| operation_error("invalid key or iv".to_string()))?; cipher.encrypt_padded_vec_mut::(data) } 192 => { // Section 10.3 Step 2 of RFC 2315 https://www.rfc-editor.org/rfc/rfc2315 type Aes192CbcEnc = cbc::Encryptor; let cipher = Aes192CbcEnc::new_from_slices(key, &iv) .map_err(|_| operation_error("invalid key or iv".to_string()))?; cipher.encrypt_padded_vec_mut::(data) } 256 => { // Section 10.3 Step 2 of RFC 2315 https://www.rfc-editor.org/rfc/rfc2315 type Aes256CbcEnc = cbc::Encryptor; let cipher = Aes256CbcEnc::new_from_slices(key, &iv) .map_err(|_| operation_error("invalid key or iv".to_string()))?; cipher.encrypt_padded_vec_mut::(data) } _ => return Err(type_error("invalid length")), }; Ok(ciphertext) } fn encrypt_aes_gcm_general>( key: &[u8], iv: Vec, length: usize, ciphertext: &mut [u8], additional_data: Vec, ) -> Result { let nonce = Nonce::::from_slice(&iv); let tag = match length { 128 => { let cipher = aes_gcm::AesGcm::::new_from_slice(key) .map_err(|_| operation_error("Encryption failed"))?; cipher .encrypt_in_place_detached(nonce, &additional_data, ciphertext) .map_err(|_| operation_error("Encryption failed"))? } 192 => { let cipher = aes_gcm::AesGcm::::new_from_slice(key) .map_err(|_| operation_error("Encryption failed"))?; cipher .encrypt_in_place_detached(nonce, &additional_data, ciphertext) .map_err(|_| operation_error("Encryption failed"))? } 256 => { let cipher = aes_gcm::AesGcm::::new_from_slice(key) .map_err(|_| operation_error("Encryption failed"))?; cipher .encrypt_in_place_detached(nonce, &additional_data, ciphertext) .map_err(|_| operation_error("Encryption failed"))? } _ => return Err(type_error("invalid length")), }; Ok(tag) } fn encrypt_aes_gcm( key: V8RawKeyData, length: usize, tag_length: usize, iv: Vec, additional_data: Option>, data: &[u8], ) -> Result, AnyError> { let key = key.as_secret_key()?; let additional_data = additional_data.unwrap_or_default(); let mut ciphertext = data.to_vec(); // Fixed 96-bit OR 128-bit nonce let tag = match iv.len() { 12 => encrypt_aes_gcm_general::( key, iv, length, &mut ciphertext, additional_data, )?, 16 => encrypt_aes_gcm_general::( key, iv, length, &mut ciphertext, additional_data, )?, _ => return Err(type_error("iv length not equal to 12 or 16")), }; // Truncated tag to the specified tag length. // `tag` is fixed to be 16 bytes long and (tag_length / 8) is always <= 16 let tag = &tag[..(tag_length / 8)]; // C | T ciphertext.extend_from_slice(tag); Ok(ciphertext) } fn encrypt_aes_ctr_gen( key: &[u8], counter: &[u8], data: &[u8], ) -> Result, AnyError> where B: KeyIvInit + StreamCipher, { let mut cipher = B::new(key.into(), counter.into()); let mut ciphertext = data.to_vec(); cipher .try_apply_keystream(&mut ciphertext) .map_err(|_| operation_error("tried to encrypt too much data"))?; Ok(ciphertext) } fn encrypt_aes_ctr( key: V8RawKeyData, key_length: usize, counter: &[u8], ctr_length: usize, data: &[u8], ) -> Result, AnyError> { let key = key.as_secret_key()?; match ctr_length { 32 => match key_length { 128 => encrypt_aes_ctr_gen::>(key, counter, data), 192 => encrypt_aes_ctr_gen::>(key, counter, data), 256 => encrypt_aes_ctr_gen::>(key, counter, data), _ => Err(type_error("invalid length")), }, 64 => match key_length { 128 => encrypt_aes_ctr_gen::>(key, counter, data), 192 => encrypt_aes_ctr_gen::>(key, counter, data), 256 => encrypt_aes_ctr_gen::>(key, counter, data), _ => Err(type_error("invalid length")), }, 128 => match key_length { 128 => encrypt_aes_ctr_gen::>(key, counter, data), 192 => encrypt_aes_ctr_gen::>(key, counter, data), 256 => encrypt_aes_ctr_gen::>(key, counter, data), _ => Err(type_error("invalid length")), }, _ => Err(type_error( "invalid counter length. Currently supported 32/64/128 bits", )), } }