#![no_main] mod common; use arbitrary::{Arbitrary, Unstructured}; use common::{ arbitrary_ciphertext_minpk, arbitrary_ciphertext_minsig, arbitrary_minpk_signature, arbitrary_minsig_signature, }; use commonware_codec::ReadExt; use commonware_cryptography::bls12381::{ primitives::{ group::Private, ops::{compute_public, sign_message}, variant::{MinPk, MinSig, Variant}, }, tle::{decrypt, encrypt, Block, Ciphertext}, }; use libfuzzer_sys::fuzz_target; use rand::{rngs::StdRng, SeedableRng}; #[derive(Debug, Clone)] enum FuzzOperation { EncryptDecryptMinPk { master_secret: Private, namespace: Vec, target: Vec, message: [u8; 32], rng_seed: u64, }, EncryptDecryptMinSig { master_secret: Private, namespace: Vec, target: Vec, message: [u8; 32], rng_seed: u64, }, DecryptWithWrongKeyMinPk { master_secret1: Private, master_secret2: Private, namespace: Vec, target: Vec, message: [u8; 32], rng_seed: u64, }, DecryptWithWrongKeyMinSig { master_secret1: Private, master_secret2: Private, namespace: Vec, target: Vec, message: [u8; 32], rng_seed: u64, }, TamperedCiphertextMinPk { master_secret: Private, namespace: Vec, target: Vec, message: [u8; 32], tamper_index: usize, tamper_value: u8, rng_seed: u64, }, TamperedCiphertextMinSig { master_secret: Private, namespace: Vec, target: Vec, message: [u8; 32], tamper_index: usize, tamper_value: u8, rng_seed: u64, }, DecryptArbitraryMinPk { signature: ::Signature, ciphertext: Ciphertext, }, DecryptArbitraryMinSig { signature: ::Signature, ciphertext: Ciphertext, }, } impl<'a> Arbitrary<'a> for FuzzOperation { fn arbitrary(u: &mut Unstructured<'a>) -> Result { let choice = u.int_in_range(0..=7)?; match choice { 0 => Ok(FuzzOperation::EncryptDecryptMinPk { master_secret: u.arbitrary()?, namespace: common::arbitrary_bytes(u, 0, 50)?, target: common::arbitrary_bytes(u, 0, 100)?, message: u.arbitrary()?, rng_seed: u.arbitrary()?, }), 1 => Ok(FuzzOperation::EncryptDecryptMinSig { master_secret: u.arbitrary()?, namespace: common::arbitrary_bytes(u, 0, 50)?, target: common::arbitrary_bytes(u, 0, 100)?, message: u.arbitrary()?, rng_seed: u.arbitrary()?, }), 2 => Ok(FuzzOperation::DecryptWithWrongKeyMinPk { master_secret1: u.arbitrary()?, master_secret2: u.arbitrary()?, namespace: common::arbitrary_bytes(u, 0, 50)?, target: common::arbitrary_bytes(u, 0, 100)?, message: u.arbitrary()?, rng_seed: u.arbitrary()?, }), 3 => Ok(FuzzOperation::DecryptWithWrongKeyMinSig { master_secret1: u.arbitrary()?, master_secret2: u.arbitrary()?, namespace: common::arbitrary_bytes(u, 0, 50)?, target: common::arbitrary_bytes(u, 0, 100)?, message: u.arbitrary()?, rng_seed: u.arbitrary()?, }), 4 => Ok(FuzzOperation::TamperedCiphertextMinPk { master_secret: u.arbitrary()?, namespace: common::arbitrary_bytes(u, 0, 50)?, target: common::arbitrary_bytes(u, 0, 100)?, message: u.arbitrary()?, tamper_index: u.int_in_range(0..=95)?, tamper_value: u.arbitrary()?, rng_seed: u.arbitrary()?, }), 5 => Ok(FuzzOperation::TamperedCiphertextMinSig { master_secret: u.arbitrary()?, namespace: common::arbitrary_bytes(u, 0, 50)?, target: common::arbitrary_bytes(u, 0, 100)?, message: u.arbitrary()?, tamper_index: u.int_in_range(0..=95)?, tamper_value: u.arbitrary()?, rng_seed: u.arbitrary()?, }), 6 => Ok(FuzzOperation::DecryptArbitraryMinPk { signature: arbitrary_minpk_signature(u)?, ciphertext: arbitrary_ciphertext_minpk(u)?, }), 7 => Ok(FuzzOperation::DecryptArbitraryMinSig { signature: arbitrary_minsig_signature(u)?, ciphertext: arbitrary_ciphertext_minsig(u)?, }), _ => unreachable!(), } } } fn fuzz(op: FuzzOperation) { match op { FuzzOperation::EncryptDecryptMinPk { master_secret, namespace, target, message, rng_seed, } => { let master_public = compute_public::(&master_secret); let message_block = Block::new(message); let mut rng = StdRng::seed_from_u64(rng_seed); let ciphertext = encrypt::<_, MinPk>( &mut rng, master_public, (&namespace, &target), &message_block, ); let signature = sign_message::(&master_secret, &namespace, &target); let decrypted = decrypt::(&signature, &ciphertext); if let Some(decrypted_block) = decrypted { assert_eq!(message_block, decrypted_block); } else { panic!("Decryption failed for valid ciphertext"); } } FuzzOperation::EncryptDecryptMinSig { master_secret, namespace, target, message, rng_seed, } => { let master_public = compute_public::(&master_secret); let message_block = Block::new(message); let mut rng = StdRng::seed_from_u64(rng_seed); let ciphertext = encrypt::<_, MinSig>( &mut rng, master_public, (&namespace, &target), &message_block, ); let signature = sign_message::(&master_secret, &namespace, &target); let decrypted = decrypt::(&signature, &ciphertext); if let Some(decrypted_block) = decrypted { assert_eq!(message_block, decrypted_block); } else { panic!("Decryption failed for valid ciphertext"); } } FuzzOperation::DecryptWithWrongKeyMinPk { master_secret1, master_secret2, namespace, target, message, rng_seed, } => { let master_public1 = compute_public::(&master_secret1); let message_block = Block::new(message); let mut rng = StdRng::seed_from_u64(rng_seed); let ciphertext = encrypt::<_, MinPk>( &mut rng, master_public1, (&namespace, &target), &message_block, ); let wrong_signature = sign_message::(&master_secret2, &namespace, &target); let _ = decrypt::(&wrong_signature, &ciphertext); } FuzzOperation::DecryptWithWrongKeyMinSig { master_secret1, master_secret2, namespace, target, message, rng_seed, } => { let master_public1 = compute_public::(&master_secret1); let message_block = Block::new(message); let mut rng = StdRng::seed_from_u64(rng_seed); let ciphertext = encrypt::<_, MinSig>( &mut rng, master_public1, (&namespace, &target), &message_block, ); let wrong_signature = sign_message::(&master_secret2, &namespace, &target); let _ = decrypt::(&wrong_signature, &ciphertext); } FuzzOperation::TamperedCiphertextMinPk { master_secret, namespace, target, message, tamper_index, tamper_value, rng_seed, } => { let master_public = compute_public::(&master_secret); let message_block = Block::new(message); let mut rng = StdRng::seed_from_u64(rng_seed); let ciphertext = encrypt::<_, MinPk>( &mut rng, master_public, (&namespace, &target), &message_block, ); let mut encoded = Vec::new(); commonware_codec::Write::write(&ciphertext, &mut encoded); if tamper_index < encoded.len() { encoded[tamper_index] ^= tamper_value; } if let Ok(tampered) = Ciphertext::::read(&mut encoded.as_slice()) { let signature = sign_message::(&master_secret, &namespace, &target); let _ = decrypt::(&signature, &tampered); } } FuzzOperation::TamperedCiphertextMinSig { master_secret, namespace, target, message, tamper_index, tamper_value, rng_seed, } => { let master_public = compute_public::(&master_secret); let message_block = Block::new(message); let mut rng = StdRng::seed_from_u64(rng_seed); let ciphertext = encrypt::<_, MinSig>( &mut rng, master_public, (&namespace, &target), &message_block, ); let mut encoded = Vec::new(); commonware_codec::Write::write(&ciphertext, &mut encoded); if tamper_index < encoded.len() { encoded[tamper_index] ^= tamper_value; } if let Ok(tampered) = Ciphertext::::read(&mut encoded.as_slice()) { let signature = sign_message::(&master_secret, &namespace, &target); let _ = decrypt::(&signature, &tampered); } } FuzzOperation::DecryptArbitraryMinPk { signature, ciphertext, } => { let _ = decrypt::(&signature, &ciphertext); } FuzzOperation::DecryptArbitraryMinSig { signature, ciphertext, } => { let _ = decrypt::(&signature, &ciphertext); } } } fuzz_target!(|ops: Vec| { for op in ops { fuzz(op); } });