Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Falcon 512 test vectors #309

Open
wants to merge 6 commits into
base: next
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## 0.9.3 (2024-04-24)

* Added `RpxRandomCoin` struct (#307).

## 0.9.2 (2024-04-21)

* Implemented serialization for the `Smt` struct (#304).
Expand Down
38 changes: 8 additions & 30 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
[package]
name = "miden-crypto"
version = "0.9.2"
version = "0.9.3"
description = "Miden Cryptographic primitives"
authors = ["miden contributors"]
readme = "README.md"
license = "MIT"
repository = "https://github.com/0xPolygonMiden/crypto"
documentation = "https://docs.rs/miden-crypto/0.9.2"
documentation = "https://docs.rs/miden-crypto/0.9.3"
categories = ["cryptography", "no-std"]
keywords = ["miden", "crypto", "hash", "merkle"]
edition = "2021"
Expand Down
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,8 @@ For the above signatures, key generation, signing, and signature verification ar
[Pseudo random element generator module](./src/rand/) provides a set of traits and data structures that facilitate generating pseudo-random elements in the context of Miden VM and Miden rollup. The module currently includes:

* `FeltRng`: a trait for generating random field elements and random 4 field elements.
* `RpoRandomCoin`: a struct implementing `FeltRng` as well as the [`RandomCoin`](https://github.com/facebook/winterfell/blob/main/crypto/src/random/mod.rs) trait.
* `RpoRandomCoin`: a struct implementing `FeltRng` as well as the [`RandomCoin`](https://github.com/facebook/winterfell/blob/main/crypto/src/random/mod.rs) trait using RPO hash function.
* `RpxRandomCoin`: a struct implementing `FeltRng` as well as the [`RandomCoin`](https://github.com/facebook/winterfell/blob/main/crypto/src/random/mod.rs) trait using RPX hash function.

## Crate features
This crate can be compiled with the following features:
Expand Down
43 changes: 0 additions & 43 deletions src/dsa/rpo_falcon512/keys/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,46 +9,3 @@ pub use public_key::{PubKeyPoly, PublicKey};

mod secret_key;
pub use secret_key::SecretKey;

// TESTS
// ================================================================================================

#[cfg(test)]
mod tests {
use crate::{dsa::rpo_falcon512::SecretKey, Word, ONE};
use rand::SeedableRng;
use rand_chacha::ChaCha20Rng;
use winter_math::FieldElement;
use winter_utils::{Deserializable, Serializable};

#[test]
fn test_falcon_verification() {
let seed = [0_u8; 32];
let mut rng = ChaCha20Rng::from_seed(seed);

// generate random keys
let sk = SecretKey::with_rng(&mut rng);
let pk = sk.public_key();

// test secret key serialization/deserialization
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());

// sign a random message
let message: Word = [ONE; 4];
let signature = sk.sign_with_rng(message, &mut rng);

// make sure the signature verifies correctly
assert!(pk.verify(message, &signature));

// a signature should not verify against a wrong message
let message2: Word = [ONE.double(); 4];
assert!(!pk.verify(message2, &signature));

// a signature should not verify against a wrong public key
let sk2 = SecretKey::with_rng(&mut rng);
assert!(!sk2.public_key().verify(message, &signature))
}
}
98 changes: 95 additions & 3 deletions src/dsa/rpo_falcon512/keys/secret_key.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ impl SecretKey {
}

/// Given a short basis [[g, -f], [G, -F]], computes the normalized LDL tree i.e., Falcon tree.
fn from_short_lattice_basis(basis: ShortLatticeBasis) -> SecretKey {
pub(crate) fn from_short_lattice_basis(basis: ShortLatticeBasis) -> SecretKey {
// FFT each polynomial of the short basis.
let basis_fft = to_complex_fft(&basis);
// compute the Gram matrix.
Expand Down Expand Up @@ -196,6 +196,98 @@ impl SecretKey {
}
}
}

// HELPER METHODS FOR TESTING
// --------------------------------------------------------------------------------------------

/// Signs a message with the secret key relying on the provided randomness generator.
#[cfg(test)]
pub fn sign_with_rng_testing<R: Rng>(
&self,
message: &[u8],
rng: &mut R,
skip_bytes: usize,
) -> Signature {
use crate::dsa::rpo_falcon512::{
hash_to_point::hash_to_point_shake256, tests::ChaCha, CHACHA_SEED_LEN,
};

let mut dummy = vec![0_u8; skip_bytes];
rng.fill_bytes(&mut dummy);
let mut nonce_bytes = [0u8; SIG_NONCE_LEN];
rng.fill_bytes(&mut nonce_bytes);
let nonce = Nonce::new(nonce_bytes);

let h = self.compute_pub_key_poly();
let c = hash_to_point_shake256(message, &nonce);

let s2 = loop {
let mut chacha_seed = [0_u8; CHACHA_SEED_LEN];
rng.fill_bytes(&mut chacha_seed);

let mut chacha_prng = ChaCha::new(chacha_seed.to_vec());

let s2 = self.sign_helper_testing(c.clone(), &mut chacha_prng);
if let Some(s2) = s2 {
break s2;
}
};

Signature::new(nonce, h, s2)
}

/// 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 signature polynomial `s2`.
#[cfg(test)]
fn sign_helper_testing<R: Rng>(
&self,
c: Polynomial<FalconFelt>,
rng: &mut R,
) -> Option<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();

// B = [[FFT(g), -FFT(f)], [FFT(G), -FFT(F)]]
let [g_fft, minus_f_fft, big_g_fft, minus_big_f_fft] = to_complex_fft(&self.secret_key);
let t0 = c_over_q_fft.hadamard_mul(&minus_big_f_fft);
let t1 = -c_over_q_fft.hadamard_mul(&minus_f_fft);

let z = ffsampling(&(t0.clone(), t1.clone()), &self.tree, rng);
let t0_min_z0 = t0.clone() - z.0;
let t1_min_z1 = t1.clone() - z.1;

// s = (t-z) * B
let s0 = t0_min_z0.hadamard_mul(&g_fft) + t1_min_z1.hadamard_mul(&big_g_fft);
let s1 = t0_min_z0.hadamard_mul(&minus_f_fft) + t1_min_z1.hadamard_mul(&minus_big_f_fft);

// compute the norm of (s0||s1) and note that they are in FFT representation
let length_squared: f64 = (s0.coefficients.iter().map(|a| (a * a.conj()).re).sum::<f64>()
+ s1.coefficients.iter().map(|a| (a * a.conj()).re).sum::<f64>())
/ (N as f64);

if length_squared < (SIG_L2_BOUND as f64) {
let bold_s = [-s0, s1];

let s2 = bold_s[1].ifft();
let s2_coef: [i16; N] = s2
.coefficients
.iter()
.map(|a| a.re.round() as i16)
.collect::<Vec<i16>>()
.try_into()
.expect("The number of coefficients should be equal to N");

if let Ok(s2) = SignaturePoly::try_from(&s2_coef) {
Some(s2)
} else {
None
}
} else {
None
}
}
}

// SERIALIZATION / DESERIALIZATION
Expand Down Expand Up @@ -304,9 +396,9 @@ impl Deserializable for SecretKey {
fn to_complex_fft(basis: &[Polynomial<i16>; 4]) -> [Polynomial<Complex<f64>>; 4] {
let [g, f, big_g, big_f] = basis.clone();
let g_fft = g.map(|cc| Complex64::new(*cc as f64, 0.0)).fft();
let minus_f_fft = f.map(|cc| -Complex64::new(*cc as f64, 0.0)).fft();
let minus_f_fft = f.map(|cc| Complex64::new(*cc as f64, 0.0)).fft();
let big_g_fft = big_g.map(|cc| Complex64::new(*cc as f64, 0.0)).fft();
let minus_big_f_fft = big_f.map(|cc| -Complex64::new(*cc as f64, 0.0)).fft();
let minus_big_f_fft = big_f.map(|cc| Complex64::new(*cc as f64, 0.0)).fft();
[g_fft, minus_f_fft, big_g_fft, minus_big_f_fft]
}

Expand Down
Loading
Loading