1#![allow(clippy::op_ref)]
4
5use super::{FieldElement, ProjectivePoint, CURVE_EQUATION_B};
6use crate::{CompressedPoint, EncodedPoint, FieldBytes, PublicKey, Scalar, Secp256k1};
7use core::ops::{Mul, Neg};
8use elliptic_curve::{
9 group::{prime::PrimeCurveAffine, GroupEncoding},
10 point::{AffineCoordinates, DecompactPoint, DecompressPoint},
11 sec1::{self, FromEncodedPoint, ToEncodedPoint},
12 subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption},
13 zeroize::DefaultIsZeroes,
14 Error, Result,
15};
16
17#[cfg(feature = "serde")]
18use serdect::serde::{de, ser, Deserialize, Serialize};
19
20#[derive(Clone, Copy, Debug)]
35pub struct AffinePoint {
36 pub(crate) x: FieldElement,
38
39 pub(crate) y: FieldElement,
41
42 pub(super) infinity: u8,
47}
48
49impl AffinePoint {
50 pub const IDENTITY: Self = Self {
52 x: FieldElement::ZERO,
53 y: FieldElement::ZERO,
54 infinity: 1,
55 };
56
57 pub const GENERATOR: Self = Self {
64 x: FieldElement::from_bytes_unchecked(&[
65 0x79, 0xbe, 0x66, 0x7e, 0xf9, 0xdc, 0xbb, 0xac, 0x55, 0xa0, 0x62, 0x95, 0xce, 0x87,
66 0x0b, 0x07, 0x02, 0x9b, 0xfc, 0xdb, 0x2d, 0xce, 0x28, 0xd9, 0x59, 0xf2, 0x81, 0x5b,
67 0x16, 0xf8, 0x17, 0x98,
68 ]),
69 y: FieldElement::from_bytes_unchecked(&[
70 0x48, 0x3a, 0xda, 0x77, 0x26, 0xa3, 0xc4, 0x65, 0x5d, 0xa4, 0xfb, 0xfc, 0x0e, 0x11,
71 0x08, 0xa8, 0xfd, 0x17, 0xb4, 0x48, 0xa6, 0x85, 0x54, 0x19, 0x9c, 0x47, 0xd0, 0x8f,
72 0xfb, 0x10, 0xd4, 0xb8,
73 ]),
74 infinity: 0,
75 };
76}
77
78impl AffinePoint {
79 pub(crate) const fn new(x: FieldElement, y: FieldElement) -> Self {
81 Self { x, y, infinity: 0 }
82 }
83}
84
85impl PrimeCurveAffine for AffinePoint {
86 type Scalar = Scalar;
87 type Curve = ProjectivePoint;
88
89 fn identity() -> Self {
91 Self::IDENTITY
92 }
93
94 fn generator() -> Self {
96 Self::GENERATOR
97 }
98
99 fn is_identity(&self) -> Choice {
101 Choice::from(self.infinity)
102 }
103
104 fn to_curve(&self) -> ProjectivePoint {
106 ProjectivePoint::from(*self)
107 }
108}
109
110impl AffineCoordinates for AffinePoint {
111 type FieldRepr = FieldBytes;
112
113 fn x(&self) -> FieldBytes {
114 self.x.to_bytes()
115 }
116
117 fn y_is_odd(&self) -> Choice {
118 self.y.normalize().is_odd()
119 }
120}
121
122impl ConditionallySelectable for AffinePoint {
123 fn conditional_select(a: &AffinePoint, b: &AffinePoint, choice: Choice) -> AffinePoint {
124 AffinePoint {
125 x: FieldElement::conditional_select(&a.x, &b.x, choice),
126 y: FieldElement::conditional_select(&a.y, &b.y, choice),
127 infinity: u8::conditional_select(&a.infinity, &b.infinity, choice),
128 }
129 }
130}
131
132impl ConstantTimeEq for AffinePoint {
133 fn ct_eq(&self, other: &AffinePoint) -> Choice {
134 (self.x.negate(1) + &other.x).normalizes_to_zero()
135 & (self.y.negate(1) + &other.y).normalizes_to_zero()
136 & self.infinity.ct_eq(&other.infinity)
137 }
138}
139
140impl Default for AffinePoint {
141 fn default() -> Self {
142 Self::IDENTITY
143 }
144}
145
146impl DefaultIsZeroes for AffinePoint {}
147
148impl PartialEq for AffinePoint {
149 fn eq(&self, other: &AffinePoint) -> bool {
150 self.ct_eq(other).into()
151 }
152}
153
154impl Eq for AffinePoint {}
155
156impl Mul<Scalar> for AffinePoint {
157 type Output = ProjectivePoint;
158
159 fn mul(self, scalar: Scalar) -> ProjectivePoint {
160 ProjectivePoint::from(self) * scalar
161 }
162}
163
164impl Mul<&Scalar> for AffinePoint {
165 type Output = ProjectivePoint;
166
167 fn mul(self, scalar: &Scalar) -> ProjectivePoint {
168 ProjectivePoint::from(self) * scalar
169 }
170}
171
172impl Neg for AffinePoint {
173 type Output = AffinePoint;
174
175 fn neg(self) -> Self::Output {
176 AffinePoint {
177 x: self.x,
178 y: self.y.negate(1).normalize_weak(),
179 infinity: self.infinity,
180 }
181 }
182}
183
184impl DecompressPoint<Secp256k1> for AffinePoint {
185 fn decompress(x_bytes: &FieldBytes, y_is_odd: Choice) -> CtOption<Self> {
186 FieldElement::from_bytes(x_bytes).and_then(|x| {
187 let alpha = (x * &x * &x) + &CURVE_EQUATION_B;
188 let beta = alpha.sqrt();
189
190 beta.map(|beta| {
191 let beta = beta.normalize(); let y = FieldElement::conditional_select(
193 &beta.negate(1),
194 &beta,
195 beta.is_odd().ct_eq(&y_is_odd),
196 );
197
198 Self::new(x, y.normalize())
199 })
200 })
201 }
202}
203
204impl DecompactPoint<Secp256k1> for AffinePoint {
208 fn decompact(x_bytes: &FieldBytes) -> CtOption<Self> {
209 Self::decompress(x_bytes, Choice::from(0))
210 }
211}
212
213impl GroupEncoding for AffinePoint {
214 type Repr = CompressedPoint;
215
216 fn from_bytes(bytes: &Self::Repr) -> CtOption<Self> {
217 EncodedPoint::from_bytes(bytes)
218 .map(|point| CtOption::new(point, Choice::from(1)))
219 .unwrap_or_else(|_| {
220 let is_identity = bytes.ct_eq(&Self::Repr::default());
223 CtOption::new(EncodedPoint::identity(), is_identity)
224 })
225 .and_then(|point| Self::from_encoded_point(&point))
226 }
227
228 fn from_bytes_unchecked(bytes: &Self::Repr) -> CtOption<Self> {
229 Self::from_bytes(bytes)
231 }
232
233 fn to_bytes(&self) -> Self::Repr {
234 let encoded = self.to_encoded_point(true);
235 let mut result = CompressedPoint::default();
236 result[..encoded.len()].copy_from_slice(encoded.as_bytes());
237 result
238 }
239}
240
241impl FromEncodedPoint<Secp256k1> for AffinePoint {
242 fn from_encoded_point(encoded_point: &EncodedPoint) -> CtOption<Self> {
248 match encoded_point.coordinates() {
249 sec1::Coordinates::Identity => CtOption::new(Self::IDENTITY, 1.into()),
250 sec1::Coordinates::Compact { x } => Self::decompact(x),
251 sec1::Coordinates::Compressed { x, y_is_odd } => {
252 AffinePoint::decompress(x, Choice::from(y_is_odd as u8))
253 }
254 sec1::Coordinates::Uncompressed { x, y } => {
255 let x = FieldElement::from_bytes(x);
256 let y = FieldElement::from_bytes(y);
257
258 x.and_then(|x| {
259 y.and_then(|y| {
260 let lhs = (y * &y).negate(1);
262 let rhs = x * &x * &x + &CURVE_EQUATION_B;
263 let point = Self::new(x, y);
264 CtOption::new(point, (lhs + &rhs).normalizes_to_zero())
265 })
266 })
267 }
268 }
269 }
270}
271
272impl ToEncodedPoint<Secp256k1> for AffinePoint {
273 fn to_encoded_point(&self, compress: bool) -> EncodedPoint {
274 EncodedPoint::conditional_select(
275 &EncodedPoint::from_affine_coordinates(
276 &self.x.to_bytes(),
277 &self.y.to_bytes(),
278 compress,
279 ),
280 &EncodedPoint::identity(),
281 self.is_identity(),
282 )
283 }
284}
285
286impl TryFrom<EncodedPoint> for AffinePoint {
287 type Error = Error;
288
289 fn try_from(point: EncodedPoint) -> Result<AffinePoint> {
290 AffinePoint::try_from(&point)
291 }
292}
293
294impl TryFrom<&EncodedPoint> for AffinePoint {
295 type Error = Error;
296
297 fn try_from(point: &EncodedPoint) -> Result<AffinePoint> {
298 Option::from(AffinePoint::from_encoded_point(point)).ok_or(Error)
299 }
300}
301
302impl From<AffinePoint> for EncodedPoint {
303 fn from(affine_point: AffinePoint) -> EncodedPoint {
304 EncodedPoint::from(&affine_point)
305 }
306}
307
308impl From<&AffinePoint> for EncodedPoint {
309 fn from(affine_point: &AffinePoint) -> EncodedPoint {
310 affine_point.to_encoded_point(true)
311 }
312}
313
314impl From<PublicKey> for AffinePoint {
315 fn from(public_key: PublicKey) -> AffinePoint {
316 *public_key.as_affine()
317 }
318}
319
320impl From<&PublicKey> for AffinePoint {
321 fn from(public_key: &PublicKey) -> AffinePoint {
322 AffinePoint::from(*public_key)
323 }
324}
325
326impl TryFrom<AffinePoint> for PublicKey {
327 type Error = Error;
328
329 fn try_from(affine_point: AffinePoint) -> Result<PublicKey> {
330 PublicKey::from_affine(affine_point)
331 }
332}
333
334impl TryFrom<&AffinePoint> for PublicKey {
335 type Error = Error;
336
337 fn try_from(affine_point: &AffinePoint) -> Result<PublicKey> {
338 PublicKey::try_from(*affine_point)
339 }
340}
341
342#[cfg(feature = "serde")]
343impl Serialize for AffinePoint {
344 fn serialize<S>(&self, serializer: S) -> core::result::Result<S::Ok, S::Error>
345 where
346 S: ser::Serializer,
347 {
348 self.to_encoded_point(true).serialize(serializer)
349 }
350}
351
352#[cfg(feature = "serde")]
353impl<'de> Deserialize<'de> for AffinePoint {
354 fn deserialize<D>(deserializer: D) -> core::result::Result<Self, D::Error>
355 where
356 D: de::Deserializer<'de>,
357 {
358 EncodedPoint::deserialize(deserializer)?
359 .try_into()
360 .map_err(de::Error::custom)
361 }
362}
363
364#[cfg(test)]
365mod tests {
366 use super::AffinePoint;
367 use crate::EncodedPoint;
368 use elliptic_curve::{
369 group::{prime::PrimeCurveAffine, GroupEncoding},
370 sec1::{FromEncodedPoint, ToEncodedPoint},
371 };
372 use hex_literal::hex;
373
374 const UNCOMPRESSED_BASEPOINT: &[u8] = &hex!(
375 "0479BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798
376 483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8"
377 );
378 const COMPRESSED_BASEPOINT: &[u8] =
379 &hex!("0279BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798");
380
381 #[test]
382 fn uncompressed_round_trip() {
383 let pubkey = EncodedPoint::from_bytes(UNCOMPRESSED_BASEPOINT).unwrap();
384 let res: EncodedPoint = AffinePoint::from_encoded_point(&pubkey)
385 .unwrap()
386 .to_encoded_point(false);
387
388 assert_eq!(res, pubkey);
389 }
390
391 #[test]
392 fn compressed_round_trip() {
393 let pubkey = EncodedPoint::from_bytes(COMPRESSED_BASEPOINT).unwrap();
394 let res: EncodedPoint = AffinePoint::from_encoded_point(&pubkey)
395 .unwrap()
396 .to_encoded_point(true);
397
398 assert_eq!(res, pubkey);
399 }
400
401 #[test]
402 fn uncompressed_to_compressed() {
403 let encoded = EncodedPoint::from_bytes(UNCOMPRESSED_BASEPOINT).unwrap();
404
405 let res = AffinePoint::from_encoded_point(&encoded)
406 .unwrap()
407 .to_encoded_point(true);
408
409 assert_eq!(res.as_bytes(), COMPRESSED_BASEPOINT);
410 }
411
412 #[test]
413 fn compressed_to_uncompressed() {
414 let encoded = EncodedPoint::from_bytes(COMPRESSED_BASEPOINT).unwrap();
415
416 let res = AffinePoint::from_encoded_point(&encoded)
417 .unwrap()
418 .to_encoded_point(false);
419
420 assert_eq!(res.as_bytes(), UNCOMPRESSED_BASEPOINT);
421 }
422
423 #[test]
424 fn affine_negation() {
425 let basepoint = AffinePoint::GENERATOR;
426 assert_eq!((-(-basepoint)), basepoint);
427 }
428
429 #[test]
430 fn identity_encoding() {
431 assert_eq!([0; 33], AffinePoint::IDENTITY.to_bytes().as_slice());
433 assert!(bool::from(
434 AffinePoint::from_bytes(&AffinePoint::IDENTITY.to_bytes())
435 .unwrap()
436 .is_identity()
437 ))
438 }
439}