openzeppelin_crypto/curve/te/
affine.rs

1//! Affine coordinates for a point on a Twisted Edwards curve ([Affine Space]).
2//!
3//! [Affine Space]: https://en.wikipedia.org/wiki/Affine_space
4use core::{
5    borrow::Borrow,
6    fmt::{Debug, Display, Formatter},
7    ops::{Add, Mul, Neg, Sub},
8};
9
10use educe::Educe;
11use num_traits::{One, Zero};
12use zeroize::Zeroize;
13
14use super::{Projective, TECurveConfig};
15use crate::{
16    bits::BitIteratorBE,
17    curve::AffineRepr,
18    field::{group::AdditiveGroup, prime::PrimeField, Field},
19};
20
21/// Affine coordinates for a point on a twisted Edwards curve, over the
22/// base field `P::BaseField`.
23#[derive(Educe)]
24#[educe(Copy, Clone, PartialEq, Eq, Hash)]
25#[must_use]
26pub struct Affine<P: TECurveConfig> {
27    /// X coordinate of the point represented as a field element
28    pub x: P::BaseField,
29    /// Y coordinate of the point represented as a field element
30    pub y: P::BaseField,
31}
32
33impl<P: TECurveConfig> Display for Affine<P> {
34    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
35        if self.is_zero() {
36            write!(f, "infinity")
37        } else {
38            write!(f, "({}, {})", self.x, self.y)
39        }
40    }
41}
42
43impl<P: TECurveConfig> Debug for Affine<P> {
44    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
45        if self.is_zero() {
46            write!(f, "infinity")
47        } else {
48            write!(f, "({}, {})", self.x, self.y)
49        }
50    }
51}
52
53impl<P: TECurveConfig> PartialEq<Projective<P>> for Affine<P> {
54    fn eq(&self, other: &Projective<P>) -> bool {
55        &self.into_group() == other
56    }
57}
58
59impl<P: TECurveConfig> Affine<P> {
60    /// Construct a new group element without checking whether the coordinates
61    /// specify a point in the subgroup.
62    pub const fn new_unchecked(x: P::BaseField, y: P::BaseField) -> Self {
63        Self { x, y }
64    }
65
66    /// Construct a new group element in a way while enforcing that points are
67    /// in the prime-order subgroup.
68    ///
69    /// # Panics
70    ///
71    /// * If point is not on curve.
72    /// * If point is not in the prime-order subgroup.
73    pub fn new(x: P::BaseField, y: P::BaseField) -> Self {
74        let p = Self::new_unchecked(x, y);
75        assert!(p.is_on_curve());
76        assert!(p.is_in_prime_order_subgroup());
77        p
78    }
79
80    /// Construct the identity of the group.
81    pub const fn zero() -> Self {
82        Self::new_unchecked(P::BaseField::ZERO, P::BaseField::ONE)
83    }
84
85    /// Is this point the identity?
86    pub fn is_zero(&self) -> bool {
87        self.x.is_zero() && self.y.is_one()
88    }
89
90    /// Checks that the current point is on the elliptic curve.
91    pub fn is_on_curve(&self) -> bool {
92        let x2 = self.x.square();
93        let y2 = self.y.square();
94
95        let lhs = y2 + P::mul_by_a(x2);
96        let rhs = P::BaseField::one() + (P::COEFF_D * (x2 * y2));
97
98        lhs == rhs
99    }
100}
101
102impl<P: TECurveConfig> Affine<P> {
103    /// Checks if this point is in the prime-order subgroup.
104    ///
105    /// This assumes the point is already on the curve and verifies it belongs
106    /// to the subgroup with order equal to `P::ScalarField`.
107    pub fn is_in_prime_order_subgroup(&self) -> bool {
108        P::is_in_prime_order_subgroup(self)
109    }
110}
111
112impl<P: TECurveConfig> AffineRepr for Affine<P> {
113    type BaseField = P::BaseField;
114    type Config = P;
115    type Group = Projective<P>;
116    type ScalarField = P::ScalarField;
117
118    fn xy(&self) -> Option<(Self::BaseField, Self::BaseField)> {
119        (!self.is_zero()).then_some((self.x, self.y))
120    }
121
122    fn generator() -> Self {
123        P::GENERATOR
124    }
125
126    fn zero() -> Self {
127        Self::new_unchecked(P::BaseField::ZERO, P::BaseField::ONE)
128    }
129
130    fn mul_bigint(&self, by: impl BitIteratorBE) -> Self::Group {
131        P::mul_affine(self, by)
132    }
133
134    /// Multiplies this element by the cofactor and output the
135    /// resulting projective element.
136    fn mul_by_cofactor_to_group(&self) -> Self::Group {
137        P::mul_affine(self, Self::Config::COFACTOR)
138    }
139
140    /// Performs cofactor clearing.
141    /// The default method is simply to multiply by the cofactor.
142    /// Some curves can implement a more efficient algorithm.
143    fn clear_cofactor(&self) -> Self {
144        P::clear_cofactor(self)
145    }
146}
147
148impl<P: TECurveConfig> Zeroize for Affine<P> {
149    fn zeroize(&mut self) {
150        self.x.zeroize();
151        self.y.zeroize();
152    }
153}
154
155impl<P: TECurveConfig> Neg for Affine<P> {
156    type Output = Self;
157
158    fn neg(self) -> Self {
159        Self::new_unchecked(-self.x, self.y)
160    }
161}
162
163impl<P: TECurveConfig, T: Borrow<Self>> Add<T> for Affine<P> {
164    type Output = Projective<P>;
165
166    fn add(self, other: T) -> Self::Output {
167        let mut copy = self.into_group();
168        copy += other.borrow();
169        copy
170    }
171}
172
173impl<P: TECurveConfig> Add<Projective<P>> for Affine<P> {
174    type Output = Projective<P>;
175
176    fn add(self, other: Projective<P>) -> Projective<P> {
177        other + self
178    }
179}
180
181impl<'a, P: TECurveConfig> Add<&'a Projective<P>> for Affine<P> {
182    type Output = Projective<P>;
183
184    fn add(self, other: &'a Projective<P>) -> Projective<P> {
185        *other + self
186    }
187}
188
189impl<P: TECurveConfig, T: Borrow<Self>> Sub<T> for Affine<P> {
190    type Output = Projective<P>;
191
192    fn sub(self, other: T) -> Self::Output {
193        let mut copy = self.into_group();
194        copy -= other.borrow();
195        copy
196    }
197}
198
199impl<P: TECurveConfig> Sub<Projective<P>> for Affine<P> {
200    type Output = Projective<P>;
201
202    fn sub(self, other: Projective<P>) -> Projective<P> {
203        self + (-other)
204    }
205}
206
207impl<'a, P: TECurveConfig> Sub<&'a Projective<P>> for Affine<P> {
208    type Output = Projective<P>;
209
210    fn sub(self, other: &'a Projective<P>) -> Projective<P> {
211        self + (-*other)
212    }
213}
214
215impl<P: TECurveConfig> Default for Affine<P> {
216    #[inline]
217    fn default() -> Self {
218        Self::zero()
219    }
220}
221
222impl<P: TECurveConfig, T: Borrow<P::ScalarField>> Mul<T> for Affine<P> {
223    type Output = Projective<P>;
224
225    #[inline]
226    fn mul(self, other: T) -> Self::Output {
227        self.mul_bigint(other.borrow().into_bigint())
228    }
229}
230
231// The projective point X, Y, T, Z is represented in the affine
232// coordinates as X/Z, Y/Z.
233impl<P: TECurveConfig> From<Projective<P>> for Affine<P> {
234    fn from(p: Projective<P>) -> Affine<P> {
235        if p.is_zero() {
236            Affine::zero()
237        } else if p.z.is_one() {
238            // If Z is one, the point is already normalized.
239            Affine::new_unchecked(p.x, p.y)
240        } else {
241            // Projective Z coordinate should not be zero after computations,
242            // unless it wasn't instantiated invalid.
243            let z_inv =
244                p.z.inverse()
245                    .expect("projective Z coordinate should not be zero");
246            let x = p.x * z_inv;
247            let y = p.y * z_inv;
248            Affine::new_unchecked(x, y)
249        }
250    }
251}