use super::super::sampler::SamplerRng;
use super::super::zint::Zint;
use super::{Q, karamul, ntru_gen};
use alloc::vec::Vec;
struct DetRng(u64);
impl SamplerRng for DetRng {
fn next_bytes(&mut self, buf: &mut [u8]) {
for b in buf.iter_mut() {
self.0 = self
.0
.wrapping_mul(0x5851_F42D_4C95_7F2D)
.wrapping_add(0x1405_7B7E_F767_814F);
*b = (self.0 >> 56) as u8;
}
}
}
fn to_zint(p: &[i64]) -> Vec<Zint> {
p.iter().map(|&c| Zint::from_i64(c)).collect()
}
fn polymul_modq(a: &[i64], b: &[i64], n: usize) -> Vec<i64> {
let mut acc = alloc::vec![0i64; n];
for i in 0..n {
for j in 0..n {
let p = a[i] * b[j];
let k = i + j;
if k < n {
acc[k] += p;
} else {
acc[k - n] -= p;
}
}
}
acc.iter().map(|v| v.rem_euclid(Q)).collect()
}
fn check_keygen(n: usize, seed: u64) {
let mut rng = DetRng(seed);
let (f, g, cap_f, cap_g, h) = ntru_gen(n, &mut rng);
assert_eq!(f.len(), n);
let fg = karamul(&to_zint(&f), &to_zint(&cap_g));
let gf = karamul(&to_zint(&g), &to_zint(&cap_f));
let q = Zint::from_i64(Q);
for i in 0..n {
let d = fg[i].sub(&gf[i]);
if i == 0 {
assert_eq!(d, q, "n={n}: (f·G − g·F)[0] must be q");
} else {
assert!(d.is_zero(), "n={n}: (f·G − g·F)[{i}] must be 0");
}
}
let h_i: Vec<i64> = h.iter().map(|&x| x as i64).collect();
let hf = polymul_modq(&h_i, &f, n);
for i in 0..n {
assert_eq!(hf[i], g[i].rem_euclid(Q), "n={n}: (h·f − g)[{i}] ≠ 0 mod q");
}
}
#[test]
fn keygen_ntru_equation_n256() {
check_keygen(256, 0x1111_2222_3333_4444);
}
#[test]
fn keygen_ntru_equation_n512() {
check_keygen(512, 0xACE1_0F1E_2D3C_4B5A);
}