From 1c9637490c1d996df55fc0d16b155d2972d6f172 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Sat, 4 Mar 2017 02:00:59 +0000 Subject: [PATCH 1/5] Obsessive whitespace fix. --- src/curve.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/curve.rs b/src/curve.rs index 7a7e01c..7d176d7 100644 --- a/src/curve.rs +++ b/src/curve.rs @@ -121,7 +121,7 @@ impl Index for CompressedEdwardsY { impl CompressedEdwardsY { /// View this `CompressedEdwardsY` as an array of bytes. - pub fn to_bytes(&self) -> [u8;32] { + pub fn to_bytes(&self) -> [u8; 32] { self.0 } @@ -165,7 +165,7 @@ pub struct CompressedMontgomeryU(pub [u8; 32]); impl CompressedMontgomeryU { /// View this `CompressedMontgomeryU` as an array of bytes. - pub fn to_bytes(&self) -> [u8;32] { + pub fn to_bytes(&self) -> [u8; 32] { self.0 } From 915f41c643920d637e1d24904aeebe83e7b84ebf Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Sat, 4 Mar 2017 02:19:48 +0000 Subject: [PATCH 2/5] Fixup CompressedMontgomeryU::decompress() and ensure no divide-by-zeroes. Also, match Signal behaviour, and split off recovery of other coordinates into separate functions. --- src/curve.rs | 84 +++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 64 insertions(+), 20 deletions(-) diff --git a/src/curve.rs b/src/curve.rs index 7d176d7..50e669b 100644 --- a/src/curve.rs +++ b/src/curve.rs @@ -171,43 +171,78 @@ impl CompressedMontgomeryU { /// Attempt to decompress to an `ExtendedPoint`. /// - /// Note that since there are two curve points with the same + /// # Note + /// + /// Since there are two curve points with the same /// `u`-coordinate, the `u`-coordinate does not fully specify a - /// point. + /// point. That is, roundtripping between an `ExtendedPoint` and + /// a `CompressedMontgomeryU` discards its sign bit. /// - /// XXX match behaviour in Signal specification re: sign choice - /// and rewrite this note + /// # Warning /// - /// XXX check for div by zero: when is u = -1 ? - /// XXX exceptional points for the birational map + /// This function is *not* constant time. + /// + /// # Return + /// + /// An `Option`, which will be `None` if either condition holds: + /// + /// * `u = -1`, or + /// * `v` is not square. + // + // XXX any other exceptional points for the birational map? pub fn decompress(&self) -> Option { - // u = (1 + y) / (1 - y) - // v = sqrt(-486664) * u / x - // - // so - // - // y = (u - 1) / (u + 1) - - let u = FieldElement::from_bytes(&self.0); + let u: FieldElement = FieldElement::from_bytes(&self.0); // If u = -1, then v^2 = u*(u^2+486662*u+1) = 486660. // But 486660 is nonsquare mod p, so this is not a curve point. // - // XXX what does Signal do here? - // // Note: currently, without this check, u = -1 will accidentally // decode to a valid (but incorrect) point, since 0.invert() = 0. if u == FieldElement::minus_one() { return None; } - let u_plus_1_inv = (&u + &FieldElement::one()).invert(); - let y = &(&u - &FieldElement::one()) * &u_plus_1_inv; + let y: FieldElement = CompressedMontgomeryU::to_edwards_y(&u); // y = (u-1)/(u+1) - // XXX this does two inversions: the above + one in .decompress() - // is it possible to do one? CompressedEdwardsY(y.to_bytes()).decompress() } + + /// Given a Montgomery `u` coordinate, compute an Edwards `y` via + /// `y = (u-1)/(u+1)`. + /// + /// # Note + /// + /// Since `u = (1+y)/(1-y)` and `v = √(u(u²+Au+1))`, we can see that + /// `y = (u-1)/(u+1)`. + /// + /// # Return + /// + /// A `FieldElement` corresponding to this coordinate, but in Edwards form. + fn to_edwards_y(u: &FieldElement) -> FieldElement { + &(u - &FieldElement::one()) * &(u + &FieldElement::one()).invert() + } + + /// Given a Montgomery `u` coordinate, compute the corresponding + /// Montgomery `v` coordinate by computing the right-hand side of + /// the Montgomery field equation, `v² = u(u² + Au +1)`. + /// + /// # Return + /// + /// A tuple of (`u8`, `FieldElement`), where the `u8` is `1` if the v² was + /// actually a square and `0` if otherwise, along with a `FieldElement`: the + /// Montgomery `v` corresponding to this `u`. + fn to_montgomery_v(u: &FieldElement) -> (u8, FieldElement) { + let one: FieldElement = FieldElement::one(); + let v_squared: FieldElement = u * &(&(&u.square() + &(&(&constants::A * u) + &one))); + let v_inv: FieldElement; + let v: FieldElement; + let okay: u8; + + let (okay, v_inv) = v_squared.invsqrt(); + let v = &v_inv * &v_squared; + + (okay, v) + } } // ------------------------------------------------------------------------ @@ -1325,6 +1360,15 @@ mod test { assert!(ExtendedPoint::identity().is_identity()); } + #[test] + fn test_montgomery_u_is_neg_one_rejected() { + let fe_u: FieldElement = FieldElement::minus_one(); + let u: CompressedMontgomeryU = CompressedMontgomeryU(fe_u.to_bytes()); + let result: Option = u.decompress(); + + assert!(result.is_none()); + } + #[bench] fn bench_basepoint_mult(b: &mut Bencher) { b.iter(|| ExtendedPoint::basepoint_mult(&A_SCALAR)); From b9de92aeafd38ea5829794ddcc8f2b1c9c51b619 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Sat, 4 Mar 2017 02:22:34 +0000 Subject: [PATCH 3/5] Add benchmarks for Edwards-Y and Mongomery-U decompression. --- src/curve.rs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/curve.rs b/src/curve.rs index 50e669b..e87ae3c 100644 --- a/src/curve.rs +++ b/src/curve.rs @@ -1443,4 +1443,20 @@ mod test { b.iter(| | p1.mult_by_pow_2(4) ); } + + #[bench] + fn bench_compress_edwards(b: &mut Bencher) { + let mut rng: OsRng = OsRng::new().unwrap(); + let p1: ExtendedPoint = ExtendedPoint::basepoint_mult(&Scalar::random(&mut rng)); + + b.iter(| | p1.compress() ); + } + + #[bench] + fn bench_compress_montgomery(b: &mut Bencher) { + let mut rng: OsRng = OsRng::new().unwrap(); + let p1: ExtendedPoint = ExtendedPoint::basepoint_mult(&Scalar::random(&mut rng)); + + b.iter(| | p1.compress_montgomery() ); + } } From cfd0e8ad6ea66c7f0813bf121de94a5d12a82391 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Sat, 4 Mar 2017 02:23:07 +0000 Subject: [PATCH 4/5] Add method for recovery of Edwards x-coordinate given Montgomery (u, v). This function is necessary for matching Signal's hash_to_point() behaviour. --- src/curve.rs | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/src/curve.rs b/src/curve.rs index e87ae3c..92457c5 100644 --- a/src/curve.rs +++ b/src/curve.rs @@ -243,6 +243,41 @@ impl CompressedMontgomeryU { (okay, v) } + + /// Given Montgomery coordinates `(u, v)`, recover the Edwards `x` coordinate. + /// + /// # Inputs + /// + /// * `u` and `v` are both `&FieldElement`s, corresponding the the `(u, v)` + /// coordinates of this `CompressedMontgomeryU`. + /// * `sign` is an &u8. + /// + /// ## Explanation of choice of `sign` + /// + /// ### Original Signal behaviour: + /// + /// - `1u8` will leave `x` negative if it is negative, and will negate + /// `x` if it is positive, and + /// - `0u8` will leave `x` positive if it is positive, and will negate + /// `x` if it is negative. + /// + /// Hence, if `sign` is `1u8`, the returned `x` will be negative. + /// Otherwise, if `sign` is `0u8`, the returned `x` will be positive. + /// + /// # Return + /// + /// A `FieldElement`, the Edwards `x` coordinate, by using `(u, v)` to + /// convert from Montgomery to Edwards form via the right-hand side of the + /// equation: `x=(u/v)*sqrt(-A-2)`. + fn to_edwards_x(u: &FieldElement, v: &FieldElement, sign: &u8) -> FieldElement { + let mut x: FieldElement = &(u * &v.invert()) * &constants::SQRT_MINUS_APLUS2; + let neg_x: FieldElement = -(&x); + let current_sign: u8 = x.is_negative_ed25519(); + + // Negate x to match the sign: + x.conditional_assign(&neg_x, current_sign ^ sign); + x + } } // ------------------------------------------------------------------------ From 2c83d48cfa4648cf93e8dd2041fbf0da1b073f43 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Sat, 4 Mar 2017 05:29:19 +0000 Subject: [PATCH 5/5] Demote a docstring note to inline code comment to avoid confusing users. --- src/curve.rs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/curve.rs b/src/curve.rs index 92457c5..da2d1a1 100644 --- a/src/curve.rs +++ b/src/curve.rs @@ -210,15 +210,11 @@ impl CompressedMontgomeryU { /// Given a Montgomery `u` coordinate, compute an Edwards `y` via /// `y = (u-1)/(u+1)`. /// - /// # Note - /// - /// Since `u = (1+y)/(1-y)` and `v = √(u(u²+Au+1))`, we can see that - /// `y = (u-1)/(u+1)`. - /// /// # Return /// /// A `FieldElement` corresponding to this coordinate, but in Edwards form. fn to_edwards_y(u: &FieldElement) -> FieldElement { + // Since `u = (1+y)/(1-y)` and `v = √(u(u²+Au+1))`, so `y = (u-1)/(u+1)`. &(u - &FieldElement::one()) * &(u + &FieldElement::one()).invert() }