use super::*;
use hex;
use std::fs::File;
use std::io::{BufRead, BufReader};
use std::path::{Path, PathBuf};
fn vectors_dir() -> PathBuf {
Path::new(env!("CARGO_MANIFEST_DIR"))
.join("..") .join("..") .join("tests")
.join("src")
.join("vectors")
.join("legacy_rsp")
.join("sha3")
}
#[test]
fn test_sha3_256_empty() {
let expected = "a7ffc6f8bf1ed76651c14756a061d662f580ff4de43b49fa82d80a4b80f8434a";
let hash = Sha3_256::digest(&[]).unwrap();
assert_eq!(hex::encode(&hash), expected);
}
#[test]
fn test_sha3_224_empty() {
let expected = "6b4e03423667dbb73b6e15454f0eb1abd4597f9a1b078e3f5b5a6bc7";
let hash = Sha3_224::digest(&[]).unwrap();
assert_eq!(hex::encode(&hash), expected);
}
#[test]
fn test_sha3_384_empty() {
let expected = "0c63a75b845e4f7d01107d852e4c2485c51a50aaaa94fc61995e71bbee983a2ac3713831264adb47fb6bd1e058d5f004";
let hash = Sha3_384::digest(&[]).unwrap();
assert_eq!(hex::encode(&hash), expected);
}
#[test]
fn test_sha3_512_empty() {
let expected = "a69f73cca23a9ac5c8b567dc185a756e97c982164fe25859e0d1dcc1475c80a615b2123af1f5f94c11e3e9402c3ac558f500199d95b6d3e301758586281dcd26";
let hash = Sha3_512::digest(&[]).unwrap();
assert_eq!(hex::encode(&hash), expected);
}
#[derive(Debug)]
struct Sha3TestVector {
len: usize, msg: String, md: String, }
fn parse_sha3_test_file(filepath: &str) -> Vec<Sha3TestVector> {
let file = File::open(Path::new(filepath)).expect("Failed to open test vector file");
let reader = BufReader::new(file);
let mut lines = reader.lines();
let mut test_vectors = Vec::new();
let mut current_vector: Option<Sha3TestVector> = None;
while let Some(Ok(line)) = lines.next() {
let line = line.trim();
if line.is_empty() || line.starts_with('#') {
continue;
}
if let Some(len_str) = line.strip_prefix("Len = ") {
if let Some(vector) = current_vector.take() {
test_vectors.push(vector);
}
let len = len_str.parse::<usize>().unwrap();
current_vector = Some(Sha3TestVector {
len,
msg: String::new(),
md: String::new(),
});
} else if let Some(ref mut vector) = current_vector {
if let Some(msg) = line.strip_prefix("Msg = ") {
vector.msg = msg.to_string();
} else if let Some(md) = line.strip_prefix("MD = ") {
vector.md = md.to_string();
}
}
}
if let Some(vector) = current_vector {
test_vectors.push(vector);
}
test_vectors
}
fn run_sha3_tests<H: HashFunction>(filepath: &str, name: &str) {
let test_vectors = parse_sha3_test_file(filepath);
for (i, test) in test_vectors.iter().enumerate() {
if test.len == 0 {
let hash = H::digest(&[]).unwrap();
let expected = hex::decode(&test.md)
.unwrap_or_else(|_| panic!("Invalid hex in expected result {}: {}", i, test.md));
let hash_vec = hash.as_ref().to_vec();
assert_eq!(
hash_vec,
expected,
"{} test case {} failed. Input: empty, Expected: {}, Got: {}",
name,
i,
test.md,
hex::encode(&hash)
);
continue;
}
let msg = if test.msg.is_empty() {
Vec::new()
} else {
hex::decode(&test.msg)
.unwrap_or_else(|_| panic!("Invalid hex in test vector {}: {}", i, test.msg))
};
if test.len % 8 != 0 {
let bytes = test.len / 8;
let bits = test.len % 8;
if bytes < msg.len() {
let mut truncated_msg = msg[..bytes].to_vec();
if bits > 0 {
let mask = (1u8 << bits) - 1;
truncated_msg.push(msg[bytes] & mask);
}
let hash = H::digest(&truncated_msg).unwrap();
let expected = hex::decode(&test.md).unwrap_or_else(|_| {
panic!("Invalid hex in expected result {}: {}", i, test.md)
});
let hash_vec = hash.as_ref().to_vec();
assert_eq!(
hash_vec,
expected,
"{} test case {} failed. Input: {}, Expected: {}, Got: {}",
name,
i,
test.msg,
test.md,
hex::encode(&hash)
);
continue;
}
}
let hash = H::digest(&msg).unwrap();
let expected = hex::decode(&test.md)
.unwrap_or_else(|_| panic!("Invalid hex in expected result {}: {}", i, test.md));
let hash_vec = hash.as_ref().to_vec();
assert_eq!(
hash_vec,
expected,
"{} test case {} failed. Input: {}, Expected: {}, Got: {}",
name,
i,
test.msg,
test.md,
hex::encode(&hash)
);
}
}
#[test]
fn test_sha3_nist_short_vectors() {
let dir = vectors_dir();
let sha3_224_path = dir.join("SHA3_224ShortMsg.rsp");
let sha3_256_path = dir.join("SHA3_256ShortMsg.rsp");
let sha3_384_path = dir.join("SHA3_384ShortMsg.rsp");
let sha3_512_path = dir.join("SHA3_512ShortMsg.rsp");
for path in [
&sha3_224_path,
&sha3_256_path,
&sha3_384_path,
&sha3_512_path,
] {
assert!(
path.exists(),
"Test vector file not found: {}",
path.display()
);
}
run_sha3_tests::<Sha3_224>(sha3_224_path.to_str().unwrap(), "SHA3-224");
run_sha3_tests::<Sha3_256>(sha3_256_path.to_str().unwrap(), "SHA3-256");
run_sha3_tests::<Sha3_384>(sha3_384_path.to_str().unwrap(), "SHA3-384");
run_sha3_tests::<Sha3_512>(sha3_512_path.to_str().unwrap(), "SHA3-512");
}
#[test]
fn test_sha3_nist_long_vectors() {
let dir = vectors_dir();
let sha3_224_path = dir.join("SHA3_224LongMsg.rsp");
let sha3_256_path = dir.join("SHA3_256LongMsg.rsp");
let sha3_384_path = dir.join("SHA3_384LongMsg.rsp");
let sha3_512_path = dir.join("SHA3_512LongMsg.rsp");
for path in [
&sha3_224_path,
&sha3_256_path,
&sha3_384_path,
&sha3_512_path,
] {
assert!(
path.exists(),
"Test vector file not found: {}",
path.display()
);
}
run_sha3_tests::<Sha3_224>(sha3_224_path.to_str().unwrap(), "SHA3-224");
run_sha3_tests::<Sha3_256>(sha3_256_path.to_str().unwrap(), "SHA3-256");
run_sha3_tests::<Sha3_384>(sha3_384_path.to_str().unwrap(), "SHA3-384");
run_sha3_tests::<Sha3_512>(sha3_512_path.to_str().unwrap(), "SHA3-512");
}
#[derive(Debug)]
struct Sha3MonteTestVector {
seed: String, count: usize, expected: String, }
fn parse_sha3_monte_test_file(filepath: &str) -> Vec<Sha3MonteTestVector> {
let file = File::open(Path::new(filepath)).expect("Failed to open test vector file");
let reader = BufReader::new(file);
let mut lines = reader.lines();
let mut test_vectors = Vec::new();
let mut current_seed = String::new();
let mut current_expected = String::new();
let mut count = 0;
while let Some(Ok(line)) = lines.next() {
let line = line.trim();
if line.is_empty() || line.starts_with('#') {
continue;
}
if let Some(seed) = line.strip_prefix("Seed = ") {
if !current_seed.is_empty() && !current_expected.is_empty() {
test_vectors.push(Sha3MonteTestVector {
seed: current_seed.clone(),
count,
expected: current_expected.clone(),
});
}
current_seed = seed.to_string();
current_expected = String::new();
count = 0;
} else if let Some(count_str) = line.strip_prefix("COUNT = ") {
count = count_str.trim().parse::<usize>().unwrap_or(0);
} else if line.starts_with("MD = ") && count == 100 {
current_expected = line.strip_prefix("MD = ").unwrap().to_string();
}
}
if !current_seed.is_empty() && !current_expected.is_empty() {
test_vectors.push(Sha3MonteTestVector {
seed: current_seed,
count,
expected: current_expected,
});
}
test_vectors
}
fn run_sha3_monte_tests<H: HashFunction>(filepath: &str, name: &str) {
let test_vectors = parse_sha3_monte_test_file(filepath);
for (i, test) in test_vectors.iter().enumerate() {
let seed_bytes = hex::decode(&test.seed).unwrap_or_else(|_| {
panic!(
"{} Monte Carlo test {}: Invalid seed hex: {}",
name, i, test.seed
)
});
let mut md = seed_bytes.clone();
for _j in 0..=test.count {
let mut hasher = H::new();
hasher.update(&md).unwrap();
md = hasher.finalize().unwrap().as_ref().to_vec();
}
let expected = hex::decode(&test.expected).unwrap_or_else(|_| {
panic!(
"{} Monte Carlo test {}: Invalid expected hex: {}",
name, i, test.expected
)
});
assert_eq!(
hex::encode(&md),
hex::encode(&expected),
"{} Monte Carlo test case {} failed.\nExpected: {}\nGot: {}",
name,
i,
test.expected,
hex::encode(&md)
);
}
}
#[test]
fn test_sha3_nist_monte_vectors() {
let dir = vectors_dir();
let sha3_224_path = dir.join("SHA3_224Monte.rsp");
let sha3_256_path = dir.join("SHA3_256Monte.rsp");
let sha3_384_path = dir.join("SHA3_384Monte.rsp");
let sha3_512_path = dir.join("SHA3_512Monte.rsp");
for path in [
&sha3_224_path,
&sha3_256_path,
&sha3_384_path,
&sha3_512_path,
] {
assert!(
path.exists(),
"Test vector file not found: {}",
path.display()
);
}
run_sha3_monte_tests::<Sha3_224>(sha3_224_path.to_str().unwrap(), "SHA3-224");
run_sha3_monte_tests::<Sha3_256>(sha3_256_path.to_str().unwrap(), "SHA3-256");
run_sha3_monte_tests::<Sha3_384>(sha3_384_path.to_str().unwrap(), "SHA3-384");
run_sha3_monte_tests::<Sha3_512>(sha3_512_path.to_str().unwrap(), "SHA3-512");
}