#![allow(dead_code)]
use super::Degree;
use super::encode::compress;
use super::fft::{Cplx, Fft, add_fft, mul_fft};
use super::fpr::Fpr;
use super::sampler::SamplerRng;
use super::tree::{FftTree, ff_sampling, ffldl, gram};
use alloc::vec::Vec;
const SALT_LEN: usize = 40;
pub(crate) struct ExpandedKey {
degree: Degree,
fft: Fft,
a: Vec<Cplx>,
b: Vec<Cplx>,
c: Vec<Cplx>,
d: Vec<Cplx>,
tree: FftTree,
sigmin: Fpr,
}
fn to_fpr(p: &[i64]) -> Vec<Fpr> {
p.iter().map(|&c| Fpr::of_i64(c)).collect()
}
fn neg_fpr(p: &[i64]) -> Vec<Fpr> {
p.iter().map(|&c| Fpr::of_i64(-c)).collect()
}
fn sigma_of(d: Degree) -> Fpr {
match d {
Degree::Falcon512 => Fpr::from_f64(165.736_617_182_977_6),
Degree::Falcon1024 => Fpr::from_f64(168.388_571_446_543_95),
}
}
fn sigmin_of(d: Degree) -> Fpr {
match d {
Degree::Falcon512 => Fpr::from_f64(1.277_833_696_912_833_7),
Degree::Falcon1024 => Fpr::from_f64(1.298_280_334_344_292),
}
}
pub(crate) fn expand_key(
f: &[i64],
g: &[i64],
cap_f: &[i64],
cap_g: &[i64],
degree: Degree,
) -> ExpandedKey {
let n = degree.n();
let fft = Fft::new(n);
let a = fft.fft(&to_fpr(g));
let b = fft.fft(&neg_fpr(f));
let c = fft.fft(&to_fpr(cap_g));
let d = fft.fft(&neg_fpr(cap_f));
let basis = [[a.clone(), b.clone()], [c.clone(), d.clone()]];
let g_gram = gram(&basis);
let tree = ffldl(&fft, &g_gram, sigma_of(degree));
ExpandedKey {
degree,
fft,
a,
b,
c,
d,
tree,
sigmin: sigmin_of(degree),
}
}
pub(crate) fn sign_internal<R: SamplerRng>(
key: &ExpandedKey,
msg: &[u8],
salt: &[u8; SALT_LEN],
rng: &mut R,
) -> Vec<u8> {
let n = key.degree.n();
let logn = n.trailing_zeros() as u8;
let sig_bound = key.degree.sig_bound();
let slen = key.degree.sig_len() - 1 - SALT_LEN;
let c = super::hash_to_point(salt, msg, n);
let c_fpr: Vec<Fpr> = c.iter().map(|&x| Fpr::of_i64(x as i64)).collect();
let point_fft = key.fft.fft(&c_fpr);
let inv_q = Fpr::from_f64(1.0).div(Fpr::of_i64(super::Q as i64));
let neg_inv_q = inv_q.neg();
loop {
let pd = mul_fft(&point_fft, &key.d);
let t0: Vec<Cplx> = pd.iter().map(|z| z.scale(inv_q)).collect();
let pb = mul_fft(&point_fft, &key.b);
let t1: Vec<Cplx> = pb.iter().map(|z| z.scale(neg_inv_q)).collect();
let (z0, z1) = ff_sampling(&key.fft, &t0, &t1, &key.tree, key.sigmin, rng);
let v0 = key
.fft
.ifft(&add_fft(&mul_fft(&z0, &key.a), &mul_fft(&z1, &key.c)));
let v1 = key
.fft
.ifft(&add_fft(&mul_fft(&z0, &key.b), &mul_fft(&z1, &key.d)));
let s0: Vec<i64> = (0..n).map(|i| c[i] as i64 - v0[i].rint()).collect();
let s1: Vec<i64> = (0..n).map(|i| -v1[i].rint()).collect();
let norm: u64 = s0.iter().chain(s1.iter()).map(|&x| (x * x) as u64).sum();
if norm > sig_bound {
continue;
}
let s1_i16: Vec<i16> = s1.iter().map(|&x| x as i16).collect();
if let Some(enc) = compress(&s1_i16, slen) {
let mut out = Vec::with_capacity(key.degree.sig_len());
out.push(0x30 | logn); out.extend_from_slice(salt);
out.extend_from_slice(&enc);
return out;
}
}
}
#[cfg(test)]
#[path = "sign_tests.rs"]
mod sign_tests;