From 409f3ececf165d25df99b661dd7eca46f2a058d5 Mon Sep 17 00:00:00 2001 From: Skalman Date: Thu, 29 Jun 2023 06:23:56 -0400 Subject: [PATCH 01/25] Implement Elligator2 hash to curve for Twisted Edward curves --- CHANGELOG.md | 1 + ec/src/hashing/curve_maps/elligator2/mod.rs | 283 ++++++++++++++++++++ ec/src/hashing/curve_maps/mod.rs | 1 + 3 files changed, 285 insertions(+) create mode 100644 ec/src/hashing/curve_maps/elligator2/mod.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index 0ea011d4c..846761f47 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ - [\#593](https://github.com/arkworks-rs/algebra/pull/593) (`ark-ec`) Change `AffineRepr::xy()` to return owned values. ### Features +- [\#xxx](https://github.com/arkworks-rs/algebra/pull/xxx) (`ark-ec`) Add Elligator2 hash-to-curve map. ### Improvements diff --git a/ec/src/hashing/curve_maps/elligator2/mod.rs b/ec/src/hashing/curve_maps/elligator2/mod.rs new file mode 100644 index 000000000..aa7451430 --- /dev/null +++ b/ec/src/hashing/curve_maps/elligator2/mod.rs @@ -0,0 +1,283 @@ +use crate::models::twisted_edwards::{MontCurveConfig, TECurveConfig}; +use ark_ff::{Field, One, Zero}; +use ark_std::string::ToString; +use core::marker::PhantomData; + +use crate::{ + hashing::{map_to_curve_hasher::MapToCurve, HashToCurveError}, + models::twisted_edwards::{Affine, Projective}, +}; + +/// Trait defining the necessary parameters for the Elligator2 hash-to-curve method +/// for twisted edwards curves form of: +/// `b * y² = x³ + a * x² + x` from [\[WB2019\]] +/// according to [\[HSSWW22\]] +/// +/// - [\[BHKL13\]] +/// - [\[HSSWW22\]] + +pub trait Elligator2Config: TECurveConfig + MontCurveConfig { + /// An element of the base field that is not a square root see \[BHKL13, Section 5\]. + /// When `BaseField` is a prime field, [\[HSSWW22\]] mandates that `Z` is the + /// non-square with lowest absolute value in the `BaseField` when its elements + /// are represented as [-(q-1)/2, (q-1)/2] + const Z: Self::BaseField; +} + +/// Represents the Elligator2 hash-to-curve map defined by `P`. +pub struct Elligator2Map(PhantomData P>); + +impl MapToCurve> for Elligator2Map

{ + /// Constructs a new map if `P` represents a valid map. + fn new() -> Result { + // Verifying that U is a non-square + if P::Z.legendre().is_qr() { + return Err(HashToCurveError::MapToCurveError( + "Z should be a quadratic non-residue for the Elligator2 map".to_string(), + )); + } + + // We assume that the Montgomery curve is correct and as such we do + // not verify the prerequisite for applicability of Elligator2 map + Ok(Elligator2Map(PhantomData)) + } + + /// Map an arbitrary base field element `element` to a curve point. + /// Based on + /// . + fn map_to_curve(&self, element: P::BaseField) -> Result, HashToCurveError> { + // 1. x1 = -(J / K) * inv0(1 + Z * u^2) + // 2. If x1 == 0, set x1 = -(J / K) + // 3. gx1 = x1^3 + (J / K) * x1^2 + x1 / K^2 + // 4. x2 = -x1 - (J / K) + // 5. gx2 = x2^3 + (J / K) * x2^2 + x2 / K^2 + // 6. If is_square(gx1), set x = x1, y = sqrt(gx1) with sgn0(y) == 1. + // 7. Else set x = x2, y = sqrt(gx2) with sgn0(y) == 0. + // 8. s = x * K + // 9. t = y * K + // 10. return (s, t) + + // ark a is irtf J + // ark b is irtf k + let j =

::COEFF_A; + let k =

::COEFF_B; + let j_on_k = j / k; + println!("j/k: {}", j_on_k); + println!("element: {}", element); + + let ksq_inv = k + .square() + .inverse() + .expect("B coefficient cannot be zero in Montgomery form"); + + let den_1 = ::one() + P::Z * element.square(); + println!("den1: {}", den_1); + + let x1 = -j_on_k + / (if den_1.is_zero() { + ::one() + } else { + den_1 + }); + println!("x1: {}", x1); + let x1sq = x1.square(); + println!("x1sq: {}", x1sq); + let x1cb = x1sq * x1; + let gx1 = x1cb + j_on_k * x1sq + x1 * ksq_inv; + // println!("gx1: {}", gx1); + + let x2 = -x1 - j_on_k; + let x2sq = x2.square(); + let x2cb = x2sq * x2; + let gx2 = x2cb + j_on_k * x2sq + x2 * ksq_inv; + + let (x, y, _sig0_y): (P::BaseField, P::BaseField, i8) = if gx1.legendre().is_qr() { + println!("gx1: {}", gx1); + println!("gx1.sqrt: {}", gx1.sqrt().expect("")); + ( + x1, + gx1.sqrt() + .expect("We have checked that gx1 is a quadratic residue. Q.E.D"), + 1, + ) + } else { + ( + x2, + gx2.sqrt() + .expect("gx2 is a quadratic residue because gx1 is not. Q.E.D"), + 0, + ) + }; + + let s = x * k; + let t = y * k; + + // this is an affine point on the Montgomery curve ideally TECurve + // should come with a mapping to its montgomery curve so we could + // just call that mapping here, but it seems not being the case, so + // we just implement the rational map here from [\[HSSWW22\]] Appendix D + + let tv1 = s + ::one(); + let tv2 = tv1 * t; + let (v, w) = if tv2.is_zero() { + (::zero(), ::one()) + } else { + let tv2_inv = tv2 + .inverse() + .expect("None zero element has inverse. Q.E.D."); + let v = tv2_inv * tv1 * s; + let w = tv2_inv * t * (s - ::one()); + (v, w) + }; + println!("s: {}", s); + println!("t: {}", t); + println!("v: {}", v); + println!("w: {}", w); + + let point_on_curve = Affine::

::new_unchecked(v, w); + assert!( + point_on_curve.is_on_curve(), + "Elligator2 mapped to a point off the curve" + ); + Ok(point_on_curve) + } +} + +#[cfg(test)] +mod test { + use crate::{ + hashing::{map_to_curve_hasher::MapToCurveBasedHasher, HashToCurve}, + CurveConfig, + }; + use ark_ff::field_hashers::DefaultFieldHasher; + + use super::*; + use ark_ff::{fields::Fp64, MontBackend, MontFp}; + use hashbrown::HashMap; + use sha2::Sha256; + + #[derive(ark_ff::MontConfig)] + #[modulus = "101"] + #[generator = "2"] + pub struct F101Config; + pub type F101 = Fp64>; + + #[derive(ark_ff::MontConfig)] + #[modulus = "11"] + #[generator = "2"] + pub struct F11Config; + pub type F11 = Fp64>; + + struct TestElligator2MapToCurveConfig; + + impl CurveConfig for TestElligator2MapToCurveConfig { + const COFACTOR: &'static [u64] = &[8]; + + #[rustfmt::skip] + const COFACTOR_INV: F11 = MontFp!("7"); + + type BaseField = F101; + type ScalarField = F11; + } + + /// sage: EnsureValidEdwards(F101,-1,12) + /// sage: Curve_EdwardsToMontgomery(F101, -1, 12) + /// (76, 23) + /// sage: Curve_EdwardsToWeierstrass(F101, -1, 12) + /// (11, 5) + /// sage: EllipticCurve(F101,[11,5]) + /// Elliptic Curve defined by y^2 = x^3 + 11*x + 5 over Finite Field of size 101 + /// sage: EW = EllipticCurve(F101,[11,5]) + /// sage: EW.order().factor() + /// 2^3 * 11 + /// sage: EW = EdwardsCurve(F101,-1,12) + /// sage: EW.gens()[0] * 8 + /// (5 : 36 : 1) + /// Point_WeierstrassToEdwards(F101, 11, 5, F101(5), F101(36), a_given=-1, d_given=12) + /// (23, 24) + + impl TECurveConfig for TestElligator2MapToCurveConfig { + /// COEFF_A = -1 + const COEFF_A: F101 = MontFp!("-1"); + + /// COEFF_D = 12 + const COEFF_D: F101 = MontFp!("12"); + + const GENERATOR: Affine = + Affine::::new_unchecked(MontFp!("23"), MontFp!("24")); + + type MontCurveConfig = TestElligator2MapToCurveConfig; + } + + impl MontCurveConfig for TestElligator2MapToCurveConfig { + /// COEFF_A = 76 + const COEFF_A: F101 = MontFp!("76"); + + /// COEFF_B = 23 + const COEFF_B: F101 = MontFp!("23"); + + type TECurveConfig = TestElligator2MapToCurveConfig; + } + + /// sage: find_z_ell2(F101) + /// 2 + impl Elligator2Config for TestElligator2MapToCurveConfig { + const Z: F101 = MontFp!("2"); + } + + /// The point of the test is to get a simple twisted edwards curve and make + /// simple hash + #[test] + fn hash_arbitary_string_to_curve_elligator2() { + let test_no: F101 = MontFp!("88"); + println!("test print 88: {}", test_no); + + let test_elligator2_to_curve_hasher = MapToCurveBasedHasher::< + Projective, + DefaultFieldHasher, + Elligator2Map, + >::new(&[1]) + .unwrap(); + + let hash_result = test_elligator2_to_curve_hasher.hash(b"if you stick a Babel fish in your ear you can instantly understand anything said to you in any form of language.").expect("fail to hash the string to curve"); + + assert!( + hash_result.is_on_curve(), + "hash results into a point off the curve" + ); + } + + /// Use a simple twisted edwards curve and map the whole field to it. We observe + /// the map behaviour. Specifically, the map should be non-constant, all + /// elements should be mapped to curve successfully. everything can be mapped + #[test] + fn map_field_to_curve_elligator2() { + let test_map_to_curve = Elligator2Map::::new().unwrap(); + + let mut map_range: Vec> = vec![]; + for current_field_element in 0..101 { + map_range.push( + test_map_to_curve + .map_to_curve(F101::from(current_field_element as u64)) + .unwrap(), + ); + } + + let mut counts = HashMap::new(); + + let mode = map_range + .iter() + .copied() + .max_by_key(|&n| { + let count = counts.entry(n).or_insert(0); + *count += 1; + *count + }) + .unwrap(); + + assert!( + *counts.get(&mode).unwrap() != 101, + "a constant hash function is not good." + ); + } +} diff --git a/ec/src/hashing/curve_maps/mod.rs b/ec/src/hashing/curve_maps/mod.rs index 7b0c3911d..189e83245 100644 --- a/ec/src/hashing/curve_maps/mod.rs +++ b/ec/src/hashing/curve_maps/mod.rs @@ -1,2 +1,3 @@ +pub mod elligator2; pub mod swu; pub mod wb; From 14fecc77ace08bce4007e0e5fd1417103600ab6b Mon Sep 17 00:00:00 2001 From: Skalman Date: Sat, 1 Jul 2023 10:11:32 -0400 Subject: [PATCH 02/25] - Update the pull request number for Elligator2 map in CHANGELOG.md - Fix fmt errors in other parts of the repo. --- CHANGELOG.md | 3 ++- ec/src/scalar_mul/glv.rs | 3 +-- ec/src/scalar_mul/mod.rs | 6 ++++-- test-curves/src/lib.rs | 6 ++---- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 846761f47..f1398f70d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,13 +2,14 @@ ## Pending +- [\#659](https://github.com/arkworks-rs/algebra/pull/659) (`ark-ec`) Add Elligator2 hash-to-curve map. + ### Breaking changes - [\#577](https://github.com/arkworks-rs/algebra/pull/577) (`ark-ff`, `ark-ec`) Add `AdditiveGroup`, a trait for additive groups (equipped with scalar field). - [\#593](https://github.com/arkworks-rs/algebra/pull/593) (`ark-ec`) Change `AffineRepr::xy()` to return owned values. ### Features -- [\#xxx](https://github.com/arkworks-rs/algebra/pull/xxx) (`ark-ec`) Add Elligator2 hash-to-curve map. ### Improvements diff --git a/ec/src/scalar_mul/glv.rs b/ec/src/scalar_mul/glv.rs index 21ef70077..4bc979641 100644 --- a/ec/src/scalar_mul/glv.rs +++ b/ec/src/scalar_mul/glv.rs @@ -1,7 +1,6 @@ -use crate::AdditiveGroup; use crate::{ short_weierstrass::{Affine, Projective, SWCurveConfig}, - CurveGroup, + AdditiveGroup, CurveGroup, }; use ark_ff::{PrimeField, Zero}; use num_bigint::{BigInt, BigUint, Sign}; diff --git a/ec/src/scalar_mul/mod.rs b/ec/src/scalar_mul/mod.rs index 1ae3a1e99..10f0f7109 100644 --- a/ec/src/scalar_mul/mod.rs +++ b/ec/src/scalar_mul/mod.rs @@ -4,8 +4,10 @@ pub mod wnaf; pub mod fixed_base; pub mod variable_base; -use crate::PrimeGroup; -use crate::short_weierstrass::{Affine, Projective, SWCurveConfig}; +use crate::{ + short_weierstrass::{Affine, Projective, SWCurveConfig}, + PrimeGroup, +}; use ark_ff::{AdditiveGroup, Zero}; use ark_std::{ ops::{Add, AddAssign, Mul, Neg, Sub, SubAssign}, diff --git a/test-curves/src/lib.rs b/test-curves/src/lib.rs index 9337089fa..0ebe128f5 100644 --- a/test-curves/src/lib.rs +++ b/test-curves/src/lib.rs @@ -1,10 +1,8 @@ #![no_std] -pub use ark_ff; -pub use ark_ff::{fields::models::*, FftField, Field, LegendreSymbol, MontFp, PrimeField}; +pub use ark_ff::{self, fields::models::*, FftField, Field, LegendreSymbol, MontFp, PrimeField}; -pub use ark_ec; -pub use ark_ec::*; +pub use ark_ec::{self, *}; #[cfg(any(feature = "bls12_381_scalar_field", feature = "bls12_381_curve"))] pub mod bls12_381; From 38dd05223e0e13542597280c95abf062d50451d0 Mon Sep 17 00:00:00 2001 From: Skalman Date: Fri, 7 Jul 2023 09:18:06 -0400 Subject: [PATCH 03/25] Remove diagnostic `println`s --- ec/src/hashing/curve_maps/elligator2/mod.rs | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/ec/src/hashing/curve_maps/elligator2/mod.rs b/ec/src/hashing/curve_maps/elligator2/mod.rs index aa7451430..1fd0bbdd2 100644 --- a/ec/src/hashing/curve_maps/elligator2/mod.rs +++ b/ec/src/hashing/curve_maps/elligator2/mod.rs @@ -62,8 +62,6 @@ impl MapToCurve> for Elligator2Map

