diff --git a/curve25519-dalek/src/edwards.rs b/curve25519-dalek/src/edwards.rs index 7838a34..7da4678 100644 --- a/curve25519-dalek/src/edwards.rs +++ b/curve25519-dalek/src/edwards.rs @@ -93,6 +93,8 @@ // affine and projective cakes and eat both of them too. #![allow(non_snake_case)] +mod affine; + use cfg_if::cfg_if; use core::array::TryFromSliceError; use core::borrow::Borrow; @@ -146,6 +148,8 @@ use crate::traits::BasepointTable; use crate::traits::ValidityCheck; use crate::traits::{Identity, IsIdentity}; +use affine::AffinePoint; + #[cfg(feature = "alloc")] use crate::traits::MultiscalarMul; #[cfg(feature = "alloc")] @@ -528,7 +532,7 @@ impl EdwardsPoint { } } - /// Dehomogenize to a AffineNielsPoint. + /// Dehomogenize to a `AffineNielsPoint`. /// Mainly for testing. pub(crate) fn as_affine_niels(&self) -> AffineNielsPoint { let recip = self.Z.invert(); @@ -542,6 +546,14 @@ impl EdwardsPoint { } } + /// Dehomogenize to `AffinePoint`. + pub(crate) fn to_affine(self) -> AffinePoint { + let recip = self.Z.invert(); + let x = &self.X * &recip; + let y = &self.Y * &recip; + AffinePoint { x, y } + } + /// Convert this `EdwardsPoint` on the Edwards model to the /// corresponding `MontgomeryPoint` on the Montgomery model. /// @@ -587,10 +599,7 @@ impl EdwardsPoint { /// Compress this point to `CompressedEdwardsY` format. pub fn compress(&self) -> CompressedEdwardsY { - let recip = self.Z.invert(); - let x = &self.X * &recip; - let y = &self.Y * &recip; - Self::compress_affine(x, y) + self.to_affine().compress() } /// Compress several `EdwardsPoint`s into `CompressedEdwardsY` format, using a batch inversion @@ -606,19 +615,11 @@ impl EdwardsPoint { .map(|(input, recip)| { let x = &input.X * recip; let y = &input.Y * recip; - Self::compress_affine(x, y) + AffinePoint { x, y }.compress() }) .collect() } - /// Compress affine Edwards coordinates into `CompressedEdwardsY` format. - #[inline] - fn compress_affine(x: FieldElement, y: FieldElement) -> CompressedEdwardsY { - let mut s = y.to_bytes(); - s[31] ^= x.is_negative().unwrap_u8() << 7; - CompressedEdwardsY(s) - } - #[cfg(feature = "digest")] /// Maps the digest of the input bytes to the curve. This is NOT a hash-to-curve function, as /// it produces points with a non-uniform distribution. Rather, it performs something that diff --git a/curve25519-dalek/src/edwards/affine.rs b/curve25519-dalek/src/edwards/affine.rs new file mode 100644 index 0000000..aa68d8e --- /dev/null +++ b/curve25519-dalek/src/edwards/affine.rs @@ -0,0 +1,114 @@ +use super::{CompressedEdwardsY, EdwardsPoint}; +use crate::traits::Identity; +use crate::{field::FieldElement, Scalar}; +use core::ops::Mul; +use subtle::{Choice, ConditionallySelectable, ConstantTimeEq}; + +#[cfg(feature = "zeroize")] +use zeroize::DefaultIsZeroes; + +/// Affine Edwards point on untwisted curve. +#[derive(Copy, Clone, Debug)] +pub struct AffinePoint { + pub(super) x: FieldElement, + pub(super) y: FieldElement, +} + +impl ConstantTimeEq for AffinePoint { + fn ct_eq(&self, other: &Self) -> Choice { + self.x.ct_eq(&other.x) & self.y.ct_eq(&other.y) + } +} + +impl ConditionallySelectable for AffinePoint { + fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { + Self { + x: FieldElement::conditional_select(&a.x, &b.x, choice), + y: FieldElement::conditional_select(&a.y, &b.y, choice), + } + } +} + +impl Default for AffinePoint { + fn default() -> AffinePoint { + AffinePoint::identity() + } +} + +impl Identity for AffinePoint { + fn identity() -> AffinePoint { + AffinePoint { + x: FieldElement::ZERO, + y: FieldElement::ONE, + } + } +} + +impl PartialEq for AffinePoint { + fn eq(&self, other: &Self) -> bool { + self.ct_eq(other).into() + } +} + +impl Eq for AffinePoint {} + +#[cfg(feature = "zeroize")] +impl DefaultIsZeroes for AffinePoint {} + +impl AffinePoint { + /// Convert to extended coordinates. + pub fn to_edwards(self) -> EdwardsPoint { + EdwardsPoint { + X: self.x, + Y: self.y, + Z: FieldElement::ONE, + T: &self.x * &self.y, + } + } + + /// Compress affine Edwards coordinates into `CompressedEdwardsY` format. + #[inline] + pub fn compress(self) -> CompressedEdwardsY { + let mut s = self.y.to_bytes(); + s[31] ^= self.x.is_negative().unwrap_u8() << 7; + CompressedEdwardsY(s) + } +} + +impl Mul for Scalar { + type Output = EdwardsPoint; + + #[inline] + fn mul(self, rhs: AffinePoint) -> EdwardsPoint { + self * &rhs + } +} + +impl Mul<&AffinePoint> for Scalar { + type Output = EdwardsPoint; + + #[inline] + fn mul(self, rhs: &AffinePoint) -> EdwardsPoint { + rhs.to_edwards() * self + } +} + +#[cfg(test)] +mod tests { + use super::{AffinePoint, EdwardsPoint, Identity}; + use crate::constants; + + #[test] + fn identity_conversion() { + assert_eq!( + AffinePoint::identity().to_edwards(), + EdwardsPoint::identity() + ); + } + + #[test] + fn generator_round_trip() { + let basepoint = constants::ED25519_BASEPOINT_POINT; + assert_eq!(basepoint.to_affine().to_edwards(), basepoint); + } +}