diff --git a/src/jubjub/edwards.rs b/src/jubjub/edwards.rs index bf9b3f4..130fd59 100644 --- a/src/jubjub/edwards.rs +++ b/src/jubjub/edwards.rs @@ -81,6 +81,53 @@ impl PartialEq for Point { } impl Point { + pub fn get_for_y(y: E::Fr, sign: bool, params: &E::Params) -> Option + { + // Given a y on the curve, x^2 = (y^2 - 1) / (dy^2 + 1) + // This is defined for all valid y-coordinates, + // as dy^2 + 1 = 0 has no solution in Fr. + + // tmp1 = y^2 + let mut tmp1 = y; + tmp1.square(); + + // tmp2 = (y^2 * d) + 1 + let mut tmp2 = tmp1; + tmp2.mul_assign(params.edwards_d()); + tmp2.add_assign(&E::Fr::one()); + + // tmp1 = y^2 - 1 + tmp1.sub_assign(&E::Fr::one()); + + match tmp2.inverse() { + Some(tmp2) => { + // tmp1 = (y^2 - 1) / (dy^2 + 1) + tmp1.mul_assign(&tmp2); + + match tmp1.sqrt() { + Some(mut x) => { + if x.into_repr().is_odd() != sign { + x.negate(); + } + + let mut t = x; + t.mul_assign(&y); + + Some(Point { + x: x, + y: y, + t: t, + z: E::Fr::one(), + _marker: PhantomData + }) + }, + None => None + } + }, + None => None + } + } + /// This guarantees the point is in the prime order subgroup pub fn mul_by_cofactor(&self, params: &E::Params) -> Point { @@ -94,44 +141,10 @@ impl Point { pub fn rand(rng: &mut R, params: &E::Params) -> Self { loop { - // given an x on the curve, y^2 = (1 + x^2) / (1 - dx^2) - let x: E::Fr = rng.gen(); - let mut x2 = x; - x2.square(); - - let mut num = E::Fr::one(); - num.add_assign(&x2); - - x2.mul_assign(params.edwards_d()); - - let mut den = E::Fr::one(); - den.sub_assign(&x2); - - match den.inverse() { - Some(invden) => { - num.mul_assign(&invden); - - match num.sqrt() { - Some(mut y) => { - if y.into_repr().is_odd() != rng.gen() { - y.negate(); - } - - let mut t = x; - t.mul_assign(&y); - - return Point { - x: x, - y: y, - t: t, - z: E::Fr::one(), - _marker: PhantomData - } - }, - None => {} - } - }, - None => {} + let y: E::Fr = rng.gen(); + + if let Some(p) = Self::get_for_y(y, rng.gen(), params) { + return p; } } } diff --git a/src/jubjub/tests.rs b/src/jubjub/tests.rs index 749b4cf..82b8eb2 100644 --- a/src/jubjub/tests.rs +++ b/src/jubjub/tests.rs @@ -20,6 +20,7 @@ pub fn test_suite(params: &E::Params) { test_back_and_forth::(params); test_jubjub_params::(params); test_rand::(params); + test_get_for::(params); test_identities::(params); test_addition_associativity::(params); test_order::(params); @@ -225,6 +226,25 @@ fn test_identities(params: &E::Params) { } } +fn test_get_for(params: &E::Params) { + let rng = &mut XorShiftRng::from_seed([0x3dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + for _ in 0..1000 { + let y = E::Fr::rand(rng); + let sign = bool::rand(rng); + + if let Some(mut p) = edwards::Point::::get_for_y(y, sign, params) { + assert!(p.into_xy().0.into_repr().is_odd() == sign); + p = p.negate(); + assert!( + edwards::Point::::get_for_y(y, !sign, params).unwrap() + == + p + ); + } + } +} + fn test_rand(params: &E::Params) { let rng = &mut XorShiftRng::from_seed([0x3dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); @@ -288,6 +308,25 @@ fn test_jubjub_params(params: &E::Params) { assert!(a.legendre() == LegendreSymbol::QuadraticResidue); } + { + // Other convenient sanity checks regarding d + + // tmp = d + let mut tmp = *params.edwards_d(); + + // 1 / d is nonsquare + assert!(tmp.inverse().unwrap().legendre() == LegendreSymbol::QuadraticNonResidue); + + // tmp = -d + tmp.negate(); + + // -d is nonsquare + assert!(tmp.legendre() == LegendreSymbol::QuadraticNonResidue); + + // 1 / -d is nonsquare + assert!(tmp.inverse().unwrap().legendre() == LegendreSymbol::QuadraticNonResidue); + } + { // Check that A^2 - 4 is nonsquare: let mut tmp = params.montgomery_a().clone();