diff --git a/ext/crypto/00_crypto.js b/ext/crypto/00_crypto.js index 3e0af0b3c0..c8c7d9810a 100644 --- a/ext/crypto/00_crypto.js +++ b/ext/crypto/00_crypto.js @@ -3136,7 +3136,7 @@ // 3-5. const hashAlgorithm = key[_algorithm].hash.name; - const cipherText = await core.opAsync("op_crypto_encrypt_key", { + const cipherText = await core.opAsync("op_crypto_encrypt", { key: keyData, algorithm: "RSA-OAEP", hash: hashAlgorithm, @@ -3158,7 +3158,7 @@ } // 2. - const cipherText = await core.opAsync("op_crypto_encrypt_key", { + const cipherText = await core.opAsync("op_crypto_encrypt", { key: keyData, algorithm: "AES-CBC", length: key[_algorithm].length, diff --git a/ext/crypto/encrypt.rs b/ext/crypto/encrypt.rs new file mode 100644 index 0000000000..87e3fd2e08 --- /dev/null +++ b/ext/crypto/encrypt.rs @@ -0,0 +1,138 @@ +use std::cell::RefCell; +use std::rc::Rc; + +use crate::shared::*; +use block_modes::BlockMode; +use deno_core::error::type_error; +use deno_core::error::AnyError; +use deno_core::OpState; +use deno_core::ZeroCopyBuf; +use rand::rngs::OsRng; +use rsa::pkcs1::FromRsaPublicKey; +use rsa::PaddingScheme; +use rsa::PublicKey; +use serde::Deserialize; +use sha1::Digest; +use sha1::Sha1; +use sha2::Sha256; +use sha2::Sha384; +use sha2::Sha512; + +#[derive(Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct EncryptOptions { + key: RawKeyData, + #[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, + }, +} +pub async fn op_crypto_encrypt( + _state: Rc>, + opts: EncryptOptions, + data: ZeroCopyBuf, +) -> 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) + } + }; + let buf = tokio::task::spawn_blocking(fun).await.unwrap()?; + Ok(buf.into()) +} + +fn encrypt_rsa_oaep( + key: RawKeyData, + hash: ShaHash, + label: Vec, + data: &[u8], +) -> Result, deno_core::anyhow::Error> { + 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: RawKeyData, + length: usize, + iv: Vec, + data: &[u8], +) -> Result, deno_core::anyhow::Error> { + 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 Aes128Cbc = + block_modes::Cbc; + + let cipher = Aes128Cbc::new_from_slices(key, &iv)?; + cipher.encrypt_vec(data) + } + 192 => { + // Section 10.3 Step 2 of RFC 2315 https://www.rfc-editor.org/rfc/rfc2315 + type Aes192Cbc = + block_modes::Cbc; + + let cipher = Aes192Cbc::new_from_slices(key, &iv)?; + cipher.encrypt_vec(data) + } + 256 => { + // Section 10.3 Step 2 of RFC 2315 https://www.rfc-editor.org/rfc/rfc2315 + type Aes256Cbc = + block_modes::Cbc; + + let cipher = Aes256Cbc::new_from_slices(key, &iv)?; + cipher.encrypt_vec(data) + } + _ => return Err(type_error("invalid length")), + }; + Ok(ciphertext) +} diff --git a/ext/crypto/lib.rs b/ext/crypto/lib.rs index a15f2d986c..bb7ad5f326 100644 --- a/ext/crypto/lib.rs +++ b/ext/crypto/lib.rs @@ -54,12 +54,14 @@ use std::path::PathBuf; pub use rand; // Re-export rand +mod encrypt; mod export_key; mod generate_key; mod import_key; mod key; mod shared; +pub use crate::encrypt::op_crypto_encrypt; pub use crate::export_key::op_crypto_export_key; pub use crate::generate_key::op_crypto_generate_key; pub use crate::import_key::op_crypto_import_key; @@ -67,7 +69,6 @@ use crate::key::Algorithm; use crate::key::CryptoHash; use crate::key::CryptoNamedCurve; use crate::key::HkdfOutput; - use crate::shared::ID_MFG1; use crate::shared::ID_P_SPECIFIED; use crate::shared::ID_SHA1_OID; @@ -96,7 +97,7 @@ pub fn init(maybe_seed: Option) -> Extension { ("op_crypto_derive_bits", op_async(op_crypto_derive_bits)), ("op_crypto_import_key", op_sync(op_crypto_import_key)), ("op_crypto_export_key", op_sync(op_crypto_export_key)), - ("op_crypto_encrypt_key", op_async(op_crypto_encrypt_key)), + ("op_crypto_encrypt", op_async(op_crypto_encrypt)), ("op_crypto_decrypt_key", op_async(op_crypto_decrypt_key)), ("op_crypto_subtle_digest", op_async(op_crypto_subtle_digest)), ("op_crypto_random_uuid", op_sync(op_crypto_random_uuid)), @@ -561,19 +562,6 @@ pub async fn op_crypto_derive_bits( } } -#[derive(Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct EncryptArg { - key: KeyData, - algorithm: Algorithm, - // RSA-OAEP - hash: Option, - label: Option, - // AES-CBC - iv: Option, - length: Option, -} - fn read_rsa_public_key(key_data: KeyData) -> Result { let public_key = match key_data.r#type { KeyType::Private => { @@ -585,98 +573,6 @@ fn read_rsa_public_key(key_data: KeyData) -> Result { Ok(public_key) } -pub async fn op_crypto_encrypt_key( - _state: Rc>, - args: EncryptArg, - zero_copy: ZeroCopyBuf, -) -> Result { - let data = &*zero_copy; - let algorithm = args.algorithm; - - match algorithm { - Algorithm::RsaOaep => { - let public_key = read_rsa_public_key(args.key)?; - let label = args.label.map(|l| String::from_utf8_lossy(&*l).to_string()); - let mut rng = OsRng; - let padding = match args - .hash - .ok_or_else(|| type_error("Missing argument hash".to_string()))? - { - CryptoHash::Sha1 => PaddingScheme::OAEP { - digest: Box::new(Sha1::new()), - mgf_digest: Box::new(Sha1::new()), - label, - }, - CryptoHash::Sha256 => PaddingScheme::OAEP { - digest: Box::new(Sha256::new()), - mgf_digest: Box::new(Sha256::new()), - label, - }, - CryptoHash::Sha384 => PaddingScheme::OAEP { - digest: Box::new(Sha384::new()), - mgf_digest: Box::new(Sha384::new()), - label, - }, - CryptoHash::Sha512 => PaddingScheme::OAEP { - digest: Box::new(Sha512::new()), - mgf_digest: Box::new(Sha512::new()), - label, - }, - }; - - Ok( - public_key - .encrypt(&mut rng, padding, data) - .map_err(|e| { - custom_error("DOMExceptionOperationError", e.to_string()) - })? - .into(), - ) - } - Algorithm::AesCbc => { - let key = &*args.key.data; - let length = args - .length - .ok_or_else(|| type_error("Missing argument length".to_string()))?; - let iv = args - .iv - .ok_or_else(|| type_error("Missing argument iv".to_string()))?; - - // 2-3. - let ciphertext = match length { - 128 => { - // Section 10.3 Step 2 of RFC 2315 https://www.rfc-editor.org/rfc/rfc2315 - type Aes128Cbc = - block_modes::Cbc; - - let cipher = Aes128Cbc::new_from_slices(key, &iv)?; - cipher.encrypt_vec(data) - } - 192 => { - // Section 10.3 Step 2 of RFC 2315 https://www.rfc-editor.org/rfc/rfc2315 - type Aes192Cbc = - block_modes::Cbc; - - let cipher = Aes192Cbc::new_from_slices(key, &iv)?; - cipher.encrypt_vec(data) - } - 256 => { - // Section 10.3 Step 2 of RFC 2315 https://www.rfc-editor.org/rfc/rfc2315 - type Aes256Cbc = - block_modes::Cbc; - - let cipher = Aes256Cbc::new_from_slices(key, &iv)?; - cipher.encrypt_vec(data) - } - _ => unreachable!(), - }; - - Ok(ciphertext.into()) - } - _ => Err(type_error("Unsupported algorithm".to_string())), - } -} - // The parameters field associated with OID id-RSASSA-PSS // Defined in RFC 3447, section A.2.3 // diff --git a/ext/crypto/shared.rs b/ext/crypto/shared.rs index 0b70db24ea..1a0703b264 100644 --- a/ext/crypto/shared.rs +++ b/ext/crypto/shared.rs @@ -90,6 +90,13 @@ impl RawKeyData { _ => Err(type_error("expected private key")), } } + + pub fn as_secret_key(&self) -> Result<&[u8], AnyError> { + match self { + RawKeyData::Secret(data) => Ok(data), + _ => Err(type_error("expected secret key")), + } + } } pub fn data_error(msg: impl Into>) -> AnyError {