diff --git a/src/decaf.rs b/src/decaf.rs index f8eb0c4..23511dd 100644 --- a/src/decaf.rs +++ b/src/decaf.rs @@ -194,6 +194,11 @@ impl<'de> Deserialize<'de> for DecafPoint { pub struct DecafPoint(pub ExtendedPoint); impl DecafPoint { + /// Convert this `DecafPoint` to its underlying `ExtendedPoint`. + pub fn to_edwards(&self) -> ExtendedPoint { + self.0 + } + /// Compress in Decaf format. pub fn compress(&self) -> CompressedDecaf { // Q: Do we want to encode twisted or untwisted? @@ -752,7 +757,7 @@ mod test { fn decaf_decompress_id() { let compressed_id = CompressedDecaf::identity(); let id = compressed_id.decompress().unwrap(); - assert_eq!(id.0.compress_edwards(), CompressedEdwardsY::identity()); + assert_eq!(id.to_edwards().compress(), CompressedEdwardsY::identity()); } #[test] @@ -764,11 +769,11 @@ mod test { #[test] fn decaf_basepoint_roundtrip() { let bp_compressed_decaf = constants::DECAF_ED25519_BASEPOINT_POINT.compress(); - let bp_recaf = bp_compressed_decaf.decompress().unwrap().0; + let bp_recaf = bp_compressed_decaf.decompress().unwrap().to_edwards(); // Check that bp_recaf differs from bp by a point of order 4 let diff = &constants::ED25519_BASEPOINT_POINT - &bp_recaf; let diff4 = diff.mult_by_pow_2(4); // XXX this is wrong - assert_eq!(diff4.compress_edwards(), CompressedEdwardsY::identity()); + assert_eq!(diff4.compress(), CompressedEdwardsY::identity()); } #[test] @@ -838,7 +843,7 @@ mod test { for _ in 0..100 { let P = DecafPoint::random(&mut rng); // Check that P is on the curve - assert!(P.0.is_valid()); + assert!(P.to_edwards().is_valid()); // Check that P is in the image of the decaf map P.compress(); } diff --git a/src/edwards.rs b/src/edwards.rs index 9a03295..0b8b333 100644 --- a/src/edwards.rs +++ b/src/edwards.rs @@ -89,7 +89,6 @@ use core::ops::Index; use constants; use field::FieldElement; use scalar::Scalar; -use montgomery::CompressedMontgomeryU; use montgomery::MontgomeryPoint; use subtle::slices_equal; @@ -124,7 +123,6 @@ impl CompressedEdwardsY { } /// Copy this `CompressedEdwardsY` to an array of bytes. - /// XXX is this useful? pub fn to_bytes(&self) -> [u8; 32] { self.0 } @@ -170,7 +168,7 @@ impl Serialize for ExtendedPoint { fn serialize(&self, serializer: S) -> Result where S: Serializer { - serializer.serialize_bytes(self.compress_edwards().as_bytes()) + serializer.serialize_bytes(self.compress().as_bytes()) } } @@ -394,8 +392,8 @@ impl ConditionallyAssignable for ExtendedPoint { impl Equal for ExtendedPoint { fn ct_eq(&self, other: &ExtendedPoint) -> u8 { - slices_equal(self.compress_edwards().as_bytes(), - other.compress_edwards().as_bytes()) + slices_equal(self.compress().as_bytes(), + other.compress().as_bytes()) } } @@ -437,7 +435,7 @@ impl ProjectivePoint { } /// Convert this point to a `CompressedEdwardsY` - pub fn compress_edwards(&self) -> CompressedEdwardsY { + pub fn compress(&self) -> CompressedEdwardsY { let recip = self.Z.invert(); let x = &self.X * &recip; let y = &self.Y * &recip; @@ -448,63 +446,67 @@ impl ProjectivePoint { CompressedEdwardsY(s) } - /// Convert this point to a Montgomery u-coordinate (affine). - /// Note that this discards the sign. + /// Convert this projective point in the Edwards model to its equivalent + /// projective point on the Montgomery form of the curve. /// - /// # Return - /// - `None` if `self` is the identity point; - /// - `Some(FieldElement)` otherwise. + /// Taking the Montgomery curve equation in affine coordinates: /// - fn convert_to_montgomery(&self) -> Option { - // u = (1 + y) / (1 - y) - // v = sqrt(-486664) * u / x - // - // since y = Y/Z, x = X/Z, - // - // u = (1 + Y/Z) / (1 - Y/Z); - // = (Z + Y) / (Z - Y); - // - // exceptional points: - // y = 1 <=> Y/Z = 1 <=> Z - Y = 0 - let Z_plus_Y = &self.Z + &self.Y; - let Z_minus_Y = &self.Z - &self.Y; - let u = &Z_plus_Y * &Z_minus_Y.invert(); - - if Z_minus_Y.is_zero() == 0u8 { - Some(u) - } else { - None - } - } - - /// Convert this point to a `CompressedMontgomeryU`. - /// Note that this discards the sign. + ///     E_(A,B) = Bv² = u³ + Au² + u   (1) /// - /// # Return - /// - `None` if `self` is the identity point; - /// - `Some(CompressedMontgomeryU)` otherwise. + /// and given its relations to the coordinates of the Edwards model: /// - pub fn compress_montgomery(&self) -> Option { - let u: Option = self.convert_to_montgomery(); - - if u.is_some() { - Some(CompressedMontgomeryU(u.unwrap().to_bytes())) - } else { - None - } - } - - /// Convert this point to its equivalent on the Montgomery form of - /// the curve, without compressing. + ///     u = (1+y)/(1-y)        (2) + ///     v = (λu)/(x) /// - /// DOCDOC - pub fn to_montgomery(&self) -> Option { - let u: Option = self.convert_to_montgomery(); - - if u.is_some() { - Some(MontgomeryPoint{ U: u.unwrap(), Z: FieldElement::one() }) - } else { - None + /// Converting from affine to projective coordinates in the Montgomery + /// model, we arrive at: + /// + ///     u = (Z+Y)/(Z-Y)        (3) + ///     v = λ * ((Z+Y)/(Z-Y)) * (Z/X) + /// + /// The transition between affine and projective is given by + /// + ///     u → U/W        (4) + ///     v → V/W + /// + /// thus the Montgomery curve equation (1) becomes + /// + ///     E_(A,B) : BV²W = U³ + AU²W + UW² ⊆ 𝗣^2  (5) + /// + /// Here, again, to differentiate from points in the twisted Edwards model, we + /// call the point `(x,y)` in affine coordinates `(u,v)` and similarly in projective + /// space we use `(U:V:W)`. However, since (as per Montgomery's original work) the + /// v-coordinate is superfluous to the definition of the group law, we merely + /// use `(U:W)`. + /// + /// Therefore, the direct translation between projective Montgomery points + /// and projective twisted Edwards points is + /// + ///     (U:W) = (Z+Y:Z-Y) (6) + /// + /// Note, however, that there appears to be an exception where `Z=Y`, + /// since—from equation 2—this would imply that `y=1` (thus causing the + /// denominator to be zero). If this is the case, then it follows from the + /// twisted Edwards curve equation + /// + ///     -x² + y² = 1 + dx²y² (7) + /// + /// that + /// + ///     -x² + 1 = 1 + dx² + /// + /// and, assuming that `d ≠ -1`, + /// + ///     -x² = x² + /// x = 0 + /// + /// Therefore, the only valid point with `y=1` is the twisted Edwards + /// identity point, which correctly becomes `(1:0)`, that is, the identity, + /// in the Montgomery model. + pub fn to_montgomery(&self) -> MontgomeryPoint { + MontgomeryPoint{ + U: &self.Z + &self.Y, + W: &self.Z - &self.Y, } } } @@ -547,25 +549,15 @@ impl ExtendedPoint { } } - /// DOCDOC - pub fn to_montgomery(&self) -> Option { + /// Convert this point to its equivalent on the Montgomery form of the + /// curve. + pub fn to_montgomery(&self) -> MontgomeryPoint { self.to_projective().to_montgomery() } /// Compress this point to `CompressedEdwardsY` format. - pub fn compress_edwards(&self) -> CompressedEdwardsY { - self.to_projective().compress_edwards() - } - - /// Convert this point to a `CompressedMontgomeryU`. - /// Note that this discards the sign. - /// - /// # Return - /// - `None` if `self` is the identity point; - /// - `Some(CompressedMontgomeryU)` otherwise. - /// - pub fn compress_montgomery(&self) -> Option { - self.to_projective().compress_montgomery() + pub fn compress(&self) -> CompressedEdwardsY { + self.to_projective().compress() } } @@ -1342,7 +1334,7 @@ mod test { assert!(bp.is_valid()); // Check that decompression actually gives the correct X coordinate assert_eq!(base_X, bp.X); - assert_eq!(bp.compress_edwards(), constants::BASE_CMPRSSD); + assert_eq!(bp.compress(), constants::BASE_CMPRSSD); } /// Test sign handling in decompression @@ -1365,7 +1357,7 @@ mod test { #[test] fn basepoint_mult_one_vs_basepoint() { let bp = &constants::ED25519_BASEPOINT_TABLE * &Scalar::one(); - let compressed = bp.compress_edwards(); + let compressed = bp.compress(); assert_eq!(compressed, constants::BASE_CMPRSSD); } @@ -1373,7 +1365,7 @@ mod test { #[test] fn basepoint_table_basepoint_function_correct() { let bp = constants::ED25519_BASEPOINT_TABLE.basepoint(); - assert_eq!(bp.compress_edwards(), constants::BASE_CMPRSSD); + assert_eq!(bp.compress(), constants::BASE_CMPRSSD); } /// Test `impl Add for ExtendedPoint` @@ -1382,7 +1374,7 @@ mod test { fn basepoint_plus_basepoint_vs_basepoint2() { let bp = constants::ED25519_BASEPOINT_POINT; let bp_added = &bp + &bp; - assert_eq!(bp_added.compress_edwards(), BASE2_CMPRSSD); + assert_eq!(bp_added.compress(), BASE2_CMPRSSD); } /// Test `impl Add for ExtendedPoint` @@ -1391,7 +1383,7 @@ mod test { fn basepoint_plus_basepoint_projective_niels_vs_basepoint2() { let bp = constants::ED25519_BASEPOINT_POINT; let bp_added = (&bp + &bp.to_projective_niels()).to_extended(); - assert_eq!(bp_added.compress_edwards(), BASE2_CMPRSSD); + assert_eq!(bp_added.compress(), BASE2_CMPRSSD); } /// Test `impl Add for ExtendedPoint` @@ -1401,7 +1393,7 @@ mod test { let bp = constants::ED25519_BASEPOINT_POINT; let bp_affine_niels = bp.to_affine_niels(); let bp_added = (&bp + &bp_affine_niels).to_extended(); - assert_eq!(bp_added.compress_edwards(), BASE2_CMPRSSD); + assert_eq!(bp_added.compress(), BASE2_CMPRSSD); } /// Check that equality of `ExtendedPoints` handles projective @@ -1426,15 +1418,15 @@ mod test { let aB = &constants::ED25519_BASEPOINT_TABLE * &A_SCALAR; let aB_affine_niels = aB.to_affine_niels(); let also_aB = (&ExtendedPoint::identity() + &aB_affine_niels).to_extended(); - assert_eq!( aB.compress_edwards(), - also_aB.compress_edwards()); + assert_eq!( aB.compress(), + also_aB.compress()); } /// Test basepoint_mult versus a known scalar multiple from ed25519.py #[test] fn basepoint_mult_vs_ed25519py() { let aB = &constants::ED25519_BASEPOINT_TABLE * &A_SCALAR; - assert_eq!(aB.compress_edwards(), A_TIMES_BASEPOINT); + assert_eq!(aB.compress(), A_TIMES_BASEPOINT); } /// Test that multiplication by the basepoint order kills the basepoint @@ -1452,20 +1444,20 @@ mod test { let table = EdwardsBasepointTable::create(&constants::ED25519_BASEPOINT_POINT); let aB_1 = &constants::ED25519_BASEPOINT_TABLE * &A_SCALAR; let aB_2 = &table * &A_SCALAR; - assert_eq!(aB_1.compress_edwards(), aB_2.compress_edwards()); + assert_eq!(aB_1.compress(), aB_2.compress()); } /// Test scalar_mult versus a known scalar multiple from ed25519.py #[test] fn scalar_mult_vs_ed25519py() { let aB = &constants::ED25519_BASEPOINT_POINT * &A_SCALAR; - assert_eq!(aB.compress_edwards(), A_TIMES_BASEPOINT); + assert_eq!(aB.compress(), A_TIMES_BASEPOINT); } /// Test basepoint.double() versus the 2*basepoint constant. #[test] fn basepoint_double_vs_basepoint2() { - assert_eq!(constants::ED25519_BASEPOINT_POINT.double().compress_edwards(), + assert_eq!(constants::ED25519_BASEPOINT_POINT.double().compress(), BASE2_CMPRSSD); } @@ -1474,14 +1466,14 @@ mod test { fn basepoint_mult_two_vs_basepoint2() { let mut two_bytes = [0u8; 32]; two_bytes[0] = 2; let bp2 = &constants::ED25519_BASEPOINT_TABLE * &Scalar(two_bytes); - assert_eq!(bp2.compress_edwards(), BASE2_CMPRSSD); + assert_eq!(bp2.compress(), BASE2_CMPRSSD); } /// Check that converting to projective and then back to extended round-trips. #[test] fn basepoint_projective_extended_round_trip() { assert_eq!(constants::ED25519_BASEPOINT_POINT - .to_projective().to_extended().compress_edwards(), + .to_projective().to_extended().compress(), constants::BASE_CMPRSSD); } @@ -1489,7 +1481,7 @@ mod test { #[test] fn basepoint16_vs_mult_by_pow_2_4() { let bp16 = constants::ED25519_BASEPOINT_POINT.mult_by_pow_2(4); - assert_eq!(bp16.compress_edwards(), BASE16_CMPRSSD); + assert_eq!(bp16.compress(), BASE16_CMPRSSD); } /// Test that the conditional assignment trait works for AffineNielsPoints. @@ -1517,7 +1509,7 @@ mod test { #[test] fn compressed_identity() { - assert_eq!(ExtendedPoint::identity().compress_edwards(), + assert_eq!(ExtendedPoint::identity().compress(), CompressedEdwardsY::identity()); } @@ -1555,7 +1547,7 @@ mod test { let P1 = &G * &s; let P2 = &s * &G; - assert!(P1.compress_edwards().to_bytes() == P2.compress_edwards().to_bytes()); + assert!(P1.compress().to_bytes() == P2.compress().to_bytes()); } #[test] @@ -1579,7 +1571,7 @@ mod test { fn double_scalar_mult_basepoint_vs_ed25519py() { let A = A_TIMES_BASEPOINT.decompress().unwrap(); let result = vartime::double_scalar_mult_basepoint(&A_SCALAR, &A, &B_SCALAR); - assert_eq!(result.compress_edwards(), DOUBLE_SCALAR_MULT_RESULT); + assert_eq!(result.compress(), DOUBLE_SCALAR_MULT_RESULT); } #[test] @@ -1589,7 +1581,7 @@ mod test { &[A_SCALAR, B_SCALAR], &[A, constants::ED25519_BASEPOINT_POINT] ); - assert_eq!(result.compress_edwards(), DOUBLE_SCALAR_MULT_RESULT); + assert_eq!(result.compress(), DOUBLE_SCALAR_MULT_RESULT); } #[test] @@ -1604,7 +1596,7 @@ mod test { &[A, constants::ED25519_BASEPOINT_POINT] ); - assert_eq!(result_vartime.compress_edwards(), result_consttime.compress_edwards()); + assert_eq!(result_vartime.compress(), result_consttime.compress()); } } @@ -1616,7 +1608,7 @@ mod test { fn serde_cbor_basepoint_roundtrip() { let output = serde_cbor::to_vec(&constants::ED25519_BASEPOINT_POINT).unwrap(); let parsed: ExtendedPoint = serde_cbor::from_slice(&output).unwrap(); - assert_eq!(parsed.compress_edwards(), constants::BASE_CMPRSSD); + assert_eq!(parsed.compress(), constants::BASE_CMPRSSD); } #[test] @@ -1652,7 +1644,7 @@ mod bench { #[bench] fn edwards_compress(b: &mut Bencher) { let B = &constants::ED25519_BASEPOINT_POINT; - b.iter(|| B.compress_edwards()); + b.iter(|| B.compress()); } #[bench] diff --git a/src/montgomery.rs b/src/montgomery.rs index 0b2c16a..f2e0268 100644 --- a/src/montgomery.rs +++ b/src/montgomery.rs @@ -119,7 +119,7 @@ impl CompressedMontgomeryU { /// # Returns /// /// A projective `MontgomeryPoint` corresponding to this compressed point. - pub fn decompress_montgomery(&self) -> MontgomeryPoint { + pub fn decompress(&self) -> MontgomeryPoint { MontgomeryPoint{ // XXX is it a problem here if we're not using a canonical encoding? —isis U: FieldElement::from_bytes(&self.0), @@ -257,8 +257,8 @@ impl Identity for MontgomeryPoint { /// `1` if the points are equal, and `0` otherwise. impl Equal for MontgomeryPoint { fn ct_eq(&self, that: &MontgomeryPoint) -> u8 { - slices_equal(self.compress_montgomery().as_bytes(), - that.compress_montgomery().as_bytes()) + slices_equal(self.compress().as_bytes(), + that.compress().as_bytes()) } } @@ -301,7 +301,7 @@ impl MontgomeryPoint { /// # Returns /// /// A `CompressedMontgomeryU`. - pub fn compress_montgomery(&self) -> CompressedMontgomeryU { + pub fn compress(&self) -> CompressedMontgomeryU { let u_affine: FieldElement = &self.U * &self.W.invert(); CompressedMontgomeryU(u_affine.to_bytes()) @@ -425,15 +425,15 @@ mod test { /// Test Montgomery conversion against the X25519 basepoint. #[test] fn basepoint_to_montgomery() { - assert_eq!(constants::ED25519_BASEPOINT_POINT.compress_montgomery().unwrap(), + assert_eq!(constants::ED25519_BASEPOINT_POINT.to_montgomery().compress(), BASE_COMPRESSED_MONTGOMERY); } /// Test Montgomery conversion against the X25519 basepoint. #[test] fn basepoint_from_montgomery() { - assert_eq!(BASE_COMPRESSED_MONTGOMERY.decompress_edwards().unwrap().compress_edwards(), - constants::BASE_CMPRSSD); + assert_eq!(BASE_COMPRESSED_MONTGOMERY, + constants::BASE_CMPRSSD.decompress().unwrap().to_montgomery().compress()); } /// If u = -1, then v^2 = u*(u^2+486662*u+1) = 486660. @@ -448,26 +448,28 @@ mod test { assert!(div_by_zero_u.decompress_edwards().is_none()); } - /// Montgomery compression of the identity point should - /// fail (it's sent to infinity). + /// Montgomery compression of the identity point should not fail (since the + /// mapping in `ProjectivePoint.to_montgomery()` should be valid for the + /// identity. #[test] fn identity_to_monty() { let id = ExtendedPoint::identity(); - assert!(id.compress_montgomery().is_none()); + assert_eq!(id.to_montgomery().compress(), MontgomeryPoint::identity().compress()); } #[test] fn projective_to_affine_roundtrips() { - let p = BASE_COMPRESSED_MONTGOMERY.decompress_montgomery(); + assert_eq!(BASE_COMPRESSED_MONTGOMERY.decompress().compress(), + BASE_COMPRESSED_MONTGOMERY); } #[test] fn differential_double_matches_double() { let p: ExtendedPoint = constants::ED25519_BASEPOINT_POINT.double(); - let q: MontgomeryPoint = BASE_COMPRESSED_MONTGOMERY.decompress_montgomery().differential_double(); + let q: MontgomeryPoint = BASE_COMPRESSED_MONTGOMERY.decompress().differential_double(); - assert_eq!(p.compress_montgomery().unwrap(), q.compress_montgomery()); + assert_eq!(p.to_montgomery().compress(), q.compress()); } #[test] @@ -480,13 +482,13 @@ mod test { let p2: ExtendedPoint = &constants::ED25519_BASEPOINT_TABLE * &s2; let diff: ExtendedPoint = &p1 - &p2; - let p1m: MontgomeryPoint = p1.to_montgomery().unwrap(); - let p2m: MontgomeryPoint = p2.to_montgomery().unwrap(); - let diffm: MontgomeryPoint = diff.to_montgomery().unwrap(); + let p1m: MontgomeryPoint = p1.to_montgomery(); + let p2m: MontgomeryPoint = p2.to_montgomery(); + let diffm: MontgomeryPoint = diff.to_montgomery(); let result = p1m.differential_add(&p2m, &diffm); - assert_eq!(result.compress_montgomery(), (&p1 + &p2).compress_montgomery().unwrap()); + assert_eq!(result.compress(), (&p1 + &p2).to_montgomery().compress()); } #[test] @@ -495,20 +497,21 @@ mod test { let s: Scalar = Scalar::random(&mut csprng); let p_edwards: ExtendedPoint = &constants::ED25519_BASEPOINT_TABLE * &s; - let p_montgomery: MontgomeryPoint = p_edwards.to_montgomery().unwrap(); + let p_montgomery: MontgomeryPoint = p_edwards.to_montgomery(); let expected = &s * &p_edwards; let result = &s * &p_montgomery; - assert_eq!(result.compress_montgomery(), expected.compress_montgomery().unwrap()) + assert_eq!(result.compress(), expected.to_montgomery().compress()) } #[test] fn ladder_basepoint_times_two_matches_double() { let two: Scalar = Scalar::from_u64(2u64); - let result: MontgomeryPoint = &BASE_COMPRESSED_MONTGOMERY.decompress_montgomery() * &two; - let mut expected: ExtendedPoint = constants::ED25519_BASEPOINT_POINT.double(); + let result: MontgomeryPoint = &BASE_COMPRESSED_MONTGOMERY.decompress() * &two; + let expected: ExtendedPoint = constants::ED25519_BASEPOINT_POINT.double(); + + assert_eq!(result.compress(), expected.to_montgomery().compress()); - assert_eq!(result.compress_montgomery(), expected.compress_montgomery().unwrap()); } }