Skip to content

Commit cdf999e

Browse files
committed
curve: extract AffinePoint type
Based on discussions about `elliptic-curve` trait impls in #746, and observing a similar type in `ed448-goldilocks` which inspired this one (not to mention in all of the @RustCrypto elliptic curve crates), adds an `AffinePoint` type with `x` and `y` coordinates. For now, the type is kept out of the public API, and used as an implementation detail for point compression. However, it's been written with the intent of eventually stabilizing and exposing it. It's been marked `pub` so unused functionality doesn't automatically trigger dead code lints. Further work could include refactoring point decompression to first produce an `AffinePoint` and then convert to extended twisted Edwards coordinates (i.e. `EdwardsPoint`), which is more or less what the existing `step_1` and `step_2` functions do (`step_1` technically produces projective coordinates, but `Z` is always set to `ONE`).
1 parent cf7b099 commit cdf999e

File tree

2 files changed

+129
-14
lines changed

2 files changed

+129
-14
lines changed

curve25519-dalek/src/edwards.rs

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,8 @@
9393
// affine and projective cakes and eat both of them too.
9494
#![allow(non_snake_case)]
9595

96+
mod affine;
97+
9698
use cfg_if::cfg_if;
9799
use core::array::TryFromSliceError;
98100
use core::borrow::Borrow;
@@ -146,6 +148,8 @@ use crate::traits::BasepointTable;
146148
use crate::traits::ValidityCheck;
147149
use crate::traits::{Identity, IsIdentity};
148150

151+
use affine::AffinePoint;
152+
149153
#[cfg(feature = "alloc")]
150154
use crate::traits::MultiscalarMul;
151155
#[cfg(feature = "alloc")]
@@ -528,7 +532,7 @@ impl EdwardsPoint {
528532
}
529533
}
530534

531-
/// Dehomogenize to a AffineNielsPoint.
535+
/// Dehomogenize to a `AffineNielsPoint`.
532536
/// Mainly for testing.
533537
pub(crate) fn as_affine_niels(&self) -> AffineNielsPoint {
534538
let recip = self.Z.invert();
@@ -542,6 +546,14 @@ impl EdwardsPoint {
542546
}
543547
}
544548

