diff --git a/fuzz/Cargo.toml b/fuzz/Cargo.toml index 9f7512ce..6aeaa0f2 100644 --- a/fuzz/Cargo.toml +++ b/fuzz/Cargo.toml @@ -168,3 +168,15 @@ path = "fuzz_targets/key/generate_keypair.rs" [[bin]] name = "constant_time_equals" path = "fuzz_targets/utils/constant_time_equals.rs" + +[[bin]] +name = "kdf_encrypted_data_deserialization" +path = "fuzz_targets/derive_encrypt/kdf_encrypted_data_deserialization.rs" + +[[bin]] +name = "derivation_parameters_deserialization" +path = "fuzz_targets/key_derivation/derivation_parameters_deserialization.rs" + +[[bin]] +name = "decrypt_with_password" +path = "fuzz_targets/derive_encrypt/decrypt_with_password.rs" diff --git a/fuzz/fuzz_targets/derive_encrypt/decrypt_with_password.rs b/fuzz/fuzz_targets/derive_encrypt/decrypt_with_password.rs new file mode 100644 index 00000000..33b79b0c --- /dev/null +++ b/fuzz/fuzz_targets/derive_encrypt/decrypt_with_password.rs @@ -0,0 +1,65 @@ +#![no_main] +use arbitrary::Arbitrary; +use libfuzzer_sys::fuzz_target; + +use devolutions_crypto::derive_encrypt::encrypt_with_password_and_aad; +use devolutions_crypto::key_derivation::Argon2; +use devolutions_crypto::{Argon2Parameters, CiphertextVersion}; + +#[derive(Arbitrary, Clone, Debug)] +struct Input { + data: Vec, + password: Vec, + aad: Vec, + decrypt_password: Vec, + decrypt_aad: Vec, + lanes: u32, + memory: u32, + iterations: u32, + salt: Vec, +} + +fuzz_target!(|input: Input| { + // Clamp Argon2 parameters to keep key derivation cheap enough for fuzzing. + // The derived key feeds a SecretKey, which requires exactly 32 bytes; any + // other length would fail in encrypt_with_password_and_aad before reaching + // the decrypt path. Arbitrary output lengths are covered by derive_key_argon2. + let length = 32; + let lanes = input.lanes.clamp(1, 16); + let memory = input.memory.clamp(8, 65536); + let iterations = input.iterations.clamp(1, 10); + + // Use only small salts for fuzzing performance. + let salt = if input.salt.len() > 64 { + &input.salt[..64] + } else if input.salt.is_empty() { + &[0u8; 8][..] + } else { + &input.salt[..] + }; + + let parameters = Argon2Parameters::builder() + .length(length) + .lanes(lanes) + .memory(memory) + .iterations(iterations) + .salt(salt.to_vec()) + .build(); + + let derivation_parameters = Argon2::with_params(parameters).parameters(); + + let blob = match encrypt_with_password_and_aad( + &input.data, + &input.password, + &input.aad, + derivation_parameters, + CiphertextVersion::Latest, + ) { + Ok(b) => b, + Err(_) => return, + }; + + // Exercise the decrypt path with both the correct and a fuzzed password/AAD. + let _ = blob.decrypt_with_password_and_aad(&input.password, &input.aad); + let _ = blob.decrypt_with_password_and_aad(&input.decrypt_password, &input.decrypt_aad); +}); diff --git a/fuzz/fuzz_targets/derive_encrypt/kdf_encrypted_data_deserialization.rs b/fuzz/fuzz_targets/derive_encrypt/kdf_encrypted_data_deserialization.rs new file mode 100644 index 00000000..557a3cdf --- /dev/null +++ b/fuzz/fuzz_targets/derive_encrypt/kdf_encrypted_data_deserialization.rs @@ -0,0 +1,10 @@ +#![no_main] +use libfuzzer_sys::fuzz_target; + +use devolutions_crypto::derive_encrypt::KdfEncryptedData; + +use std::convert::TryFrom; + +fuzz_target!(|data: &[u8]| { + let _ = KdfEncryptedData::try_from(data); +}); diff --git a/fuzz/fuzz_targets/key_derivation/derivation_parameters_deserialization.rs b/fuzz/fuzz_targets/key_derivation/derivation_parameters_deserialization.rs new file mode 100644 index 00000000..6488c20c --- /dev/null +++ b/fuzz/fuzz_targets/key_derivation/derivation_parameters_deserialization.rs @@ -0,0 +1,10 @@ +#![no_main] +use libfuzzer_sys::fuzz_target; + +use devolutions_crypto::key_derivation::DerivationParameters; + +use std::convert::TryFrom; + +fuzz_target!(|data: &[u8]| { + let _ = DerivationParameters::try_from(data); +});