Coverage Report

Created: 2025-11-16 06:36

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/primeorder-0.13.6/src/affine.rs
Line
Count
Source
1
//! Affine curve points.
2
3
#![allow(clippy::op_ref)]
4
5
use crate::{PrimeCurveParams, ProjectivePoint};
6
use core::{
7
    borrow::Borrow,
8
    ops::{Mul, Neg},
9
};
10
use 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")]
25
use serdect::serde::{de, ser, Deserialize, Serialize};
26
27
/// Point on a Weierstrass curve in affine coordinates.
28
#[derive(Clone, Copy, Debug)]
29
pub 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
43
impl<C> AffinePoint<C>
44
where
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
4.37k
    pub fn is_identity(&self) -> Choice {
63
4.37k
        Choice::from(self.infinity)
64
4.37k
    }
65
66
    /// Conditionally negate [`AffinePoint`] for use with point compaction.
67
0
    fn to_compact(self) -> Self {
68
0
        let neg_self = -self;
69
0
        let choice = C::Uint::decode_field_bytes(&self.y.to_repr())
70
0
            .ct_gt(&C::Uint::decode_field_bytes(&neg_self.y.to_repr()));
71
72
0
        Self {
73
0
            x: self.x,
74
0
            y: C::FieldElement::conditional_select(&self.y, &neg_self.y, choice),
75
0
            infinity: self.infinity,
76
0
        }
77
0
    }
78
}
79
80
impl<C> AffineCoordinates for AffinePoint<C>
81
where
82
    C: PrimeCurveParams,
83
{
84
    type FieldRepr = FieldBytes<C>;
85
86
5.79k
    fn x(&self) -> FieldBytes<C> {
87
5.79k
        self.x.to_repr()
88
5.79k
    }
89
90
2.89k
    fn y_is_odd(&self) -> Choice {
91
2.89k
        self.y.is_odd()
92
2.89k
    }
93
}
94
95
impl<C> ConditionallySelectable for AffinePoint<C>
96
where
97
    C: PrimeCurveParams,
98
{
99
    #[inline(always)]
100
7.69k
    fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
101
7.69k
        Self {
102
7.69k
            x: C::FieldElement::conditional_select(&a.x, &b.x, choice),
103
7.69k
            y: C::FieldElement::conditional_select(&a.y, &b.y, choice),
104
7.69k
            infinity: u8::conditional_select(&a.infinity, &b.infinity, choice),
105
7.69k
        }
106
7.69k
    }
107
}
108
109
impl<C> ConstantTimeEq for AffinePoint<C>
110
where
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
118
impl<C> Default for AffinePoint<C>
119
where
120
    C: PrimeCurveParams,
121
{
122
74
    fn default() -> Self {
123
74
        Self::IDENTITY
124
74
    }
125
}
126
127
impl<C> DefaultIsZeroes for AffinePoint<C> where C: PrimeCurveParams {}
128
129
impl<C> DecompressPoint<C> for AffinePoint<C>
130
where
131
    C: PrimeCurveParams,
132
    FieldBytes<C>: Copy,
133
{
134
0
    fn decompress(x_bytes: &FieldBytes<C>, y_is_odd: Choice) -> CtOption<Self> {
135
0
        C::FieldElement::from_repr(*x_bytes).and_then(|x| {
136
0
            let alpha = x * &x * &x + &(C::EQUATION_A * &x) + &C::EQUATION_B;
137
0
            let beta = alpha.sqrt();
138
139
0
            beta.map(|beta| {
140
0
                let y = C::FieldElement::conditional_select(
141
0
                    &-beta,
142
0
                    &beta,
143
0
                    beta.is_odd().ct_eq(&y_is_odd),
144
                );
145
146
0
                Self { x, y, infinity: 0 }
147
0
            })
148
0
        })
149
0
    }
150
}
151
152
impl<C> DecompactPoint<C> for AffinePoint<C>
153
where
154
    C: PrimeCurveParams,
155
    FieldBytes<C>: Copy,
