diff --git a/ec/src/hashing/curve_maps/swu/mod.rs b/ec/src/hashing/curve_maps/swu/mod.rs index 0245ffab3..250321cb6 100644 --- a/ec/src/hashing/curve_maps/swu/mod.rs +++ b/ec/src/hashing/curve_maps/swu/mod.rs @@ -15,14 +15,10 @@ use crate::{ /// - [\[WB2019\]] pub trait SWUParams: SWCurveConfig { /// An element of the base field that is not a square root see \[WB2019, Section 4\]. - /// It is also convenient to have $g(b/xi * a)$ to be square. In general - /// we use a `XI` with low absolute value coefficients when they are + /// It is also convenient to have $g(b/ZETA * a)$ to be square. In general + /// we use a `ZETA` with low absolute value coefficients when they are /// represented as integers. - const XI: Self::BaseField; - /// An arbitrary nonsquare conveniently chosen to be a primitive element of the base field const ZETA: Self::BaseField; - /// Square root of `THETA = Self::XI/Self::ZETA`. - const XI_ON_ZETA_SQRT: Self::BaseField; } /// Represents the SWU hash-to-curve map defined by `P`. @@ -43,31 +39,13 @@ pub fn parity(element: &F) -> bool { impl MapToCurve> for SWUMap

{ /// Constructs a new map if `P` represents a valid map. fn new() -> Result { - // Verifying that both XI and ZETA are non-squares - if P::XI.legendre().is_qr() || P::ZETA.legendre().is_qr() { + // Verifying that ZETA is a non-square + if P::ZETA.legendre().is_qr() { return Err(HashToCurveError::MapToCurveError( - "both xi and zeta should be quadratic non-residues for the SWU map".to_string(), + "ZETA should be a quadratic non-residue for the SWU map".to_string(), )); } - // Verifying precomupted values - let xi_on_zeta = P::XI / P::ZETA; - match xi_on_zeta.sqrt() { - Some(xi_on_zeta_sqrt) => { - if xi_on_zeta_sqrt != P::XI_ON_ZETA_SQRT && xi_on_zeta_sqrt != -P::XI_ON_ZETA_SQRT { - return Err(HashToCurveError::MapToCurveError( - "precomputed P::XI_ON_ZETA_SQRT is not what it is supposed to be" - .to_string(), - )); - } - }, - None => { - panic!( - "`xi_on_zeta` was expected to have a sqrt, since the numerator and denominator are non-residues and Legendre symbol is multiplicative. Q.E.D" - ); - }, - } - // 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())); @@ -105,40 +83,41 @@ impl MapToCurve> for SWUMap

{ // gx1 = num_gx1/div_gx1 = [num_x1^3 + A * num_x1 * div^2 + B * div^3] / div^3 let a = P::COEFF_A; let b = P::COEFF_B; - let xi_t2 = P::XI * point.square(); - let ta = xi_t2.square() + xi_t2; + + let zeta_u2 = P::ZETA * point.square(); + let ta = zeta_u2.square() + zeta_u2; let num_x1 = b * (ta + ::one()); - let div = a * if ta.is_zero() { P::XI } else { -ta }; + let div = a * if ta.is_zero() { P::ZETA } else { -ta }; + let num2_x1 = num_x1.square(); let div2 = div.square(); let div3 = div2 * div; let num_gx1 = (num2_x1 + a * div2) * num_x1 + b * div3; // 5. x2 = Z * u^2 * x1 - let num_x2 = xi_t2 * num_x1; // same div + let num_x2 = zeta_u2 * num_x1; // same div // 6. gx2 = x2^3 + A * x2 + B [optimized out; see below] // 7. If is_square(gx1), set x = x1 and y = sqrt(gx1) // 8. Else set x = x2 and y = sqrt(gx2) let gx1_square; let gx1; - let zeta_gx1; assert!( !div3.is_zero(), - "we have checked that neither a or xi are zero. Q.E.D." + "we have checked that neither a or ZETA are zero. Q.E.D." ); let y1: P::BaseField = { gx1 = num_gx1 / div3; - zeta_gx1 = P::ZETA * gx1; if gx1.legendre().is_qr() { gx1_square = true; gx1.sqrt() .expect("We have checked that gx1 is a quadratic residue. Q.E.D") } else { + let zeta_gx1 = P::ZETA * gx1; gx1_square = false; zeta_gx1.sqrt().expect( - "zeta * gx1 is a quadratic residue because legard is multiplicative. Q.E.D", + "ZETA * gx1 is a quadratic residue because legard is multiplicative. Q.E.D", ) } }; @@ -159,12 +138,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 = P::XI_ON_ZETA_SQRT * xi_t2 * point * y1; + let y2 = zeta_u2 * point * 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) { -y } else { y }; + let y_affine = if parity(&y) != parity(&point) { -y } else { y }; let point_on_curve = Affine::

::new_unchecked(x_affine, y_affine); assert!( point_on_curve.is_on_curve(), @@ -173,3 +152,139 @@ impl MapToCurve> for SWUMap

{ Ok(point_on_curve) } } + +#[cfg(test)] +mod test { + use crate::hashing::map_to_curve_hasher::MapToCurveBasedHasher; + use crate::hashing::HashToCurve; + use crate::CurveConfig; + use ark_ff::field_hashers::DefaultFieldHasher; + use ark_std::vec::Vec; + + use super::*; + use ark_ff::{fields::Fp64, MontBackend, MontFp}; + use hashbrown::HashMap; + use sha2::Sha256; + + #[derive(ark_ff::MontConfig)] + #[modulus = "127"] + #[generator = "6"] + pub struct F127Config; + pub type F127 = Fp64>; + + const F127_ONE: F127 = MontFp!("1"); + + struct TestSWUMapToCurveParams; + + impl CurveConfig for TestSWUMapToCurveParams { + const COFACTOR: &'static [u64] = &[1]; + + #[rustfmt::skip] + const COFACTOR_INV: F127 = F127_ONE; + + type BaseField = F127; + type ScalarField = F127; + } + /// just because not defining another field + /// + /// from itertools import product + /// p = 127 + /// FF = GF(p) + /// for a,b in product(range(0,p), range(0,p)): + /// try: + /// E = EllipticCurve([FF(a),FF(b)]) + /// if E.order() == p: + /// print(E) + /// except: + /// pass + /// + /// y^2 = x^3 + x + 63 + impl SWCurveConfig for TestSWUMapToCurveParams { + /// COEFF_A = 1 + const COEFF_A: F127 = F127_ONE; + + /// COEFF_B = 1 + #[rustfmt::skip] + const COEFF_B: F127 = MontFp!("63"); + + /// AFFINE_GENERATOR_COEFFS = (G1_GENERATOR_X, G1_GENERATOR_Y) + const GENERATOR: Affine = Affine::new_unchecked(MontFp!("62"), MontFp!("70")); + } + + impl SWUParams for TestSWUMapToCurveParams { + const ZETA: F127 = MontFp!("-1"); + } + + /// test that MontFp make a none zero element out of 1 + #[test] + fn test_field_element_construction() { + let a1 = F127::from(1); + let a2 = F127::from(2); + let a3 = F127::from(125); + + assert!(F127::from(0) == a2 + a3); + assert!(F127::from(0) == a2 * a1 + a3); + } + + #[test] + fn test_field_division() { + let num = F127::from(0x3d); + let den = F127::from(0x7b); + let num_on_den = F127::from(0x50); + + assert!(num / den == num_on_den); + } + + /// The point of the test is to get a simple SWU compatible curve and make + /// simple hash + #[test] + fn hash_arbitary_string_to_curve_swu() { + let test_swu_to_curve_hasher = MapToCurveBasedHasher::< + Affine, + DefaultFieldHasher, + SWUMap, + >::new(&[1]) + .unwrap(); + + let hash_result = test_swu_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 SWU compatible 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_swu() { + let test_map_to_curve = SWUMap::::new().unwrap(); + + let mut map_range: Vec> = vec![]; + for current_field_element in 0..127 { + map_range.push( + test_map_to_curve + .map_to_curve(F127::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() != 127, + "a constant hash function is not good." + ); + } +} diff --git a/ec/src/hashing/curve_maps/wb/mod.rs b/ec/src/hashing/curve_maps/wb/mod.rs index 31bb67a21..c6be86fde 100644 --- a/ec/src/hashing/curve_maps/wb/mod.rs +++ b/ec/src/hashing/curve_maps/wb/mod.rs @@ -86,3 +86,218 @@ impl MapToCurve> for WBMap

{ P::isogeny_map(point_on_isogenious_curve) } } + +#[cfg(test)] +mod test { + use crate::hashing::HashToCurve; + use crate::{ + hashing::{ + curve_maps::{ + swu::SWUParams, + wb::{WBMap, WBParams}, + }, + map_to_curve_hasher::MapToCurveBasedHasher, + }, + models::short_weierstrass::SWCurveConfig, + short_weierstrass::Affine, + CurveConfig, + }; + use ark_ff::field_hashers::DefaultFieldHasher; + use ark_ff::{fields::Fp64, MontBackend, MontFp}; + + #[derive(ark_ff::MontConfig)] + #[modulus = "127"] + #[generator = "6"] + pub struct F127Config; + pub type F127 = Fp64>; + + const F127_ZERO: F127 = MontFp!("0"); + const F127_ONE: F127 = MontFp!("1"); + + /// The struct defining our parameters for the target curve of hashing + struct TestWBF127MapToCurveParams; + + impl CurveConfig for TestWBF127MapToCurveParams { + const COFACTOR: &'static [u64] = &[1]; + + #[rustfmt::skip] + const COFACTOR_INV: F127 = F127_ONE; + + type BaseField = F127; + type ScalarField = F127; + } + + /// E: Elliptic Curve defined by y^2 = x^3 + 3 over Finite + /// Field of size 127 + impl SWCurveConfig for TestWBF127MapToCurveParams { + /// COEFF_A = 0 + const COEFF_A: F127 = F127_ZERO; + + /// COEFF_B = 3 + #[rustfmt::skip] + const COEFF_B: F127 = MontFp!("3"); + + /// AFFINE_GENERATOR_COEFFS = (G1_GENERATOR_X, G1_GENERATOR_Y) + const GENERATOR: Affine = Affine::new_unchecked(MontFp!("62"), MontFp!("70")); + } + + /// Testing WB19 hashing on a small curve + /// E_isogenous : Elliptic Curve defined by y^2 = x^3 + 109*x + 124 over Finite + /// Field of size 127 + /// Isogenous to E : y^2 = x^3 + 3 + struct TestSWU127MapToIsogenousCurveParams; + + /// First we define the isogenous curve + /// sage: E_isogenous.order() + /// 127 + impl CurveConfig for TestSWU127MapToIsogenousCurveParams { + const COFACTOR: &'static [u64] = &[1]; + + #[rustfmt::skip] + const COFACTOR_INV: F127 = F127_ONE; + + type BaseField = F127; + type ScalarField = F127; + } + + /// E_isogenous : Elliptic Curve defined by y^2 = x^3 + 109*x + 124 over Finite + /// Field of size 127 + impl SWCurveConfig for TestSWU127MapToIsogenousCurveParams { + /// COEFF_A = 109 + const COEFF_A: F127 = MontFp!("109"); + + /// COEFF_B = 124 + #[rustfmt::skip] + const COEFF_B: F127 = MontFp!("124"); + + /// AFFINE_GENERATOR_COEFFS = (G1_GENERATOR_X, G1_GENERATOR_Y) + const GENERATOR: Affine = Affine::new_unchecked(MontFp!("84"), MontFp!("2")); + } + + /// SWU parameters for E_isogenous + impl SWUParams for TestSWU127MapToIsogenousCurveParams { + /// NON-SQUARE = - 1 + const ZETA: F127 = MontFp!("-1"); + } + + /// E_isogenous : Elliptic Curve defined by y^2 = x^3 + 109*x + 124 over Finite + /// Field of size 127 + /// With psi: E_isogenous -> E + /// psi = (psi_x(x,y), psi_y(x,y)) + /// where + /// psi_x: (-57*x^13 - 21*x^12 + 10*x^11 + 34*x^10 + 40*x^9 - + /// 13*x^8 + 32*x^7 - 32*x^6 + 23*x^5 - 14*x^4 + 39*x^3 + 23*x^2 + 63*x + + /// 4)/(x^12 - 13*x^11 + 11*x^10 - 33*x^9 - 30*x^8 + 30*x^7 + 34*x^6 - 44*x^5 + + /// 63*x^4 - 20*x^3 - 10*x^2 + 31*x + 2) + /// + /// psi_y: (10*x^18*y + 59*x^17*y + 41*x^16*y + 48*x^15*y - 7*x^14*y + 6*x^13*y + + /// 5*x^12*y + 62*x^11*y + 12*x^10*y + 36*x^9*y - 49*x^8*y - 18*x^7*y - 63*x^6*y + /// - 43*x^5*y - 60*x^4*y - 18*x^3*y + 30*x^2*y - 57*x*y - 34*y)/(x^18 + 44*x^17 + /// - 63*x^16 + 52*x^15 + 3*x^14 + 38*x^13 - 30*x^12 + 11*x^11 - 42*x^10 - 13*x^9 + /// - 46*x^8 - 61*x^7 - 16*x^6 - 55*x^5 + 18*x^4 + 23*x^3 - 24*x^2 - 18*x + 32) + impl WBParams for TestWBF127MapToCurveParams { + type IsogenousCurve = TestSWU127MapToIsogenousCurveParams; + + const PHI_X_NOM: &'static [::BaseField] = &[ + MontFp!("4"), + MontFp!("63"), + MontFp!("23"), + MontFp!("39"), + MontFp!("-14"), + MontFp!("23"), + MontFp!("-32"), + MontFp!("32"), + MontFp!("-13"), + MontFp!("40"), + MontFp!("34"), + MontFp!("10"), + MontFp!("-21"), + MontFp!("-57"), + ]; + + const PHI_X_DEN: &'static [::BaseField] = &[ + MontFp!("2"), + MontFp!("31"), + MontFp!("-10"), + MontFp!("-20"), + MontFp!("63"), + MontFp!("-44"), + MontFp!("34"), + MontFp!("30"), + MontFp!("-30"), + MontFp!("-33"), + MontFp!("11"), + MontFp!("-13"), + MontFp!("1"), + ]; + + const PHI_Y_NOM: &'static [::BaseField] = &[ + MontFp!("-34"), + MontFp!("-57"), + MontFp!("30"), + MontFp!("-18"), + MontFp!("-60"), + MontFp!("-43"), + MontFp!("-63"), + MontFp!("-18"), + MontFp!("-49"), + MontFp!("36"), + MontFp!("12"), + MontFp!("62"), + MontFp!("5"), + MontFp!("6"), + MontFp!("-7"), + MontFp!("48"), + MontFp!("41"), + MontFp!("59"), + MontFp!("10"), + ]; + + const PHI_Y_DEN: &'static [::BaseField] = &[ + MontFp!("32"), + MontFp!("-18"), + MontFp!("-24"), + MontFp!("23"), + MontFp!("18"), + MontFp!("-55"), + MontFp!("-16"), + MontFp!("-61"), + MontFp!("-46"), + MontFp!("-13"), + MontFp!("-42"), + MontFp!("11"), + MontFp!("-30"), + MontFp!("38"), + MontFp!("3"), + MontFp!("52"), + MontFp!("-63"), + MontFp!("44"), + MontFp!("1"), + ]; + } + + /// The point of the test is to get a simple WB compatible curve + /// and make simple hash + #[test] + fn hash_arbitrary_string_to_curve_wb() { + use sha2::Sha256; + let test_wb_to_curve_hasher = MapToCurveBasedHasher::< + Affine, + DefaultFieldHasher, + WBMap, + >::new(&[1]) + .unwrap(); + + let hash_result = test_wb_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.x != F127_ZERO && hash_result.y != F127_ZERO, + "we assume that not both a and b coefficienst are zero for the test curve" + ); + + assert!( + hash_result.is_on_curve(), + "hash results into a point off the curve" + ); + } +} diff --git a/ec/src/hashing/map_to_curve_hasher.rs b/ec/src/hashing/map_to_curve_hasher.rs index 4e4d8e5dd..1afd17078 100644 --- a/ec/src/hashing/map_to_curve_hasher.rs +++ b/ec/src/hashing/map_to_curve_hasher.rs @@ -63,7 +63,7 @@ where let rand_curve_elem_1 = self.curve_mapper.map_to_curve(rand_field_elems[1])?; let rand_curve_elem = rand_curve_elem_0 + rand_curve_elem_1; - let rand_subgroup_elem = rand_curve_elem.mul_by_cofactor(); + let rand_subgroup_elem = rand_curve_elem.clear_cofactor(); Ok(rand_subgroup_elem) } diff --git a/ec/src/hashing/tests/mod.rs b/ec/src/hashing/tests/mod.rs index ea4b12c44..6760c4603 100644 --- a/ec/src/hashing/tests/mod.rs +++ b/ec/src/hashing/tests/mod.rs @@ -1,371 +1,11 @@ -use crate::{ - hashing::{ - curve_maps::{ - swu::{parity, SWUMap, SWUParams}, - wb::{WBMap, WBParams}, - }, - map_to_curve_hasher::{MapToCurve, MapToCurveBasedHasher}, - HashToCurve, - }, - models::short_weierstrass::SWCurveConfig, - short_weierstrass::Affine, - CurveConfig, -}; -use ark_ff::{ - biginteger::BigInteger64, field_hashers::DefaultFieldHasher, fields::Fp64, BigInt, Field, - MontBackend, MontFp, -}; - -use ark_std::vec::Vec; +use crate::hashing::curve_maps::swu::parity; use ark_test_curves::bls12_381::{Fq, Fq2, Fq6}; -use hashbrown::HashMap; #[cfg(all(test, feature = "std"))] mod json; #[cfg(all(test, feature = "std"))] mod suites; -pub struct F127Config; -pub type F127 = Fp64>; - -impl ark_ff::MontConfig<1> for F127Config { - // sage: FF(3)^63 - // 126 - #[rustfmt::skip] - const TWO_ADIC_ROOT_OF_UNITY: F127 = MontFp!("126"); - - /// MODULUS = 127 - #[rustfmt::skip] - const MODULUS: BigInteger64 = BigInt!("127"); - - // sage: FF(3).multiplicative_order() - // 126 - // Montgomery conversion 3 * 2 = 6 % 127 - /// GENERATOR = 3 - #[rustfmt::skip] - const GENERATOR: F127 = MontFp!("6"); - - // T and T_MINUS_ONE_DIV_TWO, where MODULUS - 1 = 2^S * T - // For T coprime to 2 -} - -const F127_ZERO: F127 = MontFp!("0"); -const F127_ONE: F127 = MontFp!("1"); - -struct TestSWUMapToCurveParams; - -impl CurveConfig for TestSWUMapToCurveParams { - const COFACTOR: &'static [u64] = &[1]; - - #[rustfmt::skip] - const COFACTOR_INV: F127 = F127_ONE; - - type BaseField = F127; - type ScalarField = F127; -} -/// just because not defining another field -/// -/// from itertools import product -/// p = 127 -/// FF = GF(p) -/// for a,b in product(range(0,p), range(0,p)): -/// try: -/// E = EllipticCurve([FF(a),FF(b)]) -/// if E.order() == p: -/// print(E) -/// except: -/// pass -/// -/// y^2 = x^3 + x + 63 -impl SWCurveConfig for TestSWUMapToCurveParams { - /// COEFF_A = 1 - const COEFF_A: F127 = F127_ONE; - - /// COEFF_B = 1 - #[rustfmt::skip] - const COEFF_B: F127 = MontFp!("63"); - - /// AFFINE_GENERATOR_COEFFS = (G1_GENERATOR_X, G1_GENERATOR_Y) - const GENERATOR: Affine = Affine::new_unchecked(MontFp!("62"), MontFp!("70")); -} - -impl SWUParams for TestSWUMapToCurveParams { - const XI: F127 = MontFp!("-1"); - const ZETA: F127 = MontFp!("3"); - const XI_ON_ZETA_SQRT: F127 = MontFp!("13"); -} - -/// test that MontFp make a none zero element out of 1 -#[test] -fn test_field_element_construction() { - let a1 = F127::from(1); - let a2 = F127::from(2); - let a3 = F127::from(125); - - assert!(F127::from(0) == a2 + a3); - assert!(F127::from(0) == a2 * a1 + a3); -} - -#[test] -fn test_field_division() { - let num = F127::from(0x3d); - let den = F127::from(0x7b); - let num_on_den = F127::from(0x50); - - assert!(num / den == num_on_den); -} - -/// Check that the hashing parameters are sane: zeta should be a non-square -#[test] -fn checking_the_hashing_parameters() { - assert!(!Field::legendre(&TestSWUMapToCurveParams::ZETA).is_qr()); -} - -/// The point of the test is to get a simple SWU compatible curve and make -/// simple hash -#[test] -fn hash_arbitary_string_to_curve_swu() { - let test_swu_to_curve_hasher = MapToCurveBasedHasher::< - Affine, - DefaultFieldHasher, - SWUMap, - >::new(&[1]) - .unwrap(); - - let hash_result = test_swu_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 SWU compatible 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_swu() { - let test_map_to_curve = SWUMap::::new().unwrap(); - - let mut map_range: Vec> = vec![]; - for current_field_element in 0..127 { - map_range.push( - test_map_to_curve - .map_to_curve(F127::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() != 127, - "a constant hash function is not good." - ); -} - -/// Testing WB19 hashing on a small curve -/// E_isogenous : Elliptic Curve defined by y^2 = x^3 + 109*x + 124 over Finite -/// Field of size 127 -/// Isogenous to E : y^2 = x^3 + 3 -struct TestSWU127MapToIsogenousCurveParams; - -/// First we define the isogenous curve -/// sage: E_isogenous.order() -/// 127 -impl CurveConfig for TestSWU127MapToIsogenousCurveParams { - const COFACTOR: &'static [u64] = &[1]; - - #[rustfmt::skip] - const COFACTOR_INV: F127 = F127_ONE; - - type BaseField = F127; - type ScalarField = F127; -} - -/// E_isogenous : Elliptic Curve defined by y^2 = x^3 + 109*x + 124 over Finite -/// Field of size 127 -impl SWCurveConfig for TestSWU127MapToIsogenousCurveParams { - /// COEFF_A = 109 - const COEFF_A: F127 = MontFp!("109"); - - /// COEFF_B = 124 - #[rustfmt::skip] - const COEFF_B: F127 = MontFp!("124"); - - /// AFFINE_GENERATOR_COEFFS = (G1_GENERATOR_X, G1_GENERATOR_Y) - const GENERATOR: Affine = Affine::new_unchecked(MontFp!("84"), MontFp!("2")); -} - -/// SWU parameters for E_isogenous -impl SWUParams for TestSWU127MapToIsogenousCurveParams { - /// NON-SQUARE = - 1 - const XI: F127 = MontFp!("-1"); - /// A Primitive Root of unity = 3 - const ZETA: F127 = MontFp!("3"); - /// sqrt(Xi/Zeta) - const XI_ON_ZETA_SQRT: F127 = MontFp!("13"); -} - -/// The struct defining our parameters for the target curve of hashing -struct TestWBF127MapToCurveParams; - -impl CurveConfig for TestWBF127MapToCurveParams { - const COFACTOR: &'static [u64] = &[1]; - - #[rustfmt::skip] - const COFACTOR_INV: F127 = F127_ONE; - - type BaseField = F127; - type ScalarField = F127; -} - -/// E: Elliptic Curve defined by y^2 = x^3 + 3 over Finite -/// Field of size 127 -impl SWCurveConfig for TestWBF127MapToCurveParams { - /// COEFF_A = 0 - const COEFF_A: F127 = F127_ZERO; - - /// COEFF_B = 3 - #[rustfmt::skip] - const COEFF_B: F127 = MontFp!("3"); - - /// AFFINE_GENERATOR_COEFFS = (G1_GENERATOR_X, G1_GENERATOR_Y) - const GENERATOR: Affine = Affine::new_unchecked(MontFp!("62"), MontFp!("70")); -} - -/// E_isogenous : Elliptic Curve defined by y^2 = x^3 + 109*x + 124 over Finite -/// Field of size 127 -/// With psi: E_isogenous -> E -/// psi = (psi_x(x,y), psi_y(x,y)) -/// where -/// psi_x: (-57*x^13 - 21*x^12 + 10*x^11 + 34*x^10 + 40*x^9 - -/// 13*x^8 + 32*x^7 - 32*x^6 + 23*x^5 - 14*x^4 + 39*x^3 + 23*x^2 + 63*x + -/// 4)/(x^12 - 13*x^11 + 11*x^10 - 33*x^9 - 30*x^8 + 30*x^7 + 34*x^6 - 44*x^5 + -/// 63*x^4 - 20*x^3 - 10*x^2 + 31*x + 2) -/// -/// psi_y: (10*x^18*y + 59*x^17*y + 41*x^16*y + 48*x^15*y - 7*x^14*y + 6*x^13*y -/// + 5*x^12*y + 62*x^11*y + 12*x^10*y + 36*x^9*y - 49*x^8*y - 18*x^7*y - -/// 63*x^6*y -/// - 43*x^5*y - 60*x^4*y - 18*x^3*y + 30*x^2*y - 57*x*y - 34*y)/(x^18 + 44*x^17 -/// - 63*x^16 + 52*x^15 + 3*x^14 + 38*x^13 - 30*x^12 + 11*x^11 - 42*x^10 - -/// 13*x^9 -/// - 46*x^8 - 61*x^7 - 16*x^6 - 55*x^5 + 18*x^4 + 23*x^3 - 24*x^2 - 18*x + 32) -impl WBParams for TestWBF127MapToCurveParams { - type IsogenousCurve = TestSWU127MapToIsogenousCurveParams; - - const PHI_X_NOM: &'static [::BaseField] = &[ - MontFp!("4"), - MontFp!("63"), - MontFp!("23"), - MontFp!("39"), - MontFp!("-14"), - MontFp!("23"), - MontFp!("-32"), - MontFp!("32"), - MontFp!("-13"), - MontFp!("40"), - MontFp!("34"), - MontFp!("10"), - MontFp!("-21"), - MontFp!("-57"), - ]; - - const PHI_X_DEN: &'static [::BaseField] = &[ - MontFp!("2"), - MontFp!("31"), - MontFp!("-10"), - MontFp!("-20"), - MontFp!("63"), - MontFp!("-44"), - MontFp!("34"), - MontFp!("30"), - MontFp!("-30"), - MontFp!("-33"), - MontFp!("11"), - MontFp!("-13"), - MontFp!("1"), - ]; - - const PHI_Y_NOM: &'static [::BaseField] = &[ - MontFp!("-34"), - MontFp!("-57"), - MontFp!("30"), - MontFp!("-18"), - MontFp!("-60"), - MontFp!("-43"), - MontFp!("-63"), - MontFp!("-18"), - MontFp!("-49"), - MontFp!("36"), - MontFp!("12"), - MontFp!("62"), - MontFp!("5"), - MontFp!("6"), - MontFp!("-7"), - MontFp!("48"), - MontFp!("41"), - MontFp!("59"), - MontFp!("10"), - ]; - - const PHI_Y_DEN: &'static [::BaseField] = &[ - MontFp!("32"), - MontFp!("-18"), - MontFp!("-24"), - MontFp!("23"), - MontFp!("18"), - MontFp!("-55"), - MontFp!("-16"), - MontFp!("-61"), - MontFp!("-46"), - MontFp!("-13"), - MontFp!("-42"), - MontFp!("11"), - MontFp!("-30"), - MontFp!("38"), - MontFp!("3"), - MontFp!("52"), - MontFp!("-63"), - MontFp!("44"), - MontFp!("1"), - ]; -} - -/// The point of the test is to get a simple WB compatible curve -/// and make simple hash -#[test] -fn hash_arbitary_string_to_curve_wb() { - let test_wb_to_curve_hasher = MapToCurveBasedHasher::< - Affine, - DefaultFieldHasher, - WBMap, - >::new(&[1]) - .unwrap(); - - let hash_result = test_wb_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.x != F127_ZERO && hash_result.y != F127_ZERO, - "we assume that not both a and b coefficienst are zero for the test curve" - ); - - assert!( - hash_result.is_on_curve(), - "hash results into a point off the curve" - ); -} - #[test] fn test_parity_of_prime_field_elements() { let a1 = Fq::from(0); diff --git a/ec/src/hashing/tests/suites.rs b/ec/src/hashing/tests/suites.rs index a8ba59a66..ff2b79bca 100644 --- a/ec/src/hashing/tests/suites.rs +++ b/ec/src/hashing/tests/suites.rs @@ -5,9 +5,15 @@ use super::json::SuiteVector; use ark_ff::field_hashers::{DefaultFieldHasher, HashToField}; use libtest_mimic::{run_tests, Arguments, Outcome, Test}; -use ark_ff::PrimeField; -use ark_test_curves::bls12_381::Fq; -use ark_test_curves::bls12_381::Fq2; +use ark_test_curves::{ + hashing::{curve_maps::wb::WBMap, map_to_curve_hasher::MapToCurveBasedHasher, HashToCurve}, + short_weierstrass::Affine, +}; + +use ark_ff::{Field, PrimeField}; +use ark_test_curves::bls12_381::{ + g1::Parameters as G1Parameters, g2::Parameters as G2Parameters, Fq, Fq2, +}; use sha2::Sha256; #[test] @@ -35,6 +41,18 @@ fn run_test_w(Test { data, .. }: &Test) -> Outcome { let dst = data.dst.as_bytes(); let hasher; let m; + let g1_mapper = MapToCurveBasedHasher::< + Affine, + DefaultFieldHasher, + WBMap, + >::new(dst) + .unwrap(); + let g2_mapper = MapToCurveBasedHasher::< + Affine, + DefaultFieldHasher, + WBMap, + >::new(dst) + .unwrap(); match data.curve.as_str() { "BLS12-381 G1" => { m = 1; @@ -48,14 +66,11 @@ fn run_test_w(Test { data, .. }: &Test) -> Outcome { } for v in data.vectors.iter() { + // first, hash-to-field tests let got: Vec = hasher.hash_to_field(&v.msg.as_bytes(), 2 * m); let want: Vec = (&v.u) .into_iter() - .map(|x| { - x.split(",").map(|f| { - Fq::from_be_bytes_mod_order(&hex::decode(f.trim_start_matches("0x")).unwrap()) - }) - }) + .map(|x| read_fq_vec(x)) .flatten() .collect(); if got != want { @@ -68,6 +83,59 @@ fn run_test_w(Test { data, .. }: &Test) -> Outcome { )), }; } + + // then, test curve points + let x: Vec = read_fq_vec(&v.p.x); + let y: Vec = read_fq_vec(&v.p.y); + match data.curve.as_str() { + "BLS12-381 G1" => { + let got = g1_mapper.hash(&v.msg.as_bytes()).unwrap(); + let want = Affine::::new_unchecked( + Fq::from_base_prime_field_elems(&x[..]).unwrap(), + Fq::from_base_prime_field_elems(&y[..]).unwrap(), + ); + assert!(got.is_on_curve()); + assert!(want.is_on_curve()); + if got != want { + return Outcome::Failed { + msg: Some(format!( + "Suite: {:?}\ngot: {:?}\nwant: {:?}", + data.ciphersuite, + got.to_string(), + want.to_string() + )), + }; + } + }, + "BLS12-381 G2" => { + let got = g2_mapper.hash(&v.msg.as_bytes()).unwrap(); + let want = Affine::::new_unchecked( + Fq2::from_base_prime_field_elems(&x[..]).unwrap(), + Fq2::from_base_prime_field_elems(&y[..]).unwrap(), + ); + assert!(got.is_on_curve()); + assert!(want.is_on_curve()); + if got != want { + return Outcome::Failed { + msg: Some(format!( + "Suite: {:?}\nmsg: {}\n\ngot: {:?}\nwant: {:?}", + data.ciphersuite, + v.msg, + got.to_string(), + want.to_string(), + )), + }; + } + }, + _ => return Outcome::Ignored, + } } Outcome::Passed } + +fn read_fq_vec(input: &String) -> Vec { + input + .split(",") + .map(|f| Fq::from_be_bytes_mod_order(&hex::decode(f.trim_start_matches("0x")).unwrap())) + .collect() +} diff --git a/test-curves/src/bls12_381/g1.rs b/test-curves/src/bls12_381/g1.rs index 8b045ce56..2a66ac1cd 100644 --- a/test-curves/src/bls12_381/g1.rs +++ b/test-curves/src/bls12_381/g1.rs @@ -1,5 +1,6 @@ use crate::bls12_381::*; use ark_ec::{ + hashing::curve_maps::wb::WBParams, models::CurveConfig, short_weierstrass::{self, *}, }; @@ -52,6 +53,78 @@ impl short_weierstrass::SWCurveConfig for Parameters { } } +// Parameters from the [IETF draft v16, section E.2](https://www.ietf.org/archive/id/draft-irtf-cfrg-hash-to-curve-16.html#name-11-isogeny-map-for-bls12-381). +impl WBParams for Parameters { + type IsogenousCurve = SwuIsoParameters; + + const PHI_X_NOM: &'static [::BaseField] = &[ + MontFp!("2712959285290305970661081772124144179193819192423276218370281158706191519995889425075952244140278856085036081760695"), + MontFp!("3564859427549639835253027846704205725951033235539816243131874237388832081954622352624080767121604606753339903542203"), + MontFp!("2051387046688339481714726479723076305756384619135044672831882917686431912682625619320120082313093891743187631791280"), + MontFp!("3612713941521031012780325893181011392520079402153354595775735142359240110423346445050803899623018402874731133626465"), + MontFp!("2247053637822768981792833880270996398470828564809439728372634811976089874056583714987807553397615562273407692740057"), + MontFp!("3415427104483187489859740871640064348492611444552862448295571438270821994900526625562705192993481400731539293415811"), + MontFp!("2067521456483432583860405634125513059912765526223015704616050604591207046392807563217109432457129564962571408764292"), + MontFp!("3650721292069012982822225637849018828271936405382082649291891245623305084633066170122780668657208923883092359301262"), + MontFp!("1239271775787030039269460763652455868148971086016832054354147730155061349388626624328773377658494412538595239256855"), + MontFp!("3479374185711034293956731583912244564891370843071137483962415222733470401948838363051960066766720884717833231600798"), + MontFp!("2492756312273161536685660027440158956721981129429869601638362407515627529461742974364729223659746272460004902959995"), + MontFp!("1058488477413994682556770863004536636444795456512795473806825292198091015005841418695586811009326456605062948114985"), + ]; + + const PHI_X_DEN: &'static [::BaseField] = &[ + MontFp!("1353092447850172218905095041059784486169131709710991428415161466575141675351394082965234118340787683181925558786844"), + MontFp!("2822220997908397120956501031591772354860004534930174057793539372552395729721474912921980407622851861692773516917759"), + MontFp!("1717937747208385987946072944131378949849282930538642983149296304709633281382731764122371874602115081850953846504985"), + MontFp!("501624051089734157816582944025690868317536915684467868346388760435016044027032505306995281054569109955275640941784"), + MontFp!("3025903087998593826923738290305187197829899948335370692927241015584233559365859980023579293766193297662657497834014"), + MontFp!("2224140216975189437834161136818943039444741035168992629437640302964164227138031844090123490881551522278632040105125"), + MontFp!("1146414465848284837484508420047674663876992808692209238763293935905506532411661921697047880549716175045414621825594"), + MontFp!("3179090966864399634396993677377903383656908036827452986467581478509513058347781039562481806409014718357094150199902"), + MontFp!("1549317016540628014674302140786462938410429359529923207442151939696344988707002602944342203885692366490121021806145"), + MontFp!("1442797143427491432630626390066422021593505165588630398337491100088557278058060064930663878153124164818522816175370"), + MontFp!("1"), + ]; + + const PHI_Y_NOM: &'static [::BaseField] = &[ + MontFp!("1393399195776646641963150658816615410692049723305861307490980409834842911816308830479576739332720113414154429643571"), + MontFp!("2968610969752762946134106091152102846225411740689724909058016729455736597929366401532929068084731548131227395540630"), + MontFp!("122933100683284845219599644396874530871261396084070222155796123161881094323788483360414289333111221370374027338230"), + MontFp!("303251954782077855462083823228569901064301365507057490567314302006681283228886645653148231378803311079384246777035"), + MontFp!("1353972356724735644398279028378555627591260676383150667237975415318226973994509601413730187583692624416197017403099"), + MontFp!("3443977503653895028417260979421240655844034880950251104724609885224259484262346958661845148165419691583810082940400"), + MontFp!("718493410301850496156792713845282235942975872282052335612908458061560958159410402177452633054233549648465863759602"), + MontFp!("1466864076415884313141727877156167508644960317046160398342634861648153052436926062434809922037623519108138661903145"), + MontFp!("1536886493137106337339531461344158973554574987550750910027365237255347020572858445054025958480906372033954157667719"), + MontFp!("2171468288973248519912068884667133903101171670397991979582205855298465414047741472281361964966463442016062407908400"), + MontFp!("3915937073730221072189646057898966011292434045388986394373682715266664498392389619761133407846638689998746172899634"), + MontFp!("3802409194827407598156407709510350851173404795262202653149767739163117554648574333789388883640862266596657730112910"), + MontFp!("1707589313757812493102695021134258021969283151093981498394095062397393499601961942449581422761005023512037430861560"), + MontFp!("349697005987545415860583335313370109325490073856352967581197273584891698473628451945217286148025358795756956811571"), + MontFp!("885704436476567581377743161796735879083481447641210566405057346859953524538988296201011389016649354976986251207243"), + MontFp!("3370924952219000111210625390420697640496067348723987858345031683392215988129398381698161406651860675722373763741188"), + ]; + + const PHI_Y_DEN: &'static [::BaseField] = &[ + MontFp!("3396434800020507717552209507749485772788165484415495716688989613875369612529138640646200921379825018840894888371137"), + MontFp!("3907278185868397906991868466757978732688957419873771881240086730384895060595583602347317992689443299391009456758845"), + MontFp!("854914566454823955479427412036002165304466268547334760894270240966182605542146252771872707010378658178126128834546"), + MontFp!("3496628876382137961119423566187258795236027183112131017519536056628828830323846696121917502443333849318934945158166"), + MontFp!("1828256966233331991927609917644344011503610008134915752990581590799656305331275863706710232159635159092657073225757"), + MontFp!("1362317127649143894542621413133849052553333099883364300946623208643344298804722863920546222860227051989127113848748"), + MontFp!("3443845896188810583748698342858554856823966611538932245284665132724280883115455093457486044009395063504744802318172"), + MontFp!("3484671274283470572728732863557945897902920439975203610275006103818288159899345245633896492713412187296754791689945"), + MontFp!("3755735109429418587065437067067640634211015783636675372165599470771975919172394156249639331555277748466603540045130"), + MontFp!("3459661102222301807083870307127272890283709299202626530836335779816726101522661683404130556379097384249447658110805"), + MontFp!("742483168411032072323733249644347333168432665415341249073150659015707795549260947228694495111018381111866512337576"), + MontFp!("1662231279858095762833829698537304807741442669992646287950513237989158777254081548205552083108208170765474149568658"), + MontFp!("1668238650112823419388205992952852912407572045257706138925379268508860023191233729074751042562151098884528280913356"), + MontFp!("369162719928976119195087327055926326601627748362769544198813069133429557026740823593067700396825489145575282378487"), + MontFp!("2164195715141237148945939585099633032390257748382945597506236650132835917087090097395995817229686247227784224263055"), + MontFp!("1"), + ]; +} + /// G1_GENERATOR_X = /// 3685416753713387016781088315183077757961620795782546409894578378688607592378376318836054947676345821548104185464507 #[rustfmt::skip] diff --git a/test-curves/src/bls12_381/g1_swu_iso.rs b/test-curves/src/bls12_381/g1_swu_iso.rs new file mode 100644 index 000000000..5be970f37 --- /dev/null +++ b/test-curves/src/bls12_381/g1_swu_iso.rs @@ -0,0 +1,69 @@ +use crate::bls12_381::*; +use ark_ec::hashing::curve_maps::swu::SWUParams; +use ark_ec::models::{ + short_weierstrass::{Affine, SWCurveConfig}, + CurveConfig, +}; +use ark_ff::MontFp; + +type G1Affine = Affine; + +#[derive(Clone, Default, PartialEq, Eq)] +pub struct SwuIsoParameters; + +impl CurveConfig for SwuIsoParameters { + type BaseField = Fq; + type ScalarField = Fr; + + /// COFACTOR = (x - 1)^2 / 3 = 76329603384216526031706109802092473003 + const COFACTOR: &'static [u64] = &[0x8c00aaab0000aaab, 0x396c8c005555e156]; + + /// COFACTOR_INV = COFACTOR^{-1} mod r + /// = 52435875175126190458656871551744051925719901746859129887267498875565241663483 + #[rustfmt::skip] + const COFACTOR_INV: Fr = MontFp!("52435875175126190458656871551744051925719901746859129887267498875565241663483"); +} + +// https://datatracker.ietf.org/doc/draft-irtf-cfrg-hash-to-curve/ +// Hashing to Elliptic Curves +// 8.8.1. BLS12-381 G1 +// BLS12381G1_XMD:SHA-256_SSWU_RO_ is defined as follows: +// * E': y'^2 = x'^3 + A' * x' + B', where +// - A' = 0x144698a3b8e9433d693a02c96d4982b0ea985383ee66a8d8e8981aefd881ac98936f8da0e0f97f5cf428082d584c1d +// - B' = 0x12e2908d11688030018b12e8753eee3b2016c1f0f24f4070a0b9c14fcef35ef55a23215a316ceaa5d1cc48e98e172be0 +// - A' = 12190336318893619529228877361869031420615612348429846051986726275283378313155663745811710833465465981901188123677 +// - B' = 2906670324641927570491258158026293881577086121416628140204402091718288198173574630967936031029026176254968826637280 +// * Z: 11 +impl SWCurveConfig for SwuIsoParameters { + const COEFF_A: Fq = MontFp!("12190336318893619529228877361869031420615612348429846051986726275283378313155663745811710833465465981901188123677"); + + #[rustfmt::skip] + const COEFF_B: Fq = MontFp!("2906670324641927570491258158026293881577086121416628140204402091718288198173574630967936031029026176254968826637280"); + + const GENERATOR: G1Affine = G1Affine::new_unchecked(G1_GENERATOR_X, G1_GENERATOR_Y); +} + +/// Lexicographically smallest, valid x-coordinate of a point P on the curve (with its corresponding y) multiplied by the cofactor. +/// P_x = 2 +/// P_y = 658522096176515125667361255350269797307718222519385801637008089782287711363858559738763090642304321670226247205569 +/// P = E(P_x, P_y) +/// G = P * COFACTOR +const G1_GENERATOR_X: Fq = MontFp!("1677416608493238977774703213729589714082762656433187746258164626835771660734158898989765932111853529350617333597651"); +const G1_GENERATOR_Y: Fq = MontFp!("1405098061573104639413728190240719229571583960971553962991897960445246185035342568402755187331334546673157015627211"); + +impl SWUParams for SwuIsoParameters { + // ZETA = 0xb as per the IETF draft. + const ZETA: Fq = MontFp!("11"); +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn test_gen() { + let gen: G1Affine = SwuIsoParameters::GENERATOR; + assert!(gen.is_on_curve()); + assert!(gen.is_in_correct_subgroup_assuming_on_curve()); + } +} diff --git a/test-curves/src/bls12_381/g2.rs b/test-curves/src/bls12_381/g2.rs index 4359ae7dd..07e2f0ccb 100644 --- a/test-curves/src/bls12_381/g2.rs +++ b/test-curves/src/bls12_381/g2.rs @@ -1,9 +1,12 @@ +use core::ops::Neg; + use crate::bls12_381::*; use ark_ec::{ bls12, + hashing::curve_maps::wb::WBParams, models::CurveConfig, - short_weierstrass::{self, Affine}, - AffineCurve, + short_weierstrass::{self, *}, + AffineCurve, ProjectiveCurve, }; use ark_ff::{BigInt, Field, MontFp, Zero}; @@ -70,6 +73,40 @@ impl short_weierstrass::SWCurveConfig for Parameters { x_times_point.eq(&p_times_point) } + + #[inline] + fn clear_cofactor(p: &G2Affine) -> G2Affine { + // Based on Section 4.1 of https://eprint.iacr.org/2017/419.pdf + // [h(ψ)]P = [x^2 − x − 1]P + [x − 1]ψ(P) + (ψ^2)(2P) + + // x = -15132376222941642752 + // When multiplying, use -c1 instead, and then negate the result. That's much + // more efficient, since the scalar -c1 has less limbs and a much lower Hamming + // weight. + let x: &'static [u64] = crate::bls12_381::Parameters::X; + let p_projective = p.into_projective(); + + // [x]P + let x_p = Parameters::mul_affine(p, &x).neg(); + // ψ(P) + let psi_p = p_power_endomorphism(&p); + // (ψ^2)(2P) + let mut psi2_p2 = double_p_power_endomorphism(&p_projective.double()); + + // tmp = [x]P + ψ(P) + let mut tmp = x_p.clone(); + tmp.add_assign_mixed(&psi_p); + + // tmp2 = [x^2]P + [x]ψ(P) + let mut tmp2: Projective = tmp; + tmp2 = tmp2.mul(x).neg(); + + // add up all the terms + psi2_p2 += tmp2; + psi2_p2 -= x_p; + psi2_p2.add_assign_mixed(&-psi_p); + (psi2_p2 - p_projective).into_affine() + } } pub const G2_GENERATOR_X: Fq2 = Fq2::new(G2_GENERATOR_X_C0, G2_GENERATOR_X_C1); @@ -114,6 +151,11 @@ pub const P_POWER_ENDOMORPHISM_COEFF_1: Fq2 = Fq2::new( "1028732146235106349975324479215795277384839936929757896155643118032610843298655225875571310552543014690878354869257") ); +pub const DOUBLE_P_POWER_ENDOMORPHISM: Fq2 = Fq2::new( + MontFp!("4002409555221667392624310435006688643935503118305586438271171395842971157480381377015405980053539358417135540939436"), + FQ_ZERO +); + pub fn p_power_endomorphism(p: &Affine) -> Affine { // The p-power endomorphism for G2 is defined as follows: // 1. Note that G2 is defined on curve E': y^2 = x^3 + 4(u+1). To map a point @@ -138,3 +180,75 @@ pub fn p_power_endomorphism(p: &Affine) -> Affine { res } + +/// For a p-power endomorphism psi(P), compute psi(psi(P)) +pub fn double_p_power_endomorphism(p: &Projective) -> Projective { + let mut res = *p; + + res.x *= DOUBLE_P_POWER_ENDOMORPHISM; + res.y = res.y.neg(); + + res +} + +// Parameters from the [IETF draft v16, section E.3](https://www.ietf.org/archive/id/draft-irtf-cfrg-hash-to-curve-16.html#name-3-isogeny-map-for-bls12-381). +impl WBParams for Parameters { + type IsogenousCurve = g2_swu_iso::SwuIsoParameters; + + const PHI_X_NOM: &'static [::BaseField] = &[ + Fq2::new( + MontFp!("889424345604814976315064405719089812568196182208668418962679585805340366775741747653930584250892369786198727235542"), + MontFp!("889424345604814976315064405719089812568196182208668418962679585805340366775741747653930584250892369786198727235542")), + Fq2::new( + MontFp!("0"), + MontFp!("2668273036814444928945193217157269437704588546626005256888038757416021100327225242961791752752677109358596181706522")), + Fq2::new( + MontFp!("2668273036814444928945193217157269437704588546626005256888038757416021100327225242961791752752677109358596181706526"), + MontFp!("1334136518407222464472596608578634718852294273313002628444019378708010550163612621480895876376338554679298090853261")), + Fq2::new( + MontFp!("3557697382419259905260257622876359250272784728834673675850718343221361467102966990615722337003569479144794908942033"), + MontFp!("0")), + ]; + + const PHI_X_DEN: &'static [::BaseField] = &[ + Fq2::new( + MontFp!("0"), + MontFp!("4002409555221667393417789825735904156556882819939007885332058136124031650490837864442687629129015664037894272559715")), + Fq2::new( + MontFp!("12"), + MontFp!("4002409555221667393417789825735904156556882819939007885332058136124031650490837864442687629129015664037894272559775")), + Fq2::new( + MontFp!("1"), + MontFp!("0")), + ]; + + const PHI_Y_NOM: &'static [::BaseField] = &[ + Fq2::new( + MontFp!("3261222600550988246488569487636662646083386001431784202863158481286248011511053074731078808919938689216061999863558"), + MontFp!("3261222600550988246488569487636662646083386001431784202863158481286248011511053074731078808919938689216061999863558")), + Fq2::new( + MontFp!("0"), + MontFp!("889424345604814976315064405719089812568196182208668418962679585805340366775741747653930584250892369786198727235518")), + Fq2::new( + MontFp!("2668273036814444928945193217157269437704588546626005256888038757416021100327225242961791752752677109358596181706524"), + MontFp!("1334136518407222464472596608578634718852294273313002628444019378708010550163612621480895876376338554679298090853263")), + Fq2::new( + MontFp!("2816510427748580758331037284777117739799287910327449993381818688383577828123182200904113516794492504322962636245776"), + MontFp!("0")), + ]; + + const PHI_Y_DEN: &'static [::BaseField] = &[ + Fq2::new( + MontFp!("4002409555221667393417789825735904156556882819939007885332058136124031650490837864442687629129015664037894272559355"), + MontFp!("4002409555221667393417789825735904156556882819939007885332058136124031650490837864442687629129015664037894272559355")), + Fq2::new( + MontFp!("0"), + MontFp!("4002409555221667393417789825735904156556882819939007885332058136124031650490837864442687629129015664037894272559571")), + Fq2::new( + MontFp!("18"), + MontFp!("4002409555221667393417789825735904156556882819939007885332058136124031650490837864442687629129015664037894272559769")), + Fq2::new( + MontFp!("1"), + MontFp!("0")), + ]; +} diff --git a/test-curves/src/bls12_381/g2_swu_iso.rs b/test-curves/src/bls12_381/g2_swu_iso.rs new file mode 100644 index 000000000..337ad1c78 --- /dev/null +++ b/test-curves/src/bls12_381/g2_swu_iso.rs @@ -0,0 +1,88 @@ +use crate::bls12_381::*; +use ark_ec::models::{ + short_weierstrass::{Affine, SWCurveConfig}, + CurveConfig, +}; +use ark_ff::MontFp; + +use ark_ec::hashing::curve_maps::swu::SWUParams; + +type G2Affine = Affine; + +#[derive(Clone, Default, PartialEq, Eq)] +pub struct SwuIsoParameters; + +impl CurveConfig for SwuIsoParameters { + type BaseField = Fq2; + type ScalarField = Fr; + + /// Cofactors of g2_iso and g2 are the same. + /// COFACTOR = (x^8 - 4 x^7 + 5 x^6) - (4 x^4 + 6 x^3 - 4 x^2 - 4 x + 13) // + /// 9 + /// = 305502333931268344200999753193121504214466019254188142667664032982267604182971884026507427359259977847832272839041616661285803823378372096355777062779109 + #[rustfmt::skip] + const COFACTOR: &'static [u64] = &[ + 0xcf1c38e31c7238e5, + 0x1616ec6e786f0c70, + 0x21537e293a6691ae, + 0xa628f1cb4d9e82ef, + 0xa68a205b2e5a7ddf, + 0xcd91de4547085aba, + 0x91d50792876a202, + 0x5d543a95414e7f1, + ]; + + /// COFACTOR_INV = COFACTOR^{-1} mod r + /// 26652489039290660355457965112010883481355318854675681319708643586776743290055 + #[rustfmt::skip] + const COFACTOR_INV: Fr = MontFp!("26652489039290660355457965112010883481355318854675681319708643586776743290055"); +} + +// https://datatracker.ietf.org/doc/draft-irtf-cfrg-hash-to-curve/ +// Hashing to Elliptic Curves +// 8.8.2. BLS12-381 G2 +// * E': y'^2 = x'^3 + A' * x' + B', where +// +// - A' = 240 * I +// +// - B' = 1012 * (1 + I) +// +// * Z: -(2 + I) +impl SWCurveConfig for SwuIsoParameters { + /// COEFF_A = 240 * I + const COEFF_A: Fq2 = Fq2::new(MontFp!("0"), MontFp!("240")); + + /// COEFF_B = 1012 + 1012 * I + const COEFF_B: Fq2 = Fq2::new(MontFp!("1012"), MontFp!("1012")); + + const GENERATOR: G2Affine = G2Affine::new_unchecked(G2_GENERATOR_X, G2_GENERATOR_Y); +} + +/// Lexicographically smallest, valid x-coordinate of a point P on the curve (with its corresponding y) multiplied by the cofactor. +/// P_x = 1 +/// P_y = 1199519624119946820355795551601605892701128025883245860600494152840508171012839086684258857614063467038089173303263 + 2721622435888802346851223931977585460571674503470326381323808470905804676865417627238564067834747838523978879375704 * I +/// P = E(P_x, P_y) +/// G = P * COFACTOR +const G2_GENERATOR_X: Fq2 = Fq2::new(G2_GENERATOR_X_C0, G2_GENERATOR_X_C1); +const G2_GENERATOR_Y: Fq2 = Fq2::new(G2_GENERATOR_Y_C0, G2_GENERATOR_Y_C1); + +const G2_GENERATOR_X_C0: Fq = MontFp!("2595569946714414516067015540153643524656442638788025933727967960306287756885400469291119095920626560658971252184199"); +const G2_GENERATOR_X_C1: Fq = MontFp!("1037079738597573406765355774006601850633656296583542639082316151670128374872040593053087014315526494961765370307992"); +const G2_GENERATOR_Y_C0: Fq = MontFp!("3927929472994661655038722055497331445175131868678630546921475383290711810401295661250673209427965906654429357114487"); +const G2_GENERATOR_Y_C1: Fq = MontFp!("3300326318345570015758639333209189167876318321385223785506096497597561910823001330832964776707374262378602791224889"); + +impl SWUParams for SwuIsoParameters { + // ZETA = -(2 + u) as per IETF draft. + const ZETA: Fq2 = Fq2::new(MontFp!("-2"), MontFp!("-1")); +} +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn test_gen() { + let gen: G2Affine = g2_swu_iso::SwuIsoParameters::GENERATOR; + assert!(gen.is_on_curve()); + assert!(gen.is_in_correct_subgroup_assuming_on_curve()); + } +} diff --git a/test-curves/src/bls12_381/mod.rs b/test-curves/src/bls12_381/mod.rs index eed99d0ce..3866c1a73 100644 --- a/test-curves/src/bls12_381/mod.rs +++ b/test-curves/src/bls12_381/mod.rs @@ -13,10 +13,14 @@ pub mod fq6; #[cfg(feature = "bls12_381_curve")] pub mod g1; #[cfg(feature = "bls12_381_curve")] +pub mod g1_swu_iso; +#[cfg(feature = "bls12_381_curve")] pub mod g2; - #[cfg(feature = "bls12_381_curve")] -pub use {fq::*, fq12::*, fq2::*, fq6::*, g1::*, g2::*}; +pub mod g2_swu_iso; +#[cfg(feature = "bls12_381_curve")] +pub use {fq::*, fq12::*, fq2::*, fq6::*, g1::*, g1_swu_iso::*, g2_swu_iso::*}; + #[cfg(test)] mod tests; diff --git a/test-curves/src/lib.rs b/test-curves/src/lib.rs index 580c509b0..6d94490de 100644 --- a/test-curves/src/lib.rs +++ b/test-curves/src/lib.rs @@ -3,6 +3,9 @@ extern crate ark_ff; pub use ark_ff::*; +extern crate ark_ec; +pub use ark_ec::*; + #[cfg(any(feature = "bls12_381_scalar_field", feature = "bls12_381_curve"))] pub mod bls12_381;