diff --git a/Cargo.lock b/Cargo.lock index 6ff30bb6e..a29c24b01 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -614,6 +614,7 @@ dependencies = [ [[package]] name = "fn-dsa-comm" version = "0.3.0" +source = "git+https://github.com/0xMiden/rust-fn-dsa?branch=sign-prehash#ef52824b2005fee6c6307563ac26b7e10b0b6f65" dependencies = [ "cpufeatures", "rand_core 0.6.4", @@ -622,6 +623,7 @@ dependencies = [ [[package]] name = "fn-dsa-kgen" version = "0.3.0" +source = "git+https://github.com/0xMiden/rust-fn-dsa?branch=sign-prehash#ef52824b2005fee6c6307563ac26b7e10b0b6f65" dependencies = [ "fn-dsa-comm", "zeroize", @@ -630,6 +632,7 @@ dependencies = [ [[package]] name = "fn-dsa-sign" version = "0.3.0" +source = "git+https://github.com/0xMiden/rust-fn-dsa?branch=sign-prehash#ef52824b2005fee6c6307563ac26b7e10b0b6f65" dependencies = [ "fn-dsa-comm", "zeroize", @@ -638,7 +641,7 @@ dependencies = [ [[package]] name = "fn-dsa-vrfy" version = "0.3.0" -source = "git+https://github.com/0xMiden/rust-fn-dsa?branch=expose-internals#7d76bf3b81e8ad7f0fc53d8ec23d265f257784d9" +source = "git+https://github.com/0xMiden/rust-fn-dsa?branch=sign-prehash#ef52824b2005fee6c6307563ac26b7e10b0b6f65" dependencies = [ "fn-dsa-comm", ] diff --git a/Cargo.toml b/Cargo.toml index 997001651..0b54a72ee 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,9 +26,3 @@ debug = 2 debug-assertions = true inherits = "release" overflow-checks = true - -# Local patch for faster iteration during development -[patch."https://github.com/0xMiden/rust-fn-dsa"] -fn-dsa-comm = { path = "/Users/al/Code/rust-fn-dsa/fn-dsa-comm" } -fn-dsa-kgen = { path = "/Users/al/Code/rust-fn-dsa/fn-dsa-kgen" } -fn-dsa-sign = { path = "/Users/al/Code/rust-fn-dsa/fn-dsa-sign" } diff --git a/miden-crypto/Cargo.toml b/miden-crypto/Cargo.toml index 238476595..2d0c6952e 100644 --- a/miden-crypto/Cargo.toml +++ b/miden-crypto/Cargo.toml @@ -95,9 +95,10 @@ clap = { features = ["derive"], optional = true, versio curve25519-dalek = { default-features = false, version = "4" } ed25519-dalek = { features = ["zeroize"], version = "2" } flume = { version = "0.11" } -fn-dsa-comm = { default-features = false, git = "https://github.com/0xMiden/rust-fn-dsa", branch = "expose-internals" } -fn-dsa-kgen = { default-features = false, git = "https://github.com/0xMiden/rust-fn-dsa", branch = "expose-internals" } -fn-dsa-sign = { default-features = false, git = "https://github.com/0xMiden/rust-fn-dsa", branch = "expose-internals" } +fn-dsa-comm = { branch = "sign-prehash", default-features = false, git = "https://github.com/0xMiden/rust-fn-dsa" } +fn-dsa-kgen = { branch = "sign-prehash", default-features = false, git = "https://github.com/0xMiden/rust-fn-dsa" } +fn-dsa-sign = { branch = "sign-prehash", default-features = false, git = "https://github.com/0xMiden/rust-fn-dsa" } +fn-dsa-vrfy = { branch = "sign-prehash", default-features = false, git = "https://github.com/0xMiden/rust-fn-dsa" } hashbrown = { features = ["serde"], optional = true, version = "0.16" } hkdf = { default-features = false, version = "0.12" } k256 = { features = ["ecdh", "ecdsa"], version = "0.13" } @@ -135,8 +136,8 @@ p3-miden-prover = { default-features = false, version = "0.4.2" } [dev-dependencies] assert_matches = { default-features = false, version = "1.5" } criterion = { features = ["html_reports"], version = "0.7" } -fn-dsa-sign = { git = "https://github.com/0xMiden/rust-fn-dsa", branch = "expose-internals", features = ["testing"] } -fn-dsa-vrfy = { git = "https://github.com/0xMiden/rust-fn-dsa", branch = "expose-internals" } +fn-dsa-sign = { branch = "sign-prehash", git = "https://github.com/0xMiden/rust-fn-dsa" } +fn-dsa-vrfy = { branch = "sign-prehash", git = "https://github.com/0xMiden/rust-fn-dsa" } hex = { default-features = false, features = ["alloc"], version = "0.4" } itertools = { version = "0.14" } proptest = { default-features = false, features = ["alloc"], version = "1.7" } diff --git a/miden-crypto/src/dsa/falcon512_rpo/keys/mod.rs b/miden-crypto/src/dsa/falcon512_rpo/keys/mod.rs index b02ef7bc8..12f8d9626 100644 --- a/miden-crypto/src/dsa/falcon512_rpo/keys/mod.rs +++ b/miden-crypto/src/dsa/falcon512_rpo/keys/mod.rs @@ -1,6 +1,5 @@ use super::{ ByteReader, ByteWriter, Deserializable, DeserializationError, Felt, Serializable, Signature, - math::{FalconFelt, Polynomial}, }; mod public_key; @@ -55,7 +54,7 @@ mod tests { let mut buffer = vec![]; sk.write_into(&mut buffer); let sk_deserialized = SecretKey::read_from_bytes(&buffer).unwrap(); - assert_eq!(sk.short_lattice_basis(), sk_deserialized.short_lattice_basis()); + assert_eq!(sk, sk_deserialized); // sign a random message let message = Word::new([ONE; 4]); diff --git a/miden-crypto/src/dsa/falcon512_rpo/keys/public_key.rs b/miden-crypto/src/dsa/falcon512_rpo/keys/public_key.rs index 6969418d8..c11bdcaa2 100644 --- a/miden-crypto/src/dsa/falcon512_rpo/keys/public_key.rs +++ b/miden-crypto/src/dsa/falcon512_rpo/keys/public_key.rs @@ -1,21 +1,29 @@ //! Public key types for the RPO Falcon 512 digital signature scheme used in Miden VM. use alloc::{string::ToString, vec::Vec}; -use core::ops::Deref; + +use fn_dsa_vrfy::{VerifyingKey, VerifyingKey512}; use super::{ - super::{LOG_N, N, PK_LEN}, - ByteReader, ByteWriter, Deserializable, DeserializationError, FalconFelt, Felt, Polynomial, - Serializable, Signature, + super::{ + LOG_N, N, PK_LEN, + math::{FalconFelt, Polynomial}, + }, + ByteReader, ByteWriter, Deserializable, DeserializationError, Felt, Serializable, Signature, }; use crate::{SequentialCommit, Word}; // PUBLIC KEY // ================================================================================================ -/// Public key represented as a polynomial with coefficients over the Falcon prime field. +/// Public key for Falcon-512 DSA. +/// +/// Internally stores the encoded public key bytes in fn-dsa format. The bytes can be +/// decoded to a `VerifyingKey512` on demand for verification operations. #[derive(Debug, Clone, PartialEq, Eq)] -pub struct PublicKey(Polynomial); +pub struct PublicKey { + encoded: [u8; PK_LEN], +} impl PublicKey { /// Verifies the provided signature against provided message and this public key. @@ -33,78 +41,67 @@ impl PublicKey { pub fn to_commitment(&self) -> Word { ::to_commitment(self) } -} -impl SequentialCommit for PublicKey { - type Commitment = Word; + /// Returns the encoded public key bytes. + pub fn as_bytes(&self) -> &[u8; PK_LEN] { + &self.encoded + } - fn to_elements(&self) -> Vec { - Into::>::into(self.0.clone()).coefficients + /// Returns the public key as a polynomial over the Falcon prime field. + pub fn to_polynomial(&self) -> Polynomial { + let h = self.decode_coefficients(); + let coefficients: Vec = h.iter().map(|&v| FalconFelt::new(v)).collect(); + Polynomial::new(coefficients) } -} -impl Deref for PublicKey { - type Target = Polynomial; + /// Decodes the stored bytes into a VerifyingKey512 for verification operations. + pub(crate) fn decode_verifying_key(&self) -> Option { + VerifyingKey512::decode(&self.encoded) + } - fn deref(&self) -> &Self::Target { - &self.0 + /// Decodes the public key polynomial coefficients from the stored bytes. + fn decode_coefficients(&self) -> [u16; N] { + let mut h = [0u16; N]; + // Skip the header byte (LOG_N) + fn_dsa_comm::codec::modq_decode(&self.encoded[1..], &mut h) + .expect("encoded key should be valid"); + h } } -impl From> for PublicKey { - fn from(pk_poly: Polynomial) -> Self { - Self(pk_poly) +impl SequentialCommit for PublicKey { + type Commitment = Word; + + fn to_elements(&self) -> Vec { + let h = self.decode_coefficients(); + h.iter().map(|&v| Felt::new(v as u64)).collect() } } impl Serializable for &PublicKey { fn write_into(&self, target: &mut W) { - let mut buf = [0_u8; PK_LEN]; - buf[0] = LOG_N; - - // Convert FalconFelt coefficients to u16 external representation [0, q-1] - let h: Vec = self.0.coefficients.iter().map(|c| c.value()).collect(); - - // Use fn-dsa-comm's modq_encode to encode 512 coefficients at 14 bits each - // This encodes 4 coefficients per 7 bytes (512/4 = 128 groups = 896 bytes) - let written = fn_dsa_comm::codec::modq_encode(&h, &mut buf[1..]); - assert_eq!(written, PK_LEN - 1, "modq_encode should write exactly {} bytes", PK_LEN - 1); - - target.write(buf); + target.write_bytes(&self.encoded); } } impl Deserializable for PublicKey { fn read_from(source: &mut R) -> Result { - let buf = source.read_array::()?; + let encoded: [u8; PK_LEN] = source.read_array()?; - if buf[0] != LOG_N { + if encoded[0] != LOG_N { return Err(DeserializationError::InvalidValue(format!( "Failed to decode public key: expected the first byte to be {LOG_N} but was {}", - buf[0] + encoded[0] ))); } - // Use fn-dsa-comm's modq_decode to decode 512 coefficients - let mut h = [0u16; N]; - let read_bytes = fn_dsa_comm::codec::modq_decode(&buf[1..], &mut h).ok_or_else(|| { + // Validate by attempting to decode + VerifyingKey512::decode(&encoded).ok_or_else(|| { DeserializationError::InvalidValue( - "Failed to decode public key: invalid modq encoding".to_string(), + "Failed to decode public key: invalid encoding".to_string(), ) })?; - // Verify we consumed exactly the expected number of bytes - if read_bytes != PK_LEN - 1 { - return Err(DeserializationError::InvalidValue(format!( - "Failed to decode public key: expected {} bytes, read {}", - PK_LEN - 1, - read_bytes - ))); - } - - // Convert u16 values to FalconFelt (modq_decode already validates values are in [0, q-1]) - let coefficients: Vec = h.iter().map(|&v| FalconFelt::new(v)).collect(); - - Ok(Polynomial::new(coefficients).into()) + Ok(Self { encoded }) } } diff --git a/miden-crypto/src/dsa/falcon512_rpo/keys/secret_key.rs b/miden-crypto/src/dsa/falcon512_rpo/keys/secret_key.rs index 466917d70..2e6968c96 100644 --- a/miden-crypto/src/dsa/falcon512_rpo/keys/secret_key.rs +++ b/miden-crypto/src/dsa/falcon512_rpo/keys/secret_key.rs @@ -1,16 +1,14 @@ -use alloc::{string::ToString, vec::Vec}; +use alloc::string::ToString; -use fn_dsa_comm::mq; use fn_dsa_kgen::{FN_DSA_LOGN_512, KeyPairGenerator, KeyPairGenerator512}; +use fn_dsa_sign::{SigningKey, SigningKey512}; use miden_crypto_derive::{SilentDebug, SilentDisplay}; use rand::{CryptoRng, Rng, RngCore}; use super::{ super::{ ByteReader, ByteWriter, Deserializable, DeserializationError, N, Nonce, Serializable, - ShortLatticeBasis, Signature, - math::{FalconFelt, Polynomial, flr::FLR}, - signature::SignaturePoly, + Signature, signature::SignaturePoly, }, PublicKey, }; @@ -20,61 +18,29 @@ use crate::{ utils::zeroize::{Zeroize, ZeroizeOnDrop}, }; -// CONSTANTS -// ================================================================================================ - -pub(crate) const WIDTH_BIG_POLY_COEFFICIENT: usize = 8; -pub(crate) const WIDTH_SMALL_POLY_COEFFICIENT: usize = 6; - // SECRET KEY // ================================================================================================ /// Represents the secret key for Falcon DSA. /// -/// The secret key is stored internally as a quadruple [g, f, G, F] of polynomials with integer -/// coefficients. During signing, this is transformed to the signing basis [[g, -f], [G, -F]] where -/// negations are applied. Each polynomial is of degree at most N = 512 and computations with these -/// polynomials is done modulo the monic irreducible polynomial ϕ = x^N + 1. The secret key is a -/// basis for a lattice and has the property of being short with respect to a certain norm and an -/// upper bound appropriate for a given security parameter. The public key on the other hand is -/// another basis for the same lattice and can be described by a single polynomial h with integer -/// coefficients modulo ϕ. The two keys are related by the following relation: -/// -/// 1. h = g /f [mod ϕ][mod p] -/// 2. f.G - g.F = p [mod ϕ] -/// -/// where p = 12289 is the Falcon prime. Equation 2 is called the NTRU equation. -/// 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 1. +/// The secret key consists of four polynomials [f, g, F, G] that form a short basis for +/// an NTRU lattice. The public key h = g/f (mod q) can be derived from the secret key. /// -/// To allow for fast signature generation, the secret key basis is precomputed in FFT format -/// during keygen. During signing, the Gram matrix is computed from this FFT basis, and then -/// the LDL decomposition is computed on-the-fly for fast sampling of short vectors using the -/// Fast Fourier sampling algorithm (ffSampling algorithm 11 in [1]). +/// Internally, this stores the encoded secret key bytes in fn-dsa format. The bytes are +/// decoded to a `SigningKey512` on demand for signing operations. /// /// [1]: https://falcon-sign.info/falcon.pdf #[derive(Clone, SilentDebug, SilentDisplay)] pub struct SecretKey { - secret_key: ShortLatticeBasis, - /// Precomputed basis B = [[g, -f], [G, -F]] in FFT format using FLR arithmetic. - /// This is used for fast signing with the FLR-based sampler. - /// Layout: [b00, b01, b10, b11] where each is N FLR values in FFT domain. - basis_fft_flr: [FLR; 4 * N], + encoded: [u8; SK_LEN], } impl Zeroize for SecretKey { fn zeroize(&mut self) { - self.secret_key.zeroize(); - // Manually zeroize FLR array - for i in 0..self.basis_fft_flr.len() { - self.basis_fft_flr[i] = FLR::ZERO; - } + self.encoded.zeroize(); } } -// Manual Drop implementation to ensure zeroization on drop. impl Drop for SecretKey { fn drop(&mut self) { self.zeroize(); @@ -104,44 +70,36 @@ impl SecretKey { /// OS-provided randomness unless you have specific requirements for the RNG source. pub fn with_rng(rng: &mut R) -> Self { let mut kg = KeyPairGenerator512::default(); - let mut sign_key = [0u8; SK_LEN]; + let mut encoded = [0u8; SK_LEN]; let mut vrfy_key = [0u8; PK_LEN]; // Bridge our rand 0.9 RNG into fn-dsa's rand_core 0.6 traits expected by keygen. let mut adapter = FnDsaRng { rng }; - kg.keygen(FN_DSA_LOGN_512, &mut adapter, &mut sign_key, &mut vrfy_key); - - let sk = SecretKey::read_from_bytes(&sign_key) - .expect("fn-dsa-kgen produced an invalid signing key"); + kg.keygen(FN_DSA_LOGN_512, &mut adapter, &mut encoded, &mut vrfy_key); - // Zeroize key buffers to prevent leakage - sign_key.zeroize(); + // Zeroize vrfy_key buffer (encoded is kept) vrfy_key.zeroize(); - sk - } - - /// Given a short basis [g, f, G, F], precomputes the FLR basis in FFT format. - /// Note: The basis is stored as [g, f, G, F]; negations to form [[g, -f], [G, -F]] are - /// applied during FLR basis computation. - pub(crate) fn from_short_lattice_basis(basis: ShortLatticeBasis) -> SecretKey { - // Precompute FLR basis in FFT format for fast signing - let basis_fft_flr = Self::compute_basis_fft_flr(&basis); - - Self { secret_key: basis, basis_fft_flr } + Self { encoded } } // PUBLIC ACCESSORS // -------------------------------------------------------------------------------------------- - /// Returns the polynomials of the short lattice basis of this secret key. - pub fn short_lattice_basis(&self) -> &ShortLatticeBasis { - &self.secret_key - } - /// Returns the public key corresponding to this secret key. pub fn public_key(&self) -> PublicKey { - self.compute_pub_key_poly() + let signing_key = self.decode_signing_key(); + let mut pk_bytes = [0u8; PK_LEN]; + signing_key.to_verifying_key(&mut pk_bytes); + PublicKey::read_from_bytes(&pk_bytes).expect("fn-dsa produced valid public key bytes") + } + + // PRIVATE HELPERS + // -------------------------------------------------------------------------------------------- + + /// Decodes the stored bytes into a SigningKey512 for signing operations. + fn decode_signing_key(&self) -> SigningKey512 { + SigningKey512::decode(&self.encoded).expect("encoded key should be valid") } // SIGNATURE GENERATION @@ -170,189 +128,126 @@ impl SecretKey { } /// Signs a message using the provided 56-byte seed for the PRNG. + /// + /// Following FN-DSA, each signing attempt uses a fresh nonce and recomputes + /// hash-to-point. A single PRNG seeded with the input generates both the + /// sampler seed and nonces, ensuring reproducible signatures. fn sign_with_seed(&self, message: Word, seed: &[u8; 56]) -> Signature { - use fn_dsa_comm::shake::SHAKE256_PRNG; + use fn_dsa_comm::shake::SHAKE256; + + let h = self.public_key(); + let mut signing_key = self.decode_signing_key(); + + // Initialize single PRNG from seed + let mut prng = SHAKE256::new(); + prng.inject(seed); + prng.flip(); - let nonce = Nonce::deterministic(); - let h = self.compute_pub_key_poly(); - let c = hash_to_point_rpo256(message, &nonce); - let s2 = self.sign_helper::(c, seed); + // Extract sampler seed first, then use same PRNG for nonces + let mut sampler_seed = [0u8; 56]; + prng.extract(&mut sampler_seed); + let mut sampler = signing_key.create_sampler(&sampler_seed); - Signature::new(nonce, h, s2) + let mut sig_buf = [0u8; 666]; // signature_size(9) = 666 + + loop { + // Generate fresh nonce for this attempt + let mut nonce_bytes = [0u8; 40]; + prng.extract(&mut nonce_bytes); + let nonce = Nonce::from_bytes(nonce_bytes); + + // Compute hash-to-point with this nonce + let c = hash_to_point_rpo256(message, &nonce); + let hm: [u16; N] = core::array::from_fn(|i| c.coefficients[i].value()); + + if signing_key.sign_attempt(&hm, &nonce_bytes, &mut sampler, &mut sig_buf) { + // Decode s2 from the signature buffer + // Signature format: header (1 byte) + nonce (40 bytes) + compressed s2 + let mut s2 = [0i16; N]; + fn_dsa_comm::codec::comp_decode(&sig_buf[41..], &mut s2); + + let s2_poly = SignaturePoly::try_from(&s2) + .expect("signature from sign_attempt should be valid"); + + return Signature::new(nonce, h, s2_poly); + } + // On failure, sampler and PRNG have advanced - retry with new nonce + } } /// Signs a byte message using SHAKE256 hash-to-point with a random nonce. /// - /// This produces signatures that match fn-dsa 1-to-1, using: + /// This produces signatures compatible with fn-dsa, using: /// - SHAKE256-based hash-to-point (original Falcon) - /// - Random 40-byte nonce from the provided RNG + /// - Random 40-byte nonce per attempt (FN-DSA compliant) /// - SHAKE256 PRNG for Gaussian sampling /// /// Returns a tuple of (nonce, s2_coefficients) representing the raw signature components. - /// These can be encoded into fn-dsa's signature format if needed. - /// - /// # Parameters - /// - `message`: The byte message to sign - /// - `rng`: Random number generator for nonce and PRNG seed generation - /// - /// # Returns - /// A tuple containing: - /// - `nonce`: The 40-byte random nonce used in signing - /// - `s2`: The signature polynomial coefficients as i16 values pub fn sign_shake256(&self, message: &[u8], rng: &mut R) -> ([u8; 40], [i16; N]) { - use fn_dsa_comm::shake::SHAKE256_PRNG; + use fn_dsa_comm::{DOMAIN_NONE, HASH_ID_ORIGINAL_FALCON, shake::SHAKE256}; - // Generate random 40-byte nonce - let mut nonce = [0u8; 40]; - rng.fill_bytes(&mut nonce); + let mut signing_key = self.decode_signing_key(); - // Generate random 56-byte seed for PRNG + // Generate random 56-byte seed and initialize PRNG let mut seed = [0u8; 56]; rng.fill_bytes(&mut seed); - let s2 = self.sign_shake256_inner::(message, &nonce, &seed); - - // Zeroize seed + let mut prng = SHAKE256::new(); + prng.inject(&seed); + prng.flip(); seed.zeroize(); - (nonce, s2) - } - - /// Inner implementation for SHAKE256 hash-to-point signing. - /// - /// Generic over PRNG type to support both production use (SHAKE256_PRNG) and - /// KAT testing (ChaCha20PRNG to match the C reference implementation). - pub(crate) fn sign_shake256_inner( - &self, - message: &[u8], - nonce: &[u8; 40], - seed: &[u8; 56], - ) -> [i16; N] { - use fn_dsa_comm::{DOMAIN_NONE, HASH_ID_ORIGINAL_FALCON, shake::SHAKE256}; + // Extract sampler seed first, then use same PRNG for nonces + let mut sampler_seed = [0u8; 56]; + prng.extract(&mut sampler_seed); + let mut sampler = signing_key.create_sampler(&sampler_seed); // Compute hvk = SHAKE256(verification_key) - let pk = self.compute_pub_key_poly(); - let vk_bytes = (&pk).to_bytes(); + let mut pk_bytes = [0u8; PK_LEN]; + signing_key.to_verifying_key(&mut pk_bytes); let mut hvk = [0u8; 64]; { let mut sh = SHAKE256::new(); - sh.inject(&vk_bytes); + sh.inject(&pk_bytes); sh.flip(); sh.extract(&mut hvk); } - // Compute hash-to-point using SHAKE256 (original Falcon) - let mut hm = [0u16; N]; - fn_dsa_comm::hash_to_point(nonce, &hvk, &DOMAIN_NONE, &HASH_ID_ORIGINAL_FALCON, message, &mut hm); - - // Convert hm to FalconFelt polynomial for sign_helper - let c = Polynomial::new(hm.iter().map(|&v| FalconFelt::new(v)).collect()); - - // Sign using the internal helper with the specified PRNG type - let s2_poly = self.sign_helper::

(c, seed); - - // Convert SignaturePoly to i16 array - core::array::from_fn(|i| s2_poly.coefficients[i].balanced_value()) - } - - // HELPER METHODS - // -------------------------------------------------------------------------------------------- - - /// Derives the public key corresponding to this secret key using fn-dsa's mq NTT - /// (computes h = g / f [mod ϕ][mod p]). - fn compute_pub_key_poly(&self) -> PublicKey { - const LOGN_U32: u32 = LOG_N as u32; - - let mut h = [0u16; N]; - let mut tmp = [0u16; N]; - // basis layout: [g, f, G, F] - let f = &self.secret_key[1].coefficients; - let g = &self.secret_key[0].coefficients; - mq::mqpoly_div_small(LOGN_U32, f, g, &mut h, &mut tmp); - - let pk = Polynomial::from_u16_ext_array(&h).into(); - - // Zeroize temporaries holding secret-derived values - h.fill(0); - tmp.fill(0); - - pk - } - - /// Signs a message polynomial with the secret key using FLR-based ffsampling. - /// - /// This is a thin wrapper around `fn_dsa_sign::sign_core::sign_poly` which handles - /// the Gaussian sampling and norm bound checking. This implementation uses Fixed-point - /// Linear Real (FLR) arithmetic from rust-fn-dsa, providing deterministic, no_std - /// compatible signing without requiring floating-point hardware. - /// - /// Takes a 56-byte seed and message polynomial representing `c` the hash-to-point of the - /// message to be signed. It outputs a signature polynomial `s2`. - pub(crate) fn sign_helper( - &self, - c: Polynomial, - seed: &[u8; 56], - ) -> SignaturePoly { - use fn_dsa_sign::{flr::FLR as FnFLR, sign_core::sign_poly}; - const LOGN_U32: u32 = LOG_N as u32; - - // Convert hash-to-point polynomial to u16 coefficients - let hm: Vec = c.coefficients.iter().map(|f| f.value()).collect(); - - // Allocate temporary buffer (sign_poly requires at least 9*N FLR elements) - let mut tmp = vec![FnFLR::ZERO; 9 * N]; - - // Call sign_poly which handles sampling, norm checking, and retry loop - let s2_vec = sign_poly::

(LOGN_U32, &hm, seed, &self.basis_fft_flr, &mut tmp); - - // Zeroize temporary buffer - tmp.zeroize(); - - // Convert to SignaturePoly - let s2_coef: [i16; N] = s2_vec - .try_into() - .expect("sign_poly returns exactly N coefficients"); - - SignaturePoly::try_from(&s2_coef) - .expect("signature from sign_poly should be valid; norm was already checked") - } - - /// Computes the FLR basis B = [[g, -f], [G, -F]] in FFT format. - /// Returns an array [b00, b01, b10, b11] where each is N FLR values in FFT domain. - fn compute_basis_fft_flr(basis: &ShortLatticeBasis) -> [FLR; 4 * N] { - let mut result = [FLR::ZERO; 4 * N]; - - // basis is stored as [g, f, G, F] - let g = &basis[0].coefficients; - let f = &basis[1].coefficients; - let big_g = &basis[2].coefficients; - let big_f = &basis[3].coefficients; - - fn_dsa_sign::compute_basis_inner(LOG_N as u32, f, g, big_f, big_g, &mut result); - - result + let mut sig_buf = [0u8; 666]; + + loop { + // Generate fresh nonce for this attempt + let mut nonce = [0u8; 40]; + prng.extract(&mut nonce); + + // Compute hash-to-point using SHAKE256 + let mut hm = [0u16; N]; + fn_dsa_comm::hash_to_point( + &nonce, + &hvk, + &DOMAIN_NONE, + &HASH_ID_ORIGINAL_FALCON, + message, + &mut hm, + ); + + if signing_key.sign_attempt(&hm, &nonce, &mut sampler, &mut sig_buf) { + // Decode s2 from the signature buffer + let mut s2 = [0i16; N]; + fn_dsa_comm::codec::comp_decode(&sig_buf[41..], &mut s2); + return (nonce, s2); + } + // On failure, both PRNGs have advanced - retry with new nonce + } } - /// Deterministically generates a seed for seeding the PRNG used in the trapdoor sampling - /// algorithm used during signature generation. - /// - /// This uses the argument described in [RFC 6979](https://datatracker.ietf.org/doc/html/rfc6979#section-3.5) - /// § 3.5 where the concatenation of the private key and the hashed message, i.e., sk || H(m), - /// is used in order to construct the initial seed of a PRNG. See also [1]. - /// - /// - /// Note that we hash in also a `log_2(N)` where `N = 512` in order to domain separate between - /// different versions of the Falcon DSA, see [1] Section 3.4.1. - /// - /// [1]: https://github.com/algorand/falcon/blob/main/falcon-det.pdf /// Generates a 56-byte signing seed from the message and secret key. /// - /// This uses BLAKE3 in XOF mode to produce a deterministic seed for the - /// SHAKE256 PRNG used in signing. + /// Uses BLAKE3 in XOF mode to derive a deterministic seed for the PRNG. fn generate_signing_seed(&self, message: &Word) -> [u8; 56] { let mut hasher = blake3::Hasher::new(); hasher.update(&[LOG_N]); - hasher.update(&self.to_bytes()); + hasher.update(&self.encoded); hasher.update(&message.to_bytes()); let mut seed = [0u8; 56]; @@ -365,7 +260,7 @@ impl SecretKey { impl PartialEq for SecretKey { fn eq(&self, other: &Self) -> bool { use subtle::ConstantTimeEq; - self.to_bytes().ct_eq(&other.to_bytes()).into() + self.encoded.ct_eq(&other.encoded).into() } } @@ -376,128 +271,31 @@ impl Eq for SecretKey {} impl Serializable for SecretKey { fn write_into(&self, target: &mut W) { - let basis = &self.secret_key; - - // Header byte format: high nibble = 0101 (5) indicates signing key with trim encoding, - // low nibble = log2(N) = 9 for Falcon-512. See fn-dsa specification. - let header: u8 = (5 << 4) | LOG_N; - - let f = &basis[1]; - let g = &basis[0]; - let big_f = &basis[3]; - - let mut buffer = Vec::with_capacity(SK_LEN); - buffer.push(header); - - // Coefficients are already i8 values from ShortLatticeBasis - // Encoding can only fail if the output buffer is incorrectly sized, which cannot - // happen since encode_i8 allocates the buffer internally based on input length - let mut f_i8_encoded = encode_i8(&f.coefficients, WIDTH_SMALL_POLY_COEFFICIENT) - .expect("encoding cannot fail with correctly-sized internally-allocated buffer"); - buffer.extend_from_slice(&f_i8_encoded); - f_i8_encoded.zeroize(); - - let mut g_i8_encoded = encode_i8(&g.coefficients, WIDTH_SMALL_POLY_COEFFICIENT) - .expect("encoding cannot fail with correctly-sized internally-allocated buffer"); - buffer.extend_from_slice(&g_i8_encoded); - g_i8_encoded.zeroize(); - - let mut big_f_i8_encoded = encode_i8(&big_f.coefficients, WIDTH_BIG_POLY_COEFFICIENT) - .expect("encoding cannot fail with correctly-sized internally-allocated buffer"); - buffer.extend_from_slice(&big_f_i8_encoded); - big_f_i8_encoded.zeroize(); - - target.write_bytes(&buffer); - - // Zeroize buffer - buffer.zeroize(); + target.write_bytes(&self.encoded); } } impl Deserializable for SecretKey { fn read_from(source: &mut R) -> Result { - let byte_vector: [u8; SK_LEN] = source.read_array()?; + let encoded: [u8; SK_LEN] = source.read_array()?; - // Decode using fn-dsa's decode_inner which handles f, g, F decoding and G computation - let mut f = [0i8; N]; - let mut g = [0i8; N]; - let mut big_f = [0i8; N]; - let mut big_g = [0i8; N]; - let mut vrfy_key = [0u8; PK_LEN]; - let mut hashed_vrfy_key = [0u8; 64]; - let mut tmp = [0u16; 2 * N]; - - let logn = fn_dsa_sign::decode_inner( - LOG_N as u32, - LOG_N as u32, - &mut f, - &mut g, - &mut big_f, - &mut big_g, - &mut vrfy_key, - &mut hashed_vrfy_key, - &mut tmp, - &byte_vector, - ) - .ok_or(DeserializationError::InvalidValue( - "Failed to decode secret key".to_string(), - ))?; - - if logn != LOG_N as u32 { - return Err(DeserializationError::InvalidValue( - "Unsupported Falcon DSA variant".to_string(), - )); - } + // Validate the encoded bytes by attempting to decode + SigningKey512::decode(&encoded) + .ok_or(DeserializationError::InvalidValue("Failed to decode secret key".to_string()))?; - // Zeroize temporaries - tmp.fill(0); - hashed_vrfy_key.fill(0); - - // Store basis as [g, f, G, F] - let basis = [ - Polynomial::new(g.to_vec()), - Polynomial::new(f.to_vec()), - Polynomial::new(big_g.to_vec()), - Polynomial::new(big_f.to_vec()), - ]; - Ok(Self::from_short_lattice_basis(basis)) + Ok(Self { encoded }) } } -// HELPER FUNCTIONS +// HELPER TYPES // ================================================================================================ -/// Computes the number of bytes needed to store `num_bits` bits, rounding up. -/// -/// This is equivalent to ceiling division: `(num_bits + 7) / 8`. -#[inline] -const fn bits_to_bytes_ceil(num_bits: usize) -> usize { - (num_bits + 7) >> 3 -} - -/// Encodes a slice of i8 values using fn-dsa-comm's trim encoding. -/// -/// # Returns -/// - `Some(Vec)` containing the encoded bytes if successful -/// - `None` if encoding fails (buffer size mismatch) -/// -/// # Security Note -/// When encoding secret key material, the caller is responsible for zeroizing -/// the returned buffer after use. The buffer contains a copy of the input data -/// in encoded form. -pub fn encode_i8(x: &[i8], bits: usize) -> Option> { - let out_len = bits_to_bytes_ceil(x.len() * bits); - let mut buf = vec![0_u8; out_len]; - let written = fn_dsa_comm::codec::trim_i8_encode(x, bits as u32, &mut buf); - if written == out_len { Some(buf) } else { None } -} - /// Adapts a rand 0.9 RNG to the rand_core 0.6 traits expected by fn-dsa keygen. struct FnDsaRng<'a, R: RngCore + CryptoRng> { rng: &'a mut R, } -impl<'a, R: RngCore + CryptoRng> fn_dsa_comm::RngCore for FnDsaRng<'a, R> { +impl fn_dsa_comm::RngCore for FnDsaRng<'_, R> { fn next_u32(&mut self) -> u32 { self.rng.next_u32() } @@ -519,4 +317,4 @@ impl<'a, R: RngCore + CryptoRng> fn_dsa_comm::RngCore for FnDsaRng<'a, R> { } } -impl<'a, R: RngCore + CryptoRng> fn_dsa_comm::CryptoRng for FnDsaRng<'a, R> {} +impl fn_dsa_comm::CryptoRng for FnDsaRng<'_, R> {} diff --git a/miden-crypto/src/dsa/falcon512_rpo/math/mod.rs b/miden-crypto/src/dsa/falcon512_rpo/math/mod.rs index 7365ebd30..a5a007936 100644 --- a/miden-crypto/src/dsa/falcon512_rpo/math/mod.rs +++ b/miden-crypto/src/dsa/falcon512_rpo/math/mod.rs @@ -20,9 +20,6 @@ pub use field::FalconFelt; mod polynomial; pub use polynomial::Polynomial; -// Re-export flr module from fn-dsa-sign fork -pub(crate) use fn_dsa_sign::flr; - pub trait Inverse: Copy + Zero + MulAssign + One { /// Gets the inverse of a, or zero if it is zero. fn inverse_or_zero(self) -> Self; diff --git a/miden-crypto/src/dsa/falcon512_rpo/mod.rs b/miden-crypto/src/dsa/falcon512_rpo/mod.rs index f62f9f747..0ed6157ec 100644 --- a/miden-crypto/src/dsa/falcon512_rpo/mod.rs +++ b/miden-crypto/src/dsa/falcon512_rpo/mod.rs @@ -1,32 +1,18 @@ -//! A deterministic RPO Falcon512 signature over a message. +//! Deterministic RPO Falcon512 signature scheme. //! -//! This version differs from the reference implementation in its use of the RPO algebraic hash -//! function in its hash-to-point algorithm. +//! This implementation differs from standard FN-DSA (FIPS 206) in its use of RPO256 for +//! the hash-to-point algorithm instead of SHAKE256. This enables efficient verification +//! inside Miden's VM. //! -//! Another point of difference is the determinism in the signing process. The approach used to -//! achieve this is the one proposed in [1]. -//! The main challenge in making the signing procedure deterministic is ensuring that the same -//! secret key is never used to produce two inequivalent signatures for the same `c`. -//! For a precise definition of equivalence of signatures see [1]. -//! The reference implementation uses a random nonce per signature in order to make sure that, -//! with overwhelming probability, no two c-s will ever repeat and this non-repetition turns out -//! to be enough to make the security proof of the underlying construction go through in -//! the random-oracle model. +//! ## Deterministic Signing //! -//! Making the signing process deterministic means that we cannot rely on the above use of nonce -//! in the hash-to-point algorithm, i.e., the hash-to-point algorithm is deterministic. It also -//! means that we have to derandomize the trapdoor sampling process and use the entropy in -//! the secret key, together with the message, as the seed of a CPRNG. This is exactly the approach -//! taken in [2] but, as explained at length in [1], this is not enough. The reason for this -//! is that the sampling process during signature generation must be ensured to be consistent -//! across the entire computing stack i.e., hardware, compiler, OS, sampler implementations ... +//! The signing process is deterministic: the same (secret_key, message) pair always produces +//! the same signature. This is achieved by deriving the PRNG seed from the secret key and +//! message using BLAKE3. //! -//! This implementation follows fn-dsa for the fixed-point sampler (FLR) and NTT-based arithmetic. -//! FLR selects an appropriate backend (including AVX2 where available) at compile time, avoiding -//! reliance on platform floating-point behavior while remaining deterministic and no_std friendly. -//! -//! [1]: https://github.com/algorand/falcon/blob/main/falcon-det.pdf -//! [2]: https://datatracker.ietf.org/doc/html/rfc6979#section-3.5 +//! Following FN-DSA semantics, each signing attempt generates a fresh nonce and recomputes +//! hash-to-point. The deterministic seed controls both the nonce generation and signature +//! sampling PRNGs, ensuring reproducibility across retries. use fn_dsa_comm::{FN_DSA_LOGN_512, sign_key_size, vrfy_key_size}; @@ -64,27 +50,7 @@ const LOG_N: u8 = 9; /// Length of nonce used for signature generation. const SIG_NONCE_LEN: usize = 40; -/// Length of the preversioned portion of the fixed nonce. -/// -/// Since we use one byte to encode the version of the nonce, this is equal to `SIG_NONCE_LEN - 1`. -const PREVERSIONED_NONCE_LEN: usize = 39; - -/// Current version of the fixed nonce. -/// -/// The usefulness of the notion of versioned fixed nonce is discussed in Section 2.1 in [1]. -/// -/// [1]: https://github.com/algorand/falcon/blob/main/falcon-det.pdf -const NONCE_VERSION_BYTE: u8 = 1; - -/// The preversioned portion of the fixed nonce constructed following [1]. -/// -/// Note that reference [1] uses the term salt instead of nonce. -const PREVERSIONED_NONCE: [u8; PREVERSIONED_NONCE_LEN] = [ - 9, 82, 80, 79, 45, 70, 65, 76, 67, 79, 78, 45, 68, 69, 84, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -]; - -/// Number of filed elements used to encode a nonce. +/// Number of field elements used to encode a nonce. const NONCE_ELEMENTS: usize = 8; /// Public key length as a u8 vector. @@ -97,13 +63,9 @@ pub const SK_LEN: usize = sign_key_size(FN_DSA_LOGN_512); const SIG_POLY_BYTE_LEN: usize = 625; /// Signature size when serialized as a u8 vector. +/// 1 (header) + 40 (nonce) + 625 (s2 poly) + 897 (public key) = 1563 #[cfg(test)] -const SIG_SERIALIZED_LEN: usize = 1524; - -// TYPE ALIASES -// ================================================================================================ - -type ShortLatticeBasis = [Polynomial; 4]; +const SIG_SERIALIZED_LEN: usize = 1563; // NONCE // ================================================================================================ @@ -113,35 +75,6 @@ type ShortLatticeBasis = [Polynomial; 4]; pub struct Nonce([u8; SIG_NONCE_LEN]); impl Nonce { - /// Returns a new deterministic [Nonce]. - /// - /// This is used in deterministic signing following [1] and is composed of two parts: - /// - /// 1. a byte serving as a version byte, - /// 2. a pre-versioned fixed nonce which is the UTF8 encoding of the domain separator - /// "RPO-FALCON-DET" padded with enough zeros to make it of size 39 bytes. - /// - /// The usefulness of the notion of versioned fixed nonce is discussed in Section 2.1 in [1]. - /// - /// [1]: https://github.com/algorand/falcon/blob/main/falcon-det.pdf - pub fn deterministic() -> Self { - let mut nonce_bytes = [0u8; SIG_NONCE_LEN]; - nonce_bytes[0] = NONCE_VERSION_BYTE; - nonce_bytes[1..].copy_from_slice(&PREVERSIONED_NONCE); - Self(nonce_bytes) - } - - /// Returns a new [Nonce] drawn from the provided RNG. - /// - /// This is used only in testing against the test vectors of the reference (non-deterministic) - /// Falcon DSA implementation. - #[cfg(test)] - pub fn random(rng: &mut R) -> Self { - let mut nonce_bytes = [0u8; SIG_NONCE_LEN]; - rng.fill_bytes(&mut nonce_bytes); - Self::from_bytes(nonce_bytes) - } - /// Returns the underlying concatenated bytes of this nonce. pub fn as_bytes(&self) -> [u8; SIG_NONCE_LEN] { self.0 @@ -172,18 +105,13 @@ impl Nonce { impl Serializable for &Nonce { fn write_into(&self, target: &mut W) { - target.write_u8(self.0[0]) + target.write_bytes(&self.0); } } impl Deserializable for Nonce { fn read_from(source: &mut R) -> Result { - let nonce_version: u8 = source.read()?; - - let mut nonce_bytes = [0u8; SIG_NONCE_LEN]; - nonce_bytes[0] = nonce_version; - nonce_bytes[1..].copy_from_slice(&PREVERSIONED_NONCE); - + let nonce_bytes: [u8; SIG_NONCE_LEN] = source.read_array()?; Ok(Self(nonce_bytes)) } } diff --git a/miden-crypto/src/dsa/falcon512_rpo/signature.rs b/miden-crypto/src/dsa/falcon512_rpo/signature.rs index bd0ec3f55..47df595a4 100644 --- a/miden-crypto/src/dsa/falcon512_rpo/signature.rs +++ b/miden-crypto/src/dsa/falcon512_rpo/signature.rs @@ -15,50 +15,38 @@ use crate::{Word, utils::zeroize::Zeroize}; /// A deterministic RPO Falcon512 signature over a message. /// -/// The signature is a pair of polynomials (s1, s2) in (Z_p\[x\]/(phi))^2 a nonce `r`, and a public +/// 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 /// -/// The signature verifies against a public key `pk` if and only if: +/// The signature verifies against a public key `pk` if and only if: /// 1. s1 = c - s2 * h /// 2. |s1|^2 + |s2|^2 <= β² (where β² = 34034726 for Falcon-512) /// /// where |.| is the norm and: -/// - c = HashToPoint(r || message) +/// - c = HashToPoint(r || message) using RPO256 /// - 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. -/// -/// To summarize the main points of differences with the reference implementation, we have that: /// -/// 1. the hash-to-point algorithm is made deterministic by using a fixed nonce `r`. This fixed -/// nonce is formed as `nonce_version_byte || preversioned_nonce` where `preversioned_nonce` is a -/// 39-byte string that is defined as: i. a byte representing `log_2(512)`, followed by ii. the -/// UTF8 representation of the string "RPO-FALCON-DET", followed by iii. the required number of -/// 0_u8 padding to make the total length equal 39 bytes. Note that the above means in particular -/// that only the `nonce_version_byte` needs to be serialized when serializing the signature. -/// This reduces the deterministic signature compared to the reference implementation by 39 -/// bytes. -/// 2. the RNG used in the trapdoor sampler (i.e., the ffSampling algorithm) is ChaCha20Rng seeded -/// with the `Blake3` hash of `log_2(512) || sk || message`. +/// ## Differences from Standard FN-DSA /// -/// The signature is serialized as: +/// 1. **Hash-to-point**: Uses RPO256 instead of SHAKE256 for efficient ZK verification. +/// 2. **Deterministic signing**: The PRNG seed is derived from BLAKE3(LOG_N || sk || message), +/// ensuring reproducible signatures. Following FN-DSA semantics, each signing attempt generates +/// a fresh nonce and recomputes hash-to-point. /// -/// 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. For RPO Falcon512, the header -/// byte is set to `10111001` to differentiate it from the standardized instantiation of the -/// Falcon signature. -/// 2. 1 byte for the nonce version. -/// 4. 625 bytes encoding the `s2` polynomial above. +/// ## Serialization Format /// -/// In addition to the signature itself, the polynomial h is also serialized with the signature as: -/// 1 byte for `LOG_N` (9) followed by 896 bytes for the public key. The total size including h is -/// 1524 bytes. +/// The signature is serialized as: +/// 1. Header byte (1 byte): Specifies encoding algorithm. Set to `0xB9` for RPO Falcon512. +/// 2. Nonce (40 bytes): The nonce used in hash-to-point. +/// 3. s2 polynomial (625 bytes): Compressed signature polynomial. +/// 4. Public key (897 bytes): 1 byte LOG_N + 896 bytes encoded h polynomial. /// -/// [1]: https://github.com/algorand/falcon/blob/main/falcon-det.pdf -/// [2]: https://datatracker.ietf.org/doc/html/rfc6979#section-3.5 +/// Total serialized size: 1563 bytes. #[derive(Debug, Clone, PartialEq, Eq)] pub struct Signature { header: SignatureHeader, @@ -278,45 +266,22 @@ fn decode_signature_poly(input: &[u8]) -> Result<[FalconFelt; N], Deserializatio /// the message `s2` and a public key polynomial and returns `true` if the signature is valid, /// otherwise it returns `false`. fn verify_helper(c: &Polynomial, s2: &SignaturePoly, h: &PublicKey) -> bool { - use fn_dsa_comm::mq; - - // Reuse fn-dsa's mq routines for NTT operations and norm checks. - // All conversions go through external representation [0, q-1] expected by mq. - const LOGN_U32: u32 = LOG_N as u32; - - // s2 as signed coefficients (already bounded by SignaturePoly::try_from) - let s2_signed = s2.to_i16_balanced_array(); + use fn_dsa_vrfy::VerifyingKey; - // c in external representation - let mut t1 = c.to_u16_ext_array(); - mq::mqpoly_ext_to_int(LOGN_U32, &mut t1); - - // h in NTT domain - let mut h_ntt = h.to_u16_ext_array(); - mq::mqpoly_ext_to_int(LOGN_U32, &mut h_ntt); - mq::mqpoly_int_to_NTT(LOGN_U32, &mut h_ntt); - - // t2 <- s2 in NTT domain - let mut t2 = [0u16; N]; - mq::mqpoly_signed_to_ext(LOGN_U32, &s2_signed, &mut t2); - mq::mqpoly_ext_to_int(LOGN_U32, &mut t2); - mq::mqpoly_int_to_NTT(LOGN_U32, &mut t2); - - // t2 <- s2 * h (in NTT domain) - mq::mqpoly_mul_ntt(LOGN_U32, &mut t2, &h_ntt); - mq::mqpoly_NTT_to_int(LOGN_U32, &mut t2); + // Decode the public key into fn-dsa's VerifyingKey512. + let vk = match h.decode_verifying_key() { + Some(vk) => vk, + None => return false, + }; - // t1 <- c - s2*h (internal), then convert to external for norm - mq::mqpoly_sub_int(LOGN_U32, &mut t1, &t2); - mq::mqpoly_int_to_ext(LOGN_U32, &mut t1); + // Convert c to external representation [0, q-1] + let hm: [u16; N] = core::array::from_fn(|i| c.coefficients[i].value()); - // Squared norms of s1 and s2 - let norm1 = mq::mqpoly_sqnorm(LOGN_U32, &t1); - let norm2 = mq::signed_poly_sqnorm(LOGN_U32, &s2_signed); + // Convert s2 to signed coefficients + let s2_signed = s2.to_i16_balanced_array(); - // Guard against u32 overflow when adding norms by checking norm1 <= u32::MAX - norm2, - // then apply the fn-dsa bound (inclusive) on ||(s1,s2)||^2. - norm1 < norm2.wrapping_neg() && (norm1 + norm2) <= mq::SQBETA[LOG_N as usize] + // Use fn-dsa's verify_prehash + vk.verify_prehash(&hm, &s2_signed) } /// Checks whether a set of coefficients is a valid one for a signature polynomial. diff --git a/miden-crypto/src/dsa/falcon512_rpo/tests/data.rs b/miden-crypto/src/dsa/falcon512_rpo/tests/data.rs index 7da81477c..486e2e0d7 100644 --- a/miden-crypto/src/dsa/falcon512_rpo/tests/data.rs +++ b/miden-crypto/src/dsa/falcon512_rpo/tests/data.rs @@ -1,393 +1,87 @@ -use crate::dsa::falcon512_rpo::SIG_SERIALIZED_LEN; - -/// Serialized deterministic RPO-Falcon-512 signature intended for use as a test vector -/// for the determinism in the signing procedure across platforms. +/// Serialized deterministic RPO-Falcon-512 signature for cross-platform determinism testing. /// -/// This was regenerated after switching to fn-dsa-sign's PRNG-based signing infrastructure. -/// The signature uses SHAKE256_PRNG internally and produces valid, deterministic signatures. -pub(crate) const DETERMINISTIC_SIGNATURE: [u8; SIG_SERIALIZED_LEN] = [ - 185, 1, 102, 198, 110, 243, 39, 73, 23, 129, 37, 117, 90, 76, 202, 69, 213, 168, 200, 217, 179, - 92, 253, 90, 181, 204, 177, 32, 147, 249, 153, 189, 115, 38, 132, 177, 19, 35, 80, 51, 30, 38, - 109, 151, 145, 42, 110, 66, 128, 156, 100, 201, 220, 129, 17, 230, 233, 161, 45, 37, 119, 225, - 51, 75, 220, 118, 165, 5, 197, 69, 189, 70, 34, 2, 21, 5, 160, 253, 205, 32, 101, 219, 121, - 162, 212, 77, 237, 206, 37, 186, 122, 221, 34, 68, 102, 164, 232, 79, 238, 117, 206, 41, 199, - 77, 51, 109, 107, 161, 187, 76, 188, 176, 118, 22, 42, 66, 99, 58, 30, 134, 86, 218, 143, 251, - 124, 120, 78, 253, 134, 111, 240, 201, 156, 47, 13, 218, 20, 251, 84, 18, 165, 25, 92, 196, 37, - 44, 233, 174, 221, 249, 218, 136, 233, 3, 174, 102, 139, 154, 59, 81, 73, 210, 204, 255, 102, - 70, 131, 20, 228, 107, 123, 111, 164, 216, 35, 181, 152, 26, 40, 141, 95, 97, 8, 209, 233, 184, - 161, 100, 9, 250, 195, 148, 175, 45, 197, 115, 200, 51, 211, 60, 74, 54, 175, 197, 98, 178, - 244, 4, 128, 206, 96, 100, 54, 187, 151, 72, 69, 119, 83, 144, 188, 117, 94, 126, 215, 77, 243, - 77, 155, 221, 206, 121, 21, 169, 232, 16, 110, 255, 114, 209, 101, 174, 116, 164, 76, 102, 196, - 143, 16, 249, 153, 151, 104, 245, 124, 37, 17, 164, 96, 23, 102, 7, 13, 240, 231, 194, 222, 57, - 243, 34, 174, 182, 106, 44, 150, 157, 162, 39, 169, 5, 54, 109, 251, 128, 233, 118, 106, 141, - 82, 158, 150, 198, 164, 54, 186, 54, 116, 204, 48, 9, 98, 185, 46, 21, 73, 133, 54, 209, 16, - 58, 147, 186, 23, 185, 80, 66, 139, 21, 71, 154, 126, 169, 48, 185, 237, 223, 10, 64, 80, 100, - 202, 237, 115, 175, 124, 13, 25, 194, 163, 34, 115, 29, 34, 39, 0, 214, 188, 106, 103, 121, 90, - 153, 84, 171, 72, 129, 52, 20, 84, 110, 20, 101, 53, 253, 23, 172, 147, 39, 105, 146, 74, 132, - 27, 10, 226, 50, 214, 115, 216, 214, 154, 251, 161, 210, 239, 174, 239, 127, 25, 171, 89, 151, - 78, 129, 35, 133, 108, 81, 79, 172, 97, 41, 195, 102, 19, 232, 68, 152, 124, 189, 79, 108, 238, - 217, 195, 142, 61, 104, 212, 227, 224, 65, 72, 19, 54, 160, 36, 102, 111, 217, 63, 124, 122, - 124, 170, 102, 141, 217, 74, 172, 122, 19, 188, 218, 70, 151, 143, 140, 69, 116, 226, 195, 231, - 207, 111, 12, 138, 204, 43, 218, 232, 226, 152, 251, 170, 89, 182, 140, 195, 57, 171, 237, 174, - 3, 219, 77, 88, 246, 97, 115, 72, 88, 118, 27, 241, 177, 233, 184, 222, 8, 42, 76, 150, 226, 9, - 54, 90, 54, 70, 41, 165, 54, 30, 236, 35, 30, 68, 88, 170, 199, 213, 126, 172, 70, 245, 33, 42, - 87, 95, 10, 182, 212, 140, 12, 209, 30, 161, 97, 214, 189, 164, 11, 70, 139, 112, 85, 254, 146, - 149, 170, 155, 226, 161, 237, 98, 163, 13, 17, 23, 162, 191, 126, 238, 66, 148, 201, 213, 104, - 150, 175, 149, 214, 0, 180, 197, 183, 84, 248, 34, 111, 189, 232, 248, 224, 231, 31, 57, 161, - 81, 190, 183, 188, 198, 228, 186, 244, 41, 71, 111, 125, 189, 133, 49, 56, 195, 17, 174, 51, - 42, 245, 157, 147, 142, 32, 132, 167, 217, 154, 115, 111, 79, 199, 231, 49, 144, 64, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 9, 87, 240, 198, 27, 184, 21, 116, 184, 5, 145, 176, 111, 30, 97, 74, - 244, 41, 96, 199, 196, 99, 179, 144, 113, 182, 201, 1, 153, 110, 197, 242, 179, 208, 226, 132, - 25, 46, 45, 98, 69, 135, 208, 103, 89, 229, 4, 58, 236, 51, 54, 124, 205, 107, 104, 93, 33, - 102, 240, 178, 50, 6, 22, 77, 61, 198, 112, 84, 252, 42, 20, 160, 142, 132, 160, 240, 166, 72, - 146, 189, 11, 50, 49, 29, 232, 128, 150, 72, 8, 105, 164, 185, 16, 124, 110, 176, 201, 0, 10, - 73, 34, 100, 54, 51, 217, 64, 128, 230, 83, 66, 166, 18, 34, 179, 170, 16, 21, 11, 195, 44, 38, - 217, 200, 144, 136, 141, 124, 99, 168, 104, 24, 25, 193, 251, 73, 238, 46, 233, 132, 104, 101, - 64, 25, 62, 128, 12, 4, 81, 111, 5, 23, 202, 214, 239, 72, 154, 122, 112, 20, 90, 238, 177, 94, - 192, 149, 101, 229, 71, 203, 54, 112, 93, 8, 142, 204, 205, 190, 249, 205, 199, 222, 14, 183, - 17, 94, 85, 170, 210, 76, 175, 179, 237, 17, 231, 81, 174, 3, 140, 222, 205, 37, 223, 18, 155, - 84, 112, 218, 155, 3, 70, 101, 81, 125, 116, 82, 240, 197, 207, 136, 16, 27, 67, 34, 156, 146, - 58, 73, 210, 194, 44, 166, 36, 56, 10, 157, 129, 32, 154, 131, 133, 73, 138, 1, 201, 198, 177, - 146, 29, 137, 50, 8, 32, 87, 93, 125, 24, 70, 145, 76, 194, 47, 8, 167, 101, 53, 152, 189, 56, - 105, 19, 89, 182, 34, 98, 149, 160, 63, 118, 51, 4, 220, 164, 206, 153, 82, 4, 220, 164, 157, - 57, 16, 165, 148, 30, 45, 95, 93, 102, 218, 19, 235, 11, 175, 204, 104, 192, 209, 34, 5, 11, - 170, 124, 177, 159, 99, 215, 7, 12, 55, 106, 118, 6, 233, 13, 217, 2, 19, 73, 165, 79, 67, 6, - 145, 230, 108, 13, 147, 107, 226, 71, 154, 245, 16, 24, 119, 214, 138, 73, 154, 2, 86, 118, - 162, 125, 167, 251, 174, 28, 23, 90, 193, 201, 3, 233, 40, 22, 88, 185, 101, 33, 80, 227, 23, - 78, 52, 197, 218, 76, 68, 86, 174, 29, 17, 77, 11, 179, 119, 152, 254, 219, 182, 3, 135, 48, - 105, 215, 18, 246, 225, 68, 59, 38, 240, 153, 25, 40, 236, 94, 24, 194, 196, 156, 110, 230, - 104, 60, 47, 240, 145, 108, 45, 0, 2, 231, 56, 154, 75, 121, 142, 120, 138, 120, 121, 11, 84, - 12, 30, 217, 99, 168, 233, 122, 101, 114, 42, 196, 131, 169, 40, 159, 80, 47, 246, 227, 89, 42, - 65, 42, 78, 0, 157, 231, 153, 165, 188, 162, 243, 124, 225, 26, 20, 80, 96, 106, 173, 133, 253, - 65, 181, 170, 102, 115, 215, 135, 148, 196, 173, 114, 94, 102, 246, 45, 65, 49, 177, 80, 162, - 149, 147, 222, 233, 139, 8, 230, 105, 165, 129, 29, 101, 15, 218, 62, 137, 13, 160, 168, 178, - 141, 142, 164, 144, 191, 39, 198, 66, 227, 154, 192, 196, 65, 195, 209, 40, 102, 36, 7, 97, 8, - 36, 59, 150, 78, 83, 112, 221, 77, 245, 174, 222, 56, 230, 77, 155, 15, 134, 117, 22, 19, 85, - 72, 81, 43, 120, 67, 75, 13, 31, 79, 167, 160, 40, 148, 36, 148, 156, 128, 200, 235, 130, 56, - 96, 160, 148, 156, 151, 73, 1, 151, 1, 112, 229, 101, 161, 239, 93, 184, 112, 117, 203, 103, 5, - 2, 20, 22, 6, 88, 43, 238, 13, 51, 16, 137, 141, 234, 238, 6, 23, 160, 73, 56, 2, 11, 205, 71, - 88, 118, 130, 72, 73, 111, 255, 74, 8, 43, 3, 19, 140, 48, 173, 104, 59, 97, 244, 238, 82, 93, - 182, 221, 196, 187, 18, 1, 45, 70, 123, 199, 170, 28, 196, 12, 29, 254, 249, 7, 211, 223, 117, - 148, 205, 123, 62, 167, 8, 172, 37, 167, 89, 216, 208, 166, 11, 29, 182, 251, 107, 3, 186, 60, - 38, 35, 0, 225, 232, 211, 24, 221, 55, 71, 245, 80, 10, 42, 142, 199, 18, 145, 26, 161, 138, - 225, 146, 233, 179, 21, 116, 81, 10, 152, 131, 9, 192, 88, 1, 40, 242, 245, 166, 33, 98, 143, - 70, 218, 245, 173, 93, 229, 4, 81, 104, 247, 224, 225, 206, 129, 17, 225, 91, 20, 134, 254, - 154, 80, 222, 168, 68, 175, 4, 94, 17, 194, 155, 46, 76, 123, 191, 218, 211, 164, 114, 101, - 235, 24, 252, 144, 97, 62, 208, 11, 178, 9, 247, 195, 124, 170, 168, 76, 90, 28, 200, 123, 46, - 150, 8, 144, 94, 67, 3, 29, 137, 95, 197, 200, 120, 4, 83, 2, 25, 116, 149, 201, 15, 104, 141, - 161, 157, 254, 219, 213, 83, 240, 82, 184, 68, 132, 136, 142, 155, 55, 12, 168, 224, 3, 106, - 65, 114, 38, 90, 42, 190, 21, 151, 169, 114, 73, 166, 24, 107, 88, 124, 145, 150, 22, 89, 200, - 68, 154, 137, 8, 154, 230, 142, 68, 129, 76, 175, 64, 0, 17, 60, 149, 252, 61, 198, 90, 218, - 247, 77, 14, 101, 36, 131, 159, 133, 38, 100, 158, 58, 10, 94, 146, 152, 130, 87, 210, 149, 38, - 255, -]; - -// ============================================================================= -// Known Answer Test (KAT) vectors from fn-dsa C reference implementation -// These come from fn-dsa-sign/src/lib.rs which were verified against the -// original Falcon C implementation. -// ============================================================================= - -/// Secret key polynomial f for KAT from C reference implementation -#[allow(dead_code)] -pub(crate) const FN_DSA_KAT_512_F: [i8; 512] = [ - -4, -2, -5, -1, 4, -2, 0, -3, -1, 1, -2, -2, -6, -3, 3, -5, -1, - 4, -3, -8, 4, -1, 2, -1, -8, 5, -6, -3, 6, 0, -2, 4, 5, -6, 2, - 3, 6, 4, 2, 3, 3, 7, 0, 1, 5, -3, -1, -9, -1, 6, -2, -5, 4, 0, - 4, -2, 10, -4, -3, 4, -7, -1, -7, -2, -1, -6, 5, -1, -9, 3, 2, - -5, 4, -2, 2, -4, 4, -3, -1, 0, 5, 2, 2, -1, -9, -7, -2, -1, 0, - 3, 1, 0, -1, -2, -5, 4, -1, -1, 3, -1, 1, 4, -3, 2, -5, -2, 2, - -4, 3, 6, 3, 9, 1, -2, 4, -1, -1, -6, -2, -2, 4, 5, -1, 0, 10, - -2, 1, -2, -3, 0, -4, -4, -1, 0, 1, -5, -3, -7, -2, -1, 2, -6, - 3, 0, 0, 4, -4, 0, 0, -5, -2, 5, -8, 8, 5, 4, 10, -4, 3, 8, 5, - 1, -7, 0, -5, 0, -4, 3, -4, -2, 2, -2, 6, 8, 2, -1, 4, -4, -2, - 1, 0, 3, 7, 0, 9, -3, 1, 4, -3, 2, -1, 5, -8, 4, -1, 1, -8, 2, - 4, -9, -3, 1, 3, -1, -7, 5, 5, 4, -3, 0, -7, -3, -1, -6, -7, 0, - -3, 0, 3, -3, 0, -3, 1, 3, 4, -6, -6, -3, 6, 0, 2, -5, 1, -3, - -6, -6, -1, -7, -2, -4, 3, 0, -4, -1, 2, 7, -7, -2, 4, 2, 0, 1, - -1, -3, 2, 1, 8, -1, 1, -2, 1, -1, 1, 4, 0, -4, 4, 3, -2, 6, -3, - -2, 1, 2, 3, 6, 5, -4, -7, -6, 4, 3, -4, 3, -3, 3, -3, 2, -1, 1, - 5, -2, 2, 1, 0, -7, 0, 0, -1, 4, -3, 2, 1, -3, 5, 4, -6, -1, -3, - 2, -1, -8, 4, 2, 4, 0, 1, -5, 8, 5, 4, -3, -1, -2, 4, 0, 2, -2, - 0, -2, -1, -7, 5, 0, 1, 2, 1, -2, 2, -1, 1, -4, 1, 0, 4, -4, 0, - 5, 1, 4, -5, -2, -3, -2, 1, 3, 1, 2, 5, 12, 0, -1, 4, -6, 1, -4, - 3, -5, -4, 4, 2, -2, -6, 1, 1, 3, -1, 0, -4, -4, -4, 6, -2, 4, - -3, 0, -2, -1, 0, -6, -3, -2, 0, 6, 5, -5, -5, 3, 0, 3, -3, -2, - 5, 7, -3, 1, -1, 0, 3, 0, 3, -7, 2, -4, -4, 1, 1, 1, 0, -3, -8, - 3, 6, 1, -2, -7, 3, 3, 4, -1, -2, -5, 9, 7, 1, 2, -4, 4, 0, -11, - 3, 0, -3, -5, 5, -1, -1, 7, 6, -1, 6, 3, 9, 5, -2, -3, -3, 1, - -2, 0, -1, 1, -2, 2, 0, -5, -1, -4, -2, 2, -1, -3, 0, -3, 0, 1, - 3, -3, 2, 5, 8, -2, 3, -4, -7, 0, 4, -8, 1, 8, -2, 1, -1, 2, 0, - -2, 1, 3, 3, 4, -2, -4, 3, -4, 2, 3, -2, -4, 1, -4, 10, 2 -]; - -/// Secret key polynomial g for KAT from C reference implementation -#[allow(dead_code)] -pub(crate) const FN_DSA_KAT_512_G: [i8; 512] = [ - -1, 5, -7, -1, -4, 6, 4, -1, -4, -13, -1, -5, -2, -8, 2, 1, 4, - 2, 0, 0, 2, 0, -1, 2, 5, -5, -8, 8, 1, 11, 0, -8, -4, 1, 1, -6, - -4, 1, -3, 0, -10, -4, -6, -3, -2, 1, 6, 2, 8, -2, 2, -2, 1, 3, - -4, 2, -1, -1, -2, -2, -3, 0, -3, 2, -3, 2, -3, -4, 2, 3, 4, -5, - 6, -3, -2, -1, -1, -6, -2, 1, -4, -7, 8, 0, 2, -2, 2, 0, 1, 0, - 4, 9, 7, 0, -1, -1, 4, -3, -2, 6, 6, 0, 1, 7, -6, -5, 5, 1, 4, - -1, 0, -2, 3, -4, 1, -1, -3, -2, 0, -1, -7, -8, -1, 2, 0, -5, 0, - 1, -4, 6, -5, 6, 4, 1, -4, -5, 8, -1, 1, -2, 1, 1, 1, 3, 0, -1, - 1, 1, -4, -5, -4, 2, -3, 2, -2, 3, 7, -4, 4, -1, -2, 4, -4, -5, - 2, 6, -7, 5, -1, 1, 3, 0, -5, -5, 3, -2, -3, -1, -6, 0, 2, 3, 2, - 7, -3, -2, -2, 1, -5, 3, 3, -7, 0, 4, 4, -1, 2, -3, 1, 3, -1, - -1, 0, -7, -6, -3, 7, -3, 5, -5, 1, -2, 0, 9, -2, 3, -1, -5, -3, - -5, 3, 1, -4, -3, 2, -2, 2, 8, -1, 0, 5, -3, -2, -6, 4, 0, 3, - -3, -3, 4, -1, 0, 0, -2, -1, 3, 7, 4, 5, -1, 8, 0, -1, -6, -3, - 4, 3, -3, 5, 2, -1, -2, 1, -1, 3, -2, -6, 4, 0, 0, -4, 1, 6, 2, - 0, 10, 9, 2, -2, 0, 2, 1, -3, -1, -1, 3, 2, 1, 1, -3, -2, 7, 2, - -1, 5, -3, -2, 1, -2, 2, -2, -4, 3, 2, 1, -4, 1, 4, 3, -7, -4, - 2, -5, -2, 5, -3, 1, -4, -5, 1, 0, 0, 0, 7, -5, -1, 2, 2, -3, 6, - -6, 4, -3, -5, -6, -7, -4, 3, -2, -2, -10, -3, 2, -1, -6, -4, 1, - 2, 2, 1, 4, 1, -5, -10, -2, 2, -4, 4, 4, -2, 1, 4, -3, 0, -6, - -3, 1, 5, -7, -6, -4, 8, -1, 0, -1, 6, -3, -2, -2, 6, 2, 3, -3, - -3, 5, -2, 1, 1, -4, -4, 8, 0, 3, 2, 3, 7, 4, 3, 2, -6, -9, 0, - -8, 11, -2, 2, -2, -2, 3, 0, -6, 2, -1, 4, 2, -2, 0, -3, -7, -1, - -1, 0, -1, -4, -2, -5, 3, -4, 2, 2, -1, -1, 7, -1, 3, 6, -7, 1, - -5, 0, -7, 4, 3, -5, -1, 0, 3, -4, 1, 2, -7, 1, -2, -8, -2, -5, - -5, 1, -4, -4, 4, -3, -2, 2, -4, -8, -1, 0, -9, 5, -1, -2, 3, 2, - 6, -1, 1, -1, -5, 5, 9, 3, -6, -5, 1, -6, 0, 2, -4, 6, 2, 7, 2, - 15, 0, -2, 9, 0, 1, 6, 4, -1, -1, -6, -3, 3, 1, -6, -3, 2, 2, -2 -]; - -/// Secret key polynomial F for KAT from C reference implementation -#[allow(dead_code)] -pub(crate) const FN_DSA_KAT_512_BIG_F: [i8; 512] = [ - 0, -25, -39, 21, 7, -5, -10, 4, -1, -38, -9, -1, 4, -23, 15, -1, - 8, 1, -38, 41, 29, 22, 9, 12, -46, 0, 9, -17, -19, 32, 38, -3, - 14, 6, 2, -6, -18, -1, 23, 80, -12, -20, 24, 22, -31, -38, -11, - 8, 17, 18, 19, -10, 0, -1, 28, -5, -28, -33, 4, -31, -33, -8, - -9, -44, 46, -11, -5, -21, -22, -7, 1, -11, 33, -8, 12, -7, -6, - 63, 17, 12, -49, -11, -31, -8, 7, -28, 33, -28, -19, 8, 46, -73, - 9, 32, 18, 7, -43, 0, -6, -4, 8, -39, -17, 11, 15, -25, -9, -28, - -2, 24, -23, 10, -15, 4, 41, 46, 18, 2, -3, -29, 11, -3, 20, 35, - 21, 23, 5, -8, -3, -27, -69, 0, 26, -29, -24, 8, 19, 6, -14, - -18, 47, 5, 21, -50, 17, -44, -36, 24, 9, 16, -38, -5, -54, 34, - 13, 31, -2, 9, 8, -12, -14, -17, 28, -59, -20, 19, 31, 14, 14, - 7, -32, 37, 5, -3, -7, -6, 21, -29, -33, 23, -25, -23, 14, 38, - -29, -33, -9, 23, -43, 18, -12, 2, 30, 32, -28, -21, 42, 1, 6, - -6, 58, 34, -22, 1, 5, -2, -8, 14, -19, -4, -6, 10, -3, -3, 32, - 18, -19, -12, 49, 13, 4, -18, 57, 37, -19, 25, 14, 18, -51, 13, - 4, 4, 17, -37, -2, 1, 41, -36, -8, -13, 49, -6, 9, 46, -36, -6, - -20, -18, -6, -29, -42, -21, -25, -29, 5, -41, 51, 49, -20, -22, - -9, 3, -6, -52, 10, 41, 12, -27, -20, 31, -17, -23, -16, 3, 44, - -3, -5, -2, 0, -22, 14, -30, -41, 3, -27, 3, 18, 38, 10, 49, 45, - -13, -27, -4, -10, -67, -1, -17, -2, 72, 46, 20, 24, 22, 16, 25, - 6, -6, -31, 2, 0, -13, -14, 9, 4, 31, 18, 22, 12, 59, -1, -3, - -24, -47, -10, 48, 37, -34, -32, -4, 18, -2, 52, -8, -7, 34, - -44, -14, -21, -49, -35, 41, -4, 31, 3, 23, 9, 8, 0, -24, 38, - -9, -9, 4, -10, -55, -19, 21, 27, 22, 41, 6, -23, 41, -2, 28, - -46, 20, 52, 16, 20, 32, 18, 2, -3, 9, 16, 33, -18, 12, 6, -9, - -19, 1, -5, -15, -17, 6, -3, 4, -22, 30, -34, 43, -4, 9, -3, - -33, -43, -5, -13, -56, 38, 16, 11, -36, 11, -4, -56, 2, 0, -19, - -45, -8, -34, 16, 31, -3, 16, 27, -16, -9, 8, 45, -51, -20, 62, - -17, -4, 4, 17, -45, 4, -15, -19, 39, 39, 15, 17, -19, 2, 45, - 36, -22, 16, -23, 28, 34, 12, 5, 10, -7, 28, -35, 17, -37, -50, - -28, 19, -25, 9, 45, -6, -7, -16, 57, 27, 50, -30, 2, -10, -1, - -57, -49, -23, 0, -9, -36, -4, -3, 32, -6, -25, 67, -27, -19, - 25, -6, 1, -17, -14, 0, 29, 26, -12, -20, 44, 14, 10, 8, -11, - -18, -53, 22, 25, 27, 35, 6, -16, 12, 71, -8 -]; - -/// Secret key polynomial G for KAT from C reference implementation -#[allow(dead_code)] -pub(crate) const FN_DSA_KAT_512_BIG_G: [i8; 512] = [ - 27, 6, 12, -3, -31, -42, 27, 17, 11, 8, 34, 6, -3, 2, 11, -11, - 18, 48, 1, 21, -7, -6, 9, 33, -18, -40, -55, -9, -71, -50, 32, - -36, 11, 4, 29, 33, 10, -19, -43, -10, 22, -36, -23, -21, -14, - -47, 25, -4, -14, 30, 16, -18, -11, 6, -37, -27, -12, 6, 7, 33, - -36, 33, -2, 12, -21, 1, 16, 49, -11, -16, -41, 15, 11, 8, 20, - -15, 26, -8, 11, -43, -36, 28, 2, -47, -30, -47, -1, 1, 48, -6, - -22, 24, -20, -3, -1, -15, -12, 62, 12, 7, -9, 15, -71, 49, 22, - 27, 20, -8, -28, -13, -31, 18, 28, 54, 29, 5, 0, 33, -5, -22, - -21, -12, -14, -2, 11, -24, 32, -26, -71, 21, -15, -20, -12, 36, - -5, 35, 46, 13, -34, -8, 10, -10, 10, 40, -52, 8, 0, 18, -33, - -10, 8, 43, -8, -6, -31, -17, 19, 30, 12, -9, 8, -19, -32, -18, - -1, -37, 4, 43, 27, 14, -6, -14, -44, -34, -8, 16, -39, 13, 6, - -32, 8, 17, -12, 23, -44, -25, -66, -12, -31, 30, 14, -9, -5, - -10, 44, -12, -2, -43, -22, -18, -7, -9, -15, -7, -21, -27, -5, - 1, -13, -10, 8, -8, 29, 21, 64, 47, -28, -9, -28, 25, -47, -34, - -3, -14, -26, -12, -5, -10, -27, -9, -14, -23, -2, -31, 28, 17, - -4, -30, 31, 3, -15, 25, 9, -32, 0, -6, -22, 20, -37, 3, 12, - -19, -17, 13, 30, 11, -15, 15, 50, 66, -31, -31, 16, 2, 3, -8, - 40, -21, -31, -2, 41, -29, -12, 9, 14, -4, 9, 8, -20, 28, 12, - 20, -10, 5, -6, -33, 6, 21, 51, 30, 9, 3, 8, 7, 19, -53, 19, 15, - 4, -38, 19, 29, 18, 6, 19, 3, -17, -32, 16, 3, 46, -6, -3, 47, - 3, -66, 3, 25, -6, -6, 21, -24, -9, 28, -39, -42, 42, -6, -19, - -14, 6, -8, 9, 28, -4, 23, 12, -17, -13, 3, 3, 6, 44, 6, -5, 38, - -4, -16, 12, -15, 8, -11, 45, 1, -16, 37, -35, 20, 26, 9, 13, - 34, 25, -3, -10, -2, -42, -23, -22, -56, -56, 6, 17, -9, 0, 36, - 20, 6, -58, 12, 0, -3, -29, -49, -24, -12, -13, 5, -39, -8, 36, - -9, 44, 35, -64, -22, -12, 26, -15, 41, 36, -19, -37, -20, 46, - 35, 9, 32, -5, 27, 21, -36, -51, 19, 10, -23, 28, 46, 28, 8, 22, - -31, 18, 2, -16, -9, 1, -22, -22, 31, 14, 5, 44, -3, 38, 0, -12, - 50, -23, -19, 1, 42, 15, 1, 13, 32, 45, 37, 15, 11, -9, -23, -6, - -23, 36, 4, -34, -14, -14, -37, -28, 19, 20, 14, 24, -48, -34, - -27, -34, -12, 9, -20, -30, 25, 28, -51, -13, 11, -20, -1, -3, - 6, -38, -46, -15, 28, 10, -4, 3, -1, 4, -40, 16, 61, 31, 28, 8, - -2, 21, -3, -25, -12, -32, -15, -38, 20, -7, -35, 28, 29, 9, -27 -]; - -/// Public/verification key for KAT from C reference implementation -#[allow(dead_code)] -pub(crate) const FN_DSA_KAT_512_VK: [u8; 897] = [ - 0x09, 0x02, 0xCE, 0x21, 0x6B, 0xE4, 0x2C, 0xD0, 0x4F, 0xC8, - 0x4C, 0x24, 0xC7, 0x1D, 0x13, 0x07, 0x8E, 0xCA, 0x07, 0x97, - 0x6E, 0xE4, 0xAD, 0xBA, 0x2C, 0x98, 0x23, 0x46, 0xD8, 0x78, - 0xC0, 0x94, 0x76, 0x7F, 0xE2, 0x9C, 0x34, 0x5C, 0xE2, 0xFA, - 0x87, 0x4B, 0xEE, 0x23, 0x9E, 0xA6, 0x0B, 0xDF, 0xA7, 0x27, - 0xA5, 0x16, 0x82, 0xC3, 0xDF, 0x06, 0xA2, 0x68, 0x49, 0xC3, - 0xF7, 0x26, 0x46, 0x2A, 0x59, 0xE9, 0xC4, 0x16, 0x63, 0x87, - 0xBA, 0x89, 0x56, 0xDF, 0xC9, 0xFA, 0x62, 0x20, 0x95, 0x20, - 0xED, 0x65, 0x39, 0xCA, 0xDD, 0xA8, 0xF9, 0xE8, 0x11, 0xA6, - 0x8E, 0xD8, 0x69, 0x70, 0x13, 0x5A, 0xD5, 0x02, 0x6D, 0xBD, - 0x16, 0xF1, 0x59, 0x97, 0xA4, 0xBB, 0xBE, 0x35, 0x68, 0x38, - 0xD7, 0x5C, 0x7A, 0x91, 0x34, 0xED, 0xB8, 0xBF, 0x25, 0xBC, - 0xBA, 0x0A, 0x03, 0x13, 0x77, 0xEB, 0xF0, 0x11, 0x0D, 0x54, - 0x73, 0xC8, 0x46, 0x82, 0x7B, 0x25, 0x6B, 0x9A, 0xB4, 0xD0, - 0x26, 0x1E, 0x41, 0xC8, 0xDB, 0xF1, 0xA4, 0x24, 0xB6, 0xDA, - 0x1F, 0x21, 0xD0, 0xE2, 0x1A, 0x89, 0xBD, 0x29, 0x94, 0x07, - 0x4F, 0xA5, 0x36, 0x5E, 0xA7, 0x70, 0x0E, 0xEB, 0xD2, 0x26, - 0x94, 0x7C, 0xFA, 0x7B, 0xE1, 0xA7, 0x65, 0xF4, 0xD7, 0xF9, - 0x27, 0x50, 0x02, 0x3D, 0xF2, 0x68, 0x94, 0x51, 0x2E, 0x79, - 0x48, 0xC5, 0x64, 0x69, 0xE8, 0x81, 0xD1, 0x99, 0xDA, 0x81, - 0x35, 0xAF, 0xC1, 0x6E, 0x52, 0x3A, 0xF8, 0xA2, 0x3F, 0xD5, - 0x80, 0x22, 0xAE, 0x22, 0x9A, 0xC9, 0x5C, 0xFF, 0x09, 0x5D, - 0x6F, 0xF3, 0x2C, 0x89, 0x0D, 0xB2, 0x29, 0x41, 0x19, 0x21, - 0x90, 0x5B, 0x3B, 0xA5, 0x2D, 0x54, 0xB5, 0x0D, 0xEC, 0xB4, - 0x4D, 0xC3, 0xD7, 0xC8, 0x99, 0x66, 0x79, 0xE8, 0x28, 0xA4, - 0x3B, 0x8D, 0x06, 0x87, 0xE8, 0xBD, 0xE0, 0x60, 0xC5, 0x10, - 0x15, 0xAA, 0x9E, 0x00, 0x0C, 0x92, 0x59, 0x8F, 0x05, 0xB8, - 0x70, 0xA9, 0x4B, 0x29, 0x01, 0xA9, 0xE1, 0x2A, 0xE9, 0xAB, - 0xF2, 0x0A, 0x51, 0x71, 0x4A, 0x03, 0x6A, 0x85, 0x1C, 0xCE, - 0x89, 0x15, 0x42, 0xD1, 0xEB, 0x52, 0x7E, 0x73, 0x10, 0x76, - 0xD4, 0xFF, 0x2F, 0x09, 0xBA, 0x68, 0x94, 0xA2, 0x09, 0x03, - 0xCA, 0x6F, 0xA7, 0x6E, 0x13, 0xD1, 0x2D, 0xC0, 0xAB, 0xA6, - 0xB9, 0x26, 0xED, 0x6E, 0x89, 0x54, 0x84, 0x1D, 0xC0, 0x52, - 0x4A, 0x55, 0xE3, 0x65, 0x6C, 0x9C, 0x19, 0x88, 0x5E, 0xAB, - 0x65, 0x4D, 0x86, 0x94, 0x93, 0x51, 0xFB, 0x8B, 0x02, 0xEA, - 0x32, 0xAE, 0x71, 0x5F, 0x09, 0x8B, 0xE2, 0x4E, 0x83, 0xD2, - 0xE2, 0x71, 0xCC, 0x8C, 0x24, 0x14, 0x8E, 0x7B, 0xD5, 0x92, - 0x59, 0x28, 0x38, 0xFA, 0x55, 0xB8, 0x8A, 0xDB, 0x89, 0x7B, - 0xE5, 0xD9, 0x96, 0x97, 0xE3, 0xFC, 0xAC, 0xFA, 0xC0, 0x25, - 0xB4, 0x51, 0xF6, 0x2B, 0x6C, 0x35, 0x62, 0xC9, 0xEF, 0x90, - 0x71, 0x44, 0x57, 0xA2, 0xF6, 0x49, 0x22, 0x5F, 0x70, 0x20, - 0xE9, 0xAF, 0xDB, 0xB9, 0x2A, 0xE2, 0xBE, 0xDB, 0xA6, 0x19, - 0x33, 0xB9, 0x05, 0xCF, 0xD4, 0x1A, 0x03, 0x08, 0x2B, 0xD6, - 0xDF, 0x8B, 0x24, 0x27, 0xEC, 0x7B, 0xFC, 0xAB, 0x2A, 0xDE, - 0x16, 0x78, 0x9C, 0x09, 0x67, 0x45, 0x67, 0xDE, 0x11, 0x29, - 0xC1, 0xB2, 0xF6, 0x9E, 0x9C, 0x0F, 0x8F, 0xB2, 0x37, 0xC5, - 0x5D, 0x05, 0xCF, 0x8F, 0x69, 0xAD, 0x8B, 0xB7, 0x27, 0xA2, - 0x08, 0x9A, 0x43, 0x71, 0x1E, 0xC6, 0xCA, 0x54, 0xB6, 0x12, - 0xC1, 0xD7, 0x2F, 0xA0, 0x2B, 0x66, 0x40, 0x98, 0x78, 0x6D, - 0x08, 0x53, 0xD1, 0xBC, 0x98, 0xE1, 0x4A, 0x57, 0x90, 0xB2, - 0xCA, 0xC6, 0xC7, 0xD2, 0x48, 0x57, 0xD0, 0xFB, 0x44, 0xF5, - 0xD9, 0x5F, 0x34, 0x21, 0x33, 0x96, 0x86, 0xE8, 0xAF, 0xA5, - 0xBA, 0x92, 0x4B, 0xBA, 0x94, 0xF0, 0x73, 0xC9, 0x09, 0xE9, - 0xFB, 0x8A, 0xD0, 0xA4, 0x62, 0x24, 0xD6, 0xF8, 0x1B, 0x22, - 0xA2, 0x01, 0xAE, 0xDB, 0xA8, 0x94, 0xC2, 0xAA, 0x44, 0xBA, - 0xD6, 0x87, 0x4D, 0x6E, 0x24, 0xCE, 0x1B, 0xB8, 0x3F, 0x51, - 0xE6, 0x9F, 0x34, 0xA1, 0x40, 0xAD, 0x88, 0x55, 0x4F, 0x6C, - 0x47, 0x48, 0xFF, 0x9F, 0x64, 0x6F, 0x0D, 0xDB, 0xD3, 0xA4, - 0x85, 0xD0, 0xBA, 0xD8, 0x05, 0xFA, 0x29, 0xEB, 0x99, 0x68, - 0x18, 0x51, 0x71, 0x45, 0x05, 0xE3, 0x71, 0xA6, 0x4A, 0x7B, - 0xCF, 0x68, 0x97, 0x95, 0x81, 0x44, 0x91, 0xDC, 0x9D, 0xC5, - 0x27, 0x52, 0xE9, 0xA2, 0x7F, 0x96, 0xF4, 0x6C, 0xE8, 0xF8, - 0xA4, 0x27, 0x95, 0xC7, 0x10, 0x7E, 0xC1, 0x86, 0x78, 0x92, - 0x49, 0x6C, 0x91, 0xA1, 0x77, 0xFB, 0x80, 0x95, 0x0D, 0x69, - 0x3B, 0xD4, 0xAD, 0xDE, 0x30, 0x2E, 0x90, 0x3C, 0x41, 0x32, - 0xEC, 0x95, 0x38, 0x86, 0x8D, 0xE8, 0xCF, 0x80, 0x5F, 0x5A, - 0x21, 0x92, 0x96, 0x7F, 0xA6, 0xC3, 0x50, 0x6A, 0x1A, 0xAB, - 0x3C, 0x11, 0xA1, 0x5F, 0x1E, 0x47, 0xB3, 0xB4, 0x6E, 0x64, - 0x97, 0xB1, 0x5A, 0x88, 0x2E, 0x2C, 0xC8, 0x49, 0xA1, 0xB4, - 0x42, 0x49, 0xE9, 0x7F, 0x61, 0xF1, 0x6B, 0xD0, 0xEC, 0xEA, - 0xD5, 0x47, 0xDD, 0x71, 0xC5, 0xDD, 0xA5, 0xAA, 0x8A, 0x56, - 0xFE, 0x36, 0x31, 0x22, 0x15, 0x85, 0x2E, 0x78, 0xDA, 0x98, - 0x5D, 0x55, 0xA4, 0xA4, 0xD8, 0xF7, 0x14, 0x8E, 0x45, 0x67, - 0xD1, 0xE4, 0x67, 0x87, 0xC2, 0x23, 0x87, 0xCA, 0x4A, 0x85, - 0xF0, 0x11, 0xE3, 0x75, 0xC4, 0x5C, 0xCA, 0x0C, 0xE0, 0xA1, - 0x5B, 0xCD, 0x13, 0x37, 0xBD, 0xC9, 0x27, 0x1B, 0xFA, 0x84, - 0x73, 0xE1, 0x88, 0x2F, 0x33, 0x85, 0x58, 0x69, 0x7D, 0x9A, - 0xAF, 0x07, 0x5A, 0x90, 0x78, 0x33, 0x5A, 0x1F, 0xB8, 0xA1, - 0xB3, 0xB6, 0xE9, 0xD9, 0xCF, 0x43, 0x62, 0x84, 0x06, 0x7C, - 0x58, 0xC5, 0xA4, 0x8E, 0x04, 0x7A, 0x40, 0x08, 0xD0, 0x2B, - 0x7C, 0x85, 0x07, 0xC2, 0xEE, 0x6F, 0x88, 0xDA, 0x4C, 0x97, - 0xF6, 0x0F, 0x75, 0x44, 0x4C, 0x78, 0x84, 0x96, 0x67, 0x84, - 0x32, 0xC9, 0x5F, 0x3A, 0x92, 0x08, 0xB4, 0xA8, 0xC1, 0xCB, - 0xC6, 0xE2, 0xD4, 0xDA, 0x61, 0x25, 0x3D, 0xA0, 0x81, 0x27, - 0x5E, 0x8F, 0x34, 0xDB, 0xE4, 0xA1, 0xEC, 0xC2, 0x22, 0x24, - 0xC3, 0x08, 0x00, 0xA7, 0x75, 0x35, 0x74, 0xC8, 0x95, 0x86, - 0x95, 0x66, 0x6C, 0x28, 0x95, 0xB3, 0x5C, 0xCE, 0x07, 0x89, - 0x44, 0xA3, 0x10, 0x41, 0xA5, 0x23, 0x83, 0x7C, 0xED, 0x72, - 0x17, 0x69, 0x0F, 0xA1, 0x7C, 0x36, 0xCB, 0x45, 0x92, 0x63, - 0x35, 0xE6, 0x7B, 0x18, 0x04, 0x95, 0x9D, -]; - -/// Random bytes for KAT from C reference implementation -/// First 40 bytes are the nonce, next 56 bytes are the ChaCha20 PRNG seed -#[allow(dead_code)] -pub(crate) const FN_DSA_KAT_512_RND: [u8; 96] = [ - // nonce: 40 bytes - 0x16, 0xC1, 0x25, 0x15, 0x25, 0x80, 0x93, 0x79, 0x99, 0x56, - 0x36, 0x8C, 0xDF, 0xC1, 0x82, 0xC1, 0xCA, 0x4A, 0x34, 0xF0, - 0x77, 0xE9, 0x24, 0x44, 0x16, 0xA8, 0xC4, 0xC1, 0x3F, 0xB0, - 0xCA, 0x24, 0x1E, 0x8B, 0x7A, 0xC1, 0x71, 0x2D, 0x28, 0xEB, - - // seed for the ChaCha20 PRNG: 56 bytes - 0xFF, 0xD8, 0x57, 0xF1, 0x49, 0x5C, 0xA5, 0x98, 0xDB, 0x2C, - 0x88, 0x64, 0xAF, 0x31, 0xFA, 0x8F, 0x37, 0xBC, 0x73, 0x8D, - 0xCD, 0xB6, 0xDD, 0xAA, 0xFD, 0x25, 0x4A, 0xBF, 0xE3, 0x01, - 0xB7, 0x91, 0x9B, 0x7E, 0x9B, 0x9F, 0xEC, 0xEA, 0x4E, 0xF0, - 0x01, 0xC9, 0x62, 0x9B, 0x96, 0x6B, 0x58, 0xD6, 0x81, 0x25, - 0x2F, 0xF3, 0x38, 0x9E, 0x81, 0x6B, -]; - -/// Expected raw signature (s2 polynomial) for KAT from C reference implementation -#[allow(dead_code)] -pub(crate) const FN_DSA_KAT_512_SIG_RAW: [i16; 512] = [ - 11, 201, 176, -24, -141, -151, -63, -323, 154, -363, 168, -173, - -29, -184, -142, 419, -48, 104, 103, -245, -374, 252, -59, 32, - 77, -237, 182, -9, 181, -54, -47, 52, -6, 81, 147, 113, -36, 28, - -156, -261, -277, -431, 175, -182, 115, -273, 33, -76, -270, - -124, -25, -61, -166, 65, -9, 34, 52, -104, 240, -81, 120, 55, - 9, 273, -13, -1, -193, 442, -43, -58, -86, -100, -14, -96, 245, - -120, 10, 2, -40, 341, 8, 112, -260, 100, -24, -22, -181, -207, - -123, -6, 108, -271, 194, 131, -60, 87, -66, 173, 44, 133, -270, - -182, 176, 59, 289, 25, 98, -47, 153, -257, 160, -21, 73, 58, - -4, -39, 79, -124, 31, 119, -175, -125, -222, -36, 71, 3, -153, - -101, 20, 234, 235, 162, -147, -18, 155, -11, -90, -157, -18, - -408, -18, -53, -16, 169, 104, -135, 303, -219, 572, 109, -235, - -478, 114, 66, -17, 186, -13, -57, 31, -132, 73, 134, 35, -165, - -279, 27, -360, -3, 44, -40, -262, 60, 100, 35, 78, -102, -281, - -189, -66, 122, -65, -73, -287, -236, -131, -121, -24, 72, 68, - -156, -69, 54, -127, -185, 154, 60, 144, -99, -81, 139, 80, 98, - -93, 227, 170, -338, -15, 162, 149, -247, -89, 290, 36, -231, - -77, 121, 205, -45, 140, 6, 45, -134, 248, -252, 58, 210, 204, - 272, 205, 282, 19, -15, 327, 70, 102, -36, 93, 67, -42, -243, - 106, 104, 47, -333, -139, 195, 49, -22, -138, 166, 308, 143, 57, - -305, -26, -176, -46, -243, -130, 134, -176, -131, -277, 240, - -228, -177, 142, -51, 84, 44, 187, 213, 24, 83, -134, -202, 286, - 48, 58, -199, 7, -18, 173, 113, 52, -190, 1, -117, -177, 122, - -229, 83, -90, 46, 115, 63, -33, -4, 23, -51, 148, 97, 169, - -183, -128, 37, 80, 61, 102, -28, 75, 142, 292, -89, -260, -47, - 62, 86, 184, 15, -258, -48, -47, -29, 211, -357, 228, -133, - -144, 275, -110, -127, -83, -74, -89, 149, 9, -44, -208, -46, - 121, -157, 147, 216, 133, -96, 12, 247, 189, 100, -93, 135, -14, - 105, 175, -202, 37, 178, 141, 142, -140, -174, -60, -13, 95, - -208, -84, -52, -144, -125, -2, 63, -436, -273, 47, 106, 122, - -221, -180, 104, -4, -163, -121, 87, 405, 107, -229, 259, 118, - -136, -313, -35, -84, 208, 128, -4, 13, 304, -40, 75, 165, 183, - -196, 7, -48, -21, -250, 160, -280, 370, 91, 198, -228, -70, 30, - -54, -263, -10, -125, -18, -231, -3, 287, -388, -10, 208, -358, - -107, 148, -154, 31, -6, -119, -206, -37, -59, -30, -285, -13, - 69, -57, 153, -113, -108, 100, 58, -91, -239, -68, -181, 81, 43, - 18, -110, -59, -18, 97, -96, 27, 181, -62, -156, -19, -204, 343, - 66, -110, -52, 28, -188, -35, 49, -59, 38, -43, 64, -177, 171, - 132, -38, -120, 214, -42, 110, -324, -34, 158, -102, -4, -61, - -117, -134, -310, -99, 79, -308, -306, -199, -126, -190, 27, - -43, 120, 94, 340, -435, -99, 167, 210, -70, -84, 199 +/// Generated using: +/// - Secret key from `ChaCha20Rng::from_seed([0u8; 32])` +/// - Message: `b"data"` +/// - FN-DSA style signing: single SHAKE256 PRNG seeded from BLAKE3(LOG_N || sk || msg), first 56 +/// bytes for sampler seed, then 40 bytes per attempt for nonces +/// +/// Format: header (1) + nonce (40) + s2 poly (625) + public key (897) = 1563 bytes +pub(crate) const DETERMINISTIC_SIGNATURE: [u8; 1563] = [ + 185, 250, 87, 250, 71, 82, 116, 77, 246, 252, 137, 147, 90, 221, 123, 174, 39, 201, 68, 91, + 133, 108, 242, 1, 77, 105, 31, 162, 187, 207, 31, 60, 226, 5, 46, 1, 216, 128, 57, 102, 220, + 84, 76, 109, 186, 163, 224, 159, 98, 79, 235, 99, 199, 203, 81, 205, 198, 169, 36, 204, 241, + 61, 205, 187, 160, 210, 193, 153, 25, 158, 159, 69, 171, 217, 226, 26, 71, 185, 211, 217, 11, + 131, 198, 69, 203, 93, 102, 166, 124, 219, 184, 204, 245, 57, 33, 208, 55, 57, 194, 177, 152, + 87, 79, 78, 182, 180, 214, 24, 57, 94, 170, 192, 74, 191, 3, 54, 204, 174, 178, 69, 57, 204, + 211, 225, 160, 20, 244, 119, 203, 170, 145, 167, 58, 29, 4, 149, 147, 102, 201, 114, 24, 100, + 135, 78, 155, 197, 73, 92, 205, 202, 173, 82, 80, 178, 140, 76, 149, 194, 175, 56, 249, 88, + 242, 213, 171, 246, 115, 174, 182, 246, 137, 139, 151, 92, 101, 127, 143, 20, 149, 245, 107, + 201, 14, 194, 183, 130, 103, 76, 110, 54, 140, 253, 126, 231, 127, 27, 44, 171, 15, 195, 100, + 18, 73, 129, 115, 202, 49, 252, 114, 101, 196, 144, 245, 243, 178, 125, 153, 80, 140, 156, 169, + 91, 5, 114, 222, 33, 13, 18, 214, 145, 254, 124, 17, 180, 205, 143, 85, 177, 24, 155, 230, 87, + 112, 181, 103, 49, 154, 165, 39, 57, 92, 203, 170, 93, 215, 105, 174, 221, 244, 57, 172, 204, + 174, 42, 130, 153, 207, 45, 123, 43, 159, 181, 225, 93, 53, 165, 174, 80, 184, 243, 196, 116, + 232, 228, 62, 247, 39, 246, 13, 161, 179, 51, 58, 184, 233, 27, 105, 22, 109, 206, 54, 159, 34, + 143, 62, 220, 251, 146, 32, 108, 123, 174, 217, 3, 13, 2, 77, 151, 162, 240, 117, 177, 63, 191, + 110, 38, 96, 85, 57, 227, 235, 131, 210, 225, 248, 103, 177, 91, 152, 65, 140, 248, 247, 227, + 244, 230, 50, 108, 75, 15, 172, 3, 91, 138, 126, 239, 170, 109, 1, 118, 110, 155, 179, 197, + 135, 84, 75, 127, 90, 51, 71, 167, 37, 245, 246, 72, 242, 246, 155, 92, 254, 21, 166, 228, 176, + 1, 148, 144, 49, 8, 2, 32, 87, 166, 200, 233, 28, 87, 39, 146, 50, 238, 231, 190, 6, 120, 233, + 213, 152, 162, 183, 158, 121, 242, 191, 45, 17, 13, 69, 6, 91, 188, 70, 236, 52, 198, 118, 1, + 216, 107, 49, 153, 169, 4, 55, 187, 226, 166, 165, 241, 110, 68, 159, 169, 209, 167, 64, 152, + 168, 234, 114, 179, 113, 18, 24, 222, 235, 90, 128, 122, 112, 119, 55, 27, 190, 202, 201, 146, + 249, 138, 49, 179, 209, 14, 30, 80, 103, 31, 233, 72, 155, 141, 105, 195, 47, 80, 228, 234, + 124, 215, 92, 216, 73, 83, 134, 97, 229, 253, 93, 105, 29, 207, 242, 147, 205, 185, 158, 222, + 242, 120, 247, 249, 23, 101, 249, 244, 90, 121, 235, 188, 115, 78, 158, 69, 34, 38, 113, 150, + 214, 177, 60, 255, 53, 53, 9, 130, 167, 233, 179, 79, 111, 218, 145, 40, 62, 53, 63, 162, 248, + 52, 10, 31, 72, 251, 226, 238, 49, 61, 196, 3, 23, 88, 78, 18, 216, 103, 42, 31, 162, 172, 37, + 91, 51, 170, 232, 64, 98, 249, 114, 85, 46, 198, 18, 156, 91, 189, 152, 173, 42, 250, 167, 46, + 235, 210, 210, 32, 73, 238, 197, 146, 32, 18, 102, 48, 128, 20, 68, 80, 107, 142, 62, 124, 112, + 203, 158, 14, 213, 25, 66, 31, 182, 146, 181, 34, 81, 54, 70, 153, 213, 69, 6, 95, 153, 88, + 118, 146, 76, 254, 180, 174, 173, 18, 123, 20, 136, 181, 103, 190, 122, 148, 158, 143, 189, 89, + 212, 228, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 87, 240, 198, 27, 184, 21, 116, 184, 5, 145, 176, + 111, 30, 97, 74, 244, 41, 96, 199, 196, 99, 179, 144, 113, 182, 201, 1, 153, 110, 197, 242, + 179, 208, 226, 132, 25, 46, 45, 98, 69, 135, 208, 103, 89, 229, 4, 58, 236, 51, 54, 124, 205, + 107, 104, 93, 33, 102, 240, 178, 50, 6, 22, 77, 61, 198, 112, 84, 252, 42, 20, 160, 142, 132, + 160, 240, 166, 72, 146, 189, 11, 50, 49, 29, 232, 128, 150, 72, 8, 105, 164, 185, 16, 124, 110, + 176, 201, 0, 10, 73, 34, 100, 54, 51, 217, 64, 128, 230, 83, 66, 166, 18, 34, 179, 170, 16, 21, + 11, 195, 44, 38, 217, 200, 144, 136, 141, 124, 99, 168, 104, 24, 25, 193, 251, 73, 238, 46, + 233, 132, 104, 101, 64, 25, 62, 128, 12, 4, 81, 111, 5, 23, 202, 214, 239, 72, 154, 122, 112, + 20, 90, 238, 177, 94, 192, 149, 101, 229, 71, 203, 54, 112, 93, 8, 142, 204, 205, 190, 249, + 205, 199, 222, 14, 183, 17, 94, 85, 170, 210, 76, 175, 179, 237, 17, 231, 81, 174, 3, 140, 222, + 205, 37, 223, 18, 155, 84, 112, 218, 155, 3, 70, 101, 81, 125, 116, 82, 240, 197, 207, 136, 16, + 27, 67, 34, 156, 146, 58, 73, 210, 194, 44, 166, 36, 56, 10, 157, 129, 32, 154, 131, 133, 73, + 138, 1, 201, 198, 177, 146, 29, 137, 50, 8, 32, 87, 93, 125, 24, 70, 145, 76, 194, 47, 8, 167, + 101, 53, 152, 189, 56, 105, 19, 89, 182, 34, 98, 149, 160, 63, 118, 51, 4, 220, 164, 206, 153, + 82, 4, 220, 164, 157, 57, 16, 165, 148, 30, 45, 95, 93, 102, 218, 19, 235, 11, 175, 204, 104, + 192, 209, 34, 5, 11, 170, 124, 177, 159, 99, 215, 7, 12, 55, 106, 118, 6, 233, 13, 217, 2, 19, + 73, 165, 79, 67, 6, 145, 230, 108, 13, 147, 107, 226, 71, 154, 245, 16, 24, 119, 214, 138, 73, + 154, 2, 86, 118, 162, 125, 167, 251, 174, 28, 23, 90, 193, 201, 3, 233, 40, 22, 88, 185, 101, + 33, 80, 227, 23, 78, 52, 197, 218, 76, 68, 86, 174, 29, 17, 77, 11, 179, 119, 152, 254, 219, + 182, 3, 135, 48, 105, 215, 18, 246, 225, 68, 59, 38, 240, 153, 25, 40, 236, 94, 24, 194, 196, + 156, 110, 230, 104, 60, 47, 240, 145, 108, 45, 0, 2, 231, 56, 154, 75, 121, 142, 120, 138, 120, + 121, 11, 84, 12, 30, 217, 99, 168, 233, 122, 101, 114, 42, 196, 131, 169, 40, 159, 80, 47, 246, + 227, 89, 42, 65, 42, 78, 0, 157, 231, 153, 165, 188, 162, 243, 124, 225, 26, 20, 80, 96, 106, + 173, 133, 253, 65, 181, 170, 102, 115, 215, 135, 148, 196, 173, 114, 94, 102, 246, 45, 65, 49, + 177, 80, 162, 149, 147, 222, 233, 139, 8, 230, 105, 165, 129, 29, 101, 15, 218, 62, 137, 13, + 160, 168, 178, 141, 142, 164, 144, 191, 39, 198, 66, 227, 154, 192, 196, 65, 195, 209, 40, 102, + 36, 7, 97, 8, 36, 59, 150, 78, 83, 112, 221, 77, 245, 174, 222, 56, 230, 77, 155, 15, 134, 117, + 22, 19, 85, 72, 81, 43, 120, 67, 75, 13, 31, 79, 167, 160, 40, 148, 36, 148, 156, 128, 200, + 235, 130, 56, 96, 160, 148, 156, 151, 73, 1, 151, 1, 112, 229, 101, 161, 239, 93, 184, 112, + 117, 203, 103, 5, 2, 20, 22, 6, 88, 43, 238, 13, 51, 16, 137, 141, 234, 238, 6, 23, 160, 73, + 56, 2, 11, 205, 71, 88, 118, 130, 72, 73, 111, 255, 74, 8, 43, 3, 19, 140, 48, 173, 104, 59, + 97, 244, 238, 82, 93, 182, 221, 196, 187, 18, 1, 45, 70, 123, 199, 170, 28, 196, 12, 29, 254, + 249, 7, 211, 223, 117, 148, 205, 123, 62, 167, 8, 172, 37, 167, 89, 216, 208, 166, 11, 29, 182, + 251, 107, 3, 186, 60, 38, 35, 0, 225, 232, 211, 24, 221, 55, 71, 245, 80, 10, 42, 142, 199, 18, + 145, 26, 161, 138, 225, 146, 233, 179, 21, 116, 81, 10, 152, 131, 9, 192, 88, 1, 40, 242, 245, + 166, 33, 98, 143, 70, 218, 245, 173, 93, 229, 4, 81, 104, 247, 224, 225, 206, 129, 17, 225, 91, + 20, 134, 254, 154, 80, 222, 168, 68, 175, 4, 94, 17, 194, 155, 46, 76, 123, 191, 218, 211, 164, + 114, 101, 235, 24, 252, 144, 97, 62, 208, 11, 178, 9, 247, 195, 124, 170, 168, 76, 90, 28, 200, + 123, 46, 150, 8, 144, 94, 67, 3, 29, 137, 95, 197, 200, 120, 4, 83, 2, 25, 116, 149, 201, 15, + 104, 141, 161, 157, 254, 219, 213, 83, 240, 82, 184, 68, 132, 136, 142, 155, 55, 12, 168, 224, + 3, 106, 65, 114, 38, 90, 42, 190, 21, 151, 169, 114, 73, 166, 24, 107, 88, 124, 145, 150, 22, + 89, 200, 68, 154, 137, 8, 154, 230, 142, 68, 129, 76, 175, 64, 0, 17, 60, 149, 252, 61, 198, + 90, 218, 247, 77, 14, 101, 36, 131, 159, 133, 38, 100, 158, 58, 10, 94, 146, 152, 130, 87, 210, + 149, 38, 255, ]; diff --git a/miden-crypto/src/dsa/falcon512_rpo/tests/mod.rs b/miden-crypto/src/dsa/falcon512_rpo/tests/mod.rs index 8b1484ec6..86d38668e 100644 --- a/miden-crypto/src/dsa/falcon512_rpo/tests/mod.rs +++ b/miden-crypto/src/dsa/falcon512_rpo/tests/mod.rs @@ -1,8 +1,7 @@ -use data::DETERMINISTIC_SIGNATURE; use rand::SeedableRng; use rand_chacha::ChaCha20Rng; -use crate::dsa::falcon512_rpo::{PREVERSIONED_NONCE, PREVERSIONED_NONCE_LEN, SecretKey, Serializable}; +use crate::dsa::falcon512_rpo::{SecretKey, Serializable}; mod data; @@ -28,36 +27,21 @@ fn test_signature_determinism() { let sk = SecretKey::with_rng(&mut rng); let message = b"data"; - let signature = sk.sign(message.into()); - let serialized_signature = signature.to_bytes(); - assert_eq!(serialized_signature, DETERMINISTIC_SIGNATURE); -} - -#[test] -fn check_preversioned_fixed_nonce() { - assert_eq!(build_preversioned_fixed_nonce(), PREVERSIONED_NONCE) -} - -/// Builds the preversioned portion of the fixed nonce following [1]. -/// -/// Note that [1] uses the term salt instead of nonce. -/// -/// [1]: https://github.com/algorand/falcon/blob/main/falcon-det.pdf -fn build_preversioned_fixed_nonce() -> [u8; PREVERSIONED_NONCE_LEN] { - use crate::dsa::falcon512_rpo::LOG_N; + // Sign the same message twice + let signature1 = sk.sign(message.into()); + let signature2 = sk.sign(message.into()); - let mut result = [0_u8; 39]; - result[0] = LOG_N; - let domain_separator = "RPO-FALCON-DET".as_bytes(); + // Signatures should be identical (deterministic) + let serialized = signature1.to_bytes(); + assert_eq!(serialized, signature2.to_bytes()); - result - .iter_mut() - .skip(1) - .zip(domain_separator.iter()) - .for_each(|(dst, src)| *dst = *src); + // Compare against known test vector for cross-platform determinism + assert_eq!(serialized, data::DETERMINISTIC_SIGNATURE); - result + // Also verify the signature is valid + let pk = sk.public_key(); + assert!(pk.verify(message.into(), &signature1)); } /// Tests that sign_shake256 produces signatures verifiable by fn-dsa-vrfy. @@ -114,139 +98,3 @@ fn test_sign_shake256_verified_by_fn_dsa() { let bad_verified = vk.verify(&sig, &DOMAIN_NONE, &HASH_ID_ORIGINAL_FALCON, bad_message); assert!(!bad_verified, "Verification should fail for wrong message"); } - -/// End-to-end test against fn-dsa C reference implementation KAT vectors. -/// -/// This test verifies the complete signing flow by: -/// 1. Constructing a SecretKey from the KAT basis polynomials (f, g, F, G) -/// 2. Verifying the derived public key matches the KAT verification key -/// 3. Signing with the KAT nonce and seed using sign_shake256_inner -/// 4. Verifying the signature matches the expected KAT signature -#[test] -fn test_end_to_end_against_c_reference_kat() { - use crate::dsa::falcon512_rpo::{N, SecretKey, math::Polynomial}; - use data::{ - FN_DSA_KAT_512_BIG_F, FN_DSA_KAT_512_BIG_G, FN_DSA_KAT_512_F, FN_DSA_KAT_512_G, - FN_DSA_KAT_512_RND, FN_DSA_KAT_512_SIG_RAW, FN_DSA_KAT_512_VK, - }; - use fn_dsa_sign::tests::ChaCha20PRNG; - - // Build the short lattice basis [g, f, G, F] from KAT data - let basis = [ - Polynomial::new(FN_DSA_KAT_512_G.iter().map(|&c| c).collect()), // g - Polynomial::new(FN_DSA_KAT_512_F.iter().map(|&c| c).collect()), // f - Polynomial::new(FN_DSA_KAT_512_BIG_G.iter().map(|&c| c).collect()), // G - Polynomial::new(FN_DSA_KAT_512_BIG_F.iter().map(|&c| c).collect()), // F - ]; - - // Construct SecretKey from the basis - let sk = SecretKey::from_short_lattice_basis(basis); - - // Verify the public key matches the KAT verification key - let pk = sk.public_key(); - let pk_bytes = (&pk).to_bytes(); - assert_eq!( - pk_bytes.as_slice(), - &FN_DSA_KAT_512_VK[..], - "Derived public key does not match KAT verification key" - ); - - // Extract nonce and seed from KAT data - let nonce: [u8; 40] = FN_DSA_KAT_512_RND[0..40].try_into().unwrap(); - let seed: [u8; 56] = FN_DSA_KAT_512_RND[40..96].try_into().unwrap(); - - // Sign using the test method with fixed nonce and seed - // Use ChaCha20PRNG to match the C reference implementation - let message = b"data1"; - let s2 = sk.sign_shake256_inner::(message, &nonce, &seed); - - // Verify the signature matches the expected KAT value - for i in 0..N { - assert_eq!( - s2[i], FN_DSA_KAT_512_SIG_RAW[i], - "Signature mismatch at coefficient {}: got {}, expected {}", - i, s2[i], FN_DSA_KAT_512_SIG_RAW[i] - ); - } -} - -/// Tests sign_poly against the fn-dsa C reference implementation KAT vectors. -/// -/// This test verifies that our sign_poly implementation produces the correct signature -/// when given the same inputs (basis, hash-to-point result, PRNG seed) as the C reference -/// implementation. This ensures compatibility with the upstream fn-dsa library. -#[test] -fn test_sign_poly_against_c_reference_kat() { - use data::{ - FN_DSA_KAT_512_BIG_F, FN_DSA_KAT_512_BIG_G, FN_DSA_KAT_512_F, FN_DSA_KAT_512_G, - FN_DSA_KAT_512_RND, FN_DSA_KAT_512_SIG_RAW, FN_DSA_KAT_512_VK, - }; - use fn_dsa_sign::{ - flr::FLR, - poly::{self, FFT}, - sign_core::sign_poly, - tests::ChaCha20PRNG, - DOMAIN_NONE, HASH_ID_ORIGINAL_FALCON, - }; - - const LOGN: u32 = 9; - const N: usize = 512; - - // Build the FFT basis from f, g, F, G - // The basis is B = [[g, -f], [G, -F]] stored as [b00, b01, b10, b11] - // where b00 = FFT(g), b01 = FFT(-f), b10 = FFT(G), b11 = FFT(-F) - let mut basis = [FLR::ZERO; 4 * N]; - let (b00, rest) = basis.split_at_mut(N); - let (b01, rest) = rest.split_at_mut(N); - let (b10, b11) = rest.split_at_mut(N); - - // Set small polynomials and convert to FFT domain - poly::poly_set_small(LOGN, b01, &FN_DSA_KAT_512_F); - poly::poly_set_small(LOGN, b00, &FN_DSA_KAT_512_G); - poly::poly_set_small(LOGN, b11, &FN_DSA_KAT_512_BIG_F); - poly::poly_set_small(LOGN, b10, &FN_DSA_KAT_512_BIG_G); - FFT(LOGN, b01); - FFT(LOGN, b00); - FFT(LOGN, b11); - FFT(LOGN, b10); - poly::poly_neg(LOGN, b01); - poly::poly_neg(LOGN, b11); - - // Compute hash-to-point using the original Falcon rules - // For original Falcon: hash = SHAKE256(nonce || message) - let nonce = &FN_DSA_KAT_512_RND[0..40]; - let message = b"data1"; - - // Hash the verification key for the hash-to-point input - let mut hvk = [0u8; 64]; - { - use fn_dsa_comm::shake::SHAKE256; - let mut sh = SHAKE256::new(); - sh.inject(&FN_DSA_KAT_512_VK); - sh.flip(); - sh.extract(&mut hvk); - } - - // Compute hash-to-point - let mut hm = [0u16; N]; - fn_dsa_comm::hash_to_point(nonce, &hvk, &DOMAIN_NONE, &HASH_ID_ORIGINAL_FALCON, message, &mut hm); - - // Extract the PRNG seed (bytes 40-95 of KAT_512_RND) - let seed: [u8; 56] = FN_DSA_KAT_512_RND[40..96].try_into().unwrap(); - - // Allocate temporary buffer - let mut tmp = [FLR::ZERO; 9 * N]; - - // Sign using sign_poly with ChaCha20PRNG - let s2 = sign_poly::(LOGN, &hm, &seed, &basis, &mut tmp); - - // Verify the signature matches the expected KAT value - assert_eq!(s2.len(), N); - for i in 0..N { - assert_eq!( - s2[i], FN_DSA_KAT_512_SIG_RAW[i], - "Mismatch at coefficient {}: got {}, expected {}", - i, s2[i], FN_DSA_KAT_512_SIG_RAW[i] - ); - } -}