primeorder/
affine.rs

1//! Affine curve points.
2
3#![allow(clippy::op_ref)]
4
5use crate::{PrimeCurveParams, ProjectivePoint};
6use core::{
7    borrow::Borrow,
8    ops::{Mul, Neg},
9};
10use elliptic_curve::{
11    ff::{Field, PrimeField},
12    generic_array::ArrayLength,
13    group::{prime::PrimeCurveAffine, GroupEncoding},
14    point::{AffineCoordinates, DecompactPoint, DecompressPoint, Double},
15    sec1::{
16        self, CompressedPoint, EncodedPoint, FromEncodedPoint, ModulusSize, ToCompactEncodedPoint,
17        ToEncodedPoint, UncompressedPointSize,
18    },
19    subtle::{Choice, ConditionallySelectable, ConstantTimeEq, ConstantTimeGreater, CtOption},
20    zeroize::DefaultIsZeroes,
21    Error, FieldBytes, FieldBytesEncoding, FieldBytesSize, PublicKey, Result, Scalar,
22};
23
24#[cfg(feature = "serde")]
25use serdect::serde::{de, ser, Deserialize, Serialize};
26
27/// Point on a Weierstrass curve in affine coordinates.
28#[derive(Clone, Copy, Debug)]
29pub struct AffinePoint<C: PrimeCurveParams> {
30    /// x-coordinate
31    pub(crate) x: C::FieldElement,
32
33    /// y-coordinate
34    pub(crate) y: C::FieldElement,
35
36    /// Is this point the point at infinity? 0 = no, 1 = yes
37    ///
38    /// This is a proxy for [`Choice`], but uses `u8` instead to permit `const`
39    /// constructors for `IDENTITY` and `GENERATOR`.
40    pub(crate) infinity: u8,
41}
42
43impl<C> AffinePoint<C>
44where
45    C: PrimeCurveParams,
46{
47    /// Additive identity of the group a.k.a. the point at infinity.
48    pub const IDENTITY: Self = Self {
49        x: C::FieldElement::ZERO,
50        y: C::FieldElement::ZERO,
51        infinity: 1,
52    };
53
54    /// Base point of the curve.
55    pub const GENERATOR: Self = Self {
56        x: C::GENERATOR.0,
57        y: C::GENERATOR.1,
58        infinity: 0,
59    };
60
61    /// Is this point the point at infinity?
62    pub fn is_identity(&self) -> Choice {
63        Choice::from(self.infinity)
64    }
65
66    /// Conditionally negate [`AffinePoint`] for use with point compaction.
67    fn to_compact(self) -> Self {
68        let neg_self = -self;
69        let choice = C::Uint::decode_field_bytes(&self.y.to_repr())
70            .ct_gt(&C::Uint::decode_field_bytes(&neg_self.y.to_repr()));
71
72        Self {
73            x: self.x,
74            y: C::FieldElement::conditional_select(&self.y, &neg_self.y, choice),
75            infinity: self.infinity,
76        }
77    }
78}
79
80impl<C> AffineCoordinates for AffinePoint<C>
81where
82    C: PrimeCurveParams,
83{
84    type FieldRepr = FieldBytes<C>;
85
86    fn x(&self) -> FieldBytes<C> {
87        self.x.to_repr()
88    }
89
90    fn y_is_odd(&self) -> Choice {
91        self.y.is_odd()
92    }
93}
94
95impl<C> ConditionallySelectable for AffinePoint<C>
96where
97    C: PrimeCurveParams,
98{
99    #[inline(always)]
100    fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
101        Self {
102            x: C::FieldElement::conditional_select(&a.x, &b.x, choice),
103            y: C::FieldElement::conditional_select(&a.y, &b.y, choice),
104            infinity: u8::conditional_select(&a.infinity, &b.infinity, choice),
105        }
106    }
107}
108
109impl<C> ConstantTimeEq for AffinePoint<C>
110where
111    C: PrimeCurveParams,
112{
113    fn ct_eq(&self, other: &Self) -> Choice {
114        self.x.ct_eq(&other.x) & self.y.ct_eq(&other.y) & self.infinity.ct_eq(&other.infinity)
115    }
116}
117
118impl<C> Default for AffinePoint<C>
119where
120    C: PrimeCurveParams,
121{
122    fn default() -> Self {
123        Self::IDENTITY
124    }
125}
126
127impl<C> DefaultIsZeroes for AffinePoint<C> where C: PrimeCurveParams {}
128
129impl<C> DecompressPoint<C> for AffinePoint<C>
130where
131    C: PrimeCurveParams,
132    FieldBytes<C>: Copy,
133{
134    fn decompress(x_bytes: &FieldBytes<C>, y_is_odd: Choice) -> CtOption<Self> {
135        C::FieldElement::from_repr(*x_bytes).and_then(|x| {
136            let alpha = x * &x * &x + &(C::EQUATION_A * &x) + &C::EQUATION_B;
137            let beta = alpha.sqrt();
138
139            beta.map(|beta| {
140                let y = C::FieldElement::conditional_select(
141                    &-beta,
142                    &beta,
143                    beta.is_odd().ct_eq(&y_is_odd),
144                );
145
146                Self { x, y, infinity: 0 }
147            })
148        })
149    }
150}
151
152impl<C> DecompactPoint<C> for AffinePoint<C>
153where
154    C: PrimeCurveParams,
155    FieldBytes<C>: Copy,
156{
157    fn decompact(x_bytes: &FieldBytes<C>) -> CtOption<Self> {
158        Self::decompress(x_bytes, Choice::from(0)).map(|point| point.to_compact())
159    }
160}
161
162impl<C> Eq for AffinePoint<C> where C: PrimeCurveParams {}
163
164impl<C> FromEncodedPoint<C> for AffinePoint<C>
165where
166    C: PrimeCurveParams,
167    FieldBytes<C>: Copy,
168    FieldBytesSize<C>: ModulusSize,
169    CompressedPoint<C>: Copy,
170{
171    /// Attempts to parse the given [`EncodedPoint`] as an SEC1-encoded
172    /// [`AffinePoint`].
173    ///
174    /// # Returns
175    ///
176    /// `None` value if `encoded_point` is not on the secp384r1 curve.
177    fn from_encoded_point(encoded_point: &EncodedPoint<C>) -> CtOption<Self> {
178        match encoded_point.coordinates() {
179            sec1::Coordinates::Identity => CtOption::new(Self::IDENTITY, 1.into()),
180            sec1::Coordinates::Compact { x } => Self::decompact(x),
181            sec1::Coordinates::Compressed { x, y_is_odd } => {
182                Self::decompress(x, Choice::from(y_is_odd as u8))
183            }
184            sec1::Coordinates::Uncompressed { x, y } => {
185                C::FieldElement::from_repr(*y).and_then(|y| {
186                    C::FieldElement::from_repr(*x).and_then(|x| {
187                        let lhs = y * &y;
188                        let rhs = x * &x * &x + &(C::EQUATION_A * &x) + &C::EQUATION_B;
189                        CtOption::new(Self { x, y, infinity: 0 }, lhs.ct_eq(&rhs))
190                    })
191                })
192            }
193        }
194    }
195}
196
197impl<C> From<ProjectivePoint<C>> for AffinePoint<C>
198where
199    C: PrimeCurveParams,
200{
201    fn from(p: ProjectivePoint<C>) -> AffinePoint<C> {
202        p.to_affine()
203    }
204}
205
206impl<C> From<&ProjectivePoint<C>> for AffinePoint<C>
207where
208    C: PrimeCurveParams,
209{
210    fn from(p: &ProjectivePoint<C>) -> AffinePoint<C> {
211        p.to_affine()
212    }
213}
214
215impl<C> From<PublicKey<C>> for AffinePoint<C>
216where
217    C: PrimeCurveParams,
218{
219    fn from(public_key: PublicKey<C>) -> AffinePoint<C> {
220        *public_key.as_affine()
221    }
222}
223
224impl<C> From<&PublicKey<C>> for AffinePoint<C>
225where
226    C: PrimeCurveParams,
227{
228    fn from(public_key: &PublicKey<C>) -> AffinePoint<C> {
229        AffinePoint::from(*public_key)
230    }
231}
232
233impl<C> From<AffinePoint<C>> for EncodedPoint<C>
234where
235    C: PrimeCurveParams,
236    FieldBytesSize<C>: ModulusSize,
237    CompressedPoint<C>: Copy,
238    <UncompressedPointSize<C> as ArrayLength<u8>>::ArrayType: Copy,
239{
240    fn from(affine: AffinePoint<C>) -> EncodedPoint<C> {
241        affine.to_encoded_point(false)
242    }
243}
244
245impl<C> GroupEncoding for AffinePoint<C>
246where
247    C: PrimeCurveParams,
248    FieldBytes<C>: Copy,
249    FieldBytesSize<C>: ModulusSize,
250    CompressedPoint<C>: Copy,
251    <UncompressedPointSize<C> as ArrayLength<u8>>::ArrayType: Copy,
252{
253    type Repr = CompressedPoint<C>;
254
255    /// NOTE: not constant-time with respect to identity point
256    fn from_bytes(bytes: &Self::Repr) -> CtOption<Self> {
257        EncodedPoint::<C>::from_bytes(bytes)
258            .map(|point| CtOption::new(point, Choice::from(1)))
259            .unwrap_or_else(|_| {
260                // SEC1 identity encoding is technically 1-byte 0x00, but the
261                // `GroupEncoding` API requires a fixed-width `Repr`
262                let is_identity = bytes.ct_eq(&Self::Repr::default());
263                CtOption::new(EncodedPoint::<C>::identity(), is_identity)
264            })
265            .and_then(|point| Self::from_encoded_point(&point))
266    }
267
268    fn from_bytes_unchecked(bytes: &Self::Repr) -> CtOption<Self> {
269        // No unchecked conversion possible for compressed points
270        Self::from_bytes(bytes)
271    }
272
273    fn to_bytes(&self) -> Self::Repr {
274        let encoded = self.to_encoded_point(true);
275        let mut result = CompressedPoint::<C>::default();
276        result[..encoded.len()].copy_from_slice(encoded.as_bytes());
277        result
278    }
279}
280
281impl<C> PartialEq for AffinePoint<C>
282where
283    C: PrimeCurveParams,
284{
285    fn eq(&self, other: &Self) -> bool {
286        self.ct_eq(other).into()
287    }
288}
289
290impl<C> PrimeCurveAffine for AffinePoint<C>
291where
292    C: PrimeCurveParams,
293    FieldBytes<C>: Copy,
294    FieldBytesSize<C>: ModulusSize,
295    ProjectivePoint<C>: Double,
296    CompressedPoint<C>: Copy,
297    <UncompressedPointSize<C> as ArrayLength<u8>>::ArrayType: Copy,
298{
299    type Curve = ProjectivePoint<C>;
300    type Scalar = Scalar<C>;
301
302    fn identity() -> AffinePoint<C> {
303        Self::IDENTITY
304    }
305
306    fn generator() -> AffinePoint<C> {
307        Self::GENERATOR
308    }
309
310    fn is_identity(&self) -> Choice {
311        self.is_identity()
312    }
313
314    fn to_curve(&self) -> ProjectivePoint<C> {
315        ProjectivePoint::from(*self)
316    }
317}
318
319impl<C> ToCompactEncodedPoint<C> for AffinePoint<C>
320where
321    C: PrimeCurveParams,
322    FieldBytesSize<C>: ModulusSize,
323    CompressedPoint<C>: Copy,
324    <UncompressedPointSize<C> as ArrayLength<u8>>::ArrayType: Copy,
325{
326    /// Serialize this value as a  SEC1 compact [`EncodedPoint`]
327    fn to_compact_encoded_point(&self) -> CtOption<EncodedPoint<C>> {
328        let point = self.to_compact();
329
330        let mut bytes = CompressedPoint::<C>::default();
331        bytes[0] = sec1::Tag::Compact.into();
332        bytes[1..].copy_from_slice(&point.x.to_repr());
333
334        let encoded = EncodedPoint::<C>::from_bytes(bytes);
335        let is_some = point.y.ct_eq(&self.y);
336        CtOption::new(encoded.unwrap_or_default(), is_some)
337    }
338}
339
340impl<C> ToEncodedPoint<C> for AffinePoint<C>
341where
342    C: PrimeCurveParams,
343    FieldBytesSize<C>: ModulusSize,
344    CompressedPoint<C>: Copy,
345    <UncompressedPointSize<C> as ArrayLength<u8>>::ArrayType: Copy,
346{
347    fn to_encoded_point(&self, compress: bool) -> EncodedPoint<C> {
348        EncodedPoint::<C>::conditional_select(
349            &EncodedPoint::<C>::from_affine_coordinates(
350                &self.x.to_repr(),
351                &self.y.to_repr(),
352                compress,
353            ),
354            &EncodedPoint::<C>::identity(),
355            self.is_identity(),
356        )
357    }
358}
359
360impl<C> TryFrom<EncodedPoint<C>> for AffinePoint<C>
361where
362    C: PrimeCurveParams,
363    FieldBytes<C>: Copy,
364    FieldBytesSize<C>: ModulusSize,
365    CompressedPoint<C>: Copy,
366{
367    type Error = Error;
368
369    fn try_from(point: EncodedPoint<C>) -> Result<AffinePoint<C>> {
370        AffinePoint::try_from(&point)
371    }
372}
373
374impl<C> TryFrom<&EncodedPoint<C>> for AffinePoint<C>
375where
376    C: PrimeCurveParams,
377    FieldBytes<C>: Copy,
378    FieldBytesSize<C>: ModulusSize,
379    CompressedPoint<C>: Copy,
380{
381    type Error = Error;
382
383    fn try_from(point: &EncodedPoint<C>) -> Result<AffinePoint<C>> {
384        Option::from(AffinePoint::<C>::from_encoded_point(point)).ok_or(Error)
385    }
386}
387
388impl<C> TryFrom<AffinePoint<C>> for PublicKey<C>
389where
390    C: PrimeCurveParams,
391{
392    type Error = Error;
393
394    fn try_from(affine_point: AffinePoint<C>) -> Result<PublicKey<C>> {
395        PublicKey::from_affine(affine_point)
396    }
397}
398
399impl<C> TryFrom<&AffinePoint<C>> for PublicKey<C>
400where
401    C: PrimeCurveParams,
402{
403    type Error = Error;
404
405    fn try_from(affine_point: &AffinePoint<C>) -> Result<PublicKey<C>> {
406        PublicKey::<C>::try_from(*affine_point)
407    }
408}
409
410//
411// Arithmetic trait impls
412//
413
414impl<C, S> Mul<S> for AffinePoint<C>
415where
416    C: PrimeCurveParams,
417    S: Borrow<Scalar<C>>,
418    ProjectivePoint<C>: Double,
419{
420    type Output = ProjectivePoint<C>;
421
422    fn mul(self, scalar: S) -> ProjectivePoint<C> {
423        ProjectivePoint::<C>::from(self) * scalar
424    }
425}
426
427impl<C> Neg for AffinePoint<C>
428where
429    C: PrimeCurveParams,
430{
431    type Output = Self;
432
433    fn neg(self) -> Self {
434        AffinePoint {
435            x: self.x,
436            y: -self.y,
437            infinity: self.infinity,
438        }
439    }
440}
441
442impl<C> Neg for &AffinePoint<C>
443where
444    C: PrimeCurveParams,
445{
446    type Output = AffinePoint<C>;
447
448    fn neg(self) -> AffinePoint<C> {
449        -(*self)
450    }
451}
452
453//
454// serde support
455//
456
457#[cfg(feature = "serde")]
458impl<C> Serialize for AffinePoint<C>
459where
460    C: PrimeCurveParams,
461    FieldBytesSize<C>: ModulusSize,
462    CompressedPoint<C>: Copy,
463    <UncompressedPointSize<C> as ArrayLength<u8>>::ArrayType: Copy,
464{
465    fn serialize<S>(&self, serializer: S) -> core::result::Result<S::Ok, S::Error>
466    where
467        S: ser::Serializer,
468    {
469        self.to_encoded_point(true).serialize(serializer)
470    }
471}
472
473#[cfg(feature = "serde")]
474impl<'de, C> Deserialize<'de> for AffinePoint<C>
475where
476    C: PrimeCurveParams,
477    FieldBytes<C>: Copy,
478    FieldBytesSize<C>: ModulusSize,
479    CompressedPoint<C>: Copy,
480{
481    fn deserialize<D>(deserializer: D) -> core::result::Result<Self, D::Error>
482    where
483        D: de::Deserializer<'de>,
484    {
485        EncodedPoint::<C>::deserialize(deserializer)?
486            .try_into()
487            .map_err(de::Error::custom)
488    }
489}