Skip to main content

primeorder/
affine.rs

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