1#![allow(clippy::op_ref)]
4
5use super::{CURVE_EQUATION_B, FieldElement, ProjectivePoint};
6use crate::{CompressedPoint, FieldBytes, PublicKey, Scalar, Sec1Point, Secp256k1};
7use elliptic_curve::{
8 Error, Generate, Result, ctutils,
9 ff::PrimeField,
10 group::{CurveAffine, GroupEncoding},
11 ops::{Mul, MulVartime, Neg},
12 point::{AffineCoordinates, DecompactPoint, DecompressPoint, NonIdentity},
13 rand_core::{TryCryptoRng, TryRng},
14 sec1::{self, FromSec1Point, ToSec1Point},
15 subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption},
16 zeroize::DefaultIsZeroes,
17};
18
19#[cfg(feature = "serde")]
20use serdect::serde::{Deserialize, Serialize, de, ser};
21
22#[derive(Clone, Copy, Debug)]
37pub struct AffinePoint {
38 pub(crate) x: FieldElement,
40
41 pub(crate) y: FieldElement,
43
44 pub(super) infinity: u8,
49}
50
51impl AffinePoint {
52 pub const IDENTITY: Self = Self {
54 x: FieldElement::ZERO,
55 y: FieldElement::ZERO,
56 infinity: 1,
57 };
58
59 pub const GENERATOR: Self = Self {
66 x: FieldElement::from_bytes_unchecked(&[
67 0x79, 0xbe, 0x66, 0x7e, 0xf9, 0xdc, 0xbb, 0xac, 0x55, 0xa0, 0x62, 0x95, 0xce, 0x87,
68 0x0b, 0x07, 0x02, 0x9b, 0xfc, 0xdb, 0x2d, 0xce, 0x28, 0xd9, 0x59, 0xf2, 0x81, 0x5b,
69 0x16, 0xf8, 0x17, 0x98,
70 ]),
71 y: FieldElement::from_bytes_unchecked(&[
72 0x48, 0x3a, 0xda, 0x77, 0x26, 0xa3, 0xc4, 0x65, 0x5d, 0xa4, 0xfb, 0xfc, 0x0e, 0x11,
73 0x08, 0xa8, 0xfd, 0x17, 0xb4, 0x48, 0xa6, 0x85, 0x54, 0x19, 0x9c, 0x47, 0xd0, 0x8f,
74 0xfb, 0x10, 0xd4, 0xb8,
75 ]),
76 infinity: 0,
77 };
78
79 pub(crate) fn try_random<R: TryRng + ?Sized>(
84 rng: &mut R,
85 ) -> core::result::Result<Self, R::Error> {
86 let mut bytes = FieldBytes::default();
87 let mut sign = 0;
88
89 loop {
90 rng.try_fill_bytes(&mut bytes)?;
91 rng.try_fill_bytes(core::array::from_mut(&mut sign))?;
92 if let Some(point) = Self::decompress(&bytes, Choice::from(sign & 1)).into_option() {
93 return Ok(point);
94 }
95 }
96 }
97}
98
99impl AffinePoint {
100 pub(crate) const fn new(x: FieldElement, y: FieldElement) -> Self {
102 Self { x, y, infinity: 0 }
103 }
104}
105
106impl CurveAffine for AffinePoint {
107 type Scalar = Scalar;
108 type Curve = ProjectivePoint;
109
110 fn identity() -> Self {
112 Self::IDENTITY
113 }
114
115 fn generator() -> Self {
117 Self::GENERATOR
118 }
119
120 fn is_identity(&self) -> Choice {
122 Choice::from(self.infinity)
123 }
124
125 fn to_curve(&self) -> ProjectivePoint {
127 ProjectivePoint::from(*self)
128 }
129}
130
131impl AffineCoordinates for AffinePoint {
132 type FieldRepr = FieldBytes;
133
134 fn from_coordinates(x: &Self::FieldRepr, y: &Self::FieldRepr) -> CtOption<Self> {
135 let x = FieldElement::from_bytes(x);
136 let y = FieldElement::from_bytes(y);
137
138 x.and_then(|x| {
139 y.and_then(|y| {
140 let lhs = (y * &y).negate(1);
142 let rhs = x * &x * &x + &CURVE_EQUATION_B;
143 let point = Self::new(x, y);
144 CtOption::new(point, (lhs + &rhs).normalizes_to_zero())
145 })
146 })
147 }
148
149 fn x(&self) -> FieldBytes {
150 self.x.to_bytes()
151 }
152
153 fn y(&self) -> FieldBytes {
154 self.y.to_bytes()
155 }
156
157 fn x_is_odd(&self) -> Choice {
158 self.x.normalize().is_odd()
159 }
160
161 fn y_is_odd(&self) -> Choice {
162 self.y.normalize().is_odd()
163 }
164}
165
166impl ConditionallySelectable for AffinePoint {
167 fn conditional_select(a: &AffinePoint, b: &AffinePoint, choice: Choice) -> AffinePoint {
168 AffinePoint {
169 x: FieldElement::conditional_select(&a.x, &b.x, choice),
170 y: FieldElement::conditional_select(&a.y, &b.y, choice),
171 infinity: u8::conditional_select(&a.infinity, &b.infinity, choice),
172 }
173 }
174}
175
176impl ConstantTimeEq for AffinePoint {
177 fn ct_eq(&self, other: &AffinePoint) -> Choice {
178 (self.x.negate(1) + &other.x).normalizes_to_zero()
179 & (self.y.negate(1) + &other.y).normalizes_to_zero()
180 & self.infinity.ct_eq(&other.infinity)
181 }
182}
183
184impl ctutils::CtEq for AffinePoint {
185 fn ct_eq(&self, other: &Self) -> ctutils::Choice {
186 ConstantTimeEq::ct_eq(self, other).into()
187 }
188}
189
190impl ctutils::CtSelect for AffinePoint {
191 fn ct_select(&self, other: &Self, choice: ctutils::Choice) -> Self {
192 ConditionallySelectable::conditional_select(self, other, choice.into())
193 }
194}
195
196impl Default for AffinePoint {
197 fn default() -> Self {
198 Self::IDENTITY
199 }
200}
201
202impl DefaultIsZeroes for AffinePoint {}
203
204impl PartialEq for AffinePoint {
205 fn eq(&self, other: &AffinePoint) -> bool {
206 self.ct_eq(other).into()
207 }
208}
209
210impl Eq for AffinePoint {}
211
212impl Generate for AffinePoint {
213 fn try_generate_from_rng<R: TryCryptoRng + ?Sized>(
214 rng: &mut R,
215 ) -> core::result::Result<Self, R::Error> {
216 Self::try_random(rng)
217 }
218}
219
220impl Mul<Scalar> for AffinePoint {
221 type Output = ProjectivePoint;
222
223 fn mul(self, scalar: Scalar) -> ProjectivePoint {
224 ProjectivePoint::from(self) * scalar
225 }
226}
227
228impl Mul<&Scalar> for AffinePoint {
229 type Output = ProjectivePoint;
230
231 fn mul(self, scalar: &Scalar) -> ProjectivePoint {
232 ProjectivePoint::from(self) * scalar
233 }
234}
235
236impl MulVartime<Scalar> for AffinePoint {
237 fn mul_vartime(self, scalar: Scalar) -> ProjectivePoint {
238 ProjectivePoint::from(self).mul_vartime(scalar)
239 }
240}
241
242impl MulVartime<&Scalar> for AffinePoint {
243 fn mul_vartime(self, scalar: &Scalar) -> ProjectivePoint {
244 ProjectivePoint::from(self).mul_vartime(scalar)
245 }
246}
247
248impl Neg for AffinePoint {
249 type Output = AffinePoint;
250
251 fn neg(self) -> Self::Output {
252 AffinePoint {
253 x: self.x,
254 y: self.y.negate(1).normalize_weak(),
255 infinity: self.infinity,
256 }
257 }
258}
259
260impl DecompressPoint<Secp256k1> for AffinePoint {
261 fn decompress(x_bytes: &FieldBytes, y_is_odd: Choice) -> CtOption<Self> {
262 FieldElement::from_bytes(x_bytes).and_then(|x| {
263 let alpha = (x * &x * &x) + &CURVE_EQUATION_B;
264 let beta = alpha.sqrt();
265
266 beta.map(|beta| {
267 let beta = beta.normalize(); let y = FieldElement::conditional_select(
269 &beta.negate(1),
270 &beta,
271 beta.is_odd().ct_eq(&y_is_odd),
272 );
273
274 Self::new(x, y.normalize())
275 })
276 })
277 }
278}
279
280impl DecompactPoint<Secp256k1> for AffinePoint {
284 fn decompact(x_bytes: &FieldBytes) -> CtOption<Self> {
285 Self::decompress(x_bytes, Choice::from(0))
286 }
287}
288
289impl GroupEncoding for AffinePoint {
290 type Repr = CompressedPoint;
291
292 fn from_bytes(bytes: &Self::Repr) -> CtOption<Self> {
293 Sec1Point::from_bytes(bytes)
294 .map_or_else(
295 |_| {
296 let is_identity =
299 ctutils::CtEq::ct_eq(bytes.as_slice(), Self::Repr::default().as_slice());
300
301 ctutils::CtOption::new(Sec1Point::identity(), is_identity)
302 },
303 ctutils::CtOption::some,
304 )
305 .and_then(|point| Self::from_sec1_point(&point))
306 .into()
307 }
308
309 fn from_bytes_unchecked(bytes: &Self::Repr) -> CtOption<Self> {
310 Self::from_bytes(bytes)
312 }
313
314 fn to_bytes(&self) -> Self::Repr {
315 let encoded = self.to_sec1_point(true);
316 let mut result = CompressedPoint::default();
317 result[..encoded.len()].copy_from_slice(encoded.as_bytes());
318 result
319 }
320}
321
322impl FromSec1Point<Secp256k1> for AffinePoint {
323 fn from_sec1_point(encoded_point: &Sec1Point) -> ctutils::CtOption<Self> {
329 match encoded_point.coordinates() {
330 sec1::Coordinates::Identity => ctutils::CtOption::some(Self::IDENTITY),
331 sec1::Coordinates::Compact { x } => Self::decompact(x).into(),
332 sec1::Coordinates::Compressed { x, y_is_odd } => {
333 AffinePoint::decompress(x, Choice::from(u8::from(y_is_odd))).into()
334 }
335 sec1::Coordinates::Uncompressed { x, y } => Self::from_coordinates(x, y).into(),
336 }
337 }
338}
339
340impl ToSec1Point<Secp256k1> for AffinePoint {
341 fn to_sec1_point(&self, compress: bool) -> Sec1Point {
342 ctutils::CtSelect::ct_select(
343 &Sec1Point::from_affine_coordinates(&self.x.to_repr(), &self.y.to_repr(), compress),
344 &Sec1Point::identity(),
345 self.is_identity().into(),
346 )
347 }
348}
349
350impl TryFrom<Sec1Point> for AffinePoint {
351 type Error = Error;
352
353 fn try_from(point: Sec1Point) -> Result<AffinePoint> {
354 AffinePoint::try_from(&point)
355 }
356}
357
358impl TryFrom<&Sec1Point> for AffinePoint {
359 type Error = Error;
360
361 fn try_from(point: &Sec1Point) -> Result<AffinePoint> {
362 Option::from(AffinePoint::from_sec1_point(point)).ok_or(Error)
363 }
364}
365
366impl From<AffinePoint> for Sec1Point {
367 fn from(affine_point: AffinePoint) -> Sec1Point {
368 Sec1Point::from(&affine_point)
369 }
370}
371
372impl From<&AffinePoint> for Sec1Point {
373 fn from(affine_point: &AffinePoint) -> Sec1Point {
374 affine_point.to_sec1_point(true)
375 }
376}
377
378impl From<NonIdentity<AffinePoint>> for AffinePoint {
379 fn from(affine_point: NonIdentity<AffinePoint>) -> Self {
380 affine_point.to_point()
381 }
382}
383
384impl From<PublicKey> for AffinePoint {
385 fn from(public_key: PublicKey) -> AffinePoint {
386 *public_key.as_affine()
387 }
388}
389
390impl From<&PublicKey> for AffinePoint {
391 fn from(public_key: &PublicKey) -> AffinePoint {
392 AffinePoint::from(*public_key)
393 }
394}
395
396impl TryFrom<AffinePoint> for NonIdentity<AffinePoint> {
398 type Error = Error;
399
400 fn try_from(affine_point: AffinePoint) -> Result<Self> {
401 NonIdentity::new(affine_point).into_option().ok_or(Error)
402 }
403}
404
405impl TryFrom<AffinePoint> for PublicKey {
406 type Error = Error;
407
408 fn try_from(affine_point: AffinePoint) -> Result<PublicKey> {
409 PublicKey::from_affine(affine_point)
410 }
411}
412
413impl TryFrom<&AffinePoint> for PublicKey {
414 type Error = Error;
415
416 fn try_from(affine_point: &AffinePoint) -> Result<PublicKey> {
417 PublicKey::try_from(*affine_point)
418 }
419}
420
421#[cfg(feature = "serde")]
422impl Serialize for AffinePoint {
423 fn serialize<S>(&self, serializer: S) -> core::result::Result<S::Ok, S::Error>
424 where
425 S: ser::Serializer,
426 {
427 self.to_sec1_point(true).serialize(serializer)
428 }
429}
430
431#[cfg(feature = "serde")]
432impl<'de> Deserialize<'de> for AffinePoint {
433 fn deserialize<D>(deserializer: D) -> core::result::Result<Self, D::Error>
434 where
435 D: de::Deserializer<'de>,
436 {
437 Sec1Point::deserialize(deserializer)?
438 .try_into()
439 .map_err(de::Error::custom)
440 }
441}
442
443#[cfg(test)]
444mod tests {
445 use super::AffinePoint;
446 use crate::Sec1Point;
447 use elliptic_curve::{
448 group::{CurveAffine, GroupEncoding},
449 sec1::{FromSec1Point, ToSec1Point},
450 };
451 use hex_literal::hex;
452
453 const UNCOMPRESSED_BASEPOINT: &[u8] = &hex!(
454 "0479BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798
455 483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8"
456 );
457 const COMPRESSED_BASEPOINT: &[u8] =
458 &hex!("0279BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798");
459
460 #[test]
461 fn uncompressed_round_trip() {
462 let pubkey = Sec1Point::from_bytes(UNCOMPRESSED_BASEPOINT).unwrap();
463 let res: Sec1Point = AffinePoint::from_sec1_point(&pubkey)
464 .unwrap()
465 .to_sec1_point(false);
466
467 assert_eq!(res, pubkey);
468 }
469
470 #[test]
471 fn compressed_round_trip() {
472 let pubkey = Sec1Point::from_bytes(COMPRESSED_BASEPOINT).unwrap();
473 let res: Sec1Point = AffinePoint::from_sec1_point(&pubkey)
474 .unwrap()
475 .to_sec1_point(true);
476
477 assert_eq!(res, pubkey);
478 }
479
480 #[test]
481 fn uncompressed_to_compressed() {
482 let encoded = Sec1Point::from_bytes(UNCOMPRESSED_BASEPOINT).unwrap();
483
484 let res = AffinePoint::from_sec1_point(&encoded)
485 .unwrap()
486 .to_sec1_point(true);
487
488 assert_eq!(res.as_bytes(), COMPRESSED_BASEPOINT);
489 }
490
491 #[test]
492 fn compressed_to_uncompressed() {
493 let encoded = Sec1Point::from_bytes(COMPRESSED_BASEPOINT).unwrap();
494
495 let res = AffinePoint::from_sec1_point(&encoded)
496 .unwrap()
497 .to_sec1_point(false);
498
499 assert_eq!(res.as_bytes(), UNCOMPRESSED_BASEPOINT);
500 }
501
502 #[test]
503 fn affine_negation() {
504 let basepoint = AffinePoint::GENERATOR;
505 assert_eq!((-(-basepoint)), basepoint);
506 }
507
508 #[test]
509 fn identity_encoding() {
510 assert_eq!([0; 33], AffinePoint::IDENTITY.to_bytes().as_slice());
512 assert!(bool::from(
513 AffinePoint::from_bytes(&AffinePoint::IDENTITY.to_bytes())
514 .unwrap()
515 .is_identity()
516 ));
517 }
518}