monero_io/
compressed_point.rs

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