monero_mlsag_mirror/
lib.rs1#![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#![allow(non_snake_case)]
6
7use std_shims::{
8 vec,
9 vec::Vec,
10 io::{self, Read, Write},
11};
12
13use zeroize::Zeroize;
14
15use curve25519_dalek::{traits::IsIdentity, Scalar, EdwardsPoint};
16
17use monero_io::*;
18use monero_generators::{H, hash_to_point};
19use monero_primitives::keccak256_to_scalar;
20
21#[derive(Clone, Copy, PartialEq, Eq, Debug)]
23#[cfg_attr(feature = "std", derive(thiserror::Error))]
24pub enum MlsagError {
25 #[cfg_attr(feature = "std", error("invalid ring"))]
27 InvalidRing,
28 #[cfg_attr(feature = "std", error("invalid amount of key images"))]
30 InvalidAmountOfKeyImages,
31 #[cfg_attr(feature = "std", error("invalid ss"))]
33 InvalidSs,
34 #[cfg_attr(feature = "std", error("invalid key image"))]
36 InvalidKeyImage,
37 #[cfg_attr(feature = "std", error("invalid ci"))]
39 InvalidCi,
40}
41
42#[derive(Clone, PartialEq, Eq, Debug, Zeroize)]
44pub struct RingMatrix {
45 matrix: Vec<Vec<EdwardsPoint>>,
46}
47
48impl RingMatrix {
49 fn new(matrix: Vec<Vec<EdwardsPoint>>) -> Result<Self, MlsagError> {
51 if matrix.len() < 2 {
55 Err(MlsagError::InvalidRing)?;
56 }
57 for member in &matrix {
58 if member.is_empty() || (member.len() != matrix[0].len()) {
59 Err(MlsagError::InvalidRing)?;
60 }
61 }
62
63 Ok(RingMatrix { matrix })
64 }
65
66 pub fn individual(
68 ring: &[[EdwardsPoint; 2]],
69 pseudo_out: EdwardsPoint,
70 ) -> Result<Self, MlsagError> {
71 let mut matrix = Vec::with_capacity(ring.len());
72 for ring_member in ring {
73 matrix.push(vec![ring_member[0], ring_member[1] - pseudo_out]);
74 }
75 RingMatrix::new(matrix)
76 }
77
78 fn iter(&self) -> impl Iterator<Item = &[EdwardsPoint]> {
80 self.matrix.iter().map(AsRef::as_ref)
81 }
82
83 pub fn members(&self) -> usize {
85 self.matrix.len()
86 }
87
88 pub fn member_len(&self) -> usize {
93 self.matrix[0].len()
95 }
96}
97
98#[derive(Clone, PartialEq, Eq, Debug, Zeroize)]
100pub struct Mlsag {
101 ss: Vec<Vec<Scalar>>,
102 cc: Scalar,
103}
104
105impl Mlsag {
106 pub fn write<W: Write>(&self, w: &mut W) -> io::Result<()> {
108 for ss in &self.ss {
109 write_raw_vec(write_scalar, ss, w)?;
110 }
111 write_scalar(&self.cc, w)
112 }
113
114 pub fn read<R: Read>(mixins: usize, ss_2_elements: usize, r: &mut R) -> io::Result<Mlsag> {
116 Ok(Mlsag {
117 ss: (0 .. mixins)
118 .map(|_| read_raw_vec(read_scalar, ss_2_elements, r))
119 .collect::<Result<_, _>>()?,
120 cc: read_scalar(r)?,
121 })
122 }
123
124 pub fn verify(
126 &self,
127 msg: &[u8; 32],
128 ring: &RingMatrix,
129 key_images: &[EdwardsPoint],
130 ) -> Result<(), MlsagError> {
131 if ring.member_len() != (key_images.len() + 1) {
134 Err(MlsagError::InvalidAmountOfKeyImages)?;
135 }
136
137 let mut buf = Vec::with_capacity(6 * 32);
138 buf.extend_from_slice(msg);
139
140 let mut ci = self.cc;
141
142 let key_images_iter = key_images.iter().map(|ki| Some(*ki)).chain(core::iter::once(None));
145
146 if ring.matrix.len() != self.ss.len() {
147 Err(MlsagError::InvalidSs)?;
148 }
149
150 for (ring_member, ss) in ring.iter().zip(&self.ss) {
151 if ring_member.len() != ss.len() {
152 Err(MlsagError::InvalidSs)?;
153 }
154
155 for ((ring_member_entry, s), ki) in ring_member.iter().zip(ss).zip(key_images_iter.clone()) {
156 #[allow(non_snake_case)]
157 let L = EdwardsPoint::vartime_double_scalar_mul_basepoint(&ci, ring_member_entry, s);
158
159 let compressed_ring_member_entry = ring_member_entry.compress();
160 buf.extend_from_slice(compressed_ring_member_entry.as_bytes());
161 buf.extend_from_slice(L.compress().as_bytes());
162
163 if let Some(ki) = ki {
166 if ki.is_identity() || (!ki.is_torsion_free()) {
167 Err(MlsagError::InvalidKeyImage)?;
168 }
169
170 #[allow(non_snake_case)]
171 let R = (s * hash_to_point(compressed_ring_member_entry.to_bytes())) + (ci * ki);
172 buf.extend_from_slice(R.compress().as_bytes());
173 }
174 }
175
176 ci = keccak256_to_scalar(&buf);
177 buf.drain(msg.len() ..);
179 }
180
181 if ci != self.cc {
182 Err(MlsagError::InvalidCi)?
183 }
184 Ok(())
185 }
186}
187
188#[derive(Clone, PartialEq, Eq, Debug, Zeroize)]
192pub struct AggregateRingMatrixBuilder {
193 key_ring: Vec<Vec<EdwardsPoint>>,
194 amounts_ring: Vec<EdwardsPoint>,
195 sum_out: EdwardsPoint,
196}
197
198impl AggregateRingMatrixBuilder {
199 pub fn new(commitments: &[EdwardsPoint], fee: u64) -> Self {
203 AggregateRingMatrixBuilder {
204 key_ring: vec![],
205 amounts_ring: vec![],
206 sum_out: commitments.iter().sum::<EdwardsPoint>() + (*H * Scalar::from(fee)),
207 }
208 }
209
210 pub fn push_ring(&mut self, ring: &[[EdwardsPoint; 2]]) -> Result<(), MlsagError> {
212 if self.key_ring.is_empty() {
213 self.key_ring = vec![vec![]; ring.len()];
214 self.amounts_ring = vec![-self.sum_out; ring.len()];
216 }
217
218 if (self.amounts_ring.len() != ring.len()) || ring.is_empty() {
219 return Err(MlsagError::InvalidRing);
221 }
222
223 for (i, ring_member) in ring.iter().enumerate() {
224 self.key_ring[i].push(ring_member[0]);
225 self.amounts_ring[i] += ring_member[1]
226 }
227
228 Ok(())
229 }
230
231 pub fn build(mut self) -> Result<RingMatrix, MlsagError> {
233 for (i, amount_commitment) in self.amounts_ring.drain(..).enumerate() {
234 self.key_ring[i].push(amount_commitment);
235 }
236 RingMatrix::new(self.key_ring)
237 }
238}