{ let j =

::COEFF_A; let k =

::COEFF_B; let j_on_k = j / k; - println!("j/k: {}", j_on_k); - println!("element: {}", element); let ksq_inv = k .square() @@ -71,7 +69,6 @@ impl MapToCurve> for Elligator2Map

{ .expect("B coefficient cannot be zero in Montgomery form"); let den_1 = ::one() + P::Z * element.square(); - println!("den1: {}", den_1); let x1 = -j_on_k / (if den_1.is_zero() { @@ -79,12 +76,9 @@ impl MapToCurve> for Elligator2Map

{ } else { den_1 }); - println!("x1: {}", x1); let x1sq = x1.square(); - println!("x1sq: {}", x1sq); let x1cb = x1sq * x1; let gx1 = x1cb + j_on_k * x1sq + x1 * ksq_inv; - // println!("gx1: {}", gx1); let x2 = -x1 - j_on_k; let x2sq = x2.square(); @@ -92,8 +86,6 @@ impl MapToCurve> for Elligator2Map

{ let gx2 = x2cb + j_on_k * x2sq + x2 * ksq_inv; let (x, y, _sig0_y): (P::BaseField, P::BaseField, i8) = if gx1.legendre().is_qr() { - println!("gx1: {}", gx1); - println!("gx1.sqrt: {}", gx1.sqrt().expect("")); ( x1, gx1.sqrt() @@ -129,10 +121,6 @@ impl MapToCurve> for Elligator2Map

{ let w = tv2_inv * t * (s - ::one()); (v, w) }; - println!("s: {}", s); - println!("t: {}", t); - println!("v: {}", v); - println!("w: {}", w); let point_on_curve = Affine::

::new_unchecked(v, w); assert!( @@ -229,9 +217,6 @@ mod test { /// simple hash #[test] fn hash_arbitary_string_to_curve_elligator2() { - let test_no: F101 = MontFp!("88"); - println!("test print 88: {}", test_no); - let test_elligator2_to_curve_hasher = MapToCurveBasedHasher::< Projective, DefaultFieldHasher, From 1bf19057cc74720e39ec5b1874fa3fac6ae04f98 Mon Sep 17 00:00:00 2001 From: drskalman <35698397+drskalman@users.noreply.github.com> Date: Sun, 17 Sep 2023 17:40:29 -0400 Subject: [PATCH 04/25] Remove irrelevant comment as elligator is not based on pasta Co-authored-by: mmagician --- ec/src/hashing/curve_maps/elligator2/mod.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/ec/src/hashing/curve_maps/elligator2/mod.rs b/ec/src/hashing/curve_maps/elligator2/mod.rs index 1fd0bbdd2..c4dd5687f 100644 --- a/ec/src/hashing/curve_maps/elligator2/mod.rs +++ b/ec/src/hashing/curve_maps/elligator2/mod.rs @@ -43,8 +43,6 @@ impl MapToCurve> for Elligator2Map

{ } /// Map an arbitrary base field element `element` to a curve point. - /// Based on - /// . fn map_to_curve(&self, element: P::BaseField) -> Result, HashToCurveError> { // 1. x1 = -(J / K) * inv0(1 + Z * u^2) // 2. If x1 == 0, set x1 = -(J / K) From ba62320e87c14b4590c76b3dc91201d4b000f206 Mon Sep 17 00:00:00 2001 From: drskalman <35698397+drskalman@users.noreply.github.com> Date: Sun, 17 Sep 2023 17:49:53 -0400 Subject: [PATCH 05/25] Referencing RFC9380 for hash-to-curve instead of the draft, plus comment clean up. Co-authored-by: mmagician --- ec/src/hashing/curve_maps/elligator2/mod.rs | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/ec/src/hashing/curve_maps/elligator2/mod.rs b/ec/src/hashing/curve_maps/elligator2/mod.rs index c4dd5687f..1f80ba58e 100644 --- a/ec/src/hashing/curve_maps/elligator2/mod.rs +++ b/ec/src/hashing/curve_maps/elligator2/mod.rs @@ -11,14 +11,14 @@ use crate::{ /// Trait defining the necessary parameters for the Elligator2 hash-to-curve method /// for twisted edwards curves form of: /// `b * y² = x³ + a * x² + x` from [\[WB2019\]] -/// according to [\[HSSWW22\]] +/// according to [\[HSSWW23\]] /// /// - [\[BHKL13\]] -/// - [\[HSSWW22\]] +/// - [\[HSSWW23\]] pub trait Elligator2Config: TECurveConfig + MontCurveConfig { /// An element of the base field that is not a square root see \[BHKL13, Section 5\]. - /// When `BaseField` is a prime field, [\[HSSWW22\]] mandates that `Z` is the + /// When `BaseField` is a prime field, [\[HSSWW23\]] mandates that `Z` is the /// non-square with lowest absolute value in the `BaseField` when its elements /// are represented as [-(q-1)/2, (q-1)/2] const Z: Self::BaseField; @@ -30,7 +30,7 @@ pub struct Elligator2Map(PhantomData P>); impl MapToCurve> for Elligator2Map

{ /// Constructs a new map if `P` represents a valid map. fn new() -> Result { - // Verifying that U is a non-square + // Verifying that Z is a non-square if P::Z.legendre().is_qr() { return Err(HashToCurveError::MapToCurveError( "Z should be a quadratic non-residue for the Elligator2 map".to_string(), @@ -102,9 +102,10 @@ impl MapToCurve> for Elligator2Map

{ let s = x * k; let t = y * k; - // this is an affine point on the Montgomery curve ideally TECurve - // should come with a mapping to its montgomery curve so we could - // just call that mapping here, but it seems not being the case, so + // `(s, t)` is an affine point on the Montgomery curve. + // Ideally, the TECurve would come with a mapping to its Montgomery curve, + // so we could just call that mapping here. + // This is currently not supported in arkworks, so // we just implement the rational map here from [\[HSSWW22\]] Appendix D let tv1 = s + ::one(); From 9b0ddcccf48474a8a6367ffd060544ba85092308 Mon Sep 17 00:00:00 2001 From: drskalman <35698397+drskalman@users.noreply.github.com> Date: Sun, 17 Sep 2023 17:50:54 -0400 Subject: [PATCH 06/25] Cite new reference for hash-to-curve Co-authored-by: mmagician --- ec/src/hashing/curve_maps/elligator2/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ec/src/hashing/curve_maps/elligator2/mod.rs b/ec/src/hashing/curve_maps/elligator2/mod.rs index 1f80ba58e..740d4a5fd 100644 --- a/ec/src/hashing/curve_maps/elligator2/mod.rs +++ b/ec/src/hashing/curve_maps/elligator2/mod.rs @@ -106,7 +106,7 @@ impl MapToCurve> for Elligator2Map

{ // Ideally, the TECurve would come with a mapping to its Montgomery curve, // so we could just call that mapping here. // This is currently not supported in arkworks, so - // we just implement the rational map here from [\[HSSWW22\]] Appendix D + // we just implement the rational map here from [\[HSSWW23\]] Appendix D let tv1 = s + ::one(); let tv2 = tv1 * t; From c66e98c3a0c99040878d5baac7e1b0674967b8d0 Mon Sep 17 00:00:00 2001 From: drskalman <35698397+drskalman@users.noreply.github.com> Date: Sun, 17 Sep 2023 18:02:19 -0400 Subject: [PATCH 07/25] Make sig0 function of elligator2 map boolean instead of 0u8, 1u8 Co-authored-by: mmagician --- ec/src/hashing/curve_maps/elligator2/mod.rs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/ec/src/hashing/curve_maps/elligator2/mod.rs b/ec/src/hashing/curve_maps/elligator2/mod.rs index 740d4a5fd..fb23035b6 100644 --- a/ec/src/hashing/curve_maps/elligator2/mod.rs +++ b/ec/src/hashing/curve_maps/elligator2/mod.rs @@ -83,21 +83,25 @@ impl MapToCurve> for Elligator2Map

{ let x2cb = x2sq * x2; let gx2 = x2cb + j_on_k * x2sq + x2 * ksq_inv; - let (x, y, _sig0_y): (P::BaseField, P::BaseField, i8) = if gx1.legendre().is_qr() { + let (x, mut y, sgn0): (P::BaseField, P::BaseField, bool) = if gx1.legendre().is_qr() { ( x1, gx1.sqrt() .expect("We have checked that gx1 is a quadratic residue. Q.E.D"), - 1, + true, ) } else { ( x2, gx2.sqrt() .expect("gx2 is a quadratic residue because gx1 is not. Q.E.D"), - 0, + false, ) }; + + if parity(&y) != sgn0 { + y = -y; + } let s = x * k; let t = y * k; From e7cfcaa493a05c3383bed00914dd79d5b0200f24 Mon Sep 17 00:00:00 2001 From: Skalman Date: Sun, 17 Sep 2023 18:17:18 -0400 Subject: [PATCH 08/25] Move parity method from `curve_maps::swu` to `curve_maps` as it is used by both swu and elligator. --- ec/src/hashing/curve_maps/elligator2/mod.rs | 5 +++-- ec/src/hashing/curve_maps/mod.rs | 13 +++++++++++++ ec/src/hashing/curve_maps/swu/mod.rs | 14 ++------------ ec/src/hashing/tests.rs | 2 +- 4 files changed, 19 insertions(+), 15 deletions(-) diff --git a/ec/src/hashing/curve_maps/elligator2/mod.rs b/ec/src/hashing/curve_maps/elligator2/mod.rs index fb23035b6..2db436d0f 100644 --- a/ec/src/hashing/curve_maps/elligator2/mod.rs +++ b/ec/src/hashing/curve_maps/elligator2/mod.rs @@ -4,8 +4,9 @@ use ark_std::string::ToString; use core::marker::PhantomData; use crate::{ - hashing::{map_to_curve_hasher::MapToCurve, HashToCurveError}, + hashing::{map_to_curve_hasher::MapToCurve, curve_maps::parity, HashToCurveError}, models::twisted_edwards::{Affine, Projective}, + }; /// Trait defining the necessary parameters for the Elligator2 hash-to-curve method @@ -83,7 +84,7 @@ impl MapToCurve> for Elligator2Map

{ let x2cb = x2sq * x2; let gx2 = x2cb + j_on_k * x2sq + x2 * ksq_inv; - let (x, mut y, sgn0): (P::BaseField, P::BaseField, bool) = if gx1.legendre().is_qr() { + let (x, mut y, sgn0) = if gx1.legendre().is_qr() { ( x1, gx1.sqrt() diff --git a/ec/src/hashing/curve_maps/mod.rs b/ec/src/hashing/curve_maps/mod.rs index 189e83245..d3a07a015 100644 --- a/ec/src/hashing/curve_maps/mod.rs +++ b/ec/src/hashing/curve_maps/mod.rs @@ -1,3 +1,16 @@ +use ark_ff::{BigInteger, Field, PrimeField, Zero}; pub mod elligator2; pub mod swu; pub mod wb; + +//// parity method on the Field elements based on [\[1\]] Section 4.1 +//// which is used by multiple curve maps including Elligator2 and SWU +/// +/// - [\[1\]] +pub fn parity(element: &F) -> bool { + element + + .to_base_prime_field_elements() + .find(|&x| !x.is_zero()) + .map_or(false, |x| x.into_bigint().is_odd()) +} diff --git a/ec/src/hashing/curve_maps/swu/mod.rs b/ec/src/hashing/curve_maps/swu/mod.rs index 47330c52c..73d09a9b0 100644 --- a/ec/src/hashing/curve_maps/swu/mod.rs +++ b/ec/src/hashing/curve_maps/swu/mod.rs @@ -1,10 +1,10 @@ use crate::models::short_weierstrass::SWCurveConfig; -use ark_ff::{BigInteger, Field, One, PrimeField, Zero}; +use ark_ff::{Field, One, Zero}; use ark_std::string::ToString; use core::marker::PhantomData; use crate::{ - hashing::{map_to_curve_hasher::MapToCurve, HashToCurveError}, + hashing::{map_to_curve_hasher::MapToCurve, curve_maps::parity, HashToCurveError}, models::short_weierstrass::{Affine, Projective}, }; @@ -24,16 +24,6 @@ pub trait SWUConfig: SWCurveConfig { /// Represents the SWU hash-to-curve map defined by `P`. pub struct SWUMap(PhantomData P>); -/// Trait defining a parity method on the Field elements based on [\[1\]] Section 4.1 -/// -/// - [\[1\]] -pub fn parity(element: &F) -> bool { - element - .to_base_prime_field_elements() - .find(|&x| !x.is_zero()) - .map_or(false, |x| x.into_bigint().is_odd()) -} - impl MapToCurve> for SWUMap

{ /// Constructs a new map if `P` represents a valid map. fn new() -> Result { diff --git a/ec/src/hashing/tests.rs b/ec/src/hashing/tests.rs index 1aaca09cb..8805d23c2 100644 --- a/ec/src/hashing/tests.rs +++ b/ec/src/hashing/tests.rs @@ -1,4 +1,4 @@ -use crate::hashing::curve_maps::swu::parity; +use crate::hashing::curve_maps::parity; use ark_test_curves::bls12_381::{Fq, Fq2, Fq6}; #[test] From f823e04f19924ce8d0706d1d727a8661c6600d76 Mon Sep 17 00:00:00 2001 From: Skalman Date: Sun, 17 Sep 2023 18:50:02 -0400 Subject: [PATCH 09/25] Remove map-to-curve sanity checks from release build. --- ec/src/hashing/curve_maps/elligator2/mod.rs | 10 +++------- ec/src/hashing/curve_maps/swu/mod.rs | 21 ++++++++------------- ec/src/hashing/curve_maps/wb/mod.rs | 5 ++--- 3 files changed, 13 insertions(+), 23 deletions(-) diff --git a/ec/src/hashing/curve_maps/elligator2/mod.rs b/ec/src/hashing/curve_maps/elligator2/mod.rs index 2db436d0f..3a89e4a24 100644 --- a/ec/src/hashing/curve_maps/elligator2/mod.rs +++ b/ec/src/hashing/curve_maps/elligator2/mod.rs @@ -1,6 +1,5 @@ use crate::models::twisted_edwards::{MontCurveConfig, TECurveConfig}; use ark_ff::{Field, One, Zero}; -use ark_std::string::ToString; use core::marker::PhantomData; use crate::{ @@ -32,11 +31,8 @@ impl MapToCurve> for Elligator2Map

{ /// Constructs a new map if `P` represents a valid map. fn new() -> Result { // Verifying that Z is a non-square - if P::Z.legendre().is_qr() { - return Err(HashToCurveError::MapToCurveError( - "Z should be a quadratic non-residue for the Elligator2 map".to_string(), - )); - } + debug_assert!(!P::Z.legendre().is_qr(), + "Z should be a quadratic non-residue for the Elligator2 map"); // We assume that the Montgomery curve is correct and as such we do // not verify the prerequisite for applicability of Elligator2 map @@ -127,7 +123,7 @@ impl MapToCurve> for Elligator2Map

{ }; let point_on_curve = Affine::

::new_unchecked(v, w); - assert!( + debug_assert!( point_on_curve.is_on_curve(), "Elligator2 mapped to a point off the curve" ); diff --git a/ec/src/hashing/curve_maps/swu/mod.rs b/ec/src/hashing/curve_maps/swu/mod.rs index 73d09a9b0..b7b293980 100644 --- a/ec/src/hashing/curve_maps/swu/mod.rs +++ b/ec/src/hashing/curve_maps/swu/mod.rs @@ -1,6 +1,5 @@ use crate::models::short_weierstrass::SWCurveConfig; use ark_ff::{Field, One, Zero}; -use ark_std::string::ToString; use core::marker::PhantomData; use crate::{ @@ -28,19 +27,15 @@ impl MapToCurve> for SWUMap

{ /// Constructs a new map if `P` represents a valid map. fn new() -> Result { // Verifying that ZETA is a non-square - if P::ZETA.legendre().is_qr() { - return Err(HashToCurveError::MapToCurveError( - "ZETA should be a quadratic non-residue for the SWU map".to_string(), - )); - } + debug_assert!(!P::ZETA.legendre().is_qr(), + "ZETA should be a quadratic non-residue for the SWU map"); // Verifying the prerequisite for applicability of SWU map - if P::COEFF_A.is_zero() || P::COEFF_B.is_zero() { - return Err(HashToCurveError::MapToCurveError("Simplified SWU requires a * b != 0 in the short Weierstrass form of y^2 = x^3 + a*x + b ".to_string())); - } - + debug_assert!(!P::COEFF_A.is_zero() && !P::COEFF_B.is_zero(), + "Simplified SWU requires a * b != 0 in the short Weierstrass form of y^2 = x^3 + a*x + b "); + Ok(SWUMap(PhantomData)) - } + } /// Map an arbitrary base field element to a curve point. /// Based on @@ -89,7 +84,7 @@ impl MapToCurve> for SWUMap

{ let gx1_square; let gx1; - assert!( + debug_assert!( !div3.is_zero(), "we have checked that neither a or ZETA are zero. Q.E.D." ); @@ -131,7 +126,7 @@ impl MapToCurve> for SWUMap

{ let x_affine = num_x / div; let y_affine = if parity(&y) != parity(&point) { -y } else { y }; let point_on_curve = Affine::

::new_unchecked(x_affine, y_affine); - assert!( + debug_assert!( point_on_curve.is_on_curve(), "swu mapped to a point off the curve" ); diff --git a/ec/src/hashing/curve_maps/wb/mod.rs b/ec/src/hashing/curve_maps/wb/mod.rs index 10ddbb0f7..7b992d7a7 100644 --- a/ec/src/hashing/curve_maps/wb/mod.rs +++ b/ec/src/hashing/curve_maps/wb/mod.rs @@ -88,9 +88,8 @@ impl MapToCurve> for WBMap

{ fn new() -> Result { match P::ISOGENY_MAP.apply(P::IsogenousCurve::GENERATOR) { Ok(point_on_curve) => { - if !point_on_curve.is_on_curve() { - return Err(HashToCurveError::MapToCurveError(format!("the isogeny maps the generator of its domain: {} into {} which does not belong to its codomain.",P::IsogenousCurve::GENERATOR, point_on_curve))); - } + debug_assert!(point_on_curve.is_on_curve(), + "the isogeny maps the generator of its domain: {} into {} which does not belong to its codomain.",P::IsogenousCurve::GENERATOR, point_on_curve); }, Err(e) => return Err(e), } From 26e91926365f2b9856727ab034280664aa93d1fa Mon Sep 17 00:00:00 2001 From: Skalman Date: Sun, 17 Sep 2023 18:52:41 -0400 Subject: [PATCH 10/25] cargo fmt --- ec/src/hashing/curve_maps/elligator2/mod.rs | 13 +++++++------ ec/src/hashing/curve_maps/mod.rs | 1 - ec/src/hashing/curve_maps/swu/mod.rs | 12 +++++++----- ec/src/hashing/curve_maps/wb/mod.rs | 2 +- 4 files changed, 15 insertions(+), 13 deletions(-) diff --git a/ec/src/hashing/curve_maps/elligator2/mod.rs b/ec/src/hashing/curve_maps/elligator2/mod.rs index 3a89e4a24..b0fa44db9 100644 --- a/ec/src/hashing/curve_maps/elligator2/mod.rs +++ b/ec/src/hashing/curve_maps/elligator2/mod.rs @@ -3,9 +3,8 @@ use ark_ff::{Field, One, Zero}; use core::marker::PhantomData; use crate::{ - hashing::{map_to_curve_hasher::MapToCurve, curve_maps::parity, HashToCurveError}, + hashing::{curve_maps::parity, map_to_curve_hasher::MapToCurve, HashToCurveError}, models::twisted_edwards::{Affine, Projective}, - }; /// Trait defining the necessary parameters for the Elligator2 hash-to-curve method @@ -31,8 +30,10 @@ impl MapToCurve> for Elligator2Map

{ /// Constructs a new map if `P` represents a valid map. fn new() -> Result { // Verifying that Z is a non-square - debug_assert!(!P::Z.legendre().is_qr(), - "Z should be a quadratic non-residue for the Elligator2 map"); + debug_assert!( + !P::Z.legendre().is_qr(), + "Z should be a quadratic non-residue for the Elligator2 map" + ); // We assume that the Montgomery curve is correct and as such we do // not verify the prerequisite for applicability of Elligator2 map @@ -95,7 +96,7 @@ impl MapToCurve> for Elligator2Map

{ false, ) }; - + if parity(&y) != sgn0 { y = -y; } @@ -104,7 +105,7 @@ impl MapToCurve> for Elligator2Map

{ let t = y * k; // `(s, t)` is an affine point on the Montgomery curve. - // Ideally, the TECurve would come with a mapping to its Montgomery curve, + // Ideally, the TECurve would come with a mapping to its Montgomery curve, // so we could just call that mapping here. // This is currently not supported in arkworks, so // we just implement the rational map here from [\[HSSWW23\]] Appendix D diff --git a/ec/src/hashing/curve_maps/mod.rs b/ec/src/hashing/curve_maps/mod.rs index d3a07a015..64f67d864 100644 --- a/ec/src/hashing/curve_maps/mod.rs +++ b/ec/src/hashing/curve_maps/mod.rs @@ -9,7 +9,6 @@ pub mod wb; /// - [\[1\]] pub fn parity(element: &F) -> bool { element - .to_base_prime_field_elements() .find(|&x| !x.is_zero()) .map_or(false, |x| x.into_bigint().is_odd()) diff --git a/ec/src/hashing/curve_maps/swu/mod.rs b/ec/src/hashing/curve_maps/swu/mod.rs index b7b293980..4040ae9c4 100644 --- a/ec/src/hashing/curve_maps/swu/mod.rs +++ b/ec/src/hashing/curve_maps/swu/mod.rs @@ -3,7 +3,7 @@ use ark_ff::{Field, One, Zero}; use core::marker::PhantomData; use crate::{ - hashing::{map_to_curve_hasher::MapToCurve, curve_maps::parity, HashToCurveError}, + hashing::{curve_maps::parity, map_to_curve_hasher::MapToCurve, HashToCurveError}, models::short_weierstrass::{Affine, Projective}, }; @@ -27,15 +27,17 @@ impl MapToCurve> for SWUMap

{ /// Constructs a new map if `P` represents a valid map. fn new() -> Result { // Verifying that ZETA is a non-square - debug_assert!(!P::ZETA.legendre().is_qr(), - "ZETA should be a quadratic non-residue for the SWU map"); + debug_assert!( + !P::ZETA.legendre().is_qr(), + "ZETA should be a quadratic non-residue for the SWU map" + ); // Verifying the prerequisite for applicability of SWU map debug_assert!(!P::COEFF_A.is_zero() && !P::COEFF_B.is_zero(), "Simplified SWU requires a * b != 0 in the short Weierstrass form of y^2 = x^3 + a*x + b "); - + Ok(SWUMap(PhantomData)) - } + } /// Map an arbitrary base field element to a curve point. /// Based on diff --git a/ec/src/hashing/curve_maps/wb/mod.rs b/ec/src/hashing/curve_maps/wb/mod.rs index 7b992d7a7..d822ec893 100644 --- a/ec/src/hashing/curve_maps/wb/mod.rs +++ b/ec/src/hashing/curve_maps/wb/mod.rs @@ -89,7 +89,7 @@ impl MapToCurve> for WBMap

{ match P::ISOGENY_MAP.apply(P::IsogenousCurve::GENERATOR) { Ok(point_on_curve) => { debug_assert!(point_on_curve.is_on_curve(), - "the isogeny maps the generator of its domain: {} into {} which does not belong to its codomain.",P::IsogenousCurve::GENERATOR, point_on_curve); + "the isogeny maps the generator of its domain: {} into {} which does not belong to its codomain.",P::IsogenousCurve::GENERATOR, point_on_curve); }, Err(e) => return Err(e), } From 3782a85e9e23495384b5c2ae369e070cf28ad53d Mon Sep 17 00:00:00 2001 From: Skalman Date: Sun, 17 Sep 2023 20:20:15 -0400 Subject: [PATCH 11/25] - apply new naming convention for map2curves - rename `new` to `check_parameters` --- .../{elligator2/mod.rs => elligator2.rs} | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) rename ec/src/hashing/curve_maps/{elligator2/mod.rs => elligator2.rs} (95%) diff --git a/ec/src/hashing/curve_maps/elligator2/mod.rs b/ec/src/hashing/curve_maps/elligator2.rs similarity index 95% rename from ec/src/hashing/curve_maps/elligator2/mod.rs rename to ec/src/hashing/curve_maps/elligator2.rs index b0fa44db9..2fce511a0 100644 --- a/ec/src/hashing/curve_maps/elligator2/mod.rs +++ b/ec/src/hashing/curve_maps/elligator2.rs @@ -28,7 +28,7 @@ pub struct Elligator2Map(PhantomData P>); impl MapToCurve> for Elligator2Map

{ /// Constructs a new map if `P` represents a valid map. - fn new() -> Result { + fn check_parameters() -> Result<(), HashToCurveError> { // Verifying that Z is a non-square debug_assert!( !P::Z.legendre().is_qr(), @@ -37,11 +37,11 @@ impl MapToCurve> for Elligator2Map

{ // We assume that the Montgomery curve is correct and as such we do // not verify the prerequisite for applicability of Elligator2 map - Ok(Elligator2Map(PhantomData)) + Ok(()) } /// Map an arbitrary base field element `element` to a curve point. - fn map_to_curve(&self, element: P::BaseField) -> Result, HashToCurveError> { + fn map_to_curve(element: P::BaseField) -> Result, HashToCurveError> { // 1. x1 = -(J / K) * inv0(1 + Z * u^2) // 2. If x1 == 0, set x1 = -(J / K) // 3. gx1 = x1^3 + (J / K) * x1^2 + x1 / K^2 @@ -238,14 +238,15 @@ mod test { /// elements should be mapped to curve successfully. everything can be mapped #[test] fn map_field_to_curve_elligator2() { - let test_map_to_curve = Elligator2Map::::new().unwrap(); + Elligator2Map::::check_parameters().unwrap(); let mut map_range: Vec> = vec![]; for current_field_element in 0..101 { map_range.push( - test_map_to_curve - .map_to_curve(F101::from(current_field_element as u64)) - .unwrap(), + Elligator2Map::::map_to_curve(F101::from( + current_field_element as u64, + )) + .unwrap(), ); } From 7f2ef44f1234edd1ab196a0f274490f4016f4c85 Mon Sep 17 00:00:00 2001 From: Skalman Date: Sun, 17 Sep 2023 20:22:58 -0400 Subject: [PATCH 12/25] fix the documentation for `Elligator2Map::check_parameters` --- ec/src/hashing/curve_maps/elligator2.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ec/src/hashing/curve_maps/elligator2.rs b/ec/src/hashing/curve_maps/elligator2.rs index 2fce511a0..fe5c74372 100644 --- a/ec/src/hashing/curve_maps/elligator2.rs +++ b/ec/src/hashing/curve_maps/elligator2.rs @@ -27,7 +27,7 @@ pub trait Elligator2Config: TECurveConfig + MontCurveConfig { pub struct Elligator2Map(PhantomData P>); impl MapToCurve> for Elligator2Map

{ - /// Constructs a new map if `P` represents a valid map. + /// Checks if `P` represents a valid Elligator2 map. fn check_parameters() -> Result<(), HashToCurveError> { // Verifying that Z is a non-square debug_assert!( From 354db69df8048c6ec6a0027c11a03b7f9d7bf15d Mon Sep 17 00:00:00 2001 From: Skalman Date: Mon, 18 Sep 2023 23:28:34 -0400 Subject: [PATCH 13/25] Call elements of the field `element` not `point` in SWU hash-to-curve map --- ec/src/hashing/curve_maps/swu.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/ec/src/hashing/curve_maps/swu.rs b/ec/src/hashing/curve_maps/swu.rs index e0c371d91..19a471156 100644 --- a/ec/src/hashing/curve_maps/swu.rs +++ b/ec/src/hashing/curve_maps/swu.rs @@ -42,7 +42,7 @@ impl MapToCurve> for SWUMap

{ /// Map an arbitrary base field element to a curve point. /// Based on /// . - fn map_to_curve(point: P::BaseField) -> Result, HashToCurveError> { + fn map_to_curve(element: P::BaseField) -> Result, HashToCurveError> { // 1. tv1 = inv0(Z^2 * u^4 + Z * u^2) // 2. x1 = (-B / A) * (1 + tv1) // 3. If tv1 == 0, set x1 = B / (Z * A) @@ -67,7 +67,7 @@ impl MapToCurve> for SWUMap

{ let a = P::COEFF_A; let b = P::COEFF_B; - let zeta_u2 = P::ZETA * point.square(); + let zeta_u2 = P::ZETA * element.square(); let ta = zeta_u2.square() + zeta_u2; let num_x1 = b * (ta + ::one()); let div = a * if ta.is_zero() { P::ZETA } else { -ta }; @@ -121,12 +121,12 @@ impl MapToCurve> for SWUMap

{ // u^3 * y1 is a square root of gx2. Note that we don't actually need to // compute gx2. - let y2 = zeta_u2 * point * y1; + let y2 = zeta_u2 * element * y1; let num_x = if gx1_square { num_x1 } else { num_x2 }; let y = if gx1_square { y1 } else { y2 }; let x_affine = num_x / div; - let y_affine = if parity(&y) != parity(&point) { -y } else { y }; + let y_affine = if parity(&y) != parity(&element) { -y } else { y }; let point_on_curve = Affine::

::new_unchecked(x_affine, y_affine); debug_assert!( point_on_curve.is_on_curve(), @@ -247,8 +247,8 @@ mod test { let mut map_range: Vec> = vec![]; for current_field_element in 0..127 { - let point = F127::from(current_field_element as u64); - map_range.push(SWUMap::::map_to_curve(point).unwrap()); + let element = F127::from(current_field_element as u64); + map_range.push(SWUMap::::map_to_curve(element).unwrap()); } let mut counts = HashMap::new(); From d8667ca7aa2a9d5800128bea3c87c1e1349f694f Mon Sep 17 00:00:00 2001 From: Skalman Date: Mon, 18 Sep 2023 23:33:46 -0400 Subject: [PATCH 14/25] Mention moving of `parity` function in breaking changes. --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c5434163b..c8437f1bd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ ### Breaking changes +- [\#659](https://github.com/arkworks-rs/algebra/pull/659) (`ark-ec`) Move auxiliary `parity` function from `ark_ec::hashing::curve_maps::swu` to `ark_ec::hashing::curve_maps`. - [\#577](https://github.com/arkworks-rs/algebra/pull/577) (`ark-ff`, `ark-ec`) Add `AdditiveGroup`, a trait for additive groups (equipped with scalar field). - [\#593](https://github.com/arkworks-rs/algebra/pull/593) (`ark-ec`) Change `AffineRepr::xy()` to return owned values. - [\#633](https://github.com/arkworks-rs/algebra/pull/633) (`ark-ec`) Generic pairing implementation for the curves from the BW6 family. From 9622106720a839ee6e4ed823b3bc5ffd6383141a Mon Sep 17 00:00:00 2001 From: Skalman Date: Tue, 19 Sep 2023 00:43:51 -0400 Subject: [PATCH 15/25] fmt --- ec/src/hashing/curve_maps/swu.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/ec/src/hashing/curve_maps/swu.rs b/ec/src/hashing/curve_maps/swu.rs index 19a471156..8ae0a45a8 100644 --- a/ec/src/hashing/curve_maps/swu.rs +++ b/ec/src/hashing/curve_maps/swu.rs @@ -126,7 +126,11 @@ impl MapToCurve> for SWUMap

{ let y = if gx1_square { y1 } else { y2 }; let x_affine = num_x / div; - let y_affine = if parity(&y) != parity(&element) { -y } else { y }; + let y_affine = if parity(&y) != parity(&element) { + -y + } else { + y + }; let point_on_curve = Affine::

::new_unchecked(x_affine, y_affine); debug_assert!( point_on_curve.is_on_curve(), From 318aeb8b498ef7afe530dc65ee1c7d9244643dc1 Mon Sep 17 00:00:00 2001 From: Skalman Date: Wed, 20 Sep 2023 11:55:28 -0400 Subject: [PATCH 16/25] move \#659 from pending bto features in `CHANGELOG.md` --- CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c8437f1bd..027e037e7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,8 +2,6 @@ ## Pending -- [\#659](https://github.com/arkworks-rs/algebra/pull/659) (`ark-ec`) Add Elligator2 hash-to-curve map. - ### Breaking changes - [\#659](https://github.com/arkworks-rs/algebra/pull/659) (`ark-ec`) Move auxiliary `parity` function from `ark_ec::hashing::curve_maps::swu` to `ark_ec::hashing::curve_maps`. @@ -13,6 +11,8 @@ ### Features +- [\#659](https://github.com/arkworks-rs/algebra/pull/659) (`ark-ec`) Add Elligator2 hash-to-curve map. + ### Improvements ### Bugfixes From 9370b2e55f216a37d68e892d93f7913cb952bde5 Mon Sep 17 00:00:00 2001 From: Skalman Date: Thu, 5 Oct 2023 07:09:41 -0400 Subject: [PATCH 17/25] bring back `new` and MapToCurve Object. --- ec/src/hashing/curve_maps/elligator2.rs | 27 ++++++++++++++++++------- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/ec/src/hashing/curve_maps/elligator2.rs b/ec/src/hashing/curve_maps/elligator2.rs index fe5c74372..3334ede00 100644 --- a/ec/src/hashing/curve_maps/elligator2.rs +++ b/ec/src/hashing/curve_maps/elligator2.rs @@ -26,22 +26,31 @@ pub trait Elligator2Config: TECurveConfig + MontCurveConfig { /// Represents the Elligator2 hash-to-curve map defined by `P`. pub struct Elligator2Map(PhantomData P>); -impl MapToCurve> for Elligator2Map

{ - /// Checks if `P` represents a valid Elligator2 map. +impl Elligator2Map

{ + /// Checks if `P` represents a valid Elligator2 map. Panics otherwise. fn check_parameters() -> Result<(), HashToCurveError> { + // We assume that the Montgomery curve is correct and as such we do + // not verify the prerequisite for applicability of Elligator2 map to the TECurveConfing. + // Verifying that Z is a non-square debug_assert!( !P::Z.legendre().is_qr(), "Z should be a quadratic non-residue for the Elligator2 map" ); + Ok(()) + } +} + +impl MapToCurve> for Elligator2Map

{ + fn new() -> Result { + //Checking validity `Elligator2Config` so we actually representing a valid Elligator2 map. + Self::check_parameters()?; - // We assume that the Montgomery curve is correct and as such we do - // not verify the prerequisite for applicability of Elligator2 map - Ok(()) + Ok(Elligator2Map(PhantomData)) } /// Map an arbitrary base field element `element` to a curve point. - fn map_to_curve(element: P::BaseField) -> Result, HashToCurveError> { + fn map_to_curve(&self, element: P::BaseField) -> Result, HashToCurveError> { // 1. x1 = -(J / K) * inv0(1 + Z * u^2) // 2. If x1 == 0, set x1 = -(J / K) // 3. gx1 = x1^3 + (J / K) * x1^2 + x1 / K^2 @@ -139,6 +148,7 @@ mod test { CurveConfig, }; use ark_ff::field_hashers::DefaultFieldHasher; + use ark_std::vec::Vec; use super::*; use ark_ff::{fields::Fp64, MontBackend, MontFp}; @@ -239,11 +249,14 @@ mod test { #[test] fn map_field_to_curve_elligator2() { Elligator2Map::::check_parameters().unwrap(); + let test_map_to_curve = Elligator2Map::::new().unwrap(); let mut map_range: Vec> = vec![]; + //We are mapping all elemnts of the field to the curve, verifying that + //map is not constant on that set. for current_field_element in 0..101 { map_range.push( - Elligator2Map::::map_to_curve(F101::from( + test_map_to_curve.map_to_curve(F101::from( current_field_element as u64, )) .unwrap(), From e658eac5b0047e195ca2365f9fe683b322b08900 Mon Sep 17 00:00:00 2001 From: Skalman Date: Thu, 5 Oct 2023 07:10:36 -0400 Subject: [PATCH 18/25] `cargo fmt` --- ec/src/hashing/curve_maps/elligator2.rs | 21 ++++++++++----------- ec/src/hashing/curve_maps/mod.rs | 1 - ec/src/hashing/curve_maps/swu/mod.rs | 8 ++------ ec/src/scalar_mul/mod.rs | 6 ++++-- 4 files changed, 16 insertions(+), 20 deletions(-) diff --git a/ec/src/hashing/curve_maps/elligator2.rs b/ec/src/hashing/curve_maps/elligator2.rs index 3334ede00..23d1b9698 100644 --- a/ec/src/hashing/curve_maps/elligator2.rs +++ b/ec/src/hashing/curve_maps/elligator2.rs @@ -27,9 +27,9 @@ pub trait Elligator2Config: TECurveConfig + MontCurveConfig { pub struct Elligator2Map(PhantomData P>); impl Elligator2Map

{ - /// Checks if `P` represents a valid Elligator2 map. Panics otherwise. + /// Checks if `P` represents a valid Elligator2 map. Panics otherwise. fn check_parameters() -> Result<(), HashToCurveError> { - // We assume that the Montgomery curve is correct and as such we do + // We assume that the Montgomery curve is correct and as such we do // not verify the prerequisite for applicability of Elligator2 map to the TECurveConfing. // Verifying that Z is a non-square @@ -37,14 +37,14 @@ impl Elligator2Map

{ !P::Z.legendre().is_qr(), "Z should be a quadratic non-residue for the Elligator2 map" ); - Ok(()) + Ok(()) } } impl MapToCurve> for Elligator2Map

{ fn new() -> Result { - //Checking validity `Elligator2Config` so we actually representing a valid Elligator2 map. - Self::check_parameters()?; + // Checking validity `Elligator2Config` so we actually representing a valid Elligator2 map. + Self::check_parameters()?; Ok(Elligator2Map(PhantomData)) } @@ -252,14 +252,13 @@ mod test { let test_map_to_curve = Elligator2Map::::new().unwrap(); let mut map_range: Vec> = vec![]; - //We are mapping all elemnts of the field to the curve, verifying that - //map is not constant on that set. + // We are mapping all elemnts of the field to the curve, verifying that + // map is not constant on that set. for current_field_element in 0..101 { map_range.push( - test_map_to_curve.map_to_curve(F101::from( - current_field_element as u64, - )) - .unwrap(), + test_map_to_curve + .map_to_curve(F101::from(current_field_element as u64)) + .unwrap(), ); } diff --git a/ec/src/hashing/curve_maps/mod.rs b/ec/src/hashing/curve_maps/mod.rs index 64f67d864..2fc423986 100644 --- a/ec/src/hashing/curve_maps/mod.rs +++ b/ec/src/hashing/curve_maps/mod.rs @@ -5,7 +5,6 @@ pub mod wb; //// parity method on the Field elements based on [\[1\]] Section 4.1 //// which is used by multiple curve maps including Elligator2 and SWU -/// /// - [\[1\]] pub fn parity(element: &F) -> bool { element diff --git a/ec/src/hashing/curve_maps/swu/mod.rs b/ec/src/hashing/curve_maps/swu/mod.rs index 1cb430bc9..7c3a59ade 100644 --- a/ec/src/hashing/curve_maps/swu/mod.rs +++ b/ec/src/hashing/curve_maps/swu/mod.rs @@ -251,13 +251,9 @@ mod test { let mut map_range: Vec> = vec![]; for current_field_element in 0..127 { - let element = F127::from(current_field_element as u64); + let element = F127::from(current_field_element as u64); - map_range.push( - test_map_to_curve - .map_to_curve(element) - .unwrap(), - ); + map_range.push(test_map_to_curve.map_to_curve(element).unwrap()); } let mut counts = HashMap::new(); diff --git a/ec/src/scalar_mul/mod.rs b/ec/src/scalar_mul/mod.rs index 295597576..10f0f7109 100644 --- a/ec/src/scalar_mul/mod.rs +++ b/ec/src/scalar_mul/mod.rs @@ -4,8 +4,10 @@ pub mod wnaf; pub mod fixed_base; pub mod variable_base; -use crate::short_weierstrass::{Affine, Projective, SWCurveConfig}; -use crate::PrimeGroup; +use crate::{ + short_weierstrass::{Affine, Projective, SWCurveConfig}, + PrimeGroup, +}; use ark_ff::{AdditiveGroup, Zero}; use ark_std::{ ops::{Add, AddAssign, Mul, Neg, Sub, SubAssign}, From 17bf7a060c5adaf9560034882d37a48cf5f30460 Mon Sep 17 00:00:00 2001 From: Skalman Date: Thu, 5 Oct 2023 07:56:22 -0400 Subject: [PATCH 19/25] Move Elligator2 pre-computatable values to `Elligator2Config` --- ec/src/hashing/curve_maps/elligator2.rs | 30 +++++++++++++++++++------ 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/ec/src/hashing/curve_maps/elligator2.rs b/ec/src/hashing/curve_maps/elligator2.rs index 23d1b9698..5d925ebab 100644 --- a/ec/src/hashing/curve_maps/elligator2.rs +++ b/ec/src/hashing/curve_maps/elligator2.rs @@ -21,6 +21,12 @@ pub trait Elligator2Config: TECurveConfig + MontCurveConfig { /// non-square with lowest absolute value in the `BaseField` when its elements /// are represented as [-(q-1)/2, (q-1)/2] const Z: Self::BaseField; + + /// This must be equal to 1/(MontCurveConfig::COEFF_B)^2; + const ONE_OVER_COEFF_B_SQUARE: Self::BaseField; + + /// This must be equal to MontCurveConfig::COEFF_A/MontCurveConfig::COEFF_B; + const COEFF_A_OVER_COEFF_B: Self::BaseField; } /// Represents the Elligator2 hash-to-curve map defined by `P`. @@ -37,6 +43,21 @@ impl Elligator2Map

{ !P::Z.legendre().is_qr(), "Z should be a quadratic non-residue for the Elligator2 map" ); + + debug_assert_eq!( + P::ONE_OVER_COEFF_B_SQUARE, +

::COEFF_B + .square() + .inverse() + .expect("B coefficient cannot be zero in Montgomery form"), + "ONE_OVER_COEFF_B_SQUARE is not equal to 1/COEFF_B^2 in Montgomery form" + ); + + debug_assert_eq!( + P::COEFF_A_OVER_COEFF_B, +

::COEFF_A /

::COEFF_B, + "COEFF_A_OVER_COEFF_B is not equal to COEFF_A/COEFF_B in Montgomery form" + ); Ok(()) } } @@ -64,14 +85,9 @@ impl MapToCurve> for Elligator2Map

{ // ark a is irtf J // ark b is irtf k - let j =

::COEFF_A; let k =

::COEFF_B; - let j_on_k = j / k; - - let ksq_inv = k - .square() - .inverse() - .expect("B coefficient cannot be zero in Montgomery form"); + let j_on_k = P::COEFF_A_OVER_COEFF_B; + let ksq_inv = P::ONE_OVER_COEFF_B_SQUARE; let den_1 = ::one() + P::Z * element.square(); From 5380fda82b198372afbea8188d92032405bc9893 Mon Sep 17 00:00:00 2001 From: Skalman Date: Thu, 5 Oct 2023 07:58:17 -0400 Subject: [PATCH 20/25] Pre-computatable Elligator2 test example --- ec/src/hashing/curve_maps/elligator2.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/ec/src/hashing/curve_maps/elligator2.rs b/ec/src/hashing/curve_maps/elligator2.rs index 5d925ebab..5ca0dd624 100644 --- a/ec/src/hashing/curve_maps/elligator2.rs +++ b/ec/src/hashing/curve_maps/elligator2.rs @@ -236,8 +236,16 @@ mod test { /// sage: find_z_ell2(F101) /// 2 + /// sage: F101 = FiniteField(101) + /// sage: 1/F101("23")^2 + /// 80 + /// sage: F101("76")/F101("23") + /// 56 impl Elligator2Config for TestElligator2MapToCurveConfig { const Z: F101 = MontFp!("2"); + const ONE_OVER_COEFF_B_SQUARE: F101 = MontFp!("80"); + + const COEFF_A_OVER_COEFF_B: F101 = MontFp!("56"); } /// The point of the test is to get a simple twisted edwards curve and make From 140ac576a422b135a4e08fffaae0a86e4ca09036 Mon Sep 17 00:00:00 2001 From: Skalman Date: Thu, 5 Oct 2023 08:03:50 -0400 Subject: [PATCH 21/25] Move Elligator2 `MapToCurve` implementation to its own folder. --- ec/src/hashing/curve_maps/{elligator2.rs => elligator2/mod.rs} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename ec/src/hashing/curve_maps/{elligator2.rs => elligator2/mod.rs} (100%) diff --git a/ec/src/hashing/curve_maps/elligator2.rs b/ec/src/hashing/curve_maps/elligator2/mod.rs similarity index 100% rename from ec/src/hashing/curve_maps/elligator2.rs rename to ec/src/hashing/curve_maps/elligator2/mod.rs From 99abc40b2cf94b5a07edc166b0a8b3fec208c3fa Mon Sep 17 00:00:00 2001 From: Skalman Date: Thu, 5 Oct 2023 09:53:06 -0400 Subject: [PATCH 22/25] Fix reference to the Elligator paper --- ec/src/hashing/curve_maps/elligator2/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ec/src/hashing/curve_maps/elligator2/mod.rs b/ec/src/hashing/curve_maps/elligator2/mod.rs index 5ca0dd624..f5a7fcacf 100644 --- a/ec/src/hashing/curve_maps/elligator2/mod.rs +++ b/ec/src/hashing/curve_maps/elligator2/mod.rs @@ -9,8 +9,8 @@ use crate::{ /// Trait defining the necessary parameters for the Elligator2 hash-to-curve method /// for twisted edwards curves form of: -/// `b * y² = x³ + a * x² + x` from [\[WB2019\]] -/// according to [\[HSSWW23\]] +/// `b * y² = x³ + a * x² + x` +/// from [\[BHKL13\]], according to [\[HSSWW23\]] /// /// - [\[BHKL13\]] /// - [\[HSSWW23\]] From 4b770e137e0dfe50860ee46e37004d20ddf3e4df Mon Sep 17 00:00:00 2001 From: Skalman Date: Fri, 12 Jan 2024 08:42:23 -0500 Subject: [PATCH 23/25] Make elligator curve map a static object following revert of revert of #679 --- .../{elligator2/mod.rs => elligator2.rs} | 21 ++++++------------- 1 file changed, 6 insertions(+), 15 deletions(-) rename ec/src/hashing/curve_maps/{elligator2/mod.rs => elligator2.rs} (94%) diff --git a/ec/src/hashing/curve_maps/elligator2/mod.rs b/ec/src/hashing/curve_maps/elligator2.rs similarity index 94% rename from ec/src/hashing/curve_maps/elligator2/mod.rs rename to ec/src/hashing/curve_maps/elligator2.rs index f5a7fcacf..e94ccffde 100644 --- a/ec/src/hashing/curve_maps/elligator2/mod.rs +++ b/ec/src/hashing/curve_maps/elligator2.rs @@ -32,7 +32,7 @@ pub trait Elligator2Config: TECurveConfig + MontCurveConfig { /// Represents the Elligator2 hash-to-curve map defined by `P`. pub struct Elligator2Map(PhantomData P>); -impl Elligator2Map

{ +impl MapToCurve> for Elligator2Map

{ /// Checks if `P` represents a valid Elligator2 map. Panics otherwise. fn check_parameters() -> Result<(), HashToCurveError> { // We assume that the Montgomery curve is correct and as such we do @@ -60,18 +60,9 @@ impl Elligator2Map

{ ); Ok(()) } -} - -impl MapToCurve> for Elligator2Map

{ - fn new() -> Result { - // Checking validity `Elligator2Config` so we actually representing a valid Elligator2 map. - Self::check_parameters()?; - - Ok(Elligator2Map(PhantomData)) - } /// Map an arbitrary base field element `element` to a curve point. - fn map_to_curve(&self, element: P::BaseField) -> Result, HashToCurveError> { + fn map_to_curve(element: P::BaseField) -> Result, HashToCurveError> { // 1. x1 = -(J / K) * inv0(1 + Z * u^2) // 2. If x1 == 0, set x1 = -(J / K) // 3. gx1 = x1^3 + (J / K) * x1^2 + x1 / K^2 @@ -273,16 +264,16 @@ mod test { #[test] fn map_field_to_curve_elligator2() { Elligator2Map::::check_parameters().unwrap(); - let test_map_to_curve = Elligator2Map::::new().unwrap(); let mut map_range: Vec> = vec![]; // We are mapping all elemnts of the field to the curve, verifying that // map is not constant on that set. for current_field_element in 0..101 { map_range.push( - test_map_to_curve - .map_to_curve(F101::from(current_field_element as u64)) - .unwrap(), + Elligator2Map::::map_to_curve(F101::from( + current_field_element as u64, + )) + .unwrap(), ); } From 0eeb7cbe61cb2b6ec67f78914455a4a9a4445da0 Mon Sep 17 00:00:00 2001 From: Pratyush Mishra Date: Fri, 12 Jan 2024 13:59:17 -0800 Subject: [PATCH 24/25] Remove whitespace --- ec/src/hashing/curve_maps/elligator2.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/ec/src/hashing/curve_maps/elligator2.rs b/ec/src/hashing/curve_maps/elligator2.rs index e94ccffde..1ba7546d0 100644 --- a/ec/src/hashing/curve_maps/elligator2.rs +++ b/ec/src/hashing/curve_maps/elligator2.rs @@ -14,7 +14,6 @@ use crate::{ /// /// - [\[BHKL13\]] /// - [\[HSSWW23\]] - pub trait Elligator2Config: TECurveConfig + MontCurveConfig { /// An element of the base field that is not a square root see \[BHKL13, Section 5\]. /// When `BaseField` is a prime field, [\[HSSWW23\]] mandates that `Z` is the @@ -201,7 +200,6 @@ mod test { /// (5 : 36 : 1) /// Point_WeierstrassToEdwards(F101, 11, 5, F101(5), F101(36), a_given=-1, d_given=12) /// (23, 24) - impl TECurveConfig for TestElligator2MapToCurveConfig { /// COEFF_A = -1 const COEFF_A: F101 = MontFp!("-1"); From ed79f32256daa142b0ce01865e57bb42f8e1d6c9 Mon Sep 17 00:00:00 2001 From: Pratyush Mishra Date: Sat, 13 Jan 2024 15:40:21 -0800 Subject: [PATCH 25/25] Stricter check on SWU parameters Co-authored-by: Marcin --- ec/src/hashing/curve_maps/swu.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ec/src/hashing/curve_maps/swu.rs b/ec/src/hashing/curve_maps/swu.rs index c65366140..c2e22b9ff 100644 --- a/ec/src/hashing/curve_maps/swu.rs +++ b/ec/src/hashing/curve_maps/swu.rs @@ -28,7 +28,7 @@ impl MapToCurve> for SWUMap

{ fn check_parameters() -> Result<(), HashToCurveError> { // Verifying that ZETA is a non-square debug_assert!( - !P::ZETA.legendre().is_qr(), + P::ZETA.legendre().is_qnr(), "ZETA should be a quadratic non-residue for the SWU map" );