Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor(ext/crypto): use concrete error types #26167

Merged
merged 8 commits into from
Oct 18, 2024
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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")]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All other error strings start as a lowercase. Is that supposed to be lowercase too?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am planning to clean up the messages a bit on a follow-up; for now i just moved the strings, so this is like it was before. I agree this should be more consistent, but wont do that in this PR

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