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}