From 8b0bfa4e3ab935ec4432be616a28b6364522d707 Mon Sep 17 00:00:00 2001 From: Tristan Britt <53545754+trbritt@users.noreply.github.com> Date: Mon, 11 Nov 2024 10:09:15 -0800 Subject: [PATCH] changed method of ran group element generation --- Cargo.toml | 1 + src/groups/g1.rs | 21 +++++++- src/groups/g2.rs | 23 ++++++-- src/groups/mod.rs | 133 +++++++++++++++++++++++++++++++++++++++++++++- 4 files changed, 171 insertions(+), 7 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 6d63f7e..f46e38f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -58,6 +58,7 @@ serde_json = "1.0.127" sha2 = "0.11.0-pre.4" tracing = "0.1.40" tracing-subscriber = "0.3.18" +paste = "1.0.15" [[bench]] name = "mod" diff --git a/src/groups/g1.rs b/src/groups/g1.rs index b300746..11086c5 100644 --- a/src/groups/g1.rs +++ b/src/groups/g1.rs @@ -21,6 +21,7 @@ use crate::fields::fp::{FieldExtensionTrait, Fp}; use crate::groups::group::{GroupAffine, GroupError, GroupProjective, GroupTrait}; use crate::hasher::Expander; use crate::svdw::{MapError, SvdW, SvdWTrait}; +use crate::Fr; use crypto_bigint::rand_core::CryptoRngCore; use num_traits::{One, Zero}; use std::sync::OnceLock; @@ -292,9 +293,25 @@ impl GroupTrait<1, 1, Fp> for G1Projective { Self::generator() } - /// Generates a random point in the ๐”พโ‚ group + /// Generates a random point in the ๐”พโ‚ group, using a pseudo-random + /// function according to formulation in ยง4.1.7.4 of the Moon Math Manual, + /// see fn rand(rng: &mut R) -> Self { - Self::generator() * >::rand(rng) + const K: usize = 10; + let a_i = (0..K) + .into_iter() + .map(|_| Fp::new(Fr::rand(rng).value())) + .collect::>(); + let b_i = (0..(K - 1)) + .into_iter() + .map(|_| Fp::new(Fr::rand(rng).value())) + .collect::>(); + let mut random_scalar = Fp::ONE; + (1..K).into_iter().for_each(|i| { + random_scalar = random_scalar * a_i[i] * b_i[i - 1]; + }); + random_scalar = random_scalar * a_i[0]; + Self::generator() * random_scalar } /// Hashes a message to a point on the ๐”พโ‚ group diff --git a/src/groups/g2.rs b/src/groups/g2.rs index b47ceb4..667202a 100644 --- a/src/groups/g2.rs +++ b/src/groups/g2.rs @@ -210,7 +210,9 @@ impl GroupTrait<2, 2, Fp2> for G2Projective { /// This function first generates a random point on the twist curve E'(๐”ฝโ‚šยฒ), /// then applies cofactor clearing to ensure the result is in the r-torsion subgroup. /// It is then passed through the `new` function to ensure it passes the curve and - /// subgroup checks. + /// subgroup checks. It generates the random scalar to create a pseudo-random + /// function according to formulation in ยง4.1.7.4 of the Moon Math Manual, + /// see /// /// # Examples /// @@ -228,9 +230,22 @@ impl GroupTrait<2, 2, Fp2> for G2Projective { 0, 0, ])); - let rando = Fp::new(Fr::rand(rng).value()); - let mut tmp = Self::generator() * rando; - tracing::trace!(?rando, ?tmp, "G2Projective::rand"); + const K: usize = 10; + let a_i = (0..K) + .into_iter() + .map(|_| Fp::new(Fr::rand(rng).value())) + .collect::>(); + let b_i = (0..(K - 1)) + .into_iter() + .map(|_| Fp::new(Fr::rand(rng).value())) + .collect::>(); + let mut random_scalar = Fp::ONE; + (1..K).into_iter().for_each(|i| { + random_scalar = random_scalar * a_i[i] * b_i[i - 1]; + }); + random_scalar = random_scalar * a_i[0]; + let mut tmp = Self::generator() * random_scalar; + tracing::trace!(?random_scalar, ?tmp, "G2Projective::rand"); // multiplying an element of the larger base field by the cofactor of a prime-ordered // subgroup will return an element in the prime-order subgroup, see diff --git a/src/groups/mod.rs b/src/groups/mod.rs index d26d101..25d8332 100644 --- a/src/groups/mod.rs +++ b/src/groups/mod.rs @@ -29,7 +29,7 @@ mod tests { use crate::fields::fp::{FieldExtensionTrait, Fp}; use crate::fields::fp2::Fp2; use crate::groups::g1::{G1Affine, G1Projective}; - use crate::groups::g2::G2Projective; + use crate::groups::g2::{G2Affine, G2Projective}; #[derive(Serialize, Deserialize, Clone)] struct _G2Coords { @@ -871,4 +871,135 @@ mod tests { } } } + mod random { + use super::*; + use crate::GroupTrait; + use rand_core::OsRng; + + /// This is the monobit test, which is a simplified version of NIST SP 800-22 + #[test] + fn g1_monobit_test() { + const SAMPLE_SIZE: usize = 1000; + let mut x_coordinates = Vec::with_capacity(SAMPLE_SIZE); + let mut y_coordinates = Vec::with_capacity(SAMPLE_SIZE); + + // Generate sample points + for _ in 0..SAMPLE_SIZE { + let affine = G1Affine::rand(&mut OsRng); + x_coordinates.push(affine.x); + y_coordinates.push(affine.y); + } + // Convert coordinates to bits and count 1s + let total_bits = SAMPLE_SIZE * 256; // Each Fp is 256 bits + for coords in [x_coordinates, y_coordinates] { + let mut total_ones = 0; + for coord in coords { + let bits = coord.to_be_bytes(); + for byte in bits { + total_ones += byte.count_ones() as usize; + } + } + // For truly random bits, we expect approximately 50% ones + let proportion_ones = total_ones as f64 / total_bits as f64; + assert!( + (proportion_ones - 0.5).abs() < 0.01, + "Bit distribution shows bias: {}", + proportion_ones + ); + } + } + #[test] + fn g2_monobit_test() { + const SAMPLE_SIZE: usize = 1000; + let mut x_coordinates = Vec::with_capacity(SAMPLE_SIZE); + let mut y_coordinates = Vec::with_capacity(SAMPLE_SIZE); + + // Generate sample points + for _ in 0..SAMPLE_SIZE { + let affine = G2Affine::rand(&mut OsRng); + x_coordinates.push(affine.x); + y_coordinates.push(affine.y); + } + // Convert coordinates to bits and count 1s + let total_bits = SAMPLE_SIZE * 256 * 2; // Each Fp is 256 bits + for coords in [x_coordinates, y_coordinates] { + let mut total_ones = 0; + for coord in coords { + for inner_coord in coord.0 { + let bits = inner_coord.to_be_bytes(); + for byte in bits { + total_ones += byte.count_ones() as usize; + } + } + } + // For truly random bits, we expect approximately 50% ones + let proportion_ones = total_ones as f64 / total_bits as f64; + assert!( + (proportion_ones - 0.5).abs() < 0.01, + "Bit distribution shows bias: {}", + proportion_ones + ); + } + } + } + /// These tests are mainly for line coverage to assert the correct unimplemented behaviour in + /// real usage scenarios encountered at runtime + mod unimplemented { + use super::*; + use crate::{Fp12, GroupTrait, Gt, XMDExpander}; + use rand_core::OsRng; + use sha3::Keccak256; + + mod gt { + use super::*; + #[test] + #[should_panic] + fn test_endo() { + let _ = Gt::identity().endomorphism(); + } + + #[test] + #[should_panic] + fn test_hash_to_curve() { + let expander = XMDExpander::::new(&[0xAA; 32], 180); + let _ = Gt::hash_to_curve(&expander, &[0x0F; 32]); + } + + #[test] + #[should_panic] + fn test_sign_message() { + let expander = XMDExpander::::new(&[0xAA; 32], 180); + let _ = Gt::sign_message(&expander, &[0x0F; 32], Fp12::rand(&mut OsRng)); + } + } + + mod g2 { + use super::*; + macro_rules! g2_panic_tests { + ($type:ty) => { + paste::paste! { + #[test] + #[should_panic] + fn []() { + let expander = XMDExpander::::new(&[0xAA; 32], 180); + let _ = <$type>::hash_to_curve(&expander, &[0x0F; 32]); + } + + #[test] + #[should_panic] + fn []() { + let expander = XMDExpander::::new(&[0xAA; 32], 180); + let _ = <$type>::sign_message( + &expander, + &[0x0F; 32], + >::rand(&mut OsRng), + ); + } + } + }; + } + g2_panic_tests!(G2Affine); + g2_panic_tests!(G2Projective); + } + } }