diff --git a/CHANGELOG.md b/CHANGELOG.md index 088d4c97c..f72363d2e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,7 @@ ### Features +- [\#758](https://github.com/arkworks-rs/algebra/pull/758) Implement Elligator2 hash-to-curve parameters for Bandersnatch. - [\#659](https://github.com/arkworks-rs/algebra/pull/659) (`ark-ec`) Add Elligator2 hash-to-curve map. - [\#689](https://github.com/arkworks-rs/algebra/pull/689) (`ark-serialize`) Add `CanonicalSerialize` and `CanonicalDeserialize` impls for `VecDeque` and `LinkedList`. - [\#691](https://github.com/arkworks-rs/algebra/pull/691) (`ark-poly`) Implement `Polynomial` for `SparseMultilinearExtension` and `DenseMultilinearExtension`. diff --git a/curves/ed_on_bls12_381_bandersnatch/Cargo.toml b/curves/ed_on_bls12_381_bandersnatch/Cargo.toml index d13a608e7..eb3c41db4 100644 --- a/curves/ed_on_bls12_381_bandersnatch/Cargo.toml +++ b/curves/ed_on_bls12_381_bandersnatch/Cargo.toml @@ -22,6 +22,7 @@ ark-relations = { version = "0.4.0", default-features = false } ark-serialize = { version = "0.4.0", default-features = false } ark-algebra-test-templates = { version = "0.4.0", default-features = false } ark-curve-constraint-tests = { path = "../curve-constraint-tests", default-features = false } +sha2 = { version = "0.10", default-features = false } [features] default = [] diff --git a/curves/ed_on_bls12_381_bandersnatch/src/curves/mod.rs b/curves/ed_on_bls12_381_bandersnatch/src/curves/mod.rs index 819f54669..a2dcc1a4e 100644 --- a/curves/ed_on_bls12_381_bandersnatch/src/curves/mod.rs +++ b/curves/ed_on_bls12_381_bandersnatch/src/curves/mod.rs @@ -1,4 +1,5 @@ use ark_ec::{ + hashing::curve_maps::elligator2::Elligator2Config, models::CurveConfig, short_weierstrass::{self, SWCurveConfig}, twisted_edwards::{Affine, MontCurveConfig, Projective, TECurveConfig}, @@ -137,3 +138,55 @@ impl SWCurveConfig for BandersnatchConfig { /// generators const GENERATOR: SWAffine = SWAffine::new_unchecked(SW_GENERATOR_X, SW_GENERATOR_Y); } + +// Elligator hash to curve Bandersnatch +// sage: find_z_ell2(GF(52435875175126190479447740508185965837690552500527637822603658699938581184513)) +// 5 +// +// sage: Fq = GF(52435875175126190479447740508185965837690552500527637822603658699938581184513) +// sage: 1/Fq(25465760566081946422412445027709227188579564747101592991722834452325077642517)^2 +// sage: COEFF_A = Fq(29978822694968839326280996386011761570173833766074948509196803838190355340952) +// sage: COEFF_B = Fq(25465760566081946422412445027709227188579564747101592991722834452325077642517) +// sage: 1/COEFF_B^2 +// 35484827650731063748396669747216844996598387089274032563585525486049249153249 +// sage: COEFF_A/COEFF_B +// 22511181562295907836254750456843438087744031914659733450388350895537307167857 +impl Elligator2Config for BandersnatchConfig { + const Z: Fq = MontFp!("5"); + + /// This must be equal to 1/(MontCurveConfig::COEFF_B)^2; + const ONE_OVER_COEFF_B_SQUARE: Fq = + MontFp!("35484827650731063748396669747216844996598387089274032563585525486049249153249"); + + /// This must be equal to MontCurveConfig::COEFF_A/MontCurveConfig::COEFF_B; + const COEFF_A_OVER_COEFF_B: Fq = + MontFp!("22511181562295907836254750456843438087744031914659733450388350895537307167857"); +} + +#[cfg(test)] +mod test { + use super::*; + use ark_ec::hashing::{ + curve_maps::elligator2::Elligator2Map, map_to_curve_hasher::MapToCurveBasedHasher, + HashToCurve, + }; + use ark_ff::field_hashers::DefaultFieldHasher; + use sha2::Sha512; + + #[test] + fn test_elligtor2_hash2curve_hashes_to_curve() { + 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" + ); + } +} diff --git a/ec/src/hashing/curve_maps/curve_map_parameter_helper.sage b/ec/src/hashing/curve_maps/curve_map_parameter_helper.sage new file mode 100644 index 000000000..f9331eb22 --- /dev/null +++ b/ec/src/hashing/curve_maps/curve_map_parameter_helper.sage @@ -0,0 +1,39 @@ +# Arguments: +# - F, a field object, e.g., F = GF(2^521 - 1) +# - A and B, the coefficients of the curve y^2 = x^3 + A * x + B +def find_z_sswu(F, A, B): + R. = F[] + # Polynomial ring over F + g = xx^3 + F(A) * xx + F(B) + # y^2 = g(x) = x^3 + A * x + B + ctr = F.gen() + while True: + for Z_cand in (F(ctr), F(-ctr)): + # Criterion 1: Z is non-square in F. + if is_square(Z_cand): + continue + # Criterion 2: Z != -1 in F. + if Z_cand == F(-1): + continue + # Criterion 3: g(x) - Z is irreducible over F. + if not (g - Z_cand).is_irreducible(): + continue + # Criterion 4: g(B / (Z * A)) is square in F. + if is_square(g(B / (Z_cand * A))): + return Z_cand + ctr += 1 + +# Finds the smallest z in term of non-zero bit +# in sage representation for consturcting +# elligator2 map for a curve defined over field F. +# Argument: +# - F, a field object, e.g., F = GF(2^255 - 19) +def find_z_ell2(F): + ctr = F.gen() + while True: + for Z_cand in (F(ctr), F(-ctr)): + # Z must be a non-square in F. + if is_square(Z_cand): + continue + return Z_cand + ctr += 1