156
{
157
0
    fn decompact(x_bytes: &FieldBytes<C>) -> CtOption<Self> {
158
0
        Self::decompress(x_bytes, Choice::from(0)).map(|point| point.to_compact())
159
0
    }
160
}
161
162
impl<C> Eq for AffinePoint<C> where C: PrimeCurveParams {}
163
164
impl<C> FromEncodedPoint<C> for AffinePoint<C>
165
where
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
74
    fn from_encoded_point(encoded_point: &EncodedPoint<C>) -> CtOption<Self> {
178
74
        match encoded_point.coordinates() {
179
0
            sec1::Coordinates::Identity => CtOption::new(Self::IDENTITY, 1.into()),
180
0
            sec1::Coordinates::Compact { x } => Self::decompact(x),
181
0
            sec1::Coordinates::Compressed { x, y_is_odd } => {
182
0
                Self::decompress(x, Choice::from(y_is_odd as u8))
183
            }
184
74
            sec1::Coordinates::Uncompressed { x, y } => {
185
74
                C::FieldElement::from_repr(*y).and_then(|y| {
186
74
                    C::FieldElement::from_repr(*x).and_then(|x| {
187
74
                        let lhs = y * &y;
188
74
                        let rhs = x * &x * &x + &(C::EQUATION_A * &x) + &C::EQUATION_B;
189
74
                        CtOption::new(Self { x, y, infinity: 0 }, lhs.ct_eq(&rhs))
190
74
                    })
191
74
                })
192
            }
193
        }
194
74
    }
195
}
196
197
impl<C> From<ProjectivePoint<C>> for AffinePoint<C>
198
where
199
    C: PrimeCurveParams,
200
{
201
    fn from(p: ProjectivePoint<C>) -> AffinePoint<C> {
202
        p.to_affine()
203
    }
204
}
205
206
impl<C> From<&ProjectivePoint<C>> for AffinePoint<C>
207
where
208
    C: PrimeCurveParams,
209
{
210
    fn from(p: &ProjectivePoint<C>) -> AffinePoint<C> {
211
        p.to_affine()
212
    }
213
}
214
215
impl<C> From<PublicKey<C>> for AffinePoint<C>
216
where
217
    C: PrimeCurveParams,
218
{
219
    fn from(public_key: PublicKey<C>) -> AffinePoint<C> {
220
        *public_key.as_affine()
221
    }
222
}
223
224
impl<C> From<&PublicKey<C>> for AffinePoint<C>
225
where
226
    C: PrimeCurveParams,
227
{
228
    fn from(public_key: &PublicKey<C>) -> AffinePoint<C> {
229
        AffinePoint::from(*public_key)
230
    }
231
}
232
233
impl<C> From<AffinePoint<C>> for EncodedPoint<C>
234
where
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
245
impl<C> GroupEncoding for AffinePoint<C>
246
where
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
281
impl<C> PartialEq for AffinePoint<C>
282
where
283
    C: PrimeCurveParams,
284
{
285
    fn eq(&self, other: &Self) -> bool {
286
        self.ct_eq(other).into()
287
    }
288
}
289
290
impl<C> PrimeCurveAffine for AffinePoint<C>
291
where
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
319
impl<C> ToCompactEncodedPoint<C> for AffinePoint<C>
320
where
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
340
impl<C> ToEncodedPoint<C> for AffinePoint<C>
341
where
342
    C: PrimeCurveParams,
343
    FieldBytesSize<C>: ModulusSize,
344
    CompressedPoint<C>: Copy,
345
    <UncompressedPointSize<C> as ArrayLength<u8>>::ArrayType: Copy,
346
{
347
4.37k
    fn to_encoded_point(&self, compress: bool) -> EncodedPoint<C> {
348
4.37k
        EncodedPoint::<C>::conditional_select(
349
4.37k
            &EncodedPoint::<C>::from_affine_coordinates(
350
4.37k
                &self.x.to_repr(),
351
4.37k
                &self.y.to_repr(),
352
4.37k
                compress,
353
4.37k
            ),
354
4.37k
            &EncodedPoint::<C>::identity(),
355
4.37k
            self.is_identity(),
356
        )
357
4.37k
    }
358
}
359
360
impl<C> TryFrom<EncodedPoint<C>> for AffinePoint<C>
361
where
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
374
impl<C> TryFrom<&EncodedPoint<C>> for AffinePoint<C>
375
where
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
388
impl<C> TryFrom<AffinePoint<C>> for PublicKey<C>
389
where
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
399
impl<C> TryFrom<&AffinePoint<C>> for PublicKey<C>
400
where
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
414
impl<C, S> Mul<S> for AffinePoint<C>
415
where
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
427
impl<C> Neg for AffinePoint<C>
428
where
429
    C: PrimeCurveParams,
430
{
431
    type Output = Self;
432
433
0
    fn neg(self) -> Self {
434
0
        AffinePoint {
435
0
            x: self.x,
436
0
            y: -self.y,
437
0
            infinity: self.infinity,
438
0
        }
439
0
    }
440
}
441
442
impl<C> Neg for &AffinePoint<C>
443
where
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")]
458
impl<C> Serialize for AffinePoint<C>
459
where
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")]
474
impl<'de, C> Deserialize<'de> for AffinePoint<C>
475
where
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
}