mirror of
https://github.com/denoland/deno.git
synced 2025-01-21 21:50:00 -05:00
refactor(ext/crypto): clean up encrypt rust code (#13094)
This commit is contained in:
parent
5a3ded6611
commit
f0e1a6b84c
4 changed files with 150 additions and 109 deletions
|
@ -3136,7 +3136,7 @@
|
||||||
|
|
||||||
// 3-5.
|
// 3-5.
|
||||||
const hashAlgorithm = key[_algorithm].hash.name;
|
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,
|
key: keyData,
|
||||||
algorithm: "RSA-OAEP",
|
algorithm: "RSA-OAEP",
|
||||||
hash: hashAlgorithm,
|
hash: hashAlgorithm,
|
||||||
|
@ -3158,7 +3158,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2.
|
// 2.
|
||||||
const cipherText = await core.opAsync("op_crypto_encrypt_key", {
|
const cipherText = await core.opAsync("op_crypto_encrypt", {
|
||||||
key: keyData,
|
key: keyData,
|
||||||
algorithm: "AES-CBC",
|
algorithm: "AES-CBC",
|
||||||
length: key[_algorithm].length,
|
length: key[_algorithm].length,
|
||||||
|
|
138
ext/crypto/encrypt.rs
Normal file
138
ext/crypto/encrypt.rs
Normal file
|
@ -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<u8>,
|
||||||
|
},
|
||||||
|
#[serde(rename = "AES-CBC", rename_all = "camelCase")]
|
||||||
|
AesCbc {
|
||||||
|
#[serde(with = "serde_bytes")]
|
||||||
|
iv: Vec<u8>,
|
||||||
|
length: usize,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
pub async fn op_crypto_encrypt(
|
||||||
|
_state: Rc<RefCell<OpState>>,
|
||||||
|
opts: EncryptOptions,
|
||||||
|
data: ZeroCopyBuf,
|
||||||
|
) -> Result<ZeroCopyBuf, AnyError> {
|
||||||
|
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<u8>,
|
||||||
|
data: &[u8],
|
||||||
|
) -> Result<Vec<u8>, 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<u8>,
|
||||||
|
data: &[u8],
|
||||||
|
) -> Result<Vec<u8>, 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<aes::Aes128, block_modes::block_padding::Pkcs7>;
|
||||||
|
|
||||||
|
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<aes::Aes192, block_modes::block_padding::Pkcs7>;
|
||||||
|
|
||||||
|
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<aes::Aes256, block_modes::block_padding::Pkcs7>;
|
||||||
|
|
||||||
|
let cipher = Aes256Cbc::new_from_slices(key, &iv)?;
|
||||||
|
cipher.encrypt_vec(data)
|
||||||
|
}
|
||||||
|
_ => return Err(type_error("invalid length")),
|
||||||
|
};
|
||||||
|
Ok(ciphertext)
|
||||||
|
}
|
|
@ -54,12 +54,14 @@ use std::path::PathBuf;
|
||||||
|
|
||||||
pub use rand; // Re-export rand
|
pub use rand; // Re-export rand
|
||||||
|
|
||||||
|
mod encrypt;
|
||||||
mod export_key;
|
mod export_key;
|
||||||
mod generate_key;
|
mod generate_key;
|
||||||
mod import_key;
|
mod import_key;
|
||||||
mod key;
|
mod key;
|
||||||
mod shared;
|
mod shared;
|
||||||
|
|
||||||
|
pub use crate::encrypt::op_crypto_encrypt;
|
||||||
pub use crate::export_key::op_crypto_export_key;
|
pub use crate::export_key::op_crypto_export_key;
|
||||||
pub use crate::generate_key::op_crypto_generate_key;
|
pub use crate::generate_key::op_crypto_generate_key;
|
||||||
pub use crate::import_key::op_crypto_import_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::CryptoHash;
|
||||||
use crate::key::CryptoNamedCurve;
|
use crate::key::CryptoNamedCurve;
|
||||||
use crate::key::HkdfOutput;
|
use crate::key::HkdfOutput;
|
||||||
|
|
||||||
use crate::shared::ID_MFG1;
|
use crate::shared::ID_MFG1;
|
||||||
use crate::shared::ID_P_SPECIFIED;
|
use crate::shared::ID_P_SPECIFIED;
|
||||||
use crate::shared::ID_SHA1_OID;
|
use crate::shared::ID_SHA1_OID;
|
||||||
|
@ -96,7 +97,7 @@ pub fn init(maybe_seed: Option<u64>) -> Extension {
|
||||||
("op_crypto_derive_bits", op_async(op_crypto_derive_bits)),
|
("op_crypto_derive_bits", op_async(op_crypto_derive_bits)),
|
||||||
("op_crypto_import_key", op_sync(op_crypto_import_key)),
|
("op_crypto_import_key", op_sync(op_crypto_import_key)),
|
||||||
("op_crypto_export_key", op_sync(op_crypto_export_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_decrypt_key", op_async(op_crypto_decrypt_key)),
|
||||||
("op_crypto_subtle_digest", op_async(op_crypto_subtle_digest)),
|
("op_crypto_subtle_digest", op_async(op_crypto_subtle_digest)),
|
||||||
("op_crypto_random_uuid", op_sync(op_crypto_random_uuid)),
|
("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<CryptoHash>,
|
|
||||||
label: Option<ZeroCopyBuf>,
|
|
||||||
// AES-CBC
|
|
||||||
iv: Option<ZeroCopyBuf>,
|
|
||||||
length: Option<usize>,
|
|
||||||
}
|
|
||||||
|
|
||||||
fn read_rsa_public_key(key_data: KeyData) -> Result<RsaPublicKey, AnyError> {
|
fn read_rsa_public_key(key_data: KeyData) -> Result<RsaPublicKey, AnyError> {
|
||||||
let public_key = match key_data.r#type {
|
let public_key = match key_data.r#type {
|
||||||
KeyType::Private => {
|
KeyType::Private => {
|
||||||
|
@ -585,98 +573,6 @@ fn read_rsa_public_key(key_data: KeyData) -> Result<RsaPublicKey, AnyError> {
|
||||||
Ok(public_key)
|
Ok(public_key)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn op_crypto_encrypt_key(
|
|
||||||
_state: Rc<RefCell<OpState>>,
|
|
||||||
args: EncryptArg,
|
|
||||||
zero_copy: ZeroCopyBuf,
|
|
||||||
) -> Result<ZeroCopyBuf, AnyError> {
|
|
||||||
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<aes::Aes128, block_modes::block_padding::Pkcs7>;
|
|
||||||
|
|
||||||
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<aes::Aes192, block_modes::block_padding::Pkcs7>;
|
|
||||||
|
|
||||||
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<aes::Aes256, block_modes::block_padding::Pkcs7>;
|
|
||||||
|
|
||||||
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
|
// The parameters field associated with OID id-RSASSA-PSS
|
||||||
// Defined in RFC 3447, section A.2.3
|
// Defined in RFC 3447, section A.2.3
|
||||||
//
|
//
|
||||||
|
|
|
@ -90,6 +90,13 @@ impl RawKeyData {
|
||||||
_ => Err(type_error("expected private key")),
|
_ => 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<Cow<'static, str>>) -> AnyError {
|
pub fn data_error(msg: impl Into<Cow<'static, str>>) -> AnyError {
|
||||||
|
|
Loading…
Add table
Reference in a new issue