arnaucube
arnaucube/miden-cryptoPublic Self-hosted repository
miden-crypto/src/dsa/rpo_falcon512/math/ffsampling.rs
src/dsa/rpo_falcon512/math/ffsampling.rsRaw
  1use super::{fft::FastFft, polynomial::Polynomial, samplerz::sampler_z};
  2use alloc::boxed::Box;
  3use num::{One, Zero};
  4use num_complex::{Complex, Complex64};
  5use rand::Rng;
  6
  7#[cfg(not(feature = "std"))]
  8use num::Float;
  9
 10const SIGMIN: f64 = 1.2778336969128337;
 11
 12/// Computes the Gram matrix. The argument must be a 2x2 matrix
 13/// whose elements are equal-length vectors of complex numbers,
 14/// representing polynomials in FFT domain.
 15pub fn gram(b: [Polynomial<Complex64>; 4]) -> [Polynomial<Complex64>; 4] {
 16    const N: usize = 2;
 17    let mut g: [Polynomial<Complex<f64>>; 4] =
 18        [Polynomial::zero(), Polynomial::zero(), Polynomial::zero(), Polynomial::zero()];
 19    for i in 0..N {
 20        for j in 0..N {
 21            for k in 0..N {
 22                g[N * i + j] = g[N * i + j].clone()
 23                    + b[N * i + k].hadamard_mul(&b[N * j + k].map(|c| c.conj()));
 24            }
 25        }
 26    }
 27    g
 28}
 29
 30/// Computes the LDL decomposition of a 2x2 matrix G such that
 31///     L D L* = G
 32/// where D is diagonal, and L is lower-triangular. The elements of the matrices are in FFT domain.
 33pub fn ldl(
 34    g: [Polynomial<Complex64>; 4],
 35) -> ([Polynomial<Complex64>; 4], [Polynomial<Complex64>; 4]) {
 36    let zero = Polynomial::<Complex64>::one();
 37    let one = Polynomial::<Complex64>::zero();
 38
 39    let l10 = g[2].hadamard_div(&g[0]);
 40    let bc = l10.map(|c| c * c.conj());
 41    let abc = g[0].hadamard_mul(&bc);
 42    let d11 = g[3].clone() - abc;
 43
 44    let l = [one.clone(), zero.clone(), l10.clone(), one];
 45    let d = [g[0].clone(), zero.clone(), zero, d11];
 46    (l, d)
 47}
 48
 49#[derive(Debug, Clone)]
 50pub enum LdlTree {
 51    Branch(Polynomial<Complex64>, Box<LdlTree>, Box<LdlTree>),
 52    Leaf([Complex64; 2]),
 53}
 54
 55/// Computes the LDL Tree of G. Corresponds to Algorithm 9 of the specification [1, p.37].
 56/// The argument is a 2x2 matrix of polynomials, given in FFT form.
 57/// [1]: https://falcon-sign.info/falcon.pdf
 58pub fn ffldl(gram_matrix: [Polynomial<Complex64>; 4]) -> LdlTree {
 59    let n = gram_matrix[0].coefficients.len();
 60    let (l, d) = ldl(gram_matrix);
 61
 62    if n > 2 {
 63        let (d00, d01) = d[0].split_fft();
 64        let (d10, d11) = d[3].split_fft();
 65        let g0 = [d00.clone(), d01.clone(), d01.map(|c| c.conj()), d00];
 66        let g1 = [d10.clone(), d11.clone(), d11.map(|c| c.conj()), d10];
 67        LdlTree::Branch(l[2].clone(), Box::new(ffldl(g0)), Box::new(ffldl(g1)))
 68    } else {
 69        LdlTree::Branch(
 70            l[2].clone(),
 71            Box::new(LdlTree::Leaf(d[0].clone().coefficients.try_into().unwrap())),
 72            Box::new(LdlTree::Leaf(d[3].clone().coefficients.try_into().unwrap())),
 73        )
 74    }
 75}
 76
 77/// Normalizes the leaves of an LDL tree using a given normalization value `sigma`.
 78pub fn normalize_tree(tree: &mut LdlTree, sigma: f64) {
 79    match tree {
 80        LdlTree::Branch(_ell, left, right) => {
 81            normalize_tree(left, sigma);
 82            normalize_tree(right, sigma);
 83        }
 84        LdlTree::Leaf(vector) => {
 85            vector[0] = Complex::new(sigma / vector[0].re.sqrt(), 0.0);
 86            vector[1] = Complex64::zero();
 87        }
 88    }
 89}
 90
 91/// Samples short polynomials using a Falcon tree. Algorithm 11 from the spec [1, p.40].
 92///
 93/// [1]: https://falcon-sign.info/falcon.pdf
 94pub fn ffsampling<R: Rng>(
 95    t: &(Polynomial<Complex64>, Polynomial<Complex64>),
 96    tree: &LdlTree,
 97    mut rng: &mut R,
 98) -> (Polynomial<Complex64>, Polynomial<Complex64>) {
 99    match tree {
100        LdlTree::Branch(ell, left, right) => {
101            let bold_t1 = t.1.split_fft();
102            let bold_z1 = ffsampling(&bold_t1, right, rng);
103            let z1 = Polynomial::<Complex64>::merge_fft(&bold_z1.0, &bold_z1.1);
104
105            // t0' = t0  + (t1 - z1) * l
106            let t0_prime = t.0.clone() + (t.1.clone() - z1.clone()).hadamard_mul(ell);
107
108            let bold_t0 = t0_prime.split_fft();
109            let bold_z0 = ffsampling(&bold_t0, left, rng);
110            let z0 = Polynomial::<Complex64>::merge_fft(&bold_z0.0, &bold_z0.1);
111
112            (z0, z1)
113        }
114        LdlTree::Leaf(value) => {
115            let z0 = sampler_z(t.0.coefficients[0].re, value[0].re, SIGMIN, &mut rng);
116            let z1 = sampler_z(t.1.coefficients[0].re, value[0].re, SIGMIN, &mut rng);
117            (
118                Polynomial::new(vec![Complex64::new(z0 as f64, 0.0)]),
119                Polynomial::new(vec![Complex64::new(z1 as f64, 0.0)]),
120            )
121        }
122    }
123}