Skip to content

Commit

Permalink
refactor(ext/crypto): use concrete error types (#26167)
Browse files Browse the repository at this point in the history
  • Loading branch information
crowlKats authored and bartlomieju committed Oct 25, 2024
1 parent ecf4cb3 commit 1bc3694
Show file tree
Hide file tree
Showing 13 changed files with 784 additions and 458 deletions.
2 changes: 2 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions ext/crypto/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -41,5 +41,7 @@ sha1.workspace = true
sha2.workspace = true
signature.workspace = true
spki.workspace = true
thiserror.workspace = true
tokio.workspace = true
uuid.workspace = true
x25519-dalek = "2.0.0"
124 changes: 61 additions & 63 deletions ext/crypto/decrypt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,6 @@ use ctr::cipher::StreamCipher;
use ctr::Ctr128BE;
use ctr::Ctr32BE;
use ctr::Ctr64BE;
use deno_core::error::custom_error;
use deno_core::error::type_error;
use deno_core::error::AnyError;
use deno_core::op2;
use deno_core::unsync::spawn_blocking;
use deno_core::JsBuffer;
Expand Down Expand Up @@ -73,12 +70,36 @@ pub enum DecryptAlgorithm {
},
}

#[derive(Debug, thiserror::Error)]
pub enum DecryptError {
#[error(transparent)]
General(#[from] SharedError),
#[error(transparent)]
Pkcs1(#[from] rsa::pkcs1::Error),
#[error("Decryption failed")]
Failed,
#[error("invalid length")]
InvalidLength,
#[error("invalid counter length. Currently supported 32/64/128 bits")]
InvalidCounterLength,
#[error("tag length not equal to 128")]
InvalidTagLength,
#[error("invalid key or iv")]
InvalidKeyOrIv,
#[error("tried to decrypt too much data")]
TooMuchData,
#[error("iv length not equal to 12 or 16")]
InvalidIvLength,
#[error("{0}")]
Rsa(rsa::Error),
}

#[op2(async)]
#[serde]
pub async fn op_crypto_decrypt(
#[serde] opts: DecryptOptions,
#[buffer] data: JsBuffer,
) -> Result<ToJsBuffer, AnyError> {
) -> Result<ToJsBuffer, DecryptError> {
let key = opts.key;
let fun = move || match opts.algorithm {
DecryptAlgorithm::RsaOaep { hash, label } => {
Expand Down Expand Up @@ -108,7 +129,7 @@ fn decrypt_rsa_oaep(
hash: ShaHash,
label: Vec<u8>,
data: &[u8],
) -> Result<Vec<u8>, deno_core::anyhow::Error> {
) -> Result<Vec<u8>, DecryptError> {
let key = key.as_rsa_private_key()?;

let private_key = rsa::RsaPrivateKey::from_pkcs1_der(key)?;
Expand Down Expand Up @@ -139,69 +160,48 @@ fn decrypt_rsa_oaep(

private_key
.decrypt(padding, data)
.map_err(|e| custom_error("DOMExceptionOperationError", e.to_string()))
.map_err(DecryptError::Rsa)
}

fn decrypt_aes_cbc(
key: V8RawKeyData,
length: usize,
iv: Vec<u8>,
data: &[u8],
) -> Result<Vec<u8>, deno_core::anyhow::Error> {
) -> Result<Vec<u8>, DecryptError> {
let key = key.as_secret_key()?;

// 2.
let plaintext = match length {
128 => {
// Section 10.3 Step 2 of RFC 2315 https://www.rfc-editor.org/rfc/rfc2315
type Aes128CbcDec = cbc::Decryptor<aes::Aes128>;
let cipher = Aes128CbcDec::new_from_slices(key, &iv).map_err(|_| {
custom_error(
"DOMExceptionOperationError",
"Invalid key or iv".to_string(),
)
})?;
let cipher = Aes128CbcDec::new_from_slices(key, &iv)
.map_err(|_| DecryptError::InvalidKeyOrIv)?;

cipher.decrypt_padded_vec_mut::<Pkcs7>(data).map_err(|_| {
custom_error(
"DOMExceptionOperationError",
"Decryption failed".to_string(),
)
})?
cipher
.decrypt_padded_vec_mut::<Pkcs7>(data)
.map_err(|_| DecryptError::Failed)?
}
192 => {
// Section 10.3 Step 2 of RFC 2315 https://www.rfc-editor.org/rfc/rfc2315
type Aes192CbcDec = cbc::Decryptor<aes::Aes192>;
let cipher = Aes192CbcDec::new_from_slices(key, &iv).map_err(|_| {
custom_error(
"DOMExceptionOperationError",
"Invalid key or iv".to_string(),
)
})?;
let cipher = Aes192CbcDec::new_from_slices(key, &iv)
.map_err(|_| DecryptError::InvalidKeyOrIv)?;

cipher.decrypt_padded_vec_mut::<Pkcs7>(data).map_err(|_| {
custom_error(
"DOMExceptionOperationError",
"Decryption failed".to_string(),
)
})?
cipher
.decrypt_padded_vec_mut::<Pkcs7>(data)
.map_err(|_| DecryptError::Failed)?
}
256 => {
// Section 10.3 Step 2 of RFC 2315 https://www.rfc-editor.org/rfc/rfc2315
type Aes256CbcDec = cbc::Decryptor<aes::Aes256>;
let cipher = Aes256CbcDec::new_from_slices(key, &iv).map_err(|_| {
custom_error(
"DOMExceptionOperationError",
"Invalid key or iv".to_string(),
)
})?;
let cipher = Aes256CbcDec::new_from_slices(key, &iv)
.map_err(|_| DecryptError::InvalidKeyOrIv)?;

cipher.decrypt_padded_vec_mut::<Pkcs7>(data).map_err(|_| {
custom_error(
"DOMExceptionOperationError",
"Decryption failed".to_string(),
)
})?
cipher
.decrypt_padded_vec_mut::<Pkcs7>(data)
.map_err(|_| DecryptError::Failed)?
}
_ => unreachable!(),
};
Expand All @@ -214,7 +214,7 @@ fn decrypt_aes_ctr_gen<B>(
key: &[u8],
counter: &[u8],
data: &[u8],
) -> Result<Vec<u8>, AnyError>
) -> Result<Vec<u8>, DecryptError>
where
B: KeyIvInit + StreamCipher,
{
Expand All @@ -223,7 +223,7 @@ where
let mut plaintext = data.to_vec();
cipher
.try_apply_keystream(&mut plaintext)
.map_err(|_| operation_error("tried to decrypt too much data"))?;
.map_err(|_| DecryptError::TooMuchData)?;

Ok(plaintext)
}
Expand All @@ -235,46 +235,46 @@ fn decrypt_aes_gcm_gen<N: ArrayLength<u8>>(
length: usize,
additional_data: Vec<u8>,
plaintext: &mut [u8],
) -> Result<(), AnyError> {
) -> Result<(), DecryptError> {
let nonce = Nonce::from_slice(nonce);
match length {
128 => {
let cipher = aes_gcm::AesGcm::<Aes128, N>::new_from_slice(key)
.map_err(|_| operation_error("Decryption failed"))?;
.map_err(|_| DecryptError::Failed)?;
cipher
.decrypt_in_place_detached(
nonce,
additional_data.as_slice(),
plaintext,
tag,
)
.map_err(|_| operation_error("Decryption failed"))?
.map_err(|_| DecryptError::Failed)?
}
192 => {
let cipher = aes_gcm::AesGcm::<Aes192, N>::new_from_slice(key)
.map_err(|_| operation_error("Decryption failed"))?;
.map_err(|_| DecryptError::Failed)?;
cipher
.decrypt_in_place_detached(
nonce,
additional_data.as_slice(),
plaintext,
tag,
)
.map_err(|_| operation_error("Decryption failed"))?
.map_err(|_| DecryptError::Failed)?
}
256 => {
let cipher = aes_gcm::AesGcm::<Aes256, N>::new_from_slice(key)
.map_err(|_| operation_error("Decryption failed"))?;
.map_err(|_| DecryptError::Failed)?;
cipher
.decrypt_in_place_detached(
nonce,
additional_data.as_slice(),
plaintext,
tag,
)
.map_err(|_| operation_error("Decryption failed"))?
.map_err(|_| DecryptError::Failed)?
}
_ => return Err(type_error("invalid length")),
_ => return Err(DecryptError::InvalidLength),
};

Ok(())
Expand All @@ -286,31 +286,29 @@ fn decrypt_aes_ctr(
counter: &[u8],
ctr_length: usize,
data: &[u8],
) -> Result<Vec<u8>, deno_core::anyhow::Error> {
) -> Result<Vec<u8>, DecryptError> {
let key = key.as_secret_key()?;

match ctr_length {
32 => match key_length {
128 => decrypt_aes_ctr_gen::<Ctr32BE<aes::Aes128>>(key, counter, data),
192 => decrypt_aes_ctr_gen::<Ctr32BE<aes::Aes192>>(key, counter, data),
256 => decrypt_aes_ctr_gen::<Ctr32BE<aes::Aes256>>(key, counter, data),
_ => Err(type_error("invalid length")),
_ => Err(DecryptError::InvalidLength),
},
64 => match key_length {
128 => decrypt_aes_ctr_gen::<Ctr64BE<aes::Aes128>>(key, counter, data),
192 => decrypt_aes_ctr_gen::<Ctr64BE<aes::Aes192>>(key, counter, data),
256 => decrypt_aes_ctr_gen::<Ctr64BE<aes::Aes256>>(key, counter, data),
_ => Err(type_error("invalid length")),
_ => Err(DecryptError::InvalidLength),
},
128 => match key_length {
128 => decrypt_aes_ctr_gen::<Ctr128BE<aes::Aes128>>(key, counter, data),
192 => decrypt_aes_ctr_gen::<Ctr128BE<aes::Aes192>>(key, counter, data),
256 => decrypt_aes_ctr_gen::<Ctr128BE<aes::Aes256>>(key, counter, data),
_ => Err(type_error("invalid length")),
_ => Err(DecryptError::InvalidLength),
},
_ => Err(type_error(
"invalid counter length. Currently supported 32/64/128 bits",
)),
_ => Err(DecryptError::InvalidCounterLength),
}
}

Expand All @@ -321,7 +319,7 @@ fn decrypt_aes_gcm(
iv: Vec<u8>,
additional_data: Option<Vec<u8>>,
data: &[u8],
) -> Result<Vec<u8>, AnyError> {
) -> Result<Vec<u8>, DecryptError> {
let key = key.as_secret_key()?;
let additional_data = additional_data.unwrap_or_default();

Expand All @@ -330,7 +328,7 @@ fn decrypt_aes_gcm(
// Note that encryption won't fail, it instead truncates the tag
// to the specified tag length as specified in the spec.
if tag_length != 128 {
return Err(type_error("tag length not equal to 128"));
return Err(DecryptError::InvalidTagLength);
}

let sep = data.len() - (tag_length / 8);
Expand All @@ -357,7 +355,7 @@ fn decrypt_aes_gcm(
additional_data,
&mut plaintext,
)?,
_ => return Err(type_error("iv length not equal to 12 or 16")),
_ => return Err(DecryptError::InvalidIvLength),
}

Ok(plaintext)
Expand Down
22 changes: 14 additions & 8 deletions ext/crypto/ed25519.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@

use base64::prelude::BASE64_URL_SAFE_NO_PAD;
use base64::Engine;
use deno_core::error::custom_error;
use deno_core::error::AnyError;
use deno_core::op2;
use deno_core::ToJsBuffer;
use elliptic_curve::pkcs8::PrivateKeyInfo;
Expand All @@ -15,6 +13,16 @@ use spki::der::asn1::BitString;
use spki::der::Decode;
use spki::der::Encode;

#[derive(Debug, thiserror::Error)]
pub enum Ed25519Error {
#[error("Failed to export key")]
FailedExport,
#[error(transparent)]
Der(#[from] rsa::pkcs1::der::Error),
#[error(transparent)]
KeyRejected(#[from] ring::error::KeyRejected),
}

#[op2(fast)]
pub fn op_crypto_generate_ed25519_keypair(
#[buffer] pkey: &mut [u8],
Expand Down Expand Up @@ -116,7 +124,7 @@ pub fn op_crypto_import_pkcs8_ed25519(
#[serde]
pub fn op_crypto_export_spki_ed25519(
#[buffer] pubkey: &[u8],
) -> Result<ToJsBuffer, AnyError> {
) -> Result<ToJsBuffer, Ed25519Error> {
let key_info = spki::SubjectPublicKeyInfo {
algorithm: spki::AlgorithmIdentifierOwned {
// id-Ed25519
Expand All @@ -128,9 +136,7 @@ pub fn op_crypto_export_spki_ed25519(
Ok(
key_info
.to_der()
.map_err(|_| {
custom_error("DOMExceptionOperationError", "Failed to export key")
})?
.map_err(|_| Ed25519Error::FailedExport)?
.into(),
)
}
Expand All @@ -139,7 +145,7 @@ pub fn op_crypto_export_spki_ed25519(
#[serde]
pub fn op_crypto_export_pkcs8_ed25519(
#[buffer] pkey: &[u8],
) -> Result<ToJsBuffer, AnyError> {
) -> Result<ToJsBuffer, Ed25519Error> {
use rsa::pkcs1::der::Encode;

// This should probably use OneAsymmetricKey instead
Expand All @@ -164,7 +170,7 @@ pub fn op_crypto_export_pkcs8_ed25519(
#[string]
pub fn op_crypto_jwk_x_ed25519(
#[buffer] pkey: &[u8],
) -> Result<String, AnyError> {
) -> Result<String, Ed25519Error> {
let pair = Ed25519KeyPair::from_seed_unchecked(pkey)?;
Ok(BASE64_URL_SAFE_NO_PAD.encode(pair.public_key().as_ref()))
}
Loading

0 comments on commit 1bc3694

Please sign in to comment.