#![cfg(test)]
use crate::ops::hash::{
argon2id, blake2b, blake2s, hkdf_expand, hkdf_extract, hmac_md5, hmac_sha1, hmac_sha256,
pbkdf2_sha256, sha384, sha3_256, sha3_512, siphash13,
};
use crate::ops::hash::reference::{
blake3,
hex::{bytes_to_hex, u32_words_to_hex, u64_to_hex, u64_words_to_hex},
kdf::Argon2idParams,
md5, ripemd160, sha1, sha256, sha512, xxhash,
};
#[test]
pub fn hash_kat_vectors() {
let empty = b"";
let one_zero = b"\0";
let abc = b"abc";
let one_kib_zero = vec![0u8; 1024];
assert_eq!(
u32_words_to_hex(&sha256::sha256_words(empty)),
"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
);
assert_eq!(
u32_words_to_hex(&sha256::sha256_words(one_zero)),
"6e340b9cffb37a989ca544e6bb780a2c78901d3fb33738768511a30617afa01d"
);
assert_eq!(
u32_words_to_hex(&sha256::sha256_words(abc)),
"ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad"
);
assert_eq!(
u32_words_to_hex(&sha256::sha256_words(&one_kib_zero)),
"5f70bf18a086007016e948b04aed3b82103a36bea41755b6cddfaf10ace3c6ef"
);
assert_eq!(
u32_words_to_hex(&sha1::sha1_words(empty)),
"da39a3ee5e6b4b0d3255bfef95601890afd80709"
);
assert_eq!(
u32_words_to_hex(&sha1::sha1_words(one_zero)),
"5ba93c9db0cff93f52b521d7420e43f6eda2784f"
);
assert_eq!(
u32_words_to_hex(&sha1::sha1_words(abc)),
"a9993e364706816aba3e25717850c26c9cd0d89d"
);
assert_eq!(
u32_words_to_hex(&sha1::sha1_words(&one_kib_zero)),
"60cacbf3d72e1e7834203da608037b1bf83b40e8"
);
assert_eq!(
u32_words_to_hex(&md5::md5_words(empty)),
"d41d8cd98f00b204e9800998ecf8427e"
);
assert_eq!(
u32_words_to_hex(&md5::md5_words(one_zero)),
"93b885adfe0da089cdf634904fd59f71"
);
assert_eq!(
u32_words_to_hex(&md5::md5_words(abc)),
"900150983cd24fb0d6963f7d28e17f72"
);
assert_eq!(
u32_words_to_hex(&md5::md5_words(&one_kib_zero)),
"0f343b0931126a20f133d67c2b018a3b"
);
assert_eq!(u64_to_hex(xxhash::xxhash64(empty)), "ef46db3751d8e999");
assert_eq!(u64_to_hex(xxhash::xxhash64(b"a")), "d24ec4f1a98c6e5b");
assert_eq!(u64_to_hex(xxhash::xxhash64(abc)), "44bc2cf5ad770999");
assert_eq!(
u64_to_hex(xxhash::xxhash64(&one_kib_zero)),
"27742888f085accd"
);
assert_eq!(u64_to_hex(xxhash::xxhash3_64(empty)), "2d06800538d394c2");
assert_eq!(u64_to_hex(xxhash::xxhash3_64(b"a")), "e6c632b61e964e1f");
assert_eq!(u64_to_hex(xxhash::xxhash3_64(abc)), "78af5f94892f3950");
assert_eq!(
u64_to_hex(xxhash::xxhash3_64(&one_kib_zero)),
"de5f15ab6daf7941"
);
assert_eq!(
u32_words_to_hex(&hmac_sha256::hmac_sha256(empty, empty)),
"b613679a0814d9ec772f95d778c35fc5ff1697c493715653c6c712144292c5ad"
);
assert_eq!(
u32_words_to_hex(&hmac_sha256::hmac_sha256(&[0x0b; 20], b"Hi There")),
"b0344c61d8db38535ca8afceaf0bf12b881dc200c9833da726e9376c2e32cff7"
);
assert_eq!(
u32_words_to_hex(&hmac_sha256::hmac_sha256(
b"Jefe",
b"what do ya want for nothing?"
)),
"5bdcc146bf60754e6a042426089575c75a003f089d2739839dec58b964ec3843"
);
assert_eq!(
u32_words_to_hex(&hmac_sha256::hmac_sha256(&one_kib_zero, &one_kib_zero)),
"cf12883e11051a30615126e6fe9c00c4256c6d5709a55b94f975a56d64d77e1f"
);
let key_131 = vec![0xaa; 131];
assert_eq!(
u32_words_to_hex(&hmac_sha256::hmac_sha256(
&key_131,
b"Test Using Larger Than Block-Size Key - Hash Key First",
)),
"60e431591ee0b67f0d8a26aacbf5b77f8e0bc6213728c5140546040f0ee37f54",
"RFC 4231 Test Case 6",
);
assert_eq!(
u32_words_to_hex(&hmac_sha256::hmac_sha256(
&key_131,
b"This is a test using a larger than block-size key and \
a larger than block-size data. The key needs to be hashed \
before being used by the HMAC algorithm.",
)),
"9b09ffa71b942fcb27635fbcd5b0e944bfdc63644f0713938a7f51535c3a35e2",
"RFC 4231 Test Case 7",
);
let key_64 = vec![0x0b; 64];
assert_eq!(
u32_words_to_hex(&hmac_sha256::hmac_sha256(&key_64, b"Hi There")),
"21cd586aeca0579d99a1c938127c92525a371f807bc5ba6eb78bc825bd4f2be3",
);
}
#[test]
pub fn wave_b_hash_kat_vectors() {
assert_eq!(
u32_words_to_hex(&blake2s::blake2s(b"")),
"69217a3079908094e11121d042354a7c1f55b6482ca1a51e1b250dfd1ed0eef9"
);
assert_eq!(
u64_words_to_hex(&blake2b::blake2b(b"")),
"786a02f742015903c6c6fd852552d272912f4740e15847618a86e217f71f5419d25e1031afee585313896444934eb04b903a685b1448b755d56f701afe9be2ce"
);
assert_eq!(
u32_words_to_hex(&blake3::blake3_words(b"")),
"af1349b9f5f9a1a6a0404dea36dcc9499bcb25c9adc112b7cc9a93cae41f3262"
);
assert_eq!(
u32_words_to_hex(&blake3::blake3_words(b"abc")),
"6437b3ac38465133ffb63b75273a8db548c558465d79db03fd359c6cd5bd9d85"
);
assert_eq!(
u32_words_to_hex(&blake3::blake3_words(&vec![0u8; 1024])),
"d6fd9de5bccf223f523b316c9cd1cf9a9d87ea42473d68e011dad13f09bf8917"
);
assert_eq!(
u32_words_to_hex(&blake3::blake3_words(&vec![b'a'; 1024 * 1024])),
"b5358909f8bed53f55bf9324e290e9a5a585de8b0239d18040e9d3b0c7e8f9cf"
);
assert_eq!(
u64_words_to_hex(&sha384::sha384(b"abc")),
"cb00753f45a35e8bb5a03d699ac65007272c32ab0eded1631a8b605a43ff5bed8086072ba1e7cc2358baeca134c825a7"
);
assert_eq!(
u64_words_to_hex(&sha512::sha512_words(b"abc")),
"ddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a2192992a274fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f"
);
assert_eq!(
u32_words_to_hex(&sha3_256::sha3_256(b"abc")),
"3a985da74fe225b2045c172d6bd390bd855f086e3e9d525b46bfe24511431532"
);
assert_eq!(
u64_words_to_hex(&sha3_512::sha3_512(b"abc")),
"b751850b1a57168a5693cd924b6b096e08f621827444f70d884f5d0240d2712e10e116e9192af3c91a7ec57647e3934057340b4cf408d5a56592f8274eec53f0"
);
assert_eq!(
u32_words_to_hex(&ripemd160::ripemd160_words(b"abc")),
"8eb208f7e05d987a9b044a8e98c6b087f15a0bfc"
);
}
#[test]
pub fn sha256_block_boundary_kat_vectors() {
for (len, expected) in [
(
55usize,
"9f4390f8d30c2dd92ec9f095b65e2b9ae9b0a925a5258e241c9f1e910f734318",
),
(
56,
"b35439a4ac6f0948b6d6f9e3c6af0f5f590ce20f1bde7090ef7970686ec6738a",
),
(
63,
"7d3e74a05d7db15bce4ad9ec0658ea98e3f06eeecf16b4c6fff2da457ddc2f34",
),
(
64,
"ffe054fe7ae0cb6dc65c3af9b61d5209f439851db43d0ba5997337df154668eb",
),
(
65,
"635361c48bb9eab14198e76ea8ab7f1a41685d6ad62aa9146d301d4f17eb0ae0",
),
(
127,
"c57e9278af78fa3cab38667bef4ce29d783787a2f731d4e12200270f0c32320a",
),
(
128,
"6836cf13bac400e9105071cd6af47084dfacad4e5e302c94bfed24e013afb73e",
),
(
129,
"c12cb024a2e5551cca0e08fce8f1c5e314555cc3fef6329ee994a3db752166ae",
),
] {
assert_eq!(
u32_words_to_hex(&sha256::sha256_words(&vec![b'a'; len])),
expected,
"SHA-256 digest mismatch at {len} bytes"
);
}
}
#[test]
pub fn wave_b_hmac_and_kdf_kat_vectors() -> Result<(), String> {
assert_eq!(
u32_words_to_hex(&hmac_md5::hmac_md5(&[0x0b; 16], b"Hi There")),
"9294727a3638bb1c13f48ef8158bfc9d"
);
assert_eq!(
u32_words_to_hex(&hmac_sha1::hmac_sha1(&[0x0b; 20], b"Hi There")),
"b617318655057264e28bc0b6fb378c8ef146be00"
);
let salt = hex_bytes("000102030405060708090a0b0c")?;
let ikm = hex_bytes("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b")?;
let info = hex_bytes("f0f1f2f3f4f5f6f7f8f9")?;
let prk = hkdf_extract::hkdf_extract(&salt, &ikm);
assert_eq!(
u32_words_to_hex(&prk),
"077709362c2e32df0ddc3f0dc47bba6390b6c73bb50f9c3122ec844ad7c2b3e5"
);
let prk_bytes = hex_bytes(&u32_words_to_hex(&prk))?;
let okm = hkdf_expand::hkdf_expand(&prk_bytes, &info, 42)?;
assert_eq!(
bytes_to_hex(&okm),
"3cb25f25faacd57a90434f64d0362f2a2d2d0a90cf1a5a4c5db02d56ecc4c5bf34007208d5b887185865"
);
assert_eq!(
bytes_to_hex(&pbkdf2_sha256::pbkdf2_sha256(b"password", b"salt", 1, 32)?),
"120fb6cffcf8b32c43e7225256c4f837a86548c92ccc35480805987cb70be17b"
);
assert_eq!(
bytes_to_hex(&pbkdf2_sha256::pbkdf2_sha256(b"password", b"salt", 2, 32)?),
"ae4d0c95af6b46d32d0adff928f06dd02a303f8ef3c251dfd6e2d85a95474c43"
);
let params = Argon2idParams {
memory_kib: 32,
iterations: 2,
parallelism: 1,
output_len: 32,
};
assert_eq!(
argon2id::argon2id(b"password", b"somesalt", params)?.len(),
32
);
assert_ne!(siphash13::siphash13(b"", 0), siphash13::siphash13(b"a", 0));
Ok(())
}
#[test]
pub fn wave_b_kdf_bomb_defenses_are_actionable() {
assert!(
pbkdf2_sha256::pbkdf2_sha256(b"p", b"s", 0, 32).is_err_and(|error| error.contains("Fix:"))
);
assert!(pbkdf2_sha256::pbkdf2_sha256(b"p", b"s", 1_000_001, 32)
.is_err_and(|error| error.contains("Fix:")));
assert!(hkdf_expand::hkdf_expand(b"p", b"i", 8161).is_err_and(|error| error.contains("Fix:")));
assert!(argon2id::argon2id(
b"p",
b"s",
Argon2idParams {
memory_kib: 1_048_577,
iterations: 1,
parallelism: 1,
output_len: 32,
},
)
.is_err_and(|error| error.contains("Fix:")));
}
pub fn hex_bytes(hex: &str) -> Result<Vec<u8>, String> {
if hex.len() % 2 != 0 {
return Err(format!(
"Fix: provide an even number of hex digits, got {}",
hex.len()
));
}
hex.as_bytes()
.chunks_exact(2)
.map(|pair| {
let hi = hex_nibble(pair[0])?;
let lo = hex_nibble(pair[1])?;
Ok((hi << 4) | lo)
})
.collect()
}
pub fn hex_nibble(byte: u8) -> Result<u8, String> {
match byte {
b'0'..=b'9' => Ok(byte - b'0'),
b'a'..=b'f' => Ok(byte - b'a' + 10),
b'A'..=b'F' => Ok(byte - b'A' + 10),
_ => Err(format!(
"Fix: use ASCII hex digits only, got byte 0x{byte:02x}"
)),
}
}