Skip to main content

ed448_goldilocks/decaf/
affine.rs

1use crate::decaf::points::DecafPointRepr;
2use crate::{
3    Decaf448FieldBytes, DecafPoint, DecafScalar, ORDER,
4    curve::twedwards::affine::AffinePoint as InnerAffinePoint, field::FieldElement,
5};
6use elliptic_curve::{
7    CurveGroup, Error, Generate, ctutils,
8    group::{CurveAffine, GroupEncoding},
9    ops::{Mul, MulVartime, Neg},
10    point::{AffineCoordinates, NonIdentity},
11    zeroize::DefaultIsZeroes,
12};
13use rand_core::TryCryptoRng;
14use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption};
15
16/// Affine point on the twisted curve
17#[derive(Copy, Clone, Debug, Default)]
18pub struct AffinePoint(pub(crate) InnerAffinePoint);
19
20impl AffinePoint {
21    /// The identity point
22    pub const IDENTITY: Self = Self(InnerAffinePoint::IDENTITY);
23
24    /// Convert to DecafPoint
25    pub fn to_decaf(&self) -> DecafPoint {
26        DecafPoint(self.0.to_extended())
27    }
28
29    /// The X coordinate
30    pub fn x(&self) -> [u8; 56] {
31        self.0.x.to_bytes()
32    }
33
34    /// The Y coordinate
35    pub fn y(&self) -> [u8; 56] {
36        self.0.y.to_bytes()
37    }
38}
39
40impl ConstantTimeEq for AffinePoint {
41    fn ct_eq(&self, other: &Self) -> Choice {
42        self.0.x.ct_eq(&other.0.x) & self.0.y.ct_eq(&other.0.y)
43    }
44}
45
46impl ConditionallySelectable for AffinePoint {
47    fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
48        Self(InnerAffinePoint {
49            x: FieldElement::conditional_select(&a.0.x, &b.0.x, choice),
50            y: FieldElement::conditional_select(&a.0.y, &b.0.y, choice),
51        })
52    }
53}
54
55impl ctutils::CtEq for AffinePoint {
56    fn ct_eq(&self, other: &Self) -> ctutils::Choice {
57        ConstantTimeEq::ct_eq(self, other).into()
58    }
59}
60
61impl ctutils::CtSelect for AffinePoint {
62    fn ct_select(&self, other: &Self, choice: ctutils::Choice) -> Self {
63        ConditionallySelectable::conditional_select(self, other, choice.into())
64    }
65}
66
67impl PartialEq for AffinePoint {
68    fn eq(&self, other: &Self) -> bool {
69        self.ct_eq(other).into()
70    }
71}
72
73impl Eq for AffinePoint {}
74
75impl AffineCoordinates for AffinePoint {
76    type FieldRepr = Decaf448FieldBytes;
77
78    fn from_coordinates(x: &Self::FieldRepr, y: &Self::FieldRepr) -> CtOption<Self> {
79        let point = Self(InnerAffinePoint {
80            x: FieldElement::from_bytes(&x.0),
81            y: FieldElement::from_bytes(&y.0),
82        });
83
84        CtOption::new(
85            point,
86            point.0.is_on_curve() & (point * DecafScalar::new(*ORDER)).ct_eq(&DecafPoint::IDENTITY),
87        )
88    }
89
90    fn x(&self) -> Self::FieldRepr {
91        Decaf448FieldBytes::from(self.x())
92    }
93
94    fn y(&self) -> Self::FieldRepr {
95        Decaf448FieldBytes::from(self.y())
96    }
97
98    fn x_is_odd(&self) -> Choice {
99        self.0.x.is_negative()
100    }
101
102    fn y_is_odd(&self) -> Choice {
103        self.0.y.is_negative()
104    }
105}
106
107impl CurveAffine for AffinePoint {
108    type Curve = DecafPoint;
109    type Scalar = DecafScalar;
110
111    fn identity() -> AffinePoint {
112        Self::IDENTITY
113    }
114
115    fn generator() -> AffinePoint {
116        DecafPoint::GENERATOR.to_affine()
117    }
118
119    fn is_identity(&self) -> Choice {
120        self.to_decaf().is_identity()
121    }
122
123    fn to_curve(&self) -> DecafPoint {
124        self.to_decaf()
125    }
126}
127
128impl DefaultIsZeroes for AffinePoint {}
129
130impl Generate for AffinePoint {
131    fn try_generate_from_rng<R: TryCryptoRng + ?Sized>(rng: &mut R) -> Result<Self, R::Error> {
132        DecafPoint::try_generate_from_rng(rng).map(Into::into)
133    }
134}
135
136impl GroupEncoding for AffinePoint {
137    type Repr = DecafPointRepr;
138
139    fn from_bytes(bytes: &Self::Repr) -> CtOption<Self> {
140        DecafPoint::from_bytes(bytes).map(|point| point.to_affine())
141    }
142
143    fn from_bytes_unchecked(bytes: &Self::Repr) -> CtOption<Self> {
144        // No unchecked conversion possible for compressed points
145        Self::from_bytes(bytes)
146    }
147
148    fn to_bytes(&self) -> Self::Repr {
149        self.to_decaf().to_bytes()
150    }
151}
152
153/// The constant-time alternative is available at [`NonIdentity::new()`].
154impl TryFrom<AffinePoint> for NonIdentity<AffinePoint> {
155    type Error = Error;
156
157    fn try_from(affine_point: AffinePoint) -> Result<Self, Error> {
158        NonIdentity::new(affine_point).into_option().ok_or(Error)
159    }
160}
161
162impl From<NonIdentity<AffinePoint>> for AffinePoint {
163    fn from(affine: NonIdentity<AffinePoint>) -> Self {
164        affine.to_point()
165    }
166}
167
168impl Mul<&DecafScalar> for &AffinePoint {
169    type Output = DecafPoint;
170
171    #[inline]
172    fn mul(self, scalar: &DecafScalar) -> DecafPoint {
173        self.to_decaf() * scalar
174    }
175}
176
177impl MulVartime<DecafScalar> for AffinePoint {
178    #[inline]
179    fn mul_vartime(self, scalar: DecafScalar) -> DecafPoint {
180        self.mul_vartime(&scalar)
181    }
182}
183
184impl MulVartime<&DecafScalar> for AffinePoint {
185    #[inline]
186    fn mul_vartime(self, scalar: &DecafScalar) -> DecafPoint {
187        // TODO(tarcieri): optimized vartime implementation
188        self.to_decaf() * scalar
189    }
190}
191
192impl MulVartime<&DecafScalar> for &AffinePoint {
193    #[inline]
194    fn mul_vartime(self, scalar: &DecafScalar) -> DecafPoint {
195        (*self).mul_vartime(scalar)
196    }
197}
198
199define_mul_variants!(LHS = AffinePoint, RHS = DecafScalar, Output = DecafPoint);
200
201impl Neg for AffinePoint {
202    type Output = AffinePoint;
203
204    fn neg(self) -> Self::Output {
205        self.to_decaf().neg().to_affine()
206    }
207}