549+
/// Convert to `AffinePoint`.
550+
pub(crate) fn to_affine(self) -> AffinePoint {
551+
let recip = self.Z.invert();
552+
let x = &self.X * &recip;
553+
let y = &self.Y * &recip;
554+
AffinePoint { x, y }
555+
}
556+
545557
/// Convert this `EdwardsPoint` on the Edwards model to the
546558
/// corresponding `MontgomeryPoint` on the Montgomery model.
547559
///
@@ -587,10 +599,7 @@ impl EdwardsPoint {
587599

588600
/// Compress this point to `CompressedEdwardsY` format.
589601
pub fn compress(&self) -> CompressedEdwardsY {
590-
let recip = self.Z.invert();
591-
let x = &self.X * &recip;
592-
let y = &self.Y * &recip;
593-
Self::compress_affine(x, y)
602+
self.to_affine().compress()
594603
}
595604

596605
/// Compress several `EdwardsPoint`s into `CompressedEdwardsY` format, using a batch inversion
@@ -606,19 +615,11 @@ impl EdwardsPoint {
606615
.map(|(input, recip)| {
607616
let x = &input.X * recip;
608617
let y = &input.Y * recip;
609-
Self::compress_affine(x, y)
618+
AffinePoint { x, y }.compress()
610619
})
611620
.collect()
612621
}
613622

614-
/// Compress affine Edwards coordinates into `CompressedEdwardsY` format.
615-
#[inline]
616-
fn compress_affine(x: FieldElement, y: FieldElement) -> CompressedEdwardsY {
617-
let mut s = y.to_bytes();
618-
s[31] ^= x.is_negative().unwrap_u8() << 7;
619-
CompressedEdwardsY(s)
620-
}
621-
622623
#[cfg(feature = "digest")]
623624
/// Maps the digest of the input bytes to the curve. This is NOT a hash-to-curve function, as
624625
/// it produces points with a non-uniform distribution. Rather, it performs something that
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
use super::{CompressedEdwardsY, EdwardsPoint};
2+
use crate::traits::Identity;
3+
use crate::{field::FieldElement, Scalar};
4+
use core::ops::Mul;
5+
use subtle::{Choice, ConditionallySelectable, ConstantTimeEq};
6+
7+
#[cfg(feature = "zeroize")]
8+
use zeroize::DefaultIsZeroes;
9+
10+
/// Affine Edwards point on untwisted curve.
11+
#[derive(Copy, Clone, Debug)]
12+
pub struct AffinePoint {
13+
pub(super) x: FieldElement,
14+
pub(super) y: FieldElement,
15+
}
16+
17+
impl ConstantTimeEq for AffinePoint {
18+
fn ct_eq(&self, other: &Self) -> Choice {
19+
self.x.ct_eq(&other.x) & self.y.ct_eq(&other.y)
20+
}
21+
}
22+
23+
impl ConditionallySelectable for AffinePoint {
24+
fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
25+
Self {
26+
x: FieldElement::conditional_select(&a.x, &b.x, choice),
27+
y: FieldElement::conditional_select(&a.y, &b.y, choice),
28+
}
29+
}
30+
}
31+
32+
impl Default for AffinePoint {
33+
fn default() -> AffinePoint {
34+
AffinePoint::identity()
35+
}
36+
}
37+
38+
impl Identity for AffinePoint {
39+
fn identity() -> AffinePoint {
40+
AffinePoint {
41+
x: FieldElement::ZERO,
42+
y: FieldElement::ONE,
43+
}
44+
}
45+
}
46+
47+
impl PartialEq for AffinePoint {
48+
fn eq(&self, other: &Self) -> bool {
49+
self.ct_eq(other).into()
50+
}
51+
}
52+
53+
impl Eq for AffinePoint {}
54+
55+
#[cfg(feature = "zeroize")]
56+
impl DefaultIsZeroes for AffinePoint {}
57+
58+
impl AffinePoint {
59+
/// Convert to extended coordinates.
60+
pub fn to_edwards(self) -> EdwardsPoint {
61+
EdwardsPoint {
62+
X: self.x,
63+
Y: self.y,
64+
Z: FieldElement::ONE,
65+
T: &self.x * &self.y,
66+
}
67+
}
68+
69+
/// Compress affine Edwards coordinates into `CompressedEdwardsY` format.
70+
#[inline]
71+
pub fn compress(self) -> CompressedEdwardsY {
72+
let mut s = self.y.to_bytes();
73+
s[31] ^= self.x.is_negative().unwrap_u8() << 7;
74+
CompressedEdwardsY(s)
75+
}
76+
}
77+
78+
impl Mul<AffinePoint> for Scalar {
79+
type Output = EdwardsPoint;
80+
81+
#[inline]
82+
fn mul(self, rhs: AffinePoint) -> EdwardsPoint {
83+
self * &rhs
84+
}
85+
}
86+
87+
impl Mul<&AffinePoint> for Scalar {
88+
type Output = EdwardsPoint;
89+
90+
#[inline]
91+
fn mul(self, rhs: &AffinePoint) -> EdwardsPoint {
92+
rhs.to_edwards() * self
93+
}
94+
}
95+
96+
#[cfg(test)]
97+
mod tests {
98+
use super::{AffinePoint, EdwardsPoint, Identity};
99+
use crate::constants;
100+
101+
#[test]
102+
fn identity_conversion() {
103+
assert_eq!(
104+
AffinePoint::identity().to_edwards(),
105+
EdwardsPoint::identity()
106+
);
107+
}
108+
109+
#[test]
110+
fn generator_round_trip() {
111+
let basepoint = constants::ED25519_BASEPOINT_POINT;
112+
assert_eq!(basepoint.to_affine().to_edwards(), basepoint);
113+
}
114+
}

0 commit comments

Comments
 (0)