Skip to main content

primitives/algebra/elliptic_curve/
point.rs

1use 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/// A point on a given curve.
31#[derive(Copy, Clone, Default, Debug, PartialEq, Eq, Hash)]
32#[repr(transparent)]
33pub struct Point<C: Curve>(pub(crate) C::Point);
34
35// SAFETY: Point<C> is #[repr(transparent)] over C::Point.
36unsafe 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
97// ------------------------
98// | Misc Implementations |
99// ------------------------
100
101impl<C: Curve> Point<C> {
102    /// The additive identity in the curve group
103    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    /// Check whether the given point is the identity point in the group
112    pub fn is_identity(&self) -> Choice {
113        self.ct_eq(&Point::identity())
114    }
115
116    /// Return the wrapped type
117    pub fn inner(&self) -> C::Point {
118        self.0
119    }
120
121    /// The group generator
122    pub fn generator() -> Point<C> {
123        Point(<C::Point as Group>::generator())
124    }
125
126    /// Deserialize a point from a byte buffer
127    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    /// Deserialize a point from a byte buffer
151    /// TODO: Check this is constant-time
152    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    /// Serialize the point to a byte buffer
176    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// ------------------------------------
204// | Curve Arithmetic Implementations |
205// ------------------------------------
206
207// === Addition === //
208
209#[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// === Subtraction === //
229
230#[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// === Negation === //
250
251#[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// === Scalar Multiplication === //
262
263#[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// === MulAssign === //
305
306#[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
322// === Equality === //
323
324impl<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
339// === Other === //
340
341impl<C: Curve> AdditiveShares for Point<C> {}
342
343// === Iterator traits === //
344
345impl<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(); // Invalid length
372        let result = Point::<Curve25519Ristretto>::from_le_bytes(&bytes);
373        assert!(result.is_err());
374    }
375
376    /// Wincode should reject bytes that don't encode a valid curve
377    /// point, but currently `SchemaRead` is derived on the newtype
378    /// wrapper and blindly reads the inner `C::Point` without
379    /// validation.
380    #[test]
381    fn test_wincode_rejects_invalid_point() {
382        let valid = Point::<Curve25519Ristretto>::generator();
383        let mut buf = wincode::serialize(&valid).unwrap();
384
385        // Corrupt the serialized point bytes to produce an invalid
386        // Ristretto encoding (all 0xFF bytes is not on the curve).
387        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}