diff --git a/CHANGELOG.md b/CHANGELOG.md index bc2472ee2..0b9160f76 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ - [\#772](https://github.com/arkworks-rs/algebra/pull/772) (`ark-ff`) Implementation of `mul` method for `BigInteger`. - [\#794](https://github.com/arkworks-rs/algebra/pull/794) (`ark-ff`) Fix `wasm` compilation. +- [\#XXX](https://github.com/arkworks-rs/algebra/pull/XXX) (`ark-curves`) Implement TE <-> SW map for Bandersnatch. ### Breaking changes 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 a2dcc1a4e..d3d2c0503 100644 --- a/curves/ed_on_bls12_381_bandersnatch/src/curves/mod.rs +++ b/curves/ed_on_bls12_381_bandersnatch/src/curves/mod.rs @@ -1,12 +1,11 @@ +use crate::{Fq, Fr}; use ark_ec::{ hashing::curve_maps::elligator2::Elligator2Config, models::CurveConfig, short_weierstrass::{self, SWCurveConfig}, twisted_edwards::{Affine, MontCurveConfig, Projective, TECurveConfig}, }; -use ark_ff::{AdditiveGroup, MontFp}; - -use crate::{Fq, Fr}; +use ark_ff::{AdditiveGroup, Field, MontFp, One}; #[cfg(test)] mod tests; @@ -163,6 +162,77 @@ impl Elligator2Config for BandersnatchConfig { MontFp!("22511181562295907836254750456843438087744031914659733450388350895537307167857"); } +// Constants used in mapping TE form to SW form and vice versa +// +// sage: q = 52435875175126190479447740508185965837690552500527637822603658699938581184513 +// sage: Fq = GF(q) +// sage: MONT_A = 29978822694968839326280996386011761570173833766074948509196803838190355340952 +// sage: MONT_B = 25465760566081946422412445027709227188579564747101592991722834452325077642517 +// sage: MONT_A/Fq(3) +// 9992940898322946442093665462003920523391277922024982836398934612730118446984 +// sage: Fq(1)/MONT_B +// 41180284393978236561320365279764246793818536543197771097409483252169927600582 +const MONT_A_OVER_THREE: Fq = + MontFp!("9992940898322946442093665462003920523391277922024982836398934612730118446984"); +const MONT_B_INV: Fq = + MontFp!("41180284393978236561320365279764246793818536543197771097409483252169927600582"); + +impl BandersnatchConfig { + /// Map an point in TE form to its corresponding point on SW curve + pub fn map_te_to_sw(point: EdwardsAffine) -> Option { + //First we map the point from the TE to Montgamory + //edward to mont ((1+y)/(1-y), (1+y)/(x(1-y))) + let v_denom = <::BaseField as One>::one() - point.y; + let w_denom = point.x - point.x * point.y; + + let v_denom_inv = v_denom.inverse()?; + let w_denom_inv = w_denom.inverse()?; + + let v_w_num = <::BaseField as One>::one() + point.y; + + let v = v_w_num * v_denom_inv; + let w = v_w_num * w_denom_inv; + + //now we are mappyng the montgamory point to SW. + //Mont->SW ((x+A/3)/B,y/B) + + let x = MONT_B_INV * (v + MONT_A_OVER_THREE); + let y = MONT_B_INV * w; + + let point_on_sw_curve = SWAffine::new_unchecked(x, y); + debug_assert!( + point_on_sw_curve.is_on_curve(), + "TE point mapped off the SW curve" + ); + + return Some(point_on_sw_curve); + } + + pub fn map_sw_to_te(point: SWAffine) -> Option { + //first we are mappyng the sw point to montgamory point + //SW->Mont by (Bx−A/3,By) + let mx = ::COEFF_B * point.x - MONT_A_OVER_THREE; + let my = ::COEFF_B * point.y; + + //Then we map the TE point to Montgamory + // (x,y)↦(x/y,(x−1)/(x+1)) + let v_denom = my.inverse()?; + let x_p_1 = mx + <::BaseField as One>::one(); + let w_denom = x_p_1.inverse()?; + + let v = mx * v_denom; + let w = (mx - <::BaseField as One>::one()) * w_denom; + + let point_on_te_curve = EdwardsAffine::new_unchecked(v, w); + debug_assert!( + point_on_te_curve.is_on_curve(), + "TE point mapped off the SW curve" + ); + + return Some(point_on_te_curve); + } +} + #[cfg(test)] mod test { use super::*; @@ -189,4 +259,64 @@ mod test { "hash results into a point off the curve" ); } + + #[test] + fn test_mapping_from_te_to_sw_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" + ); + + assert!( + BandersnatchConfig::map_te_to_sw(hash_result) + .expect("could not map the te point to sw form") + .is_on_curve(), + "hash results into a point off the curve" + ); + } + + #[test] + fn test_mapping_from_te_to_sw_to_te_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" + ); + + let sw_point = BandersnatchConfig::map_te_to_sw(hash_result) + .expect("could not map the te point to sw form"); + assert!( + sw_point.is_on_curve(), + "hash results into a point off the curve" + ); + + let te_point = BandersnatchConfig::map_sw_to_te(sw_point) + .expect("could not map the te point to sw form"); + assert!( + te_point.is_on_curve(), + "hash results into a point off the curve" + ); + + assert!( + te_point == hash_result, + "point mapped to sw form was re-mapped to a different point on te form" + ); + } }