1use std_shims::{
2 io::{self, *},
3 vec::Vec,
4};
56use zeroize::Zeroize;
78use curve25519_dalek::{EdwardsPoint, Scalar, edwards::CompressedEdwardsY};
910use crate::{io::*, generators::hash_to_point, primitives::keccak256_to_scalar};
1112#[derive(Clone, PartialEq, Eq, Debug, Zeroize)]
13pub(crate) struct Signature {
14#[cfg(test)]
15pub(crate) c: Scalar,
16#[cfg(test)]
17pub(crate) s: Scalar,
18#[cfg(not(test))]
19c: Scalar,
20#[cfg(not(test))]
21s: Scalar,
22}
2324impl Signature {
25fn write<W: Write>(&self, w: &mut W) -> io::Result<()> {
26 write_scalar(&self.c, w)?;
27 write_scalar(&self.s, w)?;
28Ok(())
29 }
3031fn read<R: Read>(r: &mut R) -> io::Result<Signature> {
32Ok(Signature { c: read_scalar(r)?, s: read_scalar(r)? })
33 }
34}
3536/// A ring signature.
37///
38/// This was used by the original Cryptonote transaction protocol and was deprecated with RingCT.
39#[derive(Clone, PartialEq, Eq, Debug, Zeroize)]
40pub struct RingSignature {
41#[cfg(test)]
42pub(crate) sigs: Vec<Signature>,
43#[cfg(not(test))]
44sigs: Vec<Signature>,
45}
4647impl RingSignature {
48/// Write the RingSignature.
49pub fn write<W: Write>(&self, w: &mut W) -> io::Result<()> {
50for sig in &self.sigs {
51 sig.write(w)?;
52 }
53Ok(())
54 }
5556/// Read a RingSignature.
57pub fn read<R: Read>(members: usize, r: &mut R) -> io::Result<RingSignature> {
58Ok(RingSignature { sigs: read_raw_vec(Signature::read, members, r)? })
59 }
6061/// Verify the ring signature.
62pub fn verify(
63&self,
64 msg: &[u8; 32],
65 ring: &[CompressedEdwardsY],
66 key_image: &CompressedEdwardsY,
67 ) -> bool {
68if ring.len() != self.sigs.len() {
69return false;
70 }
7172let Some(key_image) = decompress_point(*key_image) else {
73return false;
74 };
7576let mut buf = Vec::with_capacity(32 + (2 * 32 * ring.len()));
77 buf.extend_from_slice(msg);
7879let mut sum = Scalar::ZERO;
80for (ring_member, sig) in ring.iter().zip(&self.sigs) {
81/*
82 The traditional Schnorr signature is:
83 r = sample()
84 c = H(r G || m)
85 s = r - c x
86 Verified as:
87 s G + c A == R
8889 Each ring member here performs a dual-Schnorr signature for:
90 s G + c A
91 s HtP(A) + c K
92 Where the transcript is pushed both these values, r G, r HtP(A) for the real spend.
93 This also serves as a DLEq proof between the key and the key image.
9495 Checking sum(c) == H(transcript) acts a disjunction, where any one of the `c`s can be
96 modified to cause the intended sum, if and only if a corresponding `s` value is known.
97 */
9899let Some(decomp_ring_member) = decompress_point(*ring_member) else {
100return false;
101 };
102103#[allow(non_snake_case)]
104let Li =
105 EdwardsPoint::vartime_double_scalar_mul_basepoint(&sig.c, &decomp_ring_member, &sig.s);
106 buf.extend_from_slice(Li.compress().as_bytes());
107#[allow(non_snake_case)]
108let Ri = (sig.s * hash_to_point(ring_member.to_bytes())) + (sig.c * key_image);
109 buf.extend_from_slice(Ri.compress().as_bytes());
110111 sum += sig.c;
112 }
113 sum == keccak256_to_scalar(buf)
114 }
115}