monero_primitives/
lib.rs

1#![cfg_attr(docsrs, feature(doc_auto_cfg))]
2#![doc = include_str!("../README.md")]
3#![deny(missing_docs)]
4#![cfg_attr(not(feature = "std"), no_std)]
5
6use std_shims::{io, vec::Vec};
7#[cfg(feature = "std")]
8use std_shims::sync::LazyLock;
9
10use zeroize::{Zeroize, ZeroizeOnDrop};
11
12use sha3::{Digest, Keccak256};
13use curve25519_dalek::{
14  constants::ED25519_BASEPOINT_POINT,
15  traits::VartimePrecomputedMultiscalarMul,
16  scalar::Scalar,
17  edwards::{EdwardsPoint, VartimeEdwardsPrecomputation},
18};
19
20use monero_io::*;
21use monero_generators::H;
22
23mod unreduced_scalar;
24pub use unreduced_scalar::UnreducedScalar;
25
26#[cfg(test)]
27mod tests;
28
29// On std, we cache some variables in statics.
30#[cfg(feature = "std")]
31static INV_EIGHT_CELL: LazyLock<Scalar> = LazyLock::new(|| Scalar::from(8u8).invert());
32/// The inverse of 8 over l, the prime factor of the order of Ed25519.
33#[cfg(feature = "std")]
34#[allow(non_snake_case)]
35pub fn INV_EIGHT() -> Scalar {
36  *INV_EIGHT_CELL
37}
38// In no-std environments, we prefer the reduced memory use and calculate it ad-hoc.
39/// The inverse of 8 over l, the prime factor of the order of Ed25519.
40#[cfg(not(feature = "std"))]
41#[allow(non_snake_case)]
42pub fn INV_EIGHT() -> Scalar {
43  Scalar::from(8u8).invert()
44}
45
46#[cfg(feature = "std")]
47static G_PRECOMP_CELL: LazyLock<VartimeEdwardsPrecomputation> =
48  LazyLock::new(|| VartimeEdwardsPrecomputation::new([ED25519_BASEPOINT_POINT]));
49/// A cached (if std) pre-computation of the Ed25519 generator, G.
50#[cfg(feature = "std")]
51#[allow(non_snake_case)]
52pub fn G_PRECOMP() -> &'static VartimeEdwardsPrecomputation {
53  &G_PRECOMP_CELL
54}
55/// A cached (if std) pre-computation of the Ed25519 generator, G.
56#[cfg(not(feature = "std"))]
57#[allow(non_snake_case)]
58pub fn G_PRECOMP() -> VartimeEdwardsPrecomputation {
59  VartimeEdwardsPrecomputation::new([ED25519_BASEPOINT_POINT])
60}
61
62/// The Keccak-256 hash function.
63pub fn keccak256(data: impl AsRef<[u8]>) -> [u8; 32] {
64  Keccak256::digest(data.as_ref()).into()
65}
66
67/// Hash the provided data to a scalar via keccak256(data) % l.
68///
69/// This function panics if it finds the Keccak-256 preimage for [0; 32].
70pub fn keccak256_to_scalar(data: impl AsRef<[u8]>) -> Scalar {
71  let scalar = Scalar::from_bytes_mod_order(keccak256(data.as_ref()));
72  // Monero will explicitly error in this case
73  // This library acknowledges its practical impossibility of it occurring, and doesn't bother to
74  // code in logic to handle it. That said, if it ever occurs, something must happen in order to
75  // not generate/verify a proof we believe to be valid when it isn't
76  assert!(
77    scalar != Scalar::ZERO,
78    "keccak256(preimage) \\cong 0 \\mod l! Preimage: {:?}",
79    data.as_ref()
80  );
81  scalar
82}
83
84/// Transparent structure representing a Pedersen commitment's contents.
85#[allow(non_snake_case)]
86#[derive(Clone, PartialEq, Eq, Zeroize, ZeroizeOnDrop)]
87pub struct Commitment {
88  /// The mask for this commitment.
89  pub mask: Scalar,
90  /// The amount committed to by this commitment.
91  pub amount: u64,
92}
93
94impl core::fmt::Debug for Commitment {
95  fn fmt(&self, fmt: &mut core::fmt::Formatter<'_>) -> Result<(), core::fmt::Error> {
96    fmt.debug_struct("Commitment").field("amount", &self.amount).finish_non_exhaustive()
97  }
98}
99
100impl Commitment {
101  /// A commitment to zero, defined with a mask of 1 (as to not be the identity).
102  pub fn zero() -> Commitment {
103    Commitment { mask: Scalar::ONE, amount: 0 }
104  }
105
106  /// Create a new Commitment.
107  pub fn new(mask: Scalar, amount: u64) -> Commitment {
108    Commitment { mask, amount }
109  }
110
111  /// Calculate the Pedersen commitment, as a point, from this transparent structure.
112  pub fn calculate(&self) -> EdwardsPoint {
113    EdwardsPoint::vartime_double_scalar_mul_basepoint(&Scalar::from(self.amount), &H, &self.mask)
114  }
115
116  /// Write the Commitment.
117  ///
118  /// This is not a Monero protocol defined struct, and this is accordingly not a Monero protocol
119  /// defined serialization.
120  pub fn write<W: io::Write>(&self, w: &mut W) -> io::Result<()> {
121    w.write_all(&self.mask.to_bytes())?;
122    w.write_all(&self.amount.to_le_bytes())
123  }
124
125  /// Serialize the Commitment to a `Vec<u8>`.
126  ///
127  /// This is not a Monero protocol defined struct, and this is accordingly not a Monero protocol
128  /// defined serialization.
129  pub fn serialize(&self) -> Vec<u8> {
130    let mut res = Vec::with_capacity(32 + 8);
131    self.write(&mut res).expect("write failed but <Vec as io::Write> doesn't fail");
132    res
133  }
134
135  /// Read a Commitment.
136  ///
137  /// This is not a Monero protocol defined struct, and this is accordingly not a Monero protocol
138  /// defined serialization.
139  pub fn read<R: io::Read>(r: &mut R) -> io::Result<Commitment> {
140    Ok(Commitment::new(read_scalar(r)?, read_u64(r)?))
141  }
142}
143
144/// Decoy data, as used for producing Monero's ring signatures.
145#[derive(Clone, PartialEq, Eq, Zeroize, ZeroizeOnDrop)]
146pub struct Decoys {
147  offsets: Vec<u64>,
148  signer_index: u8,
149  ring: Vec<[EdwardsPoint; 2]>,
150}
151
152impl core::fmt::Debug for Decoys {
153  fn fmt(&self, fmt: &mut core::fmt::Formatter<'_>) -> Result<(), core::fmt::Error> {
154    fmt
155      .debug_struct("Decoys")
156      .field("offsets", &self.offsets)
157      .field("ring", &self.ring)
158      .finish_non_exhaustive()
159  }
160}
161
162#[allow(clippy::len_without_is_empty)]
163impl Decoys {
164  /// Create a new instance of decoy data.
165  ///
166  /// `offsets` are the positions of each ring member within the Monero blockchain, offset from the
167  /// prior member's position (with the initial ring member offset from 0).
168  pub fn new(offsets: Vec<u64>, signer_index: u8, ring: Vec<[EdwardsPoint; 2]>) -> Option<Self> {
169    if (offsets.len() > usize::from(u8::MAX)) ||
170      (offsets.len() != ring.len()) ||
171      (usize::from(signer_index) >= ring.len())
172    {
173      None?;
174    }
175    // Check these offsets form representable positions
176    if offsets.iter().copied().try_fold(0, u64::checked_add).is_none() {
177      None?;
178    }
179    Some(Decoys { offsets, signer_index, ring })
180  }
181
182  /// The length of the ring.
183  pub fn len(&self) -> usize {
184    self.offsets.len()
185  }
186
187  /// The positions of the ring members within the Monero blockchain, as their offsets.
188  ///
189  /// The list is formatted as the position of the first ring member, then the offset from each
190  /// ring member to its prior.
191  pub fn offsets(&self) -> &[u64] {
192    &self.offsets
193  }
194
195  /// The positions of the ring members within the Monero blockchain.
196  pub fn positions(&self) -> Vec<u64> {
197    let mut res = Vec::with_capacity(self.len());
198    res.push(self.offsets[0]);
199    for m in 1 .. self.len() {
200      res.push(res[m - 1] + self.offsets[m]);
201    }
202    res
203  }
204
205  /// The index of the signer within the ring.
206  pub fn signer_index(&self) -> u8 {
207    self.signer_index
208  }
209
210  /// The ring.
211  pub fn ring(&self) -> &[[EdwardsPoint; 2]] {
212    &self.ring
213  }
214
215  /// The [key, commitment] pair of the signer.
216  pub fn signer_ring_members(&self) -> [EdwardsPoint; 2] {
217    self.ring[usize::from(self.signer_index)]
218  }
219
220  /// Write the Decoys.
221  ///
222  /// This is not a Monero protocol defined struct, and this is accordingly not a Monero protocol
223  /// defined serialization.
224  pub fn write(&self, w: &mut impl io::Write) -> io::Result<()> {
225    write_vec(write_varint, &self.offsets, w)?;
226    w.write_all(&[self.signer_index])?;
227    write_raw_vec(
228      |pair, w| {
229        write_point(&pair[0], w)?;
230        write_point(&pair[1], w)
231      },
232      &self.ring,
233      w,
234    )
235  }
236
237  /// Serialize the Decoys to a `Vec<u8>`.
238  ///
239  /// This is not a Monero protocol defined struct, and this is accordingly not a Monero protocol
240  /// defined serialization.
241  pub fn serialize(&self) -> Vec<u8> {
242    let mut res =
243      Vec::with_capacity((1 + (2 * self.offsets.len())) + 1 + 1 + (self.ring.len() * 64));
244    self.write(&mut res).expect("write failed but <Vec as io::Write> doesn't fail");
245    res
246  }
247
248  /// Read a set of Decoys.
249  ///
250  /// This is not a Monero protocol defined struct, and this is accordingly not a Monero protocol
251  /// defined serialization.
252  pub fn read(r: &mut impl io::Read) -> io::Result<Decoys> {
253    let offsets = read_vec(read_varint, None, r)?;
254    let len = offsets.len();
255    Decoys::new(
256      offsets,
257      read_byte(r)?,
258      read_raw_vec(|r| Ok([read_point(r)?, read_point(r)?]), len, r)?,
259    )
260    .ok_or_else(|| io::Error::other("invalid Decoys"))
261  }
262}