monero_oxide/
ring_signatures.rs1use std_shims::{
2 io::{self, *},
3 vec::Vec,
4};
5
6use zeroize::Zeroize;
7
8use curve25519_dalek::{EdwardsPoint, Scalar};
9
10use crate::{io::*, generators::biased_hash_to_point, primitives::keccak256_to_scalar};
11
12#[derive(Clone, PartialEq, Eq, Debug, Zeroize)]
13pub(crate) struct Signature {
14 #[cfg(test)]
15 pub(crate) c: Scalar,
16 #[cfg(test)]
17 pub(crate) s: Scalar,
18 #[cfg(not(test))]
19 c: Scalar,
20 #[cfg(not(test))]
21 s: Scalar,
22}
23
24impl Signature {
25 fn write<W: Write>(&self, w: &mut W) -> io::Result<()> {
26 write_scalar(&self.c, w)?;
27 write_scalar(&self.s, w)?;
28 Ok(())
29 }
30
31 fn read<R: Read>(r: &mut R) -> io::Result<Signature> {
32 Ok(Signature { c: read_scalar(r)?, s: read_scalar(r)? })
33 }
34}
35
36#[derive(Clone, PartialEq, Eq, Debug, Zeroize)]
40pub struct RingSignature {
41 #[cfg(test)]
42 pub(crate) sigs: Vec<Signature>,
43 #[cfg(not(test))]
44 sigs: Vec<Signature>,
45}
46
47impl RingSignature {
48 pub fn write<W: Write>(&self, w: &mut W) -> io::Result<()> {
50 for sig in &self.sigs {
51 sig.write(w)?;
52 }
53 Ok(())
54 }
55
56 pub fn read<R: Read>(members: usize, r: &mut R) -> io::Result<RingSignature> {
58 Ok(RingSignature { sigs: read_raw_vec(Signature::read, members, r)? })
59 }
60
61 pub fn verify(
63 &self,
64 msg: &[u8; 32],
65 ring: &[CompressedPoint],
66 key_image: &CompressedPoint,
67 ) -> bool {
68 if ring.len() != self.sigs.len() {
69 return false;
70 }
71
72 let Some(key_image) = key_image.decompress() else {
73 return false;
74 };
75
76 if !key_image.is_torsion_free() {
77 return false;
78 }
79
80 let mut buf = Vec::with_capacity(32 + (2 * 32 * ring.len()));
81 buf.extend_from_slice(msg);
82
83 let mut sum = Scalar::ZERO;
84 for (ring_member, sig) in ring.iter().zip(&self.sigs) {
85 let Some(decomp_ring_member) = ring_member.decompress() else {
104 return false;
105 };
106
107 #[allow(non_snake_case)]
108 let Li =
109 EdwardsPoint::vartime_double_scalar_mul_basepoint(&sig.c, &decomp_ring_member, &sig.s);
110 buf.extend_from_slice(Li.compress().as_bytes());
111 #[allow(non_snake_case)]
112 let Ri = (sig.s * biased_hash_to_point(ring_member.to_bytes())) + (sig.c * key_image);
113 buf.extend_from_slice(Ri.compress().as_bytes());
114
115 sum += sig.c;
116 }
117 sum == keccak256_to_scalar(buf)
118 }
119}