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}