From 46c93e6fb1c451d6f7330e20a262ed6dacff5d3d Mon Sep 17 00:00:00 2001 From: Davide Galassi Date: Wed, 5 Jun 2024 17:27:02 +0200 Subject: [PATCH 1/5] Elligator2 hash to curve --- src/arkworks/elligator2.rs | 242 +++++++++++++++++++++++++++++++++++++ src/arkworks/mod.rs | 3 + src/lib.rs | 2 + src/utils.rs | 2 +- 4 files changed, 248 insertions(+), 1 deletion(-) create mode 100644 src/arkworks/elligator2.rs create mode 100644 src/arkworks/mod.rs diff --git a/src/arkworks/elligator2.rs b/src/arkworks/elligator2.rs new file mode 100644 index 0000000..40a9e62 --- /dev/null +++ b/src/arkworks/elligator2.rs @@ -0,0 +1,242 @@ +//! Elligator2 has been merged upstream, still we're waiting for new version on crates.io (v.0.4.3) +//! +//! Relevant PRs: +//! - Elligator2 hash-to-curve for Twisted Edwards curves: https://github.com/arkworks-rs/algebra/pull/659 +//! - Elligator2 hash-to-curve for Bandersnatch: https://github.com/arkworks-rs/algebra/pull/758 + +use ark_ec::{ + hashing::{ + // TODO: this looks identical to the one introduced by #659 + curve_maps::swu::parity, + map_to_curve_hasher::{MapToCurve, MapToCurveBasedHasher}, + HashToCurve, + HashToCurveError, + }, + twisted_edwards::{Affine, MontCurveConfig, Projective, TECurveConfig}, +}; +use ark_ff::{Field, One, Zero}; +use core::marker::PhantomData; + +/// 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 [\[BHKL13\]], according to [\[HSSWW23\]] +/// +/// - [\[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 + /// 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`. +pub struct Elligator2Map(PhantomData P>); + +impl Elligator2Map

