generic_ec/coords.rs
1//! # Elliptic points coordinates
2//!
3//! Elliptic points are defined differently for different types of curves:
4//! * Curves in non-complete form (Weierstrass or Montgomery curves): \
5//! Points have $(x, y)$ coordinates that must satisfy curve equation unless it's **point at infinity**
6//! that has no coordinates (see [points at infinity](crate#points-at-infinity))
7//! * Curves in complete form (Edwards curves): \
8//! Points always have $(x, y)$ coordinates that must satisfy curve equation
9//!
10//! ## Usage
11//! This module provides various traits that can be used to retrieve coordinates. Refer to curve documentation
12//! to see what coordinates it exposes.
13//!
14//! ```rust
15//! use generic_ec::{Point, coords::HasAffineX, curves::Secp256k1};
16//!
17//! let point = Point::<Secp256k1>::generator().to_point();
18//! let x = point.x();
19//! ```
20//!
21//! ### In generic code
22//! Generic code needs to explicitly state that it needs access to coordinates by specifying it in bounds:
23//! ```rust
24//! use generic_ec::{Point, Curve, coords::HasAffineX};
25//!
26//! fn func_that_accesses_x_coord<E: Curve>(point: &Point<E>)
27//! where
28//! Point<E>: HasAffineX<E>
29//! {
30//! let x = point.x();
31//! // ...
32//! }
33//! ```
34//!
35//! _Note:_ it's not recommended to access points coordinates in generic code unless it's really necessary.
36//! Practically it lessens variety of curves that can work with your code. If you need unique representation
37//! of a point, use [its byte representation](crate::Point::to_bytes).
38//!
39//! ## Curves support
40//! Some curve implementations intentionally chosen not to expose coordinates, so they, for instance, can
41//! expose $y$ coordinate but hide $x$.
42
43use core::fmt;
44
45#[doc(inline)]
46pub use crate::core::coords::{Parity, Sign};
47use crate::{
48 core::{ByteArray, Curve},
49 errors::InvalidCoordinate,
50 Scalar,
51};
52
53/// Affine $x, y$ coordinates of a point on elliptic curve
54#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
55pub struct Coordinates<E: Curve> {
56 /// Affine $x$ coordinate of a point
57 pub x: Coordinate<E>,
58 /// Affine $y$ coordinate of a point
59 pub y: Coordinate<E>,
60}
61
62/// Affine coordinate of a point on elliptic curve
63#[derive(Clone)]
64pub struct Coordinate<E: Curve>(E::CoordinateArray);
65
66impl<E: Curve> Coordinate<E> {
67 /// (Big-endian) bytes representation of a coordinate
68 #[inline(always)]
69 pub fn as_be_bytes(&self) -> &[u8] {
70 self.0.as_ref()
71 }
72
73 /// Parses (big-endian) bytes representation of a coordinate
74 pub fn from_be_bytes(bytes: &[u8]) -> Result<Self, InvalidCoordinate> {
75 let mut coord = E::CoordinateArray::zeroes();
76 if coord.as_ref().len() != bytes.len() {
77 return Err(InvalidCoordinate);
78 }
79 coord.as_mut().copy_from_slice(bytes);
80 Ok(Self(coord))
81 }
82
83 /// Converts coordinate into scalar (coordinate is reduced modulo curve order)
84 pub fn to_scalar(&self) -> Scalar<E> {
85 Scalar::from_be_bytes_mod_order(self.as_be_bytes())
86 }
87
88 /// Constructs a coordinate from a byte array
89 pub fn new(bytes: E::CoordinateArray) -> Self {
90 Self(bytes)
91 }
92
93 /// Bytes representation of a coordinate
94 pub fn as_array(&self) -> &E::CoordinateArray {
95 &self.0
96 }
97}
98
99impl<E: Curve> AsRef<[u8]> for Coordinate<E> {
100 #[inline(always)]
101 fn as_ref(&self) -> &[u8] {
102 self.0.as_ref()
103 }
104}
105
106impl<E: Curve> AsMut<[u8]> for Coordinate<E> {
107 fn as_mut(&mut self) -> &mut [u8] {
108 self.0.as_mut()
109 }
110}
111
112impl<E: Curve> Default for Coordinate<E> {
113 fn default() -> Self {
114 Self(ByteArray::zeroes())
115 }
116}
117
118impl<E: Curve> fmt::Debug for Coordinate<E> {
119 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
120 let mut tuple = f.debug_tuple("Coordinate");
121 #[cfg(feature = "alloc")]
122 {
123 tuple.field(&hex::encode(self.as_be_bytes()));
124 }
125 tuple.finish()
126 }
127}
128
129impl<E: Curve> PartialEq for Coordinate<E> {
130 fn eq(&self, other: &Self) -> bool {
131 self.as_be_bytes() == other.as_be_bytes()
132 }
133}
134
135impl<E: Curve> Eq for Coordinate<E> {}
136
137impl<E: Curve> PartialOrd for Coordinate<E> {
138 fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
139 Some(self.cmp(other))
140 }
141}
142
143impl<E: Curve> Ord for Coordinate<E> {
144 fn cmp(&self, other: &Self) -> core::cmp::Ordering {
145 self.as_be_bytes().cmp(other.as_be_bytes())
146 }
147}
148
149impl<E: Curve> core::hash::Hash for Coordinate<E> {
150 fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
151 self.as_be_bytes().hash(state);
152 }
153}
154
155mod sealed {
156 pub trait Sealed {}
157
158 impl<E: crate::core::Curve> Sealed for crate::Point<E> {}
159 impl<E: crate::core::Curve> Sealed for crate::NonZero<crate::Point<E>> {}
160}
161
162/// Point has affine $x$ coordinate
163pub trait HasAffineX<E: Curve>: sealed::Sealed {
164 /// Retrieves affine $x$ coordinate
165 ///
166 /// Returns `None` if it's `Point::zero()`
167 fn x(&self) -> Option<Coordinate<E>>;
168}
169
170/// Point has affine $y$ coordinate
171pub trait HasAffineY<E: Curve>: sealed::Sealed {
172 /// Retrieves affine $y$ coordinate
173 ///
174 /// Returns `None` if it's `Point::zero()`
175 fn y(&self) -> Option<Coordinate<E>>;
176}
177
178/// Point is uniquely represented by $x$ coordinate and parity of $y$ coordinate
179pub trait HasAffineXAndParity<E: Curve>: HasAffineX<E>
180where
181 Self: Sized,
182{
183 /// Retrieves affine $x$ coordinate and parity of $y$ coordinate
184 ///
185 /// Returns `None` if it's `Point::zero()`
186 fn x_and_parity(&self) -> Option<(Coordinate<E>, Parity)>;
187 /// Constructs point from its $x$ coordinate and parity of $y$ coordinate
188 ///
189 /// Returns `None` if arguments do not represent a valid `Point<E>`
190 fn from_x_and_parity(x: &Coordinate<E>, y_parity: Parity) -> Option<Self>;
191}
192
193/// Point is uniquely represented by affine $x, y$ coordinates
194pub trait HasAffineXY<E: Curve>: HasAffineX<E> + HasAffineY<E>
195where
196 Self: Sized,
197{
198 /// Retrieves affine $x, y$ coordinates
199 ///
200 /// Returns `None` if it's `Point::zero()`
201 fn coords(&self) -> Option<Coordinates<E>>;
202 /// Constructs point from its $x, y$ coordinates
203 ///
204 /// Returns `None` if coordinates do not represent a valid `Point<E>`
205 fn from_coords(coords: &Coordinates<E>) -> Option<Self>;
206}
207
208/// Point _always_ has affine $x$ coordinate (for Edwards curves and non-zero points)
209pub trait AlwaysHasAffineX<E: Curve>: sealed::Sealed {
210 /// Retrieves affine $x$ coordinate of a point
211 fn x(&self) -> Coordinate<E>;
212}
213
214/// Point _always_ has affine $y$ coordinate (for Edwards curves and non-zero points)
215pub trait AlwaysHasAffineY<E: Curve>: sealed::Sealed {
216 /// Retrieves affine $y$ coordinate
217 fn y(&self) -> Coordinate<E>;
218}
219
220/// Point is uniquely represented by affine $y$ coordinate and sign of $x$ coordinate (for Edwards curves)
221pub trait AlwaysHasAffineYAndSign<E: Curve>: AlwaysHasAffineY<E>
222where
223 Self: Sized,
224{
225 /// Retrieves affine $y$ coordinate and sign of $x$ coordinate
226 fn y_and_sign(&self) -> (Sign, Coordinate<E>);
227 /// Constructs point from its $y$ coordinate and sign of $x$ coordinate
228 ///
229 /// Returns `None` if input arguments do not represent a valid `Point<E>`
230 fn from_y_and_sign(x_sign: Sign, y: &Coordinate<E>) -> Option<Self>;
231}
232
233/// Point is uniquely represented by affine $x$ and $y$ coordinates (for Edward curves and non-zero points)
234pub trait AlwaysHasAffineXY<E: Curve>: AlwaysHasAffineX<E> + AlwaysHasAffineY<E> + Sized {
235 /// Constructs a point from affine coordinates
236 ///
237 /// Returns error is coordinates don't correspond to a valid point
238 fn from_coords(coords: &Coordinates<E>) -> Option<Self>;
239}