From cd3eb7aa9460a723eb9cfeafd953e286fc2ce8fc Mon Sep 17 00:00:00 2001 From: brycx Date: Mon, 28 Jan 2019 16:14:18 +0100 Subject: [PATCH 01/40] Tests: Re-organize chacha20poly1305 module level test. Differentiate between testing public and private functions. Integrate quickcheck. --- Cargo.toml | 1 + src/hazardous/aead/chacha20poly1305.rs | 823 ++++++++++++++++++------- src/lib.rs | 4 + 3 files changed, 601 insertions(+), 227 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index ae5a6896..08a1fb1e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,6 +34,7 @@ no_std = [ "clear_on_drop/nightly", "subtle/nightly" ] [dev-dependencies] hex = "0.3.2" serde_json = "1.0.33" +quickcheck = "0.8.0" [profile.dev] diff --git a/src/hazardous/aead/chacha20poly1305.rs b/src/hazardous/aead/chacha20poly1305.rs index f1fa7211..723a1381 100644 --- a/src/hazardous/aead/chacha20poly1305.rs +++ b/src/hazardous/aead/chacha20poly1305.rs @@ -135,16 +135,20 @@ fn padding(input: &[u8]) -> usize { #[must_use] /// Process data to be authenticated using a `Poly1305` struct initialized with -/// a one-time-key. +/// a one-time-key. Up to `buf_in_len` data in `buf` get's authenticated. The +/// indexing is needed because authentication happens on different input lenghts +/// in seal()/open(). fn process_authentication( poly1305_state: &mut poly1305::Poly1305, ad: &[u8], buf: &[u8], buf_in_len: usize, ) -> Result<(), UnknownCryptoError> { - if buf_in_len > buf.len() { - return Err(UnknownCryptoError); - } + // If buf_in_len is 0, then NO ciphertext gets authenticated. + // Because of this, buf may never be empty either. + assert!(!buf.is_empty()); + assert!(buf_in_len > 0); + assert!(buf_in_len <= buf.len()); let mut padding_max = [0u8; 16]; @@ -249,242 +253,607 @@ pub fn open( Ok(()) } -#[test] -fn length_padding_tests() { - // Integral multiple of 16 - assert_eq!(padding(&[0u8; 16]), 0); - assert_eq!(padding(&[0u8; 15]), 1); - assert_eq!(padding(&[0u8; 32]), 0); - assert_eq!(padding(&[0u8; 30]), 2); -} +// Testing public functions in the module. +#[cfg(test)] +mod public { + use super::*; + // One function tested per submodule. + + mod test_seal { + use super::*; + + #[test] + fn test_dst_out_length() { + let mut dst_out_ct = [0u8; 80]; // 64 + Poly1305TagLen + let mut dst_out_ct_less = [0u8; 79]; // 64 + Poly1305TagLen - 1 + let mut dst_out_ct_more = [0u8; 81]; // 64 + Poly1305TagLen + 1 + let mut dst_out_ct_more_2 = [0u8; 64 + (POLY1305_BLOCKSIZE * 2)]; + + assert!(seal( + &SecretKey::from_slice(&[0u8; 32]).unwrap(), + &Nonce::from_slice(&[0u8; 12]).unwrap(), + &[0u8; 64], + None, + &mut dst_out_ct, + ) + .is_ok()); + + assert!(seal( + &SecretKey::from_slice(&[0u8; 32]).unwrap(), + &Nonce::from_slice(&[0u8; 12]).unwrap(), + &[0u8; 64], + None, + &mut dst_out_ct_more, + ) + .is_ok()); + + assert!(seal( + &SecretKey::from_slice(&[0u8; 32]).unwrap(), + &Nonce::from_slice(&[0u8; 12]).unwrap(), + &[0u8; 64], + None, + &mut dst_out_ct_more_2, + ) + .is_ok()); + + assert!(seal( + &SecretKey::from_slice(&[0u8; 32]).unwrap(), + &Nonce::from_slice(&[0u8; 12]).unwrap(), + &[0u8; 64], + None, + &mut dst_out_ct_less, + ) + .is_err()); + } + + #[test] + fn test_plaintext_length() { + let mut dst_out_ct_0 = [0u8; 16]; // 0 + Poly1305TagLen + let mut dst_out_ct_1 = [0u8; 17]; // 1 + Poly1305TagLen + let mut dst_out_ct_128 = [0u8; 144]; // 128 + Poly1305TagLen + + assert!(seal( + &SecretKey::from_slice(&[0u8; 32]).unwrap(), + &Nonce::from_slice(&[0u8; 12]).unwrap(), + &[0u8; 0], + None, + &mut dst_out_ct_0, + ) + .is_err()); + + assert!(seal( + &SecretKey::from_slice(&[0u8; 32]).unwrap(), + &Nonce::from_slice(&[0u8; 12]).unwrap(), + &[0u8; 1], + None, + &mut dst_out_ct_1, + ) + .is_ok()); + + assert!(seal( + &SecretKey::from_slice(&[0u8; 32]).unwrap(), + &Nonce::from_slice(&[0u8; 12]).unwrap(), + &[0u8; 128], + None, + &mut dst_out_ct_128, + ) + .is_ok()); + } + } -#[test] -fn test_auth_process_with_above_length_index() { - let poly1305_key = poly1305_key_gen(&[0u8; 32], &[0u8; 12]).unwrap(); - let mut poly1305_state = poly1305::init(&poly1305_key); + mod test_open { + use super::*; + + #[test] + fn test_ciphertext_with_tag_length() { + let mut dst_out_pt = [0u8; 64]; + + assert!(open( + &SecretKey::from_slice(&[0u8; 32]).unwrap(), + &Nonce::from_slice(&[0u8; 12]).unwrap(), + &[0u8; 0], + None, + &mut dst_out_pt, + ) + .is_err()); + + assert!(open( + &SecretKey::from_slice(&[0u8; 32]).unwrap(), + &Nonce::from_slice(&[0u8; 12]).unwrap(), + &[0u8; POLY1305_BLOCKSIZE], + None, + &mut dst_out_pt, + ) + .is_err()); + + assert!(open( + &SecretKey::from_slice(&[0u8; 32]).unwrap(), + &Nonce::from_slice(&[0u8; 12]).unwrap(), + &[0u8; POLY1305_BLOCKSIZE - 1], + None, + &mut dst_out_pt, + ) + .is_err()); + + let mut dst_out_ct = [0u8; 64 + 16]; + seal( + &SecretKey::from_slice(&[0u8; 32]).unwrap(), + &Nonce::from_slice(&[0u8; 12]).unwrap(), + &[0u8; POLY1305_BLOCKSIZE + 1], + None, + &mut dst_out_ct, + ) + .unwrap(); + + assert!(open( + &SecretKey::from_slice(&[0u8; 32]).unwrap(), + &Nonce::from_slice(&[0u8; 12]).unwrap(), + &dst_out_ct[..(POLY1305_BLOCKSIZE + 1) + 16], + None, + &mut dst_out_pt, + ) + .is_ok()); + } + + #[test] + fn test_dst_out_length() { + let mut dst_out_ct = [0u8; 64 + 16]; + seal( + &SecretKey::from_slice(&[0u8; 32]).unwrap(), + &Nonce::from_slice(&[0u8; 12]).unwrap(), + &[0u8; 64], + None, + &mut dst_out_ct, + ) + .unwrap(); + + let mut dst_out_pt = [0u8; 64]; + let mut dst_out_pt_0 = [0u8; 0]; + let mut dst_out_pt_less = [0u8; 63]; + let mut dst_out_pt_more = [0u8; 65]; + + assert!(open( + &SecretKey::from_slice(&[0u8; 32]).unwrap(), + &Nonce::from_slice(&[0u8; 12]).unwrap(), + &dst_out_ct, + None, + &mut dst_out_pt, + ) + .is_ok()); + + assert!(open( + &SecretKey::from_slice(&[0u8; 32]).unwrap(), + &Nonce::from_slice(&[0u8; 12]).unwrap(), + &dst_out_ct, + None, + &mut dst_out_pt_0, + ) + .is_err()); + + assert!(open( + &SecretKey::from_slice(&[0u8; 32]).unwrap(), + &Nonce::from_slice(&[0u8; 12]).unwrap(), + &dst_out_ct, + None, + &mut dst_out_pt_less, + ) + .is_err()); + + assert!(open( + &SecretKey::from_slice(&[0u8; 32]).unwrap(), + &Nonce::from_slice(&[0u8; 12]).unwrap(), + &dst_out_ct, + None, + &mut dst_out_pt_more, + ) + .is_ok()); + } + } - assert!(process_authentication(&mut poly1305_state, &[0u8; 0], &[0u8; 64], 65).is_err()); + // Proptests. Only exectued when NOT testing no_std. + #[cfg(not(feature = "no_std"))] + mod proptest { + use super::*; + + // Only return true if both a and b are true. + fn check_all_true(a: bool, b: bool) -> bool { + (a == true) && (b == true) + } + + quickcheck! { + // Sealing input, and then opening should always yield the same input. + fn prop_seal_open_same_input(input: Vec, ad: Vec) -> bool { + let pt = if input.is_empty() { + vec![1u8; 10] + } else { + input + }; + + let mut dst_out_ct_no_ad = vec![0u8; pt.len() + POLY1305_BLOCKSIZE]; + let mut dst_out_pt_no_ad = vec![0u8; pt.len()]; + + let mut dst_out_ct_with_ad = vec![0u8; pt.len() + POLY1305_BLOCKSIZE]; + let mut dst_out_pt_with_ad = vec![0u8; pt.len()]; + + seal( + &SecretKey::from_slice(&[0u8; 32]).unwrap(), + &Nonce::from_slice(&[0u8; 12]).unwrap(), + &pt[..], + None, + &mut dst_out_ct_no_ad, + ).unwrap(); + + open( + &SecretKey::from_slice(&[0u8; 32]).unwrap(), + &Nonce::from_slice(&[0u8; 12]).unwrap(), + &dst_out_ct_no_ad[..], + None, + &mut dst_out_pt_no_ad, + ).unwrap(); + + seal( + &SecretKey::from_slice(&[0u8; 32]).unwrap(), + &Nonce::from_slice(&[0u8; 12]).unwrap(), + &pt[..], + Some(&ad[..]), + &mut dst_out_ct_with_ad, + ).unwrap(); + + open( + &SecretKey::from_slice(&[0u8; 32]).unwrap(), + &Nonce::from_slice(&[0u8; 12]).unwrap(), + &dst_out_ct_with_ad[..], + Some(&ad[..]), + &mut dst_out_pt_with_ad, + ).unwrap(); + + check_all_true(dst_out_pt_no_ad == pt, dst_out_pt_with_ad == pt) + } + } + + quickcheck! { + // Sealing input, modifying the tag and then opening should + // always fail due to authentication. + fn prop_fail_on_bad_auth_tag(input: Vec, ad: Vec) -> bool { + let pt = if input.is_empty() { + vec![1u8; 10] + } else { + input + }; + + let mut dst_out_ct_no_ad = vec![0u8; pt.len() + POLY1305_BLOCKSIZE]; + let mut dst_out_pt_no_ad = vec![0u8; pt.len()]; + + let mut dst_out_ct_with_ad = vec![0u8; pt.len() + POLY1305_BLOCKSIZE]; + let mut dst_out_pt_with_ad = vec![0u8; pt.len()]; + + seal( + &SecretKey::from_slice(&[0u8; 32]).unwrap(), + &Nonce::from_slice(&[0u8; 12]).unwrap(), + &pt[..], + None, + &mut dst_out_ct_no_ad, + ).unwrap(); + + // Modify tags first byte + dst_out_ct_no_ad[pt.len() + 1] ^= 1; + + let res0 = if open( + &SecretKey::from_slice(&[0u8; 32]).unwrap(), + &Nonce::from_slice(&[0u8; 12]).unwrap(), + &dst_out_ct_no_ad[..], + None, + &mut dst_out_pt_no_ad, + ).is_err() { + true + } else { + false + }; + + seal( + &SecretKey::from_slice(&[0u8; 32]).unwrap(), + &Nonce::from_slice(&[0u8; 12]).unwrap(), + &pt[..], + Some(&ad[..]), + &mut dst_out_ct_with_ad, + ).unwrap(); + + // Modify tags first byte + dst_out_ct_with_ad[pt.len() + 1] ^= 1; + + let res1 = if open( + &SecretKey::from_slice(&[0u8; 32]).unwrap(), + &Nonce::from_slice(&[0u8; 12]).unwrap(), + &dst_out_ct_with_ad[..], + Some(&ad[..]), + &mut dst_out_pt_with_ad, + ).is_err() { + true + } else { + false + }; + + check_all_true(res0, res1) + } + } + + quickcheck! { + // Sealing input, modifying the ciphertext and then opening should + // always fail due to authentication. + fn prop_fail_on_bad_ciphertext(input: Vec, ad: Vec) -> bool { + let pt = if input.is_empty() { + vec![1u8; 10] + } else { + input + }; + + let mut dst_out_ct_no_ad = vec![0u8; pt.len() + POLY1305_BLOCKSIZE]; + let mut dst_out_pt_no_ad = vec![0u8; pt.len()]; + + let mut dst_out_ct_with_ad = vec![0u8; pt.len() + POLY1305_BLOCKSIZE]; + let mut dst_out_pt_with_ad = vec![0u8; pt.len()]; + + seal( + &SecretKey::from_slice(&[0u8; 32]).unwrap(), + &Nonce::from_slice(&[0u8; 12]).unwrap(), + &pt[..], + None, + &mut dst_out_ct_no_ad, + ).unwrap(); + + // Modify ciphertexts first byte + dst_out_ct_no_ad[0] ^= 1; + + let res0 = if open( + &SecretKey::from_slice(&[0u8; 32]).unwrap(), + &Nonce::from_slice(&[0u8; 12]).unwrap(), + &dst_out_ct_no_ad[..], + None, + &mut dst_out_pt_no_ad, + ).is_err() { + true + } else { + false + }; + + seal( + &SecretKey::from_slice(&[0u8; 32]).unwrap(), + &Nonce::from_slice(&[0u8; 12]).unwrap(), + &pt[..], + Some(&ad[..]), + &mut dst_out_ct_with_ad, + ).unwrap(); + + // Modify tags first byte + dst_out_ct_with_ad[0] ^= 1; + + let res1 = if open( + &SecretKey::from_slice(&[0u8; 32]).unwrap(), + &Nonce::from_slice(&[0u8; 12]).unwrap(), + &dst_out_ct_with_ad[..], + Some(&ad[..]), + &mut dst_out_pt_with_ad, + ).is_err() { + true + } else { + false + }; + + check_all_true(res0, res1) + } + } + } } -#[test] -fn test_auth_process_ok_index_length() { - let poly1305_key = poly1305_key_gen(&[0u8; 32], &[0u8; 12]).unwrap(); - let mut poly1305_state = poly1305::init(&poly1305_key); +// Testing private functions in the module. +#[cfg(test)] +mod private { + use super::*; + // One function tested per submodule. + + mod test_poly1305_key_gen { + use super::*; + use crate::hazardous::constants::{CHACHA_KEYSIZE, IETF_CHACHA_NONCESIZE}; + + #[test] + fn test_key_lengths() { + assert!( + poly1305_key_gen(&[0u8; CHACHA_KEYSIZE], &[0u8; IETF_CHACHA_NONCESIZE]).is_ok() + ); + assert!( + poly1305_key_gen(&[0u8; CHACHA_KEYSIZE - 1], &[0u8; IETF_CHACHA_NONCESIZE]) + .is_err() + ); + assert!( + poly1305_key_gen(&[0u8; CHACHA_KEYSIZE + 1], &[0u8; IETF_CHACHA_NONCESIZE]) + .is_err() + ); + assert!(poly1305_key_gen(&[0u8; 0], &[0u8; IETF_CHACHA_NONCESIZE]).is_err()); + } + + #[test] + fn test_nonce_lengths() { + assert!( + poly1305_key_gen(&[0u8; CHACHA_KEYSIZE], &[0u8; IETF_CHACHA_NONCESIZE]).is_ok() + ); + assert!( + poly1305_key_gen(&[0u8; CHACHA_KEYSIZE], &[0u8; IETF_CHACHA_NONCESIZE - 1]) + .is_err() + ); + assert!( + poly1305_key_gen(&[0u8; CHACHA_KEYSIZE], &[0u8; IETF_CHACHA_NONCESIZE + 1]) + .is_err() + ); + assert!(poly1305_key_gen(&[0u8; CHACHA_KEYSIZE], &[0u8; 0]).is_err()); + } + } - process_authentication(&mut poly1305_state, &[0u8; 0], &[0u8; 64], 64).unwrap(); + mod test_padding { + use super::*; + #[test] + fn test_length_padding() { + assert_eq!(padding(&[0u8; 0]), 0); + assert_eq!(padding(&[0u8; 1]), 15); + assert_eq!(padding(&[0u8; 2]), 14); + assert_eq!(padding(&[0u8; 3]), 13); + assert_eq!(padding(&[0u8; 4]), 12); + assert_eq!(padding(&[0u8; 5]), 11); + assert_eq!(padding(&[0u8; 6]), 10); + assert_eq!(padding(&[0u8; 7]), 9); + assert_eq!(padding(&[0u8; 8]), 8); + assert_eq!(padding(&[0u8; 9]), 7); + assert_eq!(padding(&[0u8; 10]), 6); + assert_eq!(padding(&[0u8; 11]), 5); + assert_eq!(padding(&[0u8; 12]), 4); + assert_eq!(padding(&[0u8; 13]), 3); + assert_eq!(padding(&[0u8; 14]), 2); + assert_eq!(padding(&[0u8; 15]), 1); + assert_eq!(padding(&[0u8; 16]), 0); + } + + // Proptests. Only exectued when NOT testing no_std. + #[cfg(not(feature = "no_std"))] + mod proptest { + use super::*; + + quickcheck! { + // The usize that padding() returns should always + // be what remains to make input a multiple of 16 in length. + fn prop_padding_result(input: Vec) -> bool { + let rem = padding(&input[..]); + + (((input.len() + rem) % 16) == 0) + } + } + + quickcheck! { + // padding() should never return a usize above 15. + // The usize must always be in range of 0..=15. + fn prop_result_never_above_15(input: Vec) -> bool { + let rem: usize = padding(&input[..]); + + (rem < 16) + } + } + } + } - process_authentication(&mut poly1305_state, &[0u8; 0], &[0u8; 64], 0).unwrap(); -} + mod test_process_authentication { + use super::*; -#[test] -fn test_nonce_sizes() { - assert!(Nonce::from_slice(&[0u8; 11]).is_err()); - assert!(Nonce::from_slice(&[0u8; 13]).is_err()); - assert!(Nonce::from_slice(&[0u8; 12]).is_ok()); -} + #[test] + #[should_panic] + fn test_panic_index_0() { + let poly1305_key = poly1305_key_gen(&[0u8; 32], &[0u8; 12]).unwrap(); + let mut poly1305_state = poly1305::init(&poly1305_key); -#[test] -fn test_modified_tag_error() { - let mut dst_out_ct = [0u8; 80]; // 64 + Poly1305TagLen - let mut dst_out_pt = [0u8; 64]; - - seal( - &SecretKey::from_slice(&[0u8; 32]).unwrap(), - &Nonce::from_slice(&[0u8; 12]).unwrap(), - &[0u8; 64], - None, - &mut dst_out_ct, - ) - .unwrap(); - // Modify the tags first byte - dst_out_ct[65] ^= 1; - assert!(open( - &SecretKey::from_slice(&[0u8; 32]).unwrap(), - &Nonce::from_slice(&[0u8; 12]).unwrap(), - &dst_out_ct, - None, - &mut dst_out_pt, - ) - .is_err()); -} + process_authentication(&mut poly1305_state, &[0u8; 0], &[0u8; 64], 0).unwrap(); + } -#[test] -fn test_bad_pt_ct_lengths() { - let mut dst_out_ct_1 = [0u8; 79]; // 64 + Poly1305TagLen = 80 - let mut dst_out_ct_2 = [0u8; 80]; // 64 + Poly1305TagLen = 80 - - let mut dst_out_pt_1 = [0u8; 63]; - let mut dst_out_pt_2 = [0u8; 64]; - - assert!(seal( - &SecretKey::from_slice(&[0u8; 32]).unwrap(), - &Nonce::from_slice(&[0u8; 12]).unwrap(), - &dst_out_pt_2, - Some(&[0u8; 5]), - &mut dst_out_ct_1, - ) - .is_err()); - - seal( - &SecretKey::from_slice(&[0u8; 32]).unwrap(), - &Nonce::from_slice(&[0u8; 12]).unwrap(), - &dst_out_pt_2, - Some(&[0u8; 5]), - &mut dst_out_ct_2, - ) - .unwrap(); - - assert!(open( - &SecretKey::from_slice(&[0u8; 32]).unwrap(), - &Nonce::from_slice(&[0u8; 12]).unwrap(), - &dst_out_ct_2, - Some(&[0u8; 5]), - &mut dst_out_pt_1, - ) - .is_err()); - - open( - &SecretKey::from_slice(&[0u8; 32]).unwrap(), - &Nonce::from_slice(&[0u8; 12]).unwrap(), - &dst_out_ct_2, - Some(&[0u8; 5]), - &mut dst_out_pt_2, - ) - .unwrap(); -} + #[test] + #[should_panic] + fn test_panic_empty_buf() { + let poly1305_key = poly1305_key_gen(&[0u8; 32], &[0u8; 12]).unwrap(); + let mut poly1305_state = poly1305::init(&poly1305_key); -#[test] -fn test_bad_ct_length_and_empty_out_decrypt() { - let dst_out_ct_1 = [0u8; POLY1305_BLOCKSIZE]; - let dst_out_ct_2 = [0u8; POLY1305_BLOCKSIZE - 1]; - let dst_out_ct_3 = [0u8; POLY1305_BLOCKSIZE + 1]; - - let mut dst_out_pt_1 = [0u8; 64]; - let mut dst_out_pt_2 = [0u8; 0]; - - assert!(open( - &SecretKey::from_slice(&[0u8; 32]).unwrap(), - &Nonce::from_slice(&[0u8; 12]).unwrap(), - &dst_out_ct_1, - None, - &mut dst_out_pt_1, - ) - .is_err()); - - assert!(open( - &SecretKey::from_slice(&[0u8; 32]).unwrap(), - &Nonce::from_slice(&[0u8; 12]).unwrap(), - &dst_out_ct_2, - None, - &mut dst_out_pt_1, - ) - .is_err()); - - assert!(open( - &SecretKey::from_slice(&[0u8; 32]).unwrap(), - &Nonce::from_slice(&[0u8; 12]).unwrap(), - &dst_out_ct_3, - None, - &mut dst_out_pt_2, - ) - .is_err()); -} + process_authentication(&mut poly1305_state, &[0u8; 0], &[0u8; 0], 64).unwrap(); + } -#[test] -fn rfc_8439_test_poly1305_key_gen_1() { - let key = [0u8; 32]; - let nonce = [ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - ]; - let expected = [ - 0x76, 0xb8, 0xe0, 0xad, 0xa0, 0xf1, 0x3d, 0x90, 0x40, 0x5d, 0x6a, 0xe5, 0x53, 0x86, 0xbd, - 0x28, 0xbd, 0xd2, 0x19, 0xb8, 0xa0, 0x8d, 0xed, 0x1a, 0xa8, 0x36, 0xef, 0xcc, 0x8b, 0x77, - 0x0d, 0xc7, - ]; - - assert_eq!( - poly1305_key_gen(&key, &nonce) - .unwrap() - .unprotected_as_bytes(), - expected.as_ref() - ); -} + #[test] + #[should_panic] + fn test_panic_above_length_index() { + let poly1305_key = poly1305_key_gen(&[0u8; 32], &[0u8; 12]).unwrap(); + let mut poly1305_state = poly1305::init(&poly1305_key); -#[test] -fn rfc_8439_test_poly1305_key_gen_2() { - let key = [ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x01, - ]; - let nonce = [ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, - ]; - let expected = [ - 0xec, 0xfa, 0x25, 0x4f, 0x84, 0x5f, 0x64, 0x74, 0x73, 0xd3, 0xcb, 0x14, 0x0d, 0xa9, 0xe8, - 0x76, 0x06, 0xcb, 0x33, 0x06, 0x6c, 0x44, 0x7b, 0x87, 0xbc, 0x26, 0x66, 0xdd, 0xe3, 0xfb, - 0xb7, 0x39, - ]; - - assert_eq!( - poly1305_key_gen(&key, &nonce) - .unwrap() - .unprotected_as_bytes(), - expected.as_ref() - ); -} + process_authentication(&mut poly1305_state, &[0u8; 0], &[0u8; 64], 65).unwrap(); + } + + #[test] + fn test_length_index() { + let poly1305_key = poly1305_key_gen(&[0u8; 32], &[0u8; 12]).unwrap(); + let mut poly1305_state = poly1305::init(&poly1305_key); + + assert!(process_authentication(&mut poly1305_state, &[0u8; 0], &[0u8; 64], 64).is_ok()); + + assert!(process_authentication(&mut poly1305_state, &[0u8; 0], &[0u8; 64], 63).is_ok()); + + assert!(process_authentication(&mut poly1305_state, &[0u8; 0], &[0u8; 64], 1).is_ok()); -#[test] -fn rfc_8439_test_poly1305_key_gen_3() { - let key = [ - 0x1c, 0x92, 0x40, 0xa5, 0xeb, 0x55, 0xd3, 0x8a, 0xf3, 0x33, 0x88, 0x86, 0x04, 0xf6, 0xb5, - 0xf0, 0x47, 0x39, 0x17, 0xc1, 0x40, 0x2b, 0x80, 0x09, 0x9d, 0xca, 0x5c, 0xbc, 0x20, 0x70, - 0x75, 0xc0, - ]; - let nonce = [ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, - ]; - let expected = [ - 0x96, 0x5e, 0x3b, 0xc6, 0xf9, 0xec, 0x7e, 0xd9, 0x56, 0x08, 0x08, 0xf4, 0xd2, 0x29, 0xf9, - 0x4b, 0x13, 0x7f, 0xf2, 0x75, 0xca, 0x9b, 0x3f, 0xcb, 0xdd, 0x59, 0xde, 0xaa, 0xd2, 0x33, - 0x10, 0xae, - ]; - - assert_eq!( - poly1305_key_gen(&key, &nonce) - .unwrap() - .unprotected_as_bytes(), - expected.as_ref() - ); + assert!(process_authentication(&mut poly1305_state, &[0u8; 0], &[0u8; 1], 1).is_ok()); + } + } } -#[test] -fn regression_detect_bigger_than_slice_bug() { - let pt = [0x5B; 79]; - - let mut dst_out_ct = [0u8; 79 + (POLY1305_BLOCKSIZE * 2)]; - - seal( - &SecretKey::from_slice(&[0u8; 32]).unwrap(), - &Nonce::from_slice(&[0u8; 12]).unwrap(), - &pt[..], - None, - &mut dst_out_ct, - ) - .unwrap(); - - // Verify that using a slice that is bigger than produces the exact same - // output as using a slice that is the exact required length - let mut dst_out_ct_2 = [0u8; 79 + POLY1305_BLOCKSIZE]; - - seal( - &SecretKey::from_slice(&[0u8; 32]).unwrap(), - &Nonce::from_slice(&[0u8; 12]).unwrap(), - &pt[..], - None, - &mut dst_out_ct_2, - ) - .unwrap(); - - assert!(dst_out_ct[..dst_out_ct_2.len()] == dst_out_ct_2[..]); +// Testing any test vectors that aren't put into library's /tests folder. +#[cfg(test)] +mod test_vectors { + use super::*; + + #[test] + fn rfc8439_poly1305_key_gen_1() { + let key = [0u8; 32]; + let nonce = [ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + ]; + let expected = [ + 0x76, 0xb8, 0xe0, 0xad, 0xa0, 0xf1, 0x3d, 0x90, 0x40, 0x5d, 0x6a, 0xe5, 0x53, 0x86, + 0xbd, 0x28, 0xbd, 0xd2, 0x19, 0xb8, 0xa0, 0x8d, 0xed, 0x1a, 0xa8, 0x36, 0xef, 0xcc, + 0x8b, 0x77, 0x0d, 0xc7, + ]; + + assert_eq!( + poly1305_key_gen(&key, &nonce) + .unwrap() + .unprotected_as_bytes(), + expected.as_ref() + ); + } + + #[test] + fn rfc8439_poly1305_key_gen_2() { + let key = [ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, + ]; + let nonce = [ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + ]; + let expected = [ + 0xec, 0xfa, 0x25, 0x4f, 0x84, 0x5f, 0x64, 0x74, 0x73, 0xd3, 0xcb, 0x14, 0x0d, 0xa9, + 0xe8, 0x76, 0x06, 0xcb, 0x33, 0x06, 0x6c, 0x44, 0x7b, 0x87, 0xbc, 0x26, 0x66, 0xdd, + 0xe3, 0xfb, 0xb7, 0x39, + ]; + + assert_eq!( + poly1305_key_gen(&key, &nonce) + .unwrap() + .unprotected_as_bytes(), + expected.as_ref() + ); + } + + #[test] + fn rfc8439_poly1305_key_gen_3() { + let key = [ + 0x1c, 0x92, 0x40, 0xa5, 0xeb, 0x55, 0xd3, 0x8a, 0xf3, 0x33, 0x88, 0x86, 0x04, 0xf6, + 0xb5, 0xf0, 0x47, 0x39, 0x17, 0xc1, 0x40, 0x2b, 0x80, 0x09, 0x9d, 0xca, 0x5c, 0xbc, + 0x20, 0x70, 0x75, 0xc0, + ]; + let nonce = [ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + ]; + let expected = [ + 0x96, 0x5e, 0x3b, 0xc6, 0xf9, 0xec, 0x7e, 0xd9, 0x56, 0x08, 0x08, 0xf4, 0xd2, 0x29, + 0xf9, 0x4b, 0x13, 0x7f, 0xf2, 0x75, 0xca, 0x9b, 0x3f, 0xcb, 0xdd, 0x59, 0xde, 0xaa, + 0xd2, 0x33, 0x10, 0xae, + ]; + + assert_eq!( + poly1305_key_gen(&key, &nonce) + .unwrap() + .unprotected_as_bytes(), + expected.as_ref() + ); + } } diff --git a/src/lib.rs b/src/lib.rs index 703a5be2..1141f791 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -56,6 +56,10 @@ extern crate rand_os; extern crate subtle; extern crate tiny_keccak; +#[cfg(test)] +#[macro_use] +extern crate quickcheck; + #[macro_use] mod typedefs; From bbf564092e38c231fc2cdcf699cea0144ef200a4 Mon Sep 17 00:00:00 2001 From: brycx Date: Mon, 28 Jan 2019 16:35:51 +0100 Subject: [PATCH 02/40] Tests: Test xchacha20poly1305 with the same public test module as chacha20poly1305 except for using a bigger nonce --- src/hazardous/aead/chacha20poly1305.rs | 7 +- src/hazardous/aead/xchacha20poly1305.rs | 449 +++++++++++++++++++++--- 2 files changed, 399 insertions(+), 57 deletions(-) diff --git a/src/hazardous/aead/chacha20poly1305.rs b/src/hazardous/aead/chacha20poly1305.rs index 723a1381..65efab1e 100644 --- a/src/hazardous/aead/chacha20poly1305.rs +++ b/src/hazardous/aead/chacha20poly1305.rs @@ -109,6 +109,7 @@ use crate::{ use byteorder::{ByteOrder, LittleEndian}; #[must_use] +#[inline(always)] /// Poly1305 key generation using IETF ChaCha20. fn poly1305_key_gen(key: &[u8], nonce: &[u8]) -> Result { let one_time_key = OneTimeKey::from_slice( @@ -123,6 +124,7 @@ fn poly1305_key_gen(key: &[u8], nonce: &[u8]) -> Result usize { @@ -134,6 +136,7 @@ fn padding(input: &[u8]) -> usize { } #[must_use] +#[inline(always)] /// Process data to be authenticated using a `Poly1305` struct initialized with /// a one-time-key. Up to `buf_in_len` data in `buf` get's authenticated. The /// indexing is needed because authentication happens on different input lenghts @@ -456,9 +459,7 @@ mod public { use super::*; // Only return true if both a and b are true. - fn check_all_true(a: bool, b: bool) -> bool { - (a == true) && (b == true) - } + fn check_all_true(a: bool, b: bool) -> bool { (a == true) && (b == true) } quickcheck! { // Sealing input, and then opening should always yield the same input. diff --git a/src/hazardous/aead/xchacha20poly1305.rs b/src/hazardous/aead/xchacha20poly1305.rs index c84156d3..881dd28d 100644 --- a/src/hazardous/aead/xchacha20poly1305.rs +++ b/src/hazardous/aead/xchacha20poly1305.rs @@ -147,58 +147,399 @@ pub fn open( Ok(()) } -#[test] -fn test_modified_tag_error() { - let mut dst_out_ct = [0u8; 80]; // 64 + Poly1305TagLen - let mut dst_out_pt = [0u8; 64]; - - seal( - &SecretKey::from_slice(&[0u8; 32]).unwrap(), - &Nonce::from_slice(&[0u8; 24]).unwrap(), - &[0u8; 64], - None, - &mut dst_out_ct, - ) - .unwrap(); - // Modify the tags first byte - dst_out_ct[65] ^= 1; - assert!(open( - &SecretKey::from_slice(&[0u8; 32]).unwrap(), - &Nonce::from_slice(&[0u8; 24]).unwrap(), - &dst_out_ct, - None, - &mut dst_out_pt, - ) - .is_err()); -} +// +// The tests below are the same tests as the ones in `chacha20poly1305` +// but with a bigger nonce. It's debatable whether this is needed, but right +// now I'm keeping them as they don't seem to bring any disadvantages. +// -#[test] -fn regression_detect_bigger_than_slice_bug() { - let pt = [0x5B; 79]; - - let mut dst_out_ct = [0u8; 79 + (16 * 2)]; - - seal( - &SecretKey::from_slice(&[0u8; 32]).unwrap(), - &Nonce::from_slice(&[0u8; 24]).unwrap(), - &pt[..], - None, - &mut dst_out_ct, - ) - .unwrap(); - - // Verify that using a slice that is bigger than produces the exact same - // output as using a slice that is the exact required length - let mut dst_out_ct_2 = [0u8; 79 + 16]; - - seal( - &SecretKey::from_slice(&[0u8; 32]).unwrap(), - &Nonce::from_slice(&[0u8; 24]).unwrap(), - &pt[..], - None, - &mut dst_out_ct_2, - ) - .unwrap(); - - assert!(dst_out_ct[..dst_out_ct_2.len()] == dst_out_ct_2[..]); -} +// Testing public functions in the module. +#[cfg(test)] +mod public { + use super::*; + use crate::hazardous::constants::POLY1305_BLOCKSIZE; + // One function tested per submodule. + + mod test_seal { + use super::*; + + #[test] + fn test_dst_out_length() { + let mut dst_out_ct = [0u8; 80]; // 64 + Poly1305TagLen + let mut dst_out_ct_less = [0u8; 79]; // 64 + Poly1305TagLen - 1 + let mut dst_out_ct_more = [0u8; 81]; // 64 + Poly1305TagLen + 1 + let mut dst_out_ct_more_2 = [0u8; 64 + (POLY1305_BLOCKSIZE * 2)]; + + assert!(seal( + &SecretKey::from_slice(&[0u8; 32]).unwrap(), + &Nonce::from_slice(&[0u8; 24]).unwrap(), + &[0u8; 64], + None, + &mut dst_out_ct, + ) + .is_ok()); + + assert!(seal( + &SecretKey::from_slice(&[0u8; 32]).unwrap(), + &Nonce::from_slice(&[0u8; 24]).unwrap(), + &[0u8; 64], + None, + &mut dst_out_ct_more, + ) + .is_ok()); + + assert!(seal( + &SecretKey::from_slice(&[0u8; 32]).unwrap(), + &Nonce::from_slice(&[0u8; 24]).unwrap(), + &[0u8; 64], + None, + &mut dst_out_ct_more_2, + ) + .is_ok()); + + assert!(seal( + &SecretKey::from_slice(&[0u8; 32]).unwrap(), + &Nonce::from_slice(&[0u8; 24]).unwrap(), + &[0u8; 64], + None, + &mut dst_out_ct_less, + ) + .is_err()); + } + + #[test] + fn test_plaintext_length() { + let mut dst_out_ct_0 = [0u8; 16]; // 0 + Poly1305TagLen + let mut dst_out_ct_1 = [0u8; 17]; // 1 + Poly1305TagLen + let mut dst_out_ct_128 = [0u8; 144]; // 128 + Poly1305TagLen + + assert!(seal( + &SecretKey::from_slice(&[0u8; 32]).unwrap(), + &Nonce::from_slice(&[0u8; 24]).unwrap(), + &[0u8; 0], + None, + &mut dst_out_ct_0, + ) + .is_err()); + + assert!(seal( + &SecretKey::from_slice(&[0u8; 32]).unwrap(), + &Nonce::from_slice(&[0u8; 24]).unwrap(), + &[0u8; 1], + None, + &mut dst_out_ct_1, + ) + .is_ok()); + + assert!(seal( + &SecretKey::from_slice(&[0u8; 32]).unwrap(), + &Nonce::from_slice(&[0u8; 24]).unwrap(), + &[0u8; 128], + None, + &mut dst_out_ct_128, + ) + .is_ok()); + } + } + + mod test_open { + use super::*; + + #[test] + fn test_ciphertext_with_tag_length() { + let mut dst_out_pt = [0u8; 64]; + + assert!(open( + &SecretKey::from_slice(&[0u8; 32]).unwrap(), + &Nonce::from_slice(&[0u8; 24]).unwrap(), + &[0u8; 0], + None, + &mut dst_out_pt, + ) + .is_err()); + + assert!(open( + &SecretKey::from_slice(&[0u8; 32]).unwrap(), + &Nonce::from_slice(&[0u8; 24]).unwrap(), + &[0u8; POLY1305_BLOCKSIZE], + None, + &mut dst_out_pt, + ) + .is_err()); + + assert!(open( + &SecretKey::from_slice(&[0u8; 32]).unwrap(), + &Nonce::from_slice(&[0u8; 24]).unwrap(), + &[0u8; POLY1305_BLOCKSIZE - 1], + None, + &mut dst_out_pt, + ) + .is_err()); + + let mut dst_out_ct = [0u8; 64 + 16]; + seal( + &SecretKey::from_slice(&[0u8; 32]).unwrap(), + &Nonce::from_slice(&[0u8; 24]).unwrap(), + &[0u8; POLY1305_BLOCKSIZE + 1], + None, + &mut dst_out_ct, + ) + .unwrap(); + + assert!(open( + &SecretKey::from_slice(&[0u8; 32]).unwrap(), + &Nonce::from_slice(&[0u8; 24]).unwrap(), + &dst_out_ct[..(POLY1305_BLOCKSIZE + 1) + 16], + None, + &mut dst_out_pt, + ) + .is_ok()); + } + + #[test] + fn test_dst_out_length() { + let mut dst_out_ct = [0u8; 64 + 16]; + seal( + &SecretKey::from_slice(&[0u8; 32]).unwrap(), + &Nonce::from_slice(&[0u8; 24]).unwrap(), + &[0u8; 64], + None, + &mut dst_out_ct, + ) + .unwrap(); + + let mut dst_out_pt = [0u8; 64]; + let mut dst_out_pt_0 = [0u8; 0]; + let mut dst_out_pt_less = [0u8; 63]; + let mut dst_out_pt_more = [0u8; 65]; + + assert!(open( + &SecretKey::from_slice(&[0u8; 32]).unwrap(), + &Nonce::from_slice(&[0u8; 24]).unwrap(), + &dst_out_ct, + None, + &mut dst_out_pt, + ) + .is_ok()); + + assert!(open( + &SecretKey::from_slice(&[0u8; 32]).unwrap(), + &Nonce::from_slice(&[0u8; 24]).unwrap(), + &dst_out_ct, + None, + &mut dst_out_pt_0, + ) + .is_err()); + + assert!(open( + &SecretKey::from_slice(&[0u8; 32]).unwrap(), + &Nonce::from_slice(&[0u8; 24]).unwrap(), + &dst_out_ct, + None, + &mut dst_out_pt_less, + ) + .is_err()); + + assert!(open( + &SecretKey::from_slice(&[0u8; 32]).unwrap(), + &Nonce::from_slice(&[0u8; 24]).unwrap(), + &dst_out_ct, + None, + &mut dst_out_pt_more, + ) + .is_ok()); + } + } + + // Proptests. Only exectued when NOT testing no_std. + #[cfg(not(feature = "no_std"))] + mod proptest { + use super::*; + + // Only return true if both a and b are true. + fn check_all_true(a: bool, b: bool) -> bool { (a == true) && (b == true) } + + quickcheck! { + // Sealing input, and then opening should always yield the same input. + fn prop_seal_open_same_input(input: Vec, ad: Vec) -> bool { + let pt = if input.is_empty() { + vec![1u8; 10] + } else { + input + }; + + let mut dst_out_ct_no_ad = vec![0u8; pt.len() + POLY1305_BLOCKSIZE]; + let mut dst_out_pt_no_ad = vec![0u8; pt.len()]; + + let mut dst_out_ct_with_ad = vec![0u8; pt.len() + POLY1305_BLOCKSIZE]; + let mut dst_out_pt_with_ad = vec![0u8; pt.len()]; + + seal( + &SecretKey::from_slice(&[0u8; 32]).unwrap(), + &Nonce::from_slice(&[0u8; 24]).unwrap(), + &pt[..], + None, + &mut dst_out_ct_no_ad, + ).unwrap(); + + open( + &SecretKey::from_slice(&[0u8; 32]).unwrap(), + &Nonce::from_slice(&[0u8; 24]).unwrap(), + &dst_out_ct_no_ad[..], + None, + &mut dst_out_pt_no_ad, + ).unwrap(); + + seal( + &SecretKey::from_slice(&[0u8; 32]).unwrap(), + &Nonce::from_slice(&[0u8; 24]).unwrap(), + &pt[..], + Some(&ad[..]), + &mut dst_out_ct_with_ad, + ).unwrap(); + + open( + &SecretKey::from_slice(&[0u8; 32]).unwrap(), + &Nonce::from_slice(&[0u8; 24]).unwrap(), + &dst_out_ct_with_ad[..], + Some(&ad[..]), + &mut dst_out_pt_with_ad, + ).unwrap(); + + check_all_true(dst_out_pt_no_ad == pt, dst_out_pt_with_ad == pt) + } + } + + quickcheck! { + // Sealing input, modifying the tag and then opening should + // always fail due to authentication. + fn prop_fail_on_bad_auth_tag(input: Vec, ad: Vec) -> bool { + let pt = if input.is_empty() { + vec![1u8; 10] + } else { + input + }; + + let mut dst_out_ct_no_ad = vec![0u8; pt.len() + POLY1305_BLOCKSIZE]; + let mut dst_out_pt_no_ad = vec![0u8; pt.len()]; + + let mut dst_out_ct_with_ad = vec![0u8; pt.len() + POLY1305_BLOCKSIZE]; + let mut dst_out_pt_with_ad = vec![0u8; pt.len()]; + + seal( + &SecretKey::from_slice(&[0u8; 32]).unwrap(), + &Nonce::from_slice(&[0u8; 24]).unwrap(), + &pt[..], + None, + &mut dst_out_ct_no_ad, + ).unwrap(); + + // Modify tags first byte + dst_out_ct_no_ad[pt.len() + 1] ^= 1; + + let res0 = if open( + &SecretKey::from_slice(&[0u8; 32]).unwrap(), + &Nonce::from_slice(&[0u8; 24]).unwrap(), + &dst_out_ct_no_ad[..], + None, + &mut dst_out_pt_no_ad, + ).is_err() { + true + } else { + false + }; + + seal( + &SecretKey::from_slice(&[0u8; 32]).unwrap(), + &Nonce::from_slice(&[0u8; 24]).unwrap(), + &pt[..], + Some(&ad[..]), + &mut dst_out_ct_with_ad, + ).unwrap(); + + // Modify tags first byte + dst_out_ct_with_ad[pt.len() + 1] ^= 1; + + let res1 = if open( + &SecretKey::from_slice(&[0u8; 32]).unwrap(), + &Nonce::from_slice(&[0u8; 24]).unwrap(), + &dst_out_ct_with_ad[..], + Some(&ad[..]), + &mut dst_out_pt_with_ad, + ).is_err() { + true + } else { + false + }; + + check_all_true(res0, res1) + } + } + + quickcheck! { + // Sealing input, modifying the ciphertext and then opening should + // always fail due to authentication. + fn prop_fail_on_bad_ciphertext(input: Vec, ad: Vec) -> bool { + let pt = if input.is_empty() { + vec![1u8; 10] + } else { + input + }; + + let mut dst_out_ct_no_ad = vec![0u8; pt.len() + POLY1305_BLOCKSIZE]; + let mut dst_out_pt_no_ad = vec![0u8; pt.len()]; + + let mut dst_out_ct_with_ad = vec![0u8; pt.len() + POLY1305_BLOCKSIZE]; + let mut dst_out_pt_with_ad = vec![0u8; pt.len()]; + + seal( + &SecretKey::from_slice(&[0u8; 32]).unwrap(), + &Nonce::from_slice(&[0u8; 24]).unwrap(), + &pt[..], + None, + &mut dst_out_ct_no_ad, + ).unwrap(); + + // Modify ciphertexts first byte + dst_out_ct_no_ad[0] ^= 1; + + let res0 = if open( + &SecretKey::from_slice(&[0u8; 32]).unwrap(), + &Nonce::from_slice(&[0u8; 24]).unwrap(), + &dst_out_ct_no_ad[..], + None, + &mut dst_out_pt_no_ad, + ).is_err() { + true + } else { + false + }; + + seal( + &SecretKey::from_slice(&[0u8; 32]).unwrap(), + &Nonce::from_slice(&[0u8; 24]).unwrap(), + &pt[..], + Some(&ad[..]), + &mut dst_out_ct_with_ad, + ).unwrap(); + + // Modify tags first byte + dst_out_ct_with_ad[0] ^= 1; + + let res1 = if open( + &SecretKey::from_slice(&[0u8; 32]).unwrap(), + &Nonce::from_slice(&[0u8; 24]).unwrap(), + &dst_out_ct_with_ad[..], + Some(&ad[..]), + &mut dst_out_pt_with_ad, + ).is_err() { + true + } else { + false + }; + + check_all_true(res0, res1) + } + } + } +} \ No newline at end of file From d985be6177320f41f5d6f7385d17132c97f47cdb Mon Sep 17 00:00:00 2001 From: brycx Date: Mon, 28 Jan 2019 16:53:28 +0100 Subject: [PATCH 03/40] Tests: Setup re-organized testmodules for chacha20 --- src/hazardous/aead/xchacha20poly1305.rs | 4 +- src/hazardous/stream/chacha20.rs | 790 ++++++++++++------------ 2 files changed, 411 insertions(+), 383 deletions(-) diff --git a/src/hazardous/aead/xchacha20poly1305.rs b/src/hazardous/aead/xchacha20poly1305.rs index 881dd28d..d0cdad5b 100644 --- a/src/hazardous/aead/xchacha20poly1305.rs +++ b/src/hazardous/aead/xchacha20poly1305.rs @@ -149,7 +149,7 @@ pub fn open( // // The tests below are the same tests as the ones in `chacha20poly1305` -// but with a bigger nonce. It's debatable whether this is needed, but right +// but with a bigger nonce. It's debatable whether this is needed, but right // now I'm keeping them as they don't seem to bring any disadvantages. // @@ -542,4 +542,4 @@ mod public { } } } -} \ No newline at end of file +} diff --git a/src/hazardous/stream/chacha20.rs b/src/hazardous/stream/chacha20.rs index 8221dd55..ee5db325 100644 --- a/src/hazardous/stream/chacha20.rs +++ b/src/hazardous/stream/chacha20.rs @@ -636,416 +636,444 @@ fn init(key: &[u8], nonce: &[u8]) -> Result { Ok(chacha_state) } -#[test] -// From https://tools.ietf.org/html/rfc8439 -fn test_quarter_round_results() { - let mut chacha_state = InternalState { - state: [ - 0x11111111, 0x01020304, 0x9b8d6f43, 0x01234567, 0x11111111, 0x01020304, 0x9b8d6f43, - 0x01234567, 0x11111111, 0x01020304, 0x9b8d6f43, 0x01234567, 0x11111111, 0x01020304, - 0x9b8d6f43, 0x01234567, - ], - is_ietf: true, - }; - let expected: [u32; 4] = [0xea2a92f4, 0xcb1cf8ce, 0x4581472e, 0x5881c4bb]; - // Test all indexes - chacha_state.quarter_round(0, 1, 2, 3); - chacha_state.quarter_round(4, 5, 6, 7); - chacha_state.quarter_round(8, 9, 10, 11); - chacha_state.quarter_round(12, 13, 14, 15); - - assert_eq!(chacha_state.state[0..4], expected); - assert_eq!(chacha_state.state[4..8], expected); - assert_eq!(chacha_state.state[8..12], expected); - assert_eq!(chacha_state.state[12..16], expected); -} +// Testing public functions in the module. +#[cfg(test)] +mod public { + // One function tested per submodule. -#[test] -// From https://tools.ietf.org/html/rfc8439 -fn test_quarter_round_results_on_indices() { - let mut chacha_state = InternalState { - state: [ - 0x879531e0, 0xc5ecf37d, 0x516461b1, 0xc9a62f8a, 0x44c20ef3, 0x3390af7f, 0xd9fc690b, - 0x2a5f714c, 0x53372767, 0xb00a5631, 0x974c541a, 0x359e9963, 0x5c971061, 0x3d631689, - 0x2098d9d6, 0x91dbd320, - ], - is_ietf: true, - }; - let expected: ChaChaState = [ - 0x879531e0, 0xc5ecf37d, 0xbdb886dc, 0xc9a62f8a, 0x44c20ef3, 0x3390af7f, 0xd9fc690b, - 0xcfacafd2, 0xe46bea80, 0xb00a5631, 0x974c541a, 0x359e9963, 0x5c971061, 0xccc07c79, - 0x2098d9d6, 0x91dbd320, - ]; - - chacha_state.quarter_round(2, 7, 8, 13); - assert_eq!(chacha_state.state[..], expected); -} + mod test_encrypt {} -#[test] -// From https://tools.ietf.org/html/rfc8439 -fn test_chacha20_block_results() { - let key = [ - 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, - 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, - 0x1e, 0x1f, - ]; - let nonce = [ - 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00, 0x00, - ]; - let expected = [ - 0x10, 0xf1, 0xe7, 0xe4, 0xd1, 0x3b, 0x59, 0x15, 0x50, 0x0f, 0xdd, 0x1f, 0xa3, 0x20, 0x71, - 0xc4, 0xc7, 0xd1, 0xf4, 0xc7, 0x33, 0xc0, 0x68, 0x03, 0x04, 0x22, 0xaa, 0x9a, 0xc3, 0xd4, - 0x6c, 0x4e, 0xd2, 0x82, 0x64, 0x46, 0x07, 0x9f, 0xaa, 0x09, 0x14, 0xc2, 0xd7, 0x05, 0xd9, - 0x8b, 0x02, 0xa2, 0xb5, 0x12, 0x9c, 0xd1, 0xde, 0x16, 0x4e, 0xb9, 0xcb, 0xd0, 0x83, 0xe8, - 0xa2, 0x50, 0x3c, 0x4e, - ]; - // Test initial key-steup - let expected_init: ChaChaState = [ - 0x61707865, 0x3320646e, 0x79622d32, 0x6b206574, 0x03020100, 0x07060504, 0x0b0a0908, - 0x0f0e0d0c, 0x13121110, 0x17161514, 0x1b1a1918, 0x1f1e1d1c, 0x00000001, 0x09000000, - 0x4a000000, 0x00000000, - ]; - // Test initial key-steup - let mut state = init(&key, &nonce).unwrap(); - state.state[12] = 1_u32; - assert_eq!(state.state[..], expected_init[..]); - - let keystream_block_from_state = state.process_block(Some(1)).unwrap(); - let mut ser_block = [0u8; 64]; - state - .serialize_block(&keystream_block_from_state, &mut ser_block) - .unwrap(); + mod test_decrypt {} - let keystream_block_only = keystream_block( - &SecretKey::from_slice(&key).unwrap(), - &Nonce::from_slice(&nonce).unwrap(), - 1, - ) - .unwrap(); + mod test_keystream_block {} + + mod test_hchacha20 {} - assert_eq!(ser_block[..], expected[..]); - assert_eq!(ser_block[..], keystream_block_only[..]); + // Proptests. Only exectued when NOT testing no_std. + #[cfg(not(feature = "no_std"))] + mod proptest {} } -#[test] -// From https://tools.ietf.org/html/rfc8439 -fn chacha20_block_test_1() { - let key = [ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, - ]; - let nonce = [ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - ]; - let expected = [ - 0x76, 0xb8, 0xe0, 0xad, 0xa0, 0xf1, 0x3d, 0x90, 0x40, 0x5d, 0x6a, 0xe5, 0x53, 0x86, 0xbd, - 0x28, 0xbd, 0xd2, 0x19, 0xb8, 0xa0, 0x8d, 0xed, 0x1a, 0xa8, 0x36, 0xef, 0xcc, 0x8b, 0x77, - 0x0d, 0xc7, 0xda, 0x41, 0x59, 0x7c, 0x51, 0x57, 0x48, 0x8d, 0x77, 0x24, 0xe0, 0x3f, 0xb8, - 0xd8, 0x4a, 0x37, 0x6a, 0x43, 0xb8, 0xf4, 0x15, 0x18, 0xa1, 0x1c, 0xc3, 0x87, 0xb6, 0x69, - 0xb2, 0xee, 0x65, 0x86, - ]; - // Unserialized state - let expected_state: ChaChaState = [ - 0xade0b876, 0x903df1a0, 0xe56a5d40, 0x28bd8653, 0xb819d2bd, 0x1aed8da0, 0xccef36a8, - 0xc70d778b, 0x7c5941da, 0x8d485751, 0x3fe02477, 0x374ad8b8, 0xf4b8436a, 0x1ca11815, - 0x69b687c3, 0x8665eeb2, - ]; - - let mut state = init(&key, &nonce).unwrap(); - let keystream_block_from_state = state.process_block(Some(0)).unwrap(); - assert_eq!(keystream_block_from_state[..], expected_state[..]); - - let mut ser_block = [0u8; 64]; - state - .serialize_block(&keystream_block_from_state, &mut ser_block) - .unwrap(); +// Testing private functions in the module. +#[cfg(test)] +mod private { + // One function tested per submodule. - let keystream_block_only = keystream_block( - &SecretKey::from_slice(&key).unwrap(), - &Nonce::from_slice(&nonce).unwrap(), - 0, - ) - .unwrap(); + mod test_init_state {} - assert_eq!(ser_block[..], expected[..]); - assert_eq!(ser_block[..], keystream_block_only[..]); + mod test_process_block {} + + mod test_serialize_block {} } -#[test] -// From https://tools.ietf.org/html/rfc8439 -fn chacha20_block_test_2() { - let key = [ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, - ]; - let nonce = [ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - ]; - let expected = [ - 0x9f, 0x07, 0xe7, 0xbe, 0x55, 0x51, 0x38, 0x7a, 0x98, 0xba, 0x97, 0x7c, 0x73, 0x2d, 0x08, - 0x0d, 0xcb, 0x0f, 0x29, 0xa0, 0x48, 0xe3, 0x65, 0x69, 0x12, 0xc6, 0x53, 0x3e, 0x32, 0xee, - 0x7a, 0xed, 0x29, 0xb7, 0x21, 0x76, 0x9c, 0xe6, 0x4e, 0x43, 0xd5, 0x71, 0x33, 0xb0, 0x74, - 0xd8, 0x39, 0xd5, 0x31, 0xed, 0x1f, 0x28, 0x51, 0x0a, 0xfb, 0x45, 0xac, 0xe1, 0x0a, 0x1f, - 0x4b, 0x79, 0x4d, 0x6f, - ]; - // Unserialized state - let expected_state: ChaChaState = [ - 0xbee7079f, 0x7a385155, 0x7c97ba98, 0x0d082d73, 0xa0290fcb, 0x6965e348, 0x3e53c612, - 0xed7aee32, 0x7621b729, 0x434ee69c, 0xb03371d5, 0xd539d874, 0x281fed31, 0x45fb0a51, - 0x1f0ae1ac, 0x6f4d794b, - ]; - - let mut state = init(&key, &nonce).unwrap(); - let keystream_block_from_state = state.process_block(Some(1)).unwrap(); - assert_eq!(keystream_block_from_state[..], expected_state[..]); - - let mut ser_block = [0u8; 64]; - state - .serialize_block(&keystream_block_from_state, &mut ser_block) - .unwrap(); +// Testing any test vectors that aren't put into library's /tests folder. +#[cfg(test)] +mod test_vectors { + use super::*; + + #[test] + fn rfc8439_quarter_round_results() { + let mut chacha_state = InternalState { + state: [ + 0x11111111, 0x01020304, 0x9b8d6f43, 0x01234567, 0x11111111, 0x01020304, 0x9b8d6f43, + 0x01234567, 0x11111111, 0x01020304, 0x9b8d6f43, 0x01234567, 0x11111111, 0x01020304, + 0x9b8d6f43, 0x01234567, + ], + is_ietf: true, + }; + let expected: [u32; 4] = [0xea2a92f4, 0xcb1cf8ce, 0x4581472e, 0x5881c4bb]; + + chacha_state.quarter_round(0, 1, 2, 3); + chacha_state.quarter_round(4, 5, 6, 7); + chacha_state.quarter_round(8, 9, 10, 11); + chacha_state.quarter_round(12, 13, 14, 15); + + assert_eq!(chacha_state.state[0..4], expected); + assert_eq!(chacha_state.state[4..8], expected); + assert_eq!(chacha_state.state[8..12], expected); + assert_eq!(chacha_state.state[12..16], expected); + } - let keystream_block_only = keystream_block( - &SecretKey::from_slice(&key).unwrap(), - &Nonce::from_slice(&nonce).unwrap(), - 1, - ) - .unwrap(); + #[test] + fn rfc8439_quarter_round_results_on_indices() { + let mut chacha_state = InternalState { + state: [ + 0x879531e0, 0xc5ecf37d, 0x516461b1, 0xc9a62f8a, 0x44c20ef3, 0x3390af7f, 0xd9fc690b, + 0x2a5f714c, 0x53372767, 0xb00a5631, 0x974c541a, 0x359e9963, 0x5c971061, 0x3d631689, + 0x2098d9d6, 0x91dbd320, + ], + is_ietf: true, + }; + let expected: ChaChaState = [ + 0x879531e0, 0xc5ecf37d, 0xbdb886dc, 0xc9a62f8a, 0x44c20ef3, 0x3390af7f, 0xd9fc690b, + 0xcfacafd2, 0xe46bea80, 0xb00a5631, 0x974c541a, 0x359e9963, 0x5c971061, 0xccc07c79, + 0x2098d9d6, 0x91dbd320, + ]; - assert_eq!(ser_block[..], expected[..]); - assert_eq!(ser_block[..], keystream_block_only[..]); -} + chacha_state.quarter_round(2, 7, 8, 13); + assert_eq!(chacha_state.state[..], expected); + } -#[test] -// From https://tools.ietf.org/html/rfc8439 -fn chacha20_block_test_3() { - let key = [ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x01, - ]; - let nonce = [ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - ]; - let expected = [ - 0x3a, 0xeb, 0x52, 0x24, 0xec, 0xf8, 0x49, 0x92, 0x9b, 0x9d, 0x82, 0x8d, 0xb1, 0xce, 0xd4, - 0xdd, 0x83, 0x20, 0x25, 0xe8, 0x01, 0x8b, 0x81, 0x60, 0xb8, 0x22, 0x84, 0xf3, 0xc9, 0x49, - 0xaa, 0x5a, 0x8e, 0xca, 0x00, 0xbb, 0xb4, 0xa7, 0x3b, 0xda, 0xd1, 0x92, 0xb5, 0xc4, 0x2f, - 0x73, 0xf2, 0xfd, 0x4e, 0x27, 0x36, 0x44, 0xc8, 0xb3, 0x61, 0x25, 0xa6, 0x4a, 0xdd, 0xeb, - 0x00, 0x6c, 0x13, 0xa0, - ]; - // Unserialized state - let expected_state: ChaChaState = [ - 0x2452eb3a, 0x9249f8ec, 0x8d829d9b, 0xddd4ceb1, 0xe8252083, 0x60818b01, 0xf38422b8, - 0x5aaa49c9, 0xbb00ca8e, 0xda3ba7b4, 0xc4b592d1, 0xfdf2732f, 0x4436274e, 0x2561b3c8, - 0xebdd4aa6, 0xa0136c00, - ]; - - let mut state = init(&key, &nonce).unwrap(); - let keystream_block_from_state = state.process_block(Some(1)).unwrap(); - assert_eq!(keystream_block_from_state[..], expected_state[..]); - - let mut ser_block = [0u8; 64]; - state - .serialize_block(&keystream_block_from_state, &mut ser_block) + #[test] + fn rfc8439_chacha20_block_results() { + let key = [ + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, + 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, + 0x1c, 0x1d, 0x1e, 0x1f, + ]; + let nonce = [ + 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00, 0x00, + ]; + let expected = [ + 0x10, 0xf1, 0xe7, 0xe4, 0xd1, 0x3b, 0x59, 0x15, 0x50, 0x0f, 0xdd, 0x1f, 0xa3, 0x20, + 0x71, 0xc4, 0xc7, 0xd1, 0xf4, 0xc7, 0x33, 0xc0, 0x68, 0x03, 0x04, 0x22, 0xaa, 0x9a, + 0xc3, 0xd4, 0x6c, 0x4e, 0xd2, 0x82, 0x64, 0x46, 0x07, 0x9f, 0xaa, 0x09, 0x14, 0xc2, + 0xd7, 0x05, 0xd9, 0x8b, 0x02, 0xa2, 0xb5, 0x12, 0x9c, 0xd1, 0xde, 0x16, 0x4e, 0xb9, + 0xcb, 0xd0, 0x83, 0xe8, 0xa2, 0x50, 0x3c, 0x4e, + ]; + + let expected_init: ChaChaState = [ + 0x61707865, 0x3320646e, 0x79622d32, 0x6b206574, 0x03020100, 0x07060504, 0x0b0a0908, + 0x0f0e0d0c, 0x13121110, 0x17161514, 0x1b1a1918, 0x1f1e1d1c, 0x00000001, 0x09000000, + 0x4a000000, 0x00000000, + ]; + // Test initial key-steup + let mut state = init(&key, &nonce).unwrap(); + state.state[12] = 1_u32; + assert_eq!(state.state[..], expected_init[..]); + + let keystream_block_from_state = state.process_block(Some(1)).unwrap(); + let mut ser_block = [0u8; 64]; + state + .serialize_block(&keystream_block_from_state, &mut ser_block) + .unwrap(); + + let keystream_block_only = keystream_block( + &SecretKey::from_slice(&key).unwrap(), + &Nonce::from_slice(&nonce).unwrap(), + 1, + ) .unwrap(); - let keystream_block_only = keystream_block( - &SecretKey::from_slice(&key).unwrap(), - &Nonce::from_slice(&nonce).unwrap(), - 1, - ) - .unwrap(); - - assert_eq!(ser_block[..], expected[..]); - assert_eq!(ser_block[..], keystream_block_only[..]); -} + assert_eq!(ser_block[..], expected[..]); + assert_eq!(ser_block[..], keystream_block_only[..]); + } -#[test] -// From https://tools.ietf.org/html/rfc8439 -fn chacha20_block_test_4() { - let key = [ - 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, - ]; - let nonce = [ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - ]; - let expected = [ - 0x72, 0xd5, 0x4d, 0xfb, 0xf1, 0x2e, 0xc4, 0x4b, 0x36, 0x26, 0x92, 0xdf, 0x94, 0x13, 0x7f, - 0x32, 0x8f, 0xea, 0x8d, 0xa7, 0x39, 0x90, 0x26, 0x5e, 0xc1, 0xbb, 0xbe, 0xa1, 0xae, 0x9a, - 0xf0, 0xca, 0x13, 0xb2, 0x5a, 0xa2, 0x6c, 0xb4, 0xa6, 0x48, 0xcb, 0x9b, 0x9d, 0x1b, 0xe6, - 0x5b, 0x2c, 0x09, 0x24, 0xa6, 0x6c, 0x54, 0xd5, 0x45, 0xec, 0x1b, 0x73, 0x74, 0xf4, 0x87, - 0x2e, 0x99, 0xf0, 0x96, - ]; - // Unserialized state - let expected_state: ChaChaState = [ - 0xfb4dd572, 0x4bc42ef1, 0xdf922636, 0x327f1394, 0xa78dea8f, 0x5e269039, 0xa1bebbc1, - 0xcaf09aae, 0xa25ab213, 0x48a6b46c, 0x1b9d9bcb, 0x092c5be6, 0x546ca624, 0x1bec45d5, - 0x87f47473, 0x96f0992e, - ]; - - let mut state = init(&key, &nonce).unwrap(); - let keystream_block_from_state = state.process_block(Some(2)).unwrap(); - assert_eq!(keystream_block_from_state[..], expected_state[..]); - - let mut ser_block = [0u8; 64]; - state - .serialize_block(&keystream_block_from_state, &mut ser_block) + #[test] + fn rfc8439_chacha20_block_test_1() { + let key = [ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + ]; + let nonce = [ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + ]; + let expected = [ + 0x76, 0xb8, 0xe0, 0xad, 0xa0, 0xf1, 0x3d, 0x90, 0x40, 0x5d, 0x6a, 0xe5, 0x53, 0x86, + 0xbd, 0x28, 0xbd, 0xd2, 0x19, 0xb8, 0xa0, 0x8d, 0xed, 0x1a, 0xa8, 0x36, 0xef, 0xcc, + 0x8b, 0x77, 0x0d, 0xc7, 0xda, 0x41, 0x59, 0x7c, 0x51, 0x57, 0x48, 0x8d, 0x77, 0x24, + 0xe0, 0x3f, 0xb8, 0xd8, 0x4a, 0x37, 0x6a, 0x43, 0xb8, 0xf4, 0x15, 0x18, 0xa1, 0x1c, + 0xc3, 0x87, 0xb6, 0x69, 0xb2, 0xee, 0x65, 0x86, + ]; + // Unserialized state + let expected_state: ChaChaState = [ + 0xade0b876, 0x903df1a0, 0xe56a5d40, 0x28bd8653, 0xb819d2bd, 0x1aed8da0, 0xccef36a8, + 0xc70d778b, 0x7c5941da, 0x8d485751, 0x3fe02477, 0x374ad8b8, 0xf4b8436a, 0x1ca11815, + 0x69b687c3, 0x8665eeb2, + ]; + + let mut state = init(&key, &nonce).unwrap(); + let keystream_block_from_state = state.process_block(Some(0)).unwrap(); + assert_eq!(keystream_block_from_state[..], expected_state[..]); + + let mut ser_block = [0u8; 64]; + state + .serialize_block(&keystream_block_from_state, &mut ser_block) + .unwrap(); + + let keystream_block_only = keystream_block( + &SecretKey::from_slice(&key).unwrap(), + &Nonce::from_slice(&nonce).unwrap(), + 0, + ) .unwrap(); - let keystream_block_only = keystream_block( - &SecretKey::from_slice(&key).unwrap(), - &Nonce::from_slice(&nonce).unwrap(), - 2, - ) - .unwrap(); - - assert_eq!(ser_block[..], expected[..]); - assert_eq!(ser_block[..], keystream_block_only[..]); -} + assert_eq!(ser_block[..], expected[..]); + assert_eq!(ser_block[..], keystream_block_only[..]); + } -#[test] -// From https://tools.ietf.org/html/rfc8439 -fn chacha20_block_test_5() { - let key = [ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, - ]; - let nonce = [ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, - ]; - let expected = [ - 0xc2, 0xc6, 0x4d, 0x37, 0x8c, 0xd5, 0x36, 0x37, 0x4a, 0xe2, 0x04, 0xb9, 0xef, 0x93, 0x3f, - 0xcd, 0x1a, 0x8b, 0x22, 0x88, 0xb3, 0xdf, 0xa4, 0x96, 0x72, 0xab, 0x76, 0x5b, 0x54, 0xee, - 0x27, 0xc7, 0x8a, 0x97, 0x0e, 0x0e, 0x95, 0x5c, 0x14, 0xf3, 0xa8, 0x8e, 0x74, 0x1b, 0x97, - 0xc2, 0x86, 0xf7, 0x5f, 0x8f, 0xc2, 0x99, 0xe8, 0x14, 0x83, 0x62, 0xfa, 0x19, 0x8a, 0x39, - 0x53, 0x1b, 0xed, 0x6d, - ]; - // Unserialized state - let expected_state: ChaChaState = [ - 0x374dc6c2, 0x3736d58c, 0xb904e24a, 0xcd3f93ef, 0x88228b1a, 0x96a4dfb3, 0x5b76ab72, - 0xc727ee54, 0x0e0e978a, 0xf3145c95, 0x1b748ea8, 0xf786c297, 0x99c28f5f, 0x628314e8, - 0x398a19fa, 0x6ded1b53, - ]; - - let mut state = init(&key, &nonce).unwrap(); - let keystream_block_from_state = state.process_block(Some(0)).unwrap(); - assert_eq!(keystream_block_from_state[..], expected_state[..]); - - let mut ser_block = [0u8; 64]; - state - .serialize_block(&keystream_block_from_state, &mut ser_block) + #[test] + fn rfc8439_chacha20_block_test_2() { + let key = [ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + ]; + let nonce = [ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + ]; + let expected = [ + 0x9f, 0x07, 0xe7, 0xbe, 0x55, 0x51, 0x38, 0x7a, 0x98, 0xba, 0x97, 0x7c, 0x73, 0x2d, + 0x08, 0x0d, 0xcb, 0x0f, 0x29, 0xa0, 0x48, 0xe3, 0x65, 0x69, 0x12, 0xc6, 0x53, 0x3e, + 0x32, 0xee, 0x7a, 0xed, 0x29, 0xb7, 0x21, 0x76, 0x9c, 0xe6, 0x4e, 0x43, 0xd5, 0x71, + 0x33, 0xb0, 0x74, 0xd8, 0x39, 0xd5, 0x31, 0xed, 0x1f, 0x28, 0x51, 0x0a, 0xfb, 0x45, + 0xac, 0xe1, 0x0a, 0x1f, 0x4b, 0x79, 0x4d, 0x6f, + ]; + // Unserialized state + let expected_state: ChaChaState = [ + 0xbee7079f, 0x7a385155, 0x7c97ba98, 0x0d082d73, 0xa0290fcb, 0x6965e348, 0x3e53c612, + 0xed7aee32, 0x7621b729, 0x434ee69c, 0xb03371d5, 0xd539d874, 0x281fed31, 0x45fb0a51, + 0x1f0ae1ac, 0x6f4d794b, + ]; + + let mut state = init(&key, &nonce).unwrap(); + let keystream_block_from_state = state.process_block(Some(1)).unwrap(); + assert_eq!(keystream_block_from_state[..], expected_state[..]); + + let mut ser_block = [0u8; 64]; + state + .serialize_block(&keystream_block_from_state, &mut ser_block) + .unwrap(); + + let keystream_block_only = keystream_block( + &SecretKey::from_slice(&key).unwrap(), + &Nonce::from_slice(&nonce).unwrap(), + 1, + ) .unwrap(); - let keystream_block_only = keystream_block( - &SecretKey::from_slice(&key).unwrap(), - &Nonce::from_slice(&nonce).unwrap(), - 0, - ) - .unwrap(); - - assert_eq!(ser_block[..], expected[..]); - assert_eq!(ser_block[..], keystream_block_only[..]); -} + assert_eq!(ser_block[..], expected[..]); + assert_eq!(ser_block[..], keystream_block_only[..]); + } -#[test] -// From https://tools.ietf.org/html/rfc8439 -fn test_key_schedule() { - let key = [ - 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, - 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, - 0x1e, 0x1f, - ]; - let nonce = [ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00, 0x00, - ]; - // First block setup expected - let first_state: ChaChaState = [ - 0x61707865, 0x3320646e, 0x79622d32, 0x6b206574, 0x03020100, 0x07060504, 0x0b0a0908, - 0x0f0e0d0c, 0x13121110, 0x17161514, 0x1b1a1918, 0x1f1e1d1c, 0x00000001, 0x00000000, - 0x4a000000, 0x00000000, - ]; - // Second block setup expected - let second_state: ChaChaState = [ - 0x61707865, 0x3320646e, 0x79622d32, 0x6b206574, 0x03020100, 0x07060504, 0x0b0a0908, - 0x0f0e0d0c, 0x13121110, 0x17161514, 0x1b1a1918, 0x1f1e1d1c, 0x00000002, 0x00000000, - 0x4a000000, 0x00000000, - ]; - - // First block operation expected - let first_block: ChaChaState = [ - 0xf3514f22, 0xe1d91b40, 0x6f27de2f, 0xed1d63b8, 0x821f138c, 0xe2062c3d, 0xecca4f7e, - 0x78cff39e, 0xa30a3b8a, 0x920a6072, 0xcd7479b5, 0x34932bed, 0x40ba4c79, 0xcd343ec6, - 0x4c2c21ea, 0xb7417df0, - ]; - // Second block operation expected - let second_block: ChaChaState = [ - 0x9f74a669, 0x410f633f, 0x28feca22, 0x7ec44dec, 0x6d34d426, 0x738cb970, 0x3ac5e9f3, - 0x45590cc4, 0xda6e8b39, 0x892c831a, 0xcdea67c1, 0x2b7e1d90, 0x037463f3, 0xa11a2073, - 0xe8bcfb88, 0xedc49139, - ]; - - // Expected keystream - let expected_keystream = [ - 0x22, 0x4f, 0x51, 0xf3, 0x40, 0x1b, 0xd9, 0xe1, 0x2f, 0xde, 0x27, 0x6f, 0xb8, 0x63, 0x1d, - 0xed, 0x8c, 0x13, 0x1f, 0x82, 0x3d, 0x2c, 0x06, 0xe2, 0x7e, 0x4f, 0xca, 0xec, 0x9e, 0xf3, - 0xcf, 0x78, 0x8a, 0x3b, 0x0a, 0xa3, 0x72, 0x60, 0x0a, 0x92, 0xb5, 0x79, 0x74, 0xcd, 0xed, - 0x2b, 0x93, 0x34, 0x79, 0x4c, 0xba, 0x40, 0xc6, 0x3e, 0x34, 0xcd, 0xea, 0x21, 0x2c, 0x4c, - 0xf0, 0x7d, 0x41, 0xb7, 0x69, 0xa6, 0x74, 0x9f, 0x3f, 0x63, 0x0f, 0x41, 0x22, 0xca, 0xfe, - 0x28, 0xec, 0x4d, 0xc4, 0x7e, 0x26, 0xd4, 0x34, 0x6d, 0x70, 0xb9, 0x8c, 0x73, 0xf3, 0xe9, - 0xc5, 0x3a, 0xc4, 0x0c, 0x59, 0x45, 0x39, 0x8b, 0x6e, 0xda, 0x1a, 0x83, 0x2c, 0x89, 0xc1, - 0x67, 0xea, 0xcd, 0x90, 0x1d, 0x7e, 0x2b, 0xf3, 0x63, - ]; - - let mut state = init(&key, &nonce).unwrap(); - // Block call with initial counter - let first_block_state = state.process_block(Some(1)).unwrap(); - assert_eq!(first_block_state, first_block); - // Test first internal state - assert_eq!(first_state, state.state); - - // Next iteration call, increase counter - let second_block_state = state.process_block(Some(1 + 1)).unwrap(); - assert_eq!(second_block_state, second_block); - // Test second internal state - assert_eq!(second_state, state.state); - - let mut actual_keystream = [0u8; 128]; - // Append first keystream block - state - .serialize_block(&first_block_state, &mut actual_keystream[..64]) - .unwrap(); - state - .serialize_block(&second_block_state, &mut actual_keystream[64..]) + #[test] + fn rfc8439_chacha20_block_test_3() { + let key = [ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, + ]; + let nonce = [ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + ]; + let expected = [ + 0x3a, 0xeb, 0x52, 0x24, 0xec, 0xf8, 0x49, 0x92, 0x9b, 0x9d, 0x82, 0x8d, 0xb1, 0xce, + 0xd4, 0xdd, 0x83, 0x20, 0x25, 0xe8, 0x01, 0x8b, 0x81, 0x60, 0xb8, 0x22, 0x84, 0xf3, + 0xc9, 0x49, 0xaa, 0x5a, 0x8e, 0xca, 0x00, 0xbb, 0xb4, 0xa7, 0x3b, 0xda, 0xd1, 0x92, + 0xb5, 0xc4, 0x2f, 0x73, 0xf2, 0xfd, 0x4e, 0x27, 0x36, 0x44, 0xc8, 0xb3, 0x61, 0x25, + 0xa6, 0x4a, 0xdd, 0xeb, 0x00, 0x6c, 0x13, 0xa0, + ]; + // Unserialized state + let expected_state: ChaChaState = [ + 0x2452eb3a, 0x9249f8ec, 0x8d829d9b, 0xddd4ceb1, 0xe8252083, 0x60818b01, 0xf38422b8, + 0x5aaa49c9, 0xbb00ca8e, 0xda3ba7b4, 0xc4b592d1, 0xfdf2732f, 0x4436274e, 0x2561b3c8, + 0xebdd4aa6, 0xa0136c00, + ]; + + let mut state = init(&key, &nonce).unwrap(); + let keystream_block_from_state = state.process_block(Some(1)).unwrap(); + assert_eq!(keystream_block_from_state[..], expected_state[..]); + + let mut ser_block = [0u8; 64]; + state + .serialize_block(&keystream_block_from_state, &mut ser_block) + .unwrap(); + + let keystream_block_only = keystream_block( + &SecretKey::from_slice(&key).unwrap(), + &Nonce::from_slice(&nonce).unwrap(), + 1, + ) .unwrap(); - assert_eq!( - actual_keystream[..expected_keystream.len()].as_ref(), - expected_keystream.as_ref() - ); - actual_keystream[..64].copy_from_slice( - &keystream_block( + assert_eq!(ser_block[..], expected[..]); + assert_eq!(ser_block[..], keystream_block_only[..]); + } + + #[test] + fn rfc8439_chacha20_block_test_4() { + let key = [ + 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + ]; + let nonce = [ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + ]; + let expected = [ + 0x72, 0xd5, 0x4d, 0xfb, 0xf1, 0x2e, 0xc4, 0x4b, 0x36, 0x26, 0x92, 0xdf, 0x94, 0x13, + 0x7f, 0x32, 0x8f, 0xea, 0x8d, 0xa7, 0x39, 0x90, 0x26, 0x5e, 0xc1, 0xbb, 0xbe, 0xa1, + 0xae, 0x9a, 0xf0, 0xca, 0x13, 0xb2, 0x5a, 0xa2, 0x6c, 0xb4, 0xa6, 0x48, 0xcb, 0x9b, + 0x9d, 0x1b, 0xe6, 0x5b, 0x2c, 0x09, 0x24, 0xa6, 0x6c, 0x54, 0xd5, 0x45, 0xec, 0x1b, + 0x73, 0x74, 0xf4, 0x87, 0x2e, 0x99, 0xf0, 0x96, + ]; + // Unserialized state + let expected_state: ChaChaState = [ + 0xfb4dd572, 0x4bc42ef1, 0xdf922636, 0x327f1394, 0xa78dea8f, 0x5e269039, 0xa1bebbc1, + 0xcaf09aae, 0xa25ab213, 0x48a6b46c, 0x1b9d9bcb, 0x092c5be6, 0x546ca624, 0x1bec45d5, + 0x87f47473, 0x96f0992e, + ]; + + let mut state = init(&key, &nonce).unwrap(); + let keystream_block_from_state = state.process_block(Some(2)).unwrap(); + assert_eq!(keystream_block_from_state[..], expected_state[..]); + + let mut ser_block = [0u8; 64]; + state + .serialize_block(&keystream_block_from_state, &mut ser_block) + .unwrap(); + + let keystream_block_only = keystream_block( &SecretKey::from_slice(&key).unwrap(), &Nonce::from_slice(&nonce).unwrap(), - 1, + 2, ) - .unwrap(), - ); - actual_keystream[64..].copy_from_slice( - &keystream_block( + .unwrap(); + + assert_eq!(ser_block[..], expected[..]); + assert_eq!(ser_block[..], keystream_block_only[..]); + } + + #[test] + fn rfc8439_chacha20_block_test_5() { + let key = [ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + ]; + let nonce = [ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + ]; + let expected = [ + 0xc2, 0xc6, 0x4d, 0x37, 0x8c, 0xd5, 0x36, 0x37, 0x4a, 0xe2, 0x04, 0xb9, 0xef, 0x93, + 0x3f, 0xcd, 0x1a, 0x8b, 0x22, 0x88, 0xb3, 0xdf, 0xa4, 0x96, 0x72, 0xab, 0x76, 0x5b, + 0x54, 0xee, 0x27, 0xc7, 0x8a, 0x97, 0x0e, 0x0e, 0x95, 0x5c, 0x14, 0xf3, 0xa8, 0x8e, + 0x74, 0x1b, 0x97, 0xc2, 0x86, 0xf7, 0x5f, 0x8f, 0xc2, 0x99, 0xe8, 0x14, 0x83, 0x62, + 0xfa, 0x19, 0x8a, 0x39, 0x53, 0x1b, 0xed, 0x6d, + ]; + // Unserialized state + let expected_state: ChaChaState = [ + 0x374dc6c2, 0x3736d58c, 0xb904e24a, 0xcd3f93ef, 0x88228b1a, 0x96a4dfb3, 0x5b76ab72, + 0xc727ee54, 0x0e0e978a, 0xf3145c95, 0x1b748ea8, 0xf786c297, 0x99c28f5f, 0x628314e8, + 0x398a19fa, 0x6ded1b53, + ]; + + let mut state = init(&key, &nonce).unwrap(); + let keystream_block_from_state = state.process_block(Some(0)).unwrap(); + assert_eq!(keystream_block_from_state[..], expected_state[..]); + + let mut ser_block = [0u8; 64]; + state + .serialize_block(&keystream_block_from_state, &mut ser_block) + .unwrap(); + + let keystream_block_only = keystream_block( &SecretKey::from_slice(&key).unwrap(), &Nonce::from_slice(&nonce).unwrap(), - 1 + 1, + 0, ) - .unwrap(), - ); + .unwrap(); - assert_eq!( - actual_keystream[..expected_keystream.len()].as_ref(), - expected_keystream.as_ref() - ); + assert_eq!(ser_block[..], expected[..]); + assert_eq!(ser_block[..], keystream_block_only[..]); + } + + #[test] + fn rfc8439_key_schedule() { + let key = [ + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, + 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, + 0x1c, 0x1d, 0x1e, 0x1f, + ]; + let nonce = [ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00, 0x00, + ]; + // First block setup expected + let first_state: ChaChaState = [ + 0x61707865, 0x3320646e, 0x79622d32, 0x6b206574, 0x03020100, 0x07060504, 0x0b0a0908, + 0x0f0e0d0c, 0x13121110, 0x17161514, 0x1b1a1918, 0x1f1e1d1c, 0x00000001, 0x00000000, + 0x4a000000, 0x00000000, + ]; + // Second block setup expected + let second_state: ChaChaState = [ + 0x61707865, 0x3320646e, 0x79622d32, 0x6b206574, 0x03020100, 0x07060504, 0x0b0a0908, + 0x0f0e0d0c, 0x13121110, 0x17161514, 0x1b1a1918, 0x1f1e1d1c, 0x00000002, 0x00000000, + 0x4a000000, 0x00000000, + ]; + + // First block operation expected + let first_block: ChaChaState = [ + 0xf3514f22, 0xe1d91b40, 0x6f27de2f, 0xed1d63b8, 0x821f138c, 0xe2062c3d, 0xecca4f7e, + 0x78cff39e, 0xa30a3b8a, 0x920a6072, 0xcd7479b5, 0x34932bed, 0x40ba4c79, 0xcd343ec6, + 0x4c2c21ea, 0xb7417df0, + ]; + // Second block operation expected + let second_block: ChaChaState = [ + 0x9f74a669, 0x410f633f, 0x28feca22, 0x7ec44dec, 0x6d34d426, 0x738cb970, 0x3ac5e9f3, + 0x45590cc4, 0xda6e8b39, 0x892c831a, 0xcdea67c1, 0x2b7e1d90, 0x037463f3, 0xa11a2073, + 0xe8bcfb88, 0xedc49139, + ]; + + // Expected keystream + let expected_keystream = [ + 0x22, 0x4f, 0x51, 0xf3, 0x40, 0x1b, 0xd9, 0xe1, 0x2f, 0xde, 0x27, 0x6f, 0xb8, 0x63, + 0x1d, 0xed, 0x8c, 0x13, 0x1f, 0x82, 0x3d, 0x2c, 0x06, 0xe2, 0x7e, 0x4f, 0xca, 0xec, + 0x9e, 0xf3, 0xcf, 0x78, 0x8a, 0x3b, 0x0a, 0xa3, 0x72, 0x60, 0x0a, 0x92, 0xb5, 0x79, + 0x74, 0xcd, 0xed, 0x2b, 0x93, 0x34, 0x79, 0x4c, 0xba, 0x40, 0xc6, 0x3e, 0x34, 0xcd, + 0xea, 0x21, 0x2c, 0x4c, 0xf0, 0x7d, 0x41, 0xb7, 0x69, 0xa6, 0x74, 0x9f, 0x3f, 0x63, + 0x0f, 0x41, 0x22, 0xca, 0xfe, 0x28, 0xec, 0x4d, 0xc4, 0x7e, 0x26, 0xd4, 0x34, 0x6d, + 0x70, 0xb9, 0x8c, 0x73, 0xf3, 0xe9, 0xc5, 0x3a, 0xc4, 0x0c, 0x59, 0x45, 0x39, 0x8b, + 0x6e, 0xda, 0x1a, 0x83, 0x2c, 0x89, 0xc1, 0x67, 0xea, 0xcd, 0x90, 0x1d, 0x7e, 0x2b, + 0xf3, 0x63, + ]; + + let mut state = init(&key, &nonce).unwrap(); + // Block call with initial counter + let first_block_state = state.process_block(Some(1)).unwrap(); + assert_eq!(first_block_state, first_block); + // Test first internal state + assert_eq!(first_state, state.state); + + // Next iteration call, increase counter + let second_block_state = state.process_block(Some(1 + 1)).unwrap(); + assert_eq!(second_block_state, second_block); + // Test second internal state + assert_eq!(second_state, state.state); + + let mut actual_keystream = [0u8; 128]; + // Append first keystream block + state + .serialize_block(&first_block_state, &mut actual_keystream[..64]) + .unwrap(); + state + .serialize_block(&second_block_state, &mut actual_keystream[64..]) + .unwrap(); + assert_eq!( + actual_keystream[..expected_keystream.len()].as_ref(), + expected_keystream.as_ref() + ); + + actual_keystream[..64].copy_from_slice( + &keystream_block( + &SecretKey::from_slice(&key).unwrap(), + &Nonce::from_slice(&nonce).unwrap(), + 1, + ) + .unwrap(), + ); + actual_keystream[64..].copy_from_slice( + &keystream_block( + &SecretKey::from_slice(&key).unwrap(), + &Nonce::from_slice(&nonce).unwrap(), + 1 + 1, + ) + .unwrap(), + ); + + assert_eq!( + actual_keystream[..expected_keystream.len()].as_ref(), + expected_keystream.as_ref() + ); + } } From e6ec96be90c65f23a198d43379c2424b8ede8006 Mon Sep 17 00:00:00 2001 From: brycx Date: Tue, 29 Jan 2019 12:50:52 +0100 Subject: [PATCH 04/40] Tests: Organize encrypt()/decrypt() tests and add proptests to those --- src/hazardous/stream/chacha20.rs | 406 ++++++++++++++++++++++--------- 1 file changed, 295 insertions(+), 111 deletions(-) diff --git a/src/hazardous/stream/chacha20.rs b/src/hazardous/stream/chacha20.rs index ee5db325..c1e71b9b 100644 --- a/src/hazardous/stream/chacha20.rs +++ b/src/hazardous/stream/chacha20.rs @@ -513,114 +513,6 @@ fn test_key_sizes() { assert!(SecretKey::from_slice(&[0u8; 32]).is_ok()); } -#[test] -fn test_diff_ct_pt_len() { - let mut dst = [0u8; 64]; - - assert!(encrypt( - &SecretKey::from_slice(&[0u8; 32]).unwrap(), - &Nonce::from_slice(&[0u8; 12]).unwrap(), - 0, - &[0u8; 65], - &mut dst - ) - .is_err()); - assert!(encrypt( - &SecretKey::from_slice(&[0u8; 32]).unwrap(), - &Nonce::from_slice(&[0u8; 12]).unwrap(), - 0, - &[0u8; 63], - &mut dst - ) - .is_ok()); - assert!(encrypt( - &SecretKey::from_slice(&[0u8; 32]).unwrap(), - &Nonce::from_slice(&[0u8; 12]).unwrap(), - 0, - &[0u8; 64], - &mut dst - ) - .is_ok()); -} - -#[test] -fn test_err_on_diff_ct_pt_len_chacha_long() { - let mut dst = [0u8; 64]; - - assert!(encrypt( - &SecretKey::from_slice(&[0u8; 32]).unwrap(), - &Nonce::from_slice(&[0u8; 12]).unwrap(), - 0, - &[0u8; 128], - &mut dst, - ) - .is_err()); -} - -#[test] -fn test_err_on_diff_ct_pt_len_chacha_short() { - let mut dst = [0u8; 64]; - - assert!(encrypt( - &SecretKey::from_slice(&[0u8; 32]).unwrap(), - &Nonce::from_slice(&[0u8; 12]).unwrap(), - 0, - &[0u8; 0], - &mut dst, - ) - .is_err()); -} - -#[test] -fn test_err_on_empty_pt() { - let mut dst = [0u8; 64]; - - assert!(encrypt( - &SecretKey::from_slice(&[0u8; 32]).unwrap(), - &Nonce::from_slice(&[0u8; 12]).unwrap(), - 0, - &[0u8; 0], - &mut dst, - ) - .is_err()); -} - -#[test] -fn test_err_on_initial_counter_overflow() { - let mut dst = [0u8; 65]; - - assert!(encrypt( - &SecretKey::from_slice(&[0u8; 32]).unwrap(), - &Nonce::from_slice(&[0u8; 12]).unwrap(), - 4294967295, - &[0u8; 65], - &mut dst, - ) - .is_err()); -} - -#[test] -fn test_pass_on_one_iter_max_initial_counter() { - let mut dst = [0u8; 64]; - // Should pass because only one iteration is completed, so block_counter will - // not increase - encrypt( - &SecretKey::from_slice(&[0u8; 32]).unwrap(), - &Nonce::from_slice(&[0u8; 12]).unwrap(), - 4294967295, - &[0u8; 64], - &mut dst, - ) - .unwrap(); - // keystream_block never increases the provided counter - keystream_block( - &SecretKey::from_slice(&[0u8; 32]).unwrap(), - &Nonce::from_slice(&[0u8; 12]).unwrap(), - 4294967295, - ) - .unwrap(); -} - #[cfg(test)] // Convenience function for testing. fn init(key: &[u8], nonce: &[u8]) -> Result { @@ -639,13 +531,305 @@ fn init(key: &[u8], nonce: &[u8]) -> Result { // Testing public functions in the module. #[cfg(test)] mod public { + use super::*; // One function tested per submodule. - mod test_encrypt {} + // encrypt()/decrypt() are tested together here + // since decrypt() is just a wrapper around encrypt() + // and so only the decrypt() function is called + mod test_encrypt_decrypt { + use super::*; + #[test] + fn test_fail_on_initial_counter_overflow() { + let mut dst = [0u8; 65]; + + assert!(decrypt( + &SecretKey::from_slice(&[0u8; 32]).unwrap(), + &Nonce::from_slice(&[0u8; 12]).unwrap(), + u32::max_value(), + &[0u8; 65], + &mut dst, + ) + .is_err()); + } + + #[test] + fn test_pass_on_one_iter_max_initial_counter() { + let mut dst = [0u8; 64]; + // Should pass because only one iteration is completed, so block_counter will + // not increase + assert!(decrypt( + &SecretKey::from_slice(&[0u8; 32]).unwrap(), + &Nonce::from_slice(&[0u8; 12]).unwrap(), + u32::max_value(), + &[0u8; 64], + &mut dst, + ) + .is_ok()); + } + + #[test] + fn test_fail_on_empty_plaintext() { + let mut dst = [0u8; 64]; + + assert!(decrypt( + &SecretKey::from_slice(&[0u8; 32]).unwrap(), + &Nonce::from_slice(&[0u8; 12]).unwrap(), + 0, + &[0u8; 0], + &mut dst, + ) + .is_err()); + } + + #[test] + fn test_dst_out_length() { + let mut dst_small = [0u8; 64]; + + assert!(decrypt( + &SecretKey::from_slice(&[0u8; 32]).unwrap(), + &Nonce::from_slice(&[0u8; 12]).unwrap(), + 0, + &[0u8; 128], + &mut dst_small, + ) + .is_err()); + + let mut dst = [0u8; 64]; + + assert!(decrypt( + &SecretKey::from_slice(&[0u8; 32]).unwrap(), + &Nonce::from_slice(&[0u8; 12]).unwrap(), + 0, + &[0u8; 64], + &mut dst, + ) + .is_ok()); + + let mut dst_big = [0u8; 64]; + + assert!(decrypt( + &SecretKey::from_slice(&[0u8; 32]).unwrap(), + &Nonce::from_slice(&[0u8; 12]).unwrap(), + 0, + &[0u8; 32], + &mut dst_big, + ) + .is_ok()); + } + + // Proptests. Only exectued when NOT testing no_std. + #[cfg(not(feature = "no_std"))] + mod proptest { + use super::*; + + /// Given a input length `a` find out how many times + /// the initial counter on encrypt()/decrypt() would + /// increase. + fn counter_increase_times(a: f32) -> u32 { + // Otherwise a overvlowing subtration would happen + if a <= 64f32 { + return 0; + } + + let check_with_floor = (a / 64f32).floor(); + let actual = a / 64f32; + + assert!(actual >= check_with_floor); + // Subtract one because the first 64 in length + // the counter does not increase + if actual > check_with_floor { + (actual.ceil() as u32) - 1 + } else { + (actual as u32) - 1 + } + } + + quickcheck! { + // Encrypting input, and then decrypting should always yield the same input. + fn prop_encrypt_decrypt_same_input(input: Vec, block_counter: u32) -> bool { + let pt = if input.is_empty() { + vec![1u8; 10] + } else { + input + }; + + let mut dst_out_ct = vec![0u8; pt.len()]; + let mut dst_out_pt = vec![0u8; pt.len()]; + + // If `block_counter` is high enough check if it would overflow + if counter_increase_times(pt.len() as f32).checked_add(block_counter).is_none() { + // Overflow will occur and the operation should fail + let res = if encrypt( + &SecretKey::from_slice(&[0u8; 32]).unwrap(), + &Nonce::from_slice(&[0u8; 12]).unwrap(), + block_counter, + &pt[..], + &mut dst_out_ct, + ).is_err() { true } else { false }; + + return res; + } else { + + encrypt( + &SecretKey::from_slice(&[0u8; 32]).unwrap(), + &Nonce::from_slice(&[0u8; 12]).unwrap(), + block_counter, + &pt[..], + &mut dst_out_ct, + ).unwrap(); + + decrypt( + &SecretKey::from_slice(&[0u8; 32]).unwrap(), + &Nonce::from_slice(&[0u8; 12]).unwrap(), + block_counter, + &dst_out_ct[..], + &mut dst_out_pt, + ).unwrap(); + + return dst_out_pt == pt; + } + } + } + + 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) + } + } + } + } + + mod test_keystream_block { + use super::*; - mod test_decrypt {} + #[test] + fn test_counter() { + // keystream_block never increases the provided counter + assert!(keystream_block( + &SecretKey::from_slice(&[0u8; 32]).unwrap(), + &Nonce::from_slice(&[0u8; 12]).unwrap(), + u32::max_value(), + ) + .is_ok()); + + assert!(keystream_block( + &SecretKey::from_slice(&[0u8; 32]).unwrap(), + &Nonce::from_slice(&[0u8; 12]).unwrap(), + 0, + ) + .is_ok()); - mod test_keystream_block {} + assert!(keystream_block( + &SecretKey::from_slice(&[0u8; 32]).unwrap(), + &Nonce::from_slice(&[0u8; 12]).unwrap(), + 64, + ) + .is_ok()); + } + } mod test_hchacha20 {} From eaaf7439646edd06bb00ead49f8ac01f5809d697 Mon Sep 17 00:00:00 2001 From: brycx Date: Tue, 29 Jan 2019 13:13:15 +0100 Subject: [PATCH 05/40] Tests: Finished polish of public functions in chacha20 --- src/hazardous/stream/chacha20.rs | 134 ++++++++++++++++++++++++++++++- 1 file changed, 130 insertions(+), 4 deletions(-) diff --git a/src/hazardous/stream/chacha20.rs b/src/hazardous/stream/chacha20.rs index c1e71b9b..ab5f5959 100644 --- a/src/hazardous/stream/chacha20.rs +++ b/src/hazardous/stream/chacha20.rs @@ -828,14 +828,140 @@ mod public { 64, ) .is_ok()); + } + + #[test] + fn test_diff_keys_diff_output() { + let keystream1 = keystream_block( + &SecretKey::from_slice(&[0u8; 32]).unwrap(), + &Nonce::from_slice(&[0u8; 12]).unwrap(), + 0, + ).unwrap(); + + let keystream2 = keystream_block( + &SecretKey::from_slice(&[1u8; 32]).unwrap(), + &Nonce::from_slice(&[0u8; 12]).unwrap(), + 0, + ).unwrap(); + + assert!(keystream1[..] != keystream2[..]); + } + + #[test] + fn test_diff_nonce_diff_output() { + let keystream1 = keystream_block( + &SecretKey::from_slice(&[0u8; 32]).unwrap(), + &Nonce::from_slice(&[0u8; 12]).unwrap(), + 0, + ).unwrap(); + + let keystream2 = keystream_block( + &SecretKey::from_slice(&[0u8; 32]).unwrap(), + &Nonce::from_slice(&[1u8; 12]).unwrap(), + 0, + ).unwrap(); + + assert!(keystream1[..] != keystream2[..]); + } + + #[test] + fn test_diff_initial_counter_diff_output() { + let keystream1 = keystream_block( + &SecretKey::from_slice(&[0u8; 32]).unwrap(), + &Nonce::from_slice(&[0u8; 12]).unwrap(), + 0, + ).unwrap(); + + let keystream2 = keystream_block( + &SecretKey::from_slice(&[0u8; 32]).unwrap(), + &Nonce::from_slice(&[0u8; 12]).unwrap(), + 1, + ).unwrap(); + + assert!(keystream1[..] != keystream2[..]); + } + + // Proptests. Only exectued when NOT testing no_std. + #[cfg(not(feature = "no_std"))] + mod proptest { + use super::*; + + quickcheck! { + fn prop_same_params_same_output(counter: u32) -> bool { + let keystream1 = keystream_block( + &SecretKey::from_slice(&[0u8; 32]).unwrap(), + &Nonce::from_slice(&[0u8; 12]).unwrap(), + counter, + ).unwrap(); + + let keystream2 = keystream_block( + &SecretKey::from_slice(&[0u8; 32]).unwrap(), + &Nonce::from_slice(&[0u8; 12]).unwrap(), + counter, + ).unwrap(); + + (keystream1[..] == keystream2[..]) + } + } } } - mod test_hchacha20 {} + mod test_hchacha20 { + use super::*; + + #[test] + fn test_nonce_length() { + assert!(hchacha20( + &SecretKey::from_slice(&[0u8; 32]).unwrap(), + &[0u8; 16], + ).is_ok()); + + assert!(hchacha20( + &SecretKey::from_slice(&[0u8; 32]).unwrap(), + &[0u8; 17], + ).is_err()); + + assert!(hchacha20( + &SecretKey::from_slice(&[0u8; 32]).unwrap(), + &[0u8; 15], + ).is_err()); + + assert!(hchacha20( + &SecretKey::from_slice(&[0u8; 32]).unwrap(), + &[0u8; 0], + ).is_err()); + } + + #[test] + fn test_diff_keys_diff_output() { + let keystream1 = hchacha20( + &SecretKey::from_slice(&[0u8; 32]).unwrap(), + &[0u8; 16], + ).unwrap(); + + let keystream2 = hchacha20( + &SecretKey::from_slice(&[1u8; 32]).unwrap(), + &[0u8; 16], + ).unwrap(); - // Proptests. Only exectued when NOT testing no_std. - #[cfg(not(feature = "no_std"))] - mod proptest {} + assert!(keystream1 != keystream2); + } + + #[test] + fn test_diff_nonce_diff_output() { + let keystream1 = hchacha20( + &SecretKey::from_slice(&[0u8; 32]).unwrap(), + &[0u8; 16], + ).unwrap(); + + let keystream2 = hchacha20( + &SecretKey::from_slice(&[0u8; 32]).unwrap(), + &[1u8; 16], + ).unwrap(); + + assert!(keystream1 != keystream2); + } + } } // Testing private functions in the module. From d353129d0054bdfa415a196e557198169280ee9a Mon Sep 17 00:00:00 2001 From: brycx Date: Tue, 29 Jan 2019 13:47:21 +0100 Subject: [PATCH 06/40] Tests: Finish organizing tests for private functions in chacha20 and add proptests to init_state. --- src/hazardous/stream/chacha20.rs | 392 +++++++++++++++++-------------- 1 file changed, 219 insertions(+), 173 deletions(-) diff --git a/src/hazardous/stream/chacha20.rs b/src/hazardous/stream/chacha20.rs index ab5f5959..928382f7 100644 --- a/src/hazardous/stream/chacha20.rs +++ b/src/hazardous/stream/chacha20.rs @@ -398,136 +398,6 @@ pub fn hchacha20( Ok(keystream_block) } -#[test] -fn test_process_block_wrong_combination_of_variant_and_nonce() { - let mut chacha_state_ietf = InternalState { - state: [0_u32; 16], - is_ietf: true, - }; - chacha_state_ietf - .init_state(&SecretKey::from_slice(&[0u8; 32]).unwrap(), &[0u8; 12]) - .unwrap(); - - let mut chacha_state_hchacha = InternalState { - state: [0_u32; 16], - is_ietf: false, - }; - - chacha_state_hchacha - .init_state(&SecretKey::from_slice(&[0u8; 32]).unwrap(), &[0u8; 16]) - .unwrap(); - - assert!(chacha_state_hchacha.process_block(Some(1)).is_err()); - assert!(chacha_state_ietf.process_block(None).is_err()); - assert!(chacha_state_hchacha.process_block(None).is_ok()); - assert!(chacha_state_ietf.process_block(Some(1)).is_ok()); -} - -#[test] -fn test_serialize_block_wrong_combination_of_variant_and_dst() { - let mut chacha_state_ietf = InternalState { - state: [0_u32; 16], - is_ietf: true, - }; - - chacha_state_ietf - .init_state(&SecretKey::from_slice(&[0u8; 32]).unwrap(), &[0u8; 12]) - .unwrap(); - - let mut chacha_state_hchacha = InternalState { - state: [0_u32; 16], - is_ietf: false, - }; - - let mut hchacha_out = [0u8; HCHACHA_OUTSIZE]; - let mut ietf_out = [0u8; CHACHA_BLOCKSIZE]; - - chacha_state_hchacha - .init_state(&SecretKey::from_slice(&[0u8; 32]).unwrap(), &[0u8; 16]) - .unwrap(); - - let ietf_src = chacha_state_ietf.process_block(Some(1)).unwrap(); - let hchacha_src = chacha_state_hchacha.process_block(None).unwrap(); - - assert!(chacha_state_hchacha - .serialize_block(&hchacha_src, &mut ietf_out) - .is_err()); - assert!(chacha_state_ietf - .serialize_block(&ietf_src, &mut hchacha_out) - .is_err()); - assert!(chacha_state_hchacha - .serialize_block(&hchacha_src, &mut hchacha_out) - .is_ok()); - assert!(chacha_state_ietf - .serialize_block(&ietf_src, &mut ietf_out) - .is_ok()); -} - -#[test] -fn test_bad_key_nonce_size_init() { - let mut chacha_state = InternalState { - state: [0_u32; 16], - is_ietf: true, - }; - - assert!(chacha_state - .init_state(&SecretKey::from_slice(&[0u8; 32]).unwrap(), &[0u8; 15]) - .is_err()); - assert!(chacha_state - .init_state(&SecretKey::from_slice(&[0u8; 32]).unwrap(), &[0u8; 10]) - .is_err()); - assert!(chacha_state - .init_state(&SecretKey::from_slice(&[0u8; 32]).unwrap(), &[0u8; 12]) - .is_ok()); - - let mut hchacha_state = InternalState { - state: [0_u32; 16], - is_ietf: false, - }; - - assert!(hchacha_state - .init_state(&SecretKey::from_slice(&[0u8; 32]).unwrap(), &[0u8; 15]) - .is_err()); - assert!(hchacha_state - .init_state(&SecretKey::from_slice(&[0u8; 32]).unwrap(), &[0u8; 17]) - .is_err()); - assert!(hchacha_state - .init_state(&SecretKey::from_slice(&[0u8; 32]).unwrap(), &[0u8; 16]) - .is_ok()); -} - -#[test] -fn test_nonce_sizes() { - assert!(&Nonce::from_slice(&[0u8; 10]).is_err()); - assert!(&Nonce::from_slice(&[0u8; 13]).is_err()); - assert!(&Nonce::from_slice(&[0u8; 12]).is_ok()); -} - -#[test] -fn test_key_sizes() { - assert!(SecretKey::from_slice(&[0u8; 0]).is_err()); - assert!(SecretKey::from_slice(&[0u8; 1]).is_err()); - assert!(SecretKey::from_slice(&[0u8; 31]).is_err()); - assert!(SecretKey::from_slice(&[0u8; 64]).is_err()); - assert!(SecretKey::from_slice(&[0u8; 33]).is_err()); - assert!(SecretKey::from_slice(&[0u8; 32]).is_ok()); -} - -#[cfg(test)] -// Convenience function for testing. -fn init(key: &[u8], nonce: &[u8]) -> Result { - let mut chacha_state = InternalState { - state: [0_u32; 16], - is_ietf: true, - }; - - chacha_state - .init_state(&SecretKey::from_slice(key).unwrap(), nonce) - .unwrap(); - - Ok(chacha_state) -} - // Testing public functions in the module. #[cfg(test)] mod public { @@ -830,19 +700,21 @@ mod public { .is_ok()); } - #[test] + #[test] fn test_diff_keys_diff_output() { let keystream1 = keystream_block( &SecretKey::from_slice(&[0u8; 32]).unwrap(), &Nonce::from_slice(&[0u8; 12]).unwrap(), 0, - ).unwrap(); + ) + .unwrap(); let keystream2 = keystream_block( &SecretKey::from_slice(&[1u8; 32]).unwrap(), &Nonce::from_slice(&[0u8; 12]).unwrap(), 0, - ).unwrap(); + ) + .unwrap(); assert!(keystream1[..] != keystream2[..]); } @@ -853,13 +725,15 @@ mod public { &SecretKey::from_slice(&[0u8; 32]).unwrap(), &Nonce::from_slice(&[0u8; 12]).unwrap(), 0, - ).unwrap(); + ) + .unwrap(); let keystream2 = keystream_block( &SecretKey::from_slice(&[0u8; 32]).unwrap(), &Nonce::from_slice(&[1u8; 12]).unwrap(), 0, - ).unwrap(); + ) + .unwrap(); assert!(keystream1[..] != keystream2[..]); } @@ -870,13 +744,15 @@ mod public { &SecretKey::from_slice(&[0u8; 32]).unwrap(), &Nonce::from_slice(&[0u8; 12]).unwrap(), 0, - ).unwrap(); + ) + .unwrap(); let keystream2 = keystream_block( &SecretKey::from_slice(&[0u8; 32]).unwrap(), &Nonce::from_slice(&[0u8; 12]).unwrap(), 1, - ).unwrap(); + ) + .unwrap(); assert!(keystream1[..] != keystream2[..]); } @@ -885,7 +761,7 @@ mod public { #[cfg(not(feature = "no_std"))] mod proptest { use super::*; - + quickcheck! { fn prop_same_params_same_output(counter: u32) -> bool { let keystream1 = keystream_block( @@ -911,53 +787,33 @@ mod public { #[test] fn test_nonce_length() { - assert!(hchacha20( - &SecretKey::from_slice(&[0u8; 32]).unwrap(), - &[0u8; 16], - ).is_ok()); + assert!(hchacha20(&SecretKey::from_slice(&[0u8; 32]).unwrap(), &[0u8; 16],).is_ok()); - assert!(hchacha20( - &SecretKey::from_slice(&[0u8; 32]).unwrap(), - &[0u8; 17], - ).is_err()); + assert!(hchacha20(&SecretKey::from_slice(&[0u8; 32]).unwrap(), &[0u8; 17],).is_err()); - assert!(hchacha20( - &SecretKey::from_slice(&[0u8; 32]).unwrap(), - &[0u8; 15], - ).is_err()); + assert!(hchacha20(&SecretKey::from_slice(&[0u8; 32]).unwrap(), &[0u8; 15],).is_err()); - assert!(hchacha20( - &SecretKey::from_slice(&[0u8; 32]).unwrap(), - &[0u8; 0], - ).is_err()); + assert!(hchacha20(&SecretKey::from_slice(&[0u8; 32]).unwrap(), &[0u8; 0],).is_err()); } #[test] fn test_diff_keys_diff_output() { - let keystream1 = hchacha20( - &SecretKey::from_slice(&[0u8; 32]).unwrap(), - &[0u8; 16], - ).unwrap(); + let keystream1 = + hchacha20(&SecretKey::from_slice(&[0u8; 32]).unwrap(), &[0u8; 16]).unwrap(); - let keystream2 = hchacha20( - &SecretKey::from_slice(&[1u8; 32]).unwrap(), - &[0u8; 16], - ).unwrap(); + let keystream2 = + hchacha20(&SecretKey::from_slice(&[1u8; 32]).unwrap(), &[0u8; 16]).unwrap(); assert!(keystream1 != keystream2); } #[test] fn test_diff_nonce_diff_output() { - let keystream1 = hchacha20( - &SecretKey::from_slice(&[0u8; 32]).unwrap(), - &[0u8; 16], - ).unwrap(); + let keystream1 = + hchacha20(&SecretKey::from_slice(&[0u8; 32]).unwrap(), &[0u8; 16]).unwrap(); - let keystream2 = hchacha20( - &SecretKey::from_slice(&[0u8; 32]).unwrap(), - &[1u8; 16], - ).unwrap(); + let keystream2 = + hchacha20(&SecretKey::from_slice(&[0u8; 32]).unwrap(), &[1u8; 16]).unwrap(); assert!(keystream1 != keystream2); } @@ -967,13 +823,189 @@ mod public { // Testing private functions in the module. #[cfg(test)] mod private { + use super::*; // One function tested per submodule. - mod test_init_state {} + mod test_init_state { + use super::*; + + #[test] + fn test_nonce_length() { + let mut chacha_state = InternalState { + state: [0_u32; 16], + is_ietf: true, + }; + + assert!(chacha_state + .init_state(&SecretKey::from_slice(&[0u8; 32]).unwrap(), &[0u8; 15]) + .is_err()); + assert!(chacha_state + .init_state(&SecretKey::from_slice(&[0u8; 32]).unwrap(), &[0u8; 10]) + .is_err()); + assert!(chacha_state + .init_state(&SecretKey::from_slice(&[0u8; 32]).unwrap(), &[0u8; 12]) + .is_ok()); + + let mut hchacha_state = InternalState { + state: [0_u32; 16], + is_ietf: false, + }; + + assert!(hchacha_state + .init_state(&SecretKey::from_slice(&[0u8; 32]).unwrap(), &[0u8; 15]) + .is_err()); + assert!(hchacha_state + .init_state(&SecretKey::from_slice(&[0u8; 32]).unwrap(), &[0u8; 17]) + .is_err()); + assert!(hchacha_state + .init_state(&SecretKey::from_slice(&[0u8; 32]).unwrap(), &[0u8; 16]) + .is_ok()); + } + + // Proptests. Only exectued when NOT testing no_std. + #[cfg(not(feature = "no_std"))] + mod proptest { + use super::*; + + quickcheck! { + // Always fail to intialize state while the nonce is not + // the correct length. If it is correct length, never panic. + fn prop_test_nonce_length_ietf(nonce: Vec) -> bool { + let mut chacha_state_ietf = InternalState { + state: [0_u32; 16], + is_ietf: true, + }; + + let sk = SecretKey::from_slice(&[0u8; 32]).unwrap(); + + if nonce.len() != IETF_CHACHA_NONCESIZE { + let res = if chacha_state_ietf + .init_state(&sk, &nonce[..]).is_err() { + true + } else { + false + }; + + return res; + } else { + let res = if chacha_state_ietf + .init_state(&sk, &nonce[..]).is_ok() { + true + } else { + false + }; + + return res; + } + } + } + + quickcheck! { + // Always fail to intialize state while the nonce is not + // the correct length. If it is correct length, never panic. + fn prop_test_nonce_length_hchacha(nonce: Vec) -> bool { + let mut chacha_state_hchacha = InternalState { + state: [0_u32; 16], + is_ietf: false, + }; + + let sk = SecretKey::from_slice(&[0u8; 32]).unwrap(); - mod test_process_block {} + if nonce.len() != HCHACHA_NONCESIZE { + let res = if chacha_state_hchacha + .init_state(&sk, &nonce[..]).is_err() { + true + } else { + false + }; - mod test_serialize_block {} + return res; + } else { + let res = if chacha_state_hchacha + .init_state(&sk, &nonce[..]).is_ok() { + true + } else { + false + }; + + return res; + } + } + } + } + } + + mod test_process_block { + use super::*; + #[test] + fn test_process_block_wrong_combination_of_variant_and_nonce() { + let mut chacha_state_ietf = InternalState { + state: [0_u32; 16], + is_ietf: true, + }; + chacha_state_ietf + .init_state(&SecretKey::from_slice(&[0u8; 32]).unwrap(), &[0u8; 12]) + .unwrap(); + + let mut chacha_state_hchacha = InternalState { + state: [0_u32; 16], + is_ietf: false, + }; + + chacha_state_hchacha + .init_state(&SecretKey::from_slice(&[0u8; 32]).unwrap(), &[0u8; 16]) + .unwrap(); + + assert!(chacha_state_hchacha.process_block(Some(1)).is_err()); + assert!(chacha_state_ietf.process_block(None).is_err()); + assert!(chacha_state_hchacha.process_block(None).is_ok()); + assert!(chacha_state_ietf.process_block(Some(1)).is_ok()); + } + } + + mod test_serialize_block { + use super::*; + + #[test] + fn test_wrong_combination_of_variant_and_dst_out() { + let mut chacha_state_ietf = InternalState { + state: [0_u32; 16], + is_ietf: true, + }; + + chacha_state_ietf + .init_state(&SecretKey::from_slice(&[0u8; 32]).unwrap(), &[0u8; 12]) + .unwrap(); + + let mut chacha_state_hchacha = InternalState { + state: [0_u32; 16], + is_ietf: false, + }; + + chacha_state_hchacha + .init_state(&SecretKey::from_slice(&[0u8; 32]).unwrap(), &[0u8; 16]) + .unwrap(); + + let mut hchacha_out = [0u8; HCHACHA_OUTSIZE]; + let mut ietf_out = [0u8; CHACHA_BLOCKSIZE]; + + let ietf_src = chacha_state_ietf.process_block(Some(1)).unwrap(); + let hchacha_src = chacha_state_hchacha.process_block(None).unwrap(); + + assert!(chacha_state_hchacha + .serialize_block(&hchacha_src, &mut ietf_out) + .is_err()); + assert!(chacha_state_ietf + .serialize_block(&ietf_src, &mut hchacha_out) + .is_err()); + assert!(chacha_state_hchacha + .serialize_block(&hchacha_src, &mut hchacha_out) + .is_ok()); + assert!(chacha_state_ietf + .serialize_block(&ietf_src, &mut ietf_out) + .is_ok()); + } + } } // Testing any test vectors that aren't put into library's /tests folder. @@ -981,6 +1013,20 @@ mod private { mod test_vectors { use super::*; + // Convenience function for testing. + fn init(key: &[u8], nonce: &[u8]) -> Result { + let mut chacha_state = InternalState { + state: [0_u32; 16], + is_ietf: true, + }; + + chacha_state + .init_state(&SecretKey::from_slice(key).unwrap(), nonce) + .unwrap(); + + Ok(chacha_state) + } + #[test] fn rfc8439_quarter_round_results() { let mut chacha_state = InternalState { From 328b16e91c3e6fdcce9383fad8a191fbaeec506d Mon Sep 17 00:00:00 2001 From: brycx Date: Tue, 29 Jan 2019 13:52:59 +0100 Subject: [PATCH 07/40] Tests: Copy appropriate tests from chacha20 to xchacha20 and adjust nonce size. --- src/hazardous/stream/xchacha20.rs | 281 ++++++++++++++++++++++++++++++ 1 file changed, 281 insertions(+) diff --git a/src/hazardous/stream/xchacha20.rs b/src/hazardous/stream/xchacha20.rs index 604034c6..c5f4b73c 100644 --- a/src/hazardous/stream/xchacha20.rs +++ b/src/hazardous/stream/xchacha20.rs @@ -182,3 +182,284 @@ fn test_pass_on_one_iter_max_initial_counter() { ) .unwrap(); } + +// +// The tests below are the same tests as the ones in `chacha20` +// but with a bigger nonce. It's debatable whether this is needed, but right +// now I'm keeping them as they don't seem to bring any disadvantages. +// + +// Testing public functions in the module. +#[cfg(test)] +mod public { + use super::*; + // One function tested per submodule. + + // encrypt()/decrypt() are tested together here + // since decrypt() is just a wrapper around encrypt() + // and so only the decrypt() function is called + mod test_encrypt_decrypt { + use super::*; + #[test] + fn test_fail_on_initial_counter_overflow() { + let mut dst = [0u8; 65]; + + assert!(decrypt( + &SecretKey::from_slice(&[0u8; 32]).unwrap(), + &Nonce::from_slice(&[0u8; 24]).unwrap(), + u32::max_value(), + &[0u8; 65], + &mut dst, + ) + .is_err()); + } + + #[test] + fn test_pass_on_one_iter_max_initial_counter() { + let mut dst = [0u8; 64]; + // Should pass because only one iteration is completed, so block_counter will + // not increase + assert!(decrypt( + &SecretKey::from_slice(&[0u8; 32]).unwrap(), + &Nonce::from_slice(&[0u8; 24]).unwrap(), + u32::max_value(), + &[0u8; 64], + &mut dst, + ) + .is_ok()); + } + + #[test] + fn test_fail_on_empty_plaintext() { + let mut dst = [0u8; 64]; + + assert!(decrypt( + &SecretKey::from_slice(&[0u8; 32]).unwrap(), + &Nonce::from_slice(&[0u8; 24]).unwrap(), + 0, + &[0u8; 0], + &mut dst, + ) + .is_err()); + } + + #[test] + fn test_dst_out_length() { + let mut dst_small = [0u8; 64]; + + assert!(decrypt( + &SecretKey::from_slice(&[0u8; 32]).unwrap(), + &Nonce::from_slice(&[0u8; 24]).unwrap(), + 0, + &[0u8; 128], + &mut dst_small, + ) + .is_err()); + + let mut dst = [0u8; 64]; + + assert!(decrypt( + &SecretKey::from_slice(&[0u8; 32]).unwrap(), + &Nonce::from_slice(&[0u8; 24]).unwrap(), + 0, + &[0u8; 64], + &mut dst, + ) + .is_ok()); + + let mut dst_big = [0u8; 64]; + + assert!(decrypt( + &SecretKey::from_slice(&[0u8; 32]).unwrap(), + &Nonce::from_slice(&[0u8; 24]).unwrap(), + 0, + &[0u8; 32], + &mut dst_big, + ) + .is_ok()); + } + + // Proptests. Only exectued when NOT testing no_std. + #[cfg(not(feature = "no_std"))] + mod proptest { + use super::*; + + /// Given a input length `a` find out how many times + /// the initial counter on encrypt()/decrypt() would + /// increase. + fn counter_increase_times(a: f32) -> u32 { + // Otherwise a overvlowing subtration would happen + if a <= 64f32 { + return 0; + } + + let check_with_floor = (a / 64f32).floor(); + let actual = a / 64f32; + + assert!(actual >= check_with_floor); + // Subtract one because the first 64 in length + // the counter does not increase + if actual > check_with_floor { + (actual.ceil() as u32) - 1 + } else { + (actual as u32) - 1 + } + } + + quickcheck! { + // Encrypting input, and then decrypting should always yield the same input. + fn prop_encrypt_decrypt_same_input(input: Vec, block_counter: u32) -> bool { + let pt = if input.is_empty() { + vec![1u8; 10] + } else { + input + }; + + let mut dst_out_ct = vec![0u8; pt.len()]; + let mut dst_out_pt = vec![0u8; pt.len()]; + + // If `block_counter` is high enough check if it would overflow + if counter_increase_times(pt.len() as f32).checked_add(block_counter).is_none() { + // Overflow will occur and the operation should fail + let res = if encrypt( + &SecretKey::from_slice(&[0u8; 32]).unwrap(), + &Nonce::from_slice(&[0u8; 24]).unwrap(), + block_counter, + &pt[..], + &mut dst_out_ct, + ).is_err() { true } else { false }; + + return res; + } else { + + encrypt( + &SecretKey::from_slice(&[0u8; 32]).unwrap(), + &Nonce::from_slice(&[0u8; 24]).unwrap(), + block_counter, + &pt[..], + &mut dst_out_ct, + ).unwrap(); + + decrypt( + &SecretKey::from_slice(&[0u8; 32]).unwrap(), + &Nonce::from_slice(&[0u8; 24]).unwrap(), + block_counter, + &dst_out_ct[..], + &mut dst_out_pt, + ).unwrap(); + + return dst_out_pt == pt; + } + } + } + + 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) + } + } + } + } +} \ No newline at end of file From ba35814264f97b2a1fcfaf1bb3510164f5dccfaf Mon Sep 17 00:00:00 2001 From: brycx Date: Tue, 29 Jan 2019 14:15:27 +0100 Subject: [PATCH 08/40] Tests: Comment on those test cases that are directly related to a bug form the issue tracker and add link to bug. --- src/hazardous/aead/chacha20poly1305.rs | 5 +- src/hazardous/aead/xchacha20poly1305.rs | 3 + src/hazardous/hash/blake2b.rs | 75 +++++++++++++++++++++++++ src/hazardous/hash/sha512.rs | 3 + src/hazardous/mac/hmac.rs | 3 + src/hazardous/mac/poly1305.rs | 3 + src/hazardous/xof/cshake.rs | 2 + 7 files changed, 93 insertions(+), 1 deletion(-) diff --git a/src/hazardous/aead/chacha20poly1305.rs b/src/hazardous/aead/chacha20poly1305.rs index 65efab1e..05ac1ada 100644 --- a/src/hazardous/aead/chacha20poly1305.rs +++ b/src/hazardous/aead/chacha20poly1305.rs @@ -266,6 +266,7 @@ mod public { use super::*; #[test] + /// Related bug: https://github.com/brycx/orion/issues/52 fn test_dst_out_length() { let mut dst_out_ct = [0u8; 80]; // 64 + Poly1305TagLen let mut dst_out_ct_less = [0u8; 79]; // 64 + Poly1305TagLen - 1 @@ -280,7 +281,8 @@ mod public { &mut dst_out_ct, ) .is_ok()); - + + // Related bug: #52 assert!(seal( &SecretKey::from_slice(&[0u8; 32]).unwrap(), &Nonce::from_slice(&[0u8; 12]).unwrap(), @@ -290,6 +292,7 @@ mod public { ) .is_ok()); + // Related bug: #52 assert!(seal( &SecretKey::from_slice(&[0u8; 32]).unwrap(), &Nonce::from_slice(&[0u8; 12]).unwrap(), diff --git a/src/hazardous/aead/xchacha20poly1305.rs b/src/hazardous/aead/xchacha20poly1305.rs index d0cdad5b..efb8532c 100644 --- a/src/hazardous/aead/xchacha20poly1305.rs +++ b/src/hazardous/aead/xchacha20poly1305.rs @@ -164,6 +164,7 @@ mod public { use super::*; #[test] + /// Related bug: https://github.com/brycx/orion/issues/52 fn test_dst_out_length() { let mut dst_out_ct = [0u8; 80]; // 64 + Poly1305TagLen let mut dst_out_ct_less = [0u8; 79]; // 64 + Poly1305TagLen - 1 @@ -179,6 +180,7 @@ mod public { ) .is_ok()); + // Related bug: #52 assert!(seal( &SecretKey::from_slice(&[0u8; 32]).unwrap(), &Nonce::from_slice(&[0u8; 24]).unwrap(), @@ -188,6 +190,7 @@ mod public { ) .is_ok()); + // Related bug: #52 assert!(seal( &SecretKey::from_slice(&[0u8; 32]).unwrap(), &Nonce::from_slice(&[0u8; 24]).unwrap(), diff --git a/src/hazardous/hash/blake2b.rs b/src/hazardous/hash/blake2b.rs index 6552b05c..a5d8804c 100644 --- a/src/hazardous/hash/blake2b.rs +++ b/src/hazardous/hash/blake2b.rs @@ -551,6 +551,7 @@ fn double_finalize_with_reset_no_update_ok() { } #[test] +/// Related bug: https://github.com/brycx/orion/issues/28 fn update_after_finalize_err() { let data = "what do ya want for nothing?".as_bytes(); @@ -599,6 +600,7 @@ fn err_on_keyed_switch_on_reset() { } #[test] +/// Related bug: https://github.com/brycx/orion/issues/46 fn reset_after_update_correct_resets() { let state_1 = init(None, 64).unwrap(); @@ -618,6 +620,7 @@ fn reset_after_update_correct_resets() { } #[test] +/// Related bug: https://github.com/brycx/orion/issues/46 fn reset_after_update_correct_resets_and_verify() { // In non-keyed mode let mut state_1 = init(None, 64).unwrap(); @@ -678,3 +681,75 @@ fn test_streaming_consistency() { assert!(state.finalize().unwrap().as_bytes() == digest_one_shot.as_bytes()); } } + +// Testing public functions in the module. +#[cfg(test)] +mod public { + + // One function tested per submodule. + + mod function_0 { + #[test] + fn test_() { + assert!(1+1 == 2); + } + + // Proptests. Only exectued when NOT testing no_std. + #[cfg(not(feature = "no_std"))] + mod proptest { + } + } + + mod function_1 { + #[test] + fn test_() { + assert!(2+2 == 4); + } + + // Proptests. Only exectued when NOT testing no_std. + #[cfg(not(feature = "no_std"))] + mod proptest { + } + } +} + +// Testing private functions in the module. +#[cfg(test)] +mod private { + + // One function tested per submodule. + + mod function_0 { + #[test] + fn test_() { + assert!(1+1 == 2); + } + + // Proptests. Only exectued when NOT testing no_std. + #[cfg(not(feature = "no_std"))] + mod proptest { + } + } + + mod function_1 { + #[test] + fn test_() { + assert!(2+2 == 4); + } + + // Proptests. Only exectued when NOT testing no_std. + #[cfg(not(feature = "no_std"))] + mod proptest { + } + } +} + +// Testing any test vectors that aren't put into library's /tests folder. +#[cfg(test)] +mod test_vectors { + + #[test] + fn rfc8769_test_case_0() { + + } +} diff --git a/src/hazardous/hash/sha512.rs b/src/hazardous/hash/sha512.rs index 623d9a8b..64fdeea5 100644 --- a/src/hazardous/hash/sha512.rs +++ b/src/hazardous/hash/sha512.rs @@ -409,6 +409,7 @@ fn double_finalize_with_reset_no_update_ok() { } #[test] +/// Related bug: https://github.com/brycx/orion/issues/28 fn update_after_finalize_err() { let data = "what do ya want for nothing?".as_bytes(); @@ -441,6 +442,7 @@ fn double_reset_ok() { } #[test] +/// Related bug: https://github.com/brycx/orion/issues/46 fn reset_after_update_correct_resets() { let state_1 = init(); @@ -456,6 +458,7 @@ fn reset_after_update_correct_resets() { } #[test] +/// Related bug: https://github.com/brycx/orion/issues/46 fn reset_after_update_correct_resets_and_verify() { let mut state_1 = init(); state_1.update(b"Tests").unwrap(); diff --git a/src/hazardous/mac/hmac.rs b/src/hazardous/mac/hmac.rs index 4a5b6d69..1ebaaff8 100644 --- a/src/hazardous/mac/hmac.rs +++ b/src/hazardous/mac/hmac.rs @@ -319,6 +319,7 @@ fn double_finalize_with_reset_no_update_ok() { } #[test] +/// Related bug: https://github.com/brycx/orion/issues/28 fn update_after_finalize_err() { let secret_key = SecretKey::from_slice("Jefe".as_bytes()).unwrap(); let data = "what do ya want for nothing?".as_bytes(); @@ -354,6 +355,7 @@ fn double_reset_ok() { } #[test] +/// Related bug: https://github.com/brycx/orion/issues/46 fn reset_after_update_correct_resets() { let secret_key = SecretKey::from_slice("Jefe".as_bytes()).unwrap(); @@ -368,6 +370,7 @@ fn reset_after_update_correct_resets() { } #[test] +/// Related bug: https://github.com/brycx/orion/issues/46 fn reset_after_update_correct_resets_and_verify() { let secret_key = SecretKey::from_slice("Jefe".as_bytes()).unwrap(); diff --git a/src/hazardous/mac/poly1305.rs b/src/hazardous/mac/poly1305.rs index c33d3159..2ef6f7d2 100644 --- a/src/hazardous/mac/poly1305.rs +++ b/src/hazardous/mac/poly1305.rs @@ -494,6 +494,7 @@ fn double_finalize_with_reset_no_update_ok() { } #[test] +/// Related bug: https://github.com/brycx/orion/issues/28 fn update_after_finalize_err() { let mut poly1305_state = init(&OneTimeKey::from_slice(&[0u8; 32]).unwrap()); @@ -524,6 +525,7 @@ fn double_reset_ok() { } #[test] +/// Related bug: https://github.com/brycx/orion/issues/46 fn reset_after_update_correct_resets() { let secret_key = OneTimeKey::from_slice(&[0u8; 32]).unwrap(); @@ -542,6 +544,7 @@ fn reset_after_update_correct_resets() { } #[test] +/// Related bug: https://github.com/brycx/orion/issues/46 fn reset_after_update_correct_resets_and_verify() { let secret_key = OneTimeKey::from_slice(&[0u8; 32]).unwrap(); diff --git a/src/hazardous/xof/cshake.rs b/src/hazardous/xof/cshake.rs index 82629289..d24972a6 100644 --- a/src/hazardous/xof/cshake.rs +++ b/src/hazardous/xof/cshake.rs @@ -429,6 +429,7 @@ fn double_finalize_with_reset_no_update_ok() { } #[test] +/// Related bug: https://github.com/brycx/orion/issues/28 fn update_after_finalize_err() { let input = b"\x00\x01\x02\x03"; let custom = b""; @@ -474,6 +475,7 @@ fn double_reset_ok() { } #[test] +/// Related bug: https://github.com/brycx/orion/issues/46 fn reset_after_update_correct_resets() { let input = b"\x00\x01\x02\x03"; let custom = b"Hello world"; From 2d08c1a77dbd015aead546878b9455d10110b3f9 Mon Sep 17 00:00:00 2001 From: brycx Date: Tue, 29 Jan 2019 14:48:06 +0100 Subject: [PATCH 09/40] Blake2b: Use uverflowing_add() on increment_offset() such that it can overflow without panicking on debug builds. --- src/hazardous/aead/chacha20poly1305.rs | 2 +- src/hazardous/hash/blake2b.rs | 134 ++++++++++++++++--------- src/hazardous/stream/xchacha20.rs | 2 +- 3 files changed, 87 insertions(+), 51 deletions(-) diff --git a/src/hazardous/aead/chacha20poly1305.rs b/src/hazardous/aead/chacha20poly1305.rs index 05ac1ada..7908e42c 100644 --- a/src/hazardous/aead/chacha20poly1305.rs +++ b/src/hazardous/aead/chacha20poly1305.rs @@ -281,7 +281,7 @@ mod public { &mut dst_out_ct, ) .is_ok()); - + // Related bug: #52 assert!(seal( &SecretKey::from_slice(&[0u8; 32]).unwrap(), diff --git a/src/hazardous/hash/blake2b.rs b/src/hazardous/hash/blake2b.rs index a5d8804c..976b6bac 100644 --- a/src/hazardous/hash/blake2b.rs +++ b/src/hazardous/hash/blake2b.rs @@ -205,9 +205,11 @@ impl Blake2b { #[inline(always)] /// Increment the internal states offset value `t`. fn increment_offset(&mut self, value: u64) { - self.t[0] += value; - if self.t[0] < value { - self.t[1] += 1; + let (res, was_overflow) = self.t[0].overflowing_add(value); + self.t[0] = res; + if was_overflow { + // If this panics size limit is reached. + self.t[1] = self.t[1].checked_add(1).unwrap(); } } @@ -685,71 +687,105 @@ fn test_streaming_consistency() { // Testing public functions in the module. #[cfg(test)] mod public { - - // One function tested per submodule. - + + // One function tested per submodule. + mod function_0 { - #[test] - fn test_() { - assert!(1+1 == 2); - } - + #[test] + fn test_() { + assert!(1 + 1 == 2); + } + // Proptests. Only exectued when NOT testing no_std. #[cfg(not(feature = "no_std"))] - mod proptest { + mod proptest {} + } + + mod function_1 { + #[test] + fn test_() { + assert!(2 + 2 == 4); } - } - - mod function_1 { - #[test] - fn test_() { - assert!(2+2 == 4); - } - + // Proptests. Only exectued when NOT testing no_std. #[cfg(not(feature = "no_std"))] - mod proptest { - } - } + mod proptest {} + } } // Testing private functions in the module. #[cfg(test)] mod private { - - // One function tested per submodule. - - mod function_0 { - #[test] - fn test_() { - assert!(1+1 == 2); - } - + use super::*; + // One function tested per submodule. + + mod increment_counter { + use super::*; + + #[test] + fn test_counter_increase_values() { + let mut context = Blake2b { + init_state: [0u64; 8], + internal_state: IV, + buffer: [0u8; BLAKE2B_BLOCKSIZE], + leftover: 0, + t: [0u64; 2], + f: [0u64; 2], + is_finalized: false, + is_keyed: false, + size: 1, + }; + + context.increment_offset(1); + assert!(context.t == [1u64, 0u64]); + context.increment_offset(17); + assert!(context.t == [18u64, 0u64]); + context.increment_offset(12); + assert!(context.t == [30u64, 0u64]); + // Overflow + context.increment_offset(u64::max_value()); + assert!(context.t == [29u64, 1u64]); + } + + #[test] + #[should_panic] + fn test_panic_on_second_overflow() { + let mut context = Blake2b { + init_state: [0u64; 8], + internal_state: IV, + buffer: [0u8; BLAKE2B_BLOCKSIZE], + leftover: 0, + t: [1u64, u64::max_value()], + f: [0u64; 2], + is_finalized: false, + is_keyed: false, + size: 1, + }; + + context.increment_offset(u64::max_value()); + } + // Proptests. Only exectued when NOT testing no_std. #[cfg(not(feature = "no_std"))] - mod proptest { + mod proptest {} + } + + mod function_1 { + #[test] + fn test_() { + assert!(2 + 2 == 4); } - } - - mod function_1 { - #[test] - fn test_() { - assert!(2+2 == 4); - } - + // Proptests. Only exectued when NOT testing no_std. #[cfg(not(feature = "no_std"))] - mod proptest { - } - } + mod proptest {} + } } // Testing any test vectors that aren't put into library's /tests folder. #[cfg(test)] mod test_vectors { - - #[test] - fn rfc8769_test_case_0() { - - } + + #[test] + fn rfc8769_test_case_0() {} } diff --git a/src/hazardous/stream/xchacha20.rs b/src/hazardous/stream/xchacha20.rs index c5f4b73c..db18e743 100644 --- a/src/hazardous/stream/xchacha20.rs +++ b/src/hazardous/stream/xchacha20.rs @@ -462,4 +462,4 @@ mod public { } } } -} \ No newline at end of file +} From 27a6d6c34e476c64577532d8d471abf63025df61 Mon Sep 17 00:00:00 2001 From: brycx Date: Tue, 29 Jan 2019 14:50:08 +0100 Subject: [PATCH 10/40] Tests: Finish setup of tests for private functions in Blake2b. --- src/hazardous/hash/blake2b.rs | 25 +------------------------ 1 file changed, 1 insertion(+), 24 deletions(-) diff --git a/src/hazardous/hash/blake2b.rs b/src/hazardous/hash/blake2b.rs index 976b6bac..55423cb6 100644 --- a/src/hazardous/hash/blake2b.rs +++ b/src/hazardous/hash/blake2b.rs @@ -764,28 +764,5 @@ mod private { context.increment_offset(u64::max_value()); } - - // Proptests. Only exectued when NOT testing no_std. - #[cfg(not(feature = "no_std"))] - mod proptest {} - } - - mod function_1 { - #[test] - fn test_() { - assert!(2 + 2 == 4); - } - - // Proptests. Only exectued when NOT testing no_std. - #[cfg(not(feature = "no_std"))] - mod proptest {} } -} - -// Testing any test vectors that aren't put into library's /tests folder. -#[cfg(test)] -mod test_vectors { - - #[test] - fn rfc8769_test_case_0() {} -} +} \ No newline at end of file From eb76c20f6c443954ad0396cb99ffa436cc070f05 Mon Sep 17 00:00:00 2001 From: brycx Date: Tue, 29 Jan 2019 16:23:00 +0100 Subject: [PATCH 11/40] Tests: Continue reorganizing tests and adding proptests to Blake2b --- src/hazardous/hash/blake2b.rs | 539 +++++++++++++++++++++------------- 1 file changed, 343 insertions(+), 196 deletions(-) diff --git a/src/hazardous/hash/blake2b.rs b/src/hazardous/hash/blake2b.rs index 55423cb6..0fcdb16e 100644 --- a/src/hazardous/hash/blake2b.rs +++ b/src/hazardous/hash/blake2b.rs @@ -209,7 +209,7 @@ impl Blake2b { self.t[0] = res; if was_overflow { // If this panics size limit is reached. - self.t[1] = self.t[1].checked_add(1).unwrap(); + self.t[1] = self.t[1].checked_add(1).unwrap(); } } @@ -485,231 +485,378 @@ fn finalize_and_verify_true() { ); } -#[test] -fn test_init_bad_sizes() { - assert!(init(None, 0).is_err()); - assert!(init(None, 65).is_err()); - assert!(init(None, 64).is_ok()); - assert!(init(None, 1).is_ok()); -} - -#[test] -fn test_hasher_interface() { - let _digest_256 = Hasher::Blake2b256.digest(b"Test").unwrap(); - let _digest_384 = Hasher::Blake2b384.digest(b"Test").unwrap(); - let _digest_512 = Hasher::Blake2b512.digest(b"Test").unwrap(); - - let _state_256 = Hasher::Blake2b256.init().unwrap(); - let _state_384 = Hasher::Blake2b384.init().unwrap(); - let _state_512 = Hasher::Blake2b512.init().unwrap(); -} - -#[test] -fn double_finalize_err() { - let data = "what do ya want for nothing?".as_bytes(); - - let mut state = init(None, 64).unwrap(); - state.update(data).unwrap(); - let _ = state.finalize().unwrap(); - assert!(state.finalize().is_err()); -} - -#[test] -fn double_finalize_with_reset_ok_not_keyed() { - let data = "what do ya want for nothing?".as_bytes(); - - let mut state = init(None, 64).unwrap(); - state.update(data).unwrap(); - let one = state.finalize().unwrap(); - state.reset(None).unwrap(); - state.update(data).unwrap(); - let two = state.finalize().unwrap(); - assert_eq!(one.as_bytes(), two.as_bytes()); -} +// Testing public functions in the module. +#[cfg(test)] +mod public { + use super::*; -#[test] -fn double_finalize_with_reset_ok_keyed() { - let secret_key = SecretKey::from_slice(b"Testing").unwrap(); - let data = "what do ya want for nothing?".as_bytes(); + // One function tested per submodule. - let mut state = init(Some(&secret_key), 64).unwrap(); - state.update(data).unwrap(); - let one = state.finalize().unwrap(); - state.reset(Some(&secret_key)).unwrap(); - state.update(data).unwrap(); - let two = state.finalize().unwrap(); - assert_eq!(one.as_bytes(), two.as_bytes()); -} + mod test_init { + use super::*; -#[test] -fn double_finalize_with_reset_no_update_ok() { - let data = "what do ya want for nothing?".as_bytes(); + #[test] + fn test_init_size() { + assert!(init(None, 0).is_err()); + assert!(init(None, 65).is_err()); + assert!(init(None, 64).is_ok()); + assert!(init(None, 1).is_ok()); + } - let mut state = init(None, 64).unwrap(); - state.update(data).unwrap(); - let _ = state.finalize().unwrap(); - state.reset(None).unwrap(); - let _ = state.finalize().unwrap(); -} + // Proptests. Only exectued when NOT testing no_std. + #[cfg(not(feature = "no_std"))] + mod proptest { + use super::*; + + quickcheck! { + /// Given a valid size parameter, init should always pass. If size + /// is invalid, then init should always fail. + fn prop_init_size_no_key(size: usize) -> bool { + + if size >= 1 && size <= BLAKE2B_OUTSIZE { + let res = if init(None, size).is_ok() { + true + } else { + false + }; + + return res; + } else { + let res = if init(None, size).is_err() { + true + } else { + false + }; + + return res; + } + } + } + } + } -#[test] -/// Related bug: https://github.com/brycx/orion/issues/28 -fn update_after_finalize_err() { - let data = "what do ya want for nothing?".as_bytes(); + mod test_hasher { + use super::*; - let mut state = init(None, 64).unwrap(); - state.update(data).unwrap(); - let _ = state.finalize().unwrap(); - assert!(state.update(data).is_err()); -} + #[test] + fn test_hasher_interface_no_panic_and_same_result() { + let digest_256 = Hasher::Blake2b256.digest(b"Test").unwrap(); + let digest_384 = Hasher::Blake2b384.digest(b"Test").unwrap(); + let digest_512 = Hasher::Blake2b512.digest(b"Test").unwrap(); -#[test] -fn update_after_finalize_with_reset_ok() { - let data = "what do ya want for nothing?".as_bytes(); + assert_eq!(digest_256, Hasher::Blake2b256.digest(b"Test").unwrap(),); - let mut state = init(None, 64).unwrap(); - state.update(data).unwrap(); - let _ = state.finalize().unwrap(); - state.reset(None).unwrap(); - state.update(data).unwrap(); -} + assert_eq!(digest_384, Hasher::Blake2b384.digest(b"Test").unwrap(),); -#[test] -fn double_reset_ok() { - let data = "what do ya want for nothing?".as_bytes(); + assert_eq!(digest_512, Hasher::Blake2b512.digest(b"Test").unwrap(),); - let mut state = init(None, 64).unwrap(); - state.update(data).unwrap(); - let _ = state.finalize().unwrap(); - state.reset(None).unwrap(); - state.reset(None).unwrap(); -} + let _state_256 = Hasher::Blake2b256.init().unwrap(); + let _state_384 = Hasher::Blake2b384.init().unwrap(); + let _state_512 = Hasher::Blake2b512.init().unwrap(); + } -#[test] -fn err_on_keyed_switch_on_reset() { - let secret_key = SecretKey::from_slice(b"Testing").unwrap(); - let data = "what do ya want for nothing?".as_bytes(); + // Proptests. Only exectued when NOT testing no_std. + #[cfg(not(feature = "no_std"))] + mod proptest { + use super::*; + + quickcheck! { + /// Given some data, .digest() should never fail in practice and should + /// produce the same output on a second call. + /// Only if data is unreasonably large. + fn prop_hasher_digest_no_panic_and_same_result(data: Vec) -> bool { + let d256 = Hasher::Blake2b256.digest(&data[..]).unwrap(); + let d384 = Hasher::Blake2b384.digest(&data[..]).unwrap(); + let d512 = Hasher::Blake2b512.digest(&data[..]).unwrap(); + + let d256_re = Hasher::Blake2b256.digest(&data[..]).unwrap(); + let d384_re = Hasher::Blake2b384.digest(&data[..]).unwrap(); + let d512_re = Hasher::Blake2b512.digest(&data[..]).unwrap(); + + (d256 == d256_re) && (d384 == d384_re) && (d512 == d512_re) + } + } - let mut state = init(Some(&secret_key), 64).unwrap(); - state.update(data).unwrap(); - let _ = state.finalize().unwrap(); - assert!(state.reset(None).is_err()); + quickcheck! { + /// .init() should never fail. + fn prop_hasher_init_no_panic() -> bool { + let _d256 = Hasher::Blake2b256.init().unwrap(); + let _d384 = Hasher::Blake2b384.init().unwrap(); + let _d512 = Hasher::Blake2b512.init().unwrap(); - let mut state_second = init(None, 64).unwrap(); - state_second.update(data).unwrap(); - let _ = state_second.finalize().unwrap(); - assert!(state_second.reset(Some(&secret_key)).is_err()); -} + true + } + } + } + } -#[test] -/// Related bug: https://github.com/brycx/orion/issues/46 -fn reset_after_update_correct_resets() { - let state_1 = init(None, 64).unwrap(); - - let mut state_2 = init(None, 64).unwrap(); - state_2.update(b"Tests").unwrap(); - state_2.reset(None).unwrap(); - - assert_eq!(state_1.init_state, state_2.init_state); - assert_eq!(state_1.internal_state, state_2.internal_state); - assert_eq!(state_1.buffer[..], state_2.buffer[..]); - assert_eq!(state_1.leftover, state_2.leftover); - assert_eq!(state_1.t, state_2.t); - assert_eq!(state_1.f, state_2.f); - assert_eq!(state_1.is_finalized, state_2.is_finalized); - assert_eq!(state_1.is_keyed, state_2.is_keyed); - assert_eq!(state_1.size, state_2.size); -} + mod test_streaming_interface { + use super::*; -#[test] -/// Related bug: https://github.com/brycx/orion/issues/46 -fn reset_after_update_correct_resets_and_verify() { - // In non-keyed mode - let mut state_1 = init(None, 64).unwrap(); - state_1.update(b"Tests").unwrap(); - let d1 = state_1.finalize().unwrap(); - - let mut state_2 = init(None, 64).unwrap(); - state_2.update(b"Tests").unwrap(); - state_2.reset(None).unwrap(); - state_2.update(b"Tests").unwrap(); - let d2 = state_2.finalize().unwrap(); - - assert_eq!(d1, d2); - - // In keyed mode - let key = SecretKey::from_slice(&[0u8; 64]).unwrap(); - let mut state_1 = init(Some(&key), 64).unwrap(); - state_1.update(b"Tests").unwrap(); - let d1 = state_1.finalize().unwrap(); - - let mut state_2 = init(Some(&key), 64).unwrap(); - state_2.update(b"Tests").unwrap(); - state_2.reset(Some(&key)).unwrap(); - state_2.update(b"Tests").unwrap(); - let d2 = state_2.finalize().unwrap(); - - assert_eq!(d1, d2); -} + #[test] + fn double_finalize_err() { + let data = "what do ya want for nothing?".as_bytes(); -#[test] -#[cfg(feature = "safe_api")] -// Test for issues when incrementally processing data -// with leftover -fn test_streaming_consistency() { - for len in 0..BLAKE2B_BLOCKSIZE * 4 { - let data = vec![0u8; len]; - let mut state = init(None, 64).unwrap(); - let mut other_data: Vec = Vec::new(); - - other_data.extend_from_slice(&data); - state.update(&data).unwrap(); - - if data.len() > BLAKE2B_BLOCKSIZE { - other_data.extend_from_slice(b""); - state.update(b"").unwrap(); + let mut state = init(None, 64).unwrap(); + state.update(data).unwrap(); + let _ = state.finalize().unwrap(); + assert!(state.finalize().is_err()); } - if data.len() > BLAKE2B_BLOCKSIZE * 2 { - other_data.extend_from_slice(b"Extra"); - state.update(b"Extra").unwrap(); + + #[test] + fn double_finalize_with_reset_ok_not_keyed() { + let data = "what do ya want for nothing?".as_bytes(); + + let mut state = init(None, 64).unwrap(); + state.update(data).unwrap(); + let one = state.finalize().unwrap(); + state.reset(None).unwrap(); + state.update(data).unwrap(); + let two = state.finalize().unwrap(); + assert_eq!(one.as_bytes(), two.as_bytes()); } - if data.len() > BLAKE2B_BLOCKSIZE * 3 { - other_data.extend_from_slice(&[0u8; 256]); - state.update(&[0u8; 256]).unwrap(); + + #[test] + fn double_finalize_with_reset_ok_keyed() { + let secret_key = SecretKey::from_slice(b"Testing").unwrap(); + let data = "what do ya want for nothing?".as_bytes(); + + let mut state = init(Some(&secret_key), 64).unwrap(); + state.update(data).unwrap(); + let one = state.finalize().unwrap(); + state.reset(Some(&secret_key)).unwrap(); + state.update(data).unwrap(); + let two = state.finalize().unwrap(); + assert_eq!(one.as_bytes(), two.as_bytes()); } - let digest_one_shot = Hasher::Blake2b512.digest(&other_data).unwrap(); + #[test] + fn double_finalize_with_reset_no_update_ok() { + let data = "what do ya want for nothing?".as_bytes(); + + let mut state = init(None, 64).unwrap(); + state.update(data).unwrap(); + let _ = state.finalize().unwrap(); + state.reset(None).unwrap(); + let _ = state.finalize().unwrap(); + } - assert!(state.finalize().unwrap().as_bytes() == digest_one_shot.as_bytes()); - } -} + #[test] + /// Related bug: https://github.com/brycx/orion/issues/28 + fn update_after_finalize_err() { + let data = "what do ya want for nothing?".as_bytes(); + + let mut state = init(None, 64).unwrap(); + state.update(data).unwrap(); + let _ = state.finalize().unwrap(); + assert!(state.update(data).is_err()); + } -// Testing public functions in the module. -#[cfg(test)] -mod public { + #[test] + fn update_after_finalize_with_reset_ok() { + let data = "what do ya want for nothing?".as_bytes(); + + let mut state = init(None, 64).unwrap(); + state.update(data).unwrap(); + let _ = state.finalize().unwrap(); + state.reset(None).unwrap(); + state.update(data).unwrap(); + } - // One function tested per submodule. + #[test] + fn double_reset_ok() { + let data = "what do ya want for nothing?".as_bytes(); + + let mut state = init(None, 64).unwrap(); + state.update(data).unwrap(); + let _ = state.finalize().unwrap(); + state.reset(None).unwrap(); + state.reset(None).unwrap(); + } - mod function_0 { #[test] - fn test_() { - assert!(1 + 1 == 2); + fn err_on_keyed_switch_on_reset() { + let secret_key = SecretKey::from_slice(b"Testing").unwrap(); + let data = "what do ya want for nothing?".as_bytes(); + + let mut state = init(Some(&secret_key), 64).unwrap(); + state.update(data).unwrap(); + let _ = state.finalize().unwrap(); + assert!(state.reset(None).is_err()); + + let mut state_second = init(None, 64).unwrap(); + state_second.update(data).unwrap(); + let _ = state_second.finalize().unwrap(); + assert!(state_second.reset(Some(&secret_key)).is_err()); } - // Proptests. Only exectued when NOT testing no_std. - #[cfg(not(feature = "no_std"))] - mod proptest {} - } + #[test] + /// Related bug: https://github.com/brycx/orion/issues/46 + fn reset_after_update_correct_resets() { + let state_1 = init(None, 64).unwrap(); + + let mut state_2 = init(None, 64).unwrap(); + state_2.update(b"Tests").unwrap(); + state_2.reset(None).unwrap(); + + assert_eq!(state_1.init_state, state_2.init_state); + assert_eq!(state_1.internal_state, state_2.internal_state); + assert_eq!(state_1.buffer[..], state_2.buffer[..]); + assert_eq!(state_1.leftover, state_2.leftover); + assert_eq!(state_1.t, state_2.t); + assert_eq!(state_1.f, state_2.f); + assert_eq!(state_1.is_finalized, state_2.is_finalized); + assert_eq!(state_1.is_keyed, state_2.is_keyed); + assert_eq!(state_1.size, state_2.size); + } - mod function_1 { #[test] - fn test_() { - assert!(2 + 2 == 4); + /// Related bug: https://github.com/brycx/orion/issues/46 + fn reset_after_update_correct_resets_and_verify() { + // In non-keyed mode + let mut state_1 = init(None, 64).unwrap(); + state_1.update(b"Tests").unwrap(); + let d1 = state_1.finalize().unwrap(); + + let mut state_2 = init(None, 64).unwrap(); + state_2.update(b"Tests").unwrap(); + state_2.reset(None).unwrap(); + state_2.update(b"Tests").unwrap(); + let d2 = state_2.finalize().unwrap(); + + assert_eq!(d1, d2); + + // In keyed mode + let key = SecretKey::from_slice(&[0u8; 64]).unwrap(); + let mut state_1 = init(Some(&key), 64).unwrap(); + state_1.update(b"Tests").unwrap(); + let d1 = state_1.finalize().unwrap(); + + let mut state_2 = init(Some(&key), 64).unwrap(); + state_2.update(b"Tests").unwrap(); + state_2.reset(Some(&key)).unwrap(); + state_2.update(b"Tests").unwrap(); + let d2 = state_2.finalize().unwrap(); + + assert_eq!(d1, d2); } + #[test] + #[cfg(feature = "safe_api")] + // Test for issues when incrementally processing data + // with leftover + fn test_streaming_consistency() { + for len in 0..BLAKE2B_BLOCKSIZE * 4 { + let data = vec![0u8; len]; + let mut state = init(None, 64).unwrap(); + let mut other_data: Vec = Vec::new(); + + other_data.extend_from_slice(&data); + state.update(&data).unwrap(); + + if data.len() > BLAKE2B_BLOCKSIZE { + other_data.extend_from_slice(b""); + state.update(b"").unwrap(); + } + if data.len() > BLAKE2B_BLOCKSIZE * 2 { + other_data.extend_from_slice(b"Extra"); + state.update(b"Extra").unwrap(); + } + if data.len() > BLAKE2B_BLOCKSIZE * 3 { + other_data.extend_from_slice(&[0u8; 256]); + state.update(&[0u8; 256]).unwrap(); + } + + let digest_one_shot = Hasher::Blake2b512.digest(&other_data).unwrap(); + + assert!(state.finalize().unwrap().as_bytes() == digest_one_shot.as_bytes()); + } + } // Proptests. Only exectued when NOT testing no_std. #[cfg(not(feature = "no_std"))] - mod proptest {} + mod proptest { + use super::*; + + quickcheck! { + /// Never panic when calling update() on an object that is not finalized. + fn prop_update_no_panic(data: Vec) -> bool { + let mut state = init(None, 64).unwrap(); + state.update(&data[..]).unwrap(); + + true + } + } + + quickcheck! { + /// Never panic when calling reset() with correct secret key option. + fn prop_reset_no_panic(data: Vec) -> bool { + /* + // In non-keyed mode + let mut state = init(None, 64).unwrap(); + state.reset(None).unwrap(); + state.update(&data[..]).unwrap(); + state.reset(None).unwrap(); + let _ = state.finalize().unwrap(); + state.reset(None).unwrap(); + state.update(&data[..]).unwrap(); + let _ = state.finalize().unwrap(); + state.reset(None).unwrap(); + state.reset(None).unwrap(); + */ + + + // In keyed mode + let key = SecretKey::from_slice(&[0u8; 64]).unwrap(); + let mut state2 = init(Some(&key), 64).unwrap(); + state2.reset(Some(&key)).unwrap(); + state2.update(&data[..]).unwrap(); + state2.reset(Some(&key)).unwrap(); + let _ = state2.finalize().unwrap(); + state2.reset(Some(&key)).unwrap(); + state2.update(&data[..]).unwrap(); + let _ = state2.finalize().unwrap(); + state2.reset(Some(&key)).unwrap(); + state2.reset(Some(&key)).unwrap(); + + true + } + } + + quickcheck! { + // Never panic when calling finalize() on an object that is not finalized. + fn prop_finalize_no_panic(_data: Vec) -> bool { + /* + // In non-keyed mode + let mut state = init(None, 64).unwrap(); + state.reset(None).unwrap(); + state.update(&data[..]).unwrap(); + state.reset(None).unwrap(); + let _ = state.finalize().unwrap(); + state.reset(None).unwrap(); + state.update(&data[..]).unwrap(); + let _ = state.finalize().unwrap(); + state.reset(None).unwrap(); + state.reset(None).unwrap(); + */ + /* + // In keyed mode + let key = SecretKey::from_slice(&[0u8; 64]).unwrap(); + let mut state = init(Some(&key), 64).unwrap(); + state.reset(Some(&key)).unwrap(); + state.update(&data[..]).unwrap(); + state.reset(Some(&key)).unwrap(); + let _ = state.finalize().unwrap(); + state.reset(Some(&key)).unwrap(); + state.update(&data[..]).unwrap(); + let _ = state.finalize().unwrap(); + state.reset(Some(&key)).unwrap(); + state.reset(Some(&key)).unwrap(); + */ + true + } + } + + } } } @@ -719,11 +866,11 @@ mod private { use super::*; // One function tested per submodule. - mod increment_counter { + mod test_increment_offset { use super::*; #[test] - fn test_counter_increase_values() { + fn test_offset_increase_values() { let mut context = Blake2b { init_state: [0u64; 8], internal_state: IV, @@ -765,4 +912,4 @@ mod private { context.increment_offset(u64::max_value()); } } -} \ No newline at end of file +} From dc377c8c559b2f790f8112d38673eb1800560508 Mon Sep 17 00:00:00 2001 From: brycx Date: Tue, 29 Jan 2019 22:50:58 +0100 Subject: [PATCH 12/40] Tests: Finish organizing Blake2b tests --- src/hazardous/hash/blake2b.rs | 135 ++++++++++++---------------------- 1 file changed, 45 insertions(+), 90 deletions(-) diff --git a/src/hazardous/hash/blake2b.rs b/src/hazardous/hash/blake2b.rs index b0f9c7df..b754dfe5 100644 --- a/src/hazardous/hash/blake2b.rs +++ b/src/hazardous/hash/blake2b.rs @@ -460,26 +460,6 @@ pub fn verify( } } -#[test] -fn finalize_and_verify_true() { - let secret_key = SecretKey::from_slice("Jefe".as_bytes()).unwrap(); - let data = "what do ya want for nothing?".as_bytes(); - - let mut tag = init(Some(&secret_key), 64).unwrap(); - tag.update(data).unwrap(); - - assert_eq!( - verify( - &tag.finalize().unwrap(), - &SecretKey::from_slice("Jefe".as_bytes()).unwrap(), - 64, - data - ) - .unwrap(), - true - ); -} - // Testing public functions in the module. #[cfg(test)] mod public { @@ -530,6 +510,51 @@ mod public { } } + mod test_verify { + use super::*; + + #[test] + fn finalize_and_verify_true() { + let secret_key = SecretKey::from_slice("Jefe".as_bytes()).unwrap(); + let data = "what do ya want for nothing?".as_bytes(); + + let mut tag = init(Some(&secret_key), 64).unwrap(); + tag.update(data).unwrap(); + + assert_eq!( + verify( + &tag.finalize().unwrap(), + &SecretKey::from_slice("Jefe".as_bytes()).unwrap(), + 64, + data + ) + .unwrap(), + true + ); + } + + // Proptests. Only exectued when NOT testing no_std. + #[cfg(not(feature = "no_std"))] + mod proptest { + use super::*; + + quickcheck! { + /// When using the same parameters verify() should always yeild true. + fn prop_verify_same_params_true(data: Vec) -> bool { + let sk = SecretKey::generate().unwrap(); + + let mut state = init(Some(&sk), 64).unwrap(); + state.update(&data[..]).unwrap(); + let tag = state.finalize().unwrap(); + // Failed verification on Err so res is not needed. + let _res = verify(&tag, &sk, 64, &data[..]).unwrap(); + + true + } + } + } + } + mod test_hasher { use super::*; @@ -781,76 +806,6 @@ mod public { true } } - - quickcheck! { - /// Never panic when calling reset() with correct secret key option. - fn prop_reset_no_panic(data: Vec) -> bool { - /* - // In non-keyed mode - let mut state = init(None, 64).unwrap(); - state.reset(None).unwrap(); - state.update(&data[..]).unwrap(); - state.reset(None).unwrap(); - let _ = state.finalize().unwrap(); - state.reset(None).unwrap(); - state.update(&data[..]).unwrap(); - let _ = state.finalize().unwrap(); - state.reset(None).unwrap(); - state.reset(None).unwrap(); - */ - - - // In keyed mode - let key = SecretKey::from_slice(&[0u8; 64]).unwrap(); - let mut state2 = init(Some(&key), 64).unwrap(); - state2.reset(Some(&key)).unwrap(); - state2.update(&data[..]).unwrap(); - state2.reset(Some(&key)).unwrap(); - let _ = state2.finalize().unwrap(); - state2.reset(Some(&key)).unwrap(); - state2.update(&data[..]).unwrap(); - let _ = state2.finalize().unwrap(); - state2.reset(Some(&key)).unwrap(); - state2.reset(Some(&key)).unwrap(); - - true - } - } - - quickcheck! { - // Never panic when calling finalize() on an object that is not finalized. - fn prop_finalize_no_panic(_data: Vec) -> bool { - /* - // In non-keyed mode - let mut state = init(None, 64).unwrap(); - state.reset(None).unwrap(); - state.update(&data[..]).unwrap(); - state.reset(None).unwrap(); - let _ = state.finalize().unwrap(); - state.reset(None).unwrap(); - state.update(&data[..]).unwrap(); - let _ = state.finalize().unwrap(); - state.reset(None).unwrap(); - state.reset(None).unwrap(); - */ - /* - // In keyed mode - let key = SecretKey::from_slice(&[0u8; 64]).unwrap(); - let mut state = init(Some(&key), 64).unwrap(); - state.reset(Some(&key)).unwrap(); - state.update(&data[..]).unwrap(); - state.reset(Some(&key)).unwrap(); - let _ = state.finalize().unwrap(); - state.reset(Some(&key)).unwrap(); - state.update(&data[..]).unwrap(); - let _ = state.finalize().unwrap(); - state.reset(Some(&key)).unwrap(); - state.reset(Some(&key)).unwrap(); - */ - true - } - } - } } } From b3188dde22b383de07af6c1dd4b9668848fc2b54 Mon Sep 17 00:00:00 2001 From: brycx Date: Wed, 30 Jan 2019 12:34:25 +0100 Subject: [PATCH 13/40] Tests: Small additions to Blake2b testing --- src/hazardous/hash/blake2b.rs | 217 ++++++++++++++++++++++++---------- 1 file changed, 152 insertions(+), 65 deletions(-) diff --git a/src/hazardous/hash/blake2b.rs b/src/hazardous/hash/blake2b.rs index b754dfe5..0a90e09d 100644 --- a/src/hazardous/hash/blake2b.rs +++ b/src/hazardous/hash/blake2b.rs @@ -470,12 +470,33 @@ mod public { mod test_init { use super::*; + /// Convenience testing function to avoid repetition when testing + /// init sizes with and without a secret key. Returns false if + /// incorrect Result is returned. + fn init_tester(sk: Option<&SecretKey>, size: usize) -> bool { + if size >= 1 && size <= BLAKE2B_OUTSIZE { + let res = if init(sk, size).is_ok() { true } else { false }; + + return res; + } else { + let res = if init(sk, size).is_err() { true } else { false }; + + return res; + } + } + #[test] fn test_init_size() { - assert!(init(None, 0).is_err()); - assert!(init(None, 65).is_err()); - assert!(init(None, 64).is_ok()); - assert!(init(None, 1).is_ok()); + assert!(init_tester(None, 0)); + assert!(init_tester(None, 65)); + assert!(init_tester(None, 64)); + assert!(init_tester(None, 1)); + + let sk = SecretKey::from_slice(&[0u8; 64]).unwrap(); + assert!(init_tester(Some(&sk), 0)); + assert!(init_tester(Some(&sk), 65)); + assert!(init_tester(Some(&sk), 64)); + assert!(init_tester(Some(&sk), 1)); } // Proptests. Only exectued when NOT testing no_std. @@ -487,24 +508,16 @@ mod public { /// Given a valid size parameter, init should always pass. If size /// is invalid, then init should always fail. fn prop_init_size_no_key(size: usize) -> bool { + init_tester(None, size) + } + } - if size >= 1 && size <= BLAKE2B_OUTSIZE { - let res = if init(None, size).is_ok() { - true - } else { - false - }; - - return res; - } else { - let res = if init(None, size).is_err() { - true - } else { - false - }; - - return res; - } + quickcheck! { + /// Given a valid size parameter, init should always pass. If size + /// is invalid, then init should always fail. + fn prop_init_size_key(size: usize) -> bool { + let sk = SecretKey::generate().unwrap(); + init_tester(Some(&sk), size) } } } @@ -537,7 +550,7 @@ mod public { #[cfg(not(feature = "no_std"))] mod proptest { use super::*; - + quickcheck! { /// When using the same parameters verify() should always yeild true. fn prop_verify_same_params_true(data: Vec) -> bool { @@ -552,6 +565,26 @@ mod public { true } } + + quickcheck! { + /// When using the same parameters verify() should always yeild true. + fn prop_verify_diff_key_false(data: Vec) -> bool { + let sk = SecretKey::generate().unwrap(); + let mut state = init(Some(&sk), 64).unwrap(); + state.update(&data[..]).unwrap(); + let tag = state.finalize().unwrap(); + + let bad_sk = SecretKey::generate().unwrap(); + + let res = if verify(&tag, &bad_sk, 64, &data[..]).is_err() { + true + } else { + false + }; + + res + } + } } } @@ -564,11 +597,13 @@ mod public { let digest_384 = Hasher::Blake2b384.digest(b"Test").unwrap(); let digest_512 = Hasher::Blake2b512.digest(b"Test").unwrap(); - assert_eq!(digest_256, Hasher::Blake2b256.digest(b"Test").unwrap(),); - - assert_eq!(digest_384, Hasher::Blake2b384.digest(b"Test").unwrap(),); + assert_eq!(digest_256, Hasher::Blake2b256.digest(b"Test").unwrap()); + assert_eq!(digest_384, Hasher::Blake2b384.digest(b"Test").unwrap()); + assert_eq!(digest_512, Hasher::Blake2b512.digest(b"Test").unwrap()); - assert_eq!(digest_512, Hasher::Blake2b512.digest(b"Test").unwrap(),); + assert_ne!(digest_256, Hasher::Blake2b256.digest(b"Wrong").unwrap()); + assert_ne!(digest_384, Hasher::Blake2b384.digest(b"Wrong").unwrap()); + assert_ne!(digest_512, Hasher::Blake2b512.digest(b"Wrong").unwrap()); let _state_256 = Hasher::Blake2b256.init().unwrap(); let _state_384 = Hasher::Blake2b384.init().unwrap(); @@ -597,6 +632,22 @@ mod public { } } + quickcheck! { + /// Given two different data, .digest() should never produce the + /// same output.ValidationCryptoError + fn prop_hasher_digest_diff_input_diff_result(data: Vec) -> bool { + let d256 = Hasher::Blake2b256.digest(&data[..]).unwrap(); + let d384 = Hasher::Blake2b384.digest(&data[..]).unwrap(); + let d512 = Hasher::Blake2b512.digest(&data[..]).unwrap(); + + let d256_re = Hasher::Blake2b256.digest(b"Wrong data").unwrap(); + let d384_re = Hasher::Blake2b384.digest(b"Wrong data").unwrap(); + let d512_re = Hasher::Blake2b512.digest(b"Wrong data").unwrap(); + + (d256 != d256_re) && (d384 != d384_re) && (d512 != d512_re) + } + } + quickcheck! { /// .init() should never fail. fn prop_hasher_init_no_panic() -> bool { @@ -610,11 +661,47 @@ mod public { } } + mod test_reset { + use super::*; + + #[test] + fn test_switching_keyed_modes_fails() { + let secret_key = SecretKey::from_slice(b"Testing").unwrap(); + let data = "what do ya want for nothing?".as_bytes(); + + let mut state = init(Some(&secret_key), 64).unwrap(); + state.update(data).unwrap(); + let _ = state.finalize().unwrap(); + assert!(state.reset(None).is_err()); + assert!(state.reset(Some(&secret_key)).is_ok()); + + let mut state_second = init(None, 64).unwrap(); + state_second.update(data).unwrap(); + let _ = state_second.finalize().unwrap(); + assert!(state_second.reset(Some(&secret_key)).is_err()); + assert!(state_second.reset(None).is_ok()); + } + } + mod test_streaming_interface { use super::*; + /// Compare two Blake2b state objects to check if their fields + /// are the same. + fn compare_blake2b_states(state_1: Blake2b, state_2: Blake2b) { + assert_eq!(state_1.init_state, state_2.init_state); + assert_eq!(state_1.internal_state, state_2.internal_state); + assert_eq!(state_1.buffer[..], state_2.buffer[..]); + assert_eq!(state_1.leftover, state_2.leftover); + assert_eq!(state_1.t, state_2.t); + assert_eq!(state_1.f, state_2.f); + assert_eq!(state_1.is_finalized, state_2.is_finalized); + assert_eq!(state_1.is_keyed, state_2.is_keyed); + assert_eq!(state_1.size, state_2.size); + } + #[test] - fn double_finalize_err() { + fn test_double_finalize_err() { let data = "what do ya want for nothing?".as_bytes(); let mut state = init(None, 64).unwrap(); @@ -624,7 +711,7 @@ mod public { } #[test] - fn double_finalize_with_reset_ok_not_keyed() { + fn test_double_finalize_with_reset_no_key() { let data = "what do ya want for nothing?".as_bytes(); let mut state = init(None, 64).unwrap(); @@ -637,7 +724,7 @@ mod public { } #[test] - fn double_finalize_with_reset_ok_keyed() { + fn test_double_finalize_with_reset_with_key() { let secret_key = SecretKey::from_slice(b"Testing").unwrap(); let data = "what do ya want for nothing?".as_bytes(); @@ -651,7 +738,7 @@ mod public { } #[test] - fn double_finalize_with_reset_no_update_ok() { + fn test_double_finalize_with_reset_no_update() { let data = "what do ya want for nothing?".as_bytes(); let mut state = init(None, 64).unwrap(); @@ -663,7 +750,7 @@ mod public { #[test] /// Related bug: https://github.com/brycx/orion/issues/28 - fn update_after_finalize_err() { + fn test_update_after_finalize_err() { let data = "what do ya want for nothing?".as_bytes(); let mut state = init(None, 64).unwrap(); @@ -673,66 +760,66 @@ mod public { } #[test] - fn update_after_finalize_with_reset_ok() { + fn test_double_reset_ok() { let data = "what do ya want for nothing?".as_bytes(); let mut state = init(None, 64).unwrap(); state.update(data).unwrap(); let _ = state.finalize().unwrap(); state.reset(None).unwrap(); - state.update(data).unwrap(); + state.reset(None).unwrap(); } #[test] - fn double_reset_ok() { - let data = "what do ya want for nothing?".as_bytes(); + /// Related bug: https://github.com/brycx/orion/issues/46 + fn test_reset_after_update_correct_resets() { + // No key + let state_1 = init(None, 64).unwrap(); - let mut state = init(None, 64).unwrap(); - state.update(data).unwrap(); - let _ = state.finalize().unwrap(); - state.reset(None).unwrap(); - state.reset(None).unwrap(); - } + let mut state_2 = init(None, 64).unwrap(); + state_2.update(b"Tests").unwrap(); + state_2.reset(None).unwrap(); - #[test] - fn err_on_keyed_switch_on_reset() { - let secret_key = SecretKey::from_slice(b"Testing").unwrap(); - let data = "what do ya want for nothing?".as_bytes(); + compare_blake2b_states(state_1, state_2); - let mut state = init(Some(&secret_key), 64).unwrap(); - state.update(data).unwrap(); - let _ = state.finalize().unwrap(); - assert!(state.reset(None).is_err()); + // Keyed + let sk = SecretKey::from_slice(b"Testing").unwrap(); + let state_1 = init(Some(&sk), 64).unwrap(); - let mut state_second = init(None, 64).unwrap(); - state_second.update(data).unwrap(); - let _ = state_second.finalize().unwrap(); - assert!(state_second.reset(Some(&secret_key)).is_err()); + let mut state_2 = init(Some(&sk), 64).unwrap(); + state_2.update(b"Tests").unwrap(); + state_2.reset(Some(&sk)).unwrap(); + + compare_blake2b_states(state_1, state_2); } #[test] - /// Related bug: https://github.com/brycx/orion/issues/46 - fn reset_after_update_correct_resets() { + fn test_reset_after_finalize_correct_resets() { + // No key let state_1 = init(None, 64).unwrap(); let mut state_2 = init(None, 64).unwrap(); state_2.update(b"Tests").unwrap(); + let _ = state_2.finalize().unwrap(); state_2.reset(None).unwrap(); - assert_eq!(state_1.init_state, state_2.init_state); - assert_eq!(state_1.internal_state, state_2.internal_state); - assert_eq!(state_1.buffer[..], state_2.buffer[..]); - assert_eq!(state_1.leftover, state_2.leftover); - assert_eq!(state_1.t, state_2.t); - assert_eq!(state_1.f, state_2.f); - assert_eq!(state_1.is_finalized, state_2.is_finalized); - assert_eq!(state_1.is_keyed, state_2.is_keyed); - assert_eq!(state_1.size, state_2.size); + compare_blake2b_states(state_1, state_2); + + // Keyed + let sk = SecretKey::from_slice(b"Testing").unwrap(); + let state_1 = init(Some(&sk), 64).unwrap(); + + let mut state_2 = init(Some(&sk), 64).unwrap(); + state_2.update(b"Tests").unwrap(); + let _ = state_2.finalize().unwrap(); + state_2.reset(Some(&sk)).unwrap(); + + compare_blake2b_states(state_1, state_2); } #[test] /// Related bug: https://github.com/brycx/orion/issues/46 - fn reset_after_update_correct_resets_and_verify() { + fn test_reset_after_update_correct_resets_and_verify() { // In non-keyed mode let mut state_1 = init(None, 64).unwrap(); state_1.update(b"Tests").unwrap(); From fa4ad99d676293075f4f63cc02d3a27233fbe871 Mon Sep 17 00:00:00 2001 From: brycx Date: Wed, 30 Jan 2019 13:36:05 +0100 Subject: [PATCH 14/40] Tests: Refactor streaming state usage tests into testing functions. --- src/hazardous/hash/blake2b.rs | 276 ++++++++++++++++++---------------- 1 file changed, 150 insertions(+), 126 deletions(-) diff --git a/src/hazardous/hash/blake2b.rs b/src/hazardous/hash/blake2b.rs index 0a90e09d..6986e152 100644 --- a/src/hazardous/hash/blake2b.rs +++ b/src/hazardous/hash/blake2b.rs @@ -467,6 +467,20 @@ mod public { // One function tested per submodule. + /// Compare two Blake2b state objects to check if their fields + /// are the same. + fn compare_blake2b_states(state_1: &Blake2b, state_2: &Blake2b) { + assert_eq!(state_1.init_state, state_2.init_state); + assert_eq!(state_1.internal_state, state_2.internal_state); + assert_eq!(state_1.buffer[..], state_2.buffer[..]); + assert_eq!(state_1.leftover, state_2.leftover); + assert_eq!(state_1.t, state_2.t); + assert_eq!(state_1.f, state_2.f); + assert_eq!(state_1.is_finalized, state_2.is_finalized); + assert_eq!(state_1.is_keyed, state_2.is_keyed); + assert_eq!(state_1.size, state_2.size); + } + mod test_init { use super::*; @@ -664,188 +678,178 @@ mod public { mod test_reset { use super::*; + #[test] + fn test_double_reset_ok() { + let mut state = init(None, 64).unwrap(); + state.update(b"Tests").unwrap(); + let _ = state.finalize().unwrap(); + state.reset(None).unwrap(); + state.reset(None).unwrap(); + } + #[test] fn test_switching_keyed_modes_fails() { let secret_key = SecretKey::from_slice(b"Testing").unwrap(); - let data = "what do ya want for nothing?".as_bytes(); let mut state = init(Some(&secret_key), 64).unwrap(); - state.update(data).unwrap(); + state.update(b"Tests").unwrap(); let _ = state.finalize().unwrap(); assert!(state.reset(None).is_err()); assert!(state.reset(Some(&secret_key)).is_ok()); let mut state_second = init(None, 64).unwrap(); - state_second.update(data).unwrap(); + state_second.update(b"Tests").unwrap(); let _ = state_second.finalize().unwrap(); assert!(state_second.reset(Some(&secret_key)).is_err()); assert!(state_second.reset(None).is_ok()); } } - mod test_streaming_interface { + mod test_update { use super::*; - /// Compare two Blake2b state objects to check if their fields - /// are the same. - fn compare_blake2b_states(state_1: Blake2b, state_2: Blake2b) { - assert_eq!(state_1.init_state, state_2.init_state); - assert_eq!(state_1.internal_state, state_2.internal_state); - assert_eq!(state_1.buffer[..], state_2.buffer[..]); - assert_eq!(state_1.leftover, state_2.leftover); - assert_eq!(state_1.t, state_2.t); - assert_eq!(state_1.f, state_2.f); - assert_eq!(state_1.is_finalized, state_2.is_finalized); - assert_eq!(state_1.is_keyed, state_2.is_keyed); - assert_eq!(state_1.size, state_2.size); - } - #[test] - fn test_double_finalize_err() { - let data = "what do ya want for nothing?".as_bytes(); - + /// Related bug: https://github.com/brycx/orion/issues/28 + fn test_update_after_finalize_fail() { let mut state = init(None, 64).unwrap(); - state.update(data).unwrap(); + state.update(b"Test").unwrap(); let _ = state.finalize().unwrap(); - assert!(state.finalize().is_err()); + assert!(state.update(b"Test").is_err()); } #[test] - fn test_double_finalize_with_reset_no_key() { - let data = "what do ya want for nothing?".as_bytes(); - + fn test_update_after_finalize_with_reset() { let mut state = init(None, 64).unwrap(); - state.update(data).unwrap(); - let one = state.finalize().unwrap(); + state.update(b"Test").unwrap(); + let _ = state.finalize().unwrap(); state.reset(None).unwrap(); - state.update(data).unwrap(); - let two = state.finalize().unwrap(); - assert_eq!(one.as_bytes(), two.as_bytes()); + assert!(state.update(b"Test").is_ok()); } + } - #[test] - fn test_double_finalize_with_reset_with_key() { - let secret_key = SecretKey::from_slice(b"Testing").unwrap(); - let data = "what do ya want for nothing?".as_bytes(); - - let mut state = init(Some(&secret_key), 64).unwrap(); - state.update(data).unwrap(); - let one = state.finalize().unwrap(); - state.reset(Some(&secret_key)).unwrap(); - state.update(data).unwrap(); - let two = state.finalize().unwrap(); - assert_eq!(one.as_bytes(), two.as_bytes()); - } + mod test_finalize { + use super::*; #[test] - fn test_double_finalize_with_reset_no_update() { - let data = "what do ya want for nothing?".as_bytes(); - + fn test_double_finalize_fail() { let mut state = init(None, 64).unwrap(); - state.update(data).unwrap(); - let _ = state.finalize().unwrap(); - state.reset(None).unwrap(); + state.update(b"Test").unwrap(); let _ = state.finalize().unwrap(); + assert!(state.finalize().is_err()); } #[test] - /// Related bug: https://github.com/brycx/orion/issues/28 - fn test_update_after_finalize_err() { - let data = "what do ya want for nothing?".as_bytes(); - + fn test_finalize_after_reset() { let mut state = init(None, 64).unwrap(); - state.update(data).unwrap(); + state.update(b"Test").unwrap(); let _ = state.finalize().unwrap(); - assert!(state.update(data).is_err()); + state.reset(None).unwrap(); + assert!(state.finalize().is_ok()); } #[test] - fn test_double_reset_ok() { - let data = "what do ya want for nothing?".as_bytes(); - + fn test_double_finalize_with_reset_no_update() { let mut state = init(None, 64).unwrap(); - state.update(data).unwrap(); + state.update(b"Test").unwrap(); let _ = state.finalize().unwrap(); state.reset(None).unwrap(); - state.reset(None).unwrap(); + let _ = state.finalize().unwrap(); } + } + + mod test_streaming_interface { + use super::*; - #[test] /// Related bug: https://github.com/brycx/orion/issues/46 - fn test_reset_after_update_correct_resets() { - // No key - let state_1 = init(None, 64).unwrap(); + /// Testing different usage combinations of init(), update(), + /// finalize() and reset() produce the same Digest/Tag. + fn produces_same_hash(sk: Option<&SecretKey>, size: usize, data: &[u8]) { + // init(), update(), finalize() + let mut state_1 = init(sk, size).unwrap(); + state_1.update(data).unwrap(); + let res_1 = state_1.finalize().unwrap(); + + // init(), reset(), update(), finalize() + let mut state_2 = init(sk, size).unwrap(); + state_2.reset(sk).unwrap(); + state_2.update(data).unwrap(); + let res_2 = state_2.finalize().unwrap(); + + // init(), update(), reset(), update(), finalize() + let mut state_3 = init(sk, size).unwrap(); + state_3.update(data).unwrap(); + state_3.reset(sk).unwrap(); + state_3.update(data).unwrap(); + let res_3 = state_3.finalize().unwrap(); + + // init(), update(), finalize(), reset(), update(), finalize() + let mut state_4 = init(sk, size).unwrap(); + state_4.update(data).unwrap(); + let _ = state_4.finalize().unwrap(); + state_4.reset(sk).unwrap(); + state_4.update(data).unwrap(); + let res_4 = state_4.finalize().unwrap(); + + assert_eq!(res_1, res_2); + assert_eq!(res_2, res_3); + assert_eq!(res_3, res_4); + } - let mut state_2 = init(None, 64).unwrap(); - state_2.update(b"Tests").unwrap(); - state_2.reset(None).unwrap(); + /// Related bug: https://github.com/brycx/orion/issues/46 + /// Testing different usage combinations of init(), update(), + /// finalize() and reset() produce the same Digest/Tag. + fn produces_same_state(sk: Option<&SecretKey>, size: usize, data: &[u8]) { + // init() + let state_1 = init(sk, size).unwrap(); - compare_blake2b_states(state_1, state_2); + // init(), reset() + let mut state_2 = init(sk, size).unwrap(); + state_2.reset(sk).unwrap(); - // Keyed - let sk = SecretKey::from_slice(b"Testing").unwrap(); - let state_1 = init(Some(&sk), 64).unwrap(); + // init(), update(), reset() + let mut state_3 = init(sk, size).unwrap(); + state_3.update(data).unwrap(); + state_3.reset(sk).unwrap(); - let mut state_2 = init(Some(&sk), 64).unwrap(); - state_2.update(b"Tests").unwrap(); - state_2.reset(Some(&sk)).unwrap(); + // init(), update(), finalize(), reset() + let mut state_4 = init(sk, size).unwrap(); + state_4.update(data).unwrap(); + let _ = state_4.finalize().unwrap(); + state_4.reset(sk).unwrap(); - compare_blake2b_states(state_1, state_2); + compare_blake2b_states(&state_1, &state_2); + compare_blake2b_states(&state_2, &state_3); + compare_blake2b_states(&state_3, &state_4); } #[test] - fn test_reset_after_finalize_correct_resets() { - // No key - let state_1 = init(None, 64).unwrap(); - - let mut state_2 = init(None, 64).unwrap(); - state_2.update(b"Tests").unwrap(); - let _ = state_2.finalize().unwrap(); - state_2.reset(None).unwrap(); - - compare_blake2b_states(state_1, state_2); - - // Keyed - let sk = SecretKey::from_slice(b"Testing").unwrap(); - let state_1 = init(Some(&sk), 64).unwrap(); - - let mut state_2 = init(Some(&sk), 64).unwrap(); - state_2.update(b"Tests").unwrap(); - let _ = state_2.finalize().unwrap(); - state_2.reset(Some(&sk)).unwrap(); + /// Related bug: https://github.com/brycx/orion/issues/46 + fn test_produce_same_state() { + produces_same_state(None, 1, b"Tests"); + produces_same_state(None, 32, b"Tests"); + produces_same_state(None, 64, b"Tests"); + produces_same_state(None, 28, b"Tests"); - compare_blake2b_states(state_1, state_2); + let sk = SecretKey::generate().unwrap(); + produces_same_state(Some(&sk), 1, b"Tests"); + produces_same_state(Some(&sk), 32, b"Tests"); + produces_same_state(Some(&sk), 64, b"Tests"); + produces_same_state(Some(&sk), 28, b"Tests"); } #[test] /// Related bug: https://github.com/brycx/orion/issues/46 - fn test_reset_after_update_correct_resets_and_verify() { - // In non-keyed mode - let mut state_1 = init(None, 64).unwrap(); - state_1.update(b"Tests").unwrap(); - let d1 = state_1.finalize().unwrap(); - - let mut state_2 = init(None, 64).unwrap(); - state_2.update(b"Tests").unwrap(); - state_2.reset(None).unwrap(); - state_2.update(b"Tests").unwrap(); - let d2 = state_2.finalize().unwrap(); - - assert_eq!(d1, d2); - - // In keyed mode - let key = SecretKey::from_slice(&[0u8; 64]).unwrap(); - let mut state_1 = init(Some(&key), 64).unwrap(); - state_1.update(b"Tests").unwrap(); - let d1 = state_1.finalize().unwrap(); + fn test_produce_same_hash() { + produces_same_hash(None, 1, b"Tests"); + produces_same_hash(None, 32, b"Tests"); + produces_same_hash(None, 64, b"Tests"); + produces_same_hash(None, 28, b"Tests"); - let mut state_2 = init(Some(&key), 64).unwrap(); - state_2.update(b"Tests").unwrap(); - state_2.reset(Some(&key)).unwrap(); - state_2.update(b"Tests").unwrap(); - let d2 = state_2.finalize().unwrap(); - - assert_eq!(d1, d2); + let sk = SecretKey::generate().unwrap(); + produces_same_hash(Some(&sk), 1, b"Tests"); + produces_same_hash(Some(&sk), 32, b"Tests"); + produces_same_hash(Some(&sk), 64, b"Tests"); + produces_same_hash(Some(&sk), 28, b"Tests"); } #[test] @@ -885,10 +889,30 @@ mod public { use super::*; quickcheck! { - /// Never panic when calling update() on an object that is not finalized. - fn prop_update_no_panic(data: Vec) -> bool { - let mut state = init(None, 64).unwrap(); - state.update(&data[..]).unwrap(); + /// Related bug: https://github.com/brycx/orion/issues/46 + /// Test different streaming state usage patterns. + fn prop_same_hash_different_usage(data: Vec, size: usize) -> bool { + if size >= 1 && size <= BLAKE2B_OUTSIZE { + // Will panic on incorrect results. + produces_same_hash(None, size, &data[..]); + let sk = SecretKey::generate().unwrap(); + produces_same_hash(Some(&sk), size, &data[..]); + } + + true + } + } + + quickcheck! { + /// Related bug: https://github.com/brycx/orion/issues/46 + /// Test different streaming state usage patterns. + fn prop_same_state_different_usage(data: Vec, size: usize) -> bool { + if size >= 1 && size <= BLAKE2B_OUTSIZE { + // Will panic on incorrect results. + produces_same_state(None, size, &data[..]); + let sk = SecretKey::generate().unwrap(); + produces_same_state(Some(&sk), size, &data[..]); + } true } From 286163eeea3de6b21f00d559628b3ce05e2a6ba4 Mon Sep 17 00:00:00 2001 From: brycx Date: Wed, 30 Jan 2019 15:36:31 +0100 Subject: [PATCH 15/40] Tests: Compare digest() with streaming and start organizing Sha512 tests. increment_mlen sha512 detect incorrect message length handling. --- src/hazardous/hash/blake2b.rs | 39 ++++ src/hazardous/hash/sha512.rs | 371 ++++++++++++++++++++++++---------- 2 files changed, 304 insertions(+), 106 deletions(-) diff --git a/src/hazardous/hash/blake2b.rs b/src/hazardous/hash/blake2b.rs index 6986e152..44c68bc5 100644 --- a/src/hazardous/hash/blake2b.rs +++ b/src/hazardous/hash/blake2b.rs @@ -646,6 +646,45 @@ mod public { } } + quickcheck! { + /// Given some data, .digest() should produce the same output as when + /// calling with streaming state. + fn prop_hasher_digest_256_same_as_streaming(data: Vec) -> bool { + let d256 = Hasher::Blake2b256.digest(&data[..]).unwrap();; + + let mut state = init(None, 32).unwrap(); + state.update(&data[..]).unwrap(); + + (d256 == state.finalize().unwrap()) + } + } + + quickcheck! { + /// Given some data, .digest() should produce the same output as when + /// calling with streaming state. + fn prop_hasher_digest_384_same_as_streaming(data: Vec) -> bool { + let d256 = Hasher::Blake2b384.digest(&data[..]).unwrap();; + + let mut state = init(None, 48).unwrap(); + state.update(&data[..]).unwrap(); + + (d256 == state.finalize().unwrap()) + } + } + + quickcheck! { + /// Given some data, .digest() should produce the same output as when + /// calling with streaming state. + fn prop_hasher_digest_512_same_as_streaming(data: Vec) -> bool { + let d256 = Hasher::Blake2b512.digest(&data[..]).unwrap();; + + let mut state = init(None, 64).unwrap(); + state.update(&data[..]).unwrap(); + + (d256 == state.finalize().unwrap()) + } + } + quickcheck! { /// Given two different data, .digest() should never produce the /// same output.ValidationCryptoError diff --git a/src/hazardous/hash/sha512.rs b/src/hazardous/hash/sha512.rs index 75de3e66..0726c422 100644 --- a/src/hazardous/hash/sha512.rs +++ b/src/hazardous/hash/sha512.rs @@ -374,133 +374,292 @@ pub fn digest(data: &[u8]) -> Result { Ok(state.finalize()?) } -#[test] -fn double_finalize_err() { - let data = "what do ya want for nothing?".as_bytes(); - - let mut state = init(); - state.update(data).unwrap(); - let _ = state.finalize().unwrap(); - assert!(state.finalize().is_err()); -} +// Testing public functions in the module. +#[cfg(test)] +mod public { + use super::*; + + // One function tested per submodule. + + /// Compare two Sha512 state objects to check if their fields + /// are the same. + fn compare_sha512_states(state_1: &Sha512, state_2: &Sha512) { + assert_eq!(state_1.working_state, state_2.working_state); + assert_eq!(state_1.buffer[..], state_2.buffer[..]); + assert_eq!(state_1.leftover, state_2.leftover); + assert_eq!(state_1.message_len, state_2.message_len); + assert_eq!(state_1.is_finalized, state_2.is_finalized); + } -#[test] -fn double_finalize_with_reset_ok_keyed() { - let data = "what do ya want for nothing?".as_bytes(); + mod test_reset { + use super::*; - let mut state = init(); - state.update(data).unwrap(); - let one = state.finalize().unwrap(); - state.reset(); - state.update(data).unwrap(); - let two = state.finalize().unwrap(); - assert_eq!(one.as_bytes(), two.as_bytes()); -} + #[test] + fn double_reset_ok() { + let data = "what do ya want for nothing?".as_bytes(); -#[test] -fn double_finalize_with_reset_no_update_ok() { - let data = "what do ya want for nothing?".as_bytes(); + let mut state = init(); + state.update(data).unwrap(); + let _ = state.finalize().unwrap(); + state.reset(); + state.reset(); + } + } - let mut state = init(); - state.update(data).unwrap(); - let _ = state.finalize().unwrap(); - state.reset(); - let _ = state.finalize().unwrap(); -} + mod test_update { + use super::*; -#[test] -/// Related bug: https://github.com/brycx/orion/issues/28 -fn update_after_finalize_err() { - let data = "what do ya want for nothing?".as_bytes(); + #[test] + fn update_after_finalize_with_reset_ok() { + let data = "what do ya want for nothing?".as_bytes(); - let mut state = init(); - state.update(data).unwrap(); - let _ = state.finalize().unwrap(); - assert!(state.update(data).is_err()); -} + let mut state = init(); + state.update(data).unwrap(); + let _ = state.finalize().unwrap(); + state.reset(); + state.update(data).unwrap(); + } -#[test] -fn update_after_finalize_with_reset_ok() { - let data = "what do ya want for nothing?".as_bytes(); + #[test] + /// Related bug: https://github.com/brycx/orion/issues/28 + fn update_after_finalize_err() { + let data = "what do ya want for nothing?".as_bytes(); - let mut state = init(); - state.update(data).unwrap(); - let _ = state.finalize().unwrap(); - state.reset(); - state.update(data).unwrap(); -} + let mut state = init(); + state.update(data).unwrap(); + let _ = state.finalize().unwrap(); + assert!(state.update(data).is_err()); + } + } -#[test] -fn double_reset_ok() { - let data = "what do ya want for nothing?".as_bytes(); + mod test_finalize { + use super::*; - let mut state = init(); - state.update(data).unwrap(); - let _ = state.finalize().unwrap(); - state.reset(); - state.reset(); -} + #[test] + fn double_finalize_with_reset_no_update_ok() { + let data = "what do ya want for nothing?".as_bytes(); -#[test] -/// Related bug: https://github.com/brycx/orion/issues/46 -fn reset_after_update_correct_resets() { - let state_1 = init(); + let mut state = init(); + state.update(data).unwrap(); + let _ = state.finalize().unwrap(); + state.reset(); + let _ = state.finalize().unwrap(); + } - let mut state_2 = init(); - state_2.update(b"Tests").unwrap(); - state_2.reset(); + #[test] + fn double_finalize_with_reset_ok() { + let data = "what do ya want for nothing?".as_bytes(); + + let mut state = init(); + state.update(data).unwrap(); + let one = state.finalize().unwrap(); + state.reset(); + state.update(data).unwrap(); + let two = state.finalize().unwrap(); + assert_eq!(one.as_bytes(), two.as_bytes()); + } - assert_eq!(state_1.working_state, state_2.working_state); - assert_eq!(state_1.buffer[..], state_2.buffer[..]); - assert_eq!(state_1.leftover, state_2.leftover); - assert_eq!(state_1.message_len, state_2.message_len); - assert_eq!(state_1.is_finalized, state_2.is_finalized); -} + #[test] + fn double_finalize_err() { + let data = "what do ya want for nothing?".as_bytes(); -#[test] -/// Related bug: https://github.com/brycx/orion/issues/46 -fn reset_after_update_correct_resets_and_verify() { - let mut state_1 = init(); - state_1.update(b"Tests").unwrap(); - let d1 = state_1.finalize().unwrap(); + let mut state = init(); + state.update(data).unwrap(); + let _ = state.finalize().unwrap(); + assert!(state.finalize().is_err()); + } - let mut state_2 = init(); - state_2.update(b"Tests").unwrap(); - state_2.reset(); - state_2.update(b"Tests").unwrap(); - let d2 = state_2.finalize().unwrap(); + } - assert_eq!(d1, d2); -} + mod test_streaming_interface { + use super::*; + + /// Related bug: https://github.com/brycx/orion/issues/46 + /// Testing different usage combinations of init(), update(), + /// finalize() and reset() produce the same Digest. + fn produces_same_hash(data: &[u8]) { + // init(), update(), finalize() + let mut state_1 = init(); + state_1.update(data).unwrap(); + let res_1 = state_1.finalize().unwrap(); + + // init(), reset(), update(), finalize() + let mut state_2 = init(); + state_2.reset(); + state_2.update(data).unwrap(); + let res_2 = state_2.finalize().unwrap(); + + // init(), update(), reset(), update(), finalize() + let mut state_3 = init(); + state_3.update(data).unwrap(); + state_3.reset(); + state_3.update(data).unwrap(); + let res_3 = state_3.finalize().unwrap(); + + // init(), update(), finalize(), reset(), update(), finalize() + let mut state_4 = init(); + state_4.update(data).unwrap(); + let _ = state_4.finalize().unwrap(); + state_4.reset(); + state_4.update(data).unwrap(); + let res_4 = state_4.finalize().unwrap(); + + assert_eq!(res_1, res_2); + assert_eq!(res_2, res_3); + assert_eq!(res_3, res_4); + } -#[test] -#[cfg(feature = "safe_api")] -// Test for issues when incrementally processing data -// with leftover -fn test_streaming_consistency() { - for len in 0..SHA2_BLOCKSIZE * 4 { - let data = vec![0u8; len]; - let mut state = init(); - let mut other_data: Vec = Vec::new(); - - other_data.extend_from_slice(&data); - state.update(&data).unwrap(); - - if data.len() > SHA2_BLOCKSIZE { - other_data.extend_from_slice(b""); - state.update(b"").unwrap(); + /// Related bug: https://github.com/brycx/orion/issues/46 + /// Testing different usage combinations of init(), update(), + /// finalize() and reset() produce the same Digest. + fn produces_same_state(data: &[u8]) { + // init() + let state_1 = init(); + + // init(), reset() + let mut state_2 = init(); + state_2.reset(); + + // init(), update(), reset() + let mut state_3 = init(); + state_3.update(data).unwrap(); + state_3.reset(); + + // init(), update(), finalize(), reset() + let mut state_4 = init(); + state_4.update(data).unwrap(); + let _ = state_4.finalize().unwrap(); + state_4.reset(); + + compare_sha512_states(&state_1, &state_2); + compare_sha512_states(&state_2, &state_3); + compare_sha512_states(&state_3, &state_4); } - if data.len() > SHA2_BLOCKSIZE * 2 { - other_data.extend_from_slice(b"Extra"); - state.update(b"Extra").unwrap(); + + #[test] + /// Related bug: https://github.com/brycx/orion/issues/46 + fn test_produce_same_state() { produces_same_state(b"Tests"); } + + #[test] + /// Related bug: https://github.com/brycx/orion/issues/46 + fn test_produce_same_hash() { produces_same_hash(b"Tests"); } + + #[test] + #[cfg(feature = "safe_api")] + // Test for issues when incrementally processing data + // with leftover + fn test_streaming_consistency() { + for len in 0..SHA2_BLOCKSIZE * 4 { + let data = vec![0u8; len]; + let mut state = init(); + let mut other_data: Vec = Vec::new(); + + other_data.extend_from_slice(&data); + state.update(&data).unwrap(); + + if data.len() > SHA2_BLOCKSIZE { + other_data.extend_from_slice(b""); + state.update(b"").unwrap(); + } + if data.len() > SHA2_BLOCKSIZE * 2 { + other_data.extend_from_slice(b"Extra"); + state.update(b"Extra").unwrap(); + } + if data.len() > SHA2_BLOCKSIZE * 3 { + other_data.extend_from_slice(&[0u8; 256]); + state.update(&[0u8; 256]).unwrap(); + } + + let digest_one_shot = digest(&other_data).unwrap(); + + assert!(state.finalize().unwrap().as_bytes() == digest_one_shot.as_bytes()); + } } - if data.len() > SHA2_BLOCKSIZE * 3 { - other_data.extend_from_slice(&[0u8; 256]); - state.update(&[0u8; 256]).unwrap(); + // Proptests. Only exectued when NOT testing no_std. + #[cfg(not(feature = "no_std"))] + mod proptest { + use super::*; + + quickcheck! { + /// Related bug: https://github.com/brycx/orion/issues/46 + /// Test different streaming state usage patterns. + fn prop_same_hash_different_usage(data: Vec) -> bool { + // Will panic on incorrect results. + produces_same_hash(&data[..]); + + true + } + } + + quickcheck! { + /// Related bug: https://github.com/brycx/orion/issues/46 + /// Test different streaming state usage patterns. + fn prop_same_state_different_usage(data: Vec) -> bool { + // Will panic on incorrect results. + produces_same_state(&data[..]); + + true + } + } + + quickcheck! { + /// Using the one-shot function should always produce the + /// same result as when using the streaming interface. + fn prop_digest_same_as_streaming(data: Vec) -> bool { + let mut state = init(); + state.update(&data[..]).unwrap(); + let stream = state.finalize().unwrap(); + let one_shot = digest(&data[..]).unwrap(); + + (one_shot == stream) + } + } } + } +} - let digest_one_shot = digest(&other_data).unwrap(); +// Testing private functions in the module. +#[cfg(test)] +mod private { + use super::*; + // One function tested per submodule. + + mod test_increment_mlen { + use super::*; + + #[test] + fn test_mlen_increase_values() { + let mut context = Sha512 { + working_state: H0, + buffer: [0u8; SHA2_BLOCKSIZE], + leftover: 0, + message_len: [0u64; 2], + is_finalized: false, + }; + + context.increment_mlen(1); + assert!(context.message_len == [0u64, 8u64]); + context.increment_mlen(17); + assert!(context.message_len == [0u64, 144u64]); + context.increment_mlen(12); + assert!(context.message_len == [0u64, 240u64]); + // Overflow + context.increment_mlen(u64::max_value()); + assert!(context.message_len == [1u64, 232u64]); + } - assert!(state.finalize().unwrap().as_bytes() == digest_one_shot.as_bytes()); + #[test] + #[should_panic] + fn test_panic_on_second_overflow() { + let mut context = Sha512 { + working_state: H0, + buffer: [0u8; SHA2_BLOCKSIZE], + leftover: 0, + message_len: [u64::max_value(), 1u64], + is_finalized: false, + }; + + context.increment_mlen(u64::max_value()); + } } } From f3839354be06a7934c700a528f1ac84da97d4e12 Mon Sep 17 00:00:00 2001 From: brycx Date: Wed, 30 Jan 2019 15:43:35 +0100 Subject: [PATCH 16/40] Fix Sha512 increment_mlen --- src/hazardous/hash/sha512.rs | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/src/hazardous/hash/sha512.rs b/src/hazardous/hash/sha512.rs index 0726c422..c85a9f59 100644 --- a/src/hazardous/hash/sha512.rs +++ b/src/hazardous/hash/sha512.rs @@ -250,10 +250,13 @@ impl Sha512 { // left-shift to get bit-sized representation of length // using .unwrap() because it should not panic in practice let len = length.checked_shl(3).unwrap(); - self.message_len[1] += len; - - if self.message_len[1] < len { - self.message_len[0] += 1; + let (res, was_overflow) = self.message_len[1].overflowing_add(len); + self.message_len[1] = res; + + if was_overflow { + // If this panics size limit is reached. + self.message_len[0] = self.message_len[0].checked_add(1).unwrap(); + } } @@ -655,11 +658,13 @@ mod private { working_state: H0, buffer: [0u8; SHA2_BLOCKSIZE], leftover: 0, - message_len: [u64::max_value(), 1u64], + message_len: [u64::max_value(), u64::max_value() - 7], is_finalized: false, }; + // u64::max_value() - 7, to leave so that the length represented + // in bites should overflow by exactly one. - context.increment_mlen(u64::max_value()); + context.increment_mlen(1); } } } From 2d32d3124c5dc8eac7ade5b6df1dadc4fa2155c6 Mon Sep 17 00:00:00 2001 From: brycx Date: Wed, 30 Jan 2019 15:50:24 +0100 Subject: [PATCH 17/40] Tests: Finish re-organizing Sha512 tests --- src/hazardous/hash/sha512.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/hazardous/hash/sha512.rs b/src/hazardous/hash/sha512.rs index c85a9f59..f4be80d1 100644 --- a/src/hazardous/hash/sha512.rs +++ b/src/hazardous/hash/sha512.rs @@ -398,7 +398,7 @@ mod public { use super::*; #[test] - fn double_reset_ok() { + fn test_double_reset_ok() { let data = "what do ya want for nothing?".as_bytes(); let mut state = init(); @@ -413,7 +413,7 @@ mod public { use super::*; #[test] - fn update_after_finalize_with_reset_ok() { + fn test_update_after_finalize_with_reset_ok() { let data = "what do ya want for nothing?".as_bytes(); let mut state = init(); @@ -425,7 +425,7 @@ mod public { #[test] /// Related bug: https://github.com/brycx/orion/issues/28 - fn update_after_finalize_err() { + fn test_update_after_finalize_err() { let data = "what do ya want for nothing?".as_bytes(); let mut state = init(); @@ -439,7 +439,7 @@ mod public { use super::*; #[test] - fn double_finalize_with_reset_no_update_ok() { + fn test_double_finalize_with_reset_no_update_ok() { let data = "what do ya want for nothing?".as_bytes(); let mut state = init(); @@ -450,7 +450,7 @@ mod public { } #[test] - fn double_finalize_with_reset_ok() { + fn test_double_finalize_with_reset_ok() { let data = "what do ya want for nothing?".as_bytes(); let mut state = init(); @@ -463,7 +463,7 @@ mod public { } #[test] - fn double_finalize_err() { + fn test_double_finalize_err() { let data = "what do ya want for nothing?".as_bytes(); let mut state = init(); From 62d66c8af7e978c687295bf4cc72cb584267e4c7 Mon Sep 17 00:00:00 2001 From: brycx Date: Wed, 30 Jan 2019 16:09:24 +0100 Subject: [PATCH 18/40] Tests: Reorganize Hmac tests --- src/hazardous/hash/sha512.rs | 25 +- src/hazardous/mac/hmac.rs | 428 ++++++++++++++++++++++++----------- 2 files changed, 310 insertions(+), 143 deletions(-) diff --git a/src/hazardous/hash/sha512.rs b/src/hazardous/hash/sha512.rs index f4be80d1..ea29b546 100644 --- a/src/hazardous/hash/sha512.rs +++ b/src/hazardous/hash/sha512.rs @@ -252,11 +252,10 @@ impl Sha512 { let len = length.checked_shl(3).unwrap(); let (res, was_overflow) = self.message_len[1].overflowing_add(len); self.message_len[1] = res; - + if was_overflow { // If this panics size limit is reached. self.message_len[0] = self.message_len[0].checked_add(1).unwrap(); - } } @@ -377,23 +376,23 @@ pub fn digest(data: &[u8]) -> Result { Ok(state.finalize()?) } +#[cfg(test)] +/// Compare two Sha512 state objects to check if their fields +/// are the same. +pub fn compare_sha512_states(state_1: &Sha512, state_2: &Sha512) { + assert_eq!(state_1.working_state, state_2.working_state); + assert_eq!(state_1.buffer[..], state_2.buffer[..]); + assert_eq!(state_1.leftover, state_2.leftover); + assert_eq!(state_1.message_len, state_2.message_len); + assert_eq!(state_1.is_finalized, state_2.is_finalized); +} + // Testing public functions in the module. #[cfg(test)] mod public { use super::*; // One function tested per submodule. - - /// Compare two Sha512 state objects to check if their fields - /// are the same. - fn compare_sha512_states(state_1: &Sha512, state_2: &Sha512) { - assert_eq!(state_1.working_state, state_2.working_state); - assert_eq!(state_1.buffer[..], state_2.buffer[..]); - assert_eq!(state_1.leftover, state_2.leftover); - assert_eq!(state_1.message_len, state_2.message_len); - assert_eq!(state_1.is_finalized, state_2.is_finalized); - } - mod test_reset { use super::*; diff --git a/src/hazardous/mac/hmac.rs b/src/hazardous/mac/hmac.rs index 66351509..5adbf211 100644 --- a/src/hazardous/mac/hmac.rs +++ b/src/hazardous/mac/hmac.rs @@ -228,159 +228,327 @@ pub fn verify( } } -#[test] -fn finalize_and_verify_true() { - let secret_key = SecretKey::from_slice("Jefe".as_bytes()).unwrap(); - let data = "what do ya want for nothing?".as_bytes(); - - let mut tag = init(&secret_key); - tag.update(data).unwrap(); - - assert_eq!( - verify( - &tag.finalize().unwrap(), - &SecretKey::from_slice("Jefe".as_bytes()).unwrap(), - data - ) - .unwrap(), - true - ); -} +// Testing public functions in the module. +#[cfg(test)] +mod public { + use super::*; -#[test] -fn veriy_false_wrong_data() { - let secret_key = SecretKey::from_slice("Jefe".as_bytes()).unwrap(); - let data = "what do ya want for nothing?".as_bytes(); + use crate::hazardous::hash::sha512::compare_sha512_states; - let mut tag = init(&secret_key); - tag.update(data).unwrap(); + // One function tested per submodule. - assert!(verify( - &tag.finalize().unwrap(), - &SecretKey::from_slice("Jefe".as_bytes()).unwrap(), - "what do ya want for something?".as_bytes() - ) - .is_err()); -} + /// Compare two Sha512 state objects to check if their fields + /// are the same. + fn compare_hmac_states(state_1: &Hmac, state_2: &Hmac) { + compare_sha512_states(&state_1.opad_hasher, &state_1.opad_hasher); + compare_sha512_states(&state_1.ipad_hasher, &state_1.ipad_hasher); -#[test] -fn veriy_false_wrong_secret_key() { - let secret_key = SecretKey::from_slice("Jefe".as_bytes()).unwrap(); - let data = "what do ya want for nothing?".as_bytes(); + assert_eq!(state_1.ipad[..], state_2.ipad[..]); + assert_eq!(state_1.is_finalized, state_2.is_finalized); + } - let mut tag = init(&secret_key); - tag.update(data).unwrap(); + mod test_verify { + use super::*; + + #[test] + fn finalize_and_verify_true() { + let secret_key = SecretKey::from_slice("Jefe".as_bytes()).unwrap(); + let data = "what do ya want for nothing?".as_bytes(); + + let mut tag = init(&secret_key); + tag.update(data).unwrap(); + + assert_eq!( + verify( + &tag.finalize().unwrap(), + &SecretKey::from_slice("Jefe".as_bytes()).unwrap(), + data + ) + .unwrap(), + true + ); + } - assert!(verify( - &tag.finalize().unwrap(), - &SecretKey::from_slice("Jose".as_bytes()).unwrap(), - data - ) - .is_err()); -} + // Proptests. Only exectued when NOT testing no_std. + #[cfg(not(feature = "no_std"))] + mod proptest { + use super::*; + + quickcheck! { + /// When using the same parameters verify() should always yeild true. + fn prop_verify_same_params_true(data: Vec) -> bool { + let sk = SecretKey::generate().unwrap(); + + let mut state = init(&sk); + state.update(&data[..]).unwrap(); + let tag = state.finalize().unwrap(); + // Failed verification on Err so res is not needed. + let _res = verify(&tag, &sk, &data[..]).unwrap(); + + true + } + } + + quickcheck! { + /// When using the same parameters verify() should always yeild true. + fn prop_verify_diff_key_false(data: Vec) -> bool { + let sk = SecretKey::generate().unwrap(); + let mut state = init(&sk); + state.update(&data[..]).unwrap(); + let tag = state.finalize().unwrap(); + + let bad_sk = SecretKey::generate().unwrap(); + + let res = if verify(&tag, &bad_sk, &data[..]).is_err() { + true + } else { + false + }; + + res + } + } + } + } -#[test] -fn double_finalize_err() { - let secret_key = SecretKey::from_slice("Jefe".as_bytes()).unwrap(); - let data = "what do ya want for nothing?".as_bytes(); + mod test_reset { + use super::*; - let mut tag = init(&secret_key); - tag.update(data).unwrap(); - let _ = tag.finalize().unwrap(); - assert!(tag.finalize().is_err()); -} + #[test] + fn test_double_reset_ok() { + let sk = SecretKey::generate().unwrap(); + let data = "what do ya want for nothing?".as_bytes(); -#[test] -fn double_finalize_with_reset_ok() { - let secret_key = SecretKey::from_slice("Jefe".as_bytes()).unwrap(); - let data = "what do ya want for nothing?".as_bytes(); - - let mut tag = init(&secret_key); - tag.update(data).unwrap(); - let one = tag.finalize().unwrap(); - tag.reset(); - tag.update(data).unwrap(); - let two = tag.finalize().unwrap(); - assert_eq!(one.unprotected_as_bytes(), two.unprotected_as_bytes()); -} + let mut state = init(&sk); + state.update(data).unwrap(); + let _ = state.finalize().unwrap(); + state.reset(); + state.reset(); + } + } -#[test] -fn double_finalize_with_reset_no_update_ok() { - let secret_key = SecretKey::from_slice("Jefe".as_bytes()).unwrap(); - let data = "what do ya want for nothing?".as_bytes(); + mod test_update { + use super::*; - let mut tag = init(&secret_key); - tag.update(data).unwrap(); - let _ = tag.finalize().unwrap(); - tag.reset(); - let _ = tag.finalize().unwrap(); -} + #[test] + fn test_update_after_finalize_with_reset_ok() { + let sk = SecretKey::generate().unwrap(); + let data = "what do ya want for nothing?".as_bytes(); + + let mut state = init(&sk); + state.update(data).unwrap(); + let _ = state.finalize().unwrap(); + state.reset(); + state.update(data).unwrap(); + } -#[test] -/// Related bug: https://github.com/brycx/orion/issues/28 -fn update_after_finalize_err() { - let secret_key = SecretKey::from_slice("Jefe".as_bytes()).unwrap(); - let data = "what do ya want for nothing?".as_bytes(); + #[test] + /// Related bug: https://github.com/brycx/orion/issues/28 + fn test_update_after_finalize_err() { + let sk = SecretKey::generate().unwrap(); + let data = "what do ya want for nothing?".as_bytes(); - let mut tag = init(&secret_key); - tag.update(data).unwrap(); - let _ = tag.finalize().unwrap(); - assert!(tag.update(data).is_err()); -} + let mut state = init(&sk); + state.update(data).unwrap(); + let _ = state.finalize().unwrap(); + assert!(state.update(data).is_err()); + } + } -#[test] -fn update_after_finalize_with_reset_ok() { - let secret_key = SecretKey::from_slice("Jefe".as_bytes()).unwrap(); - let data = "what do ya want for nothing?".as_bytes(); + mod test_finalize { + use super::*; - let mut tag = init(&secret_key); - tag.update(data).unwrap(); - let _ = tag.finalize().unwrap(); - tag.reset(); - tag.update(data).unwrap(); -} + #[test] + fn test_double_finalize_with_reset_no_update_ok() { + let sk = SecretKey::generate().unwrap(); + let data = "what do ya want for nothing?".as_bytes(); -#[test] -fn double_reset_ok() { - let secret_key = SecretKey::from_slice("Jefe".as_bytes()).unwrap(); - let data = "what do ya want for nothing?".as_bytes(); + let mut state = init(&sk); + state.update(data).unwrap(); + let _ = state.finalize().unwrap(); + state.reset(); + let _ = state.finalize().unwrap(); + } - let mut tag = init(&secret_key); - tag.update(data).unwrap(); - let _ = tag.finalize().unwrap(); - tag.reset(); - tag.reset(); -} + #[test] + fn test_double_finalize_with_reset_ok() { + let sk = SecretKey::generate().unwrap(); + let data = "what do ya want for nothing?".as_bytes(); + + let mut state = init(&sk); + state.update(data).unwrap(); + let one = state.finalize().unwrap(); + state.reset(); + state.update(data).unwrap(); + let two = state.finalize().unwrap(); + assert_eq!(one, two); + } -#[test] -/// Related bug: https://github.com/brycx/orion/issues/46 -fn reset_after_update_correct_resets() { - let secret_key = SecretKey::from_slice("Jefe".as_bytes()).unwrap(); + #[test] + fn test_double_finalize_err() { + let sk = SecretKey::generate().unwrap(); + let data = "what do ya want for nothing?".as_bytes(); - let state_1 = init(&secret_key); + let mut state = init(&sk); + state.update(data).unwrap(); + let _ = state.finalize().unwrap(); + assert!(state.finalize().is_err()); + } - let mut state_2 = init(&secret_key); - state_2.update(b"Tests").unwrap(); - state_2.reset(); + } - assert_eq!(state_1.ipad[..], state_2.ipad[..]); - assert_eq!(state_1.is_finalized, state_2.is_finalized); -} + mod test_streaming_interface { + use super::*; + + /// Related bug: https://github.com/brycx/orion/issues/46 + /// Testing different usage combinations of init(), update(), + /// finalize() and reset() produce the same Digest. + fn produces_same_hash(sk: &SecretKey, data: &[u8]) { + // init(), update(), finalize() + let mut state_1 = init(&sk); + state_1.update(data).unwrap(); + let res_1 = state_1.finalize().unwrap(); + + // init(), reset(), update(), finalize() + let mut state_2 = init(&sk); + state_2.reset(); + state_2.update(data).unwrap(); + let res_2 = state_2.finalize().unwrap(); + + // init(), update(), reset(), update(), finalize() + let mut state_3 = init(&sk); + state_3.update(data).unwrap(); + state_3.reset(); + state_3.update(data).unwrap(); + let res_3 = state_3.finalize().unwrap(); + + // init(), update(), finalize(), reset(), update(), finalize() + let mut state_4 = init(&sk); + state_4.update(data).unwrap(); + let _ = state_4.finalize().unwrap(); + state_4.reset(); + state_4.update(data).unwrap(); + let res_4 = state_4.finalize().unwrap(); + + assert_eq!(res_1, res_2); + assert_eq!(res_2, res_3); + assert_eq!(res_3, res_4); + } -#[test] -/// Related bug: https://github.com/brycx/orion/issues/46 -fn reset_after_update_correct_resets_and_verify() { - let secret_key = SecretKey::from_slice("Jefe".as_bytes()).unwrap(); + /// Related bug: https://github.com/brycx/orion/issues/46 + /// Testing different usage combinations of init(), update(), + /// finalize() and reset() produce the same Digest. + fn produces_same_state(sk: &SecretKey, data: &[u8]) { + // init() + let state_1 = init(&sk); + + // init(), reset() + let mut state_2 = init(&sk); + state_2.reset(); + + // init(), update(), reset() + let mut state_3 = init(&sk); + state_3.update(data).unwrap(); + state_3.reset(); + + // init(), update(), finalize(), reset() + let mut state_4 = init(&sk); + state_4.update(data).unwrap(); + let _ = state_4.finalize().unwrap(); + state_4.reset(); + + compare_hmac_states(&state_1, &state_2); + compare_hmac_states(&state_2, &state_3); + compare_hmac_states(&state_3, &state_4); + } - let mut state_1 = init(&secret_key); - state_1.update(b"Tests").unwrap(); - let d1 = state_1.finalize().unwrap(); + #[test] + /// Related bug: https://github.com/brycx/orion/issues/46 + fn test_produce_same_state() { + let sk = SecretKey::generate().unwrap(); + produces_same_state(&sk, b"Tests"); + } - let mut state_2 = init(&secret_key); - state_2.update(b"Tests").unwrap(); - state_2.reset(); - state_2.update(b"Tests").unwrap(); - let d2 = state_2.finalize().unwrap(); + #[test] + /// Related bug: https://github.com/brycx/orion/issues/46 + fn test_produce_same_hash() { + let sk = SecretKey::generate().unwrap(); + produces_same_hash(&sk, b"Tests"); + } - assert_eq!(d1, d2); + #[test] + #[cfg(feature = "safe_api")] + // Test for issues when incrementally processing data. + fn test_streaming_consistency() { + for len in 0..SHA2_BLOCKSIZE * 4 { + let sk = SecretKey::generate().unwrap(); + let data = vec![0u8; len]; + let mut state = init(&sk); + let mut other_data: Vec = Vec::new(); + + other_data.extend_from_slice(&data); + state.update(&data).unwrap(); + + if data.len() > SHA2_BLOCKSIZE { + other_data.extend_from_slice(b""); + state.update(b"").unwrap(); + } + if data.len() > SHA2_BLOCKSIZE * 2 { + other_data.extend_from_slice(b"Extra"); + state.update(b"Extra").unwrap(); + } + if data.len() > SHA2_BLOCKSIZE * 3 { + other_data.extend_from_slice(&[0u8; 256]); + state.update(&[0u8; 256]).unwrap(); + } + + let digest_one_shot = hmac(&sk, &other_data).unwrap(); + + assert!(state.finalize().unwrap() == digest_one_shot); + } + } + // Proptests. Only exectued when NOT testing no_std. + #[cfg(not(feature = "no_std"))] + mod proptest { + use super::*; + + quickcheck! { + /// Related bug: https://github.com/brycx/orion/issues/46 + /// Test different streaming state usage patterns. + fn prop_same_hash_different_usage(data: Vec) -> bool { + let sk = SecretKey::generate().unwrap(); + // Will panic on incorrect results. + produces_same_hash(&sk, &data[..]); + + true + } + } + + quickcheck! { + /// Related bug: https://github.com/brycx/orion/issues/46 + /// Test different streaming state usage patterns. + fn prop_same_state_different_usage(data: Vec) -> bool { + let sk = SecretKey::generate().unwrap(); + // Will panic on incorrect results. + produces_same_state(&sk, &data[..]); + + true + } + } + + quickcheck! { + /// Using the one-shot function should always produce the + /// same result as when using the streaming interface. + fn prop_hmac_same_as_streaming(data: Vec) -> bool { + let sk = SecretKey::generate().unwrap(); + let mut state = init(&sk); + state.update(&data[..]).unwrap(); + let stream = state.finalize().unwrap(); + let one_shot = hmac(&sk, &data[..]).unwrap(); + + (one_shot == stream) + } + } + } + } } From 5fb5057a946b4bd71b5db0e450fe2b94bde45494 Mon Sep 17 00:00:00 2001 From: brycx Date: Wed, 30 Jan 2019 16:33:47 +0100 Subject: [PATCH 19/40] Tests: Re-organize poly1305 tests --- src/hazardous/mac/hmac.rs | 18 +- src/hazardous/mac/poly1305.rs | 504 ++++++++++++++++++++++++---------- 2 files changed, 372 insertions(+), 150 deletions(-) diff --git a/src/hazardous/mac/hmac.rs b/src/hazardous/mac/hmac.rs index 5adbf211..f9634da4 100644 --- a/src/hazardous/mac/hmac.rs +++ b/src/hazardous/mac/hmac.rs @@ -316,7 +316,7 @@ mod public { #[test] fn test_double_reset_ok() { - let sk = SecretKey::generate().unwrap(); + let sk = SecretKey::from_slice("Jefe".as_bytes()).unwrap(); let data = "what do ya want for nothing?".as_bytes(); let mut state = init(&sk); @@ -332,7 +332,7 @@ mod public { #[test] fn test_update_after_finalize_with_reset_ok() { - let sk = SecretKey::generate().unwrap(); + let sk = SecretKey::from_slice("Jefe".as_bytes()).unwrap(); let data = "what do ya want for nothing?".as_bytes(); let mut state = init(&sk); @@ -345,7 +345,7 @@ mod public { #[test] /// Related bug: https://github.com/brycx/orion/issues/28 fn test_update_after_finalize_err() { - let sk = SecretKey::generate().unwrap(); + let sk = SecretKey::from_slice("Jefe".as_bytes()).unwrap(); let data = "what do ya want for nothing?".as_bytes(); let mut state = init(&sk); @@ -360,7 +360,7 @@ mod public { #[test] fn test_double_finalize_with_reset_no_update_ok() { - let sk = SecretKey::generate().unwrap(); + let sk = SecretKey::from_slice("Jefe".as_bytes()).unwrap(); let data = "what do ya want for nothing?".as_bytes(); let mut state = init(&sk); @@ -372,7 +372,7 @@ mod public { #[test] fn test_double_finalize_with_reset_ok() { - let sk = SecretKey::generate().unwrap(); + let sk = SecretKey::from_slice("Jefe".as_bytes()).unwrap(); let data = "what do ya want for nothing?".as_bytes(); let mut state = init(&sk); @@ -386,7 +386,7 @@ mod public { #[test] fn test_double_finalize_err() { - let sk = SecretKey::generate().unwrap(); + let sk = SecretKey::from_slice("Jefe".as_bytes()).unwrap(); let data = "what do ya want for nothing?".as_bytes(); let mut state = init(&sk); @@ -465,14 +465,14 @@ mod public { #[test] /// Related bug: https://github.com/brycx/orion/issues/46 fn test_produce_same_state() { - let sk = SecretKey::generate().unwrap(); + let sk = SecretKey::from_slice("Jefe".as_bytes()).unwrap(); produces_same_state(&sk, b"Tests"); } #[test] /// Related bug: https://github.com/brycx/orion/issues/46 fn test_produce_same_hash() { - let sk = SecretKey::generate().unwrap(); + let sk = SecretKey::from_slice("Jefe".as_bytes()).unwrap(); produces_same_hash(&sk, b"Tests"); } @@ -481,7 +481,7 @@ mod public { // Test for issues when incrementally processing data. fn test_streaming_consistency() { for len in 0..SHA2_BLOCKSIZE * 4 { - let sk = SecretKey::generate().unwrap(); + let sk = SecretKey::from_slice("Jefe".as_bytes()).unwrap(); let data = vec![0u8; len]; let mut state = init(&sk); let mut other_data: Vec = Vec::new(); diff --git a/src/hazardous/mac/poly1305.rs b/src/hazardous/mac/poly1305.rs index 62ed5c61..91a81faa 100644 --- a/src/hazardous/mac/poly1305.rs +++ b/src/hazardous/mac/poly1305.rs @@ -412,180 +412,402 @@ pub fn verify( } } -#[test] -fn test_wrong_key_len() { - assert!(OneTimeKey::from_slice(&[0u8; 31]).is_err()); - assert!(OneTimeKey::from_slice(&[0u8; 33]).is_err()); - assert!(OneTimeKey::from_slice(&[0u8; 32]).is_ok()); -} +// Testing public functions in the module. +#[cfg(test)] +mod public { + use super::*; + + // One function tested per submodule. + + /// Compare two Poly1305 state objects to check if their fields + /// are the same. + fn compare_poly1305_states(state_1: &Poly1305, state_2: &Poly1305) { + assert_eq!(state_1.a, state_2.a); + assert_eq!(state_1.r, state_2.r); + assert_eq!(state_1.s, state_2.s); + assert_eq!(state_1.leftover, state_2.leftover); + assert_eq!(state_1.buffer[..], state_2.buffer[..]); + assert_eq!(state_1.is_finalized, state_2.is_finalized); + } -#[test] -fn test_poly1305_oneshot_ok() { - assert!(poly1305(&OneTimeKey::from_slice(&[0u8; 32]).unwrap(), &[0u8; 16]).is_ok()); -} + mod test_verify { + use super::*; + + #[test] + fn test_poly1305_verify_ok() { + let tag = poly1305(&OneTimeKey::from_slice(&[0u8; 32]).unwrap(), &[0u8; 16]).unwrap(); + verify( + &tag, + &OneTimeKey::from_slice(&[0u8; 32]).unwrap(), + &[0u8; 16], + ) + .unwrap(); + } -#[test] -fn test_poly1305_verify_ok() { - let tag = poly1305(&OneTimeKey::from_slice(&[0u8; 32]).unwrap(), &[0u8; 16]).unwrap(); - verify( - &tag, - &OneTimeKey::from_slice(&[0u8; 32]).unwrap(), - &[0u8; 16], - ) - .unwrap(); -} + #[test] + fn test_poly1305_verify_err() { + let mut tag = + poly1305(&OneTimeKey::from_slice(&[0u8; 32]).unwrap(), &[0u8; 16]).unwrap(); + tag.value[0] ^= 1; + assert!(verify( + &tag, + &OneTimeKey::from_slice(&[0u8; 32]).unwrap(), + &[0u8; 16], + ) + .is_err()); + } -#[test] -fn test_poly1305_verify_err() { - let mut tag = poly1305(&OneTimeKey::from_slice(&[0u8; 32]).unwrap(), &[0u8; 16]).unwrap(); - tag.value[0] ^= 1; - assert!(verify( - &tag, - &OneTimeKey::from_slice(&[0u8; 32]).unwrap(), - &[0u8; 16], - ) - .is_err()); -} + #[test] + fn finalize_and_verify_true() { + let secret_key = OneTimeKey::from_slice(&[0u8; 32]).unwrap(); + let data = "what do ya want for nothing?".as_bytes(); + + let mut tag = init(&secret_key); + tag.update(data).unwrap(); + + assert_eq!( + verify( + &tag.finalize().unwrap(), + &OneTimeKey::from_slice(&[0u8; 32]).unwrap(), + data + ) + .unwrap(), + true + ); + } -#[test] -fn test_bad_key_err_less() { - assert!(OneTimeKey::from_slice(&[0u8; 31]).is_err()); -} + // Proptests. Only exectued when NOT testing no_std. + #[cfg(not(feature = "no_std"))] + mod proptest { + use super::*; -#[test] -fn test_poly1305_oneshot_bad_key_err_greater() { - assert!(OneTimeKey::from_slice(&[0u8; 33]).is_err()); -} + quickcheck! { + /// When using the same parameters verify() should always yeild true. + fn prop_verify_same_params_true(data: Vec) -> bool { + let sk = OneTimeKey::generate().unwrap(); -#[test] -fn double_finalize_err() { - let mut poly1305_state = init(&OneTimeKey::from_slice(&[0u8; 32]).unwrap()); + let mut state = init(&sk); + state.update(&data[..]).unwrap(); + let tag = state.finalize().unwrap(); + // Failed verification on Err so res is not needed. + let _res = verify(&tag, &sk, &data[..]).unwrap(); - poly1305_state.update(&[0u8; 16]).unwrap(); - let _ = poly1305_state.finalize().unwrap(); - assert!(poly1305_state.finalize().is_err()); -} + true + } + } -#[test] -fn double_finalize_with_reset_ok() { - let mut poly1305_state = init(&OneTimeKey::from_slice(&[0u8; 32]).unwrap()); + quickcheck! { + /// When using the same parameters verify() should always yeild true. + fn prop_verify_diff_key_false(data: Vec) -> bool { + let sk = OneTimeKey::generate().unwrap(); + let mut state = init(&sk); + state.update(&data[..]).unwrap(); + let tag = state.finalize().unwrap(); - poly1305_state.update(&[0u8; 16]).unwrap(); - let one = poly1305_state.finalize().unwrap(); - poly1305_state.reset(); - poly1305_state.update(&[0u8; 16]).unwrap(); - let two = poly1305_state.finalize().unwrap(); - assert_eq!(one.unprotected_as_bytes(), two.unprotected_as_bytes()); -} + let bad_sk = OneTimeKey::generate().unwrap(); -#[test] -fn double_finalize_with_reset_no_update_ok() { - let mut poly1305_state = init(&OneTimeKey::from_slice(&[0u8; 32]).unwrap()); + let res = if verify(&tag, &bad_sk, &data[..]).is_err() { + true + } else { + false + }; - poly1305_state.update(&[0u8; 16]).unwrap(); - let _ = poly1305_state.finalize().unwrap(); - poly1305_state.reset(); - let _ = poly1305_state.finalize().unwrap(); -} + res + } + } + } + } -#[test] -/// Related bug: https://github.com/brycx/orion/issues/28 -fn update_after_finalize_err() { - let mut poly1305_state = init(&OneTimeKey::from_slice(&[0u8; 32]).unwrap()); + mod test_reset { + use super::*; - poly1305_state.update(&[0u8; 16]).unwrap(); - let _ = poly1305_state.finalize().unwrap(); - assert!(poly1305_state.update(&[0u8; 16]).is_err()); -} + #[test] + fn test_double_reset_ok() { + let sk = OneTimeKey::from_slice(&[0u8; 32]).unwrap(); + let data = "what do ya want for nothing?".as_bytes(); -#[test] -fn update_after_finalize_with_reset_ok() { - let mut poly1305_state = init(&OneTimeKey::from_slice(&[0u8; 32]).unwrap()); + let mut state = init(&sk); + state.update(data).unwrap(); + let _ = state.finalize().unwrap(); + state.reset(); + state.reset(); + } + } - poly1305_state.update(&[0u8; 16]).unwrap(); - let expected = poly1305_state.finalize().unwrap(); - poly1305_state.reset(); - poly1305_state.update(&[0u8; 16]).unwrap(); - assert!(&expected == &poly1305_state.finalize().unwrap()); -} + mod test_update { + use super::*; -#[test] -fn double_reset_ok() { - let mut poly1305_state = init(&OneTimeKey::from_slice(&[0u8; 32]).unwrap()); + #[test] + fn test_update_after_finalize_with_reset_ok() { + let sk = OneTimeKey::from_slice(&[0u8; 32]).unwrap(); + let data = "what do ya want for nothing?".as_bytes(); - poly1305_state.update(&[0u8; 16]).unwrap(); - let _ = poly1305_state.finalize().unwrap(); - poly1305_state.reset(); - poly1305_state.reset(); -} + let mut state = init(&sk); + state.update(data).unwrap(); + let _ = state.finalize().unwrap(); + state.reset(); + state.update(data).unwrap(); + } -#[test] -/// Related bug: https://github.com/brycx/orion/issues/46 -fn reset_after_update_correct_resets() { - let secret_key = OneTimeKey::from_slice(&[0u8; 32]).unwrap(); + #[test] + /// Related bug: https://github.com/brycx/orion/issues/28 + fn test_update_after_finalize_err() { + let sk = OneTimeKey::from_slice(&[0u8; 32]).unwrap(); + let data = "what do ya want for nothing?".as_bytes(); - let state_1 = init(&secret_key); + let mut state = init(&sk); + state.update(data).unwrap(); + let _ = state.finalize().unwrap(); + assert!(state.update(data).is_err()); + } + } - let mut state_2 = init(&secret_key); - state_2.update(b"Tests").unwrap(); - state_2.reset(); + mod test_finalize { + use super::*; - assert_eq!(state_1.a, state_2.a); - assert_eq!(state_1.r, state_2.r); - assert_eq!(state_1.s, state_2.s); - assert_eq!(state_1.leftover, state_2.leftover); - assert_eq!(state_1.buffer[..], state_2.buffer[..]); - assert_eq!(state_1.is_finalized, state_2.is_finalized); -} + #[test] + fn test_double_finalize_with_reset_no_update_ok() { + let sk = OneTimeKey::from_slice(&[0u8; 32]).unwrap(); + let data = "what do ya want for nothing?".as_bytes(); -#[test] -/// Related bug: https://github.com/brycx/orion/issues/46 -fn reset_after_update_correct_resets_and_verify() { - let secret_key = OneTimeKey::from_slice(&[0u8; 32]).unwrap(); + let mut state = init(&sk); + state.update(data).unwrap(); + let _ = state.finalize().unwrap(); + state.reset(); + let _ = state.finalize().unwrap(); + } - let mut state_1 = init(&secret_key); - state_1.update(b"Tests").unwrap(); - let d1 = state_1.finalize().unwrap(); + #[test] + fn test_double_finalize_with_reset_ok() { + let sk = OneTimeKey::from_slice(&[0u8; 32]).unwrap(); + let data = "what do ya want for nothing?".as_bytes(); + + let mut state = init(&sk); + state.update(data).unwrap(); + let one = state.finalize().unwrap(); + state.reset(); + state.update(data).unwrap(); + let two = state.finalize().unwrap(); + assert_eq!(one, two); + } - let mut state_2 = init(&secret_key); - state_2.update(b"Tests").unwrap(); - state_2.reset(); - state_2.update(b"Tests").unwrap(); - let d2 = state_2.finalize().unwrap(); + #[test] + fn test_double_finalize_err() { + let sk = OneTimeKey::from_slice(&[0u8; 32]).unwrap(); + let data = "what do ya want for nothing?".as_bytes(); - assert_eq!(d1, d2); -} + let mut state = init(&sk); + state.update(data).unwrap(); + let _ = state.finalize().unwrap(); + assert!(state.finalize().is_err()); + } -#[test] -#[cfg(feature = "safe_api")] -// Test for issues when incrementally processing data -// with leftover -fn test_streaming_consistency() { - let key = OneTimeKey::from_slice(&[0u8; 32]).unwrap(); + } - for len in 0..POLY1305_BLOCKSIZE * 4 { - let data = vec![0u8; len]; - let mut state = init(&key); - let mut other_data: Vec = Vec::new(); + mod test_streaming_interface { + use super::*; + + /// Related bug: https://github.com/brycx/orion/issues/46 + /// Testing different usage combinations of init(), update(), + /// finalize() and reset() produce the same Digest. + fn produces_same_hash(sk: &OneTimeKey, data: &[u8]) { + // init(), update(), finalize() + let mut state_1 = init(&sk); + state_1.update(data).unwrap(); + let res_1 = state_1.finalize().unwrap(); + + // init(), reset(), update(), finalize() + let mut state_2 = init(&sk); + state_2.reset(); + state_2.update(data).unwrap(); + let res_2 = state_2.finalize().unwrap(); + + // init(), update(), reset(), update(), finalize() + let mut state_3 = init(&sk); + state_3.update(data).unwrap(); + state_3.reset(); + state_3.update(data).unwrap(); + let res_3 = state_3.finalize().unwrap(); + + // init(), update(), finalize(), reset(), update(), finalize() + let mut state_4 = init(&sk); + state_4.update(data).unwrap(); + let _ = state_4.finalize().unwrap(); + state_4.reset(); + state_4.update(data).unwrap(); + let res_4 = state_4.finalize().unwrap(); + + assert_eq!(res_1, res_2); + assert_eq!(res_2, res_3); + assert_eq!(res_3, res_4); + } - other_data.extend_from_slice(&data); - state.update(&data).unwrap(); + /// Related bug: https://github.com/brycx/orion/issues/46 + /// Testing different usage combinations of init(), update(), + /// finalize() and reset() produce the same Digest. + fn produces_same_state(sk: &OneTimeKey, data: &[u8]) { + // init() + let state_1 = init(&sk); + + // init(), reset() + let mut state_2 = init(&sk); + state_2.reset(); + + // init(), update(), reset() + let mut state_3 = init(&sk); + state_3.update(data).unwrap(); + state_3.reset(); + + // init(), update(), finalize(), reset() + let mut state_4 = init(&sk); + state_4.update(data).unwrap(); + let _ = state_4.finalize().unwrap(); + state_4.reset(); + + compare_poly1305_states(&state_1, &state_2); + compare_poly1305_states(&state_2, &state_3); + compare_poly1305_states(&state_3, &state_4); + } - if data.len() > POLY1305_BLOCKSIZE { - other_data.extend_from_slice(b""); - state.update(b"").unwrap(); + #[test] + /// Related bug: https://github.com/brycx/orion/issues/46 + fn test_produce_same_state() { + let sk = OneTimeKey::from_slice(&[0u8; 32]).unwrap(); + produces_same_state(&sk, b"Tests"); } - if data.len() > POLY1305_BLOCKSIZE * 2 { - other_data.extend_from_slice(b"Extra"); - state.update(b"Extra").unwrap(); + + #[test] + /// Related bug: https://github.com/brycx/orion/issues/46 + fn test_produce_same_hash() { + let sk = OneTimeKey::from_slice(&[0u8; 32]).unwrap(); + produces_same_hash(&sk, b"Tests"); } - if data.len() > POLY1305_BLOCKSIZE * 3 { - other_data.extend_from_slice(&[0u8; 256]); - state.update(&[0u8; 256]).unwrap(); + + #[test] + #[cfg(feature = "safe_api")] + // Test for issues when incrementally processing data + // with leftover + fn test_streaming_consistency() { + for len in 0..POLY1305_BLOCKSIZE * 4 { + let key = OneTimeKey::from_slice(&[0u8; 32]).unwrap(); + let data = vec![0u8; len]; + let mut state = init(&key); + let mut other_data: Vec = Vec::new(); + + other_data.extend_from_slice(&data); + state.update(&data).unwrap(); + + if data.len() > POLY1305_BLOCKSIZE { + other_data.extend_from_slice(b""); + state.update(b"").unwrap(); + } + if data.len() > POLY1305_BLOCKSIZE * 2 { + other_data.extend_from_slice(b"Extra"); + state.update(b"Extra").unwrap(); + } + if data.len() > POLY1305_BLOCKSIZE * 3 { + other_data.extend_from_slice(&[0u8; 256]); + state.update(&[0u8; 256]).unwrap(); + } + + let digest_one_shot = poly1305(&key, &other_data).unwrap(); + + assert!(state.finalize().unwrap() == digest_one_shot); + } } + // Proptests. Only exectued when NOT testing no_std. + #[cfg(not(feature = "no_std"))] + mod proptest { + use super::*; + + quickcheck! { + /// Related bug: https://github.com/brycx/orion/issues/46 + /// Test different streaming state usage patterns. + fn prop_same_tag_different_usage(data: Vec) -> bool { + let sk = OneTimeKey::generate().unwrap(); + // Will panic on incorrect results. + produces_same_hash(&sk, &data[..]); + + true + } + } + + quickcheck! { + /// Related bug: https://github.com/brycx/orion/issues/46 + /// Test different streaming state usage patterns. + fn prop_same_state_different_usage(data: Vec) -> bool { + let sk = OneTimeKey::generate().unwrap(); + // Will panic on incorrect results. + produces_same_state(&sk, &data[..]); - let digest_one_shot = poly1305(&key, &other_data).unwrap(); + true + } + } - assert!(state.finalize().unwrap() == digest_one_shot); + quickcheck! { + /// Using the one-shot function should always produce the + /// same result as when using the streaming interface. + fn prop_poly1305_same_as_streaming(data: Vec) -> bool { + let sk = OneTimeKey::generate().unwrap(); + let mut state = init(&sk); + state.update(&data[..]).unwrap(); + let stream = state.finalize().unwrap(); + let one_shot = poly1305(&sk, &data[..]).unwrap(); + + (one_shot == stream) + } + } + } } } + +// Testing private functions in the module. +#[cfg(test)] +mod private { + use super::*; + + // One function tested per submodule. + + mod test_process_block { + use super::*; + + #[test] + fn test_process_block_len() { + let block_0 = [0u8; 0]; + let block_1 = [0u8; 15]; + let block_2 = [0u8; 17]; + let block_3 = [0u8; 16]; + + let sk = OneTimeKey::from_slice(&[0u8; 32]).unwrap(); + let mut state = init(&sk); + + assert!(state.process_block(&block_0).is_err()); + assert!(state.process_block(&block_1).is_err()); + assert!(state.process_block(&block_2).is_err()); + assert!(state.process_block(&block_3).is_ok()); + + } + } + + mod test_process_end_of_stream { + use super::*; + + #[test] + fn test_process_no_panic() { + let block = [0u8; 16]; + let sk = OneTimeKey::from_slice(&[0u8; 32]).unwrap(); + let mut state = init(&sk); + // Should not panic + state.process_end_of_stream(); + state.reset(); + state.process_end_of_stream(); + + let mut state = init(&sk); + state.process_block(&block).unwrap(); + // Should not panic + state.process_end_of_stream(); + state.reset(); + state.process_end_of_stream(); + } + } +} From a886bb82860dfb0f019aa93131a6795800acc56a Mon Sep 17 00:00:00 2001 From: brycx Date: Wed, 30 Jan 2019 17:09:46 +0100 Subject: [PATCH 20/40] Tests: Re-organize cshake tests --- src/hazardous/mac/poly1305.rs | 27 +- src/hazardous/xof/cshake.rs | 667 ++++++++++++++++++++-------------- 2 files changed, 399 insertions(+), 295 deletions(-) diff --git a/src/hazardous/mac/poly1305.rs b/src/hazardous/mac/poly1305.rs index 91a81faa..aaf0b295 100644 --- a/src/hazardous/mac/poly1305.rs +++ b/src/hazardous/mac/poly1305.rs @@ -765,15 +765,15 @@ mod public { #[cfg(test)] mod private { use super::*; - - // One function tested per submodule. - + + // One function tested per submodule. + mod test_process_block { use super::*; - #[test] - fn test_process_block_len() { - let block_0 = [0u8; 0]; + #[test] + fn test_process_block_len() { + let block_0 = [0u8; 0]; let block_1 = [0u8; 15]; let block_2 = [0u8; 17]; let block_3 = [0u8; 16]; @@ -785,16 +785,15 @@ mod private { assert!(state.process_block(&block_1).is_err()); assert!(state.process_block(&block_2).is_err()); assert!(state.process_block(&block_3).is_ok()); - - } - } + } + } mod test_process_end_of_stream { use super::*; - #[test] - fn test_process_no_panic() { - let block = [0u8; 16]; + #[test] + fn test_process_no_panic() { + let block = [0u8; 16]; let sk = OneTimeKey::from_slice(&[0u8; 32]).unwrap(); let mut state = init(&sk); // Should not panic @@ -808,6 +807,6 @@ mod private { state.process_end_of_stream(); state.reset(); state.process_end_of_stream(); - } - } + } + } } diff --git a/src/hazardous/xof/cshake.rs b/src/hazardous/xof/cshake.rs index d24972a6..fadc6ce3 100644 --- a/src/hazardous/xof/cshake.rs +++ b/src/hazardous/xof/cshake.rs @@ -99,7 +99,15 @@ impl core::fmt::Debug for CShake { impl CShake { /// Initial setup with encoding of `custom` and `name`. - fn setup(&mut self, custom: &[u8], name: &[u8]) { + fn setup(&mut self, custom: &[u8], name: &[u8]) -> Result<(), UnknownCryptoError> { + if (name.is_empty()) && (custom.is_empty()) { + return Err(UnknownCryptoError); + } + + if name.len() > 65536 || custom.len() > 65536 { + return Err(UnknownCryptoError); + } + // Only append the left encoded rate, not the rate itself as with `name` and // `custom` let (encoded, offset) = left_encode(136_u64); @@ -117,6 +125,8 @@ impl CShake { // Pad with zeroes before calling pad() in finalize() self.hasher.fill_block(); self.setup_hasher = self.hasher.clone(); + + Ok(()) } /// Reset to `init()` state. @@ -167,12 +177,6 @@ pub fn init(custom: &[u8], name: Option<&[u8]>) -> Result *n_val, None => &[0u8; 0], }; - if (name_val.is_empty()) && (custom.is_empty()) { - return Err(UnknownCryptoError); - } - if name_val.len() > 65536 || custom.len() > 65536 { - return Err(UnknownCryptoError); - } // 136 is the rate of Keccak512 let mut hash = CShake { @@ -181,7 +185,7 @@ pub fn init(custom: &[u8], name: Option<&[u8]>) -> Result ([u8; 9], usize) { (input, offset) } -#[test] -fn test_left_encode() { - let (test_1, offset_1) = left_encode(32); - let (test_2, offset_2) = left_encode(255); - let (test_3, offset_3) = left_encode(0); - let (test_4, offset_4) = left_encode(64); - let (test_5, offset_5) = left_encode(u64::max_value()); - - assert_eq!(&test_1[(offset_1 - 1)..], &[1, 32]); - assert_eq!(&test_2[(offset_2 - 1)..], &[1, 255]); - assert_eq!(&test_3[(offset_3 - 1)..], &[1, 0]); - assert_eq!(&test_4[(offset_4 - 1)..], &[1, 64]); - assert_eq!( - &test_5[(offset_5 - 1)..], - &[8, 255, 255, 255, 255, 255, 255, 255, 255] - ); -} +// Testing public functions in the module. +#[cfg(test)] +mod public { + use super::*; + + mod test_endianness_issue { + use super::*; + // See: https://github.com/brycx/orion/issues/15 + #[test] + #[cfg(target_endian = "little")] + fn non_8_div_len() { + let input = b"\x00\x01\x02\x03"; + let custom = b"Email Signature"; + let mut out = [0u8; 17]; + + let mut cshake = init(custom, None).unwrap(); + cshake.update(input).unwrap(); + cshake.finalize(&mut out).unwrap(); + + let expected = b"\xD0\x08\x82\x8E\x2B\x80\xAC\x9D\x22\x18\xFF\xEE\x1D\x07\x0C\x48\xB8\ + \xE4\xC8\x7B\xFF\x32\xC9\x69\x9D\x5B\x68\x96\xEE\xE0\xED\xD1\x64\x02\ + \x0E\x2B\xE0\x56\x08\x58\xD9\xC0\x0C\x03\x7E\x34\xA9\x69\x37\xC5\x61\ + \xA7\x4C\x41\x2B\xB4\xC7\x46\x46\x95\x27\x28\x1C\x8C"; -#[test] -fn err_on_empty_name_custom() { - let custom = b""; - let name = b""; + assert_eq!(expected[..17].len(), out.len()); + assert_eq!(out, &expected[..17]); + } - assert!(init(custom, Some(name)).is_err()); -} + // See: https://github.com/brycx/orion/issues/15 + #[test] + #[cfg(target_endian = "little")] + fn result_ok() { + let input = b"\x00\x01\x02\x03"; + let custom = b"Email Signature"; + let mut out = [0u8; 64]; -#[test] -fn empty_custom_ok() { - let custom = b""; - let name = b"Email signature"; + let mut cshake = init(custom, None).unwrap(); + cshake.update(input).unwrap(); + cshake.finalize(&mut out).unwrap(); - assert!(init(custom, Some(name)).is_ok()); -} + let expected = b"\xD0\x08\x82\x8E\x2B\x80\xAC\x9D\x22\x18\xFF\xEE\x1D\x07\x0C\x48\xB8\ + \xE4\xC8\x7B\xFF\x32\xC9\x69\x9D\x5B\x68\x96\xEE\xE0\xED\xD1\x64\x02\ + \x0E\x2B\xE0\x56\x08\x58\xD9\xC0\x0C\x03\x7E\x34\xA9\x69\x37\xC5\x61\ + \xA7\x4C\x41\x2B\xB4\xC7\x46\x46\x95\x27\x28\x1C\x8C"; -#[test] -fn empty_input_ok() { - let custom = b"Custom String"; - let name = b"Email signature"; + assert_eq!(out.as_ref(), expected.as_ref()); + } - assert!(init(custom, Some(name)).is_ok()); -} + // See: https://github.com/brycx/orion/issues/15 + // Detecting test-case that if tiny-keccak is fixed then this should panic + #[test] + #[cfg(target_endian = "big")] + fn result_ok_assume_wrong_on_big_endian() { + let input = b"\x00\x01\x02\x03"; + let custom = b"Email Signature"; + let mut out = [0u8; 64]; -#[test] -fn err_on_zero_length() { - let input = b"\x00\x01\x02\x03"; - let custom = b""; - let name = b"Email signature"; - let mut out = [0u8; 0]; + let mut cshake = init(custom, None).unwrap(); + cshake.update(input).unwrap(); + cshake.finalize(&mut out).unwrap(); - let mut hash = init(custom, Some(name)).unwrap(); - hash.update(input).unwrap(); - assert!(hash.finalize(&mut out).is_err()); -} + let expected = b"\xD0\x08\x82\x8E\x2B\x80\xAC\x9D\x22\x18\xFF\xEE\x1D\x07\x0C\x48\xB8\ + \xE4\xC8\x7B\xFF\x32\xC9\x69\x9D\x5B\x68\x96\xEE\xE0\xED\xD1\x64\x02\ + \x0E\x2B\xE0\x56\x08\x58\xD9\xC0\x0C\x03\x7E\x34\xA9\x69\x37\xC5\x61\ + \xA7\x4C\x41\x2B\xB4\xC7\x46\x46\x95\x27\x28\x1C\x8C"; -#[test] -fn err_on_above_max_length() { - let input = b"\x00\x01\x02\x03"; - let custom = b""; - let name = b"Email signature"; - let mut out = [0u8; 65537]; + assert_ne!(out.as_ref(), expected.as_ref()); + } - let mut hash = init(custom, Some(name)).unwrap(); - hash.update(input).unwrap(); - assert!(hash.finalize(&mut out).is_err()); -} + // See: https://github.com/brycx/orion/issues/15 + #[test] + #[cfg(target_endian = "little")] + fn verify_err() { + // `name` and `custom` values have been switched here compared to the previous + // one + let input = b"\x00\x01\x02\x03"; + let custom = b""; + let name = b"Email Signature"; + let mut out = [0u8; 64]; + + let mut cshake = init(custom, Some(name)).unwrap(); + cshake.update(input).unwrap(); + cshake.finalize(&mut out).unwrap(); + + let expected = b"\xD0\x08\x82\x8E\x2B\x80\xAC\x9D\x22\x18\xFF\xEE\x1D\x07\x0C\x48\xB8\ + \xE4\xC8\x7B\xFF\x32\xC9\x69\x9D\x5B\x68\x96\xEE\xE0\xED\xD1\x64\x02\ + \x0E\x2B\xE0\x56\x08\x58\xD9\xC0\x0C\x03\x7E\x34\xA9\x69\x37\xC5\x61\ + \xA7\x4C\x41\x2B\xB4\xC7\x46\x46\x95\x27\x28\x1C\x8C"; -#[test] -fn err_on_name_max_length() { - let custom = b""; - let name = [0u8; 65537]; + assert_ne!(out.as_ref(), expected.as_ref()); + } + } - assert!(init(custom, Some(&name)).is_err()); -} + mod test_init { + use super::*; -#[test] -fn err_on_n_c_max_length() { - let custom = [0u8; 65537]; - let name = [0u8; 65537]; + #[test] + fn err_on_empty_name_custom() { + let custom = b""; + let name = b""; - assert!(init(&custom, Some(&name)).is_err()); -} + assert!(init(custom, Some(name)).is_err()); + } -#[test] -fn err_on_custom_max_length() { - let custom = [0u8; 65537]; - let name = [0u8; 0]; + #[test] + fn empty_custom_ok() { + let custom = b""; + let name = b"Email signature"; - assert!(init(&custom, Some(&name)).is_err()); - assert!(init(&custom, None).is_err()); -} + assert!(init(custom, Some(name)).is_ok()); + } -// See: https://github.com/brycx/orion/issues/15 -#[test] -#[cfg(target_endian = "little")] -fn non_8_div_len() { - let input = b"\x00\x01\x02\x03"; - let custom = b"Email Signature"; - let mut out = [0u8; 17]; + #[test] + fn empty_input_ok() { + let custom = b"Custom String"; + let name = b"Email signature"; - let mut cshake = init(custom, None).unwrap(); - cshake.update(input).unwrap(); - cshake.finalize(&mut out).unwrap(); + assert!(init(custom, Some(name)).is_ok()); + } - let expected = b"\xD0\x08\x82\x8E\x2B\x80\xAC\x9D\x22\x18\xFF\xEE\x1D\x07\x0C\x48\xB8\ - \xE4\xC8\x7B\xFF\x32\xC9\x69\x9D\x5B\x68\x96\xEE\xE0\xED\xD1\x64\x02\ - \x0E\x2B\xE0\x56\x08\x58\xD9\xC0\x0C\x03\x7E\x34\xA9\x69\x37\xC5\x61\ - \xA7\x4C\x41\x2B\xB4\xC7\x46\x46\x95\x27\x28\x1C\x8C"; + #[test] + fn err_on_name_max_length() { + let custom = b""; + let name = [0u8; 65537]; - assert_eq!(expected[..17].len(), out.len()); - assert_eq!(out, &expected[..17]); -} + assert!(init(custom, Some(&name)).is_err()); + } -// See: https://github.com/brycx/orion/issues/15 -#[test] -#[cfg(target_endian = "little")] -fn result_ok() { - let input = b"\x00\x01\x02\x03"; - let custom = b"Email Signature"; - let mut out = [0u8; 64]; + #[test] + fn err_on_n_c_max_length() { + let custom = [0u8; 65537]; + let name = [0u8; 65537]; - let mut cshake = init(custom, None).unwrap(); - cshake.update(input).unwrap(); - cshake.finalize(&mut out).unwrap(); + assert!(init(&custom, Some(&name)).is_err()); + } - let expected = b"\xD0\x08\x82\x8E\x2B\x80\xAC\x9D\x22\x18\xFF\xEE\x1D\x07\x0C\x48\xB8\ - \xE4\xC8\x7B\xFF\x32\xC9\x69\x9D\x5B\x68\x96\xEE\xE0\xED\xD1\x64\x02\ - \x0E\x2B\xE0\x56\x08\x58\xD9\xC0\x0C\x03\x7E\x34\xA9\x69\x37\xC5\x61\ - \xA7\x4C\x41\x2B\xB4\xC7\x46\x46\x95\x27\x28\x1C\x8C"; + #[test] + fn err_on_custom_max_length() { + let custom = [0u8; 65537]; + let name = [0u8; 0]; - assert_eq!(out.as_ref(), expected.as_ref()); -} + assert!(init(&custom, Some(&name)).is_err()); + assert!(init(&custom, None).is_err()); + } + } -// See: https://github.com/brycx/orion/issues/15 -// Detecting test-case that if tiny-keccak is fixed then this should panic -#[test] -#[cfg(target_endian = "big")] -fn result_ok_assume_wrong_on_big_endian() { - let input = b"\x00\x01\x02\x03"; - let custom = b"Email Signature"; - let mut out = [0u8; 64]; + mod test_reset { + use super::*; + + #[test] + fn double_reset_ok() { + let input = b"\x00\x01\x02\x03"; + let custom = b""; + let name = b"Email Signature"; + let mut out = [0u8; 64]; + + let mut cshake = init(custom, Some(name)).unwrap(); + cshake.update(input).unwrap(); + cshake.finalize(&mut out).unwrap(); + cshake.reset(); + cshake.reset(); + } + } - let mut cshake = init(custom, None).unwrap(); - cshake.update(input).unwrap(); - cshake.finalize(&mut out).unwrap(); + mod test_update { + use super::*; + + #[test] + /// Related bug: https://github.com/brycx/orion/issues/28 + fn update_after_finalize_err() { + let input = b"\x00\x01\x02\x03"; + let custom = b""; + let name = b"Email Signature"; + let mut out = [0u8; 64]; + + let mut cshake = init(custom, Some(name)).unwrap(); + cshake.update(input).unwrap(); + cshake.finalize(&mut out).unwrap(); + assert!(cshake.update(input).is_err()); + } - let expected = b"\xD0\x08\x82\x8E\x2B\x80\xAC\x9D\x22\x18\xFF\xEE\x1D\x07\x0C\x48\xB8\ - \xE4\xC8\x7B\xFF\x32\xC9\x69\x9D\x5B\x68\x96\xEE\xE0\xED\xD1\x64\x02\ - \x0E\x2B\xE0\x56\x08\x58\xD9\xC0\x0C\x03\x7E\x34\xA9\x69\x37\xC5\x61\ - \xA7\x4C\x41\x2B\xB4\xC7\x46\x46\x95\x27\x28\x1C\x8C"; + #[test] + fn update_after_finalize_with_reset_ok() { + let input = b"\x00\x01\x02\x03"; + let custom = b""; + let name = b"Email Signature"; + let mut out = [0u8; 64]; + let mut out_check = [0u8; 64]; + + let mut cshake = init(custom, Some(name)).unwrap(); + cshake.update(input).unwrap(); + cshake.finalize(&mut out).unwrap(); + cshake.reset(); + cshake.update(input).unwrap(); + cshake.finalize(&mut out_check).unwrap(); + + assert_eq!(out.as_ref(), out_check.as_ref()); + } + } - assert_ne!(out.as_ref(), expected.as_ref()); -} + mod test_finalize { + use super::*; -// See: https://github.com/brycx/orion/issues/15 -#[test] -#[cfg(target_endian = "little")] -fn verify_err() { - // `name` and `custom` values have been switched here compared to the previous - // one - let input = b"\x00\x01\x02\x03"; - let custom = b""; - let name = b"Email Signature"; - let mut out = [0u8; 64]; - - let mut cshake = init(custom, Some(name)).unwrap(); - cshake.update(input).unwrap(); - cshake.finalize(&mut out).unwrap(); - - let expected = b"\xD0\x08\x82\x8E\x2B\x80\xAC\x9D\x22\x18\xFF\xEE\x1D\x07\x0C\x48\xB8\ - \xE4\xC8\x7B\xFF\x32\xC9\x69\x9D\x5B\x68\x96\xEE\xE0\xED\xD1\x64\x02\ - \x0E\x2B\xE0\x56\x08\x58\xD9\xC0\x0C\x03\x7E\x34\xA9\x69\x37\xC5\x61\ - \xA7\x4C\x41\x2B\xB4\xC7\x46\x46\x95\x27\x28\x1C\x8C"; + #[test] + fn err_on_zero_length() { + let input = b"\x00\x01\x02\x03"; + let custom = b""; + let name = b"Email signature"; + let mut out = [0u8; 0]; - assert_ne!(out.as_ref(), expected.as_ref()); -} + let mut hash = init(custom, Some(name)).unwrap(); + hash.update(input).unwrap(); + assert!(hash.finalize(&mut out).is_err()); + } -#[test] -fn double_finalize_err() { - let input = b"\x00\x01\x02\x03"; - let custom = b""; - let name = b"Email Signature"; - let mut out = [0u8; 64]; - - let mut cshake = init(custom, Some(name)).unwrap(); - cshake.update(input).unwrap(); - cshake.finalize(&mut out).unwrap(); - assert!(cshake.finalize(&mut out).is_err()); -} + #[test] + fn err_on_above_max_length() { + let input = b"\x00\x01\x02\x03"; + let custom = b""; + let name = b"Email signature"; + let mut out = [0u8; 65537]; -#[test] -fn double_finalize_with_reset_ok() { - let input = b"\x00\x01\x02\x03"; - let custom = b""; - let name = b"Email Signature"; - let mut out = [0u8; 64]; - - let mut cshake = init(custom, Some(name)).unwrap(); - cshake.update(input).unwrap(); - cshake.finalize(&mut out).unwrap(); - cshake.reset(); - cshake.update(input).unwrap(); - cshake.finalize(&mut out).unwrap(); -} + let mut hash = init(custom, Some(name)).unwrap(); + hash.update(input).unwrap(); + assert!(hash.finalize(&mut out).is_err()); + } -#[test] -fn double_finalize_with_reset_no_update_ok() { - let input = b"\x00\x01\x02\x03"; - let custom = b""; - let name = b"Email Signature"; - let mut out = [0u8; 64]; - - let mut cshake = init(custom, Some(name)).unwrap(); - cshake.update(input).unwrap(); - cshake.finalize(&mut out).unwrap(); - cshake.reset(); - cshake.finalize(&mut out).unwrap(); -} + #[test] + fn double_finalize_err() { + let input = b"\x00\x01\x02\x03"; + let custom = b""; + let name = b"Email Signature"; + let mut out = [0u8; 64]; + + let mut cshake = init(custom, Some(name)).unwrap(); + cshake.update(input).unwrap(); + cshake.finalize(&mut out).unwrap(); + assert!(cshake.finalize(&mut out).is_err()); + } -#[test] -/// Related bug: https://github.com/brycx/orion/issues/28 -fn update_after_finalize_err() { - let input = b"\x00\x01\x02\x03"; - let custom = b""; - let name = b"Email Signature"; - let mut out = [0u8; 64]; - - let mut cshake = init(custom, Some(name)).unwrap(); - cshake.update(input).unwrap(); - cshake.finalize(&mut out).unwrap(); - assert!(cshake.update(input).is_err()); -} + #[test] + fn double_finalize_with_reset_ok() { + let input = b"\x00\x01\x02\x03"; + let custom = b""; + let name = b"Email Signature"; + let mut out = [0u8; 64]; + + let mut cshake = init(custom, Some(name)).unwrap(); + cshake.update(input).unwrap(); + cshake.finalize(&mut out).unwrap(); + cshake.reset(); + cshake.update(input).unwrap(); + cshake.finalize(&mut out).unwrap(); + } -#[test] -fn update_after_finalize_with_reset_ok() { - let input = b"\x00\x01\x02\x03"; - let custom = b""; - let name = b"Email Signature"; - let mut out = [0u8; 64]; - let mut out_check = [0u8; 64]; - - let mut cshake = init(custom, Some(name)).unwrap(); - cshake.update(input).unwrap(); - cshake.finalize(&mut out).unwrap(); - cshake.reset(); - cshake.update(input).unwrap(); - cshake.finalize(&mut out_check).unwrap(); - - assert_eq!(out.as_ref(), out_check.as_ref()); -} + #[test] + fn double_finalize_with_reset_no_update_ok() { + let input = b"\x00\x01\x02\x03"; + let custom = b""; + let name = b"Email Signature"; + let mut out = [0u8; 64]; + + let mut cshake = init(custom, Some(name)).unwrap(); + cshake.update(input).unwrap(); + cshake.finalize(&mut out).unwrap(); + cshake.reset(); + cshake.finalize(&mut out).unwrap(); + } + + } + + mod test_streaming_interface { + use super::*; + + #[cfg(feature = "safe_api")] + /// Related bug: https://github.com/brycx/orion/issues/46 + /// Testing different usage combinations of init(), update(), + /// finalize() and reset() produce the same output. + /// Cannot be on no_std due to use of Vec. + fn produces_same_out(input: &[u8], custom: &[u8], name: &[u8], outsize: usize) { + let custom_checked = if custom.is_empty() && name.is_empty() { + b"Hello world" + } else { + custom + }; + + let outsize_checked = if outsize == 0 || outsize > 65536 { + 64 + } else { + outsize + }; + + // init(), update(), finalize() + let mut state_1 = init(custom_checked, Some(name)).unwrap(); + state_1.update(input).unwrap(); + let mut res_1 = vec![0u8; outsize_checked]; + state_1.finalize(&mut res_1).unwrap(); + + // init(), reset(), update(), finalize() + let mut state_2 = init(custom_checked, Some(name)).unwrap(); + state_2.reset(); + state_2.update(input).unwrap(); + let mut res_2 = vec![0u8; outsize_checked]; + state_2.finalize(&mut res_2).unwrap(); + + // init(), update(), reset(), update(), finalize() + let mut state_3 = init(custom_checked, Some(name)).unwrap(); + state_3.update(input).unwrap(); + state_3.reset(); + state_3.update(input).unwrap(); + let mut res_3 = vec![0u8; outsize_checked]; + state_3.finalize(&mut res_3).unwrap(); + + // init(), update(), finalize(), reset(), update(), finalize() + let mut state_4 = init(custom_checked, Some(name)).unwrap(); + state_4.update(input).unwrap(); + let mut res_4 = vec![0u8; outsize_checked]; + state_4.finalize(&mut res_4).unwrap(); + state_4.reset(); + state_4.update(input).unwrap(); + state_4.finalize(&mut res_4).unwrap(); + + assert_eq!(res_1, res_2); + assert_eq!(res_2, res_3); + assert_eq!(res_3, res_4); + } -#[test] -fn double_reset_ok() { - let input = b"\x00\x01\x02\x03"; - let custom = b""; - let name = b"Email Signature"; - let mut out = [0u8; 64]; - - let mut cshake = init(custom, Some(name)).unwrap(); - cshake.update(input).unwrap(); - cshake.finalize(&mut out).unwrap(); - cshake.reset(); - cshake.reset(); + #[test] + #[cfg(feature = "safe_api")] + // Test for issues when incrementally processing data. + fn test_streaming_consistency() { + for len in 0..128 * 4 { + let input = vec![0u8; len]; + let custom = b"Email Signature"; + let mut out = vec![0u8; len + 1]; + + let mut state = init(custom, None).unwrap(); + let mut other_data: Vec = Vec::new(); + + other_data.extend_from_slice(&input); + state.update(&input).unwrap(); + + if input.len() > 128 { + other_data.extend_from_slice(b""); + state.update(b"").unwrap(); + } + if input.len() > 128 * 2 { + other_data.extend_from_slice(b"Extra"); + state.update(b"Extra").unwrap(); + } + if input.len() > 128 * 3 { + other_data.extend_from_slice(&[0u8; 256]); + state.update(&[0u8; 256]).unwrap(); + } + + state.finalize(&mut out).unwrap(); + + let mut non_incremental = init(custom, None).unwrap(); + non_incremental.update(&other_data[..]).unwrap(); + let mut out_non_incremental = vec![0u8; len + 1]; + non_incremental.finalize(&mut out_non_incremental).unwrap(); + + assert_eq!(out, out_non_incremental); + } + } + // Proptests. Only exectued when NOT testing no_std. + #[cfg(not(feature = "no_std"))] + mod proptest { + use super::*; + + quickcheck! { + /// Related bug: https://github.com/brycx/orion/issues/46 + /// Test different streaming state usage patterns. + fn prop_same_result_different_usage(input: Vec, custom: Vec, name: Vec, outsize: usize) -> bool { + // Will panic on incorrect results. + produces_same_out(&input[..], &custom[..], &name[..], outsize); + + true + } + } + } + } } -#[test] -/// Related bug: https://github.com/brycx/orion/issues/46 -fn reset_after_update_correct_resets() { - let input = b"\x00\x01\x02\x03"; - let custom = b"Hello world"; - let name = b"Email Signature"; - let mut out = [0u8; 64]; - let mut out2 = [0u8; 64]; - - // With optional name paramter and non-empty custom - let mut cshake = init(custom, Some(name)).unwrap(); - cshake.update(input).unwrap(); - cshake.finalize(&mut out).unwrap(); - - let mut cshake2 = init(custom, Some(name)).unwrap(); - cshake2.update(input).unwrap(); - cshake2.reset(); - cshake2.update(input).unwrap(); - cshake2.finalize(&mut out2).unwrap(); - - assert!(out[..] == out2[..]); - - // With optional name paramter and empty custom - let mut cshake = init(b"", Some(name)).unwrap(); - cshake.update(input).unwrap(); - cshake.finalize(&mut out).unwrap(); - - let mut cshake2 = init(b"", Some(name)).unwrap(); - cshake2.update(input).unwrap(); - cshake2.reset(); - cshake2.update(input).unwrap(); - cshake2.finalize(&mut out2).unwrap(); - - assert!(out[..] == out2[..]); - - // Without optional name parameter - let mut cshake = init(custom, None).unwrap(); - cshake.update(input).unwrap(); - cshake.finalize(&mut out).unwrap(); - - let mut cshake2 = init(custom, None).unwrap(); - cshake2.update(input).unwrap(); - cshake2.reset(); - cshake2.update(input).unwrap(); - cshake2.finalize(&mut out2).unwrap(); - - assert!(out[..] == out2[..]); +// Testing private functions in the module. +#[cfg(test)] +mod private { + use super::*; + + // One function tested per submodule. + + mod test_left_encode { + use super::*; + + #[test] + fn test_left_encode() { + let (test_1, offset_1) = left_encode(32); + let (test_2, offset_2) = left_encode(255); + let (test_3, offset_3) = left_encode(0); + let (test_4, offset_4) = left_encode(64); + let (test_5, offset_5) = left_encode(u64::max_value()); + + assert_eq!(&test_1[(offset_1 - 1)..], &[1, 32]); + assert_eq!(&test_2[(offset_2 - 1)..], &[1, 255]); + assert_eq!(&test_3[(offset_3 - 1)..], &[1, 0]); + assert_eq!(&test_4[(offset_4 - 1)..], &[1, 64]); + assert_eq!( + &test_5[(offset_5 - 1)..], + &[8, 255, 255, 255, 255, 255, 255, 255, 255] + ); + } + } } From eb35bfdeb93db5a460a9a15336014cb9ee74999d Mon Sep 17 00:00:00 2001 From: brycx Date: Wed, 30 Jan 2019 17:29:33 +0100 Subject: [PATCH 21/40] Tests: Re-organize hkdf tests --- src/hazardous/kdf/hkdf.rs | 173 ++++++++++++++++++++++-------------- src/hazardous/xof/cshake.rs | 2 +- 2 files changed, 105 insertions(+), 70 deletions(-) diff --git a/src/hazardous/kdf/hkdf.rs b/src/hazardous/kdf/hkdf.rs index 98058fb9..710561fe 100644 --- a/src/hazardous/kdf/hkdf.rs +++ b/src/hazardous/kdf/hkdf.rs @@ -148,89 +148,124 @@ pub fn verify( } } +// Testing public functions in the module. #[cfg(test)] -mod test { - extern crate hex; - use self::hex::decode; - use crate::hazardous::kdf::hkdf::*; - - #[test] - fn hkdf_maximum_length_512() { - // Max allowed length here is 16320 - let mut okm_out = [0u8; 17000]; - let prk = extract("".as_bytes(), "".as_bytes()).unwrap(); - - assert!(expand(&prk, Some(b""), &mut okm_out).is_err()); - } +mod public { + use super::*; - #[test] - fn hkdf_zero_length() { - let mut okm_out = [0u8; 0]; - let prk = extract("".as_bytes(), "".as_bytes()).unwrap(); + mod test_expand { + use super::*; - assert!(expand(&prk, Some(b""), &mut okm_out).is_err()); - } + #[test] + fn hkdf_maximum_length() { + // Max allowed length here is 16320 + let mut okm_out = [0u8; 17000]; + let prk = extract("".as_bytes(), "".as_bytes()).unwrap(); + + assert!(expand(&prk, Some(b""), &mut okm_out).is_err()); + } + + #[test] + fn hkdf_zero_length() { + let mut okm_out = [0u8; 0]; + let prk = extract("".as_bytes(), "".as_bytes()).unwrap(); - #[test] - fn hkdf_verify_true() { - let ikm = decode("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b").unwrap(); - let salt = decode("000102030405060708090a0b0c").unwrap(); - let info = decode("f0f1f2f3f4f5f6f7f8f9").unwrap(); - let mut okm_out = [0u8; 42]; - - let expected_okm = decode( - "832390086cda71fb47625bb5ceb168e4c8e26a1a16ed34d9fc7fe92c1481579338da362cb8d9f925d7cb", - ) - .unwrap(); - - assert_eq!( - verify(&expected_okm, &salt, &ikm, Some(&info), &mut okm_out).unwrap(), - true - ); + assert!(expand(&prk, Some(b""), &mut okm_out).is_err()); + } + // Proptests. Only exectued when NOT testing no_std. + #[cfg(not(feature = "no_std"))] + mod proptest {} } - #[test] - fn hkdf_verify_wrong_salt() { - let salt = "salt".as_bytes(); - let ikm = decode("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b").unwrap(); - let info = "".as_bytes(); - let mut okm_out = [0u8; 42]; + mod test_derive_key { + use super::*; - let expected_okm = decode( - "8da4e775a563c18f715f802a063c5a31b8a11f5c5ee1879ec3454e5f3c738d2d9d201395faa4b61a96c8", - ) - .unwrap(); + // Proptests. Only exectued when NOT testing no_std. + #[cfg(not(feature = "no_std"))] + mod proptest { + use super::*; - assert!(verify(&expected_okm, &salt, &ikm, Some(info), &mut okm_out).is_err()); - } + quickcheck! { + /// Using derive_key() should always yield the same result + /// as using extract and expand seperately. + fn prop_test_derive_key_same_seperate(salt: Vec, ikm: Vec, info: Vec, outsize: usize) -> bool { + + let outsize_checked = if outsize == 0 || outsize > 16320 { + 64 + } else { + outsize + }; - #[test] - fn hkdf_verify_wrong_ikm() { - let salt = "".as_bytes(); - let ikm = decode("0b").unwrap(); - let info = "".as_bytes(); - let mut okm_out = [0u8; 42]; + let prk = extract(&salt[..], &ikm[..]).unwrap(); + let mut out = vec![0u8; outsize_checked]; + expand(&prk, Some(&info[..]), &mut out).unwrap(); - let expected_okm = decode( - "8da4e775a563c18f715f802a063c5a31b8a11f5c5ee1879ec3454e5f3c738d2d9d201395faa4b61a96c8", - ) - .unwrap(); + let mut out_one_shot = vec![0u8; outsize_checked]; + derive_key(&salt[..], &ikm[..], Some(&info[..]), &mut out_one_shot).unwrap(); - assert!(verify(&expected_okm, &salt, &ikm, Some(info), &mut okm_out).is_err()); + (out == out_one_shot) + } + } + } } - #[test] - fn verify_diff_length() { - let salt = "".as_bytes(); - let ikm = decode("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b").unwrap(); - let info = "".as_bytes(); - let mut okm_out = [0u8; 72]; + mod test_verify { + use super::*; - let expected_okm = decode( - "8da4e775a563c18f715f802a063c5a31b8a11f5c5ee1879ec3454e5f3c738d2d9d201395faa4b61a96c8", - ) - .unwrap(); + #[test] + fn hkdf_verify_true() { + let ikm = b"0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b"; + let salt = b"000102030405060708090a0b0c"; + let info = b"f0f1f2f3f4f5f6f7f8f9"; + let mut okm_out = [0u8; 42]; + let mut okm_out_verify = [0u8; 42]; + + derive_key(salt, ikm, Some(info), &mut okm_out).unwrap(); + + assert!( + verify(&okm_out, salt, ikm, Some(info), &mut okm_out_verify).is_ok()); + } - assert!(verify(&expected_okm, &salt, &ikm, Some(info), &mut okm_out).is_err()); + #[test] + fn hkdf_verify_wrong_salt() { + let ikm = b"0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b"; + let salt = b"000102030405060708090a0b0c"; + let info = b"f0f1f2f3f4f5f6f7f8f9"; + let mut okm_out = [0u8; 42]; + let mut okm_out_verify = [0u8; 42]; + + derive_key(salt, ikm, Some(info), &mut okm_out).unwrap(); + + assert!( + verify(&okm_out, b"", ikm, Some(info), &mut okm_out_verify).is_err()); + } + + #[test] + fn hkdf_verify_wrong_ikm() { + let ikm = b"0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b"; + let salt = b"000102030405060708090a0b0c"; + let info = b"f0f1f2f3f4f5f6f7f8f9"; + let mut okm_out = [0u8; 42]; + let mut okm_out_verify = [0u8; 42]; + + derive_key(salt, ikm, Some(info), &mut okm_out).unwrap(); + + assert!( + verify(&okm_out, salt, b"", Some(info), &mut okm_out_verify).is_err()); + } + + #[test] + fn verify_diff_length() { + let ikm = b"0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b"; + let salt = b"000102030405060708090a0b0c"; + let info = b"f0f1f2f3f4f5f6f7f8f9"; + let mut okm_out = [0u8; 42]; + let mut okm_out_verify = [0u8; 43]; + + derive_key(salt, ikm, Some(info), &mut okm_out).unwrap(); + + assert!( + verify(&okm_out, salt, ikm, Some(info), &mut okm_out_verify).is_err()); + } } } diff --git a/src/hazardous/xof/cshake.rs b/src/hazardous/xof/cshake.rs index fadc6ce3..65f25620 100644 --- a/src/hazardous/xof/cshake.rs +++ b/src/hazardous/xof/cshake.rs @@ -103,7 +103,7 @@ impl CShake { if (name.is_empty()) && (custom.is_empty()) { return Err(UnknownCryptoError); } - + if name.len() > 65536 || custom.len() > 65536 { return Err(UnknownCryptoError); } From 18047ac4b1da0ea7494038cb9084a36c5286a8ff Mon Sep 17 00:00:00 2001 From: brycx Date: Wed, 30 Jan 2019 17:33:01 +0100 Subject: [PATCH 22/40] Use new endianness conversion function in pbkdf2 as this does not need TryFrom --- src/hazardous/kdf/pbkdf2.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/hazardous/kdf/pbkdf2.rs b/src/hazardous/kdf/pbkdf2.rs index f9b369e7..85bb6caf 100644 --- a/src/hazardous/kdf/pbkdf2.rs +++ b/src/hazardous/kdf/pbkdf2.rs @@ -67,7 +67,6 @@ use crate::{ }, util, }; -use byteorder::{BigEndian, ByteOrder}; construct_hmac_key! { /// A type to represent the `Password` that PBKDF2 hashes. @@ -94,7 +93,7 @@ fn function_f( ) -> Result<(), UnknownCryptoError> { let mut u_step: HLenArray = [0u8; 64]; // First 4 bytes used for index BE conversion - BigEndian::write_u32(&mut u_step[..4], index); + u_step[..4].copy_from_slice(&index.to_be_bytes()); hmac.update(salt)?; hmac.update(&u_step[..4])?; From 4f460a30cb1a5addc1c65b0ddf2867b97b059278 Mon Sep 17 00:00:00 2001 From: brycx Date: Wed, 30 Jan 2019 18:27:29 +0100 Subject: [PATCH 23/40] Tests: Re-organize tests in pbkdf2 --- src/hazardous/kdf/hkdf.rs | 12 +-- src/hazardous/kdf/pbkdf2.rs | 151 ++++++++++++++++++++---------------- 2 files changed, 88 insertions(+), 75 deletions(-) diff --git a/src/hazardous/kdf/hkdf.rs b/src/hazardous/kdf/hkdf.rs index 710561fe..ff8dfef8 100644 --- a/src/hazardous/kdf/hkdf.rs +++ b/src/hazardous/kdf/hkdf.rs @@ -222,8 +222,7 @@ mod public { derive_key(salt, ikm, Some(info), &mut okm_out).unwrap(); - assert!( - verify(&okm_out, salt, ikm, Some(info), &mut okm_out_verify).is_ok()); + assert!(verify(&okm_out, salt, ikm, Some(info), &mut okm_out_verify).is_ok()); } #[test] @@ -236,8 +235,7 @@ mod public { derive_key(salt, ikm, Some(info), &mut okm_out).unwrap(); - assert!( - verify(&okm_out, b"", ikm, Some(info), &mut okm_out_verify).is_err()); + assert!(verify(&okm_out, b"", ikm, Some(info), &mut okm_out_verify).is_err()); } #[test] @@ -250,8 +248,7 @@ mod public { derive_key(salt, ikm, Some(info), &mut okm_out).unwrap(); - assert!( - verify(&okm_out, salt, b"", Some(info), &mut okm_out_verify).is_err()); + assert!(verify(&okm_out, salt, b"", Some(info), &mut okm_out_verify).is_err()); } #[test] @@ -264,8 +261,7 @@ mod public { derive_key(salt, ikm, Some(info), &mut okm_out).unwrap(); - assert!( - verify(&okm_out, salt, ikm, Some(info), &mut okm_out_verify).is_err()); + assert!(verify(&okm_out, salt, ikm, Some(info), &mut okm_out_verify).is_err()); } } } diff --git a/src/hazardous/kdf/pbkdf2.rs b/src/hazardous/kdf/pbkdf2.rs index 85bb6caf..4b4b3b04 100644 --- a/src/hazardous/kdf/pbkdf2.rs +++ b/src/hazardous/kdf/pbkdf2.rs @@ -176,92 +176,109 @@ pub fn verify( } } +// Testing public functions in the module. #[cfg(test)] -mod test { +mod public { + use super::*; - extern crate hex; - use self::hex::decode; - use crate::hazardous::kdf::pbkdf2::*; + // One function tested per submodule. - #[test] - fn zero_iterations_err() { - let password = Password::from_slice("password".as_bytes()).unwrap(); - let salt = "salt".as_bytes(); - let iterations: usize = 0; - let mut okm_out = [0u8; 15]; + mod test_verify { + use super::*; - assert!(derive_key(&password, salt, iterations, &mut okm_out).is_err()); - } + #[test] + fn verify_true() { + let password = Password::from_slice("pass\0word".as_bytes()).unwrap(); + let salt = "sa\0lt".as_bytes(); + let iterations: usize = 4096; + let mut okm_out = [0u8; 16]; + let mut okm_out_verify = [0u8; 16]; - #[test] - fn zero_dklen_err() { - let password = Password::from_slice("password".as_bytes()).unwrap(); - let salt = "salt".as_bytes(); - let iterations: usize = 1; - let mut okm_out = [0u8; 0]; + derive_key(&password, &salt, iterations, &mut okm_out).unwrap(); - assert!(derive_key(&password, salt, iterations, &mut okm_out).is_err()); - } + assert!(verify(&okm_out, &password, salt, iterations, &mut okm_out_verify).is_ok()); + } - #[test] - fn verify_true() { - let password = Password::from_slice("pass\0word".as_bytes()).unwrap(); - let salt = "sa\0lt".as_bytes(); - let iterations: usize = 4096; - let mut okm_out = [0u8; 16]; + #[test] + fn verify_false_wrong_salt() { + let password = Password::from_slice("pass\0word".as_bytes()).unwrap(); + let salt = "sa\0lt".as_bytes(); + let iterations: usize = 4096; + let mut okm_out = [0u8; 16]; + let mut okm_out_verify = [0u8; 16]; - let expected_dk = decode("9d9e9c4cd21fe4be24d5b8244c759665").unwrap(); + derive_key(&password, &salt, iterations, &mut okm_out).unwrap(); - assert_eq!( - verify(&expected_dk, &password, salt, iterations, &mut okm_out).unwrap(), - true - ); - } + assert!(verify(&okm_out, &password, b"", iterations, &mut okm_out_verify).is_err()); + } + #[test] + fn verify_false_wrong_password() { + let password = Password::from_slice("pass\0word".as_bytes()).unwrap(); + let salt = "sa\0lt".as_bytes(); + let iterations: usize = 4096; + let mut okm_out = [0u8; 16]; + let mut okm_out_verify = [0u8; 16]; + + derive_key(&password, &salt, iterations, &mut okm_out).unwrap(); + + assert!(verify( + &okm_out, + &Password::from_slice(b"").unwrap(), + salt, + iterations, + &mut okm_out_verify + ) + .is_err()); + } - #[test] - fn verify_false_wrong_salt() { - let password = Password::from_slice("pass\0word".as_bytes()).unwrap(); - let salt = "".as_bytes(); - let iterations: usize = 4096; - let mut okm_out = [0u8; 16]; + #[test] + fn verify_diff_dklen_error() { + let password = Password::from_slice("pass\0word".as_bytes()).unwrap(); + let salt = "sa\0lt".as_bytes(); + let iterations: usize = 4096; + let mut okm_out = [0u8; 16]; + let mut okm_out_verify = [0u8; 32]; - let expected_dk = decode("9d9e9c4cd21fe4be24d5b8244c759665").unwrap(); + derive_key(&password, &salt, iterations, &mut okm_out).unwrap(); - assert!(verify(&expected_dk, &password, salt, iterations, &mut okm_out).is_err()); - } - #[test] - fn verify_false_wrong_password() { - let password = Password::from_slice("".as_bytes()).unwrap(); - let salt = "sa\0lt".as_bytes(); - let iterations: usize = 4096; - let mut okm_out = [0u8; 16]; + assert!(verify(&okm_out, &password, salt, iterations, &mut okm_out_verify).is_err()); + } - let expected_dk = decode("9d9e9c4cd21fe4be24d5b8244c759665").unwrap(); + #[test] + fn verify_diff_iter_error() { + let password = Password::from_slice("pass\0word".as_bytes()).unwrap(); + let salt = "sa\0lt".as_bytes(); + let iterations: usize = 4096; + let mut okm_out = [0u8; 16]; + let mut okm_out_verify = [0u8; 16]; - assert!(verify(&expected_dk, &password, salt, iterations, &mut okm_out).is_err()); - } + derive_key(&password, &salt, iterations, &mut okm_out).unwrap(); - #[test] - fn verify_diff_dklen_error() { - let password = Password::from_slice("pass\0word".as_bytes()).unwrap(); - let salt = "sa\0lt".as_bytes(); - let iterations: usize = 4096; - let mut okm_out = [0u8; 32]; + assert!(verify(&okm_out, &password, salt, 1024, &mut okm_out_verify).is_err()); + } + } - let expected_dk = decode("9d9e9c4cd21fe4be24d5b8244c759665").unwrap(); + mod test_derive_key { + use super::*; - assert!(verify(&expected_dk, &password, salt, iterations, &mut okm_out).is_err()); - } + #[test] + fn zero_iterations_err() { + let password = Password::from_slice("password".as_bytes()).unwrap(); + let salt = "salt".as_bytes(); + let iterations: usize = 0; + let mut okm_out = [0u8; 15]; - #[test] - fn verify_diff_iter_error() { - let password = Password::from_slice("pass\0word".as_bytes()).unwrap(); - let salt = "sa\0lt".as_bytes(); - let iterations: usize = 512; - let mut okm_out = [0u8; 16]; + assert!(derive_key(&password, salt, iterations, &mut okm_out).is_err()); + } - let expected_dk = decode("9d9e9c4cd21fe4be24d5b8244c759665").unwrap(); + #[test] + fn zero_dklen_err() { + let password = Password::from_slice("password".as_bytes()).unwrap(); + let salt = "salt".as_bytes(); + let iterations: usize = 1; + let mut okm_out = [0u8; 0]; - assert!(verify(&expected_dk, &password, salt, iterations, &mut okm_out).is_err()); + assert!(derive_key(&password, salt, iterations, &mut okm_out).is_err()); + } } } From 6cdf528697179be8b0fce85bfc0aa47402b17747 Mon Sep 17 00:00:00 2001 From: brycx Date: Wed, 30 Jan 2019 18:28:45 +0100 Subject: [PATCH 24/40] Needless copy of index into ustep pbkdf2 --- src/hazardous/kdf/pbkdf2.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/hazardous/kdf/pbkdf2.rs b/src/hazardous/kdf/pbkdf2.rs index 4b4b3b04..21553042 100644 --- a/src/hazardous/kdf/pbkdf2.rs +++ b/src/hazardous/kdf/pbkdf2.rs @@ -92,10 +92,8 @@ fn function_f( hmac: &mut hmac::Hmac, ) -> Result<(), UnknownCryptoError> { let mut u_step: HLenArray = [0u8; 64]; - // First 4 bytes used for index BE conversion - u_step[..4].copy_from_slice(&index.to_be_bytes()); hmac.update(salt)?; - hmac.update(&u_step[..4])?; + hmac.update(&index.to_be_bytes())?; u_step.copy_from_slice(&hmac.finalize()?.unprotected_as_bytes()); dk_block.copy_from_slice(&u_step[..block_len]); From b5a5a0a5a965a979019f1175124814f363fceaea Mon Sep 17 00:00:00 2001 From: brycx Date: Wed, 30 Jan 2019 18:32:33 +0100 Subject: [PATCH 25/40] Use new endianness converison functions in cshake without relying on TryFrom --- src/hazardous/xof/cshake.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/hazardous/xof/cshake.rs b/src/hazardous/xof/cshake.rs index 65f25620..8340e6a1 100644 --- a/src/hazardous/xof/cshake.rs +++ b/src/hazardous/xof/cshake.rs @@ -75,7 +75,6 @@ extern crate core; use self::core::mem; use crate::errors::{FinalizationCryptoError, UnknownCryptoError}; -use byteorder::{BigEndian, ByteOrder}; use tiny_keccak::Keccak; #[must_use] @@ -198,7 +197,7 @@ fn left_encode(x: u64) -> ([u8; 9], usize) { 8 } else { let mut tmp: usize = 0; - BigEndian::write_u64(&mut input[1..], x); + input[1..].copy_from_slice(&x.to_be_bytes()); for idx in &input { if *idx != 0 { break; From c3187d9e6b0511d000f6bafbe3b9f814daf9dfbf Mon Sep 17 00:00:00 2001 From: brycx Date: Wed, 30 Jan 2019 18:54:55 +0100 Subject: [PATCH 26/40] typedefs: Added tests for secret_key used in chacha20 and poly1305. Added impl_normal_debug_trait --- src/typedefs.rs | 76 ++++++++++++++++++++++++++++--------------------- 1 file changed, 44 insertions(+), 32 deletions(-) diff --git a/src/typedefs.rs b/src/typedefs.rs index 53afc624..d0ad2674 100644 --- a/src/typedefs.rs +++ b/src/typedefs.rs @@ -56,7 +56,7 @@ macro_rules! impl_partialeq_trait (($name:ident) => ( /// Macro that implements the `Debug` trait on a object called `$name`. /// This `Debug` will omit any fields of object `$name` to avoid them being /// written to logs. -macro_rules! impl_debug_trait (($name:ident) => ( +macro_rules! impl_omitted_debug_trait (($name:ident) => ( impl core::fmt::Debug for $name { fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { write!(f, "{} {{***OMITTED***}}", stringify!($name)) @@ -64,6 +64,17 @@ macro_rules! impl_debug_trait (($name:ident) => ( } )); +/// Macro that implements the `Debug` trait on a object called `$name`. +/// This `Debug` will omit any fields of object `$name` to avoid them being +/// written to logs. +macro_rules! impl_normal_debug_trait (($name:ident) => ( + impl core::fmt::Debug for $name { + fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { + write!(f, "{} {:?}", stringify!($name), &self.value[..]) + } + } +)); + /// Macro that implements the `Drop` trait on a object called `$name` which as a /// field `value`. This `Drop` will zero out the field `value` when the objects /// destructor is called. WARNING: This requires value to be an array as @@ -170,7 +181,7 @@ macro_rules! construct_secret_key { /// that the type implements. pub struct $name { value: [u8; $size] } - impl_debug_trait!($name); + impl_omitted_debug_trait!($name); impl_drop_stack_trait!($name); impl_partialeq_trait!($name); @@ -183,16 +194,35 @@ macro_rules! construct_secret_key { #[test] fn test_key_size() { - // We don't test above $size here in case it's passed as a `max_value()` assert!($name::from_slice(&[0u8; $size]).is_ok()); - assert!($name::from_slice(&[0u8; $size - $size]).is_err()); + assert!($name::from_slice(&[0u8; 0]).is_err()); assert!($name::from_slice(&[0u8; $size - 1]).is_err()); + assert!($name::from_slice(&[0u8; $size + 1]).is_err()); } #[test] fn test_unprotected_as_bytes_secret_key() { let test = $name::from_slice(&[0u8; $size]).unwrap(); + assert!(test.unprotected_as_bytes() == &[0u8; $size]); assert!(test.unprotected_as_bytes().len() == $size); } + + #[test] + fn test_get_length_secret_key() { + let test = $name::from_slice(&[0u8; $size]).unwrap(); + assert!(test.unprotected_as_bytes().len() == test.get_length()); + assert!($size == test.get_length()); + } + + #[test] + #[cfg(feature = "safe_api")] + fn test_generate() { + let test_zero = $name::from_slice(&[0u8; $size]).unwrap(); + // A random one should never be all 0's. + let test_rand = $name::generate().unwrap(); + assert!(test_zero != test_rand); + // A random generated one should always be $size in length. + assert!(test_rand.get_length() == $size); + } ); } @@ -204,18 +234,14 @@ macro_rules! construct_nonce_no_generator { $(#[$meta])* pub struct $name { value: [u8; $size] } + impl_normal_debug_trait!($name); + impl $name { func_from_slice!($name, $size); func_as_bytes!(); func_get_length!(); } - impl core::fmt::Debug for $name { - fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { - write!(f, "{} {:?}", stringify!($name), &self.value) - } - } - #[test] fn test_nonce_size() { // We don't test above $size here in case it's passed as a `max_value()` @@ -239,6 +265,8 @@ macro_rules! construct_nonce_with_generator { $(#[$meta])* pub struct $name { value: [u8; $size] } + impl_normal_debug_trait!($name); + impl $name { func_from_slice!($name, $size); func_as_bytes!(); @@ -246,12 +274,6 @@ macro_rules! construct_nonce_with_generator { func_get_length!(); } - impl core::fmt::Debug for $name { - fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { - write!(f, "{} {:?}", stringify!($name), &self.value) - } - } - #[test] fn test_nonce_size() { // We don't test above $size here in case it's passed as a `max_value()` @@ -280,6 +302,7 @@ macro_rules! construct_tag { /// that the type implements. pub struct $name { value: [u8; $size] } + impl_normal_debug_trait!($name); impl_partialeq_trait!($name); impl $name { @@ -288,12 +311,6 @@ macro_rules! construct_tag { func_get_length!(); } - impl core::fmt::Debug for $name { - fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { - write!(f, "{} {:?}", stringify!($name), &self.value[..]) - } - } - #[test] fn test_tag_size() { // We don't test above $size here in case it's passed as a `max_value()` @@ -322,7 +339,7 @@ macro_rules! construct_hmac_key { /// that the type implements. pub struct $name { value: [u8; $size] } - impl_debug_trait!($name); + impl_omitted_debug_trait!($name); impl_drop_stack_trait!($name); impl_partialeq_trait!($name); @@ -383,7 +400,7 @@ macro_rules! construct_blake2b_key { original_size: usize, } - impl_debug_trait!($name); + impl_omitted_debug_trait!($name); impl_drop_stack_trait!($name); impl_partialeq_trait!($name); @@ -527,7 +544,7 @@ macro_rules! construct_secret_key_variable_size { /// that the type implements. pub struct $name { value: Vec } - impl_debug_trait!($name); + impl_omitted_debug_trait!($name); impl_drop_heap_trait!($name); impl_partialeq_trait!($name); impl_default_trait!($name, $size); @@ -595,6 +612,7 @@ macro_rules! construct_salt_variable_size { /// pub struct $name { value: Vec } + impl_normal_debug_trait!($name); impl_default_trait!($name, $size); impl $name { @@ -627,12 +645,6 @@ macro_rules! construct_salt_variable_size { } } - impl core::fmt::Debug for $name { - fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { - write!(f, "{} {:?}", stringify!($name), &self.value[..]) - } - } - #[test] fn test_form_slice_salt() { let _ = $name::from_slice(&[0u8; 256]).unwrap(); @@ -668,7 +680,7 @@ macro_rules! construct_password_variable_size { /// that the type implements. pub struct $name { value: Vec } - impl_debug_trait!($name); + impl_omitted_debug_trait!($name); impl_drop_heap_trait!($name); impl_partialeq_trait!($name); From 161d572863ffe93bf1ace1d550de70e1ed23a899 Mon Sep 17 00:00:00 2001 From: brycx Date: Wed, 30 Jan 2019 19:02:34 +0100 Subject: [PATCH 27/40] typedefs: Make generator for variable size length into macro --- src/typedefs.rs | 49 +++++++++++++++++++++---------------------------- 1 file changed, 21 insertions(+), 28 deletions(-) diff --git a/src/typedefs.rs b/src/typedefs.rs index d0ad2674..5b589fa0 100644 --- a/src/typedefs.rs +++ b/src/typedefs.rs @@ -168,6 +168,25 @@ macro_rules! func_generate (($name:ident, $size:expr) => ( } )); +/// Macro to implement a `generate()` function for objects that benefit from +/// having a CSPRNG available to generate data of a variable length. +macro_rules! func_generate_variable_size (($name:ident) => ( + #[must_use] + #[cfg(feature = "safe_api")] + /// Randomly generate using a CSPRNG. Not available in `no_std` context. + pub fn generate(length: usize) -> Result<$name, UnknownCryptoError> { + use crate::util; + if length < 1 || length >= (u32::max_value() as usize) { + return Err(UnknownCryptoError); + } + + let mut value = vec![0u8; length]; + util::secure_rand_bytes(&mut value)?; + + Ok($name { value: value }) + } +)); + /// Macro to construct a type containing sensitive data, using a fixed-size /// array. macro_rules! construct_secret_key { @@ -563,20 +582,7 @@ macro_rules! construct_secret_key_variable_size { func_unprotected_as_bytes!(); func_get_length!(); - #[must_use] - #[cfg(feature = "safe_api")] - /// Randomly generate using a CSPRNG. Not available in `no_std` context. - pub fn generate(length: usize) -> Result<$name, UnknownCryptoError> { - use crate::util; - if length < 1 || length >= (u32::max_value() as usize) { - return Err(UnknownCryptoError); - } - - let mut value = vec![0u8; length]; - util::secure_rand_bytes(&mut value)?; - - Ok($name { value: value }) - } + func_generate_variable_size!($name); } #[test] @@ -629,20 +635,7 @@ macro_rules! construct_salt_variable_size { func_as_bytes!(); func_get_length!(); - #[must_use] - #[cfg(feature = "safe_api")] - /// Randomly generate using a CSPRNG. Not available in `no_std` context. - pub fn generate(length: usize) -> Result<$name, UnknownCryptoError> { - use crate::util; - if length < 1 || length >= (u32::max_value() as usize) { - return Err(UnknownCryptoError); - } - - let mut value = vec![0u8; length]; - util::secure_rand_bytes(&mut value)?; - - Ok($name { value: value }) - } + func_generate_variable_size!($name); } #[test] From b61062739064afdcc5c1ca325f827db5d07c2257 Mon Sep 17 00:00:00 2001 From: brycx Date: Wed, 30 Jan 2019 19:18:14 +0100 Subject: [PATCH 28/40] typedefs: Non-constant time PartialEq for salts and nonce --- src/typedefs.rs | 39 ++++++++++++++++++++++++++++++++------- 1 file changed, 32 insertions(+), 7 deletions(-) diff --git a/src/typedefs.rs b/src/typedefs.rs index 5b589fa0..4e7ad768 100644 --- a/src/typedefs.rs +++ b/src/typedefs.rs @@ -42,7 +42,7 @@ macro_rules! impl_default_trait (($name:ident, $size:expr) => ( /// Macro that implements the `PartialEq` trait on a object called `$name` that /// also implements `unprotected_as_bytes()`. This `PartialEq` will perform in /// constant time. -macro_rules! impl_partialeq_trait (($name:ident) => ( +macro_rules! impl_ct_partialeq_trait (($name:ident) => ( impl PartialEq for $name { fn eq(&self, other: &$name) -> bool { use subtle::ConstantTimeEq; @@ -53,6 +53,17 @@ macro_rules! impl_partialeq_trait (($name:ident) => ( } )); +/// Macro that implements the `PartialEq` trait on a object called `$name` that +/// also implements `as_bytes()`. This `PartialEq` will NOT perform in +/// constant time. +macro_rules! impl_normal_partialeq_trait (($name:ident) => ( + impl PartialEq for $name { + fn eq(&self, other: &$name) -> bool { + (&self.as_bytes() == &other.as_bytes()) + } + } +)); + /// Macro that implements the `Debug` trait on a object called `$name`. /// This `Debug` will omit any fields of object `$name` to avoid them being /// written to logs. @@ -202,7 +213,7 @@ macro_rules! construct_secret_key { impl_omitted_debug_trait!($name); impl_drop_stack_trait!($name); - impl_partialeq_trait!($name); + impl_ct_partialeq_trait!($name); impl $name { func_from_slice!($name, $size); @@ -254,6 +265,7 @@ macro_rules! construct_nonce_no_generator { pub struct $name { value: [u8; $size] } impl_normal_debug_trait!($name); + impl_normal_partialeq_trait!($name); impl $name { func_from_slice!($name, $size); @@ -285,6 +297,7 @@ macro_rules! construct_nonce_with_generator { pub struct $name { value: [u8; $size] } impl_normal_debug_trait!($name); + impl_normal_partialeq_trait!($name); impl $name { func_from_slice!($name, $size); @@ -305,6 +318,17 @@ macro_rules! construct_nonce_with_generator { let test = $name::from_slice(&[0u8; $size]).unwrap(); assert!(test.as_bytes().len() == $size); } + + #[test] + #[cfg(feature = "safe_api")] + fn test_generate() { + let test_zero = $name::from_slice(&[0u8; $size]).unwrap(); + // A random one should never be all 0's. + let test_rand = $name::generate().unwrap(); + assert!(test_zero != test_rand); + // A random generated one should always be $size in length. + assert!(test_rand.get_length() == $size); + } ); } @@ -322,7 +346,7 @@ macro_rules! construct_tag { pub struct $name { value: [u8; $size] } impl_normal_debug_trait!($name); - impl_partialeq_trait!($name); + impl_ct_partialeq_trait!($name); impl $name { func_from_slice!($name, $size); @@ -360,7 +384,7 @@ macro_rules! construct_hmac_key { impl_omitted_debug_trait!($name); impl_drop_stack_trait!($name); - impl_partialeq_trait!($name); + impl_ct_partialeq_trait!($name); impl $name { #[must_use] @@ -421,7 +445,7 @@ macro_rules! construct_blake2b_key { impl_omitted_debug_trait!($name); impl_drop_stack_trait!($name); - impl_partialeq_trait!($name); + impl_ct_partialeq_trait!($name); impl $name { #[must_use] @@ -565,7 +589,7 @@ macro_rules! construct_secret_key_variable_size { impl_omitted_debug_trait!($name); impl_drop_heap_trait!($name); - impl_partialeq_trait!($name); + impl_ct_partialeq_trait!($name); impl_default_trait!($name, $size); impl $name { @@ -620,6 +644,7 @@ macro_rules! construct_salt_variable_size { impl_normal_debug_trait!($name); impl_default_trait!($name, $size); + impl_normal_partialeq_trait!($name); impl $name { #[must_use] @@ -675,7 +700,7 @@ macro_rules! construct_password_variable_size { impl_omitted_debug_trait!($name); impl_drop_heap_trait!($name); - impl_partialeq_trait!($name); + impl_ct_partialeq_trait!($name); impl $name { #[must_use] From 0346ef730cd25be91798f87639070dcddad05877 Mon Sep 17 00:00:00 2001 From: brycx Date: Wed, 30 Jan 2019 20:06:39 +0100 Subject: [PATCH 29/40] Tests: General improvements to all tests in typedefs --- Cargo.toml | 1 - src/typedefs.rs | 256 +++++++++++++++++++++++++++++++++++------------- 2 files changed, 190 insertions(+), 67 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 565e5f14..5035ae3c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -36,7 +36,6 @@ hex = "0.3.2" serde_json = "1.0.37" quickcheck = "0.8.0" - [profile.dev] opt-level = 1 diff --git a/src/typedefs.rs b/src/typedefs.rs index 4e7ad768..792ad535 100644 --- a/src/typedefs.rs +++ b/src/typedefs.rs @@ -133,6 +133,21 @@ macro_rules! func_from_slice (($name:ident, $size:expr) => ( } )); +/// Macro to implement a `from_slice()` function. Returns `UnknownCryptoError` +/// if the slice is not of length `$size`. +macro_rules! func_from_slice_variable_size (($name:ident) => ( + #[must_use] + #[cfg(feature = "safe_api")] + /// Make an object from a given byte slice. + pub fn from_slice(slice: &[u8]) -> Result<$name, UnknownCryptoError> { + if slice.is_empty() { + return Err(UnknownCryptoError); + } + + Ok($name { value: Vec::from(slice) }) + } +)); + /// Macro to implement a `unprotected_as_bytes()` function for objects that /// implement extra protections. Typically used on objects that implement /// `Drop`, `Debug` and/or `PartialEq`. @@ -245,7 +260,7 @@ macro_rules! construct_secret_key { #[test] #[cfg(feature = "safe_api")] - fn test_generate() { + fn test_generate_secret_key() { let test_zero = $name::from_slice(&[0u8; $size]).unwrap(); // A random one should never be all 0's. let test_rand = $name::generate().unwrap(); @@ -275,15 +290,22 @@ macro_rules! construct_nonce_no_generator { #[test] fn test_nonce_size() { - // We don't test above $size here in case it's passed as a `max_value()` assert!($name::from_slice(&[0u8; $size]).is_ok()); assert!($name::from_slice(&[0u8; $size - $size]).is_err()); - assert!($name::from_slice(&[0u8; $size - 1]).is_err()); + assert!($name::from_slice(&[0u8; $size + 1]).is_err()); } #[test] fn test_as_bytes_nonce_no_gen() { let test = $name::from_slice(&[0u8; $size]).unwrap(); assert!(test.as_bytes().len() == $size); + assert!(test.as_bytes() == &[0u8; $size]); + } + + #[test] + fn test_get_length_nonce_no_gen() { + let test = $name::from_slice(&[0u8; $size]).unwrap(); + assert!(test.as_bytes().len() == test.get_length()); + assert!($size == test.get_length()); } ); } @@ -308,20 +330,27 @@ macro_rules! construct_nonce_with_generator { #[test] fn test_nonce_size() { - // We don't test above $size here in case it's passed as a `max_value()` assert!($name::from_slice(&[0u8; $size]).is_ok()); assert!($name::from_slice(&[0u8; $size - $size]).is_err()); - assert!($name::from_slice(&[0u8; $size - 1]).is_err()); + assert!($name::from_slice(&[0u8; $size + 1]).is_err()); } #[test] fn test_as_bytes_nonce_with_gen() { let test = $name::from_slice(&[0u8; $size]).unwrap(); assert!(test.as_bytes().len() == $size); + assert!(test.as_bytes() == &[0u8; $size]); + } + + #[test] + fn test_get_length_nonce_with_gen() { + let test = $name::from_slice(&[0u8; $size]).unwrap(); + assert!(test.as_bytes().len() == test.get_length()); + assert!($size == test.get_length()); } #[test] #[cfg(feature = "safe_api")] - fn test_generate() { + fn test_generate_nonce() { let test_zero = $name::from_slice(&[0u8; $size]).unwrap(); // A random one should never be all 0's. let test_rand = $name::generate().unwrap(); @@ -345,7 +374,7 @@ macro_rules! construct_tag { /// that the type implements. pub struct $name { value: [u8; $size] } - impl_normal_debug_trait!($name); + impl_omitted_debug_trait!($name); impl_ct_partialeq_trait!($name); impl $name { @@ -356,15 +385,22 @@ macro_rules! construct_tag { #[test] fn test_tag_size() { - // We don't test above $size here in case it's passed as a `max_value()` assert!($name::from_slice(&[0u8; $size]).is_ok()); assert!($name::from_slice(&[0u8; $size - $size]).is_err()); - assert!($name::from_slice(&[0u8; $size - 1]).is_err()); + assert!($name::from_slice(&[0u8; $size + 1]).is_err()); } #[test] fn test_unprotected_as_bytes_tag() { let test = $name::from_slice(&[0u8; $size]).unwrap(); assert!(test.unprotected_as_bytes().len() == $size); + assert!(test.unprotected_as_bytes() == [0u8; $size].as_ref()); + } + + #[test] + fn test_get_length_tag() { + let test = $name::from_slice(&[0u8; $size]).unwrap(); + assert!(test.unprotected_as_bytes().len() == test.get_length()); + assert!($size == test.get_length()); } ); } @@ -413,15 +449,34 @@ macro_rules! construct_hmac_key { #[test] fn test_key_size() { - // We don't test above $size here in case it's passed as a `max_value()` - let _ = $name::from_slice(&[0u8; $size]).unwrap(); - let _ = $name::from_slice(&[0u8; $size - $size]).unwrap(); - let _ = $name::from_slice(&[0u8; $size - 1]).unwrap(); + assert!($name::from_slice(&[0u8; $size]).is_ok()); + assert!($name::from_slice(&[0u8; $size - $size]).is_ok()); + assert!($name::from_slice(&[0u8; $size + 1]).is_ok()); } + #[test] fn test_unprotected_as_bytes_hmac_key() { let test = $name::from_slice(&[0u8; $size]).unwrap(); assert!(test.unprotected_as_bytes().len() == $size); + assert!(test.unprotected_as_bytes() == [0u8; $size].as_ref()); + } + + #[test] + fn test_get_length_hmac_key() { + let test = $name::from_slice(&[0u8; $size]).unwrap(); + assert!(test.unprotected_as_bytes().len() == test.get_length()); + assert!($size == test.get_length()); + } + + #[test] + #[cfg(feature = "safe_api")] + fn test_generate_hmac() { + let test_zero = $name::from_slice(&[0u8; $size]).unwrap(); + // A random one should never be all 0's. + let test_rand = $name::generate().unwrap(); + assert!(test_zero != test_rand); + // A random generated one should always be $size in length. + assert!(test_rand.get_length() == $size); } ); } @@ -465,7 +520,6 @@ macro_rules! construct_blake2b_key { }) } - #[must_use] /// Get the original size of the key, before padding. pub fn get_original_length(&self) -> usize { self.original_size @@ -492,15 +546,57 @@ macro_rules! construct_blake2b_key { #[test] fn test_blake2b_key_size() { - // We don't test above $size here in case it's passed as a `max_value()` - let _ = $name::from_slice(&[0u8; 64]).unwrap(); - let _ = $name::from_slice(&[0u8; 64 - 63]).unwrap(); - let _ = $name::from_slice(&[0u8; 64 - 1]).unwrap(); + assert!($name::from_slice(&[0u8; 64]).is_ok()); + assert!($name::from_slice(&[0u8; 63]).is_ok()); + assert!($name::from_slice(&[0u8; 65]).is_err()); + assert!($name::from_slice(&[0u8; 0]).is_err()); } #[test] fn test_unprotected_as_bytes_blake2b_key() { + // Verify against $size because that's how the key is padded. let test = $name::from_slice(&[0u8; 64]).unwrap(); - assert!(test.unprotected_as_bytes().len() == $size); + assert!(test.unprotected_as_bytes() == [0u8; $size].as_ref()); + + let test = $name::from_slice(&[0u8; 32]).unwrap(); + assert!(test.unprotected_as_bytes() == [0u8; $size].as_ref()); + + let test = $name::from_slice(&[0u8; 1]).unwrap(); + assert!(test.unprotected_as_bytes() == [0u8; $size].as_ref()); + } + + #[test] + fn test_get_length_blake2b_key() { + let test = $name::from_slice(&[0u8; 64]).unwrap(); + assert!(test.unprotected_as_bytes().len() == test.get_length()); + assert!($size == test.get_length()); + } + + #[test] + fn test_get_original_length_blake2b_key() { + let test = $name::from_slice(&[0u8; 64]).unwrap(); + assert!(test.unprotected_as_bytes().len() != test.get_original_length()); + assert!(64 == test.get_original_length()); + } + + #[test] + fn test_blake2b_key_zero_padding() { + let test = $name::from_slice(&[1u8; 64]).unwrap(); + assert!([0u8; 64].as_ref() == &test.unprotected_as_bytes()[64..]); + } + + #[test] + #[cfg(feature = "safe_api")] + fn test_generate_blake2b() { + let test_zero = $name::from_slice(&[0u8; 64]).unwrap(); + // A random one should never be all 0's. + let test_rand = $name::generate().unwrap(); + assert!(test_zero != test_rand); + assert_ne!( + test_zero.unprotected_as_bytes(), + &test_rand.unprotected_as_bytes()[..test_zero.get_original_length()] + ); + // A random generated one should always be 64 in length for Blake2b. + assert!(test_rand.get_original_length() == 64); } ); } @@ -549,6 +645,8 @@ macro_rules! construct_digest { digest_size: slice.len(), }) } + + func_get_length!(); } impl core::fmt::Debug for $name { @@ -558,16 +656,29 @@ macro_rules! construct_digest { } #[test] - fn test_blake2b_mac_size() { - // We don't test above $size here in case it's passed as a `max_value()` - let _ = $name::from_slice(&[0u8; 64]).unwrap(); - let _ = $name::from_slice(&[0u8; 64 - 63]).unwrap(); - let _ = $name::from_slice(&[0u8; 64 - 1]).unwrap(); + fn test_blake2b_digest_size() { + assert!($name::from_slice(&[0u8; 64]).is_ok()); + assert!($name::from_slice(&[0u8; 63]).is_ok()); + assert!($name::from_slice(&[0u8; 65]).is_err()); + assert!($name::from_slice(&[0u8; 0]).is_err()); } #[test] - fn test_unprotected_as_bytes_blake2b_mac() { + fn test_as_bytes_blake2b_digest() { let test = $name::from_slice(&[0u8; 64]).unwrap(); - assert!(test.as_bytes().len() == 64); + assert!(test.as_bytes() == [0u8; 64].as_ref()); + + let test = $name::from_slice(&[0u8; 32]).unwrap(); + assert!(test.as_bytes() == [0u8; 32].as_ref()); + + let test = $name::from_slice(&[0u8; 1]).unwrap(); + assert!(test.as_bytes() == [0u8; 1].as_ref()); + } + + #[test] + fn test_get_length_blake2b_digest() { + let test = $name::from_slice(&[0u8; 64]).unwrap(); + assert!(test.as_bytes().len() == test.get_length()); + assert!($size == test.get_length()); } ); } @@ -593,17 +704,7 @@ macro_rules! construct_secret_key_variable_size { impl_default_trait!($name, $size); impl $name { - #[must_use] - #[cfg(feature = "safe_api")] - /// Make an object from a given byte slice. - pub fn from_slice(slice: &[u8]) -> Result<$name, UnknownCryptoError> { - if slice.is_empty() { - return Err(UnknownCryptoError); - } - - Ok($name { value: Vec::from(slice) }) - } - + func_from_slice_variable_size!($name); func_unprotected_as_bytes!(); func_get_length!(); func_generate_variable_size!($name); @@ -611,21 +712,33 @@ macro_rules! construct_secret_key_variable_size { #[test] fn test_from_slice_key() { - let _ = $name::from_slice(&[0u8; 256]).unwrap(); - let _ = $name::from_slice(&[0u8; 512]).unwrap(); + assert!($name::from_slice(&[0u8; 512]).is_ok()); + assert!($name::from_slice(&[0u8; 256]).is_ok()); + assert!($name::from_slice(&[0u8; 1]).is_ok()); assert!($name::from_slice(&[0u8; 0]).is_err()); } + #[test] fn test_unprotected_as_bytes_derived_key() { let test = $name::from_slice(&[0u8; 256]).unwrap(); assert!(test.unprotected_as_bytes().len() == 256); + assert!(test.unprotected_as_bytes() == [0u8; 256].as_ref()); } + #[test] - fn test_generate_key() { + #[cfg(feature = "safe_api")] + fn test_generate_secret_key() { assert!($name::generate(0).is_err()); assert!($name::generate(usize::max_value()).is_err()); assert!($name::generate(1).is_ok()); assert!($name::generate(64).is_ok()); + + let test_zero = $name::from_slice(&[0u8; 128]).unwrap(); + // A random one should never be all 0's. + let test_rand = $name::generate(128).unwrap(); + assert!(test_zero != test_rand); + // A random generated one should always be $size in length. + assert!(test_rand.get_length() == 128); } ); } @@ -647,17 +760,7 @@ macro_rules! construct_salt_variable_size { impl_normal_partialeq_trait!($name); impl $name { - #[must_use] - #[cfg(feature = "safe_api")] - /// Make an object from a given byte slice. - pub fn from_slice(slice: &[u8]) -> Result<$name, UnknownCryptoError> { - if slice.is_empty() { - return Err(UnknownCryptoError); - } - - Ok($name { value: Vec::from(slice) }) - } - + func_from_slice_variable_size!($name); func_as_bytes!(); func_get_length!(); func_generate_variable_size!($name); @@ -665,21 +768,33 @@ macro_rules! construct_salt_variable_size { #[test] fn test_form_slice_salt() { - let _ = $name::from_slice(&[0u8; 256]).unwrap(); - let _ = $name::from_slice(&[0u8; 512]).unwrap(); + assert!($name::from_slice(&[0u8; 512]).is_ok()); + assert!($name::from_slice(&[0u8; 256]).is_ok()); + assert!($name::from_slice(&[0u8; 1]).is_ok()); assert!($name::from_slice(&[0u8; 0]).is_err()); } + #[test] fn test_as_bytes_salt() { let test = $name::from_slice(&[0u8; 256]).unwrap(); assert!(test.as_bytes().len() == 256); + assert!(test.as_bytes() == [0u8; 256].as_ref()); } + #[test] + #[cfg(feature = "safe_api")] fn test_generate_salt() { assert!($name::generate(0).is_err()); assert!($name::generate(usize::max_value()).is_err()); assert!($name::generate(1).is_ok()); assert!($name::generate(64).is_ok()); + + let test_zero = $name::from_slice(&[0u8; 128]).unwrap(); + // A random one should never be all 0's. + let test_rand = $name::generate(128).unwrap(); + assert!(test_zero != test_rand); + // A random generated one should always be $size in length. + assert!(test_rand.get_length() == 128); } ); } @@ -703,31 +818,40 @@ macro_rules! construct_password_variable_size { impl_ct_partialeq_trait!($name); impl $name { - #[must_use] - #[cfg(feature = "safe_api")] - /// Make an object from a given byte slice. - pub fn from_slice(slice: &[u8]) -> Result<$name, UnknownCryptoError> { - if slice.is_empty() { - return Err(UnknownCryptoError); - } - - Ok($name { value: Vec::from(slice) }) - } - + func_from_slice_variable_size!($name); func_unprotected_as_bytes!(); func_get_length!(); + func_generate_variable_size!($name); } #[test] fn test_form_slice_password() { - let _ = $name::from_slice(&[0u8; 256]).unwrap(); - let _ = $name::from_slice(&[0u8; 512]).unwrap(); + assert!($name::from_slice(&[0u8; 512]).is_ok()); + assert!($name::from_slice(&[0u8; 256]).is_ok()); + assert!($name::from_slice(&[0u8; 1]).is_ok()); assert!($name::from_slice(&[0u8; 0]).is_err()); } #[test] fn test_unprotected_as_bytes_password() { let test = $name::from_slice(&[0u8; 256]).unwrap(); assert!(test.unprotected_as_bytes().len() == 256); + assert!(test.unprotected_as_bytes() == [0u8; 256].as_ref()); + } + + #[test] + #[cfg(feature = "safe_api")] + fn test_generate_password() { + assert!($name::generate(0).is_err()); + assert!($name::generate(usize::max_value()).is_err()); + assert!($name::generate(1).is_ok()); + assert!($name::generate(64).is_ok()); + + let test_zero = $name::from_slice(&[0u8; 128]).unwrap(); + // A random one should never be all 0's. + let test_rand = $name::generate(128).unwrap(); + assert!(test_zero != test_rand); + // A random generated one should always be $size in length. + assert!(test_rand.get_length() == 128); } ); } From 566d60cb812d7e9f218b10c6104e5bdbc6bcbfa3 Mon Sep 17 00:00:00 2001 From: brycx Date: Wed, 30 Jan 2019 20:25:41 +0100 Subject: [PATCH 30/40] Tests: Add tests to all types in typedef that implement the omitted Debug that the value of the type actually is not in the Debug string formatter --- src/typedefs.rs | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/src/typedefs.rs b/src/typedefs.rs index 792ad535..e221bd12 100644 --- a/src/typedefs.rs +++ b/src/typedefs.rs @@ -268,6 +268,13 @@ macro_rules! construct_secret_key { // A random generated one should always be $size in length. assert!(test_rand.get_length() == $size); } + + #[test] + fn test_omitted_debug_secret_key() { + let secret = format!("{:?}", [0u8; $size].as_ref()); + let test_debug_contents = format!("{:?}", $name::from_slice(&[0u8; $size]).unwrap()); + assert_eq!(test_debug_contents.contains(&secret), false); + } ); } @@ -402,6 +409,13 @@ macro_rules! construct_tag { assert!(test.unprotected_as_bytes().len() == test.get_length()); assert!($size == test.get_length()); } + + #[test] + fn test_omitted_debug_tag() { + let secret = format!("{:?}", [0u8; $size].as_ref()); + let test_debug_contents = format!("{:?}", $name::from_slice(&[0u8; $size]).unwrap()); + assert_eq!(test_debug_contents.contains(&secret), false); + } ); } @@ -478,6 +492,13 @@ macro_rules! construct_hmac_key { // A random generated one should always be $size in length. assert!(test_rand.get_length() == $size); } + + #[test] + fn test_omitted_debug_hmac_key() { + let secret = format!("{:?}", [0u8; $size].as_ref()); + let test_debug_contents = format!("{:?}", $name::from_slice(&[0u8; $size]).unwrap()); + assert_eq!(test_debug_contents.contains(&secret), false); + } ); } @@ -598,6 +619,13 @@ macro_rules! construct_blake2b_key { // A random generated one should always be 64 in length for Blake2b. assert!(test_rand.get_original_length() == 64); } + + #[test] + fn test_omitted_debug_blake2b_key() { + let secret = format!("{:?}", [0u8; 64].as_ref()); + let test_debug_contents = format!("{:?}", $name::from_slice(&[0u8; 64]).unwrap()); + assert_eq!(test_debug_contents.contains(&secret), false); + } ); } @@ -740,6 +768,13 @@ macro_rules! construct_secret_key_variable_size { // A random generated one should always be $size in length. assert!(test_rand.get_length() == 128); } + + #[test] + fn test_omitted_debug_secret_key() { + let secret = format!("{:?}", [0u8; $size].as_ref()); + let test_debug_contents = format!("{:?}", $name::from_slice(&[0u8; $size]).unwrap()); + assert_eq!(test_debug_contents.contains(&secret), false); + } ); } @@ -853,5 +888,12 @@ macro_rules! construct_password_variable_size { // A random generated one should always be $size in length. assert!(test_rand.get_length() == 128); } + + #[test] + fn test_omitted_debug_password() { + let secret = format!("{:?}", [0u8; 64].as_ref()); + let test_debug_contents = format!("{:?}", $name::from_slice(&[0u8; 64]).unwrap()); + assert_eq!(test_debug_contents.contains(&secret), false); + } ); } From a38f47dea1cda3c9973418a9042a4985f0af039c Mon Sep 17 00:00:00 2001 From: brycx Date: Wed, 30 Jan 2019 20:51:59 +0100 Subject: [PATCH 31/40] Tests: Re-organize test and add proptest for high-level aead and auth --- src/aead.rs | 189 +++++++++++++++++++++++++++++++----------------- src/auth.rs | 111 +++++++++++++++++++++------- src/typedefs.rs | 2 +- 3 files changed, 207 insertions(+), 95 deletions(-) diff --git a/src/aead.rs b/src/aead.rs index e3430a77..8e2aee12 100644 --- a/src/aead.rs +++ b/src/aead.rs @@ -130,85 +130,140 @@ pub fn open( Ok(dst_out) } -#[test] -fn auth_enc_encryption_decryption() { - let key = SecretKey::default(); - let plaintext = "Secret message".as_bytes().to_vec(); - - let dst_ciphertext = seal(&key, &plaintext).unwrap(); - assert!(dst_ciphertext.len() == plaintext.len() + (24 + 16)); - let dst_plaintext = open(&key, &dst_ciphertext).unwrap(); - assert!(dst_plaintext.len() == plaintext.len()); - assert_eq!(plaintext, dst_plaintext); -} +// Testing public functions in the module. +#[cfg(test)] +mod public { + use super::*; -#[test] -fn auth_enc_plaintext_empty_err() { - let key = SecretKey::default(); - let plaintext = "".as_bytes().to_vec(); + mod test_seal_open { + use super::*; + #[test] + fn test_auth_enc_encryption_decryption() { + let key = SecretKey::default(); + let plaintext = "Secret message".as_bytes().to_vec(); - assert!(seal(&key, &plaintext).is_err()); -} + let dst_ciphertext = seal(&key, &plaintext).unwrap(); + assert!(dst_ciphertext.len() == plaintext.len() + (24 + 16)); + let dst_plaintext = open(&key, &dst_ciphertext).unwrap(); + assert!(dst_plaintext.len() == plaintext.len()); + assert_eq!(plaintext, dst_plaintext); + } -#[test] -fn auth_enc_ciphertext_less_than_41_err() { - let key = SecretKey::default(); - let ciphertext = [0u8; 40]; + #[test] + fn test_auth_enc_plaintext_empty_err() { + let key = SecretKey::default(); + let plaintext = "".as_bytes().to_vec(); - assert!(open(&key, &ciphertext).is_err()); -} + assert!(seal(&key, &plaintext).is_err()); + } -#[test] -fn test_modified_nonce_err() { - let key = SecretKey::default(); - let plaintext = "Secret message".as_bytes().to_vec(); + #[test] + fn test_auth_enc_ciphertext_less_than_41_err() { + let key = SecretKey::default(); + let ciphertext = [0u8; 40]; - let mut dst_ciphertext = seal(&key, &plaintext).unwrap(); - // Modify nonce - dst_ciphertext[10] ^= 1; - assert!(open(&key, &dst_ciphertext).is_err()); -} + assert!(open(&key, &ciphertext).is_err()); + } -#[test] -fn test_modified_ciphertext_err() { - let key = SecretKey::default(); - let plaintext = "Secret message".as_bytes().to_vec(); + #[test] + fn test_modified_nonce_err() { + let key = SecretKey::default(); + let plaintext = "Secret message".as_bytes().to_vec(); - let mut dst_ciphertext = seal(&key, &plaintext).unwrap(); - // Modify ciphertext - dst_ciphertext[25] ^= 1; - assert!(open(&key, &dst_ciphertext).is_err()); -} + let mut dst_ciphertext = seal(&key, &plaintext).unwrap(); + // Modify nonce + dst_ciphertext[10] ^= 1; + assert!(open(&key, &dst_ciphertext).is_err()); + } -#[test] -fn test_modified_tag_err() { - let key = SecretKey::default(); - let plaintext = "Secret message".as_bytes().to_vec(); + #[test] + fn test_modified_ciphertext_err() { + let key = SecretKey::default(); + let plaintext = "Secret message".as_bytes().to_vec(); - let mut dst_ciphertext = seal(&key, &plaintext).unwrap(); - let dst_ciphertext_len = dst_ciphertext.len(); - // Modify tag - dst_ciphertext[dst_ciphertext_len - 6] ^= 1; - assert!(open(&key, &dst_ciphertext).is_err()); -} + let mut dst_ciphertext = seal(&key, &plaintext).unwrap(); + // Modify ciphertext + dst_ciphertext[25] ^= 1; + assert!(open(&key, &dst_ciphertext).is_err()); + } -#[test] -fn test_diff_secret_key_err() { - let key = SecretKey::default(); - let plaintext = "Secret message".as_bytes().to_vec(); + #[test] + fn test_modified_tag_err() { + let key = SecretKey::default(); + let plaintext = "Secret message".as_bytes().to_vec(); - let dst_ciphertext = seal(&key, &plaintext).unwrap(); - let bad_key = SecretKey::default(); - assert!(open(&bad_key, &dst_ciphertext).is_err()); -} + let mut dst_ciphertext = seal(&key, &plaintext).unwrap(); + let dst_ciphertext_len = dst_ciphertext.len(); + // Modify tag + dst_ciphertext[dst_ciphertext_len - 6] ^= 1; + assert!(open(&key, &dst_ciphertext).is_err()); + } + + #[test] + fn test_diff_secret_key_err() { + let key = SecretKey::default(); + let plaintext = "Secret message".as_bytes().to_vec(); -#[test] -fn test_secret_length_err() { - let key = SecretKey::generate(31).unwrap(); - let plaintext = "Secret message Secret message Secret message Secret message " - .as_bytes() - .to_vec(); + let dst_ciphertext = seal(&key, &plaintext).unwrap(); + let bad_key = SecretKey::default(); + assert!(open(&bad_key, &dst_ciphertext).is_err()); + } - assert!(seal(&key, &plaintext).is_err()); - assert!(open(&key, &plaintext).is_err()); + #[test] + fn test_secret_length_err() { + let key = SecretKey::generate(31).unwrap(); + let plaintext = "Secret message Secret message Secret message Secret message " + .as_bytes() + .to_vec(); + + assert!(seal(&key, &plaintext).is_err()); + assert!(open(&key, &plaintext).is_err()); + } + } + + // Proptests. Only exectued when NOT testing no_std. + #[cfg(not(feature = "no_std"))] + mod proptest { + use super::*; + + quickcheck! { + // Sealing input, and then opening should always yield the same input. + fn prop_seal_open_same_input(input: Vec) -> bool { + let pt = if input.is_empty() { + vec![1u8; 10] + } else { + input + }; + + let sk = SecretKey::default(); + + let ct = seal(&sk, &pt).unwrap(); + let pt_decrypted = open(&sk, &ct).unwrap(); + + (pt == pt_decrypted) + } + } + + quickcheck! { + // Sealing input, modifying the tag and then opening should + // always fail due to authentication. + fn prop_fail_on_diff_key(input: Vec) -> bool { + let pt = if input.is_empty() { + vec![1u8; 10] + } else { + input + }; + + let sk = SecretKey::default(); + let sk2 = SecretKey::default(); + + let ct = seal(&sk, &pt).unwrap(); + if open(&sk2, &ct).is_err() { + true + } else { + false + } + } + } + } } diff --git a/src/auth.rs b/src/auth.rs index 318a4fea..fea46ead 100644 --- a/src/auth.rs +++ b/src/auth.rs @@ -91,31 +91,88 @@ pub fn authenticate_verify( Ok(true) } -#[test] -fn test_authenticate_verify_bad_key() { - let sec_key_correct = SecretKey::generate(64).unwrap(); - let sec_key_false = SecretKey::default(); - let msg = "what do ya want for nothing?".as_bytes().to_vec(); - - let hmac_bob = authenticate(&sec_key_correct, &msg).unwrap(); - - assert_eq!( - authenticate_verify(&hmac_bob, &sec_key_correct, &msg).unwrap(), - true - ); - assert!(authenticate_verify(&hmac_bob, &sec_key_false, &msg).is_err()); -} - -#[test] -fn test_authenticate_verify_bad_msg() { - let sec_key = SecretKey::generate(64).unwrap(); - let msg = "what do ya want for nothing?".as_bytes().to_vec(); - - let hmac_bob = authenticate(&sec_key, &msg).unwrap(); - - assert_eq!( - authenticate_verify(&hmac_bob, &sec_key, &msg).unwrap(), - true - ); - assert!(authenticate_verify(&hmac_bob, &sec_key, b"bad msg").is_err()); +// Testing public functions in the module. +#[cfg(test)] +mod public { + use super::*; + + mod test_auth_auth_verify { + use super::*; + #[test] + fn test_authenticate_verify_bad_key() { + let sec_key_correct = SecretKey::generate(64).unwrap(); + let sec_key_false = SecretKey::default(); + let msg = "what do ya want for nothing?".as_bytes().to_vec(); + + let hmac_bob = authenticate(&sec_key_correct, &msg).unwrap(); + + assert_eq!( + authenticate_verify(&hmac_bob, &sec_key_correct, &msg).unwrap(), + true + ); + assert!(authenticate_verify(&hmac_bob, &sec_key_false, &msg).is_err()); + } + + #[test] + fn test_authenticate_verify_bad_msg() { + let sec_key = SecretKey::generate(64).unwrap(); + let msg = "what do ya want for nothing?".as_bytes().to_vec(); + + let hmac_bob = authenticate(&sec_key, &msg).unwrap(); + + assert_eq!( + authenticate_verify(&hmac_bob, &sec_key, &msg).unwrap(), + true + ); + assert!(authenticate_verify(&hmac_bob, &sec_key, b"bad msg").is_err()); + } + } + + // Proptests. Only exectued when NOT testing no_std. + #[cfg(not(feature = "no_std"))] + mod proptest { + use super::*; + + quickcheck! { + /// Authentication and verifing that authentication with the same parameters + /// should always be true. + fn prop_authenticate_verify(input: Vec) -> bool { + let sk = SecretKey::default(); + + let tag = authenticate(&sk, &input[..]).unwrap(); + authenticate_verify(&tag, &sk, &input[..]).unwrap() + } + } + + quickcheck! { + /// Authentication and verifing that authentication with a different key should + /// never be true. + fn prop_verify_fail_diff_key(input: Vec) -> bool { + let sk = SecretKey::default(); + let sk2 = SecretKey::default(); + + let tag = authenticate(&sk, &input[..]).unwrap(); + if authenticate_verify(&tag, &sk2, &input[..]).is_err() { + true + } else { + false + } + } + } + + quickcheck! { + /// Authentication and verifing that authentication with different input should + /// never be true. + fn prop_verify_fail_diff_input(input: Vec) -> bool { + let sk = SecretKey::default(); + + let tag = authenticate(&sk, &input[..]).unwrap(); + if authenticate_verify(&tag, &sk, b"Completely wrong input").is_err() { + true + } else { + false + } + } + } + } } diff --git a/src/typedefs.rs b/src/typedefs.rs index e221bd12..ce66f972 100644 --- a/src/typedefs.rs +++ b/src/typedefs.rs @@ -409,7 +409,7 @@ macro_rules! construct_tag { assert!(test.unprotected_as_bytes().len() == test.get_length()); assert!($size == test.get_length()); } - + #[test] fn test_omitted_debug_tag() { let secret = format!("{:?}", [0u8; $size].as_ref()); From 9fc3bb7b1fa64da52561522d67b9959cd28ad946 Mon Sep 17 00:00:00 2001 From: brycx Date: Wed, 30 Jan 2019 21:20:44 +0100 Subject: [PATCH 32/40] Tests: Re-organize tests in high-level auth, kdf, hash and pwhash as well as add proptests to those --- src/auth.rs | 2 +- src/hash.rs | 33 +++++++++- src/kdf.rs | 140 ++++++++++++++++++++++++++++++++++++------- src/pwhash.rs | 163 ++++++++++++++++++++++++++++++++++---------------- 4 files changed, 261 insertions(+), 77 deletions(-) diff --git a/src/auth.rs b/src/auth.rs index fea46ead..15fb1a2a 100644 --- a/src/auth.rs +++ b/src/auth.rs @@ -96,7 +96,7 @@ pub fn authenticate_verify( mod public { use super::*; - mod test_auth_auth_verify { + mod test_auth_and_verify { use super::*; #[test] fn test_authenticate_verify_bad_key() { diff --git a/src/hash.rs b/src/hash.rs index a9025503..f29de146 100644 --- a/src/hash.rs +++ b/src/hash.rs @@ -60,5 +60,34 @@ pub fn digest(data: &[u8]) -> Result { Ok(blake2b::Hasher::Blake2b256.digest(data)?) } -#[test] -fn basic_test() { let _digest = digest(b"Some data").unwrap(); } +// Testing public functions in the module. +#[cfg(test)] +mod public { + use super::*; + + mod test_digest { + use super::*; + #[test] + fn basic_test() { let _digest = digest(b"Some data").unwrap(); } + + // Proptests. Only exectued when NOT testing no_std. + #[cfg(not(feature = "no_std"))] + mod proptest { + use super::*; + + quickcheck! { + /// Hashing twice with same input should always produce same output. + fn prop_digest_same_result(input: Vec) -> bool { + (digest(&input[..]).unwrap() == digest(&input[..]).unwrap()) + } + } + + quickcheck! { + /// Hashing twice with different input should never produce same output. + fn prop_digest_diff_result(input: Vec) -> bool { + (digest(&input[..]).unwrap() != digest(b"Completely wrong input").unwrap()) + } + } + } + } +} diff --git a/src/kdf.rs b/src/kdf.rs index 99784a2b..de68c243 100644 --- a/src/kdf.rs +++ b/src/kdf.rs @@ -129,32 +129,130 @@ pub fn derive_key_verify( Ok(is_good) } -#[test] -fn derive_key_and_verify() { - let password = Password::from_slice(&[0u8; 64]).unwrap(); - let salt = Salt::from_slice(&[0u8; 64]).unwrap(); +// Testing public functions in the module. +#[cfg(test)] +mod public { + use super::*; - let dk = derive_key(&password, &salt, 100, 64).unwrap(); + mod test_derive_key_and_verify { + use super::*; + #[test] + fn test_derive_key_and_verify() { + let password = Password::from_slice(&[0u8; 64]).unwrap(); + let salt = Salt::from_slice(&[0u8; 64]).unwrap(); - assert!(derive_key_verify(&dk, &password, &salt, 100).unwrap()); -} + let dk = derive_key(&password, &salt, 100, 64).unwrap(); -#[test] -fn derive_key_and_verify_err() { - let password = Password::from_slice(&[0u8; 64]).unwrap(); - let salt = Salt::from_slice(&[0u8; 64]).unwrap(); + assert!(derive_key_verify(&dk, &password, &salt, 100).unwrap()); + } - let dk = derive_key(&password, &salt, 100, 64).unwrap(); + #[test] + fn test_derive_key_and_verify_err() { + let password = Password::from_slice(&[0u8; 64]).unwrap(); + let salt = Salt::from_slice(&[0u8; 64]).unwrap(); - assert!(derive_key_verify(&dk, &password, &salt, 50).is_err()); -} + let dk = derive_key(&password, &salt, 100, 64).unwrap(); + + assert!(derive_key_verify(&dk, &password, &salt, 50).is_err()); + } + + #[test] + fn test_derive_key_bad_length() { + let password = Password::from_slice(&[0u8; 64]).unwrap(); + let salt = Salt::from_slice(&[0u8; 64]).unwrap(); + + assert!(derive_key(&password, &salt, 100, 0).is_err()); + assert!(derive_key(&password, &salt, 100, 1).is_ok()); + assert!(derive_key(&password, &salt, 100, usize::max_value()).is_err()); + } + } + + // Proptests. Only exectued when NOT testing no_std. + #[cfg(not(feature = "no_std"))] + mod proptest { + use super::*; + + quickcheck! { + /// Deriving a key and verifying with the same parameters should always be true. + fn prop_derive_key_verify(input: Vec, size: usize) -> bool { + let passin = if input.is_empty() { + vec![1u8; 10] + } else { + input + }; + + let size_checked = if size == 0 { + 32 + } else { + size + }; -#[test] -fn derive_key_bad_length() { - let password = Password::from_slice(&[0u8; 64]).unwrap(); - let salt = Salt::from_slice(&[0u8; 64]).unwrap(); + let pass = Password::from_slice(&passin[..]).unwrap(); + let salt = Salt::default(); + let derived_key = derive_key(&pass, &salt, 100, size_checked).unwrap(); - assert!(derive_key(&password, &salt, 100, 0).is_err()); - assert!(derive_key(&password, &salt, 100, 1).is_ok()); - assert!(derive_key(&password, &salt, 100, usize::max_value()).is_err()); + if derive_key_verify(&derived_key, &pass, &salt, 100).is_ok() { + true + } else { + false + } + } + } + + quickcheck! { + /// Deriving a key and verifying with a different password should always be false. + fn prop_derive_key_verify_false_bad_password(input: Vec, size: usize) -> bool { + let passin = if input.is_empty() { + vec![1u8; 10] + } else { + input + }; + + let size_checked = if size == 0 { + 32 + } else { + size + }; + + let pass = Password::from_slice(&passin[..]).unwrap(); + let salt = Salt::default(); + let derived_key = derive_key(&pass, &salt, 100, size_checked).unwrap(); + let bad_pass = Password::generate(32).unwrap(); + + if derive_key_verify(&derived_key, &bad_pass, &salt, 100).is_err() { + true + } else { + false + } + } + } + + quickcheck! { + /// Deriving a key and verifying with a different salt should always be false. + fn prop_derive_key_verify_false_bad_salt(input: Vec, size: usize) -> bool { + let passin = if input.is_empty() { + vec![1u8; 10] + } else { + input + }; + + let size_checked = if size == 0 { + 32 + } else { + size + }; + + let pass = Password::from_slice(&passin[..]).unwrap(); + let salt = Salt::default(); + let derived_key = derive_key(&pass, &salt, 100, size_checked).unwrap(); + let bad_salt = Salt::default(); + + if derive_key_verify(&derived_key, &pass, &bad_salt, 100).is_err() { + true + } else { + false + } + } + } + } } diff --git a/src/pwhash.rs b/src/pwhash.rs index 5e313148..47000dac 100644 --- a/src/pwhash.rs +++ b/src/pwhash.rs @@ -124,57 +124,114 @@ pub fn hash_password_verify( Ok(is_good) } -#[test] -fn pbkdf2_verify() { - let password = Password::from_slice(&[0u8; 64]).unwrap(); - - let pbkdf2_dk = hash_password(&password, 100).unwrap(); - - assert_eq!( - hash_password_verify(&pbkdf2_dk, &password, 100).unwrap(), - true - ); -} - -#[test] -fn pbkdf2_verify_err_modified_salt() { - let password = Password::from_slice(&[0u8; 64]).unwrap(); - - let pbkdf2_dk = hash_password(&password, 100).unwrap(); - let mut pwd_mod = pbkdf2_dk.unprotected_as_bytes().to_vec(); - pwd_mod[0..32].copy_from_slice(&[0u8; 32]); - let modified = PasswordHash::from_slice(&pwd_mod).unwrap(); - - assert!(hash_password_verify(&modified, &password, 100).is_err()); -} - -#[test] -fn pbkdf2_verify_err_modified_password() { - let password = Password::from_slice(&[0u8; 64]).unwrap(); - - let pbkdf2_dk = hash_password(&password, 100).unwrap(); - let mut pwd_mod = pbkdf2_dk.unprotected_as_bytes().to_vec(); - pwd_mod[120..128].copy_from_slice(&[0u8; 8]); - let modified = PasswordHash::from_slice(&pwd_mod).unwrap(); - - assert!(hash_password_verify(&modified, &password, 100).is_err()); -} - -#[test] -fn pbkdf2_verify_err_modified_salt_and_password() { - let password = Password::from_slice(&[0u8; 64]).unwrap(); - - let pbkdf2_dk = hash_password(&password, 100).unwrap(); - let mut pwd_mod = pbkdf2_dk.unprotected_as_bytes().to_vec(); - pwd_mod[64..96].copy_from_slice(&[0u8; 32]); - let modified = PasswordHash::from_slice(&pwd_mod).unwrap(); - - assert!(hash_password_verify(&modified, &password, 100).is_err()); -} - -#[test] -fn pbkdf2_zero_iterations() { - let password = Password::from_slice(&[0u8; 64]).unwrap(); - - assert!(hash_password(&password, 0).is_err()); +// Testing public functions in the module. +#[cfg(test)] +mod public { + use super::*; + + mod test_pwhash_and_verify { + use super::*; + + #[test] + fn test_pbkdf2_verify() { + let password = Password::from_slice(&[0u8; 64]).unwrap(); + + let pbkdf2_dk = hash_password(&password, 100).unwrap(); + + assert_eq!( + hash_password_verify(&pbkdf2_dk, &password, 100).unwrap(), + true + ); + } + + #[test] + fn test_pbkdf2_verify_err_modified_salt() { + let password = Password::from_slice(&[0u8; 64]).unwrap(); + + let pbkdf2_dk = hash_password(&password, 100).unwrap(); + let mut pwd_mod = pbkdf2_dk.unprotected_as_bytes().to_vec(); + pwd_mod[0..32].copy_from_slice(&[0u8; 32]); + let modified = PasswordHash::from_slice(&pwd_mod).unwrap(); + + assert!(hash_password_verify(&modified, &password, 100).is_err()); + } + + #[test] + fn test_pbkdf2_verify_err_modified_password() { + let password = Password::from_slice(&[0u8; 64]).unwrap(); + + let pbkdf2_dk = hash_password(&password, 100).unwrap(); + let mut pwd_mod = pbkdf2_dk.unprotected_as_bytes().to_vec(); + pwd_mod[120..128].copy_from_slice(&[0u8; 8]); + let modified = PasswordHash::from_slice(&pwd_mod).unwrap(); + + assert!(hash_password_verify(&modified, &password, 100).is_err()); + } + + #[test] + fn test_pbkdf2_verify_err_modified_salt_and_password() { + let password = Password::from_slice(&[0u8; 64]).unwrap(); + + let pbkdf2_dk = hash_password(&password, 100).unwrap(); + let mut pwd_mod = pbkdf2_dk.unprotected_as_bytes().to_vec(); + pwd_mod[64..96].copy_from_slice(&[0u8; 32]); + let modified = PasswordHash::from_slice(&pwd_mod).unwrap(); + + assert!(hash_password_verify(&modified, &password, 100).is_err()); + } + + #[test] + fn test_pbkdf2_zero_iterations() { + let password = Password::from_slice(&[0u8; 64]).unwrap(); + + assert!(hash_password(&password, 0).is_err()); + } + } + + // Proptests. Only exectued when NOT testing no_std. + #[cfg(not(feature = "no_std"))] + mod proptest { + use super::*; + + quickcheck! { + /// Hashing and verifying the same password should always be true. + fn prop_pwhash_verify(input: Vec) -> bool { + let passin = if input.is_empty() { + vec![1u8; 10] + } else { + input + }; + + let pass = Password::from_slice(&passin[..]).unwrap(); + let pass_hash = hash_password(&pass, 100).unwrap(); + + if hash_password_verify(&pass_hash, &pass, 100).is_ok() { + true + } else { + false + } + } + } + + quickcheck! { + /// Hashing and verifying different passwords should always be false. + fn prop_pwhash_verify_false(input: Vec) -> bool { + let passin = if input.is_empty() { + vec![1u8; 10] + } else { + input + }; + + let pass = Password::from_slice(&passin[..]).unwrap(); + let pass_hash = hash_password(&pass, 100).unwrap(); + let bad_pass = Password::generate(32).unwrap(); + + if hash_password_verify(&pass_hash, &bad_pass, 100).is_err() { + true + } else { + false + } + } + } + } } From d4d9c0be1ed0c81e67d477d4c86a49c9f4b53110 Mon Sep 17 00:00:00 2001 From: brycx Date: Wed, 30 Jan 2019 21:38:35 +0100 Subject: [PATCH 33/40] Tests: Mark the two new macro_rules! for variable size to only be included on safe_api --- src/typedefs.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/typedefs.rs b/src/typedefs.rs index ce66f972..c932c03f 100644 --- a/src/typedefs.rs +++ b/src/typedefs.rs @@ -133,6 +133,7 @@ macro_rules! func_from_slice (($name:ident, $size:expr) => ( } )); +#[cfg(feature = "safe_api")] /// Macro to implement a `from_slice()` function. Returns `UnknownCryptoError` /// if the slice is not of length `$size`. macro_rules! func_from_slice_variable_size (($name:ident) => ( @@ -194,6 +195,7 @@ macro_rules! func_generate (($name:ident, $size:expr) => ( } )); +#[cfg(feature = "safe_api")] /// Macro to implement a `generate()` function for objects that benefit from /// having a CSPRNG available to generate data of a variable length. macro_rules! func_generate_variable_size (($name:ident) => ( From 118ab45d4efc51655fc3a04b9b27498d8b3acd06 Mon Sep 17 00:00:00 2001 From: brycx Date: Wed, 30 Jan 2019 21:44:55 +0100 Subject: [PATCH 34/40] Tests: Correctly mark proptest modules only to be included with safe_api --- src/aead.rs | 2 +- src/auth.rs | 2 +- src/hash.rs | 2 +- src/hazardous/aead/chacha20poly1305.rs | 4 ++-- src/hazardous/aead/xchacha20poly1305.rs | 2 +- src/hazardous/hash/blake2b.rs | 8 ++++---- src/hazardous/hash/sha512.rs | 2 +- src/hazardous/kdf/hkdf.rs | 5 +---- src/hazardous/mac/hmac.rs | 4 ++-- src/hazardous/mac/poly1305.rs | 4 ++-- src/hazardous/stream/chacha20.rs | 6 +++--- src/hazardous/stream/xchacha20.rs | 2 +- src/hazardous/xof/cshake.rs | 2 +- src/kdf.rs | 2 +- src/pwhash.rs | 2 +- 15 files changed, 23 insertions(+), 26 deletions(-) diff --git a/src/aead.rs b/src/aead.rs index 8e2aee12..9debdeae 100644 --- a/src/aead.rs +++ b/src/aead.rs @@ -222,7 +222,7 @@ mod public { } // Proptests. Only exectued when NOT testing no_std. - #[cfg(not(feature = "no_std"))] + #[cfg(feature = "safe_api")] mod proptest { use super::*; diff --git a/src/auth.rs b/src/auth.rs index 15fb1a2a..a8f3fbd7 100644 --- a/src/auth.rs +++ b/src/auth.rs @@ -129,7 +129,7 @@ mod public { } // Proptests. Only exectued when NOT testing no_std. - #[cfg(not(feature = "no_std"))] + #[cfg(feature = "safe_api")] mod proptest { use super::*; diff --git a/src/hash.rs b/src/hash.rs index f29de146..967278d3 100644 --- a/src/hash.rs +++ b/src/hash.rs @@ -71,7 +71,7 @@ mod public { fn basic_test() { let _digest = digest(b"Some data").unwrap(); } // Proptests. Only exectued when NOT testing no_std. - #[cfg(not(feature = "no_std"))] + #[cfg(feature = "safe_api")] mod proptest { use super::*; diff --git a/src/hazardous/aead/chacha20poly1305.rs b/src/hazardous/aead/chacha20poly1305.rs index 630dfe5e..686c4308 100644 --- a/src/hazardous/aead/chacha20poly1305.rs +++ b/src/hazardous/aead/chacha20poly1305.rs @@ -457,7 +457,7 @@ mod public { } // Proptests. Only exectued when NOT testing no_std. - #[cfg(not(feature = "no_std"))] + #[cfg(feature = "safe_api")] mod proptest { use super::*; @@ -716,7 +716,7 @@ mod private { } // Proptests. Only exectued when NOT testing no_std. - #[cfg(not(feature = "no_std"))] + #[cfg(feature = "safe_api")] mod proptest { use super::*; diff --git a/src/hazardous/aead/xchacha20poly1305.rs b/src/hazardous/aead/xchacha20poly1305.rs index efb8532c..ac27ee8f 100644 --- a/src/hazardous/aead/xchacha20poly1305.rs +++ b/src/hazardous/aead/xchacha20poly1305.rs @@ -355,7 +355,7 @@ mod public { } // Proptests. Only exectued when NOT testing no_std. - #[cfg(not(feature = "no_std"))] + #[cfg(feature = "safe_api")] mod proptest { use super::*; diff --git a/src/hazardous/hash/blake2b.rs b/src/hazardous/hash/blake2b.rs index 44c68bc5..9222d97e 100644 --- a/src/hazardous/hash/blake2b.rs +++ b/src/hazardous/hash/blake2b.rs @@ -514,7 +514,7 @@ mod public { } // Proptests. Only exectued when NOT testing no_std. - #[cfg(not(feature = "no_std"))] + #[cfg(feature = "safe_api")] mod proptest { use super::*; @@ -561,7 +561,7 @@ mod public { } // Proptests. Only exectued when NOT testing no_std. - #[cfg(not(feature = "no_std"))] + #[cfg(feature = "safe_api")] mod proptest { use super::*; @@ -625,7 +625,7 @@ mod public { } // Proptests. Only exectued when NOT testing no_std. - #[cfg(not(feature = "no_std"))] + #[cfg(feature = "safe_api")] mod proptest { use super::*; @@ -923,7 +923,7 @@ mod public { } } // Proptests. Only exectued when NOT testing no_std. - #[cfg(not(feature = "no_std"))] + #[cfg(feature = "safe_api")] mod proptest { use super::*; diff --git a/src/hazardous/hash/sha512.rs b/src/hazardous/hash/sha512.rs index ea29b546..3d59520b 100644 --- a/src/hazardous/hash/sha512.rs +++ b/src/hazardous/hash/sha512.rs @@ -578,7 +578,7 @@ mod public { } } // Proptests. Only exectued when NOT testing no_std. - #[cfg(not(feature = "no_std"))] + #[cfg(feature = "safe_api")] mod proptest { use super::*; diff --git a/src/hazardous/kdf/hkdf.rs b/src/hazardous/kdf/hkdf.rs index ff8dfef8..5c3cc1fc 100644 --- a/src/hazardous/kdf/hkdf.rs +++ b/src/hazardous/kdf/hkdf.rs @@ -172,16 +172,13 @@ mod public { assert!(expand(&prk, Some(b""), &mut okm_out).is_err()); } - // Proptests. Only exectued when NOT testing no_std. - #[cfg(not(feature = "no_std"))] - mod proptest {} } mod test_derive_key { use super::*; // Proptests. Only exectued when NOT testing no_std. - #[cfg(not(feature = "no_std"))] + #[cfg(feature = "safe_api")] mod proptest { use super::*; diff --git a/src/hazardous/mac/hmac.rs b/src/hazardous/mac/hmac.rs index f9634da4..9633917b 100644 --- a/src/hazardous/mac/hmac.rs +++ b/src/hazardous/mac/hmac.rs @@ -270,7 +270,7 @@ mod public { } // Proptests. Only exectued when NOT testing no_std. - #[cfg(not(feature = "no_std"))] + #[cfg(feature = "safe_api")] mod proptest { use super::*; @@ -508,7 +508,7 @@ mod public { } } // Proptests. Only exectued when NOT testing no_std. - #[cfg(not(feature = "no_std"))] + #[cfg(feature = "safe_api")] mod proptest { use super::*; diff --git a/src/hazardous/mac/poly1305.rs b/src/hazardous/mac/poly1305.rs index aaf0b295..e1b4758c 100644 --- a/src/hazardous/mac/poly1305.rs +++ b/src/hazardous/mac/poly1305.rs @@ -477,7 +477,7 @@ mod public { } // Proptests. Only exectued when NOT testing no_std. - #[cfg(not(feature = "no_std"))] + #[cfg(feature = "safe_api")] mod proptest { use super::*; @@ -716,7 +716,7 @@ mod public { } } // Proptests. Only exectued when NOT testing no_std. - #[cfg(not(feature = "no_std"))] + #[cfg(feature = "safe_api")] mod proptest { use super::*; diff --git a/src/hazardous/stream/chacha20.rs b/src/hazardous/stream/chacha20.rs index 928382f7..f38ea426 100644 --- a/src/hazardous/stream/chacha20.rs +++ b/src/hazardous/stream/chacha20.rs @@ -489,7 +489,7 @@ mod public { } // Proptests. Only exectued when NOT testing no_std. - #[cfg(not(feature = "no_std"))] + #[cfg(feature = "safe_api")] mod proptest { use super::*; @@ -758,7 +758,7 @@ mod public { } // Proptests. Only exectued when NOT testing no_std. - #[cfg(not(feature = "no_std"))] + #[cfg(feature = "safe_api")] mod proptest { use super::*; @@ -863,7 +863,7 @@ mod private { } // Proptests. Only exectued when NOT testing no_std. - #[cfg(not(feature = "no_std"))] + #[cfg(feature = "safe_api")] mod proptest { use super::*; diff --git a/src/hazardous/stream/xchacha20.rs b/src/hazardous/stream/xchacha20.rs index db18e743..07ba7bd4 100644 --- a/src/hazardous/stream/xchacha20.rs +++ b/src/hazardous/stream/xchacha20.rs @@ -280,7 +280,7 @@ mod public { } // Proptests. Only exectued when NOT testing no_std. - #[cfg(not(feature = "no_std"))] + #[cfg(feature = "safe_api")] mod proptest { use super::*; diff --git a/src/hazardous/xof/cshake.rs b/src/hazardous/xof/cshake.rs index 8340e6a1..324ff19a 100644 --- a/src/hazardous/xof/cshake.rs +++ b/src/hazardous/xof/cshake.rs @@ -579,7 +579,7 @@ mod public { } } // Proptests. Only exectued when NOT testing no_std. - #[cfg(not(feature = "no_std"))] + #[cfg(feature = "safe_api")] mod proptest { use super::*; diff --git a/src/kdf.rs b/src/kdf.rs index de68c243..205902aa 100644 --- a/src/kdf.rs +++ b/src/kdf.rs @@ -168,7 +168,7 @@ mod public { } // Proptests. Only exectued when NOT testing no_std. - #[cfg(not(feature = "no_std"))] + #[cfg(feature = "safe_api")] mod proptest { use super::*; diff --git a/src/pwhash.rs b/src/pwhash.rs index 47000dac..1156a83f 100644 --- a/src/pwhash.rs +++ b/src/pwhash.rs @@ -189,7 +189,7 @@ mod public { } // Proptests. Only exectued when NOT testing no_std. - #[cfg(not(feature = "no_std"))] + #[cfg(feature = "safe_api")] mod proptest { use super::*; From da30f67f960f3d81e1d50f59708826d9e7d7a263 Mon Sep 17 00:00:00 2001 From: brycx Date: Wed, 30 Jan 2019 21:47:30 +0100 Subject: [PATCH 35/40] Tests: Mark typedefs tests using format! with only on safe_api because that macro needs sts --- src/typedefs.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/typedefs.rs b/src/typedefs.rs index c932c03f..958f3c2d 100644 --- a/src/typedefs.rs +++ b/src/typedefs.rs @@ -272,6 +272,8 @@ macro_rules! construct_secret_key { } #[test] + #[cfg(feature = "safe_api")] + // format! is only available with std fn test_omitted_debug_secret_key() { let secret = format!("{:?}", [0u8; $size].as_ref()); let test_debug_contents = format!("{:?}", $name::from_slice(&[0u8; $size]).unwrap()); @@ -413,6 +415,8 @@ macro_rules! construct_tag { } #[test] + #[cfg(feature = "safe_api")] + // format! is only available with std fn test_omitted_debug_tag() { let secret = format!("{:?}", [0u8; $size].as_ref()); let test_debug_contents = format!("{:?}", $name::from_slice(&[0u8; $size]).unwrap()); @@ -496,6 +500,8 @@ macro_rules! construct_hmac_key { } #[test] + #[cfg(feature = "safe_api")] + // format! is only available with std fn test_omitted_debug_hmac_key() { let secret = format!("{:?}", [0u8; $size].as_ref()); let test_debug_contents = format!("{:?}", $name::from_slice(&[0u8; $size]).unwrap()); @@ -623,6 +629,8 @@ macro_rules! construct_blake2b_key { } #[test] + #[cfg(feature = "safe_api")] + // format! is only available with std fn test_omitted_debug_blake2b_key() { let secret = format!("{:?}", [0u8; 64].as_ref()); let test_debug_contents = format!("{:?}", $name::from_slice(&[0u8; 64]).unwrap()); @@ -772,6 +780,8 @@ macro_rules! construct_secret_key_variable_size { } #[test] + #[cfg(feature = "safe_api")] + // format! is only available with std fn test_omitted_debug_secret_key() { let secret = format!("{:?}", [0u8; $size].as_ref()); let test_debug_contents = format!("{:?}", $name::from_slice(&[0u8; $size]).unwrap()); @@ -892,6 +902,8 @@ macro_rules! construct_password_variable_size { } #[test] + #[cfg(feature = "safe_api")] + // format! is only available with std fn test_omitted_debug_password() { let secret = format!("{:?}", [0u8; 64].as_ref()); let test_debug_contents = format!("{:?}", $name::from_slice(&[0u8; 64]).unwrap()); From f867aba69e231ea925c427f30f35f8bae6d64f5d Mon Sep 17 00:00:00 2001 From: brycx Date: Wed, 30 Jan 2019 22:11:01 +0100 Subject: [PATCH 36/40] Tests: Fix imports when not used outside of safe_api --- src/hazardous/hash/blake2b.rs | 4 ++-- src/hazardous/kdf/hkdf.rs | 2 ++ src/hazardous/xof/cshake.rs | 3 +++ src/lib.rs | 1 + 4 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/hazardous/hash/blake2b.rs b/src/hazardous/hash/blake2b.rs index 9222d97e..9d176a0b 100644 --- a/src/hazardous/hash/blake2b.rs +++ b/src/hazardous/hash/blake2b.rs @@ -869,7 +869,7 @@ mod public { produces_same_state(None, 64, b"Tests"); produces_same_state(None, 28, b"Tests"); - let sk = SecretKey::generate().unwrap(); + let sk = SecretKey::from_slice(b"Testing").unwrap(); produces_same_state(Some(&sk), 1, b"Tests"); produces_same_state(Some(&sk), 32, b"Tests"); produces_same_state(Some(&sk), 64, b"Tests"); @@ -884,7 +884,7 @@ mod public { produces_same_hash(None, 64, b"Tests"); produces_same_hash(None, 28, b"Tests"); - let sk = SecretKey::generate().unwrap(); + let sk = SecretKey::from_slice(b"Testing").unwrap(); produces_same_hash(Some(&sk), 1, b"Tests"); produces_same_hash(Some(&sk), 32, b"Tests"); produces_same_hash(Some(&sk), 64, b"Tests"); diff --git a/src/hazardous/kdf/hkdf.rs b/src/hazardous/kdf/hkdf.rs index 5c3cc1fc..93c865d4 100644 --- a/src/hazardous/kdf/hkdf.rs +++ b/src/hazardous/kdf/hkdf.rs @@ -174,6 +174,8 @@ mod public { } } + #[cfg(feature = "safe_api")] + // Mark safe_api because currently it only contains proptests. mod test_derive_key { use super::*; diff --git a/src/hazardous/xof/cshake.rs b/src/hazardous/xof/cshake.rs index 324ff19a..f2843a44 100644 --- a/src/hazardous/xof/cshake.rs +++ b/src/hazardous/xof/cshake.rs @@ -484,6 +484,9 @@ mod public { } + #[cfg(feature = "safe_api")] + // Mark safe_api because currently it only contains proptests and tests that need + // vec![]. mod test_streaming_interface { use super::*; diff --git a/src/lib.rs b/src/lib.rs index 1141f791..c0502e70 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -57,6 +57,7 @@ extern crate subtle; extern crate tiny_keccak; #[cfg(test)] +#[cfg(feature = "safe_api")] #[macro_use] extern crate quickcheck; From 5f631805aad18759337a6fdb1e38b5b9e82b7d3c Mon Sep 17 00:00:00 2001 From: brycx Date: Wed, 30 Jan 2019 22:11:22 +0100 Subject: [PATCH 37/40] fmt --- src/hazardous/xof/cshake.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/hazardous/xof/cshake.rs b/src/hazardous/xof/cshake.rs index f2843a44..e75eb7ed 100644 --- a/src/hazardous/xof/cshake.rs +++ b/src/hazardous/xof/cshake.rs @@ -485,8 +485,8 @@ mod public { } #[cfg(feature = "safe_api")] - // Mark safe_api because currently it only contains proptests and tests that need - // vec![]. + // Mark safe_api because currently it only contains proptests and tests that + // need vec![]. mod test_streaming_interface { use super::*; From 0d390d3ba6fb5a2cb3f48db5e8d89a7e1fe5e300 Mon Sep 17 00:00:00 2001 From: brycx Date: Wed, 30 Jan 2019 22:47:38 +0100 Subject: [PATCH 38/40] Fuzz: Update fuzzing comparison dependencies and fix breaking changes --- fuzz/Cargo.toml | 4 ++-- fuzz/fuzz_targets/chacha20_poly1305_compare.rs | 10 ++++++++-- fuzz/fuzz_targets/ring_compare.rs | 4 ++-- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/fuzz/Cargo.toml b/fuzz/Cargo.toml index e338930e..b9a4e9b4 100644 --- a/fuzz/Cargo.toml +++ b/fuzz/Cargo.toml @@ -9,9 +9,9 @@ publish = false cargo-fuzz = true [dependencies] -ring = "0.13.5" +ring = "0.14.3" sp800-185 = "0.2.0" -chacha = "0.2.0" +chacha = "0.3.0" sodiumoxide = { git = "https://github.com/sodiumoxide/sodiumoxide" } blake2-rfc = "0.2.18" diff --git a/fuzz/fuzz_targets/chacha20_poly1305_compare.rs b/fuzz/fuzz_targets/chacha20_poly1305_compare.rs index 13250ba4..1159ed37 100644 --- a/fuzz/fuzz_targets/chacha20_poly1305_compare.rs +++ b/fuzz/fuzz_targets/chacha20_poly1305_compare.rs @@ -35,6 +35,12 @@ fuzz_target!(|data: &[u8]| { let enc_key = ring::aead::SealingKey::new(&ring::aead::CHACHA20_POLY1305, &key).unwrap(); let dec_key = ring::aead::OpeningKey::new(&ring::aead::CHACHA20_POLY1305, &key).unwrap(); + + let ring_nonce_enc = ring::aead::Nonce::try_assume_unique_for_key(&nonce).unwrap(); + let ring_aad_enc = ring::aead::Aad::from(&aad); + + let ring_nonce_dec = ring::aead::Nonce::try_assume_unique_for_key(&nonce).unwrap(); + let ring_aad_dec = ring::aead::Aad::from(&aad); let mut ciphertext_with_tag_ring: Vec = vec![0u8; plaintext.len() + 16]; let mut plaintext_out_ring = Vec::new(); @@ -42,7 +48,7 @@ fuzz_target!(|data: &[u8]| { ciphertext_with_tag_ring[..plaintext.len()].copy_from_slice(&plaintext); let index = - ring::aead::seal_in_place(&enc_key, &nonce, &aad, &mut ciphertext_with_tag_ring, 16) + ring::aead::seal_in_place(&enc_key, ring_nonce_enc, ring_aad_enc, &mut ciphertext_with_tag_ring, 16) .unwrap(); assert_eq!( &ciphertext_with_tag_ring[..index].as_ref(), @@ -53,7 +59,7 @@ fuzz_target!(|data: &[u8]| { &ciphertext_with_tag_ring[index - 16..index].as_ref(), &ciphertext_with_tag_orion[plaintext.len()..].as_ref() ); - ring::aead::open_in_place(&dec_key, &nonce, &aad, 0, &mut ciphertext_with_tag_ring).unwrap(); + ring::aead::open_in_place(&dec_key, ring_nonce_dec, ring_aad_dec, 0, &mut ciphertext_with_tag_ring).unwrap(); plaintext_out_ring.extend_from_slice(&ciphertext_with_tag_ring); assert_eq!( &ciphertext_with_tag_ring[..plaintext.len()].as_ref(), diff --git a/fuzz/fuzz_targets/ring_compare.rs b/fuzz/fuzz_targets/ring_compare.rs index 65a26a9c..1a46014f 100644 --- a/fuzz/fuzz_targets/ring_compare.rs +++ b/fuzz/fuzz_targets/ring_compare.rs @@ -56,7 +56,7 @@ fn ro_pbkdf2(data: &[u8]) { pbkdf2::derive_key(&orion_password, &salt, iter, &mut dk_out_orion).unwrap(); ring_pbkdf2::derive( &digest::SHA512, - iter as u32, + std::num::NonZeroU32::new(iter as u32).unwrap(), &salt, &password, &mut dk_out_ring, @@ -65,7 +65,7 @@ fn ro_pbkdf2(data: &[u8]) { assert_eq!(&dk_out_ring, &dk_out_orion); assert!(ring_pbkdf2::verify( &digest::SHA512, - iter as u32, + std::num::NonZeroU32::new(iter as u32).unwrap(), &salt, &password, &dk_out_orion From 3695b271c19488eced04c0f321dce2ef676b3a66 Mon Sep 17 00:00:00 2001 From: brycx Date: Wed, 30 Jan 2019 22:49:31 +0100 Subject: [PATCH 39/40] v0.12.4 --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 5035ae3c..b791bd55 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "orion" -version = "0.12.3" +version = "0.12.4" authors = ["brycx "] description = "Easy and usable rust crypto" keywords = [ "cryptography", "blake2", "aead", "sha2", "xchacha20_poly1305" ] From 0bac2a2e4d22e1492d31b59ecb334a8523171635 Mon Sep 17 00:00:00 2001 From: brycx Date: Wed, 30 Jan 2019 23:53:05 +0100 Subject: [PATCH 40/40] typedefs: Fix get_length on Blake2b Digest to return digest_size --- src/typedefs.rs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/typedefs.rs b/src/typedefs.rs index 958f3c2d..291fef87 100644 --- a/src/typedefs.rs +++ b/src/typedefs.rs @@ -684,7 +684,10 @@ macro_rules! construct_digest { }) } - func_get_length!(); + /// Return the length of the object. + pub fn get_length(&self) -> usize { + self.digest_size + } } impl core::fmt::Debug for $name { @@ -714,9 +717,9 @@ macro_rules! construct_digest { #[test] fn test_get_length_blake2b_digest() { - let test = $name::from_slice(&[0u8; 64]).unwrap(); + let test = $name::from_slice(&[0u8; 32]).unwrap(); assert!(test.as_bytes().len() == test.get_length()); - assert!($size == test.get_length()); + assert!(32 == test.get_length()); } ); }