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}