Skip to content

Commit

Permalink
Add random secret-key/nonce generation to stream cipher and AEAD tests (
Browse files Browse the repository at this point in the history
#123)

* Add ability to randomly generate SecretKey and Nonce in (StreamCipher/Aead)TestRunner

This must be implemented seperately from the test runner, and is therefor a pub function.
The reason for this is, that the trait that let's us randomly generate these types, should
only be available when testing. But since StreamCipherTestRunner is used in intergation tests (/tests),
compiling it conditionally with cfg(test) would still make it inaccessible in /tests. Using
cfg(debug_assertions) is not desired either, because we want to test in both release and debug mode.

* (x)chacha20: Use new testing function to replace old quickcheck tests

* (x)chacha20poly1305: Add tests for randomly generated secret-key/nonce. See previous commit for details

* Tests: Fixed input length of 16 for new sk/n combination test. With some very small lengths, 'collisions' would occur.
  • Loading branch information
brycx authored Jun 7, 2020
1 parent a67b205 commit fda1c14
Show file tree
Hide file tree
Showing 6 changed files with 119 additions and 218 deletions.
2 changes: 1 addition & 1 deletion src/hazardous/aead/chacha20poly1305.rs
Original file line number Diff line number Diff line change
Expand Up @@ -242,7 +242,7 @@ mod public {
let secret_key = SecretKey::generate();
let nonce = Nonce::from_slice(&[0u8; chacha20::IETF_CHACHA_NONCESIZE]).unwrap();
AeadTestRunner(seal, open, secret_key, nonce, &input, None, POLY1305_OUTSIZE, &ad);

test_diff_params_err(&seal, &open, &input, POLY1305_OUTSIZE);
true
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/hazardous/aead/xchacha20poly1305.rs
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ mod public {
let secret_key = SecretKey::generate();
let nonce = Nonce::generate();
AeadTestRunner(seal, open, secret_key, nonce, &input, None, POLY1305_OUTSIZE, &ad);

test_diff_params_err(&seal, &open, &input, POLY1305_OUTSIZE);
true
}
}
Expand Down
123 changes: 15 additions & 108 deletions src/hazardous/stream/chacha20.rs
Original file line number Diff line number Diff line change
Expand Up @@ -386,6 +386,20 @@ mod public {
use super::*;
use crate::test_framework::streamcipher_interface::*;

impl TestingRandom for SecretKey {
fn gen() -> Self {
Self::generate()
}
}

impl TestingRandom for Nonce {
fn gen() -> Self {
let mut n = [0u8; IETF_CHACHA_NONCESIZE];
crate::util::secure_rand_bytes(&mut n).unwrap();
Self::from_slice(&n).unwrap()
}
}

// Proptests. Only executed when NOT testing no_std.
mod proptest {
use super::*;
Expand All @@ -395,118 +409,11 @@ mod public {
let secret_key = SecretKey::generate();
let nonce = Nonce::from_slice(&[0u8; IETF_CHACHA_NONCESIZE]).unwrap();
StreamCipherTestRunner(encrypt, decrypt, secret_key, nonce, counter, &input, None);
test_diff_params_diff_output(&encrypt, &decrypt);

true
}
}

quickcheck! {
// Encrypting and decrypting using two different secret keys and the same nonce
// should never yield the same input.
fn prop_encrypt_decrypt_diff_keys_diff_input(input: Vec<u8>) -> 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<u8>) -> 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<u8>) -> 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
}
}
}
}

Expand Down
115 changes: 7 additions & 108 deletions src/hazardous/stream/xchacha20.rs
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,12 @@ mod public {
use super::*;
use crate::test_framework::streamcipher_interface::*;

impl TestingRandom for Nonce {
fn gen() -> Self {
Self::generate()
}
}

// Proptests. Only executed when NOT testing no_std.
#[cfg(feature = "safe_api")]
mod proptest {
Expand All @@ -162,118 +168,11 @@ mod public {
let secret_key = SecretKey::generate();
let nonce = Nonce::generate();
StreamCipherTestRunner(encrypt, decrypt, secret_key, nonce, counter, &input, None);
test_diff_params_diff_output(&encrypt, &decrypt);

true
}
}

quickcheck! {
// Encrypting and decrypting using two different secret keys and the same nonce
// should never yield the same input.
fn prop_encrypt_decrypt_diff_keys_diff_input(input: Vec<u8>) -> 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<u8>) -> 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<u8>) -> 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
}
}
}
}
}
43 changes: 43 additions & 0 deletions src/test_framework/aead_interface.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@
#[cfg(feature = "safe_api")]
use crate::errors::UnknownCryptoError;

#[cfg(test)]
#[cfg(feature = "safe_api")]
use crate::test_framework::streamcipher_interface::TestingRandom;

#[allow(clippy::too_many_arguments)]
#[cfg(feature = "safe_api")]
/// Test runner for AEADs.
Expand Down Expand Up @@ -369,3 +373,42 @@ fn none_or_empty_some_aad_same_result<Sealer, Opener, Key, Nonce>(
.is_ok());
assert!(opener(&key, &nonce, &dst_out_ct_some_empty, None, &mut dst_out_pt).is_ok());
}

#[cfg(test)]
#[cfg(feature = "safe_api")]
/// Test that sealing and opening with different secret-key/nonce yields an error.
pub fn test_diff_params_err<Sealer, Opener, Key, Nonce>(
sealer: &Sealer,
opener: &Opener,
input: &[u8],
tag_size: usize,
) where
Key: TestingRandom + PartialEq<Key>,
Nonce: TestingRandom + PartialEq<Nonce>,
Sealer: Fn(&Key, &Nonce, &[u8], Option<&[u8]>, &mut [u8]) -> Result<(), UnknownCryptoError>,
Opener: Fn(&Key, &Nonce, &[u8], Option<&[u8]>, &mut [u8]) -> Result<(), UnknownCryptoError>,
{
let mut input = input;
if input.is_empty() {
input = &[0u8; 1];
}

let sk1 = Key::gen();
let sk2 = Key::gen();
assert!(sk1 != sk2);

let n1 = Nonce::gen();
let n2 = Nonce::gen();
assert!(n1 != n2);

let mut dst_out_ct = vec![0u8; input.len() + tag_size];
let mut dst_out_pt = vec![0u8; input.len()];

// Different secret key
sealer(&sk1, &n1, input, None, &mut dst_out_ct).unwrap();
assert!(opener(&sk2, &n1, &dst_out_ct, None, &mut dst_out_pt).is_err());

// Different nonce
sealer(&sk1, &n1, input, None, &mut dst_out_ct).unwrap();
assert!(opener(&sk1, &n2, &dst_out_ct, None, &mut dst_out_pt).is_err());
}
Loading

0 comments on commit fda1c14

Please sign in to comment.