monero_io/compressed_point.rs
1use std_shims::io::{self, Read, Write};
2
3use zeroize::Zeroize;
4
5use curve25519_dalek::{EdwardsPoint, edwards::CompressedEdwardsY};
6
7use crate::read_bytes;
8
9/// A compressed Ed25519 point.
10///
11/// [`CompressedEdwardsY`], the [`curve25519_dalek`] version of this struct exposes a
12/// [`CompressedEdwardsY::decompress`] function that does not check the point is canonically
13/// encoded. This struct exposes a [`CompressedPoint::decompress`] function that does check
14/// the point is canonically encoded, check that function for details.
15#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Zeroize)]
16pub struct CompressedPoint(pub [u8; 32]);
17
18impl CompressedPoint {
19 /// Read a [`CompressedPoint`] without checking if this point can be decompressed.
20 pub fn read<R: Read>(r: &mut R) -> io::Result<CompressedPoint> {
21 Ok(CompressedPoint(read_bytes(r)?))
22 }
23
24 /// Write a compressed point.
25 pub fn write<W: Write>(&self, w: &mut W) -> io::Result<()> {
26 w.write_all(&self.0)
27 }
28
29 /// Returns the raw bytes of the compressed point.
30 pub fn to_bytes(&self) -> [u8; 32] {
31 self.0
32 }
33
34 /// Returns a reference to the raw bytes of the compressed point.
35 pub fn as_bytes(&self) -> &[u8; 32] {
36 &self.0
37 }
38
39 /// Decompress a canonically-encoded Ed25519 point.
40 ///
41 /// Ed25519 is of order `8 * l`. This function ensures each of those `8 * l` points have a
42 /// singular encoding by checking points aren't encoded with an unreduced field element,
43 /// and aren't negative when the negative is equivalent (0 == -0).
44 ///
45 /// Since this decodes an Ed25519 point, it does not check the point is in the prime-order
46 /// subgroup. Torsioned points do have a canonical encoding, and only aren't canonical when
47 /// considered in relation to the prime-order subgroup.
48 pub fn decompress(&self) -> Option<EdwardsPoint> {
49 CompressedEdwardsY(self.0)
50 .decompress()
51 // Ban points which are either unreduced or -0
52 .filter(|point| point.compress().to_bytes() == self.0)
53 }
54}
55
56impl From<[u8; 32]> for CompressedPoint {
57 fn from(value: [u8; 32]) -> Self {
58 Self(value)
59 }
60}
61
62impl From<CompressedEdwardsY> for CompressedPoint {
63 fn from(compressed: CompressedEdwardsY) -> Self {
64 Self(compressed.0)
65 }
66}