{ + /// Checks if `P` represents a valid Elligator2 map. Panics otherwise. + fn check_parameters() -> Result<(), HashToCurveError> { + // TODO + Ok(()) + } +} + +impl MapToCurve> for Elligator2Map

{ + fn new() -> Result { + Self::check_parameters()?; + Ok(Elligator2Map(PhantomData)) + } + + fn map_to_curve( + &self, + element: as ark_ec::CurveGroup>::BaseField, + ) -> Result< as ark_ec::CurveGroup>::Affine, ark_ec::hashing::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 k =

::COEFF_B; + 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(); + + let x1 = -j_on_k + / (if den_1.is_zero() { + ::one() + } else { + den_1 + }); + let x1sq = x1.square(); + let x1cb = x1sq * x1; + let gx1 = x1cb + j_on_k * x1sq + x1 * ksq_inv; + + 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, mut y, sgn0) = if gx1.legendre().is_qr() { + ( + x1, + gx1.sqrt() + .expect("We have checked that gx1 is a quadratic residue. Q.E.D"), + true, + ) + } else { + ( + x2, + gx2.sqrt() + .expect("gx2 is a quadratic residue because gx1 is not. Q.E.D"), + false, + ) + }; + + if parity(&y) != sgn0 { + y = -y; + } + + let s = x * k; + 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, + // 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 + + 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) + }; + + let point_on_curve = Affine::

::new_unchecked(v, w); + debug_assert!( + point_on_curve.is_on_curve(), + "Elligator2 mapped to a point off the curve" + ); + Ok(point_on_curve) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use ark_ff::{field_hashers::DefaultFieldHasher, Fp64, MontBackend, MontFp}; + 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 ark_ec::CurveConfig for TestElligator2MapToCurveConfig { + const COFACTOR: &'static [u64] = &[8]; + 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 + /// 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"); + } + + #[test] + fn hash_arbitary_string_to_curve_elligator2() { + let hasher = MapToCurveBasedHasher::< + Projective, + DefaultFieldHasher, + Elligator2Map, + >::new(b"domain") + .unwrap(); + + let point = hasher + .hash(b"foo") + .expect("fail to hash the string to curve"); + assert!(point.is_on_curve(),); + } +} diff --git a/src/arkworks/mod.rs b/src/arkworks/mod.rs new file mode 100644 index 0000000..1f1e0eb --- /dev/null +++ b/src/arkworks/mod.rs @@ -0,0 +1,3 @@ +//! Features expected to land into Arkworks at some point in the future + +pub mod elligator2; diff --git a/src/lib.rs b/src/lib.rs index c95c85e..e6090d8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -31,6 +31,8 @@ pub mod utils; #[cfg(feature = "ring")] pub mod ring; +mod arkworks; + #[cfg(test)] mod testing; diff --git a/src/utils.rs b/src/utils.rs index 3362853..36900e3 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -245,7 +245,7 @@ pub(crate) mod ark_next { let v = v_w_num * v_denom_inv; let w = v_w_num * w_denom_inv; - // Mapp Montgamory to SW: ((x+A/3)/B,y/B) + // Map Montgamory to SW: ((x+A/3)/B,y/B) let x = C::MONT_B_INV * (v + C::MONT_A_OVER_THREE); let y = C::MONT_B_INV * w; From 3aff0238ed65d3accfed3ed4da15969602e54229 Mon Sep 17 00:00:00 2001 From: Davide Galassi Date: Wed, 5 Jun 2024 17:27:18 +0200 Subject: [PATCH 2/5] Elligator2 hash to curve for Bandersnatch --- src/suites/bandersnatch.rs | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/src/suites/bandersnatch.rs b/src/suites/bandersnatch.rs index a7f7770..9db9a98 100644 --- a/src/suites/bandersnatch.rs +++ b/src/suites/bandersnatch.rs @@ -146,6 +146,30 @@ pub mod edwards { }; } + impl arkworks::elligator2::Elligator2Config + for ark_ed_on_bls12_381_bandersnatch::BandersnatchConfig + { + const Z: ark_ed_on_bls12_381_bandersnatch::Fq = MontFp!("5"); + + /// This must be equal to 1/(MontCurveConfig::COEFF_B)^2; + const ONE_OVER_COEFF_B_SQUARE: ark_ed_on_bls12_381_bandersnatch::Fq = MontFp!( + "35484827650731063748396669747216844996598387089274032563585525486049249153249" + ); + + /// This must be equal to MontCurveConfig::COEFF_A/MontCurveConfig::COEFF_B; + const COEFF_A_OVER_COEFF_B: ark_ed_on_bls12_381_bandersnatch::Fq = MontFp!( + "22511181562295907836254750456843438087744031914659733450388350895537307167857" + ); + } + + use ark_ec::hashing::HashToCurve; + + pub type Elligator2MapToCurve = ark_ec::hashing::map_to_curve_hasher::MapToCurveBasedHasher< + ark_ec::twisted_edwards::Projective, + ark_ff::field_hashers::DefaultFieldHasher, + arkworks::elligator2::Elligator2Map, + >; + #[cfg(feature = "ring")] mod ring_defs { use super::*; @@ -179,6 +203,14 @@ pub mod edwards { #[cfg(test)] suite_tests!(BandersnatchSha512Edwards, true); + + #[test] + fn test_elligator2_hash_to_curve() { + let hasher = Elligator2MapToCurve::new(b"dom").unwrap(); + let point = hasher.hash(b"foo").unwrap(); + assert!(point.is_on_curve()); + assert!(point.is_in_correct_subgroup_assuming_on_curve()); + } } // sage: q = 52435875175126190479447740508185965837690552500527637822603658699938581184513 From ace3e25aa572ac27821505c9782ec33a98c8c546 Mon Sep 17 00:00:00 2001 From: Davide Galassi Date: Thu, 6 Jun 2024 15:45:24 +0200 Subject: [PATCH 3/5] Implement Ell2 as general utility --- src/arkworks/elligator2.rs | 4 ++-- src/suites/bandersnatch.rs | 25 +++++++++++++++---------- src/utils.rs | 29 +++++++++++++++++++++++++++-- 3 files changed, 44 insertions(+), 14 deletions(-) diff --git a/src/arkworks/elligator2.rs b/src/arkworks/elligator2.rs index 40a9e62..50147ab 100644 --- a/src/arkworks/elligator2.rs +++ b/src/arkworks/elligator2.rs @@ -8,8 +8,7 @@ use ark_ec::{ hashing::{ // TODO: this looks identical to the one introduced by #659 curve_maps::swu::parity, - map_to_curve_hasher::{MapToCurve, MapToCurveBasedHasher}, - HashToCurve, + map_to_curve_hasher::MapToCurve, HashToCurveError, }, twisted_edwards::{Affine, MontCurveConfig, Projective, TECurveConfig}, @@ -148,6 +147,7 @@ impl MapToCurve> for Elligator2Map

{ #[cfg(test)] mod tests { use super::*; + use ark_ec::hashing::{map_to_curve_hasher::MapToCurveBasedHasher, HashToCurve}; use ark_ff::{field_hashers::DefaultFieldHasher, Fp64, MontBackend, MontFp}; use sha2::Sha256; diff --git a/src/suites/bandersnatch.rs b/src/suites/bandersnatch.rs index 9db9a98..7cee30a 100644 --- a/src/suites/bandersnatch.rs +++ b/src/suites/bandersnatch.rs @@ -162,14 +162,6 @@ pub mod edwards { ); } - use ark_ec::hashing::HashToCurve; - - pub type Elligator2MapToCurve = ark_ec::hashing::map_to_curve_hasher::MapToCurveBasedHasher< - ark_ec::twisted_edwards::Projective, - ark_ff::field_hashers::DefaultFieldHasher, - arkworks::elligator2::Elligator2Map, - >; - #[cfg(feature = "ring")] mod ring_defs { use super::*; @@ -206,10 +198,23 @@ pub mod edwards { #[test] fn test_elligator2_hash_to_curve() { - let hasher = Elligator2MapToCurve::new(b"dom").unwrap(); - let point = hasher.hash(b"foo").unwrap(); + let point = + utils::hash_to_curve_ell2_rfc_9380::(b"foo").unwrap(); assert!(point.is_on_curve()); assert!(point.is_in_correct_subgroup_assuming_on_curve()); + + { + use ietf::{Prover, Verifier}; + + let secret = Secret::from_seed(b"asd"); + let public = secret.public(); + let input = Input::from(point); + let output = secret.output(input); + + let proof = secret.prove(input, output, b"foo"); + let result = public.verify(input, output, b"foo", &proof); + assert!(result.is_ok()); + } } } diff --git a/src/utils.rs b/src/utils.rs index 36900e3..ec7e16b 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -1,7 +1,8 @@ use crate::{AffinePoint, HashOutput, ScalarField, Suite}; +use ark_ec::AffineRepr; use ark_ff::PrimeField; -use digest::Digest; +use digest::{Digest, FixedOutputReset}; #[cfg(not(feature = "std"))] use ark_std::vec::Vec; @@ -73,7 +74,9 @@ pub fn hash_to_curve_tai_rfc_9381( const DOM_SEP_FRONT: u8 = 0x01; const DOM_SEP_BACK: u8 = 0x00; - let mod_size = <<::BaseField as Field>::BasePrimeField as PrimeField>::MODULUS_BIT_SIZE as usize / 8; + let mod_size = < as Field>::BasePrimeField as PrimeField>::MODULUS_BIT_SIZE + as usize + / 8; if S::Hasher::output_size() < mod_size { return None; } @@ -100,6 +103,28 @@ pub fn hash_to_curve_tai_rfc_9381( None } +pub fn hash_to_curve_ell2_rfc_9380(data: &[u8]) -> Option> +where + ::Hasher: Default + Clone + FixedOutputReset + 'static, + crate::CurveConfig: ark_ec::twisted_edwards::TECurveConfig, + crate::CurveConfig: crate::arkworks::elligator2::Elligator2Config, + crate::arkworks::elligator2::Elligator2Map>: + ark_ec::hashing::map_to_curve_hasher::MapToCurve< as AffineRepr>::Group>, +{ + use ark_ec::hashing::HashToCurve; + const SEC_PARAM: usize = 128; + + let hasher = ark_ec::hashing::map_to_curve_hasher::MapToCurveBasedHasher::< + as AffineRepr>::Group, + ark_ff::field_hashers::DefaultFieldHasher<::Hasher, SEC_PARAM>, + crate::arkworks::elligator2::Elligator2Map>, + >::new(b"") + .ok()?; + + let res = hasher.hash(data).ok()?; + Some(res) +} + /// Challenge generation according to RFC 9381 section 5.4.3. pub fn challenge_rfc_9381(pts: &[&AffinePoint], ad: &[u8]) -> ScalarField { const DOM_SEP_START: u8 = 0x02; From 47cfccf430615793ffa5b70fbee92904335c562c Mon Sep 17 00:00:00 2001 From: Davide Galassi Date: Thu, 6 Jun 2024 15:49:07 +0200 Subject: [PATCH 4/5] Check ELL2 params --- src/arkworks/elligator2.rs | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/src/arkworks/elligator2.rs b/src/arkworks/elligator2.rs index 50147ab..77b25e9 100644 --- a/src/arkworks/elligator2.rs +++ b/src/arkworks/elligator2.rs @@ -43,7 +43,29 @@ pub struct Elligator2Map(PhantomData P>); impl Elligator2Map

{ /// Checks if `P` represents a valid Elligator2 map. Panics otherwise. fn check_parameters() -> Result<(), HashToCurveError> { - // TODO + // 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" + ); + + 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(()) } } From 91101ed38a0988c27d1c7af409ac8585124efa76 Mon Sep 17 00:00:00 2001 From: Davide Galassi Date: Thu, 6 Jun 2024 16:59:09 +0200 Subject: [PATCH 5/5] Use correct h2c string --- src/arkworks/elligator2.rs | 2 +- src/suites/bandersnatch.rs | 76 ++++++++++++++++++-------------------- src/utils.rs | 26 +++++++++++-- 3 files changed, 58 insertions(+), 46 deletions(-) diff --git a/src/arkworks/elligator2.rs b/src/arkworks/elligator2.rs index 77b25e9..d88f65b 100644 --- a/src/arkworks/elligator2.rs +++ b/src/arkworks/elligator2.rs @@ -248,7 +248,7 @@ mod tests { } #[test] - fn hash_arbitary_string_to_curve_elligator2() { + fn elligator2_works() { let hasher = MapToCurveBasedHasher::< Projective, DefaultFieldHasher, diff --git a/src/suites/bandersnatch.rs b/src/suites/bandersnatch.rs index 7cee30a..6f5b640 100644 --- a/src/suites/bandersnatch.rs +++ b/src/suites/bandersnatch.rs @@ -1,4 +1,4 @@ -//! `ECVRF-BANDERSNATCH-BLAKE2-TAI` suite. +//! `ECVRF-BANDERSNATCH-SHA512-ELL2` suite. //! //! Configuration: //! @@ -41,7 +41,7 @@ //! [RFC6234](https://www.rfc-editor.org/rfc/rfc6234), with hLen = 64. //! //! * The ECVRF_encode_to_curve function is as specified in -//! Section 5.4.1.2, with `h2c_suite_ID_string` = `"BANDERSNATCH_XMD:BLAKE2b_ELL2_RO_"`. +//! Section 5.4.1.2, with `h2c_suite_ID_string` = `"Bandersnatch_XMD:SHA-512_ELL2_RO_"`. //! The suite is defined in Section 8.5 of [RFC9380](https://datatracker.ietf.org/doc/rfc9380/). //! //! * The prime subgroup generator is generated following Zcash's fashion: @@ -60,11 +60,11 @@ pub mod weierstrass { use super::*; #[derive(Debug, Copy, Clone)] - pub struct BandersnatchSha512; + pub struct BandersnatchSha512Tai; - suite_types!(BandersnatchSha512); + suite_types!(BandersnatchSha512Tai); - impl Suite for BandersnatchSha512 { + impl Suite for BandersnatchSha512Tai { const SUITE_ID: &'static [u8] = b"bandersnatch-sha512-tai-sw"; const CHALLENGE_LEN: usize = 32; @@ -72,7 +72,7 @@ pub mod weierstrass { type Hasher = sha2::Sha512; } - impl PedersenSuite for BandersnatchSha512 { + impl PedersenSuite for BandersnatchSha512Tai { const BLINDING_BASE: AffinePoint = { const X: BaseField = MontFp!( "4956610287995045830459834427365747411162584416641336688940534788579455781570" @@ -89,13 +89,13 @@ pub mod weierstrass { use super::*; use crate::ring as ring_suite; - pub type RingContext = ring_suite::RingContext; - pub type VerifierKey = ring_suite::VerifierKey; - pub type RingProver = ring_suite::RingProver; - pub type RingVerifier = ring_suite::RingVerifier; - pub type Proof = ring_suite::Proof; + pub type RingContext = ring_suite::RingContext; + pub type VerifierKey = ring_suite::VerifierKey; + pub type RingProver = ring_suite::RingProver; + pub type RingVerifier = ring_suite::RingVerifier; + pub type Proof = ring_suite::Proof; - impl ring_suite::RingSuite for BandersnatchSha512 { + impl ring_suite::RingSuite for BandersnatchSha512Tai { type Pairing = ark_bls12_381::Bls12_381; /// A point on the curve not belonging to the prime order subgroup. @@ -114,26 +114,34 @@ pub mod weierstrass { pub use ring_defs::*; #[cfg(test)] - suite_tests!(BandersnatchSha512, true); + suite_tests!(BandersnatchSha512Tai, true); } pub mod edwards { use super::*; #[derive(Debug, Copy, Clone)] - pub struct BandersnatchSha512Edwards; + pub struct BandersnatchSha512Ell2; - suite_types!(BandersnatchSha512Edwards); + suite_types!(BandersnatchSha512Ell2); - impl Suite for BandersnatchSha512Edwards { - const SUITE_ID: &'static [u8] = b"bandersnatch-sha512-tai-te"; + impl Suite for BandersnatchSha512Ell2 { + const SUITE_ID: &'static [u8] = b"bandersnatch-sha512-ell2-ed"; const CHALLENGE_LEN: usize = 32; type Affine = ark_ed_on_bls12_381_bandersnatch::EdwardsAffine; type Hasher = sha2::Sha512; + + /// Hash data to a curve point using Elligator2 method described by RFC 9380. + fn data_to_point(data: &[u8]) -> Option { + // "XMD" for expand_message_xmd (Section 5.3.1). + // "RO" for random oracle (Section 3 - hash_to_curve method) + let h2c_suite_id = b"bandersnatch_XMD:SHA-512_ELL2_RO_"; + utils::hash_to_curve_ell2_rfc_9380::(data, h2c_suite_id) + } } - impl PedersenSuite for BandersnatchSha512Edwards { + impl PedersenSuite for BandersnatchSha512Ell2 { /// Found mapping the `BLINDING_BASE` of `weierstrass` module using the `utils::map_sw_to_te` const BLINDING_BASE: AffinePoint = { const X: BaseField = MontFp!( @@ -167,13 +175,13 @@ pub mod edwards { use super::*; use crate::ring as ring_suite; - pub type RingContext = ring_suite::RingContext; - pub type VerifierKey = ring_suite::VerifierKey; - pub type RingProver = ring_suite::RingProver; - pub type RingVerifier = ring_suite::RingVerifier; - pub type Proof = ring_suite::Proof; + pub type RingContext = ring_suite::RingContext; + pub type VerifierKey = ring_suite::VerifierKey; + pub type RingProver = ring_suite::RingProver; + pub type RingVerifier = ring_suite::RingVerifier; + pub type Proof = ring_suite::Proof; - impl ring_suite::RingSuite for BandersnatchSha512Edwards { + impl ring_suite::RingSuite for BandersnatchSha512Ell2 { type Pairing = ark_bls12_381::Bls12_381; /// A point on the curve not belonging to the prime order subgroup. @@ -194,27 +202,13 @@ pub mod edwards { pub use ring_defs::*; #[cfg(test)] - suite_tests!(BandersnatchSha512Edwards, true); + suite_tests!(BandersnatchSha512Ell2, true); #[test] - fn test_elligator2_hash_to_curve() { - let point = - utils::hash_to_curve_ell2_rfc_9380::(b"foo").unwrap(); + fn elligator2_hash_to_curve() { + let point = BandersnatchSha512Ell2::data_to_point(b"foo").unwrap(); assert!(point.is_on_curve()); assert!(point.is_in_correct_subgroup_assuming_on_curve()); - - { - use ietf::{Prover, Verifier}; - - let secret = Secret::from_seed(b"asd"); - let public = secret.public(); - let input = Input::from(point); - let output = secret.output(input); - - let proof = secret.prove(input, output, b"foo"); - let result = public.verify(input, output, b"foo", &proof); - assert!(result.is_ok()); - } } } diff --git a/src/utils.rs b/src/utils.rs index ec7e16b..c44ab43 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -51,7 +51,7 @@ pub(crate) fn hmac(sk: &[u8], data: .to_vec() } -/// Try-And-Increment (TAI) method as defined by RFC9381 section 5.4.1.1. +/// Try-And-Increment (TAI) method as defined by RFC 9381 section 5.4.1.1. /// /// Implements ECVRF_encode_to_curve in a simple and generic way that works /// for any elliptic curve. @@ -62,7 +62,7 @@ pub(crate) fn hmac(sk: &[u8], data: /// ciphersuites specified in Section 5.5, this algorithm is expected to /// find a valid curve point after approximately two attempts on average. /// -/// The input `data` is defined to be `salt || alpha` according to the spec. +/// The input `data` is defined to be `salt || alpha` according to the RFC 9281. pub fn hash_to_curve_tai_rfc_9381( data: &[u8], point_be_encoding: bool, @@ -103,7 +103,17 @@ pub fn hash_to_curve_tai_rfc_9381( None } -pub fn hash_to_curve_ell2_rfc_9380(data: &[u8]) -> Option> +/// Elligator2 method as defined by RFC 9380 and further refined in RFC 9381 section 5.4.1.2. +/// +/// Implements ECVRF_encode_to_curve using one of the several hash-to-curve options defined +/// in [RFC9380]. The specific choice of the hash-to-curve option (called the Suite ID in [RFC9380]) +/// is given by the h2c_suite_ID_string parameter. +/// +/// The input `data` is defined to be `salt || alpha` according to the RFC 9281. +pub fn hash_to_curve_ell2_rfc_9380( + data: &[u8], + h2c_suite_id: &[u8], +) -> Option> where ::Hasher: Default + Clone + FixedOutputReset + 'static, crate::CurveConfig: ark_ec::twisted_edwards::TECurveConfig, @@ -114,11 +124,19 @@ where use ark_ec::hashing::HashToCurve; const SEC_PARAM: usize = 128; + // Domain Separation Tag := "ECVRF_" || h2c_suite_ID_string || suite_string + let dst: Vec<_> = b"ECVRF_" + .iter() + .chain(h2c_suite_id.iter()) + .chain(S::SUITE_ID) + .cloned() + .collect(); + let hasher = ark_ec::hashing::map_to_curve_hasher::MapToCurveBasedHasher::< as AffineRepr>::Group, ark_ff::field_hashers::DefaultFieldHasher<::Hasher, SEC_PARAM>, crate::arkworks::elligator2::Elligator2Map>, - >::new(b"") + >::new(&dst) .ok()?; let res = hasher.hash(data).ok()?;