use crate::params::{CHALLENGE_SEED_SIZE, DOMAIN_CHALLENGE, DOMAIN_OUTPUT, N, OUTPUT_SIZE};
use crate::poly::{poly_vec_to_bytes, poly_zero, Poly};
use sha3::{
digest::{ExtendableOutput, Update, XofReader},
Shake256,
};
pub fn sample_in_ball(seed: &[u8; CHALLENGE_SEED_SIZE], tau: usize) -> Poly {
let mut c = poly_zero();
let mut hasher = Shake256::default();
hasher.update(seed);
let mut reader = hasher.finalize_xof();
let mut sign_buf = [0u8; 8];
reader.read(&mut sign_buf);
let mut signs = u64::from_le_bytes(sign_buf);
for i in (N - tau)..N {
let j = loop {
let mut buf = [0u8; 1];
reader.read(&mut buf);
let val = buf[0] as usize;
if val <= i {
break val;
}
};
c[i] = c[j];
c[j] = if signs & 1 == 0 { 1 } else { -1 };
signs >>= 1;
}
c
}
pub fn hash_to_challenge_seed(w1: &[Poly], t: &[Poly], x: &[u8]) -> [u8; CHALLENGE_SEED_SIZE] {
let mut hasher = Shake256::default();
hasher.update(DOMAIN_CHALLENGE);
hasher.update(&poly_vec_to_bytes(w1));
hasher.update(&poly_vec_to_bytes(t));
hasher.update(x);
let mut reader = hasher.finalize_xof();
let mut seed = [0u8; CHALLENGE_SEED_SIZE];
reader.read(&mut seed);
seed
}
pub fn hash_vrf_output(w1: &[Poly], x: &[u8]) -> [u8; OUTPUT_SIZE] {
let mut hasher = Shake256::default();
hasher.update(DOMAIN_OUTPUT);
hasher.update(&poly_vec_to_bytes(w1));
hasher.update(x);
let mut reader = hasher.finalize_xof();
let mut output = [0u8; OUTPUT_SIZE];
reader.read(&mut output);
output
}
#[cfg(test)]
mod tests {
use super::*;
use crate::params::EYVARA_128;
#[test]
fn test_sample_in_ball_weight() {
let seed = [42u8; CHALLENGE_SEED_SIZE];
let c = sample_in_ball(&seed, EYVARA_128.tau());
let weight: usize = c.iter().filter(|&&v| v != 0).count();
assert_eq!(
weight,
EYVARA_128.tau(),
"challenge should have exactly tau nonzero coefficients"
);
for &v in c.iter() {
assert!(
v == -1 || v == 0 || v == 1,
"coefficient {v} not in {{-1, 0, 1}}"
);
}
}
#[test]
fn test_sample_in_ball_deterministic() {
let seed = [7u8; CHALLENGE_SEED_SIZE];
let c1 = sample_in_ball(&seed, 39);
let c2 = sample_in_ball(&seed, 39);
assert_eq!(c1, c2);
}
#[test]
fn test_sample_in_ball_different_seeds() {
let seed1 = [1u8; CHALLENGE_SEED_SIZE];
let seed2 = [2u8; CHALLENGE_SEED_SIZE];
let c1 = sample_in_ball(&seed1, 39);
let c2 = sample_in_ball(&seed2, 39);
assert_ne!(
c1, c2,
"different seeds should produce different challenges"
);
}
#[test]
fn test_hash_vrf_output_deterministic() {
let w1 = vec![poly_zero()];
let x = b"test_input";
let o1 = hash_vrf_output(&w1, x);
let o2 = hash_vrf_output(&w1, x);
assert_eq!(o1, o2);
}
#[test]
fn test_hash_vrf_output_different_inputs() {
let w1 = vec![poly_zero()];
let o1 = hash_vrf_output(&w1, b"input1");
let o2 = hash_vrf_output(&w1, b"input2");
assert_ne!(o1, o2, "different inputs should produce different outputs");
}
#[test]
fn test_hash_vrf_output_size() {
let w1 = vec![poly_zero()];
let o = hash_vrf_output(&w1, b"input");
assert_eq!(o.len(), OUTPUT_SIZE);
}
}