use super::*;
use hex;
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("sha2")
}
#[test]
fn test_sha256_empty() {
let expected = "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855";
let hash = Sha256::digest(&[]).unwrap();
assert_eq!(hex::encode(hash.as_ref()), expected);
}
#[test]
fn test_sha256_abc() {
let expected = "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad";
let hash = Sha256::digest(b"abc").unwrap();
assert_eq!(hex::encode(hash.as_ref()), expected);
}
#[test]
fn test_sha256_long() {
let expected = "248d6a61d20638b8e5c026930c3e6039a33ce45964ff2167f6ecedd419db06c1";
let hash = Sha256::digest(b"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq").unwrap();
assert_eq!(hex::encode(hash.as_ref()), expected);
}
#[test]
fn test_sha224_empty() {
let expected = "d14a028c2a3a2bc9476102bb288234c415a2b01f828ea62ac5b3e42f";
let hash = Sha224::digest(&[]).unwrap();
assert_eq!(hex::encode(hash.as_ref()), expected);
}
#[test]
fn test_sha512_empty() {
let expected = "cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e";
let hash = Sha512::digest(&[]).unwrap();
assert_eq!(hex::encode(hash.as_ref()), expected);
}
#[test]
fn test_sha384_empty() {
let expected = "38b060a751ac96384cd9327eb1b1e36a21fdb71114be07434c0cc7bf63f6e1da274edebfe76f65fbd51ad2f14898b95b";
let hash = Sha384::digest(&[]).unwrap();
assert_eq!(hex::encode(hash.as_ref()), expected);
}
#[test]
fn test_sha512_224_empty() {
let expected = "6ed0dd02806fa89e25de060c19d3ac86cabb87d6a0ddd05c333b84f4";
let hash = Sha512_224::digest(&[]).unwrap();
assert_eq!(hex::encode(hash.as_ref()), expected);
}
#[test]
fn test_sha512_256_empty() {
let expected = "c672b8d1ef56ed28ab87c3622c5114069bdd3ad7b8f9737498d0c01ecef0967a";
let hash = Sha512_256::digest(&[]).unwrap();
assert_eq!(hex::encode(hash.as_ref()), expected);
}
#[test]
fn test_sha512_224_abc() {
let expected = "4634270f707b6a54daae7530460842e20e37ed265ceee9a43e8924aa";
let hash = Sha512_224::digest(b"abc").unwrap();
assert_eq!(hex::encode(hash.as_ref()), expected);
}
#[test]
fn test_sha512_256_abc() {
let expected = "53048e2681941ef99b2e29b76b4c7dabe4c2d0c634fc6d46e0e2f13107e7af23";
let hash = Sha512_256::digest(b"abc").unwrap();
assert_eq!(hex::encode(hash.as_ref()), expected);
}
#[test]
fn test_sha2_nist_short_vectors() {
let dir = vectors_dir();
let sha224_path = dir.join("SHA224ShortMsg.rsp");
let sha256_path = dir.join("SHA256ShortMsg.rsp");
let sha384_path = dir.join("SHA384ShortMsg.rsp");
let sha512_path = dir.join("SHA512ShortMsg.rsp");
for path in [&sha224_path, &sha256_path, &sha384_path, &sha512_path] {
assert!(
path.exists(),
"Test vector file not found: {}",
path.display()
);
}
run_sha2_tests::<Sha224>(sha224_path.to_str().unwrap(), "SHA-224");
run_sha2_tests::<Sha256>(sha256_path.to_str().unwrap(), "SHA-256");
run_sha2_tests::<Sha384>(sha384_path.to_str().unwrap(), "SHA-384");
run_sha2_tests::<Sha512>(sha512_path.to_str().unwrap(), "SHA-512");
}
#[test]
fn test_sha2_nist_long_vectors() {
let dir = vectors_dir();
let sha224_path = dir.join("SHA224LongMsg.rsp");
let sha256_path = dir.join("SHA256LongMsg.rsp");
let sha384_path = dir.join("SHA384LongMsg.rsp");
let sha512_path = dir.join("SHA512LongMsg.rsp");
for path in [&sha224_path, &sha256_path, &sha384_path, &sha512_path] {
assert!(
path.exists(),
"Test vector file not found: {}",
path.display()
);
}
run_sha2_tests::<Sha224>(sha224_path.to_str().unwrap(), "SHA-224");
run_sha2_tests::<Sha256>(sha256_path.to_str().unwrap(), "SHA-256");
run_sha2_tests::<Sha384>(sha384_path.to_str().unwrap(), "SHA-384");
run_sha2_tests::<Sha512>(sha512_path.to_str().unwrap(), "SHA-512");
}
#[derive(Debug)]
struct Sha2TestVector {
len: usize, msg: String, md: String, }
fn parse_sha2_test_file(filepath: &str) -> Vec<Sha2TestVector> {
use std::fs::File;
use std::io::{BufRead, BufReader};
use std::path::Path;
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<Sha2TestVector> = 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(Sha2TestVector {
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_sha2_tests<H: HashFunction>(filepath: &str, name: &str)
where
H::Output: AsRef<[u8]>,
{
let test_vectors = parse_sha2_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));
assert_eq!(
hash.as_ref(),
expected.as_slice(),
"{} test case {} failed. Input: empty, Expected: {}, Got: {}",
name,
i,
test.md,
hex::encode(hash.as_ref())
);
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)
});
assert_eq!(
hash.as_ref(),
expected.as_slice(),
"{} test case {} failed. Input: {}, Expected: {}, Got: {}",
name,
i,
test.msg,
test.md,
hex::encode(hash.as_ref())
);
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));
assert_eq!(
hash.as_ref(),
expected.as_slice(),
"{} test case {} failed. Input: {}, Expected: {}, Got: {}",
name,
i,
test.msg,
test.md,
hex::encode(hash.as_ref())
);
}
}
#[test]
fn test_sha2_nist_monte_vectors() {
let dir = vectors_dir();
let sha224_path = dir.join("SHA224Monte.rsp");
let sha256_path = dir.join("SHA256Monte.rsp");
let sha384_path = dir.join("SHA384Monte.rsp");
let sha512_path = dir.join("SHA512Monte.rsp");
for path in [&sha224_path, &sha256_path, &sha384_path, &sha512_path] {
assert!(
path.exists(),
"Test vector file not found: {}",
path.display()
);
}
run_sha2_monte_tests::<Sha224>(sha224_path.to_str().unwrap(), "SHA-224");
run_sha2_monte_tests::<Sha256>(sha256_path.to_str().unwrap(), "SHA-256");
run_sha2_monte_tests::<Sha384>(sha384_path.to_str().unwrap(), "SHA-384");
run_sha2_monte_tests::<Sha512>(sha512_path.to_str().unwrap(), "SHA-512");
}
#[derive(Debug)]
struct Sha2MonteTestVector {
seed: String, count: usize, expected: String, }
fn parse_sha2_monte_test_file(filepath: &str) -> Vec<Sha2MonteTestVector> {
use std::fs::File;
use std::io::{BufRead, BufReader};
use std::path::Path;
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(Sha2MonteTestVector {
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 == 1000 {
current_expected = line.strip_prefix("MD = ").unwrap().to_string();
}
}
if !current_seed.is_empty() && !current_expected.is_empty() {
test_vectors.push(Sha2MonteTestVector {
seed: current_seed,
count,
expected: current_expected,
});
}
test_vectors
}
fn run_sha2_monte_tests<H: HashFunction>(filepath: &str, name: &str)
where
H::Output: AsRef<[u8]>,
{
let test_vectors = parse_sha2_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 = Vec::new();
md.push(seed_bytes.clone());
let digest1 = H::digest(&seed_bytes).unwrap();
md.push(digest1.as_ref().to_vec());
let digest2 = H::digest(md[1].as_slice()).unwrap();
md.push(digest2.as_ref().to_vec());
for _j in 3..=test.count {
let mut input = Vec::new();
input.extend_from_slice(&md[md.len() - 3]);
input.extend_from_slice(&md[md.len() - 2]);
input.extend_from_slice(&md[md.len() - 1]);
let digest = H::digest(&input).unwrap();
md.push(digest.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!(
md[test.count].as_slice(),
expected.as_slice(),
"{} Monte Carlo test case {} failed.\nExpected: {}\nGot: {}",
name,
i,
test.expected,
hex::encode(&md[test.count])
);
}
}