Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add new Wycheproof test vectors for (X)ChaCha20Poly1305, HMAC-SHA512 and HKDF-HMAC-SHA512 #116

Merged
merged 7 commits into from
Jan 28, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/hazardous/kdf/hkdf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ pub fn verify(
info: Option<&[u8]>,
dst_out: &mut [u8],
) -> Result<(), UnknownCryptoError> {
expand(&extract(salt, ikm)?, info, dst_out)?;
derive_key(salt, ikm, info, dst_out)?;
util::secure_cmp(&dst_out, expected)
}

Expand Down
82 changes: 42 additions & 40 deletions tests/aead/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ pub mod boringssl_xchacha20_poly1305;
pub mod other_xchacha20_poly1305;
pub mod pynacl_streaming_aead;
pub mod rfc_chacha20_poly1305;
pub mod wycheproof_chacha20_poly1305;
pub mod wycheproof_aead;

extern crate orion;
use self::{
Expand Down Expand Up @@ -102,6 +102,7 @@ fn wycheproof_test_runner(
output: &[u8],
result: bool,
tcid: u64,
is_ietf: bool,
) -> Result<(), UnknownCryptoError> {
// Leave test vectors out that have empty input/output and are otherwise valid
// since orion does not accept this. This will be test cases with "tcId" = 2, 3.
Expand All @@ -115,56 +116,57 @@ fn wycheproof_test_runner(
let mut dst_pt_out = vec![0u8; input.len()];

if result {
aead::chacha20poly1305::seal(
&SecretKey::from_slice(&key)?,
&chacha20poly1305::Nonce::from_slice(&nonce)?,
input,
Some(aad),
&mut dst_ct_out,
)?;

aead::chacha20poly1305::open(
&SecretKey::from_slice(&key)?,
&chacha20poly1305::Nonce::from_slice(&nonce)?,
&dst_ct_out,
Some(aad),
&mut dst_pt_out,
)?;
let key = SecretKey::from_slice(&key)?;

if is_ietf {
let nonce = chacha20poly1305::Nonce::from_slice(&nonce)?;
chacha20poly1305::seal(&key, &nonce, input, Some(aad), &mut dst_ct_out)?;
chacha20poly1305::open(&key, &nonce, &dst_ct_out, Some(aad), &mut dst_pt_out)?;
} else {
let nonce = xchacha20poly1305::Nonce::from_slice(&nonce)?;
xchacha20poly1305::seal(&key, &nonce, input, Some(aad), &mut dst_ct_out)?;
xchacha20poly1305::open(&key, &nonce, &dst_ct_out, Some(aad), &mut dst_pt_out)?;
}

assert!(dst_ct_out[..input.len()].as_ref() == output);
assert!(dst_ct_out[input.len()..].as_ref() == tag);
assert!(dst_pt_out[..].as_ref() == input);
} else {
let new_key = SecretKey::from_slice(&key);
let new_nonce = chacha20poly1305::Nonce::from_slice(&nonce);

// Detecting cases where there is invalid size of nonce and/or key
if new_key.is_err() || new_nonce.is_err() {
return Ok(());
// Tests that run here have a "invalid" flag set
let key = match SecretKey::from_slice(&key) {
Ok(k) => k,
Err(UnknownCryptoError) => return Ok(()), // Invalid key size test
};

// Save the return values from sealing/opening operations
// to match for errors.
let sealres: Result<(), UnknownCryptoError>;
let openres: Result<(), UnknownCryptoError>;

if is_ietf {
let nonce = match chacha20poly1305::Nonce::from_slice(&nonce) {
Ok(n) => n,
Err(UnknownCryptoError) => return Ok(()), // Invalid nonce size test
};

sealres = chacha20poly1305::seal(&key, &nonce, input, Some(aad), &mut dst_ct_out);
openres = chacha20poly1305::open(&key, &nonce, &dst_ct_out, Some(aad), &mut dst_pt_out);
} else {
let nonce = match xchacha20poly1305::Nonce::from_slice(&nonce) {
Ok(n) => n,
Err(UnknownCryptoError) => return Ok(()), // Invalid nonce size test
};

sealres = xchacha20poly1305::seal(&key, &nonce, input, Some(aad), &mut dst_ct_out);
openres =
xchacha20poly1305::open(&key, &nonce, &dst_ct_out, Some(aad), &mut dst_pt_out);
}

let encryption = aead::chacha20poly1305::seal(
&new_key.unwrap(),
&new_nonce.unwrap(),
input,
Some(aad),
&mut dst_ct_out,
);
// Because of the early return, there is no need to check for invalid size of
// nonce and/or key
let decryption = aead::chacha20poly1305::open(
&SecretKey::from_slice(&key)?,
&chacha20poly1305::Nonce::from_slice(&nonce)?,
&dst_ct_out,
Some(aad),
&mut dst_pt_out,
);

// Test case results may be invalid, but this does not mean both seal() and
// open() fails. We use a match arm to allow failure combinations, with
// possible successful calls, but never a combination of two successful
// calls where the output matches the expected values.
match (encryption, decryption) {
match (sealres, openres) {
(Ok(_), Err(_)) => (),
(Err(_), Ok(_)) => (),
(Err(_), Err(_)) => (),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,16 @@
// Testing against Google Wycheproof test vectors
// Latest commit when these test vectors were pulled: https://github.com/google/wycheproof/commit/8f2cba4d3fe693aa312fed6939ef5454952d830d
// Latest commit when these test vectors were pulled: https://github.com/google/wycheproof/commit/2196000605e45d91097147c9c71f26b72af58003
extern crate hex;
extern crate serde_json;

use self::hex::decode;

use self::serde_json::{Deserializer, Value};
use crate::aead::wycheproof_test_runner as chacha20_poly1305_test_runner;
use crate::aead::wycheproof_test_runner;
use std::{fs::File, io::BufReader};

#[test]
fn test_wycheproof() {
let file = File::open("./tests/test_data/original/Wycheproof_ChaCha20_Poly1305.json").unwrap();
fn wycheproof_runner(path: &str, is_ietf: bool) {
let file = File::open(path).unwrap();
let reader = BufReader::new(file);
let stream = Deserializer::from_reader(reader).into_iter::<Value>();

Expand Down Expand Up @@ -40,9 +39,10 @@ fn test_wycheproof() {
_ => panic!("Unrecognized result detected!"),
};
let tcid = test_case.get("tcId").unwrap().as_u64().unwrap();
println!("tcId: {}, is_ietf: {}", tcid, is_ietf);

chacha20_poly1305_test_runner(
&key, &iv, &aad, &tag, &msg, &ct, result, tcid,
wycheproof_test_runner(
&key, &iv, &aad, &tag, &msg, &ct, result, tcid, is_ietf,
)
.unwrap();
}
Expand All @@ -52,3 +52,15 @@ fn test_wycheproof() {
}
}
}

#[test]
fn test_wycheproof_aead() {
wycheproof_runner(
"./tests/test_data/original/wycheproof_chacha20_poly1305_test.json",
true,
);
wycheproof_runner(
"./tests/test_data/original/wycheproof_xchacha20_poly1305_test.json",
false,
);
}
55 changes: 25 additions & 30 deletions tests/kdf/custom_hkdf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,19 +15,18 @@ mod custom_hkdf {
let ikm = decode("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b").unwrap();
let salt = decode("000102030405060708090a0b0c").unwrap();
let info = decode("").unwrap();
let mut okm = [0u8; 32];

let expected_okm =
decode("f81b87481a18b664936daeb222f58cba0ebc55f5c85996b9f1cb396c327b70bb").unwrap();

assert!(hkdf_test_runner(
hkdf_test_runner(
None,
&expected_okm,
&salt,
&ikm,
&info,
&mut okm
));
expected_okm.len(),
true,
);
}

#[test]
Expand All @@ -40,8 +39,6 @@ mod custom_hkdf {
.unwrap();
let salt = "salt".as_bytes();
let info = "random InF\0".as_bytes();
let mut okm = [0u8; 128];

let expected_okm = decode(
"a246ef99f6a0f783fc004682508e6f288f036469788f004fcbac9414caa889fa175e746ee663914d\
678c155d510fa536f7d49b1054e85e7751d9745ea02079a78608eec9aacdd82fa9421d6223c158c71\
Expand All @@ -50,14 +47,15 @@ mod custom_hkdf {
)
.unwrap();

assert!(hkdf_test_runner(
hkdf_test_runner(
None,
&expected_okm,
&salt,
&ikm,
&info,
&mut okm
));
expected_okm.len(),
true,
);
}

#[test]
Expand All @@ -70,8 +68,6 @@ mod custom_hkdf {
)
.unwrap();
let info = decode("").unwrap();
let mut okm = [0u8; 256];

let expected_okm = decode(
"245d63179146a61ca1a25f92c38391d406bb52da4b773714fb0e43ce90\
84ce430f43e1980a8817cf0af320fb684776d81f674d2b187449d62200d3e39cb51ab7a444f7964944895\
Expand All @@ -83,23 +79,22 @@ mod custom_hkdf {
)
.unwrap();

assert!(hkdf_test_runner(
hkdf_test_runner(
None,
&expected_okm,
&salt,
&ikm,
&info,
&mut okm
));
expected_okm.len(),
true,
);
}

#[test]
fn test_case_4() {
let ikm = decode("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b").unwrap();
let salt = "s$#$#%$#SBGHWE@#W#lt".as_bytes();
let info = "random InF\0".as_bytes();
let mut okm = [0u8; 256];

let expected_okm = decode(
"e93182a8af74a1e70a6202075759bbbceb1926a18aa9f9ee31796557\
0b507cea7ef11f94d83760bb6f8a2f6031edb581c1ae43f45ead820223d34c6ffadab43d3cfaf9cd782\
Expand All @@ -110,34 +105,35 @@ mod custom_hkdf {
7e9fbf127ff88d33b984582ced74fa029b50f441e",
)
.unwrap();
assert!(hkdf_test_runner(

hkdf_test_runner(
None,
&expected_okm,
&salt,
&ikm,
&info,
&mut okm
));
expected_okm.len(),
true,
);
}

#[test]
fn test_case_5() {
let ikm = "passwordPASSWORDpassword".as_bytes();
let salt = "salt".as_bytes();
let info = decode("").unwrap();
let mut okm = [0u8; 32];

let expected_okm =
decode("1ef9dccc02d5786f0d7133da824afe212547f2d8c97e9299345db86814dcb9b8").unwrap();

assert!(hkdf_test_runner(
hkdf_test_runner(
None,
&expected_okm,
&salt,
&ikm,
&info,
&mut okm
));
expected_okm.len(),
true,
);
}

#[test]
Expand All @@ -150,17 +146,16 @@ mod custom_hkdf {
3f4f5f6f7f8f9fafbfcfdfeff",
)
.unwrap();
let mut okm = [0u8; 16];

let expected_okm = decode("8ae15623215eaaa156bad552f411c4ad").unwrap();

assert!(hkdf_test_runner(
hkdf_test_runner(
None,
&expected_okm,
&salt,
&ikm,
&info,
&mut okm
));
expected_okm.len(),
true,
);
}
}
22 changes: 15 additions & 7 deletions tests/kdf/mod.rs
Original file line number Diff line number Diff line change
@@ -1,23 +1,31 @@
pub mod custom_hkdf;
pub mod custom_pbkdf2;
pub mod other_hkdf;
pub mod wycheproof_hkdf;

extern crate orion;
use self::orion::hazardous::{kdf::hkdf::*, mac::hmac};

pub fn hkdf_test_runner(
excp_prk: Option<&[u8]>,
excp_okm: &[u8],
expected_prk: Option<&[u8]>,
expected_okm: &[u8],
salt: &[u8],
ikm: &[u8],
info: &[u8],
okm_out: &mut [u8],
) -> bool {
if excp_prk.is_some() {
okm_len: usize,
valid_result: bool,
) {
if expected_prk.is_some() {
let actual_prk = extract(salt, &ikm).unwrap();
assert!(actual_prk == hmac::Tag::from_slice(excp_prk.unwrap()).unwrap());
assert!(actual_prk == hmac::Tag::from_slice(expected_prk.unwrap()).unwrap());
}

let mut okm_out = vec![0u8; okm_len];

// verify() also runs derive_key()
verify(excp_okm, salt, ikm, Some(&info), &mut okm_out.to_vec()).is_ok()
if valid_result {
assert!(verify(expected_okm, salt, ikm, Some(&info), &mut okm_out).is_ok());
} else {
assert!(verify(expected_okm, salt, ikm, Some(&info), &mut okm_out).is_err());
}
}
Loading