1use rand_core::{CryptoRng, RngCore};
4use zeroize::Zeroize;
5
6use crate::challenge::{hash_chain, normalize_challenge, sample_challenge, validate_challenge};
7use crate::error::RuneError;
8use crate::keys::{PublicKey, SecretKey};
9use crate::math::{
10 poly_add, poly_infinity_norm, poly_is_well_formed, poly_mul_schoolbook, poly_sub,
11 sample_bounded_poly, zero_poly, Poly, ZeroizingPoly,
12};
13use crate::params::Params;
14
15#[derive(Clone, Debug, PartialEq, Eq)]
21#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
22pub struct RingSignature {
23 pub z_s: Vec<Poly>,
25 pub z_e: Vec<Poly>,
27 pub c: Vec<Poly>,
29}
30
31#[allow(clippy::many_single_char_names, clippy::similar_names)]
39pub fn ring_sign(
40 message: &[u8],
41 signer_index: usize,
42 sk: &SecretKey,
43 ring: &[PublicKey],
44 params: &Params,
45 rng: &mut (impl CryptoRng + RngCore),
46) -> Result<RingSignature, RuneError> {
47 let k = ring.len();
48 validate_ring(ring, params)?;
49
50 if signer_index >= k {
51 return Err(RuneError::SignerIndexOutOfRange);
52 }
53 if sk.t() != &ring[signer_index].t {
54 return Err(RuneError::MalformedPublicKey);
55 }
56
57 let a = &ring[0].a;
58 let response_bound = params.response_bound();
59
60 for _ in 0..params.max_attempts() {
62 let y_s = ZeroizingPoly(sample_bounded_poly(params.gamma(), params, rng));
63 let y_e = ZeroizingPoly(sample_bounded_poly(params.gamma(), params, rng));
64
65 let mut z_s = vec![zero_poly(params); k];
66 let mut z_e = vec![zero_poly(params); k];
67 let mut c = vec![zero_poly(params); k];
68 let mut w = vec![zero_poly(params); k];
69
70 w[signer_index] = poly_add(&poly_mul_schoolbook(a, &y_s.0, params), &y_e.0, params);
71
72 for j in 1..k {
75 let i = (signer_index + j) % k;
76 let prev = (i + k - 1) % k;
77 let seed = hash_chain(message, ring, i, &w[prev], params);
78 c[i] = sample_challenge(&seed, params);
79 z_s[i] = sample_bounded_poly(response_bound - 1, params, rng);
80 z_e[i] = sample_bounded_poly(response_bound - 1, params, rng);
81
82 let az = poly_mul_schoolbook(a, &z_s[i], params);
83 let az_plus_e = poly_add(&az, &z_e[i], params);
84 let ct = poly_mul_schoolbook(&c[i], &ring[i].t, params);
85 w[i] = poly_sub(&az_plus_e, &ct, params);
86 }
87
88 let prev = (signer_index + k - 1) % k;
89 let seed = hash_chain(message, ring, signer_index, &w[prev], params);
90 c[signer_index] = sample_challenge(&seed, params);
91
92 let cs = ZeroizingPoly(poly_mul_schoolbook(&c[signer_index], sk.s(), params));
93 let ce = ZeroizingPoly(poly_mul_schoolbook(&c[signer_index], sk.e(), params));
94 z_s[signer_index] = poly_add(&y_s.0, &cs.0, params);
95 z_e[signer_index] = poly_add(&y_e.0, &ce.0, params);
96
97 if poly_infinity_norm(&z_s[signer_index]) >= response_bound
98 || poly_infinity_norm(&z_e[signer_index]) >= response_bound
99 {
100 z_s[signer_index].zeroize();
101 z_e[signer_index].zeroize();
102 continue;
103 }
104
105 return Ok(RingSignature { z_s, z_e, c });
107 }
108
109 Err(RuneError::RejectionSamplingFailed)
110}
111
112pub fn ring_verify(
119 message: &[u8],
120 sig: &RingSignature,
121 ring: &[PublicKey],
122 params: &Params,
123) -> Result<bool, RuneError> {
124 let k = ring.len();
125 validate_ring(ring, params)?;
126
127 if sig.z_s.len() != k || sig.z_e.len() != k || sig.c.len() != k {
128 return Err(RuneError::MalformedSignature);
129 }
130
131 let response_bound = params.response_bound();
132 for i in 0..k {
133 if !poly_is_well_formed(&sig.z_s[i], params)
134 || !poly_is_well_formed(&sig.z_e[i], params)
135 || !poly_is_well_formed(&sig.c[i], params)
136 {
137 return Err(RuneError::MalformedSignature);
138 }
139 if !validate_challenge(&sig.c[i], params) {
140 return Ok(false);
141 }
142 if normalize_challenge(&sig.c[i], params) != sig.c[i] {
143 return Ok(false);
144 }
145 if poly_infinity_norm(&sig.z_s[i]) >= response_bound
146 || poly_infinity_norm(&sig.z_e[i]) >= response_bound
147 {
148 return Ok(false);
149 }
150 }
151
152 let a = &ring[0].a;
153 let mut commitments = vec![zero_poly(params); k];
154 for (i, pk) in ring.iter().enumerate() {
155 let az = poly_mul_schoolbook(a, &sig.z_s[i], params);
156 let az_plus_e = poly_add(&az, &sig.z_e[i], params);
157 let ct = poly_mul_schoolbook(&sig.c[i], &pk.t, params);
158 commitments[i] = poly_sub(&az_plus_e, &ct, params);
159 }
160
161 for i in 0..k {
162 let prev = (i + k - 1) % k;
163 let seed = hash_chain(message, ring, i, &commitments[prev], params);
164 let expected = sample_challenge(&seed, params);
165 if !validate_challenge(&expected, params) {
166 return Err(RuneError::MalformedChallenge);
167 }
168 if expected != sig.c[i] {
169 return Ok(false);
170 }
171 }
172
173 Ok(true)
174}
175
176fn validate_ring(ring: &[PublicKey], params: &Params) -> Result<(), RuneError> {
177 if ring.len() < 2 {
178 return Err(RuneError::RingTooSmall);
179 }
180
181 let a = &ring[0].a;
182 if !poly_is_well_formed(a, params) {
183 return Err(RuneError::MalformedPublicKey);
184 }
185
186 for pk in ring {
187 if pk.a != *a {
188 return Err(RuneError::InconsistentRingParameter);
189 }
190 if !poly_is_well_formed(&pk.a, params) || !poly_is_well_formed(&pk.t, params) {
191 return Err(RuneError::MalformedPublicKey);
192 }
193 }
194
195 Ok(())
196}