1use core::fmt;
2use core::hash::{self, Hash};
3use core::iter::Sum;
4
5use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption};
6
7use crate::as_raw::FromRaw;
8use crate::{
9 as_raw::{AsRaw, TryFromRaw},
10 core::*,
11 errors::InvalidPoint,
12 EncodedPoint, Generator,
13};
14
15use self::definition::Point;
16
17pub mod coords;
18pub mod definition;
19
20impl<E: Curve> Point<E> {
21 pub fn generator() -> Generator<E> {
25 Generator::default()
26 }
27
28 pub fn zero() -> Self {
40 Self::from_raw_unchecked(E::Point::zero())
44 }
45
46 pub fn is_zero(&self) -> bool {
55 self.ct_is_zero().into()
56 }
57
58 pub fn ct_is_zero(&self) -> Choice {
62 Zero::is_zero(self.as_raw())
63 }
64
65 pub fn to_bytes(&self, compressed: bool) -> EncodedPoint<E> {
86 if compressed {
87 let bytes = self.as_raw().to_bytes_compressed();
88 EncodedPoint::new_compressed(bytes)
89 } else {
90 let bytes = self.as_raw().to_bytes_uncompressed();
91 EncodedPoint::new_uncompressed(bytes)
92 }
93 }
94
95 pub fn from_bytes(bytes: impl AsRef<[u8]>) -> Result<Self, InvalidPoint> {
97 E::Point::decode(bytes.as_ref())
98 .and_then(Self::try_from_raw)
99 .ok_or(InvalidPoint)
100 }
101
102 pub fn serialized_len(compressed: bool) -> usize {
108 if compressed {
109 E::CompressedPointArray::zeroes().as_ref().len()
110 } else {
111 E::UncompressedPointArray::zeroes().as_ref().len()
112 }
113 }
114}
115
116impl<E: Curve + NoInvalidPoints> FromRaw for Point<E> {
117 fn from_raw(raw: Self::Raw) -> Self {
118 Point::from_raw_unchecked(raw)
119 }
120}
121
122impl<E: Curve> TryFromRaw for Point<E> {
123 fn ct_try_from_raw(point: E::Point) -> CtOption<Self> {
124 let is_on_curve = point.is_on_curve();
125 let is_torsion_free = point.is_torsion_free();
126 let is_valid = is_on_curve & is_torsion_free;
127
128 CtOption::new(Point::from_raw_unchecked(point), is_valid)
132 }
133}
134
135impl<E: Curve> ConditionallySelectable for Point<E> {
136 fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
137 Point::from_raw_unchecked(<E::Point as ConditionallySelectable>::conditional_select(
139 a.as_raw(),
140 b.as_raw(),
141 choice,
142 ))
143 }
144}
145
146impl<E: Curve> ConstantTimeEq for Point<E> {
147 fn ct_eq(&self, other: &Self) -> Choice {
148 self.as_raw().ct_eq(other.as_raw())
149 }
150}
151
152impl<E: Curve> AsRef<Point<E>> for Point<E> {
153 fn as_ref(&self) -> &Point<E> {
154 self
155 }
156}
157
158impl<E: Curve> Sum for Point<E> {
159 fn sum<I: Iterator<Item = Self>>(mut iter: I) -> Self {
160 let Some(first_point) = iter.next() else {
161 return Point::zero();
162 };
163 iter.fold(first_point, |acc, p| acc + p)
164 }
165}
166
167impl<'a, E: Curve> Sum<&'a Point<E>> for Point<E> {
168 fn sum<I: Iterator<Item = &'a Point<E>>>(mut iter: I) -> Self {
169 let Some(first_point) = iter.next() else {
170 return Point::zero();
171 };
172 iter.fold(*first_point, |acc, p| acc + p)
173 }
174}
175
176impl<E: Curve> fmt::Debug for Point<E> {
177 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
178 let mut s = f.debug_struct("Point");
179 s.field("curve", &E::CURVE_NAME);
180
181 #[cfg(feature = "std")]
182 {
183 s.field("value", &hex::encode(self.to_bytes(true)));
184 }
185 #[cfg(not(feature = "std"))]
186 {
187 s.field("value", &"...");
188 }
189
190 s.finish()
191 }
192}
193#[allow(clippy::derived_hash_with_manual_eq)]
194impl<E: Curve> Hash for Point<E> {
195 fn hash<H: hash::Hasher>(&self, state: &mut H) {
196 state.write(self.to_bytes(true).as_bytes())
197 }
198}
199
200impl<E: Curve> PartialOrd for Point<E> {
201 fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
202 Some(self.cmp(other))
203 }
204}
205
206impl<E: Curve> Ord for Point<E> {
207 fn cmp(&self, other: &Self) -> core::cmp::Ordering {
208 self.to_bytes(true)
209 .as_bytes()
210 .cmp(other.to_bytes(true).as_bytes())
211 }
212}
213
214impl<E: Curve> crate::traits::IsZero for Point<E> {
215 fn is_zero(&self) -> bool {
216 *self == Point::zero()
217 }
218}
219
220impl<E: Curve> crate::traits::Zero for Point<E> {
221 fn zero() -> Self {
222 Point::zero()
223 }
224
225 fn is_zero(x: &Self) -> Choice {
226 x.ct_eq(&Self::zero())
227 }
228}
229
230#[cfg(feature = "udigest")]
231impl<E: Curve> udigest::Digestable for Point<E> {
232 fn unambiguously_encode<B>(&self, encoder: udigest::encoding::EncodeValue<B>)
233 where
234 B: udigest::Buffer,
235 {
236 let mut s = encoder.encode_struct();
237 s.add_field("curve").encode_leaf_value(E::CURVE_NAME);
238 s.add_field("point").encode_leaf_value(self.to_bytes(true));
239 s.finish();
240 }
241}