use std::time::{
Duration,
Instant,
};
use lib_q_random::{
DOMAIN_LIBQ_DET_RNG,
Kt128Expander,
};
const ITERATIONS: usize = 2_000;
const REPS: usize = 7;
fn min_time(iterations: usize, mut op: impl FnMut()) -> Duration {
let mut best = Duration::MAX;
for _ in 0..REPS {
let start = Instant::now();
for _ in 0..iterations {
op();
}
let elapsed = start.elapsed();
if elapsed < best {
best = elapsed;
}
}
best
}
#[test]
fn test_seed_expansion_constant_time() {
let seeds: [[u8; 32]; 4] = [
[0x00; 32],
[0xFF; 32],
core::array::from_fn(|i| (i as u8).wrapping_mul(251)),
core::array::from_fn(|i| ((i as u32).wrapping_mul(73).wrapping_add(19) % 256) as u8),
];
for _ in 0..500 {
let mut expander = Kt128Expander::from_seed_32(DOMAIN_LIBQ_DET_RNG, seeds[0]);
let mut out = [0u8; 64];
expander.fill_bytes(&mut out);
std::hint::black_box(out);
}
let mut times = Vec::with_capacity(seeds.len());
for seed in &seeds {
times.push(min_time(ITERATIONS, || {
let mut expander = Kt128Expander::from_seed_32(DOMAIN_LIBQ_DET_RNG, *seed);
let mut out = [0u8; 64];
expander.fill_bytes(&mut out);
std::hint::black_box(out);
}));
}
let avg_time = times.iter().sum::<Duration>() / times.len() as u32;
let tolerance = avg_time * 60 / 100;
for (i, time) in times.iter().enumerate() {
let diff = (*time).abs_diff(avg_time);
assert!(
diff <= tolerance,
"seed {} expansion timing {}ns differs too much from average {}ns (diff {}ns, tolerance {}ns)",
i,
time.as_nanos(),
avg_time.as_nanos(),
diff.as_nanos(),
tolerance.as_nanos()
);
}
}
#[test]
fn test_seed_expansion_with_refill_constant_time() {
let seeds: [[u8; 32]; 3] = [
[0x11; 32],
[0x22; 32],
core::array::from_fn(|i| (i as u8) ^ 0x5A),
];
const OUT_LEN: usize = 96;
for _ in 0..300 {
let mut expander = Kt128Expander::from_seed_32(DOMAIN_LIBQ_DET_RNG, seeds[0]);
let mut out = [0u8; OUT_LEN];
expander.fill_bytes(&mut out);
std::hint::black_box(out);
}
let mut times = Vec::with_capacity(seeds.len());
for seed in &seeds {
times.push(min_time(ITERATIONS, || {
let mut expander = Kt128Expander::from_seed_32(DOMAIN_LIBQ_DET_RNG, *seed);
let mut out = [0u8; OUT_LEN];
expander.fill_bytes(&mut out);
std::hint::black_box(out);
}));
}
let avg_time = times.iter().sum::<Duration>() / times.len() as u32;
let tolerance = avg_time * 60 / 100;
for (i, time) in times.iter().enumerate() {
let diff = (*time).abs_diff(avg_time);
assert!(
diff <= tolerance,
"seed {} refill-path timing {}ns differs too much from average {}ns (diff {}ns, tolerance {}ns)",
i,
time.as_nanos(),
avg_time.as_nanos(),
diff.as_nanos(),
tolerance.as_nanos()
);
}
}