From f0099c186a3a03bb2e59d3d48d9af1e9ed9dd382 Mon Sep 17 00:00:00 2001 From: jotabulacios Date: Thu, 29 Aug 2024 12:24:25 -0300 Subject: [PATCH 01/20] save work --- .../curves/bn_254/compression.rs | 196 ++++++++++++++++++ .../short_weierstrass/curves/bn_254/curve.rs | 6 + .../short_weierstrass/curves/bn_254/sqrt.rs | 128 ++++++++++++ 3 files changed, 330 insertions(+) create mode 100644 math/src/elliptic_curve/short_weierstrass/curves/bn_254/compression.rs create mode 100644 math/src/elliptic_curve/short_weierstrass/curves/bn_254/sqrt.rs diff --git a/math/src/elliptic_curve/short_weierstrass/curves/bn_254/compression.rs b/math/src/elliptic_curve/short_weierstrass/curves/bn_254/compression.rs new file mode 100644 index 000000000..13d1d3367 --- /dev/null +++ b/math/src/elliptic_curve/short_weierstrass/curves/bn_254/compression.rs @@ -0,0 +1,196 @@ +use super::field_extension::BN254PrimeField; +use crate::{ + elliptic_curve::short_weierstrass::{ + curves::bn_254::curve::BN254Curve, point::ShortWeierstrassProjectivePoint, + }, + field::element::FieldElement, +}; +use core::cmp::Ordering; + +use crate::{ + cyclic_group::IsGroup, elliptic_curve::traits::FromAffine, errors::ByteConversionError, + traits::ByteConversion, +}; + +pub type G1Point = ShortWeierstrassProjectivePoint; +pub type BN254FieldElement = FieldElement; + + + +pub fn decompress_g1_point(input_bytes: &mut [u8; 48]) -> Result { + let first_byte = input_bytes.first().unwrap(); + // We get the 3 most significant bits + let prefix_bits = first_byte >> 5; + let first_bit = (prefix_bits & 4_u8) >> 2; + // If first bit is not 1, then the value is not compressed. + if first_bit != 1 { + return Err(ByteConversionError::ValueNotCompressed); + } + let second_bit = (prefix_bits & 2_u8) >> 1; + // If the second bit is 1, then the compressed point is the + // point at infinity and we return it directly. + if second_bit == 1 { + return Ok(G1Point::neutral_element()); + } + let third_bit = prefix_bits & 1_u8; + + let first_byte_without_control_bits = (first_byte << 3) >> 3; + input_bytes[0] = first_byte_without_control_bits; + + let x = BN254FieldElement::from_bytes_be(input_bytes)?; + + // We apply the elliptic curve formula to know the y^2 value. + let y_squared = x.pow(3_u16) + BN254FieldElement::from(3); + + let (y_sqrt_1, y_sqrt_2) = &y_squared.sqrt().ok_or(ByteConversionError::InvalidValue)?; + + // we call "negative" to the greate root, + // if the third bit is 1, we take this grater value. + // Otherwise, we take the second one. + let y = match ( + y_sqrt_1.representative().cmp(&y_sqrt_2.representative()), + third_bit, + ) { + (Ordering::Greater, 0) => y_sqrt_2, + (Ordering::Greater, _) => y_sqrt_1, + (Ordering::Less, 0) => y_sqrt_1, + (Ordering::Less, _) => y_sqrt_2, + (Ordering::Equal, _) => y_sqrt_1, + }; + + let point = + G1Point::from_affine(x, y.clone()).map_err(|_| ByteConversionError::InvalidValue)?; + + point + .is_in_subgroup() + .then_some(point) + .ok_or(ByteConversionError::PointNotInSubgroup) +} + +#[cfg(feature = "alloc")] +pub fn compress_g1_point(point: &G1Point) -> alloc::vec::Vec { + if *point == G1Point::neutral_element() { + // point is at infinity + let mut x_bytes = alloc::vec![0_u8; 48]; + x_bytes[0] |= 1 << 7; + x_bytes[0] |= 1 << 6; + x_bytes + } else { + // point is not at infinity + let point_affine = point.to_affine(); + let x = point_affine.x(); + let y = point_affine.y(); + + let mut x_bytes = x.to_bytes_be(); + + // Set first bit to to 1 indicate this is compressed element. + x_bytes[0] |= 1 << 7; + + let y_neg = core::ops::Neg::neg(y); + if y_neg.representative() < y.representative() { + x_bytes[0] |= 1 << 5; + } + x_bytes + } +} + + + +#[cfg(test)] +mod tests { + use super::{BN254FieldElement, G1Point}; + use crate::elliptic_curve::short_weierstrass::curves::bn_254::curve::BN254Curve; + use crate::elliptic_curve::traits::{FromAffine, IsEllipticCurve}; + + #[cfg(feature = "alloc")] + use super::compress_g1_point; + use super::decompress_g1_point; + use crate::{ + cyclic_group::IsGroup, traits::ByteConversion, unsigned_integer::element::UnsignedInteger, + }; + + /* + // This test isn't necessary for BN254 because the subgroup of G1 is G1. + #[test] + fn test_zero_point() { + let g1 = BN254Curve::generator(); + + assert!(g1.is_in_subgroup()); + let new_x = BN254FieldElement::zero(); + let new_y = BN254FieldElement::one() + BN254FieldElement::one(); + + let false_point2 = G1Point::from_affine(new_x, new_y).unwrap(); + + assert!(!false_point2.is_in_subgroup()); + } + */ + + #[cfg(feature = "alloc")] + #[test] + fn test_g1_compress_generator() { + let g = BN254Curve::generator(); + let mut compressed_g = compress_g1_point(&g); + let first_byte = compressed_g.first().unwrap(); + + let first_byte_without_control_bits = (first_byte << 3) >> 3; + compressed_g[0] = first_byte_without_control_bits; + + let compressed_g_x = BN254FieldElement::from_bytes_be(&compressed_g).unwrap(); + let g_x = g.x(); + + assert_eq!(*g_x, compressed_g_x); + } + + #[cfg(feature = "alloc")] + #[test] + fn test_g1_compress_point_at_inf() { + let inf = G1Point::neutral_element(); + let compressed_inf = compress_g1_point(&inf); + let first_byte = compressed_inf.first().unwrap(); + + assert_eq!(*first_byte >> 6, 3_u8); + } + + #[cfg(feature = "alloc")] + #[test] + /*fn test_compress_decompress_generator() { + let g = BN254Curve::generator(); + let compressed_g = compress_g1_point(&g); + let mut compressed_g_slice: [u8; 48] = compressed_g.try_into().unwrap(); + + let decompressed_g = decompress_g1_point(&mut compressed_g_slice).unwrap(); + + assert_eq!(g, decompressed_g); + } + +*/ + fn test_compress_decompress_generator() { + let g = BN254Curve::generator(); + let compressed_g = compress_g1_point(&g); + println!("Compressed point: {:?}", compressed_g); + let mut compressed_g_slice: [u8; 48] = compressed_g.try_into().unwrap(); + match decompress_g1_point(&mut compressed_g_slice) { + Ok(decompressed_g) => { + assert_eq!(g, decompressed_g, "Decompressed point does not match original"); + }, + Err(e) => { + panic!("Failed to decompress point: {:?}", e); + } + } + } + + #[cfg(feature = "alloc")] + #[test] + fn test_compress_decompress_2g() { + let g = BN254Curve::generator(); + // calculate g point operate with itself + let g_2 = g.operate_with_self(UnsignedInteger::<4>::from("2")); + + let compressed_g2 = compress_g1_point(&g_2); + let mut compressed_g2_slice: [u8; 48] = compressed_g2.try_into().unwrap(); + + let decompressed_g2 = decompress_g1_point(&mut compressed_g2_slice).unwrap(); + + assert_eq!(g_2, decompressed_g2); + } +} diff --git a/math/src/elliptic_curve/short_weierstrass/curves/bn_254/curve.rs b/math/src/elliptic_curve/short_weierstrass/curves/bn_254/curve.rs index 0f737b3da..02ede27c4 100644 --- a/math/src/elliptic_curve/short_weierstrass/curves/bn_254/curve.rs +++ b/math/src/elliptic_curve/short_weierstrass/curves/bn_254/curve.rs @@ -39,6 +39,12 @@ impl IsShortWeierstrass for BN254Curve { } } +impl ShortWeierstrassProjectivePoint { + pub fn is_in_subgroup(&self) -> bool { + true + } +} + impl ShortWeierstrassProjectivePoint { /// phi morphism used to G2 subgroup check for twisted curve. /// We also use phi at the last lines of the Miller Loop of the pairing. diff --git a/math/src/elliptic_curve/short_weierstrass/curves/bn_254/sqrt.rs b/math/src/elliptic_curve/short_weierstrass/curves/bn_254/sqrt.rs new file mode 100644 index 000000000..369417c6c --- /dev/null +++ b/math/src/elliptic_curve/short_weierstrass/curves/bn_254/sqrt.rs @@ -0,0 +1,128 @@ +use crate::field::traits::LegendreSymbol; + +use super::{curve::BN254FieldElement, curve::BN254TwistCurveFieldElement}; +use core::cmp::Ordering; + +#[must_use] +pub fn select_sqrt_value_from_third_bit( + sqrt_1: BN254FieldElement, + sqrt_2: BN254FieldElement, + third_bit: u8, +) -> BN254FieldElement { + match ( + sqrt_1.representative().cmp(&sqrt_2.representative()), + third_bit, + ) { + (Ordering::Greater, 0) => sqrt_2, + (Ordering::Greater, _) | (Ordering::Less, 0) | (Ordering::Equal, _) => sqrt_1, + (Ordering::Less, _) => sqrt_2, + } +} + +/// * `third_bit` - if 1, then the square root is the greater one, otherwise it is the smaller one. +#[must_use] +pub fn sqrt_qfe( + input: &BN254TwistCurveFieldElement, + third_bit: u8, +) -> Option { + // Algorithm 8, https://eprint.iacr.org/2012/685.pdf + if *input == BN254TwistCurveFieldElement::zero() { + Some(BN254TwistCurveFieldElement::zero()) + } else { + let a = input.value()[0].clone(); + let b = input.value()[1].clone(); + if b == BN254FieldElement::zero() { + // second part is zero + let (y_sqrt_1, y_sqrt_2) = a.sqrt()?; + let y_aux = select_sqrt_value_from_third_bit(y_sqrt_1, y_sqrt_2, third_bit); + + Some(BN254TwistCurveFieldElement::new([ + y_aux, + BN254FieldElement::zero(), + ])) + } else { + // second part of the input field number is non-zero + // instead of "sum" is: -beta + let alpha = a.pow(2u64) + b.pow(2u64); + let gamma = alpha.legendre_symbol(); + match gamma { + LegendreSymbol::One => { + let two = BN254FieldElement::from(2u64); + let two_inv = two.inv().unwrap(); + // calculate the square root of alpha + let (y_sqrt1, y_sqrt2) = alpha.sqrt()?; + let mut delta = (a.clone() + y_sqrt1) * two_inv.clone(); + + let legendre_delta = delta.legendre_symbol(); + if legendre_delta == LegendreSymbol::MinusOne { + delta = (a + y_sqrt2) * two_inv; + }; + let (x_sqrt_1, x_sqrt_2) = delta.sqrt()?; + let x_0 = select_sqrt_value_from_third_bit(x_sqrt_1, x_sqrt_2, third_bit); + let x_1 = b * (two * x_0.clone()).inv().unwrap(); + Some(BN254TwistCurveFieldElement::new([x_0, x_1])) + } + LegendreSymbol::MinusOne => None, + LegendreSymbol::Zero => { + unreachable!("The input is zero, but we already handled this case.") + } + } + } + } +} + +#[cfg(test)] +mod tests { + use super::super::curve::BN254FieldElement; + + #[test] + fn test_sqrt_qfe() { + let c1 = BN254FieldElement::from_hex( + "0x13e02b6052719f607dacd3a088274f65596bd0d09920b61ab5da61bbdc7f5049334cf11213945d57e5ac7d055d042b7e", + ).unwrap(); + let c0 = BN254FieldElement::from_hex( + "0x024aa2b2f08f0a91260805272dc51051c6e47ad4fa403b02b4510b647ae3d1770bac0326a805bbefd48056c8c121bdb8" + ).unwrap(); + let qfe = super::BN254TwistCurveFieldElement::new([c0, c1]); + + let b1 = BN254FieldElement::from_hex("0x4").unwrap(); + let b0 = BN254FieldElement::from_hex("0x4").unwrap(); + let qfe_b = super::BN254TwistCurveFieldElement::new([b0, b1]); + + let cubic_value = qfe.pow(3_u64) + qfe_b; + let root = super::sqrt_qfe(&cubic_value, 0).unwrap(); + + let c0_expected = BN254FieldElement::from_hex("0x0ce5d527727d6e118cc9cdc6da2e351aadfd9baa8cbdd3a76d429a695160d12c923ac9cc3baca289e193548608b82801").unwrap(); + let c1_expected = BN254FieldElement::from_hex("0x0606c4a02ea734cc32acd2b02bc28b99cb3e287e85a763af267492ab572e99ab3f370d275cec1da1aaa9075ff05f79be").unwrap(); + let qfe_expected = super::BN254TwistCurveFieldElement::new([c0_expected, c1_expected]); + + let value_root = root.value(); + let value_qfe_expected = qfe_expected.value(); + + assert_eq!(value_root[0].clone(), value_qfe_expected[0].clone()); + assert_eq!(value_root[1].clone(), value_qfe_expected[1].clone()); + } + + #[test] + fn test_sqrt_qfe_2() { + let c0 = BN254FieldElement::from_hex("0x02").unwrap(); + let c1 = BN254FieldElement::from_hex("0x00").unwrap(); + let qfe = super::BN254TwistCurveFieldElement::new([c0, c1]); + + let c0_expected = BN254FieldElement::from_hex("0x013a59858b6809fca4d9a3b6539246a70051a3c88899964a42bc9a69cf9acdd9dd387cfa9086b894185b9a46a402be73").unwrap(); + let c1_expected = BN254FieldElement::from_hex("0x02d27e0ec3356299a346a09ad7dc4ef68a483c3aed53f9139d2f929a3eecebf72082e5e58c6da24ee32e03040c406d4f").unwrap(); + let qfe_expected = super::BN254TwistCurveFieldElement::new([c0_expected, c1_expected]); + + let b1 = BN254FieldElement::from_hex("0x4").unwrap(); + let b0 = BN254FieldElement::from_hex("0x4").unwrap(); + let qfe_b = super::BN254TwistCurveFieldElement::new([b0, b1]); + + let root = super::sqrt_qfe(&(qfe.pow(3_u64) + qfe_b), 0).unwrap(); + + let value_root = root.value(); + let value_qfe_expected = qfe_expected.value(); + + assert_eq!(value_root[0].clone(), value_qfe_expected[0].clone()); + assert_eq!(value_root[1].clone(), value_qfe_expected[1].clone()); + } +} From 8c381f57ab25b54d89687ba80bb07d527e1c1d59 Mon Sep 17 00:00:00 2001 From: jotabulacios Date: Tue, 3 Sep 2024 16:41:37 -0300 Subject: [PATCH 02/20] add sqrt for bn254 --- .../short_weierstrass/curves/bn_254/mod.rs | 1 + .../short_weierstrass/curves/bn_254/sqrt.rs | 119 +++++++++++++----- 2 files changed, 86 insertions(+), 34 deletions(-) diff --git a/math/src/elliptic_curve/short_weierstrass/curves/bn_254/mod.rs b/math/src/elliptic_curve/short_weierstrass/curves/bn_254/mod.rs index 04fb6ce92..a0c43ad8f 100644 --- a/math/src/elliptic_curve/short_weierstrass/curves/bn_254/mod.rs +++ b/math/src/elliptic_curve/short_weierstrass/curves/bn_254/mod.rs @@ -2,4 +2,5 @@ pub mod curve; pub mod default_types; pub mod field_extension; pub mod pairing; +pub mod sqrt; pub mod twist; diff --git a/math/src/elliptic_curve/short_weierstrass/curves/bn_254/sqrt.rs b/math/src/elliptic_curve/short_weierstrass/curves/bn_254/sqrt.rs index 369417c6c..5298645a3 100644 --- a/math/src/elliptic_curve/short_weierstrass/curves/bn_254/sqrt.rs +++ b/math/src/elliptic_curve/short_weierstrass/curves/bn_254/sqrt.rs @@ -1,6 +1,5 @@ +use super::curve::{BN254FieldElement, BN254TwistCurveFieldElement}; use crate::field::traits::LegendreSymbol; - -use super::{curve::BN254FieldElement, curve::BN254TwistCurveFieldElement}; use core::cmp::Ordering; #[must_use] @@ -74,50 +73,101 @@ pub fn sqrt_qfe( #[cfg(test)] mod tests { use super::super::curve::BN254FieldElement; + use super::super::twist::BN254TwistCurve; + use crate::elliptic_curve::short_weierstrass::traits::IsShortWeierstrass; #[test] + /// We took the q1 point of the test two_pairs_of_points_match_1 from pairing.rs + /// to get the values of x and y. fn test_sqrt_qfe() { - let c1 = BN254FieldElement::from_hex( - "0x13e02b6052719f607dacd3a088274f65596bd0d09920b61ab5da61bbdc7f5049334cf11213945d57e5ac7d055d042b7e", - ).unwrap(); - let c0 = BN254FieldElement::from_hex( - "0x024aa2b2f08f0a91260805272dc51051c6e47ad4fa403b02b4510b647ae3d1770bac0326a805bbefd48056c8c121bdb8" - ).unwrap(); - let qfe = super::BN254TwistCurveFieldElement::new([c0, c1]); - - let b1 = BN254FieldElement::from_hex("0x4").unwrap(); - let b0 = BN254FieldElement::from_hex("0x4").unwrap(); - let qfe_b = super::BN254TwistCurveFieldElement::new([b0, b1]); - - let cubic_value = qfe.pow(3_u64) + qfe_b; - let root = super::sqrt_qfe(&cubic_value, 0).unwrap(); - - let c0_expected = BN254FieldElement::from_hex("0x0ce5d527727d6e118cc9cdc6da2e351aadfd9baa8cbdd3a76d429a695160d12c923ac9cc3baca289e193548608b82801").unwrap(); - let c1_expected = BN254FieldElement::from_hex("0x0606c4a02ea734cc32acd2b02bc28b99cb3e287e85a763af267492ab572e99ab3f370d275cec1da1aaa9075ff05f79be").unwrap(); - let qfe_expected = super::BN254TwistCurveFieldElement::new([c0_expected, c1_expected]); - - let value_root = root.value(); - let value_qfe_expected = qfe_expected.value(); + // Coordinate x of q. + let x = super::BN254TwistCurveFieldElement::new([ + BN254FieldElement::from_hex_unchecked( + "1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed", + ), + BN254FieldElement::from_hex_unchecked( + "198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c2", + ), + ]); + + let qfe_b = BN254TwistCurve::b(); + // The equation of the twisted curve is y^2 = x^3 + 3 /(9+u) + let y_square = x.pow(3_u64) + qfe_b; + let y = super::sqrt_qfe(&y_square, 0).unwrap(); + + // Coordinate y of q. + let y_expected = super::BN254TwistCurveFieldElement::new([ + BN254FieldElement::from_hex_unchecked( + "12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa", + ), + BN254FieldElement::from_hex_unchecked( + "090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b", + ), + ]); + + let value_y = y.value(); + let value_y_expected = y_expected.value(); + + assert_eq!(value_y[0].clone(), value_y_expected[0].clone()); + assert_eq!(value_y[1].clone(), value_y_expected[1].clone()); + } - assert_eq!(value_root[0].clone(), value_qfe_expected[0].clone()); - assert_eq!(value_root[1].clone(), value_qfe_expected[1].clone()); + #[test] + /// We took the q1 point of the test two_pairs_of_points_match_2 from pairing.rs + fn test_sqrt_qfe_2() { + let x = super::BN254TwistCurveFieldElement::new([ + BN254FieldElement::from_hex_unchecked( + "3010c68cb50161b7d1d96bb71edfec9880171954e56871abf3d93cc94d745fa1", + ), + BN254FieldElement::from_hex_unchecked( + "0476be093a6d2b4bbf907172049874af11e1b6267606e00804d3ff0037ec57fd", + ), + ]); + + let qfe_b = BN254TwistCurve::b(); + + let y_square = x.pow(3_u64) + qfe_b; + let y = super::sqrt_qfe(&y_square, 0).unwrap(); + + let y_expected = super::BN254TwistCurveFieldElement::new([ + BN254FieldElement::from_hex_unchecked( + "01b33461f39d9e887dbb100f170a2345dde3c07e256d1dfa2b657ba5cd030427", + ), + BN254FieldElement::from_hex_unchecked( + "14c059d74e5b6c4ec14ae5864ebe23a71781d86c29fb8fb6cce94f70d3de7a21", + ), + ]); + + let value_y = y.value(); + let value_y_expected = y_expected.value(); + + assert_eq!(value_y[0].clone(), value_y_expected[0].clone()); + assert_eq!(value_y[1].clone(), value_y_expected[1].clone()); } + /* #[test] fn test_sqrt_qfe_2() { - let c0 = BN254FieldElement::from_hex("0x02").unwrap(); - let c1 = BN254FieldElement::from_hex("0x00").unwrap(); + let c0 = BN254FieldElement::from_hex_unchecked( + "3010c68cb50161b7d1d96bb71edfec9880171954e56871abf3d93cc94d745fa1", + ); + let c1 = BN254FieldElement::from_hex_unchecked( + "0476be093a6d2b4bbf907172049874af11e1b6267606e00804d3ff0037ec57fd", + ); let qfe = super::BN254TwistCurveFieldElement::new([c0, c1]); - let c0_expected = BN254FieldElement::from_hex("0x013a59858b6809fca4d9a3b6539246a70051a3c88899964a42bc9a69cf9acdd9dd387cfa9086b894185b9a46a402be73").unwrap(); - let c1_expected = BN254FieldElement::from_hex("0x02d27e0ec3356299a346a09ad7dc4ef68a483c3aed53f9139d2f929a3eecebf72082e5e58c6da24ee32e03040c406d4f").unwrap(); - let qfe_expected = super::BN254TwistCurveFieldElement::new([c0_expected, c1_expected]); + let qfe_b = BN254TwistCurve::b(); - let b1 = BN254FieldElement::from_hex("0x4").unwrap(); - let b0 = BN254FieldElement::from_hex("0x4").unwrap(); - let qfe_b = super::BN254TwistCurveFieldElement::new([b0, b1]); + let cubic_value = qfe.pow(3_u64) + qfe_b; + let root = super::sqrt_qfe(&cubic_value, 0).unwrap(); - let root = super::sqrt_qfe(&(qfe.pow(3_u64) + qfe_b), 0).unwrap(); + let c0_expected = BN254FieldElement::from_hex_unchecked( + "01b33461f39d9e887dbb100f170a2345dde3c07e256d1dfa2b657ba5cd030427", + ); + let c1_expected = BN254FieldElement::from_hex_unchecked( + "14c059d74e5b6c4ec14ae5864ebe23a71781d86c29fb8fb6cce94f70d3de7a21", + ); + let qfe_expected = super::BN254TwistCurveFieldElement::new([c0_expected, c1_expected]); let value_root = root.value(); let value_qfe_expected = qfe_expected.value(); @@ -125,4 +175,5 @@ mod tests { assert_eq!(value_root[0].clone(), value_qfe_expected[0].clone()); assert_eq!(value_root[1].clone(), value_qfe_expected[1].clone()); } + */ } From 0341a433bc32acfeb5a8fb74ff65547bd8cb8ba2 Mon Sep 17 00:00:00 2001 From: Nicole Date: Tue, 3 Sep 2024 18:56:00 -0300 Subject: [PATCH 03/20] compression and decompression for G1 working --- .../curves/bn_254/compression.rs | 269 ++++++++++-------- .../short_weierstrass/curves/bn_254/mod.rs | 1 + .../short_weierstrass/traits.rs | 14 + 3 files changed, 168 insertions(+), 116 deletions(-) diff --git a/math/src/elliptic_curve/short_weierstrass/curves/bn_254/compression.rs b/math/src/elliptic_curve/short_weierstrass/curves/bn_254/compression.rs index 13d1d3367..0165aa3b5 100644 --- a/math/src/elliptic_curve/short_weierstrass/curves/bn_254/compression.rs +++ b/math/src/elliptic_curve/short_weierstrass/curves/bn_254/compression.rs @@ -1,7 +1,12 @@ -use super::field_extension::BN254PrimeField; +use super::{field_extension::BN254PrimeField, twist::BN254TwistCurve}; use crate::{ - elliptic_curve::short_weierstrass::{ - curves::bn_254::curve::BN254Curve, point::ShortWeierstrassProjectivePoint, + elliptic_curve::{ + short_weierstrass::{ + curves::bn_254::{curve::BN254Curve, field_extension::Degree2ExtensionField, sqrt}, + point::ShortWeierstrassProjectivePoint, + traits::{Compress, IsShortWeierstrass}, + }, + traits::IsEllipticCurve, }, field::element::FieldElement, }; @@ -12,124 +17,168 @@ use crate::{ traits::ByteConversion, }; -pub type G1Point = ShortWeierstrassProjectivePoint; -pub type BN254FieldElement = FieldElement; +type G1Point = ShortWeierstrassProjectivePoint; +type BN254FieldElement = FieldElement; +// Bytes returns binary representation of p +// will store X coordinate in regular form and a parity bit +// as we have less than 3 bits available in our coordinate, we can't follow BLS12-381 style encoding (ZCash/IETF) +// +// we use the 2 most significant bits instead +// +// 00 -> uncompressed +// 10 -> compressed and y_neg >= y +// 11 -> compressed and y_neg < y +// 01 -> compressed infinity point +// the "uncompressed infinity point" will just have 00 (uncompressed) followed by zeroes (infinity = 0,0 in affine coordinates) +impl Compress for BN254Curve { + type G1Point = G1Point; -pub fn decompress_g1_point(input_bytes: &mut [u8; 48]) -> Result { - let first_byte = input_bytes.first().unwrap(); - // We get the 3 most significant bits - let prefix_bits = first_byte >> 5; - let first_bit = (prefix_bits & 4_u8) >> 2; - // If first bit is not 1, then the value is not compressed. - if first_bit != 1 { - return Err(ByteConversionError::ValueNotCompressed); - } - let second_bit = (prefix_bits & 2_u8) >> 1; - // If the second bit is 1, then the compressed point is the - // point at infinity and we return it directly. - if second_bit == 1 { - return Ok(G1Point::neutral_element()); + type G2Point = ::PointRepresentation; + + type Error = ByteConversionError; + + #[cfg(feature = "alloc")] + fn compress_g1_point(point: &Self::G1Point) -> alloc::vec::Vec { + if *point == G1Point::neutral_element() { + // point is at infinity + let mut x_bytes = alloc::vec![0_u8; 32]; + x_bytes[0] |= 1 << 6; // x_bytes = 01000000 + x_bytes + } else { + // point is not at infinity + let point_affine = point.to_affine(); + let x = point_affine.x(); + let y = point_affine.y(); + + let mut x_bytes = x.to_bytes_be(); + + // Set first bit to to 1 indicate this is compressed element. + x_bytes[0] |= 1 << 7; // x_bytes = 10000000 + + let y_neg = core::ops::Neg::neg(y); + if y_neg.representative() < y.representative() { + x_bytes[0] |= 1 << 6; // x_bytes = 11000000 + } + x_bytes + } } - let third_bit = prefix_bits & 1_u8; - - let first_byte_without_control_bits = (first_byte << 3) >> 3; - input_bytes[0] = first_byte_without_control_bits; - - let x = BN254FieldElement::from_bytes_be(input_bytes)?; - - // We apply the elliptic curve formula to know the y^2 value. - let y_squared = x.pow(3_u16) + BN254FieldElement::from(3); - - let (y_sqrt_1, y_sqrt_2) = &y_squared.sqrt().ok_or(ByteConversionError::InvalidValue)?; - - // we call "negative" to the greate root, - // if the third bit is 1, we take this grater value. - // Otherwise, we take the second one. - let y = match ( - y_sqrt_1.representative().cmp(&y_sqrt_2.representative()), - third_bit, - ) { - (Ordering::Greater, 0) => y_sqrt_2, - (Ordering::Greater, _) => y_sqrt_1, - (Ordering::Less, 0) => y_sqrt_1, - (Ordering::Less, _) => y_sqrt_2, - (Ordering::Equal, _) => y_sqrt_1, - }; - let point = - G1Point::from_affine(x, y.clone()).map_err(|_| ByteConversionError::InvalidValue)?; - - point - .is_in_subgroup() - .then_some(point) - .ok_or(ByteConversionError::PointNotInSubgroup) -} + fn decompress_g1_point(input_bytes: &mut [u8; 32]) -> Result { + let first_byte = input_bytes.first().unwrap(); + // We get the 2 most significant bits + let prefix_bits = first_byte >> 6; -#[cfg(feature = "alloc")] -pub fn compress_g1_point(point: &G1Point) -> alloc::vec::Vec { - if *point == G1Point::neutral_element() { - // point is at infinity - let mut x_bytes = alloc::vec![0_u8; 48]; - x_bytes[0] |= 1 << 7; - x_bytes[0] |= 1 << 6; - x_bytes - } else { - // point is not at infinity - let point_affine = point.to_affine(); - let x = point_affine.x(); - let y = point_affine.y(); - - let mut x_bytes = x.to_bytes_be(); - - // Set first bit to to 1 indicate this is compressed element. - x_bytes[0] |= 1 << 7; - - let y_neg = core::ops::Neg::neg(y); - if y_neg.representative() < y.representative() { - x_bytes[0] |= 1 << 5; + // If first byte is 00000000, then the value is not compressed. + if prefix_bits == 0_u8 { + return Err(ByteConversionError::ValueNotCompressed); } - x_bytes + + // If first byte is 01000000, then the compressed point is the + // point at infinity and we return it directly. + if prefix_bits == 1_u8 { + return Ok(G1Point::neutral_element()); + } + + let first_byte_without_control_bits = (first_byte << 2) >> 2; + input_bytes[0] = first_byte_without_control_bits; + + let x = BN254FieldElement::from_bytes_be(input_bytes)?; + + // We apply the elliptic curve formula to know the y^2 value. + let y_squared = x.pow(3_u16) + BN254FieldElement::from(3); + + let (y_sqrt_1, y_sqrt_2) = &y_squared.sqrt().ok_or(ByteConversionError::InvalidValue)?; + + // we call "negative" to the greater root, + // if the frist two bits are 10, we take this grater value. + // if the first two bits are 11, we take the second one. + let y = match ( + y_sqrt_1.representative().cmp(&y_sqrt_2.representative()), + prefix_bits, + ) { + (Ordering::Greater, 2_u8) => y_sqrt_2, + (Ordering::Greater, _) => y_sqrt_1, + (Ordering::Less, 2_u8) => y_sqrt_1, + (Ordering::Less, _) => y_sqrt_2, + (Ordering::Equal, _) => y_sqrt_1, + }; + + let point = + G1Point::from_affine(x, y.clone()).map_err(|_| ByteConversionError::InvalidValue)?; + + Ok(point) } -} + #[allow(unused)] + fn decompress_g2_point(input_bytes: &mut [u8; 64]) -> Result { + let first_byte = input_bytes.first().unwrap(); + + // We get the first 2 bits + let prefix_bits = first_byte >> 6; + // If first two bits are 00, then the value is not compressed. + if prefix_bits == 0_u8 { + return Err(ByteConversionError::InvalidValue); + } + + // If the first two bits are 01, then the compressed point is the + // point at infinity and we return it directly. + if prefix_bits == 1_u8 { + return Ok(Self::G2Point::neutral_element()); + } + + let first_byte_without_control_bits = (first_byte << 2) >> 2; + input_bytes[0] = first_byte_without_control_bits; + + let input0 = &input_bytes[32..]; + let input1 = &input_bytes[0..32]; + let x0 = BN254FieldElement::from_bytes_be(input0).unwrap(); + let x1 = BN254FieldElement::from_bytes_be(input1).unwrap(); + let x: FieldElement = FieldElement::new([x0, x1]); + + let b_param_qfe = BN254TwistCurve::b(); + + let y = sqrt::sqrt_qfe(&(x.pow(3_u64) + b_param_qfe), 0) + .ok_or(ByteConversionError::InvalidValue)?; + + Self::G2Point::from_affine(x, y).map_err(|_| ByteConversionError::InvalidValue) + } +} #[cfg(test)] mod tests { use super::{BN254FieldElement, G1Point}; use crate::elliptic_curve::short_weierstrass::curves::bn_254::curve::BN254Curve; + use crate::elliptic_curve::short_weierstrass::traits::Compress; use crate::elliptic_curve::traits::{FromAffine, IsEllipticCurve}; #[cfg(feature = "alloc")] - use super::compress_g1_point; - use super::decompress_g1_point; use crate::{ cyclic_group::IsGroup, traits::ByteConversion, unsigned_integer::element::UnsignedInteger, }; - /* - // This test isn't necessary for BN254 because the subgroup of G1 is G1. - #[test] - fn test_zero_point() { - let g1 = BN254Curve::generator(); + #[test] + fn test_zero_point() { + let g1 = BN254Curve::generator(); - assert!(g1.is_in_subgroup()); - let new_x = BN254FieldElement::zero(); - let new_y = BN254FieldElement::one() + BN254FieldElement::one(); + assert!(g1.is_in_subgroup()); + let new_x = BN254FieldElement::zero(); + let new_y = BN254FieldElement::one() + BN254FieldElement::one(); - let false_point2 = G1Point::from_affine(new_x, new_y).unwrap(); + let false_point2 = G1Point::from_affine(new_x, new_y).unwrap(); - assert!(!false_point2.is_in_subgroup()); - } + assert!(!false_point2.is_in_subgroup()); + } */ - #[cfg(feature = "alloc")] #[test] fn test_g1_compress_generator() { + use crate::elliptic_curve::short_weierstrass::traits::Compress; + let g = BN254Curve::generator(); - let mut compressed_g = compress_g1_point(&g); + let mut compressed_g = BN254Curve::compress_g1_point(&g); let first_byte = compressed_g.first().unwrap(); let first_byte_without_control_bits = (first_byte << 3) >> 3; @@ -144,40 +193,28 @@ mod tests { #[cfg(feature = "alloc")] #[test] fn test_g1_compress_point_at_inf() { + use crate::elliptic_curve::short_weierstrass::traits::Compress; + let inf = G1Point::neutral_element(); - let compressed_inf = compress_g1_point(&inf); + let compressed_inf = BN254Curve::compress_g1_point(&inf); let first_byte = compressed_inf.first().unwrap(); - assert_eq!(*first_byte >> 6, 3_u8); + assert_eq!(*first_byte >> 6, 1_u8); } #[cfg(feature = "alloc")] #[test] - /*fn test_compress_decompress_generator() { + fn test_compress_decompress_generator() { + use crate::elliptic_curve::short_weierstrass::traits::Compress; + let g = BN254Curve::generator(); - let compressed_g = compress_g1_point(&g); - let mut compressed_g_slice: [u8; 48] = compressed_g.try_into().unwrap(); + let compressed_g = BN254Curve::compress_g1_point(&g); + let mut compressed_g_slice: [u8; 32] = compressed_g.try_into().unwrap(); - let decompressed_g = decompress_g1_point(&mut compressed_g_slice).unwrap(); + let decompressed_g = BN254Curve::decompress_g1_point(&mut compressed_g_slice).unwrap(); assert_eq!(g, decompressed_g); } - -*/ - fn test_compress_decompress_generator() { - let g = BN254Curve::generator(); - let compressed_g = compress_g1_point(&g); - println!("Compressed point: {:?}", compressed_g); - let mut compressed_g_slice: [u8; 48] = compressed_g.try_into().unwrap(); - match decompress_g1_point(&mut compressed_g_slice) { - Ok(decompressed_g) => { - assert_eq!(g, decompressed_g, "Decompressed point does not match original"); - }, - Err(e) => { - panic!("Failed to decompress point: {:?}", e); - } - } - } #[cfg(feature = "alloc")] #[test] @@ -186,10 +223,10 @@ mod tests { // calculate g point operate with itself let g_2 = g.operate_with_self(UnsignedInteger::<4>::from("2")); - let compressed_g2 = compress_g1_point(&g_2); - let mut compressed_g2_slice: [u8; 48] = compressed_g2.try_into().unwrap(); + let compressed_g2 = BN254Curve::compress_g1_point(&g_2); + let mut compressed_g2_slice: [u8; 32] = compressed_g2.try_into().unwrap(); - let decompressed_g2 = decompress_g1_point(&mut compressed_g2_slice).unwrap(); + let decompressed_g2 = BN254Curve::decompress_g1_point(&mut compressed_g2_slice).unwrap(); assert_eq!(g_2, decompressed_g2); } diff --git a/math/src/elliptic_curve/short_weierstrass/curves/bn_254/mod.rs b/math/src/elliptic_curve/short_weierstrass/curves/bn_254/mod.rs index a0c43ad8f..dae5eebe5 100644 --- a/math/src/elliptic_curve/short_weierstrass/curves/bn_254/mod.rs +++ b/math/src/elliptic_curve/short_weierstrass/curves/bn_254/mod.rs @@ -1,3 +1,4 @@ +pub mod compression; pub mod curve; pub mod default_types; pub mod field_extension; diff --git a/math/src/elliptic_curve/short_weierstrass/traits.rs b/math/src/elliptic_curve/short_weierstrass/traits.rs index 100623303..dc3acfcb2 100644 --- a/math/src/elliptic_curve/short_weierstrass/traits.rs +++ b/math/src/elliptic_curve/short_weierstrass/traits.rs @@ -1,3 +1,4 @@ +use crate::cyclic_group::IsGroup; use crate::elliptic_curve::traits::IsEllipticCurve; use crate::field::element::FieldElement; use core::fmt::Debug; @@ -18,3 +19,16 @@ pub trait IsShortWeierstrass: IsEllipticCurve + Clone + Debug { y.pow(2_u16) - x.pow(3_u16) - Self::a() * x - Self::b() } } + +pub trait Compress { + type G1Point: IsGroup; + type G2Point: IsGroup; + type Error; + + #[cfg(feature = "alloc")] + fn compress_g1_point(point: &Self::G1Point) -> alloc::vec::Vec; + + fn decompress_g1_point(input_bytes: &mut [u8; 32]) -> Result; + + fn decompress_g2_point(input_bytes: &mut [u8; 64]) -> Result; +} From e81273c5f46953212f5171a903965fff4392c735 Mon Sep 17 00:00:00 2001 From: jotabulacios Date: Wed, 4 Sep 2024 17:49:17 -0300 Subject: [PATCH 04/20] save work --- .../curves/bn_254/compression.rs | 178 ++++++++++++++++-- .../short_weierstrass/curves/bn_254/sqrt.rs | 2 +- .../short_weierstrass/traits.rs | 2 + 3 files changed, 169 insertions(+), 13 deletions(-) diff --git a/math/src/elliptic_curve/short_weierstrass/curves/bn_254/compression.rs b/math/src/elliptic_curve/short_weierstrass/curves/bn_254/compression.rs index 0165aa3b5..afda2f5c6 100644 --- a/math/src/elliptic_curve/short_weierstrass/curves/bn_254/compression.rs +++ b/math/src/elliptic_curve/short_weierstrass/curves/bn_254/compression.rs @@ -18,6 +18,7 @@ use crate::{ }; type G1Point = ShortWeierstrassProjectivePoint; +type G2Point = ShortWeierstrassProjectivePoint; type BN254FieldElement = FieldElement; // Bytes returns binary representation of p @@ -35,7 +36,7 @@ type BN254FieldElement = FieldElement; impl Compress for BN254Curve { type G1Point = G1Point; - type G2Point = ::PointRepresentation; + type G2Point = G2Point; type Error = ByteConversionError; @@ -92,8 +93,8 @@ impl Compress for BN254Curve { let (y_sqrt_1, y_sqrt_2) = &y_squared.sqrt().ok_or(ByteConversionError::InvalidValue)?; // we call "negative" to the greater root, - // if the frist two bits are 10, we take this grater value. - // if the first two bits are 11, we take the second one. + // if the frist two bits are 10, we take the smaller root. + // if the first two bits are 11, we take the grater . let y = match ( y_sqrt_1.representative().cmp(&y_sqrt_2.representative()), prefix_bits, @@ -111,6 +112,44 @@ impl Compress for BN254Curve { Ok(point) } + #[cfg(feature = "alloc")] + fn compress_g2_point(point: &Self::G2Point) -> alloc::vec::Vec { + if *point == G2Point::neutral_element() { + // point is at infinity + let mut x_bytes = alloc::vec![0_u8;64]; + x_bytes[0] |= 1 << 6; // x_bytes = 01000000 + x_bytes + } else { + // point is not at infinity + let point_affine = point.to_affine(); + let x = point_affine.x(); + let y = point_affine.y(); + + let mut x_bytes = x.to_bytes_be(); + + // Set first bit to to 1 indicate this is compressed element. + x_bytes[0] |= 1 << 7; // x_bytes = 10000000 + let [y0, y1] = y.value(); + + // We see if y_neg < y lexicographically where the lexicographic order is a + // Let a = a0 + a1 * u and b = b0 + b1 * u in Fp2, then a < b if a1 < b1 or + // a1 = b1 and a0 < b0. + if y1 == &BN254FieldElement::zero() { + let y0_neg = core::ops::Neg::neg(y0); + if y0_neg.representative() < y0.representative() { + x_bytes[0] |= 1 << 6; // x_bytes = 11000000 + } + } else { + let y1_neg = core::ops::Neg::neg(y1); + if y1_neg.representative() < y1.representative() { + x_bytes[0] |= 1 << 6; // x_bytes = 11000000 + } + } + + x_bytes + } + } + #[allow(unused)] fn decompress_g2_point(input_bytes: &mut [u8; 64]) -> Result { let first_byte = input_bytes.first().unwrap(); @@ -132,28 +171,42 @@ impl Compress for BN254Curve { let first_byte_without_control_bits = (first_byte << 2) >> 2; input_bytes[0] = first_byte_without_control_bits; - let input0 = &input_bytes[32..]; - let input1 = &input_bytes[0..32]; + let input0 = &input_bytes[0..32]; + let input1 = &input_bytes[32..]; let x0 = BN254FieldElement::from_bytes_be(input0).unwrap(); let x1 = BN254FieldElement::from_bytes_be(input1).unwrap(); let x: FieldElement = FieldElement::new([x0, x1]); let b_param_qfe = BN254TwistCurve::b(); - - let y = sqrt::sqrt_qfe(&(x.pow(3_u64) + b_param_qfe), 0) - .ok_or(ByteConversionError::InvalidValue)?; + let mut y = FieldElement::::one(); + // If the first two bits are 11, then the square root chosen is the greater one. + if prefix_bits == 3_u8 { + y = sqrt::sqrt_qfe(&(x.pow(3_u64) + b_param_qfe), 0) + .ok_or(ByteConversionError::InvalidValue)?; + // If the first two bits are 10, then the square root chosen is the smaller one. + } else { + y = sqrt::sqrt_qfe(&(x.pow(3_u64) + b_param_qfe), 1) + .ok_or(ByteConversionError::InvalidValue)?; + } Self::G2Point::from_affine(x, y).map_err(|_| ByteConversionError::InvalidValue) + + //TODO: Do we have to check that the point is in the subgroup? } } #[cfg(test)] mod tests { - use super::{BN254FieldElement, G1Point}; + use super::*; + use super::{BN254FieldElement, G1Point, G2Point}; use crate::elliptic_curve::short_weierstrass::curves::bn_254::curve::BN254Curve; + use crate::elliptic_curve::short_weierstrass::curves::bn_254::twist::BN254TwistCurve; use crate::elliptic_curve::short_weierstrass::traits::Compress; use crate::elliptic_curve::traits::{FromAffine, IsEllipticCurve}; + type FpE = BN254FieldElement; + type Fp2E = FieldElement; + #[cfg(feature = "alloc")] use crate::{ cyclic_group::IsGroup, traits::ByteConversion, unsigned_integer::element::UnsignedInteger, @@ -181,7 +234,7 @@ mod tests { let mut compressed_g = BN254Curve::compress_g1_point(&g); let first_byte = compressed_g.first().unwrap(); - let first_byte_without_control_bits = (first_byte << 3) >> 3; + let first_byte_without_control_bits = (first_byte << 2) >> 2; compressed_g[0] = first_byte_without_control_bits; let compressed_g_x = BN254FieldElement::from_bytes_be(&compressed_g).unwrap(); @@ -204,7 +257,7 @@ mod tests { #[cfg(feature = "alloc")] #[test] - fn test_compress_decompress_generator() { + fn test_g1_compress_decompress_generator() { use crate::elliptic_curve::short_weierstrass::traits::Compress; let g = BN254Curve::generator(); @@ -218,7 +271,7 @@ mod tests { #[cfg(feature = "alloc")] #[test] - fn test_compress_decompress_2g() { + fn test_g1_compress_decompress_two_times_generator() { let g = BN254Curve::generator(); // calculate g point operate with itself let g_2 = g.operate_with_self(UnsignedInteger::<4>::from("2")); @@ -230,4 +283,105 @@ mod tests { assert_eq!(g_2, decompressed_g2); } + + #[cfg(feature = "alloc")] + #[test] + fn test_g2_compress_generator() { + use crate::elliptic_curve::short_weierstrass::traits::Compress; + + let g = BN254TwistCurve::generator(); + let mut compressed_g = BN254Curve::compress_g2_point(&g); + let first_byte = compressed_g.first().unwrap(); + + let first_byte_without_control_bits = (first_byte << 2) >> 2; + compressed_g[0] = first_byte_without_control_bits; + + let compressed_g_x = + FieldElement::::from_bytes_be(&compressed_g).unwrap(); + let g_x = g.x(); + + assert_eq!(*g_x, compressed_g_x); + } + + #[cfg(feature = "alloc")] + #[test] + fn test_g2_compress_point_at_inf() { + use crate::elliptic_curve::short_weierstrass::traits::Compress; + + let inf = G2Point::neutral_element(); + let compressed_inf = BN254Curve::compress_g2_point(&inf); + let first_byte = compressed_inf.first().unwrap(); + + assert_eq!(*first_byte >> 6, 1_u8); + } + + #[cfg(feature = "alloc")] + #[test] + fn test_g2_compress_decompress_generator() { + use crate::elliptic_curve::short_weierstrass::traits::Compress; + + let g = BN254TwistCurve::generator(); + let compressed_g = BN254Curve::compress_g2_point(&g); + let mut compressed_g_slice: [u8; 64] = compressed_g.try_into().unwrap(); + + let decompressed_g = BN254Curve::decompress_g2_point(&mut compressed_g_slice).unwrap(); + + assert_eq!(g, decompressed_g); + } + + #[cfg(feature = "alloc")] + #[test] + fn test_compress_decompress_two_times_geneartor_of_g2() { + let g = BN254TwistCurve::generator(); + // calculate g point operate with itself + let g_2 = g.operate_with_self(UnsignedInteger::<4>::from("2")); + + let compressed_g2 = BN254Curve::compress_g2_point(&g_2); + let mut compressed_g2_slice: [u8; 64] = compressed_g2.try_into().unwrap(); + + let decompressed_g2 = BN254Curve::decompress_g2_point(&mut compressed_g2_slice).unwrap(); + + assert_eq!(g_2, decompressed_g2); + } + + #[cfg(feature = "alloc")] + #[test] + fn test_compress_decompress_3() { + use crate::unsigned_integer::element::U256; + + let g = G2Point::from_affine( + Fp2E::new([ + FpE::new(U256::from_hex_unchecked( + "1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed", + )), + FpE::new(U256::from_hex_unchecked( + "198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c2", + )), + ]), + Fp2E::new([ + FpE::new(U256::from_hex_unchecked( + "12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa", + )), + FpE::new(U256::from_hex_unchecked( + "090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b", + )), + ]), + ) + .unwrap(); + // calculate g point operate with itself + let g_2 = g.operate_with_self(UnsignedInteger::<4>::from("2")); + + let compressed_g2 = BN254Curve::compress_g2_point(&g_2); + let mut compressed_g2_slice: [u8; 64] = compressed_g2.try_into().unwrap(); + + let decompressed_g2 = BN254Curve::decompress_g2_point(&mut compressed_g2_slice).unwrap(); + + assert_eq!(g_2, decompressed_g2); + } + + #[test] + fn is_g2_generator_in_subgroup() { + let g = BN254TwistCurve::generator().operate_with_self(UnsignedInteger::<4>::from("2")); + assert!(g.is_in_subgroup()) + } } diff --git a/math/src/elliptic_curve/short_weierstrass/curves/bn_254/sqrt.rs b/math/src/elliptic_curve/short_weierstrass/curves/bn_254/sqrt.rs index 5298645a3..3f243bfd7 100644 --- a/math/src/elliptic_curve/short_weierstrass/curves/bn_254/sqrt.rs +++ b/math/src/elliptic_curve/short_weierstrass/curves/bn_254/sqrt.rs @@ -94,7 +94,7 @@ mod tests { // The equation of the twisted curve is y^2 = x^3 + 3 /(9+u) let y_square = x.pow(3_u64) + qfe_b; let y = super::sqrt_qfe(&y_square, 0).unwrap(); - + // Coordinate y of q. let y_expected = super::BN254TwistCurveFieldElement::new([ BN254FieldElement::from_hex_unchecked( diff --git a/math/src/elliptic_curve/short_weierstrass/traits.rs b/math/src/elliptic_curve/short_weierstrass/traits.rs index dc3acfcb2..24ef4858a 100644 --- a/math/src/elliptic_curve/short_weierstrass/traits.rs +++ b/math/src/elliptic_curve/short_weierstrass/traits.rs @@ -30,5 +30,7 @@ pub trait Compress { fn decompress_g1_point(input_bytes: &mut [u8; 32]) -> Result; + fn compress_g2_point(point: &Self::G2Point) -> alloc::vec::Vec; + fn decompress_g2_point(input_bytes: &mut [u8; 64]) -> Result; } From 90cc5e1e3c0f78e20552a346adb49ab3f2fdf696 Mon Sep 17 00:00:00 2001 From: Nicole Date: Wed, 4 Sep 2024 18:43:54 -0300 Subject: [PATCH 05/20] g2 decompress working with sqrt input 0 --- .../curves/bn_254/compression.rs | 48 ++++++++++++++++--- .../short_weierstrass/curves/bn_254/sqrt.rs | 24 ++++++++++ 2 files changed, 66 insertions(+), 6 deletions(-) diff --git a/math/src/elliptic_curve/short_weierstrass/curves/bn_254/compression.rs b/math/src/elliptic_curve/short_weierstrass/curves/bn_254/compression.rs index afda2f5c6..e9737e79b 100644 --- a/math/src/elliptic_curve/short_weierstrass/curves/bn_254/compression.rs +++ b/math/src/elliptic_curve/short_weierstrass/curves/bn_254/compression.rs @@ -179,13 +179,14 @@ impl Compress for BN254Curve { let b_param_qfe = BN254TwistCurve::b(); let mut y = FieldElement::::one(); + //TODO: Why do we have to use always 0? // If the first two bits are 11, then the square root chosen is the greater one. if prefix_bits == 3_u8 { y = sqrt::sqrt_qfe(&(x.pow(3_u64) + b_param_qfe), 0) .ok_or(ByteConversionError::InvalidValue)?; // If the first two bits are 10, then the square root chosen is the smaller one. } else { - y = sqrt::sqrt_qfe(&(x.pow(3_u64) + b_param_qfe), 1) + y = sqrt::sqrt_qfe(&(x.pow(3_u64) + b_param_qfe), 0) .ok_or(ByteConversionError::InvalidValue)?; } @@ -257,7 +258,7 @@ mod tests { #[cfg(feature = "alloc")] #[test] - fn test_g1_compress_decompress_generator() { + fn g1_compress_decompress_is_identity() { use crate::elliptic_curve::short_weierstrass::traits::Compress; let g = BN254Curve::generator(); @@ -271,7 +272,7 @@ mod tests { #[cfg(feature = "alloc")] #[test] - fn test_g1_compress_decompress_two_times_generator() { + fn g1_compress_decompress_is_identity_2() { let g = BN254Curve::generator(); // calculate g point operate with itself let g_2 = g.operate_with_self(UnsignedInteger::<4>::from("2")); @@ -317,7 +318,7 @@ mod tests { #[cfg(feature = "alloc")] #[test] - fn test_g2_compress_decompress_generator() { + fn g2_compress_decompress_is_identity() { use crate::elliptic_curve::short_weierstrass::traits::Compress; let g = BN254TwistCurve::generator(); @@ -331,7 +332,7 @@ mod tests { #[cfg(feature = "alloc")] #[test] - fn test_compress_decompress_two_times_geneartor_of_g2() { + fn g2_compress_decompress_is_identity_2() { let g = BN254TwistCurve::generator(); // calculate g point operate with itself let g_2 = g.operate_with_self(UnsignedInteger::<4>::from("2")); @@ -346,7 +347,7 @@ mod tests { #[cfg(feature = "alloc")] #[test] - fn test_compress_decompress_3() { + fn g2_compress_decompress_is_identity_3() { use crate::unsigned_integer::element::U256; let g = G2Point::from_affine( @@ -379,6 +380,41 @@ mod tests { assert_eq!(g_2, decompressed_g2); } + #[cfg(feature = "alloc")] + #[test] + fn g2_compress_decompress_is_identity_4() { + use crate::unsigned_integer::element::U256; + + let g = G2Point::from_affine( + Fp2E::new([ + FpE::new(U256::from_hex_unchecked( + "3010c68cb50161b7d1d96bb71edfec9880171954e56871abf3d93cc94d745fa1", + )), + FpE::new(U256::from_hex_unchecked( + "0476be093a6d2b4bbf907172049874af11e1b6267606e00804d3ff0037ec57fd", + )), + ]), + Fp2E::new([ + FpE::new(U256::from_hex_unchecked( + "01b33461f39d9e887dbb100f170a2345dde3c07e256d1dfa2b657ba5cd030427", + )), + FpE::new(U256::from_hex_unchecked( + "14c059d74e5b6c4ec14ae5864ebe23a71781d86c29fb8fb6cce94f70d3de7a21", + )), + ]), + ) + .unwrap(); + // calculate g point operate with itself + let g_2 = g.operate_with_self(UnsignedInteger::<4>::from("2")); + + let compressed_g2 = BN254Curve::compress_g2_point(&g_2); + let mut compressed_g2_slice: [u8; 64] = compressed_g2.try_into().unwrap(); + + let decompressed_g2 = BN254Curve::decompress_g2_point(&mut compressed_g2_slice).unwrap(); + + assert_eq!(g_2, decompressed_g2); + } + #[test] fn is_g2_generator_in_subgroup() { let g = BN254TwistCurve::generator().operate_with_self(UnsignedInteger::<4>::from("2")); diff --git a/math/src/elliptic_curve/short_weierstrass/curves/bn_254/sqrt.rs b/math/src/elliptic_curve/short_weierstrass/curves/bn_254/sqrt.rs index 3f243bfd7..e614fe162 100644 --- a/math/src/elliptic_curve/short_weierstrass/curves/bn_254/sqrt.rs +++ b/math/src/elliptic_curve/short_weierstrass/curves/bn_254/sqrt.rs @@ -74,7 +74,9 @@ pub fn sqrt_qfe( mod tests { use super::super::curve::BN254FieldElement; use super::super::twist::BN254TwistCurve; + use crate::cyclic_group::IsGroup; use crate::elliptic_curve::short_weierstrass::traits::IsShortWeierstrass; + use crate::elliptic_curve::traits::IsEllipticCurve; #[test] /// We took the q1 point of the test two_pairs_of_points_match_1 from pairing.rs @@ -145,6 +147,28 @@ mod tests { assert_eq!(value_y[1].clone(), value_y_expected[1].clone()); } + #[test] + fn test_sqrt_qfe_3() { + let g = BN254TwistCurve::generator().to_affine(); + let y = &g.coordinates()[1]; + let y_square = &y.square(); + let y_result = super::sqrt_qfe(&y_square, 0).unwrap(); + + assert_eq!(y_result, y.clone()); + } + + #[test] + fn test_sqrt_qfe_4() { + let g = BN254TwistCurve::generator() + .operate_with_self(2 as u16) + .to_affine(); + let y = &g.coordinates()[1]; + let y_square = &y.square(); + let y_result = super::sqrt_qfe(&y_square, 0).unwrap(); + + assert_eq!(y_result, y.clone()); + } + /* #[test] fn test_sqrt_qfe_2() { From 3818f2b529b66a23e4de0baa228b1a6c5faaccba Mon Sep 17 00:00:00 2001 From: Nicole Date: Thu, 5 Sep 2024 10:43:29 -0300 Subject: [PATCH 06/20] add random sqrt test --- .../short_weierstrass/curves/bn_254/sqrt.rs | 51 +++++++++---------- 1 file changed, 23 insertions(+), 28 deletions(-) diff --git a/math/src/elliptic_curve/short_weierstrass/curves/bn_254/sqrt.rs b/math/src/elliptic_curve/short_weierstrass/curves/bn_254/sqrt.rs index e614fe162..00e79774d 100644 --- a/math/src/elliptic_curve/short_weierstrass/curves/bn_254/sqrt.rs +++ b/math/src/elliptic_curve/short_weierstrass/curves/bn_254/sqrt.rs @@ -72,11 +72,12 @@ pub fn sqrt_qfe( #[cfg(test)] mod tests { - use super::super::curve::BN254FieldElement; + use super::super::curve::{BN254FieldElement, BN254TwistCurveFieldElement}; use super::super::twist::BN254TwistCurve; use crate::cyclic_group::IsGroup; use crate::elliptic_curve::short_weierstrass::traits::IsShortWeierstrass; use crate::elliptic_curve::traits::IsEllipticCurve; + use rand::{rngs::StdRng, Rng, SeedableRng}; #[test] /// We took the q1 point of the test two_pairs_of_points_match_1 from pairing.rs @@ -169,35 +170,29 @@ mod tests { assert_eq!(y_result, y.clone()); } - /* #[test] - fn test_sqrt_qfe_2() { - let c0 = BN254FieldElement::from_hex_unchecked( - "3010c68cb50161b7d1d96bb71edfec9880171954e56871abf3d93cc94d745fa1", - ); - let c1 = BN254FieldElement::from_hex_unchecked( - "0476be093a6d2b4bbf907172049874af11e1b6267606e00804d3ff0037ec57fd", - ); - let qfe = super::BN254TwistCurveFieldElement::new([c0, c1]); - - let qfe_b = BN254TwistCurve::b(); - - let cubic_value = qfe.pow(3_u64) + qfe_b; - let root = super::sqrt_qfe(&cubic_value, 0).unwrap(); - - let c0_expected = BN254FieldElement::from_hex_unchecked( - "01b33461f39d9e887dbb100f170a2345dde3c07e256d1dfa2b657ba5cd030427", - ); - let c1_expected = BN254FieldElement::from_hex_unchecked( - "14c059d74e5b6c4ec14ae5864ebe23a71781d86c29fb8fb6cce94f70d3de7a21", - ); - let qfe_expected = super::BN254TwistCurveFieldElement::new([c0_expected, c1_expected]); + fn test_sqrt_qfe_5() { + let a = BN254TwistCurveFieldElement::new([ + BN254FieldElement::from(3), + BN254FieldElement::from(4), + ]); + let a_square = a.square(); + let a_result = super::sqrt_qfe(&a_square, 0).unwrap(); - let value_root = root.value(); - let value_qfe_expected = qfe_expected.value(); + assert_eq!(a_result, a); + } + #[test] + fn test_sqrt_qfe_random() { + let mut rng = StdRng::seed_from_u64(42); + let a_val: u64 = rng.gen(); + let b_val: u64 = rng.gen(); + let a = BN254TwistCurveFieldElement::new([ + BN254FieldElement::from(a_val), + BN254FieldElement::from(b_val), + ]); + let a_square = a.square(); + let a_result = super::sqrt_qfe(&a_square, 0).unwrap(); - assert_eq!(value_root[0].clone(), value_qfe_expected[0].clone()); - assert_eq!(value_root[1].clone(), value_qfe_expected[1].clone()); + assert_eq!(a_result, a); } - */ } From 59e2d1ae52f4c1e95e8df2546b99e6e2d8dc0361 Mon Sep 17 00:00:00 2001 From: Nicole Date: Thu, 5 Sep 2024 11:44:41 -0300 Subject: [PATCH 07/20] change trait and add documentation --- .../curves/bn_254/compression.rs | 126 +++++++----------- .../short_weierstrass/curves/bn_254/sqrt.rs | 1 + .../short_weierstrass/traits.rs | 4 +- 3 files changed, 54 insertions(+), 77 deletions(-) diff --git a/math/src/elliptic_curve/short_weierstrass/curves/bn_254/compression.rs b/math/src/elliptic_curve/short_weierstrass/curves/bn_254/compression.rs index e9737e79b..709c61c67 100644 --- a/math/src/elliptic_curve/short_weierstrass/curves/bn_254/compression.rs +++ b/math/src/elliptic_curve/short_weierstrass/curves/bn_254/compression.rs @@ -1,12 +1,9 @@ use super::{field_extension::BN254PrimeField, twist::BN254TwistCurve}; use crate::{ - elliptic_curve::{ - short_weierstrass::{ - curves::bn_254::{curve::BN254Curve, field_extension::Degree2ExtensionField, sqrt}, - point::ShortWeierstrassProjectivePoint, - traits::{Compress, IsShortWeierstrass}, - }, - traits::IsEllipticCurve, + elliptic_curve::short_weierstrass::{ + curves::bn_254::{curve::BN254Curve, field_extension::Degree2ExtensionField, sqrt}, + point::ShortWeierstrassProjectivePoint, + traits::{Compress, IsShortWeierstrass}, }, field::element::FieldElement, }; @@ -21,17 +18,16 @@ type G1Point = ShortWeierstrassProjectivePoint; type G2Point = ShortWeierstrassProjectivePoint; type BN254FieldElement = FieldElement; -// Bytes returns binary representation of p -// will store X coordinate in regular form and a parity bit -// as we have less than 3 bits available in our coordinate, we can't follow BLS12-381 style encoding (ZCash/IETF) -// -// we use the 2 most significant bits instead -// -// 00 -> uncompressed -// 10 -> compressed and y_neg >= y -// 11 -> compressed and y_neg < y -// 01 -> compressed infinity point -// the "uncompressed infinity point" will just have 00 (uncompressed) followed by zeroes (infinity = 0,0 in affine coordinates) +/// As we have less than 3 bits available in our coordinate x, we can't follow BLS12-381 style encoding. +/// +/// We use the 2 most significant bits instead +/// +/// 00 -> uncompressed +/// 10 -> compressed and y_neg >= y +/// 11 -> compressed and y_neg < y +/// 01 -> compressed infinity point +/// the "uncompressed infinity point" will just have 00 (uncompressed) followed by zeroes (infinity = 0,0 in affine coordinates). +/// adapted from gnark https://github.com/consensys/gnark-crypto/blob/v0.13.0/ecc/bn254/marshal.go impl Compress for BN254Curve { type G1Point = G1Point; @@ -43,19 +39,19 @@ impl Compress for BN254Curve { #[cfg(feature = "alloc")] fn compress_g1_point(point: &Self::G1Point) -> alloc::vec::Vec { if *point == G1Point::neutral_element() { - // point is at infinity + // Point is at infinity let mut x_bytes = alloc::vec![0_u8; 32]; x_bytes[0] |= 1 << 6; // x_bytes = 01000000 x_bytes } else { - // point is not at infinity + // Point is not at infinity let point_affine = point.to_affine(); let x = point_affine.x(); let y = point_affine.y(); let mut x_bytes = x.to_bytes_be(); - // Set first bit to to 1 indicate this is compressed element. + // Set first bit to 1 to indicate this is a compressed element. x_bytes[0] |= 1 << 7; // x_bytes = 10000000 let y_neg = core::ops::Neg::neg(y); @@ -66,17 +62,17 @@ impl Compress for BN254Curve { } } - fn decompress_g1_point(input_bytes: &mut [u8; 32]) -> Result { + fn decompress_g1_point(input_bytes: &mut [u8]) -> Result { let first_byte = input_bytes.first().unwrap(); // We get the 2 most significant bits let prefix_bits = first_byte >> 6; - // If first byte is 00000000, then the value is not compressed. + // If first two bits are 00, then the value is not compressed. if prefix_bits == 0_u8 { return Err(ByteConversionError::ValueNotCompressed); } - // If first byte is 01000000, then the compressed point is the + // If first two bits are 01, then the compressed point is the // point at infinity and we return it directly. if prefix_bits == 1_u8 { return Ok(G1Point::neutral_element()); @@ -92,9 +88,8 @@ impl Compress for BN254Curve { let (y_sqrt_1, y_sqrt_2) = &y_squared.sqrt().ok_or(ByteConversionError::InvalidValue)?; - // we call "negative" to the greater root, - // if the frist two bits are 10, we take the smaller root. - // if the first two bits are 11, we take the grater . + // If the frist two bits are 10, we take the smaller root. + // If the first two bits are 11, we take the grater one. let y = match ( y_sqrt_1.representative().cmp(&y_sqrt_2.representative()), prefix_bits, @@ -115,12 +110,12 @@ impl Compress for BN254Curve { #[cfg(feature = "alloc")] fn compress_g2_point(point: &Self::G2Point) -> alloc::vec::Vec { if *point == G2Point::neutral_element() { - // point is at infinity + // Point is at infinity let mut x_bytes = alloc::vec![0_u8;64]; x_bytes[0] |= 1 << 6; // x_bytes = 01000000 x_bytes } else { - // point is not at infinity + // Point is not at infinity let point_affine = point.to_affine(); let x = point_affine.x(); let y = point_affine.y(); @@ -128,21 +123,22 @@ impl Compress for BN254Curve { let mut x_bytes = x.to_bytes_be(); // Set first bit to to 1 indicate this is compressed element. - x_bytes[0] |= 1 << 7; // x_bytes = 10000000 + x_bytes[0] |= 1 << 7; let [y0, y1] = y.value(); - // We see if y_neg < y lexicographically where the lexicographic order is a + // We see if y_neg < y lexicographically where the lexicographic order is as follows: // Let a = a0 + a1 * u and b = b0 + b1 * u in Fp2, then a < b if a1 < b1 or // a1 = b1 and a0 < b0. + // TODO: We won't use this prefix in decompress_g2_point. Why? if y1 == &BN254FieldElement::zero() { let y0_neg = core::ops::Neg::neg(y0); if y0_neg.representative() < y0.representative() { - x_bytes[0] |= 1 << 6; // x_bytes = 11000000 + x_bytes[0] |= 1 << 6; // Prefix: 11 } } else { let y1_neg = core::ops::Neg::neg(y1); if y1_neg.representative() < y1.representative() { - x_bytes[0] |= 1 << 6; // x_bytes = 11000000 + x_bytes[0] |= 1 << 6; // PRefix: 11 } } @@ -151,10 +147,10 @@ impl Compress for BN254Curve { } #[allow(unused)] - fn decompress_g2_point(input_bytes: &mut [u8; 64]) -> Result { + fn decompress_g2_point(input_bytes: &mut [u8]) -> Result { let first_byte = input_bytes.first().unwrap(); - // We get the first 2 bits + // We get the first 2 bits. let prefix_bits = first_byte >> 6; // If first two bits are 00, then the value is not compressed. @@ -179,12 +175,17 @@ impl Compress for BN254Curve { let b_param_qfe = BN254TwistCurve::b(); let mut y = FieldElement::::one(); - //TODO: Why do we have to use always 0? + + //TODO: Why do we always have to set sqrt_qfe input to 0 for tests to pass? + // If the first two bits are 11, then the square root chosen is the greater one. + // So we should use sqrt_qfe with the input 1. if prefix_bits == 3_u8 { y = sqrt::sqrt_qfe(&(x.pow(3_u64) + b_param_qfe), 0) .ok_or(ByteConversionError::InvalidValue)?; + // If the first two bits are 10, then the square root chosen is the smaller one. + // So we should use sqrt_qfe with the input 0. } else { y = sqrt::sqrt_qfe(&(x.pow(3_u64) + b_param_qfe), 0) .ok_or(ByteConversionError::InvalidValue)?; @@ -212,20 +213,7 @@ mod tests { use crate::{ cyclic_group::IsGroup, traits::ByteConversion, unsigned_integer::element::UnsignedInteger, }; - /* - #[test] - fn test_zero_point() { - let g1 = BN254Curve::generator(); - assert!(g1.is_in_subgroup()); - let new_x = BN254FieldElement::zero(); - let new_y = BN254FieldElement::one() + BN254FieldElement::one(); - - let false_point2 = G1Point::from_affine(new_x, new_y).unwrap(); - - assert!(!false_point2.is_in_subgroup()); - } - */ #[cfg(feature = "alloc")] #[test] fn test_g1_compress_generator() { @@ -273,16 +261,14 @@ mod tests { #[cfg(feature = "alloc")] #[test] fn g1_compress_decompress_is_identity_2() { - let g = BN254Curve::generator(); - // calculate g point operate with itself - let g_2 = g.operate_with_self(UnsignedInteger::<4>::from("2")); + let g = BN254Curve::generator().operate_with_self(UnsignedInteger::<4>::from("2")); - let compressed_g2 = BN254Curve::compress_g1_point(&g_2); - let mut compressed_g2_slice: [u8; 32] = compressed_g2.try_into().unwrap(); + let compressed_g = BN254Curve::compress_g1_point(&g); + let mut compressed_g_slice: [u8; 32] = compressed_g.try_into().unwrap(); - let decompressed_g2 = BN254Curve::decompress_g1_point(&mut compressed_g2_slice).unwrap(); + let decompressed_g = BN254Curve::decompress_g1_point(&mut compressed_g_slice).unwrap(); - assert_eq!(g_2, decompressed_g2); + assert_eq!(g, decompressed_g); } #[cfg(feature = "alloc")] @@ -333,16 +319,14 @@ mod tests { #[cfg(feature = "alloc")] #[test] fn g2_compress_decompress_is_identity_2() { - let g = BN254TwistCurve::generator(); - // calculate g point operate with itself - let g_2 = g.operate_with_self(UnsignedInteger::<4>::from("2")); + let g = BN254TwistCurve::generator().operate_with_self(UnsignedInteger::<4>::from("2")); - let compressed_g2 = BN254Curve::compress_g2_point(&g_2); - let mut compressed_g2_slice: [u8; 64] = compressed_g2.try_into().unwrap(); + let compressed_g = BN254Curve::compress_g2_point(&g); + let mut compressed_g_slice: [u8; 64] = compressed_g.try_into().unwrap(); - let decompressed_g2 = BN254Curve::decompress_g2_point(&mut compressed_g2_slice).unwrap(); + let decompressed_g = BN254Curve::decompress_g2_point(&mut compressed_g_slice).unwrap(); - assert_eq!(g_2, decompressed_g2); + assert_eq!(g, decompressed_g); } #[cfg(feature = "alloc")] @@ -369,15 +353,13 @@ mod tests { ]), ) .unwrap(); - // calculate g point operate with itself - let g_2 = g.operate_with_self(UnsignedInteger::<4>::from("2")); - let compressed_g2 = BN254Curve::compress_g2_point(&g_2); - let mut compressed_g2_slice: [u8; 64] = compressed_g2.try_into().unwrap(); + let compressed_g = BN254Curve::compress_g2_point(&g); + let mut compressed_g_slice: [u8; 64] = compressed_g.try_into().unwrap(); - let decompressed_g2 = BN254Curve::decompress_g2_point(&mut compressed_g2_slice).unwrap(); + let decompressed_g = BN254Curve::decompress_g2_point(&mut compressed_g_slice).unwrap(); - assert_eq!(g_2, decompressed_g2); + assert_eq!(g, decompressed_g); } #[cfg(feature = "alloc")] @@ -414,10 +396,4 @@ mod tests { assert_eq!(g_2, decompressed_g2); } - - #[test] - fn is_g2_generator_in_subgroup() { - let g = BN254TwistCurve::generator().operate_with_self(UnsignedInteger::<4>::from("2")); - assert!(g.is_in_subgroup()) - } } diff --git a/math/src/elliptic_curve/short_weierstrass/curves/bn_254/sqrt.rs b/math/src/elliptic_curve/short_weierstrass/curves/bn_254/sqrt.rs index 00e79774d..51b9e5fde 100644 --- a/math/src/elliptic_curve/short_weierstrass/curves/bn_254/sqrt.rs +++ b/math/src/elliptic_curve/short_weierstrass/curves/bn_254/sqrt.rs @@ -181,6 +181,7 @@ mod tests { assert_eq!(a_result, a); } + #[test] fn test_sqrt_qfe_random() { let mut rng = StdRng::seed_from_u64(42); diff --git a/math/src/elliptic_curve/short_weierstrass/traits.rs b/math/src/elliptic_curve/short_weierstrass/traits.rs index 24ef4858a..9d6d3a46a 100644 --- a/math/src/elliptic_curve/short_weierstrass/traits.rs +++ b/math/src/elliptic_curve/short_weierstrass/traits.rs @@ -28,9 +28,9 @@ pub trait Compress { #[cfg(feature = "alloc")] fn compress_g1_point(point: &Self::G1Point) -> alloc::vec::Vec; - fn decompress_g1_point(input_bytes: &mut [u8; 32]) -> Result; + fn decompress_g1_point(input_bytes: &mut [u8]) -> Result; fn compress_g2_point(point: &Self::G2Point) -> alloc::vec::Vec; - fn decompress_g2_point(input_bytes: &mut [u8; 64]) -> Result; + fn decompress_g2_point(input_bytes: &mut [u8]) -> Result; } From 48afdc690d03051f3c1dd3275d45b9f3cd05d861 Mon Sep 17 00:00:00 2001 From: Nicole Date: Thu, 5 Sep 2024 12:03:41 -0300 Subject: [PATCH 08/20] fix bls compression and decompression --- .../short_weierstrass/curves/bls12_381/compression.rs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/math/src/elliptic_curve/short_weierstrass/curves/bls12_381/compression.rs b/math/src/elliptic_curve/short_weierstrass/curves/bls12_381/compression.rs index a7fec81e0..6fbeb76b6 100644 --- a/math/src/elliptic_curve/short_weierstrass/curves/bls12_381/compression.rs +++ b/math/src/elliptic_curve/short_weierstrass/curves/bls12_381/compression.rs @@ -56,7 +56,7 @@ impl Compress for BLS12381Curve { } } - fn decompress_g1_point(input_bytes: &mut [u8; 48]) -> Result { + fn decompress_g1_point(input_bytes: &mut [u8]) -> Result { let first_byte = input_bytes.first().unwrap(); // We get the 3 most significant bits let prefix_bits = first_byte >> 5; @@ -107,7 +107,7 @@ impl Compress for BLS12381Curve { } #[allow(unused)] - fn decompress_g2_point(input_bytes: &mut [u8; 96]) -> Result { + fn decompress_g2_point(input_bytes: &mut [u8]) -> Result { let first_byte = input_bytes.first().unwrap(); // We get the first 3 bits @@ -141,6 +141,11 @@ impl Compress for BLS12381Curve { Self::G2Point::from_affine(x, y).map_err(|_| ByteConversionError::InvalidValue) } + + /// g2 point compression wasn't needed. + fn compress_g2_point(_: &Self::G2Point) -> alloc::vec::Vec { + todo!() + } } #[cfg(test)] From 3a6d679c5f7cf2849e3f4197dfbf8f053dd55e9c Mon Sep 17 00:00:00 2001 From: Nicole Date: Thu, 5 Sep 2024 12:44:53 -0300 Subject: [PATCH 09/20] fix cargo clippy and check --- .../short_weierstrass/curves/bls12_381/compression.rs | 1 + .../elliptic_curve/short_weierstrass/curves/bn_254/sqrt.rs | 6 +++--- math/src/elliptic_curve/short_weierstrass/traits.rs | 1 + 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/math/src/elliptic_curve/short_weierstrass/curves/bls12_381/compression.rs b/math/src/elliptic_curve/short_weierstrass/curves/bls12_381/compression.rs index 6fbeb76b6..47111a97c 100644 --- a/math/src/elliptic_curve/short_weierstrass/curves/bls12_381/compression.rs +++ b/math/src/elliptic_curve/short_weierstrass/curves/bls12_381/compression.rs @@ -143,6 +143,7 @@ impl Compress for BLS12381Curve { } /// g2 point compression wasn't needed. + #[cfg(feature = "alloc")] fn compress_g2_point(_: &Self::G2Point) -> alloc::vec::Vec { todo!() } diff --git a/math/src/elliptic_curve/short_weierstrass/curves/bn_254/sqrt.rs b/math/src/elliptic_curve/short_weierstrass/curves/bn_254/sqrt.rs index 51b9e5fde..6379f12cb 100644 --- a/math/src/elliptic_curve/short_weierstrass/curves/bn_254/sqrt.rs +++ b/math/src/elliptic_curve/short_weierstrass/curves/bn_254/sqrt.rs @@ -153,7 +153,7 @@ mod tests { let g = BN254TwistCurve::generator().to_affine(); let y = &g.coordinates()[1]; let y_square = &y.square(); - let y_result = super::sqrt_qfe(&y_square, 0).unwrap(); + let y_result = super::sqrt_qfe(y_square, 0).unwrap(); assert_eq!(y_result, y.clone()); } @@ -161,11 +161,11 @@ mod tests { #[test] fn test_sqrt_qfe_4() { let g = BN254TwistCurve::generator() - .operate_with_self(2 as u16) + .operate_with_self(2_u16) .to_affine(); let y = &g.coordinates()[1]; let y_square = &y.square(); - let y_result = super::sqrt_qfe(&y_square, 0).unwrap(); + let y_result = super::sqrt_qfe(y_square, 0).unwrap(); assert_eq!(y_result, y.clone()); } diff --git a/math/src/elliptic_curve/short_weierstrass/traits.rs b/math/src/elliptic_curve/short_weierstrass/traits.rs index 9d6d3a46a..7f623deab 100644 --- a/math/src/elliptic_curve/short_weierstrass/traits.rs +++ b/math/src/elliptic_curve/short_weierstrass/traits.rs @@ -30,6 +30,7 @@ pub trait Compress { fn decompress_g1_point(input_bytes: &mut [u8]) -> Result; + #[cfg(feature = "alloc")] fn compress_g2_point(point: &Self::G2Point) -> alloc::vec::Vec; fn decompress_g2_point(input_bytes: &mut [u8]) -> Result; From 73632ba7a2577f4c8bf513d36eacc8b12597dc74 Mon Sep 17 00:00:00 2001 From: Nicole Date: Thu, 5 Sep 2024 16:03:32 -0300 Subject: [PATCH 10/20] resolve conversations --- .../short_weierstrass/curves/bn_254/sqrt.rs | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/math/src/elliptic_curve/short_weierstrass/curves/bn_254/sqrt.rs b/math/src/elliptic_curve/short_weierstrass/curves/bn_254/sqrt.rs index 6379f12cb..e3b86d4bd 100644 --- a/math/src/elliptic_curve/short_weierstrass/curves/bn_254/sqrt.rs +++ b/math/src/elliptic_curve/short_weierstrass/curves/bn_254/sqrt.rs @@ -2,6 +2,10 @@ use super::curve::{BN254FieldElement, BN254TwistCurveFieldElement}; use crate::field::traits::LegendreSymbol; use core::cmp::Ordering; +pub const TWO_INV: BN254FieldElement = BN254FieldElement::from_hex_unchecked( + "183227397098D014DC2822DB40C0AC2ECBC0B548B438E5469E10460B6C3E7EA4", +); + #[must_use] pub fn select_sqrt_value_from_third_bit( sqrt_1: BN254FieldElement, @@ -42,23 +46,22 @@ pub fn sqrt_qfe( } else { // second part of the input field number is non-zero // instead of "sum" is: -beta - let alpha = a.pow(2u64) + b.pow(2u64); + let alpha = a.square() + b.square(); let gamma = alpha.legendre_symbol(); match gamma { LegendreSymbol::One => { let two = BN254FieldElement::from(2u64); - let two_inv = two.inv().unwrap(); // calculate the square root of alpha let (y_sqrt1, y_sqrt2) = alpha.sqrt()?; - let mut delta = (a.clone() + y_sqrt1) * two_inv.clone(); + let mut delta = (&a + y_sqrt1) * TWO_INV; let legendre_delta = delta.legendre_symbol(); if legendre_delta == LegendreSymbol::MinusOne { - delta = (a + y_sqrt2) * two_inv; + delta = (a + y_sqrt2) * TWO_INV; }; let (x_sqrt_1, x_sqrt_2) = delta.sqrt()?; let x_0 = select_sqrt_value_from_third_bit(x_sqrt_1, x_sqrt_2, third_bit); - let x_1 = b * (two * x_0.clone()).inv().unwrap(); + let x_1 = b * (two * &x_0).inv().unwrap(); Some(BN254TwistCurveFieldElement::new([x_0, x_1])) } LegendreSymbol::MinusOne => None, @@ -95,7 +98,7 @@ mod tests { let qfe_b = BN254TwistCurve::b(); // The equation of the twisted curve is y^2 = x^3 + 3 /(9+u) - let y_square = x.pow(3_u64) + qfe_b; + let y_square = x.square() * &x + qfe_b; let y = super::sqrt_qfe(&y_square, 0).unwrap(); // Coordinate y of q. From 7b11333bf2bf70d098dfd56d368a233120038a94 Mon Sep 17 00:00:00 2001 From: Nicole Date: Fri, 6 Sep 2024 11:35:38 -0300 Subject: [PATCH 11/20] decompression check bytes length --- .../curves/bls12_381/compression.rs | 18 +++++++++++++++ .../curves/bn_254/compression.rs | 23 +++++++++++++++++++ 2 files changed, 41 insertions(+) diff --git a/math/src/elliptic_curve/short_weierstrass/curves/bls12_381/compression.rs b/math/src/elliptic_curve/short_weierstrass/curves/bls12_381/compression.rs index 47111a97c..ea493355b 100644 --- a/math/src/elliptic_curve/short_weierstrass/curves/bls12_381/compression.rs +++ b/math/src/elliptic_curve/short_weierstrass/curves/bls12_381/compression.rs @@ -57,6 +57,9 @@ impl Compress for BLS12381Curve { } fn decompress_g1_point(input_bytes: &mut [u8]) -> Result { + if !input_bytes.len() == 48 { + return Err(ByteConversionError::InvalidValue); + } let first_byte = input_bytes.first().unwrap(); // We get the 3 most significant bits let prefix_bits = first_byte >> 5; @@ -108,6 +111,9 @@ impl Compress for BLS12381Curve { #[allow(unused)] fn decompress_g2_point(input_bytes: &mut [u8]) -> Result { + if !input_bytes.len() == 96 { + return Err(ByteConversionError::InvalidValue); + } let first_byte = input_bytes.first().unwrap(); // We get the first 3 bits @@ -232,4 +238,16 @@ mod tests { assert_eq!(g_2, decompressed_g2); } + #[test] + fn g1_decompress_wrong_length_bytes() { + let mut input_bytes: [u8; 48] = [0; 48]; + let result = BLS12381Curve::decompress_g1_point(&mut input_bytes); + assert!(result.is_err()); + } + #[test] + fn g2_decompress_wrong_length_bytes() { + let mut input_bytes: [u8; 95] = [0; 95]; + let result = BLS12381Curve::decompress_g2_point(&mut input_bytes); + assert!(result.is_err()); + } } diff --git a/math/src/elliptic_curve/short_weierstrass/curves/bn_254/compression.rs b/math/src/elliptic_curve/short_weierstrass/curves/bn_254/compression.rs index 709c61c67..a7195a863 100644 --- a/math/src/elliptic_curve/short_weierstrass/curves/bn_254/compression.rs +++ b/math/src/elliptic_curve/short_weierstrass/curves/bn_254/compression.rs @@ -63,6 +63,11 @@ impl Compress for BN254Curve { } fn decompress_g1_point(input_bytes: &mut [u8]) -> Result { + // We check that input_bytes has 32 bytes. + if !input_bytes.len() == 32 { + return Err(ByteConversionError::InvalidValue); + } + let first_byte = input_bytes.first().unwrap(); // We get the 2 most significant bits let prefix_bits = first_byte >> 6; @@ -148,6 +153,10 @@ impl Compress for BN254Curve { #[allow(unused)] fn decompress_g2_point(input_bytes: &mut [u8]) -> Result { + if !input_bytes.len() == 64 { + return Err(ByteConversionError::InvalidValue); + } + let first_byte = input_bytes.first().unwrap(); // We get the first 2 bits. @@ -396,4 +405,18 @@ mod tests { assert_eq!(g_2, decompressed_g2); } + + #[test] + fn g1_decompress_wrong_length_bytes() { + let mut input_bytes: [u8; 31] = [0; 31]; + let result = BN254Curve::decompress_g1_point(&mut input_bytes); + assert!(result.is_err()); + } + + #[test] + fn g2_decompress_wrong_length_bytes() { + let mut input_bytes: [u8; 65] = [0; 65]; + let result = BN254Curve::decompress_g2_point(&mut input_bytes); + assert!(result.is_err()); + } } From 56770acff17574929eb95a52ad4ec14421b9a421 Mon Sep 17 00:00:00 2001 From: Nicole Date: Fri, 6 Sep 2024 11:40:12 -0300 Subject: [PATCH 12/20] decompression checks correct bytes length --- .../short_weierstrass/curves/bls12_381/compression.rs | 4 ++-- .../short_weierstrass/curves/bn_254/compression.rs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/math/src/elliptic_curve/short_weierstrass/curves/bls12_381/compression.rs b/math/src/elliptic_curve/short_weierstrass/curves/bls12_381/compression.rs index ea493355b..1fd19198d 100644 --- a/math/src/elliptic_curve/short_weierstrass/curves/bls12_381/compression.rs +++ b/math/src/elliptic_curve/short_weierstrass/curves/bls12_381/compression.rs @@ -239,13 +239,13 @@ mod tests { assert_eq!(g_2, decompressed_g2); } #[test] - fn g1_decompress_wrong_length_bytes() { + fn g1_decompress_wrong_bytes_length() { let mut input_bytes: [u8; 48] = [0; 48]; let result = BLS12381Curve::decompress_g1_point(&mut input_bytes); assert!(result.is_err()); } #[test] - fn g2_decompress_wrong_length_bytes() { + fn g2_decompress_wrong_bytes_length() { let mut input_bytes: [u8; 95] = [0; 95]; let result = BLS12381Curve::decompress_g2_point(&mut input_bytes); assert!(result.is_err()); diff --git a/math/src/elliptic_curve/short_weierstrass/curves/bn_254/compression.rs b/math/src/elliptic_curve/short_weierstrass/curves/bn_254/compression.rs index a7195a863..51cb163a1 100644 --- a/math/src/elliptic_curve/short_weierstrass/curves/bn_254/compression.rs +++ b/math/src/elliptic_curve/short_weierstrass/curves/bn_254/compression.rs @@ -407,14 +407,14 @@ mod tests { } #[test] - fn g1_decompress_wrong_length_bytes() { + fn g1_decompress_wrong_bytes_length() { let mut input_bytes: [u8; 31] = [0; 31]; let result = BN254Curve::decompress_g1_point(&mut input_bytes); assert!(result.is_err()); } #[test] - fn g2_decompress_wrong_length_bytes() { + fn g2_decompress_wrong_bytes_length() { let mut input_bytes: [u8; 65] = [0; 65]; let result = BN254Curve::decompress_g2_point(&mut input_bytes); assert!(result.is_err()); From 7b0de79f3e2cedcb50fbfa3c7118f70309f7a6ac Mon Sep 17 00:00:00 2001 From: Nicole Date: Fri, 6 Sep 2024 11:55:31 -0300 Subject: [PATCH 13/20] fix clippy no default features --- .../curves/bn_254/field_extension.rs | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/math/src/elliptic_curve/short_weierstrass/curves/bn_254/field_extension.rs b/math/src/elliptic_curve/short_weierstrass/curves/bn_254/field_extension.rs index c287fdca0..ca318c03b 100644 --- a/math/src/elliptic_curve/short_weierstrass/curves/bn_254/field_extension.rs +++ b/math/src/elliptic_curve/short_weierstrass/curves/bn_254/field_extension.rs @@ -6,10 +6,10 @@ use crate::field::{ }, fields::montgomery_backed_prime_fields::{IsModulus, MontgomeryBackendPrimeField}, }; -use crate::unsigned_integer::element::U256; - -#[cfg(feature = "std")] +#[cfg(feature = "alloc")] use crate::traits::ByteConversion; +use crate::unsigned_integer::element::U256; +use core::marker; pub const BN254_PRIME_FIELD_ORDER: U256 = U256::from_hex_unchecked("30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47"); @@ -33,26 +33,26 @@ impl HasQuadraticNonResidue for BN254Residue { } } -#[cfg(feature = "std")] +#[cfg(feature = "alloc")] impl ByteConversion for FieldElement { - #[cfg(feature = "std")] - fn to_bytes_be(&self) -> Vec { + #[cfg(feature = "alloc")] + fn to_bytes_be(&self) -> alloc::vec::Vec { let mut byte_slice = ByteConversion::to_bytes_be(&self.value()[0]); byte_slice.extend(ByteConversion::to_bytes_be(&self.value()[1])); byte_slice } - #[cfg(feature = "std")] - fn to_bytes_le(&self) -> Vec { + #[cfg(feature = "alloc")] + fn to_bytes_le(&self) -> alloc::vec::Vec { let mut byte_slice = ByteConversion::to_bytes_le(&self.value()[0]); byte_slice.extend(ByteConversion::to_bytes_le(&self.value()[1])); byte_slice } - #[cfg(feature = "std")] + #[cfg(feature = "alloc")] fn from_bytes_be(bytes: &[u8]) -> Result where - Self: std::marker::Sized, + Self: marker::Sized, { const BYTES_PER_FIELD: usize = 32; let x0 = FieldElement::from_bytes_be(&bytes[0..BYTES_PER_FIELD])?; @@ -60,10 +60,10 @@ impl ByteConversion for FieldElement { Ok(Self::new([x0, x1])) } - #[cfg(feature = "std")] + #[cfg(feature = "alloc")] fn from_bytes_le(bytes: &[u8]) -> Result where - Self: std::marker::Sized, + Self: marker::Sized, { const BYTES_PER_FIELD: usize = 32; let x0 = FieldElement::from_bytes_le(&bytes[0..BYTES_PER_FIELD])?; From b8a6bdc56c185c712da023c74baf9f5fef8f00b7 Mon Sep 17 00:00:00 2001 From: Nicole Date: Fri, 6 Sep 2024 12:03:08 -0300 Subject: [PATCH 14/20] fix ubuntu cargo clippy --- .../short_weierstrass/curves/bn_254/compression.rs | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/math/src/elliptic_curve/short_weierstrass/curves/bn_254/compression.rs b/math/src/elliptic_curve/short_weierstrass/curves/bn_254/compression.rs index 51cb163a1..01bdbc89e 100644 --- a/math/src/elliptic_curve/short_weierstrass/curves/bn_254/compression.rs +++ b/math/src/elliptic_curve/short_weierstrass/curves/bn_254/compression.rs @@ -19,15 +19,13 @@ type G2Point = ShortWeierstrassProjectivePoint; type BN254FieldElement = FieldElement; /// As we have less than 3 bits available in our coordinate x, we can't follow BLS12-381 style encoding. -/// /// We use the 2 most significant bits instead -/// -/// 00 -> uncompressed -/// 10 -> compressed and y_neg >= y -/// 11 -> compressed and y_neg < y -/// 01 -> compressed infinity point -/// the "uncompressed infinity point" will just have 00 (uncompressed) followed by zeroes (infinity = 0,0 in affine coordinates). -/// adapted from gnark https://github.com/consensys/gnark-crypto/blob/v0.13.0/ecc/bn254/marshal.go +/// 00: uncompressed +/// 10: compressed and y_neg >= y +/// 11: compressed and y_neg < y +/// 01: compressed infinity point +/// the "uncompressed infinity point" will just have 00 (uncompressed) followed by zeroes (infinity = 0,0 in affine coordinates). +/// adapted from gnark https://github.com/consensys/gnark-crypto/blob/v0.13.0/ecc/bn254/marshal.go impl Compress for BN254Curve { type G1Point = G1Point; From 0b1d663b30ca14af30cecbf3f7c3f5525065ab94 Mon Sep 17 00:00:00 2001 From: Nicole Date: Fri, 6 Sep 2024 12:31:22 -0300 Subject: [PATCH 15/20] fix ubuntu clippy --- math/Cargo.toml | 15 +++++++++------ .../curves/bn_254/field_extension.rs | 5 ++--- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/math/Cargo.toml b/math/Cargo.toml index 1d0df36ef..9a6838a10 100644 --- a/math/Cargo.toml +++ b/math/Cargo.toml @@ -8,11 +8,15 @@ license.workspace = true # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -serde = { version = "1.0", default-features = false, features = ["derive"], optional = true } -serde_json = { version = "1.0", default-features = false, features = ["alloc"], optional = true } +serde = { version = "1.0", default-features = false, features = [ + "derive", +], optional = true } +serde_json = { version = "1.0", default-features = false, features = [ + "alloc", +], optional = true } proptest = { version = "1.1.0", optional = true } winter-math = { package = "winter-math", version = "0.6.4", default-features = false, optional = true } -miden-core = { package = "miden-core" , version = "0.7", default-features = false, optional = true } +miden-core = { package = "miden-core", version = "0.7", default-features = false, optional = true } # rayon rayon = { version = "1.7", optional = true } @@ -28,13 +32,13 @@ cudarc = { version = "0.9.7", optional = true } lambdaworks-gpu = { workspace = true, optional = true } [dev-dependencies] -rand = { version = "0.8.5", default-features = false } +rand = { version = "0.8.5", features = ["std"] } rand_chacha = "0.3.1" criterion = "0.5.1" const-random = "0.1.15" iai-callgrind.workspace = true proptest = "1.1.0" -pprof = { version = "0.13.0", features = ["criterion","flamegraph"] } +pprof = { version = "0.13.0", features = ["criterion", "flamegraph"] } [features] default = ["parallel", "std"] @@ -97,4 +101,3 @@ harness = false name = "criterion_metal" harness = false required-features = ["metal"] - diff --git a/math/src/elliptic_curve/short_weierstrass/curves/bn_254/field_extension.rs b/math/src/elliptic_curve/short_weierstrass/curves/bn_254/field_extension.rs index ca318c03b..6f3dcc219 100644 --- a/math/src/elliptic_curve/short_weierstrass/curves/bn_254/field_extension.rs +++ b/math/src/elliptic_curve/short_weierstrass/curves/bn_254/field_extension.rs @@ -9,7 +9,6 @@ use crate::field::{ #[cfg(feature = "alloc")] use crate::traits::ByteConversion; use crate::unsigned_integer::element::U256; -use core::marker; pub const BN254_PRIME_FIELD_ORDER: U256 = U256::from_hex_unchecked("30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47"); @@ -52,7 +51,7 @@ impl ByteConversion for FieldElement { #[cfg(feature = "alloc")] fn from_bytes_be(bytes: &[u8]) -> Result where - Self: marker::Sized, + Self: core::marker::Sized, { const BYTES_PER_FIELD: usize = 32; let x0 = FieldElement::from_bytes_be(&bytes[0..BYTES_PER_FIELD])?; @@ -63,7 +62,7 @@ impl ByteConversion for FieldElement { #[cfg(feature = "alloc")] fn from_bytes_le(bytes: &[u8]) -> Result where - Self: marker::Sized, + Self: core::marker::Sized, { const BYTES_PER_FIELD: usize = 32; let x0 = FieldElement::from_bytes_le(&bytes[0..BYTES_PER_FIELD])?; From 6d65eb2af7d733a76b5d21da6d84c81f6995230f Mon Sep 17 00:00:00 2001 From: diegokingston Date: Mon, 9 Sep 2024 18:56:43 -0300 Subject: [PATCH 16/20] fix errors --- .../curves/bls12_381/compression.rs | 6 ------ .../curves/bn_254/compression.rs | 21 ++++++++++++------- 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/math/src/elliptic_curve/short_weierstrass/curves/bls12_381/compression.rs b/math/src/elliptic_curve/short_weierstrass/curves/bls12_381/compression.rs index e03d7066f..a77614251 100644 --- a/math/src/elliptic_curve/short_weierstrass/curves/bls12_381/compression.rs +++ b/math/src/elliptic_curve/short_weierstrass/curves/bls12_381/compression.rs @@ -189,12 +189,6 @@ impl Compress for BLS12381Curve { Self::G2Point::from_affine(x, y).map_err(|_| ByteConversionError::InvalidValue) } - - /// g2 point compression wasn't needed. - #[cfg(feature = "alloc")] - fn compress_g2_point(_: &Self::G2Point) -> alloc::vec::Vec { - todo!() - } } #[cfg(test)] diff --git a/math/src/elliptic_curve/short_weierstrass/curves/bn_254/compression.rs b/math/src/elliptic_curve/short_weierstrass/curves/bn_254/compression.rs index 01bdbc89e..39ad96ba3 100644 --- a/math/src/elliptic_curve/short_weierstrass/curves/bn_254/compression.rs +++ b/math/src/elliptic_curve/short_weierstrass/curves/bn_254/compression.rs @@ -32,13 +32,17 @@ impl Compress for BN254Curve { type G2Point = G2Point; + type G1Compressed = [u8; 32]; + + type G2Compressed = [u8; 64]; + type Error = ByteConversionError; #[cfg(feature = "alloc")] - fn compress_g1_point(point: &Self::G1Point) -> alloc::vec::Vec { + fn compress_g1_point(point: &Self::G1Point) -> Self::G1Compressed { if *point == G1Point::neutral_element() { // Point is at infinity - let mut x_bytes = alloc::vec![0_u8; 32]; + let mut x_bytes = [0_u8; 32]; x_bytes[0] |= 1 << 6; // x_bytes = 01000000 x_bytes } else { @@ -47,8 +51,9 @@ impl Compress for BN254Curve { let x = point_affine.x(); let y = point_affine.y(); - let mut x_bytes = x.to_bytes_be(); - + let mut x_bytes = [0u8; 32]; + let bytes = x.to_bytes_be(); + x_bytes.copy_from_slice(&bytes); // Set first bit to 1 to indicate this is a compressed element. x_bytes[0] |= 1 << 7; // x_bytes = 10000000 @@ -111,10 +116,10 @@ impl Compress for BN254Curve { } #[cfg(feature = "alloc")] - fn compress_g2_point(point: &Self::G2Point) -> alloc::vec::Vec { + fn compress_g2_point(point: &Self::G2Point) -> Self::G2Compressed { if *point == G2Point::neutral_element() { // Point is at infinity - let mut x_bytes = alloc::vec![0_u8;64]; + let mut x_bytes = [0_u8;64]; x_bytes[0] |= 1 << 6; // x_bytes = 01000000 x_bytes } else { @@ -123,7 +128,9 @@ impl Compress for BN254Curve { let x = point_affine.x(); let y = point_affine.y(); - let mut x_bytes = x.to_bytes_be(); + let mut x_bytes = [0u8; 64]; + let bytes = x.to_bytes_be(); + x_bytes.copy_from_slice(&bytes); // Set first bit to to 1 indicate this is compressed element. x_bytes[0] |= 1 << 7; From 75e3d0436e7ae36dbdb5f417a35fcbe1331b444c Mon Sep 17 00:00:00 2001 From: diegokingston Date: Mon, 9 Sep 2024 19:01:02 -0300 Subject: [PATCH 17/20] fmt --- .../short_weierstrass/curves/bn_254/compression.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/math/src/elliptic_curve/short_weierstrass/curves/bn_254/compression.rs b/math/src/elliptic_curve/short_weierstrass/curves/bn_254/compression.rs index 39ad96ba3..abb3eff99 100644 --- a/math/src/elliptic_curve/short_weierstrass/curves/bn_254/compression.rs +++ b/math/src/elliptic_curve/short_weierstrass/curves/bn_254/compression.rs @@ -119,7 +119,7 @@ impl Compress for BN254Curve { fn compress_g2_point(point: &Self::G2Point) -> Self::G2Compressed { if *point == G2Point::neutral_element() { // Point is at infinity - let mut x_bytes = [0_u8;64]; + let mut x_bytes = [0_u8; 64]; x_bytes[0] |= 1 << 6; // x_bytes = 01000000 x_bytes } else { From fd1e55c2214d04dd30dd52504752c93bb801bb14 Mon Sep 17 00:00:00 2001 From: diegokingston Date: Mon, 9 Sep 2024 19:07:03 -0300 Subject: [PATCH 18/20] fix clippy --- .../curves/bn_254/compression.rs | 20 ++++++++----------- 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/math/src/elliptic_curve/short_weierstrass/curves/bn_254/compression.rs b/math/src/elliptic_curve/short_weierstrass/curves/bn_254/compression.rs index abb3eff99..b0d7618b3 100644 --- a/math/src/elliptic_curve/short_weierstrass/curves/bn_254/compression.rs +++ b/math/src/elliptic_curve/short_weierstrass/curves/bn_254/compression.rs @@ -264,8 +264,8 @@ mod tests { use crate::elliptic_curve::short_weierstrass::traits::Compress; let g = BN254Curve::generator(); - let compressed_g = BN254Curve::compress_g1_point(&g); - let mut compressed_g_slice: [u8; 32] = compressed_g.try_into().unwrap(); + + let mut compressed_g_slice: [u8; 32] = BN254Curve::compress_g1_point(&g); let decompressed_g = BN254Curve::decompress_g1_point(&mut compressed_g_slice).unwrap(); @@ -277,8 +277,7 @@ mod tests { fn g1_compress_decompress_is_identity_2() { let g = BN254Curve::generator().operate_with_self(UnsignedInteger::<4>::from("2")); - let compressed_g = BN254Curve::compress_g1_point(&g); - let mut compressed_g_slice: [u8; 32] = compressed_g.try_into().unwrap(); + let mut compressed_g_slice: [u8; 32] = BN254Curve::compress_g1_point(&g); let decompressed_g = BN254Curve::decompress_g1_point(&mut compressed_g_slice).unwrap(); @@ -322,8 +321,8 @@ mod tests { use crate::elliptic_curve::short_weierstrass::traits::Compress; let g = BN254TwistCurve::generator(); - let compressed_g = BN254Curve::compress_g2_point(&g); - let mut compressed_g_slice: [u8; 64] = compressed_g.try_into().unwrap(); + + let mut compressed_g_slice: [u8; 64] = BN254Curve::compress_g2_point(&g); let decompressed_g = BN254Curve::decompress_g2_point(&mut compressed_g_slice).unwrap(); @@ -335,8 +334,7 @@ mod tests { fn g2_compress_decompress_is_identity_2() { let g = BN254TwistCurve::generator().operate_with_self(UnsignedInteger::<4>::from("2")); - let compressed_g = BN254Curve::compress_g2_point(&g); - let mut compressed_g_slice: [u8; 64] = compressed_g.try_into().unwrap(); + let mut compressed_g_slice: [u8; 64] = BN254Curve::compress_g2_point(&g); let decompressed_g = BN254Curve::decompress_g2_point(&mut compressed_g_slice).unwrap(); @@ -368,8 +366,7 @@ mod tests { ) .unwrap(); - let compressed_g = BN254Curve::compress_g2_point(&g); - let mut compressed_g_slice: [u8; 64] = compressed_g.try_into().unwrap(); + let mut compressed_g_slice: [u8; 64] = BN254Curve::compress_g2_point(&g); let decompressed_g = BN254Curve::decompress_g2_point(&mut compressed_g_slice).unwrap(); @@ -403,8 +400,7 @@ mod tests { // calculate g point operate with itself let g_2 = g.operate_with_self(UnsignedInteger::<4>::from("2")); - let compressed_g2 = BN254Curve::compress_g2_point(&g_2); - let mut compressed_g2_slice: [u8; 64] = compressed_g2.try_into().unwrap(); + let mut compressed_g2_slice: [u8; 64] = BN254Curve::compress_g2_point(&g_2); let decompressed_g2 = BN254Curve::decompress_g2_point(&mut compressed_g2_slice).unwrap(); From 1453e8efa58277074434f1d85f7784a5594f185a Mon Sep 17 00:00:00 2001 From: jotabulacios Date: Tue, 10 Sep 2024 12:06:54 -0300 Subject: [PATCH 19/20] change compress g2 function --- .../curves/bn_254/compression.rs | 59 ++++++++----------- 1 file changed, 26 insertions(+), 33 deletions(-) diff --git a/math/src/elliptic_curve/short_weierstrass/curves/bn_254/compression.rs b/math/src/elliptic_curve/short_weierstrass/curves/bn_254/compression.rs index b0d7618b3..2daa32534 100644 --- a/math/src/elliptic_curve/short_weierstrass/curves/bn_254/compression.rs +++ b/math/src/elliptic_curve/short_weierstrass/curves/bn_254/compression.rs @@ -128,30 +128,32 @@ impl Compress for BN254Curve { let x = point_affine.x(); let y = point_affine.y(); + let x_rev: FieldElement = + FieldElement::new([x.value()[1].clone(), x.value()[0].clone()]); let mut x_bytes = [0u8; 64]; - let bytes = x.to_bytes_be(); + let bytes = x_rev.to_bytes_be(); x_bytes.copy_from_slice(&bytes); // Set first bit to to 1 indicate this is compressed element. x_bytes[0] |= 1 << 7; - let [y0, y1] = y.value(); // We see if y_neg < y lexicographically where the lexicographic order is as follows: - // Let a = a0 + a1 * u and b = b0 + b1 * u in Fp2, then a < b if a1 < b1 or - // a1 = b1 and a0 < b0. - // TODO: We won't use this prefix in decompress_g2_point. Why? - if y1 == &BN254FieldElement::zero() { - let y0_neg = core::ops::Neg::neg(y0); - if y0_neg.representative() < y0.representative() { + // Let a = a0 + a1 * u and b = b0 + b1 * u in Fp2, then a < b if a0 < b0 or + // a0 = b0 and a1 < b1. + let y_neg = -y; + match ( + y.value()[0] + .representative() + .cmp(&y_neg.value()[0].representative()), + y.value()[1] + .representative() + .cmp(&y_neg.value()[1].representative()), + ) { + (Ordering::Greater, _) | (Ordering::Equal, Ordering::Greater) => { x_bytes[0] |= 1 << 6; // Prefix: 11 } - } else { - let y1_neg = core::ops::Neg::neg(y1); - if y1_neg.representative() < y1.representative() { - x_bytes[0] |= 1 << 6; // PRefix: 11 - } + (_, _) => (), } - x_bytes } } @@ -178,36 +180,24 @@ impl Compress for BN254Curve { return Ok(Self::G2Point::neutral_element()); } + let second_bit = prefix_bits & 1_u8; let first_byte_without_control_bits = (first_byte << 2) >> 2; input_bytes[0] = first_byte_without_control_bits; - let input0 = &input_bytes[0..32]; - let input1 = &input_bytes[32..]; + let input1 = &input_bytes[0..32]; + let input0 = &input_bytes[32..]; let x0 = BN254FieldElement::from_bytes_be(input0).unwrap(); let x1 = BN254FieldElement::from_bytes_be(input1).unwrap(); let x: FieldElement = FieldElement::new([x0, x1]); let b_param_qfe = BN254TwistCurve::b(); - let mut y = FieldElement::::one(); - - //TODO: Why do we always have to set sqrt_qfe input to 0 for tests to pass? // If the first two bits are 11, then the square root chosen is the greater one. // So we should use sqrt_qfe with the input 1. - if prefix_bits == 3_u8 { - y = sqrt::sqrt_qfe(&(x.pow(3_u64) + b_param_qfe), 0) - .ok_or(ByteConversionError::InvalidValue)?; - - // If the first two bits are 10, then the square root chosen is the smaller one. - // So we should use sqrt_qfe with the input 0. - } else { - y = sqrt::sqrt_qfe(&(x.pow(3_u64) + b_param_qfe), 0) - .ok_or(ByteConversionError::InvalidValue)?; - } + let y = sqrt::sqrt_qfe(&(x.pow(3_u64) + b_param_qfe), second_bit) + .ok_or(ByteConversionError::InvalidValue)?; Self::G2Point::from_affine(x, y).map_err(|_| ByteConversionError::InvalidValue) - - //TODO: Do we have to check that the point is in the subgroup? } } @@ -296,8 +286,11 @@ mod tests { let first_byte_without_control_bits = (first_byte << 2) >> 2; compressed_g[0] = first_byte_without_control_bits; - let compressed_g_x = - FieldElement::::from_bytes_be(&compressed_g).unwrap(); + let [x1, x0] = FieldElement::::from_bytes_be(&compressed_g) + .unwrap() + .value() + .clone(); + let compressed_g_x = FieldElement::::new([x0, x1]); let g_x = g.x(); assert_eq!(*g_x, compressed_g_x); From 4a69b4b17b7efb46905979b991df2c4f333083e7 Mon Sep 17 00:00:00 2001 From: Nicole Date: Tue, 10 Sep 2024 12:44:48 -0300 Subject: [PATCH 20/20] check correct input bytes in decompress_g2_point function for bls12 --- .../short_weierstrass/curves/bls12_381/compression.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/math/src/elliptic_curve/short_weierstrass/curves/bls12_381/compression.rs b/math/src/elliptic_curve/short_weierstrass/curves/bls12_381/compression.rs index a77614251..18f30299f 100644 --- a/math/src/elliptic_curve/short_weierstrass/curves/bls12_381/compression.rs +++ b/math/src/elliptic_curve/short_weierstrass/curves/bls12_381/compression.rs @@ -154,6 +154,10 @@ impl Compress for BLS12381Curve { } fn decompress_g2_point(input_bytes: &mut [u8]) -> Result { + if !input_bytes.len() == 96 { + return Err(ByteConversionError::InvalidValue); + } + let first_byte = input_bytes.first().unwrap(); // We get the first 3 bits