Skip to content

Commit

Permalink
chore: refactorings and no-std fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
Al-Kindi-0 committed Mar 19, 2024
1 parent c8411da commit 7a450da
Show file tree
Hide file tree
Showing 9 changed files with 113 additions and 137 deletions.
10 changes: 5 additions & 5 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -46,24 +46,24 @@ std = [
[dependencies]
blake3 = { version = "1.5", default-features = false }
clap = { version = "4.5", features = ["derive"], optional = true }
num = { version = "0.4.1", default-features = false, features = ["alloc", "rand"] }
num-complex = { version = "0.4.4", default-features = false }
rand = { version = "0.8.5", default-features = false, features = ["getrandom"] }
rand_utils = { version = "0.8", package = "winter-rand-utils", optional = true }
serde = { version = "1.0", features = [
"derive",
], default-features = false, optional = true }
sha3 = { version = "0.10.8", default-features = false }
winter_crypto = { version = "0.8", package = "winter-crypto", default-features = false }
winter_math = { version = "0.8", package = "winter-math", default-features = false }
winter_utils = { version = "0.8", package = "winter-utils", default-features = false }
rand = {version = "0.8.5"}
num-complex = "0.4.4"
num = "0.4.1"
sha3 = "0.10.8"
hex = "0.4.3"

[dev-dependencies]
seq-macro = { version = "0.3" }
criterion = { version = "0.5", features = ["html_reports"] }
proptest = "1.4"
rand_utils = { version = "0.8", package = "winter-rand-utils" }
hex = { version = "0.4.3", default-features = false, features = ["alloc"] }

[build-dependencies]
cc = { version = "1.0", features = ["parallel"], optional = true }
Expand Down
1 change: 1 addition & 0 deletions src/dsa/rpo_falcon512/hash_to_point.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ pub fn hash_to_point_rpo256(message: Word, nonce: &Nonce) -> Polynomial<FalconFe

/// Returns a polynomial in Z_p[x]/(phi) representing the hash of the provided message and
/// nonce using SHAKE256. This is the hash-to-point algorithm used in the reference implementation.
#[allow(dead_code)]
pub fn hash_to_point_shake256(message: &[u8], nonce: &Nonce) -> Polynomial<FalconFelt> {
use sha3::{
digest::{ExtendableOutput, Update, XofReader},
Expand Down
45 changes: 9 additions & 36 deletions src/dsa/rpo_falcon512/keys/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,7 @@ pub use secret_key::SecretKey;
#[cfg(test)]
mod tests {
use super::{Felt, SecretKey, Word};
use rand::thread_rng;
use rand_utils::{rand_array, rand_vector};
use rand::rngs::OsRng;
use winter_utils::{Deserializable, Serializable};

#[test]
Expand All @@ -32,47 +31,21 @@ mod tests {
let sk = SecretKey::read_from_bytes(&buffer).unwrap();

// sign a random message
let message: Word = rand_vector::<Felt>(4).try_into().expect("Should not fail.");
let mut rng = thread_rng();
let message: Word =
rand_utils::rand_vector::<Felt>(4).try_into().expect("Should not fail.");
let mut rng = OsRng;
let signature = sk.sign(message, &mut rng);

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

// a signature should not verify against a wrong message
let message2: Word = rand_vector::<Felt>(4).try_into().expect("Should not fail.");
assert!(!pk.verify(message2, signature.as_ref().unwrap()));
let message2: Word =
rand_utils::rand_vector::<Felt>(4).try_into().expect("Should not fail.");
assert!(!pk.verify(message2, &signature));

// a signature should not verify against a wrong public key
let sk2 = SecretKey::new();
assert!(!sk2.public_key().verify(message, signature.as_ref().unwrap()))
}

#[test]
fn test_falcon_verification_from_seed() {
// generate keys from a random seed
let seed: [u8; 32] = rand_array();
let sk = SecretKey::from_seed(seed);
let pk = sk.public_key();

// test secret key serialization/deserialization
let sk_bytes = sk.to_bytes();
let sk = SecretKey::read_from_bytes(&sk_bytes).unwrap();

// sign a random message
let message: Word = rand_vector::<Felt>(4).try_into().expect("Should not fail.");
let mut rng = thread_rng();
let signature = sk.sign(message, &mut rng);

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

// a signature should not verify against a wrong message
let message2: Word = rand_vector::<Felt>(4).try_into().expect("Should not fail.");
assert!(!pk.verify(message2, signature.as_ref().unwrap()));

// a signature should not verify against a wrong public key
let keys2 = SecretKey::new();
assert!(!keys2.public_key().verify(message, signature.as_ref().unwrap()))
assert!(!sk2.public_key().verify(message, &signature))
}
}
23 changes: 12 additions & 11 deletions src/dsa/rpo_falcon512/keys/public_key.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use crate::dsa::rpo_falcon512::FALCON_ENCODING_BITS;

use super::{
super::{Rpo256, LOG_N, N, PK_LEN},
ByteReader, ByteWriter, Deserializable, DeserializationError, FalconFelt, Felt, Polynomial,
Expand All @@ -13,9 +15,8 @@ use num::Zero;
/// A public key for verifying signatures.
///
/// The public key is a [Word] (i.e., 4 field elements) that is the hash of the coefficients of
/// the polynomial representing the raw bytes of the expanded public key.
///
/// For Falcon-512, the first byte of the expanded public key is always equal to log2(512) i.e., 9.
/// the polynomial representing the raw bytes of the expanded public key. The hash is computed
/// using Rpo256.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct PublicKey(Word);

Expand Down Expand Up @@ -68,16 +69,16 @@ impl From<Polynomial<FalconFelt>> for PubKeyPoly {
impl Serializable for &PubKeyPoly {
fn write_into<W: ByteWriter>(&self, target: &mut W) {
let mut buf = [0_u8; PK_LEN];
buf[0] = 9;
buf[0] = LOG_N;

let mut acc = 0_u32;
let mut acc_len = 0;
let mut acc_len: u32 = 0;

let mut input_pos = 1;
for c in self.0.coefficients.iter() {
let c = c.value();
acc = (acc << 14) | c as u32;
acc_len += 14;
acc = (acc << FALCON_ENCODING_BITS) | c as u32;
acc_len += FALCON_ENCODING_BITS;
while acc_len >= 8 {
acc_len -= 8;
buf[input_pos] = (acc >> acc_len) as u8;
Expand All @@ -96,7 +97,7 @@ impl Deserializable for PubKeyPoly {
fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
let buf = source.read_array::<PK_LEN>()?;

if buf[0] != LOG_N as u8 {
if buf[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]
Expand All @@ -113,13 +114,13 @@ impl Deserializable for PubKeyPoly {
acc = (acc << 8) | (byte as u32);
acc_len += 8;

if acc_len >= 14 {
acc_len -= 14;
if acc_len >= FALCON_ENCODING_BITS {
acc_len -= FALCON_ENCODING_BITS;
let w = (acc >> acc_len) & 0x3FFF;
if w >= MODULUS as u32 {
return Err(DeserializationError::InvalidValue(format!("Failed to decode public key: coefficient {w} is greater than or equal to the field modulus {MODULUS}")));
}
output[output_idx] = FalconFelt::new((w) as i16);
output[output_idx] = FalconFelt::new(w as i16);
output_idx += 1;
}
}
Expand Down
94 changes: 45 additions & 49 deletions src/dsa/rpo_falcon512/keys/secret_key.rs
Original file line number Diff line number Diff line change
@@ -1,21 +1,30 @@
use super::{
super::{
math::{
decode_i8, encode_i8, ffldl, ffsampling, gram, normalize_tree, ntru_gen, FalconFelt,
FastFft, LdlTree, Polynomial,
decode_i8, encode_i8, ffldl, ffsampling, gram, normalize_tree, FalconFelt, FastFft,
LdlTree, Polynomial,
},
signature::SignaturePoly,
ByteReader, ByteWriter, Deserializable, DeserializationError, FalconError, Nonce,
Serializable, ShortLatticeBasis, Signature, Word, MODULUS, N, SIGMA, SIG_L2_BOUND,
ByteReader, ByteWriter, Deserializable, DeserializationError, Nonce, Serializable,
ShortLatticeBasis, Signature, Word, MODULUS, N, SIGMA, SIG_L2_BOUND,
},
PubKeyPoly, PublicKey,
};
use crate::dsa::rpo_falcon512::{hash_to_point::hash_to_point_rpo256, SIG_NONCE_LEN, SK_LEN};
use crate::dsa::rpo_falcon512::{
hash_to_point::hash_to_point_rpo256, math::ntru_gen, SIG_NONCE_LEN, SK_LEN,
};
use alloc::{string::ToString, vec::Vec};
use num::{Complex, Zero};
use rand::{thread_rng, Rng};

use num_complex::Complex64;
use rand::{rngs::OsRng, Rng, RngCore};

//#[cfg(all(feature = "std", feature = "std_rng"))]

// CONSTANTS
// ================================================================================================

const WIDTH_BIG_POLY_COEFFICIENT: usize = 8;
const WIDTH_SMALL_POLY_COEFFICIENT: usize = 6;

// SECRET KEY
// ================================================================================================
Expand Down Expand Up @@ -54,14 +63,16 @@ impl SecretKey {
// --------------------------------------------------------------------------------------------

/// Generates a secret key from OS-provided randomness.
#[cfg(feature = "std")]
pub fn new() -> Self {
Self::from_seed(thread_rng().gen())
let mut seed: [u8; 32] = [0; 32];
OsRng.fill_bytes(&mut seed);
//let rng: rand::rngs::StdRng = rand::SeedableRng::from_seed(seed);
Self::with_rng(&mut OsRng)
}

/// Generates a secret_key from the provided seed.
pub fn from_seed(seed: [u8; 32]) -> Self {
let basis = ntru_gen(N, seed);
/// Generates a secret_key using the provided random number generator `Rng`.
pub fn with_rng<R: Rng>(rng: &mut R) -> Self {
let basis = ntru_gen(N, rng);
Self::from_short_lattice_basis(basis)
}

Expand Down Expand Up @@ -105,15 +116,15 @@ impl SecretKey {
///
/// # Errors
/// Returns an error of signature generation fails.
pub fn sign<R: Rng>(&self, message: Word, rng: &mut R) -> Result<Signature, FalconError> {
pub fn sign<R: Rng>(&self, message: Word, rng: &mut R) -> Signature {
let mut nonce_bytes = [0u8; SIG_NONCE_LEN];
rng.fill_bytes(&mut nonce_bytes);
let nonce = Nonce::new(nonce_bytes);

let c = hash_to_point_rpo256(message, &nonce);
let (s1, s2) = self.sign_helper(c, rng)?;
let (s1, s2) = self.sign_helper(c, rng);

Ok(Signature::new(nonce, s1, s2))
Signature::new(nonce, s1, s2)
}

// HELPER METHODS
Expand All @@ -133,15 +144,13 @@ impl SecretKey {
/// Signs a message polynomial with the secret key.
///
/// Takes a randomness generator implementing `Rng` and message polynomial representing `c`
/// the hash-to-point of the message to be signed. It outputs a signature `Signature`.
///
/// # Errors
/// Returns an error of signature generation fails.
/// the hash-to-point of the message to be signed. It outputs a tuple of signature polynomials
/// `(s1, s2)`.
fn sign_helper<R: Rng>(
&self,
c: Polynomial<FalconFelt>,
rng: &mut R,
) -> Result<(SignaturePoly, SignaturePoly), FalconError> {
) -> (SignaturePoly, 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();

Expand All @@ -150,7 +159,7 @@ impl SecretKey {
let t0 = c_over_q_fft.hadamard_mul(&minus_big_f_fft);
let t1 = -c_over_q_fft.hadamard_mul(&minus_f_fft);

let s = loop {
loop {
let bold_s = loop {
let z = ffsampling(&(t0.clone(), t1.clone()), &self.tree, rng);
let t0_min_z0 = t0.clone() - z.0;
Expand Down Expand Up @@ -193,12 +202,11 @@ impl SecretKey {
if let Ok(s1) = SignaturePoly::try_from(&s1_coef) {
if let Ok(s2) = SignaturePoly::try_from(&s2_coef) {
if s2.fft().coefficients.iter().all(|&c| c != FalconFelt::zero()) {
break (s1, s2);
return (s1, s2);
}
}
}
};
Ok(s)
}
}
}

Expand All @@ -222,15 +230,15 @@ impl Serializable for SecretKey {
buffer.push(header);

let f_i8: Vec<i8> = f.coefficients.iter().map(|&a| -a as i8).collect();
let f_i8_encoded = encode_i8(&f_i8, 6).unwrap();
let f_i8_encoded = encode_i8(&f_i8, WIDTH_SMALL_POLY_COEFFICIENT).unwrap();
buffer.extend_from_slice(&f_i8_encoded);

let g_i8: Vec<i8> = g.coefficients.iter().map(|&a| a as i8).collect();
let g_i8_encoded = encode_i8(&g_i8, 6).unwrap();
let g_i8_encoded = encode_i8(&g_i8, WIDTH_SMALL_POLY_COEFFICIENT).unwrap();
buffer.extend_from_slice(&g_i8_encoded);

let big_f_i8: Vec<i8> = capital_f.coefficients.iter().map(|&a| -a as i8).collect();
let big_f_i8_encoded = encode_i8(&big_f_i8, 8).unwrap();
let big_f_i8_encoded = encode_i8(&big_f_i8, WIDTH_BIG_POLY_COEFFICIENT).unwrap();
buffer.extend_from_slice(&big_f_i8_encoded);
target.write_bytes(&buffer);
}
Expand Down Expand Up @@ -264,26 +272,24 @@ impl Deserializable for SecretKey {
));
}

let width_f = field_element_width(0);
let width_g = field_element_width(1);
let width_big_f = field_element_width(2);

if byte_vector.len() != SK_LEN {
return Err(DeserializationError::InvalidValue("Invalid encoding length: Failed to decode as length is different from the one expected".to_string()));
}

let chunk_size_f = ((n * width_f) + 7) >> 3;
let chunk_size_g = ((n * width_g) + 7) >> 3;
let chunk_size_big_f = ((n * width_big_f) + 7) >> 3;
let chunk_size_f = ((n * WIDTH_SMALL_POLY_COEFFICIENT) + 7) >> 3;
let chunk_size_g = ((n * WIDTH_SMALL_POLY_COEFFICIENT) + 7) >> 3;
let chunk_size_big_f = ((n * WIDTH_BIG_POLY_COEFFICIENT) + 7) >> 3;

let f = decode_i8(&byte_vector[1..chunk_size_f + 1], width_f).unwrap();
let g =
decode_i8(&byte_vector[chunk_size_f + 1..(chunk_size_f + chunk_size_g + 1)], width_g)
.unwrap();
let f = decode_i8(&byte_vector[1..chunk_size_f + 1], WIDTH_SMALL_POLY_COEFFICIENT).unwrap();
let g = decode_i8(
&byte_vector[chunk_size_f + 1..(chunk_size_f + chunk_size_g + 1)],
WIDTH_SMALL_POLY_COEFFICIENT,
)
.unwrap();
let big_f = decode_i8(
&byte_vector[(chunk_size_f + chunk_size_g + 1)
..(chunk_size_f + chunk_size_g + chunk_size_big_f + 1)],
width_big_f,
WIDTH_BIG_POLY_COEFFICIENT,
)
.unwrap();

Expand Down Expand Up @@ -315,13 +321,3 @@ fn to_complex_fft(basis: &[Polynomial<i16>; 4]) -> [Polynomial<Complex<f64>>; 4]
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]
}

/// Determines how many bits to use for each field element of a given polynomial of the secret
/// key during decoding.
fn field_element_width(polynomial_index: usize) -> usize {
if polynomial_index == 2 {
8
} else {
6
}
}
9 changes: 4 additions & 5 deletions src/dsa/rpo_falcon512/math/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use alloc::{string::String, vec::Vec};
use core::ops::MulAssign;
use num::{BigInt, FromPrimitive, One, Zero};
use num_complex::Complex64;
use rand::{rngs::StdRng, Rng, SeedableRng};
use rand::Rng;

mod fft;
pub use fft::{CyclotomicFourier, FastFft};
Expand Down Expand Up @@ -73,11 +73,10 @@ impl Inverse for f64 {
/// Algorithm 5 (NTRUgen) of the documentation [1, p.34].
///
/// [1]: https://falcon-sign.info/falcon.pdf
pub(crate) fn ntru_gen(n: usize, seed: [u8; 32]) -> [Polynomial<i16>; 4] {
let mut rng: StdRng = SeedableRng::from_seed(seed);
pub(crate) fn ntru_gen<R: Rng>(n: usize, rng: &mut R) -> [Polynomial<i16>; 4] {
loop {
let f = gen_poly(n, &mut rng);
let g = gen_poly(n, &mut rng);
let f = gen_poly(n, rng);
let g = gen_poly(n, rng);
let f_ntt = f.map(|&i| FalconFelt::new(i)).fft();
if f_ntt.coefficients.iter().any(|e| e.is_zero()) {
continue;
Expand Down
4 changes: 2 additions & 2 deletions src/dsa/rpo_falcon512/math/polynomial.rs
Original file line number Diff line number Diff line change
Expand Up @@ -601,8 +601,8 @@ mod tests {

#[test]
fn test_negacyclic_reduction() {
let coef1: [u16; N] = rand_utils::rand_array();
let coef2: [u16; N] = rand_utils::rand_array();
let coef1: [u8; N] = rand_utils::rand_array();
let coef2: [u8; N] = rand_utils::rand_array();

let poly1 = Polynomial::new(coef1.iter().map(|&a| FalconFelt::new(a as i16)).collect());
let poly2 = Polynomial::new(coef2.iter().map(|&a| FalconFelt::new(a as i16)).collect());
Expand Down
Loading

0 comments on commit 7a450da

Please sign in to comment.