diff --git a/src/hazardous/aead/chacha20poly1305.rs b/src/hazardous/aead/chacha20poly1305.rs index e12069e7..5814dc48 100644 --- a/src/hazardous/aead/chacha20poly1305.rs +++ b/src/hazardous/aead/chacha20poly1305.rs @@ -242,7 +242,7 @@ mod public { let secret_key = SecretKey::generate(); let nonce = Nonce::from_slice(&[0u8; chacha20::IETF_CHACHA_NONCESIZE]).unwrap(); AeadTestRunner(seal, open, secret_key, nonce, &input, None, POLY1305_OUTSIZE, &ad); - + test_diff_params_err(&seal, &open, &input, POLY1305_OUTSIZE); true } } diff --git a/src/hazardous/aead/xchacha20poly1305.rs b/src/hazardous/aead/xchacha20poly1305.rs index 97af91b6..5a4860fc 100644 --- a/src/hazardous/aead/xchacha20poly1305.rs +++ b/src/hazardous/aead/xchacha20poly1305.rs @@ -136,7 +136,7 @@ mod public { let secret_key = SecretKey::generate(); let nonce = Nonce::generate(); AeadTestRunner(seal, open, secret_key, nonce, &input, None, POLY1305_OUTSIZE, &ad); - + test_diff_params_err(&seal, &open, &input, POLY1305_OUTSIZE); true } } diff --git a/src/hazardous/stream/chacha20.rs b/src/hazardous/stream/chacha20.rs index 72f62029..333ade38 100644 --- a/src/hazardous/stream/chacha20.rs +++ b/src/hazardous/stream/chacha20.rs @@ -386,6 +386,20 @@ mod public { use super::*; use crate::test_framework::streamcipher_interface::*; + impl TestingRandom for SecretKey { + fn gen() -> Self { + Self::generate() + } + } + + impl TestingRandom for Nonce { + fn gen() -> Self { + let mut n = [0u8; IETF_CHACHA_NONCESIZE]; + crate::util::secure_rand_bytes(&mut n).unwrap(); + Self::from_slice(&n).unwrap() + } + } + // Proptests. Only executed when NOT testing no_std. mod proptest { use super::*; @@ -395,118 +409,11 @@ mod public { let secret_key = SecretKey::generate(); let nonce = Nonce::from_slice(&[0u8; IETF_CHACHA_NONCESIZE]).unwrap(); StreamCipherTestRunner(encrypt, decrypt, secret_key, nonce, counter, &input, None); + test_diff_params_diff_output(&encrypt, &decrypt); true } } - - quickcheck! { - // Encrypting and decrypting using two different secret keys and the same nonce - // should never yield the same input. - fn prop_encrypt_decrypt_diff_keys_diff_input(input: Vec) -> bool { - let pt = if input.is_empty() { - vec![1u8; 10] - } else { - input - }; - - let sk1 = SecretKey::from_slice(&[0u8; 32]).unwrap(); - let sk2 = SecretKey::from_slice(&[1u8; 32]).unwrap(); - - let mut dst_out_ct = vec![0u8; pt.len()]; - let mut dst_out_pt = vec![0u8; pt.len()]; - - encrypt( - &sk1, - &Nonce::from_slice(&[0u8; 12]).unwrap(), - 0, - &pt[..], - &mut dst_out_ct, - ).unwrap(); - - decrypt( - &sk2, - &Nonce::from_slice(&[0u8; 12]).unwrap(), - 0, - &dst_out_ct[..], - &mut dst_out_pt, - ).unwrap(); - - dst_out_pt != pt - } - } - - quickcheck! { - // Encrypting and decrypting using two different nonces and the same secret key - // should never yield the same input. - fn prop_encrypt_decrypt_diff_nonces_diff_input(input: Vec) -> bool { - let pt = if input.is_empty() { - vec![1u8; 10] - } else { - input - }; - - let n1 = Nonce::from_slice(&[0u8; 12]).unwrap(); - let n2 = Nonce::from_slice(&[1u8; 12]).unwrap(); - - let mut dst_out_ct = vec![0u8; pt.len()]; - let mut dst_out_pt = vec![0u8; pt.len()]; - - encrypt( - &SecretKey::from_slice(&[0u8; 32]).unwrap(), - &n1, - 0, - &pt[..], - &mut dst_out_ct, - ).unwrap(); - - decrypt( - &SecretKey::from_slice(&[0u8; 32]).unwrap(), - &n2, - 0, - &dst_out_ct[..], - &mut dst_out_pt, - ).unwrap(); - - dst_out_pt != pt - } - } - - quickcheck! { - // Encrypting and decrypting using two different initial counters - // should never yield the same input. - fn prop_encrypt_decrypt_diff_init_counter_diff_input(input: Vec) -> bool { - let pt = if input.is_empty() { - vec![1u8; 10] - } else { - input - }; - - let init_counter1 = 32; - let init_counter2 = 64; - - let mut dst_out_ct = vec![0u8; pt.len()]; - let mut dst_out_pt = vec![0u8; pt.len()]; - - encrypt( - &SecretKey::from_slice(&[0u8; 32]).unwrap(), - &Nonce::from_slice(&[0u8; 12]).unwrap(), - init_counter1, - &pt[..], - &mut dst_out_ct, - ).unwrap(); - - decrypt( - &SecretKey::from_slice(&[0u8; 32]).unwrap(), - &Nonce::from_slice(&[0u8; 12]).unwrap(), - init_counter2, - &dst_out_ct[..], - &mut dst_out_pt, - ).unwrap(); - - dst_out_pt != pt - } - } } } diff --git a/src/hazardous/stream/xchacha20.rs b/src/hazardous/stream/xchacha20.rs index bf6d4a4b..ebf1f2ba 100644 --- a/src/hazardous/stream/xchacha20.rs +++ b/src/hazardous/stream/xchacha20.rs @@ -152,6 +152,12 @@ mod public { use super::*; use crate::test_framework::streamcipher_interface::*; + impl TestingRandom for Nonce { + fn gen() -> Self { + Self::generate() + } + } + // Proptests. Only executed when NOT testing no_std. #[cfg(feature = "safe_api")] mod proptest { @@ -162,118 +168,11 @@ mod public { let secret_key = SecretKey::generate(); let nonce = Nonce::generate(); StreamCipherTestRunner(encrypt, decrypt, secret_key, nonce, counter, &input, None); + test_diff_params_diff_output(&encrypt, &decrypt); true } } - - quickcheck! { - // Encrypting and decrypting using two different secret keys and the same nonce - // should never yield the same input. - fn prop_encrypt_decrypt_diff_keys_diff_input(input: Vec) -> bool { - let pt = if input.is_empty() { - vec![1u8; 10] - } else { - input - }; - - let sk1 = SecretKey::from_slice(&[0u8; 32]).unwrap(); - let sk2 = SecretKey::from_slice(&[1u8; 32]).unwrap(); - - let mut dst_out_ct = vec![0u8; pt.len()]; - let mut dst_out_pt = vec![0u8; pt.len()]; - - encrypt( - &sk1, - &Nonce::from_slice(&[0u8; 24]).unwrap(), - 0, - &pt[..], - &mut dst_out_ct, - ).unwrap(); - - decrypt( - &sk2, - &Nonce::from_slice(&[0u8; 24]).unwrap(), - 0, - &dst_out_ct[..], - &mut dst_out_pt, - ).unwrap(); - - dst_out_pt != pt - } - } - - quickcheck! { - // Encrypting and decrypting using two different nonces and the same secret key - // should never yield the same input. - fn prop_encrypt_decrypt_diff_nonces_diff_input(input: Vec) -> bool { - let pt = if input.is_empty() { - vec![1u8; 10] - } else { - input - }; - - let n1 = Nonce::from_slice(&[0u8; 24]).unwrap(); - let n2 = Nonce::from_slice(&[1u8; 24]).unwrap(); - - let mut dst_out_ct = vec![0u8; pt.len()]; - let mut dst_out_pt = vec![0u8; pt.len()]; - - encrypt( - &SecretKey::from_slice(&[0u8; 32]).unwrap(), - &n1, - 0, - &pt[..], - &mut dst_out_ct, - ).unwrap(); - - decrypt( - &SecretKey::from_slice(&[0u8; 32]).unwrap(), - &n2, - 0, - &dst_out_ct[..], - &mut dst_out_pt, - ).unwrap(); - - dst_out_pt != pt - } - } - - quickcheck! { - // Encrypting and decrypting using two different initial counters - // should never yield the same input. - fn prop_encrypt_decrypt_diff_init_counter_diff_input(input: Vec) -> bool { - let pt = if input.is_empty() { - vec![1u8; 10] - } else { - input - }; - - let init_counter1 = 32; - let init_counter2 = 64; - - let mut dst_out_ct = vec![0u8; pt.len()]; - let mut dst_out_pt = vec![0u8; pt.len()]; - - encrypt( - &SecretKey::from_slice(&[0u8; 32]).unwrap(), - &Nonce::from_slice(&[0u8; 24]).unwrap(), - init_counter1, - &pt[..], - &mut dst_out_ct, - ).unwrap(); - - decrypt( - &SecretKey::from_slice(&[0u8; 32]).unwrap(), - &Nonce::from_slice(&[0u8; 24]).unwrap(), - init_counter2, - &dst_out_ct[..], - &mut dst_out_pt, - ).unwrap(); - - dst_out_pt != pt - } - } } } } diff --git a/src/test_framework/aead_interface.rs b/src/test_framework/aead_interface.rs index d2ca760a..f772acd6 100644 --- a/src/test_framework/aead_interface.rs +++ b/src/test_framework/aead_interface.rs @@ -24,6 +24,10 @@ #[cfg(feature = "safe_api")] use crate::errors::UnknownCryptoError; +#[cfg(test)] +#[cfg(feature = "safe_api")] +use crate::test_framework::streamcipher_interface::TestingRandom; + #[allow(clippy::too_many_arguments)] #[cfg(feature = "safe_api")] /// Test runner for AEADs. @@ -369,3 +373,42 @@ fn none_or_empty_some_aad_same_result( .is_ok()); assert!(opener(&key, &nonce, &dst_out_ct_some_empty, None, &mut dst_out_pt).is_ok()); } + +#[cfg(test)] +#[cfg(feature = "safe_api")] +/// Test that sealing and opening with different secret-key/nonce yields an error. +pub fn test_diff_params_err( + sealer: &Sealer, + opener: &Opener, + input: &[u8], + tag_size: usize, +) where + Key: TestingRandom + PartialEq, + Nonce: TestingRandom + PartialEq, + Sealer: Fn(&Key, &Nonce, &[u8], Option<&[u8]>, &mut [u8]) -> Result<(), UnknownCryptoError>, + Opener: Fn(&Key, &Nonce, &[u8], Option<&[u8]>, &mut [u8]) -> Result<(), UnknownCryptoError>, +{ + let mut input = input; + if input.is_empty() { + input = &[0u8; 1]; + } + + let sk1 = Key::gen(); + let sk2 = Key::gen(); + assert!(sk1 != sk2); + + let n1 = Nonce::gen(); + let n2 = Nonce::gen(); + assert!(n1 != n2); + + let mut dst_out_ct = vec![0u8; input.len() + tag_size]; + let mut dst_out_pt = vec![0u8; input.len()]; + + // Different secret key + sealer(&sk1, &n1, input, None, &mut dst_out_ct).unwrap(); + assert!(opener(&sk2, &n1, &dst_out_ct, None, &mut dst_out_pt).is_err()); + + // Different nonce + sealer(&sk1, &n1, input, None, &mut dst_out_ct).unwrap(); + assert!(opener(&sk1, &n2, &dst_out_ct, None, &mut dst_out_pt).is_err()); +} diff --git a/src/test_framework/streamcipher_interface.rs b/src/test_framework/streamcipher_interface.rs index d107bba7..c03a4b70 100644 --- a/src/test_framework/streamcipher_interface.rs +++ b/src/test_framework/streamcipher_interface.rs @@ -25,6 +25,13 @@ #[cfg(feature = "safe_api")] use crate::errors::UnknownCryptoError; +#[cfg(test)] +#[cfg(feature = "safe_api")] +pub trait TestingRandom { + /// Randomly generate self. + fn gen() -> Self; +} + #[cfg(feature = "safe_api")] /// Test runner for stream ciphers. pub fn StreamCipherTestRunner( @@ -249,3 +256,48 @@ fn initial_counter_max_ok( ) .is_ok()); } + +#[cfg(test)] +#[cfg(feature = "safe_api")] +/// Test that encrypting using different secret-key/nonce/initial-counter combinations yields different +/// ciphertexts. +pub fn test_diff_params_diff_output( + encryptor: &Encryptor, + decryptor: &Decryptor, +) where + Key: TestingRandom + PartialEq, + Nonce: TestingRandom + PartialEq, + Encryptor: Fn(&Key, &Nonce, u32, &[u8], &mut [u8]) -> Result<(), UnknownCryptoError>, + Decryptor: Fn(&Key, &Nonce, u32, &[u8], &mut [u8]) -> Result<(), UnknownCryptoError>, +{ + let input = &[0u8; 16]; + + let sk1 = Key::gen(); + let sk2 = Key::gen(); + assert!(sk1 != sk2); + + let n1 = Nonce::gen(); + let n2 = Nonce::gen(); + assert!(n1 != n2); + + let c1 = 0u32; + let c2 = 1u32; + + let mut dst_out_ct = vec![0u8; input.len()]; + let mut dst_out_pt = vec![0u8; input.len()]; + + // Different secret key + encryptor(&sk1, &n1, c1, input, &mut dst_out_ct).unwrap(); + decryptor(&sk2, &n1, c1, &dst_out_ct, &mut dst_out_pt).unwrap(); + assert_ne!(&dst_out_pt[..], input); + + // Different nonce + encryptor(&sk1, &n1, c1, input, &mut dst_out_ct).unwrap(); + decryptor(&sk1, &n2, c1, &dst_out_ct, &mut dst_out_pt).unwrap(); + assert_ne!(&dst_out_pt[..], input); + + // Different initial counter + encryptor(&sk1, &n1, c1, input, &mut dst_out_ct).unwrap(); + decryptor(&sk1, &n1, c2, &dst_out_ct, &mut dst_out_pt).unwrap(); + assert_ne!(&dst_out_pt[..], input); +}