primitives/algebra/elliptic_curve/
point.rs1use std::{
2 hash::Hash,
3 iter::Sum,
4 mem::MaybeUninit,
5 ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign},
6 sync::Arc,
7};
8
9use elliptic_curve::group::{Group, GroupEncoding};
10use rand::{
11 distributions::{Distribution, Standard},
12 RngCore,
13};
14use serde::{Deserialize, Serialize};
15use subtle::{Choice, ConditionallySelectable, ConstantTimeEq};
16use wincode::{ReadResult, WriteResult};
17
18use crate::{
19 algebra::elliptic_curve::{
20 curve::{FromCoordinates, PointAtInfinityError, PointCoordinates, ToCoordinates},
21 Curve,
22 Scalar,
23 ScalarAsExtension,
24 },
25 errors::PrimitiveError,
26 random::{CryptoRngCore, Random},
27 sharing::unauthenticated::AdditiveShares,
28};
29
30#[derive(Copy, Clone, Default, Debug, PartialEq, Eq, Hash)]
32#[repr(transparent)]
33pub struct Point<C: Curve>(pub(crate) C::Point);
34
35unsafe impl<C: Curve> bytemuck::TransparentWrapper<C::Point> for Point<C> {}
37
38impl<C: Curve> wincode::SchemaWrite for Point<C> {
39 type Src = Self;
40
41 fn size_of(_src: &Self::Src) -> WriteResult<usize> {
42 let repr = <C::Point as GroupEncoding>::Repr::default();
43 Ok(repr.as_ref().len())
44 }
45
46 fn write(writer: &mut impl wincode::io::Writer, src: &Self::Src) -> WriteResult<()> {
47 let bytes = src.0.to_bytes();
48 Ok(writer.write(bytes.as_ref())?)
49 }
50}
51
52impl<'de, C: Curve> wincode::SchemaRead<'de> for Point<C> {
53 type Dst = Self;
54
55 fn read(
56 reader: &mut impl wincode::io::Reader<'de>,
57 dst: &mut MaybeUninit<Self::Dst>,
58 ) -> ReadResult<()> {
59 let mut repr = <C::Point as GroupEncoding>::Repr::default();
60 let len = repr.as_ref().len();
61 let bytes = reader.fill_exact(len)?;
62 repr.as_mut().copy_from_slice(bytes);
63 reader.consume(len)?;
64
65 let point = Option::from(C::Point::from_bytes(&repr))
66 .ok_or(wincode::ReadError::Custom("invalid curve point encoding"))?;
67
68 dst.write(Point(point));
69 Ok(())
70 }
71}
72
73impl<C: Curve> Unpin for Point<C> {}
74
75impl<C: Curve> Serialize for Point<C> {
76 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
77 let bytes = self.0.to_bytes();
78 serde_bytes::serialize(bytes.as_ref(), serializer)
79 }
80}
81
82impl<'de, C: Curve> Deserialize<'de> for Point<C> {
83 fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
84 let bytes: &[u8] = serde_bytes::deserialize(deserializer)?;
85 let endian_bytes = if C::POINT_BIG_ENDIAN {
86 Point::from_be_bytes(bytes)
87 } else {
88 Point::from_le_bytes(bytes)
89 };
90 let point = endian_bytes.map_err(|err| {
91 serde::de::Error::custom(format!("Failed to deserialize curve point: {err:?}"))
92 })?;
93 Ok(point)
94 }
95}
96
97impl<C: Curve> Point<C> {
102 pub fn identity() -> Point<C> {
104 Point(C::Point::identity())
105 }
106
107 pub fn new(point: C::Point) -> Point<C> {
108 Point(point)
109 }
110
111 pub fn is_identity(&self) -> Choice {
113 self.ct_eq(&Point::identity())
114 }
115
116 pub fn inner(&self) -> C::Point {
118 self.0
119 }
120
121 pub fn generator() -> Point<C> {
123 Point(<C::Point as Group>::generator())
124 }
125
126 pub fn from_be_bytes(bytes: &[u8]) -> Result<Point<C>, PrimitiveError> {
128 let mut encoding = <C::Point as GroupEncoding>::Repr::default();
129 if bytes.len() != encoding.as_ref().len() {
130 return Err(PrimitiveError::DeserializationFailed(format!(
131 "Invalid point encoding length: expected {}, got {}",
132 encoding.as_ref().len(),
133 bytes.len()
134 )));
135 }
136
137 if C::POINT_BIG_ENDIAN {
138 encoding.as_mut().copy_from_slice(bytes);
139 } else {
140 encoding.as_mut().copy_from_slice(bytes);
141 encoding.as_mut().reverse();
142 }
143
144 let point = Option::from(C::Point::from_bytes(&encoding)).ok_or_else(|| {
145 PrimitiveError::DeserializationFailed("Invalid point encoding".to_string())
146 })?;
147 Ok(Point(point))
148 }
149
150 pub fn from_le_bytes(bytes: &[u8]) -> Result<Point<C>, PrimitiveError> {
153 let mut encoding = <C::Point as GroupEncoding>::Repr::default();
154 if bytes.len() != encoding.as_ref().len() {
155 return Err(PrimitiveError::DeserializationFailed(format!(
156 "Invalid point encoding length: expected {}, got {}",
157 encoding.as_ref().len(),
158 bytes.len()
159 )));
160 }
161
162 if C::POINT_BIG_ENDIAN {
163 encoding.as_mut().copy_from_slice(bytes);
164 encoding.as_mut().reverse();
165 } else {
166 encoding.as_mut().copy_from_slice(bytes);
167 }
168
169 let point = Option::from(C::Point::from_bytes(&encoding)).ok_or_else(|| {
170 PrimitiveError::DeserializationFailed("Invalid point encoding".to_string())
171 })?;
172 Ok(Point(point))
173 }
174
175 pub fn to_bytes(&self) -> Arc<[u8]> {
177 self.0.to_bytes().as_ref().into()
178 }
179
180 pub fn from_coordinates(coordinates: PointCoordinates<C>) -> Option<Point<C>> {
181 C::Point::from_coordinates(coordinates).map(Point)
182 }
183
184 pub fn to_coordinates(self) -> Result<PointCoordinates<C>, PointAtInfinityError> {
185 self.0.to_coordinates()
186 }
187}
188
189impl<C: Curve> Random for Point<C> {
190 #[inline]
191 fn random(rng: impl CryptoRngCore) -> Self {
192 Point(C::Point::random(rng))
193 }
194}
195
196impl<C: Curve> Distribution<Point<C>> for Standard {
197 #[inline]
198 fn sample<R: RngCore + ?Sized>(&self, rng: &mut R) -> Point<C> {
199 Point(C::Point::random(rng))
200 }
201}
202
203#[macros::op_variants(owned, borrowed, flipped_commutative)]
210impl<C: Curve> Add<&Point<C>> for Point<C> {
211 type Output = Point<C>;
212
213 #[inline]
214 fn add(mut self, rhs: &Point<C>) -> Self::Output {
215 self.0 += rhs.0;
216 self
217 }
218}
219
220#[macros::op_variants(owned)]
221impl<C: Curve> AddAssign<&Point<C>> for Point<C> {
222 #[inline]
223 fn add_assign(&mut self, rhs: &Point<C>) {
224 self.0 += rhs.0;
225 }
226}
227
228#[macros::op_variants(owned, borrowed, flipped)]
231impl<C: Curve> Sub<&Point<C>> for Point<C> {
232 type Output = Point<C>;
233
234 #[inline]
235 fn sub(mut self, rhs: &Point<C>) -> Self::Output {
236 self.0 -= rhs.0;
237 self
238 }
239}
240
241#[macros::op_variants(owned)]
242impl<C: Curve> SubAssign<&Point<C>> for Point<C> {
243 #[inline]
244 fn sub_assign(&mut self, rhs: &Point<C>) {
245 self.0 -= rhs.0;
246 }
247}
248
249#[macros::op_variants(borrowed)]
252impl<C: Curve> Neg for Point<C> {
253 type Output = Point<C>;
254
255 #[inline]
256 fn neg(self) -> Self::Output {
257 Point(-self.0)
258 }
259}
260
261#[macros::op_variants(owned, borrowed, flipped)]
264impl<C: Curve> Mul<&ScalarAsExtension<C>> for Point<C> {
265 type Output = Point<C>;
266
267 #[inline]
268 fn mul(mut self, rhs: &ScalarAsExtension<C>) -> Self::Output {
269 self.0 *= rhs.0;
270 self
271 }
272}
273
274#[macros::op_variants(owned, borrowed, flipped_commutative)]
275impl<C: Curve> Mul<&Point<C>> for ScalarAsExtension<C> {
276 type Output = Point<C>;
277
278 #[inline]
279 fn mul(self, rhs: &Point<C>) -> Self::Output {
280 Point(rhs.0 * self.0)
281 }
282}
283
284#[macros::op_variants(owned, borrowed, flipped)]
285impl<C: Curve> Mul<&Scalar<C>> for Point<C> {
286 type Output = Point<C>;
287
288 #[inline]
289 fn mul(self, rhs: &Scalar<C>) -> Self::Output {
290 Point(self.0 * rhs.0)
291 }
292}
293
294#[macros::op_variants(owned, borrowed, flipped_commutative)]
295impl<C: Curve> Mul<&Point<C>> for Scalar<C> {
296 type Output = Point<C>;
297
298 #[inline]
299 fn mul(self, rhs: &Point<C>) -> Self::Output {
300 Point(rhs.0 * self.0)
301 }
302}
303
304#[macros::op_variants(owned)]
307impl<C: Curve> MulAssign<&ScalarAsExtension<C>> for Point<C> {
308 #[inline]
309 fn mul_assign(&mut self, rhs: &ScalarAsExtension<C>) {
310 self.0 *= rhs.0;
311 }
312}
313
314#[macros::op_variants(owned)]
315impl<C: Curve> MulAssign<&Scalar<C>> for Point<C> {
316 #[inline]
317 fn mul_assign(&mut self, rhs: &Scalar<C>) {
318 self.0 *= rhs.0;
319 }
320}
321
322impl<C: Curve> ConstantTimeEq for Point<C> {
325 #[inline]
326 fn ct_eq(&self, other: &Self) -> Choice {
327 self.0.ct_eq(&other.0)
328 }
329}
330
331impl<C: Curve> ConditionallySelectable for Point<C> {
332 #[inline]
333 fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
334 let selected = C::Point::conditional_select(&a.0, &b.0, choice);
335 Point(selected)
336 }
337}
338
339impl<C: Curve> AdditiveShares for Point<C> {}
342
343impl<C: Curve> Sum for Point<C> {
346 #[inline]
347 fn sum<I: Iterator<Item = Point<C>>>(iter: I) -> Self {
348 iter.fold(Point::identity(), |acc, x| acc + x)
349 }
350}
351
352impl<'a, C: Curve> Sum<&'a Point<C>> for Point<C> {
353 #[inline]
354 fn sum<I: Iterator<Item = &'a Point<C>>>(iter: I) -> Self {
355 iter.fold(Point::identity(), |acc, x| acc + x)
356 }
357}
358
359#[cfg(test)]
360mod tests {
361 use super::*;
362 use crate::algebra::elliptic_curve::Curve25519Ristretto;
363
364 #[test]
365 fn test_point_serialization() {
366 let point = Point::<Curve25519Ristretto>::generator();
367 let bytes = point.to_bytes();
368 let deserialized_point = Point::<Curve25519Ristretto>::from_le_bytes(&bytes).unwrap();
369 assert_eq!(point, deserialized_point);
370
371 let bytes = bytes.as_ref()[1..].to_vec(); let result = Point::<Curve25519Ristretto>::from_le_bytes(&bytes);
373 assert!(result.is_err());
374 }
375
376 #[test]
381 fn test_wincode_rejects_invalid_point() {
382 let valid = Point::<Curve25519Ristretto>::generator();
383 let mut buf = wincode::serialize(&valid).unwrap();
384
385 let len = buf.len();
388 buf[len - 32..].fill(0xFF);
389
390 let result = wincode::deserialize::<Point<Curve25519Ristretto>>(&buf);
391 assert!(
392 result.is_err(),
393 "wincode deserialized an invalid curve point \
394 without returning an error"
395 );
396 }
397}