From 1e4e11bb56b14679c57df8c970e186de08599ff0 Mon Sep 17 00:00:00 2001 From: Al-Kindi-0 <82364884+Al-Kindi-0@users.noreply.github.com> Date: Thu, 21 Mar 2024 18:24:47 +0100 Subject: [PATCH] chore: revert back key recovery mode --- Cargo.toml | 1 + src/dsa/rpo_falcon512/keys/secret_key.rs | 46 +++++++-------------- src/dsa/rpo_falcon512/signature.rs | 52 ++++++++++++------------ 3 files changed, 43 insertions(+), 56 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 3e2a2cd8..2e66c129 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -39,6 +39,7 @@ std = [ "blake3/std", "dep:cc", "rand/std", + "rand/std_rng", "winter-crypto/std", "winter-math/std", "winter-utils/std", diff --git a/src/dsa/rpo_falcon512/keys/secret_key.rs b/src/dsa/rpo_falcon512/keys/secret_key.rs index eee87276..e804ea43 100644 --- a/src/dsa/rpo_falcon512/keys/secret_key.rs +++ b/src/dsa/rpo_falcon512/keys/secret_key.rs @@ -11,12 +11,9 @@ use crate::dsa::rpo_falcon512::{ hash_to_point::hash_to_point_rpo256, math::ntru_gen, SIG_NONCE_LEN, SK_LEN, }; use alloc::{string::ToString, vec::Vec}; -use num::{Complex, Zero}; +use num::Complex; use num_complex::Complex64; -use rand::Rng; - -#[cfg(feature = "std")] -use rand::{rngs::OsRng, RngCore}; +use rand::{Rng, RngCore}; #[cfg(not(feature = "std"))] use num::Float; @@ -45,7 +42,7 @@ const WIDTH_SMALL_POLY_COEFFICIENT: usize = 6; /// The secret key is generated by first sampling a random pair (f, g) of polynomials using /// an appropriate distribution that yields short but not too short polynomials with integer /// coefficients modulo ϕ. The NTRU equation is then used to find a matching pair (F, G). -/// The public key is then derived from the secret key using equation 2. +/// The public key is then derived from the secret key using equation 1. /// /// To allow for fast signature generation, the secret key is pre-processed into a more suitable /// form, called the LDL tree, and this allows for fast sampling of short vectors in the lattice @@ -66,9 +63,12 @@ impl SecretKey { /// Generates a secret key from OS-provided randomness. #[cfg(feature = "std")] pub fn new() -> Self { + use rand::{rngs::StdRng, SeedableRng}; + let mut seed: [u8; 32] = [0; 32]; - OsRng.fill_bytes(&mut seed); - Self::with_rng(&mut OsRng) + let mut rng = StdRng::from_entropy(); + rng.fill_bytes(&mut seed); + Self::with_rng(&mut rng) } /// Generates a secret_key using the provided random number generator `Rng`. @@ -122,10 +122,11 @@ impl SecretKey { rng.fill_bytes(&mut nonce_bytes); let nonce = Nonce::new(nonce_bytes); + let h = self.compute_pub_key_poly(); let c = hash_to_point_rpo256(message, &nonce); - let (s1, s2) = self.sign_helper(c, rng); + let s2 = self.sign_helper(c, rng); - Signature::new(nonce, s1, s2) + Signature::new(nonce, h, s2) } // HELPER METHODS @@ -145,13 +146,8 @@ impl SecretKey { /// Signs a message polynomial with the secret key. /// /// Takes a randomness generator implementing `Rng` and message polynomial representing `c` - /// the hash-to-point of the message to be signed. It outputs a tuple of signature polynomials - /// `(s1, s2)`. - fn sign_helper( - &self, - c: Polynomial, - rng: &mut R, - ) -> (SignaturePoly, SignaturePoly) { + /// the hash-to-point of the message to be signed. It outputs a signature polynomial `s2`. + fn sign_helper(&self, c: Polynomial, rng: &mut R) -> SignaturePoly { let one_over_q = 1.0 / (MODULUS as f64); let c_over_q_fft = c.map(|cc| Complex::new(one_over_q * cc.value() as f64, 0.0)).fft(); @@ -184,15 +180,7 @@ impl SecretKey { break [-s0, s1]; }; - let s1 = bold_s[0].ifft(); let s2 = bold_s[1].ifft(); - let s1_coef: [i16; N] = s1 - .coefficients - .iter() - .map(|a| a.re.round() as i16) - .collect::>() - .try_into() - .expect("The number of coefficients should be equal to N"); let s2_coef: [i16; N] = s2 .coefficients .iter() @@ -201,12 +189,8 @@ impl SecretKey { .try_into() .expect("The number of coefficients should be equal to N"); - if let Ok(s1) = SignaturePoly::try_from(&s1_coef) { - if let Ok(s2) = SignaturePoly::try_from(&s2_coef) { - if s2.fft().coefficients.iter().all(|&c| c != FalconFelt::zero()) { - return (s1, s2); - } - } + if let Ok(s2) = SignaturePoly::try_from(&s2_coef) { + return s2; } } } diff --git a/src/dsa/rpo_falcon512/signature.rs b/src/dsa/rpo_falcon512/signature.rs index 93260978..de1467ba 100644 --- a/src/dsa/rpo_falcon512/signature.rs +++ b/src/dsa/rpo_falcon512/signature.rs @@ -15,8 +15,8 @@ use num::Zero; /// An RPO Falcon512 signature over a message. /// -/// The signature is a pair of polynomials (s1, s2) in (Z_p\[x\]/(phi))^2 and a nonce `r`, -/// where: +/// The signature is a pair of polynomials (s1, s2) in (Z_p\[x\]/(phi))^2 a nonce `r`, and a public +/// key polynomial `h` where: /// - p := 12289 /// - phi := x^512 + 1 /// @@ -26,12 +26,16 @@ use num::Zero; /// /// where |.| is the norm and: /// - c = HashToPoint(r || message) -/// - h = s2^(-1) * (c - s1) /// - pk = Rpo256::hash(h) /// /// Here h is a polynomial representing the public key and pk is its digest using the Rpo256 hash /// function. c is a polynomial that is the hash-to-point of the message being signed. /// +/// The polynomial h is serialized as: +/// +/// 1. 1 byte representing the log2(512) i.e., 9. +/// 2. 896 bytes for the public key itself. +/// /// The signature is serialized as: /// 1. A header byte specifying the algorithm used to encode the coefficients of the `s2` polynomial /// together with the degree of the irreducible polynomial phi. @@ -42,36 +46,40 @@ use num::Zero; /// The current implementation works always with cc equal to 0b01 and nnnn equal to 0b1001 and /// thus the header byte is always equal to 0b00111001. /// 2. 40 bytes for the nonce. -/// 3. 625 bytes encoding the `s1` polynomial above. /// 4. 625 bytes encoding the `s2` polynomial above. /// -/// The total size of the signature is 1291 bytes. +/// The total size of the signature is (including the extended public key) is 1563 bytes. #[derive(Debug, Clone, PartialEq, Eq)] pub struct Signature { header: SignatureHeader, nonce: Nonce, - s1: SignaturePoly, s2: SignaturePoly, + h: PubKeyPoly, } impl Signature { // CONSTRUCTOR // -------------------------------------------------------------------------------------------- - pub fn new(nonce: Nonce, s1: SignaturePoly, s2: SignaturePoly) -> Signature { + pub fn new(nonce: Nonce, h: PubKeyPoly, s2: SignaturePoly) -> Signature { Self { header: SignatureHeader::default(), nonce, - s1, s2, + h, } } // PUBLIC ACCESSORS // -------------------------------------------------------------------------------------------- + /// Returns the public key polynomial h. + pub fn pk_poly(&self) -> &PubKeyPoly { + &self.h + } + // Returns the polynomial representation of the signature in Z_p[x]/(phi). - pub fn sig_poly(&self) -> (&Polynomial, &Polynomial) { - (&self.s1, &self.s2) + pub fn sig_poly(&self) -> &Polynomial { + &self.s2 } /// Returns the nonce component of the signature. @@ -85,21 +93,15 @@ impl Signature { /// Returns true if this signature is a valid signature for the specified message generated /// against the secret key matching the specified public key commitment. pub fn verify(&self, message: Word, pubkey_com: Word) -> bool { - let c = hash_to_point_rpo256(message, &self.nonce); - - let s1_fft = self.s1.fft(); - let s2_fft = self.s2.fft(); - let c_fft = c.fft(); - - // recover the public key polynomial using h = s2^(-1) * (c - s1) - let h_fft = (c_fft - s1_fft).hadamard_div(&s2_fft); - let h = h_fft.ifft(); - // compute the hash of the public key polynomial - let h_felt: Polynomial = h.clone().into(); + let h_felt: Polynomial = (&**self.pk_poly()).into(); let h_digest: Word = Rpo256::hash_elements(&h_felt.coefficients).into(); + if h_digest != pubkey_com { + return false; + } - h_digest == pubkey_com && verify_helper(&c, &self.s2, &h.into()) + let c = hash_to_point_rpo256(message, &self.nonce); + h_digest == pubkey_com && verify_helper(&c, &self.s2, self.pk_poly()) } } @@ -107,8 +109,8 @@ impl Serializable for Signature { fn write_into(&self, target: &mut W) { target.write(&self.header); target.write(&self.nonce); - target.write(&self.s1); target.write(&self.s2); + target.write(&self.h); } } @@ -116,10 +118,10 @@ impl Deserializable for Signature { fn read_from(source: &mut R) -> Result { let header = source.read()?; let nonce = source.read()?; - let s1 = source.read()?; let s2 = source.read()?; + let h = source.read()?; - Ok(Self { header, nonce, s1, s2 }) + Ok(Self { header, nonce, s2, h }) } }