use rand_core::{CryptoRng, RngCore};
use zeroize::Zeroize;
use crate::challenge::{hash_chain, normalize_challenge, sample_challenge, validate_challenge};
use crate::error::RuneError;
use crate::keys::{PublicKey, SecretKey};
use crate::math::{
poly_add, poly_infinity_norm, poly_is_well_formed, poly_mul_schoolbook, poly_sub,
sample_bounded_poly, zero_poly, Poly, ZeroizingPoly,
};
use crate::params::Params;
#[derive(Clone, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct RingSignature {
pub z_s: Vec<Poly>,
pub z_e: Vec<Poly>,
pub c: Vec<Poly>,
}
#[allow(clippy::many_single_char_names, clippy::similar_names)]
pub fn ring_sign(
message: &[u8],
signer_index: usize,
sk: &SecretKey,
ring: &[PublicKey],
params: &Params,
rng: &mut (impl CryptoRng + RngCore),
) -> Result<RingSignature, RuneError> {
let k = ring.len();
validate_ring(ring, params)?;
if signer_index >= k {
return Err(RuneError::SignerIndexOutOfRange);
}
if sk.t() != &ring[signer_index].t {
return Err(RuneError::MalformedPublicKey);
}
let a = &ring[0].a;
let response_bound = params.response_bound();
for _ in 0..params.max_attempts() {
let y_s = ZeroizingPoly(sample_bounded_poly(params.gamma(), params, rng));
let y_e = ZeroizingPoly(sample_bounded_poly(params.gamma(), params, rng));
let mut z_s = vec![zero_poly(params); k];
let mut z_e = vec![zero_poly(params); k];
let mut c = vec![zero_poly(params); k];
let mut w = vec![zero_poly(params); k];
w[signer_index] = poly_add(&poly_mul_schoolbook(a, &y_s.0, params), &y_e.0, params);
for j in 1..k {
let i = (signer_index + j) % k;
let prev = (i + k - 1) % k;
let seed = hash_chain(message, ring, i, &w[prev], params);
c[i] = sample_challenge(&seed, params);
z_s[i] = sample_bounded_poly(response_bound - 1, params, rng);
z_e[i] = sample_bounded_poly(response_bound - 1, params, rng);
let az = poly_mul_schoolbook(a, &z_s[i], params);
let az_plus_e = poly_add(&az, &z_e[i], params);
let ct = poly_mul_schoolbook(&c[i], &ring[i].t, params);
w[i] = poly_sub(&az_plus_e, &ct, params);
}
let prev = (signer_index + k - 1) % k;
let seed = hash_chain(message, ring, signer_index, &w[prev], params);
c[signer_index] = sample_challenge(&seed, params);
let cs = ZeroizingPoly(poly_mul_schoolbook(&c[signer_index], sk.s(), params));
let ce = ZeroizingPoly(poly_mul_schoolbook(&c[signer_index], sk.e(), params));
z_s[signer_index] = poly_add(&y_s.0, &cs.0, params);
z_e[signer_index] = poly_add(&y_e.0, &ce.0, params);
if poly_infinity_norm(&z_s[signer_index]) >= response_bound
|| poly_infinity_norm(&z_e[signer_index]) >= response_bound
{
z_s[signer_index].zeroize();
z_e[signer_index].zeroize();
continue;
}
return Ok(RingSignature { z_s, z_e, c });
}
Err(RuneError::RejectionSamplingFailed)
}
pub fn ring_verify(
message: &[u8],
sig: &RingSignature,
ring: &[PublicKey],
params: &Params,
) -> Result<bool, RuneError> {
let k = ring.len();
validate_ring(ring, params)?;
if sig.z_s.len() != k || sig.z_e.len() != k || sig.c.len() != k {
return Err(RuneError::MalformedSignature);
}
let response_bound = params.response_bound();
for i in 0..k {
if !poly_is_well_formed(&sig.z_s[i], params)
|| !poly_is_well_formed(&sig.z_e[i], params)
|| !poly_is_well_formed(&sig.c[i], params)
{
return Err(RuneError::MalformedSignature);
}
if !validate_challenge(&sig.c[i], params) {
return Ok(false);
}
if normalize_challenge(&sig.c[i], params) != sig.c[i] {
return Ok(false);
}
if poly_infinity_norm(&sig.z_s[i]) >= response_bound
|| poly_infinity_norm(&sig.z_e[i]) >= response_bound
{
return Ok(false);
}
}
let a = &ring[0].a;
let mut commitments = vec![zero_poly(params); k];
for (i, pk) in ring.iter().enumerate() {
let az = poly_mul_schoolbook(a, &sig.z_s[i], params);
let az_plus_e = poly_add(&az, &sig.z_e[i], params);
let ct = poly_mul_schoolbook(&sig.c[i], &pk.t, params);
commitments[i] = poly_sub(&az_plus_e, &ct, params);
}
for i in 0..k {
let prev = (i + k - 1) % k;
let seed = hash_chain(message, ring, i, &commitments[prev], params);
let expected = sample_challenge(&seed, params);
if !validate_challenge(&expected, params) {
return Err(RuneError::MalformedChallenge);
}
if expected != sig.c[i] {
return Ok(false);
}
}
Ok(true)
}
fn validate_ring(ring: &[PublicKey], params: &Params) -> Result<(), RuneError> {
if ring.len() < 2 {
return Err(RuneError::RingTooSmall);
}
let a = &ring[0].a;
if !poly_is_well_formed(a, params) {
return Err(RuneError::MalformedPublicKey);
}
for pk in ring {
if pk.a != *a {
return Err(RuneError::InconsistentRingParameter);
}
if !poly_is_well_formed(&pk.a, params) || !poly_is_well_formed(&pk.t, params) {
return Err(RuneError::MalformedPublicKey);
}
}
Ok(())
}