use libtest_mimic::{run, Arguments, Failed, Trial};
use sha2::{Sha256, Sha384, Sha512};
use sha3::{Shake128, Shake256};
use std::{
fs::{read_dir, File},
io::BufReader,
marker::PhantomData,
};
use super::{Expander, ExpanderXmd, ExpanderXof};
#[derive(Debug, serde_derive::Serialize, serde_derive::Deserialize)]
pub struct ExpanderVector {
#[serde(rename = "DST")]
pub dst: String,
pub k: usize,
pub hash: String,
pub name: String,
#[serde(rename = "tests")]
pub vectors: Vec<TestExpander>,
}
#[derive(Debug, serde_derive::Serialize, serde_derive:: Deserialize)]
pub struct TestExpander {
#[serde(rename = "DST_prime")]
pub dst_prime: String,
pub len_in_bytes: String,
pub msg: String,
pub msg_prime: String,
pub uniform_bytes: String,
}
#[test]
fn expander() {
let args = Arguments::from_args();
let mut tests = Vec::<Trial>::new();
for filename in read_dir("./src/fields/field_hashers/expander/testdata").unwrap() {
let ff = filename.unwrap();
let file = File::open(ff.path()).unwrap();
let u: ExpanderVector = serde_json::from_reader(BufReader::new(file)).unwrap();
tests.push(Trial::test(
ff.file_name().to_str().unwrap().to_string(),
move || do_test(u),
));
}
run(&args, tests).exit_if_failed();
}
#[derive(Copy, Clone)]
pub enum ExpID {
XMD(HashID),
XOF(XofID),
}
#[derive(Clone, Copy, PartialEq, Eq)]
pub enum HashID {
SHA256,
SHA384,
SHA512,
}
#[derive(Clone, Copy, PartialEq, Eq)]
pub enum XofID {
SHAKE128,
SHAKE256,
}
fn do_test(data: ExpanderVector) -> Result<(), Failed> {
let exp_id = match data.hash.as_str() {
"SHA256" => ExpID::XMD(HashID::SHA256),
"SHA384" => ExpID::XMD(HashID::SHA384),
"SHA512" => ExpID::XMD(HashID::SHA512),
"SHAKE128" => ExpID::XOF(XofID::SHAKE128),
"SHAKE256" => ExpID::XOF(XofID::SHAKE256),
_ => unimplemented!(),
};
let exp = get_expander(exp_id, data.dst.as_bytes(), data.k);
for v in data.vectors.iter() {
let len = usize::from_str_radix(v.len_in_bytes.trim_start_matches("0x"), 16).unwrap();
let got = exp.expand(v.msg.as_bytes(), len);
let want = hex::decode(&v.uniform_bytes).unwrap();
if got != want {
return Err(format!(
"Expander: {}\nVector: {}\ngot: {:?}\nwant: {:?}",
data.hash, v.msg, got, want,
)
.into());
}
}
Ok(())
}
fn get_expander(id: ExpID, _dst: &[u8], k: usize) -> Box<dyn Expander> {
let dst = _dst.to_vec();
match id {
ExpID::XMD(h) => match h {
HashID::SHA256 => Box::new(ExpanderXmd {
hasher: PhantomData::<Sha256>,
block_size: 64,
dst,
}),
HashID::SHA384 => Box::new(ExpanderXmd {
hasher: PhantomData::<Sha384>,
block_size: 128,
dst,
}),
HashID::SHA512 => Box::new(ExpanderXmd {
hasher: PhantomData::<Sha512>,
block_size: 128,
dst,
}),
},
ExpID::XOF(x) => match x {
XofID::SHAKE128 => Box::new(ExpanderXof {
xofer: PhantomData::<Shake128>,
k,
dst,
}),
XofID::SHAKE256 => Box::new(ExpanderXof {
xofer: PhantomData::<Shake256>,
k,
dst,
}),
},
}
}