Skip to content

Commit

Permalink
Add NIST SHAVS tests for |ring::digest|.
Browse files Browse the repository at this point in the history
  • Loading branch information
briansmith committed Dec 1, 2015
1 parent b32546c commit 5cbbcdc
Show file tree
Hide file tree
Showing 20 changed files with 5,369 additions and 1 deletion.
114 changes: 113 additions & 1 deletion src/digest.rs
Expand Up @@ -437,8 +437,9 @@ pub mod test_util {
mod tests {
use super::super::{digest, file_test};

/// Test vectors from BoringSSL.
#[test]
fn test_digests() {
fn test_bssl() {
file_test::run("src/digest_tests.txt", |section, test_case| {
assert_eq!(section, "");
let digest_alg = test_case.consume_digest_alg("Hash").unwrap();
Expand All @@ -460,6 +461,117 @@ mod tests {
});
}

mod shavs {
use super::super::super::{digest, file_test};
use rustc_serialize::hex::ToHex;

macro_rules! shavs_tests {
( $algorithm_name:ident ) => {
#[allow(non_snake_case)]
mod $algorithm_name {
use super::{run_known_answer_test, run_monte_carlo_test};
use super::super::super::super::{digest};

#[test]
fn short_msg_known_answer_test() {
run_known_answer_test(
&digest::$algorithm_name,
&format!("third-party/NIST/SHAVS/{}ShortMsg.rsp",
stringify!($algorithm_name)));
}

#[test]
fn long_msg_known_answer_test() {
run_known_answer_test(
&digest::$algorithm_name,
&format!("third-party/NIST/SHAVS/{}LongMsg.rsp",
stringify!($algorithm_name)));
}

#[test]
fn monte_carlo_test() {
run_monte_carlo_test(
&digest::$algorithm_name,
&format!("third-party/NIST/SHAVS/{}Monte.rsp",
stringify!($algorithm_name)));
}
}
}
}

fn run_known_answer_test(digest_alg: &'static digest::Algorithm,
file_name: &str, ) {
let section_name = &format!("L = {}", digest_alg.output_len);
file_test::run(file_name, |section, test_case| {
assert_eq!(section_name, section);
let len_bits = test_case.consume_usize("Len");

let mut msg = test_case.consume_bytes("Msg");
// The "msg" field contains the dummy value "00" when the
// length is zero.
if len_bits == 0 {
assert_eq!(msg, &[0u8]);
msg.truncate(0);
}

assert_eq!(msg.len() * 8, len_bits);
let expected = test_case.consume_bytes("MD");
let actual = digest::digest(digest_alg, &msg);
assert_eq!(&expected, &actual.as_ref());
});
}

fn run_monte_carlo_test(digest_alg: &'static digest::Algorithm,
file_name: &str) {
let section_name = &format!("L = {}", digest_alg.output_len);

let mut expected_count: isize = -1;
let mut seed = Vec::with_capacity(digest_alg.output_len);

file_test::run_mut(file_name, &mut |section, test_case| {
assert_eq!(section_name, section);

if expected_count == -1 {
seed.extend(test_case.consume_bytes("Seed"));
expected_count = 0;
return;
}

assert!(expected_count >= 0);
let actual_count = test_case.consume_usize("COUNT");
assert_eq!(expected_count as usize, actual_count);
expected_count += 1;

let expected_md = test_case.consume_bytes("MD");

let mut mds = Vec::with_capacity(1000);
mds.push(seed.clone());
mds.push(seed.clone());
mds.push(seed.clone());
for i in 3..1003 {
let mut ctx = digest::Context::new(digest_alg);
ctx.update(&mds[i - 3]);
ctx.update(&mds[i - 2]);
ctx.update(&mds[i - 1]);
let md_i = ctx.finish();
mds.push(Vec::from(md_i.as_ref()));
}
assert_eq!(1003, mds.len());
let md_j = mds.last().unwrap();
println!("{}\n{}", expected_md.to_hex(), md_j.to_hex());
assert_eq!(&expected_md, md_j);
seed = md_j.clone();
});

assert_eq!(expected_count, 100);
}

shavs_tests!(SHA1);
shavs_tests!(SHA256);
shavs_tests!(SHA384);
shavs_tests!(SHA512);
}

/// Test some ways in which `Context::update` and/or `Context::finish`
/// could go wrong by testing every combination of updating three inputs
/// that vary from zero bytes to twice the size of the block length.
Expand Down
24 changes: 24 additions & 0 deletions src/file_test.rs
Expand Up @@ -94,6 +94,30 @@ pub fn run<F>(test_data_relative_file_path: &str, f: F)
}
}

pub fn run_mut<F>(test_data_relative_file_path: &str, f: &mut F)
where F: FnMut(&str, &mut TestCase) {
let path = std::path::PathBuf::from(test_data_relative_file_path);
let file = std::fs::File::open(path).unwrap();
let mut lines = std::io::BufReader::new(&file).lines();

let mut current_section = String::from("");

loop {
match parse_test_case(&mut current_section, &mut lines) {
Some(ref mut test_case) => {
f(&current_section, test_case);

// Make sure all the attributes in the test case were consumed.
assert!(test_case.attributes.is_empty());
},

None => {
break;
}
}
}
}

type FileLines<'a> = std::io::Lines<std::io::BufReader<&'a std::fs::File>>;

fn parse_test_case(current_section: &mut String,
Expand Down
3 changes: 3 additions & 0 deletions third-party/NIST/.gitattributes
@@ -0,0 +1,3 @@
# Preserve the line endings on the NIST test vectors so that we can use
# sha256sum and friends to verify that they haven't been tampered with.
*.rsp eol=crlf
9 changes: 9 additions & 0 deletions third-party/NIST/README.md
@@ -0,0 +1,9 @@
# NIST Test Vectors

[SHAVS](SHAVS) contains the [SHA Test Vectors for Hashing Byte-Oriented
Messages](http://csrc.nist.gov/groups/STM/cavp/documents/shs/shabytetestvectors.zip)
from [Secure Hash Algorithm Validation System
(SHAVS)](http://csrc.nist.gov/groups/STM/cavp/#03).

[sha256sums.txt](sha256sums.txt) contains the SHA-256 checksums for the ZIP
files from which the test vectors were extracted.

0 comments on commit 5cbbcdc

Please sign in to comment.