use rand_core::{RngCore, CryptoRng}; use subtle::{ConstantTimeEq, ConditionallySelectable}; use transcript::Transcript; use group::{ff::{Field, PrimeFieldBits}, prime::PrimeGroup}; use multiexp::BatchVerifier; use crate::{ Generators, cross_group::{DLEqError, scalar::{scalar_convert, mutual_scalar_from_bytes}, bits::RingSignature} }; #[cfg(feature = "serialize")] use std::io::{Read, Write}; #[cfg(feature = "serialize")] use ff::PrimeField; #[cfg(feature = "serialize")] use crate::{read_scalar, cross_group::read_point}; #[allow(non_snake_case)] fn nonces< T: Transcript, G0: PrimeGroup, G1: PrimeGroup >(mut transcript: T, nonces: (G0, G1)) -> (G0::Scalar, G1::Scalar) where G0::Scalar: PrimeFieldBits, G1::Scalar: PrimeFieldBits { transcript.append_message(b"nonce_0", nonces.0.to_bytes().as_ref()); transcript.append_message(b"nonce_1", nonces.1.to_bytes().as_ref()); mutual_scalar_from_bytes(transcript.challenge(b"challenge").as_ref()) } #[allow(non_snake_case)] fn calculate_R( generators: (Generators, Generators), s: (G0::Scalar, G1::Scalar), A: (G0, G1), e: (G0::Scalar, G1::Scalar) ) -> (G0, G1) { (((generators.0.alt * s.0) - (A.0 * e.0)), ((generators.1.alt * s.1) - (A.1 * e.1))) } #[allow(non_snake_case)] fn R_nonces( transcript: T, generators: (Generators, Generators), s: (G0::Scalar, G1::Scalar), A: (G0, G1), e: (G0::Scalar, G1::Scalar) ) -> (G0::Scalar, G1::Scalar) where G0::Scalar: PrimeFieldBits, G1::Scalar: PrimeFieldBits { nonces(transcript, calculate_R(generators, s, A, e)) } #[allow(non_snake_case)] #[derive(Clone, PartialEq, Eq, Debug)] pub struct ClassicAos { // Merged challenges have a slight security reduction, yet one already applied to the scalar // being proven for, and this saves ~8kb. Alternatively, challenges could be redefined as a seed, // present here, which is then hashed for each of the two challenges, remaining unbiased/unique // while maintaining the bandwidth savings, yet also while adding 252 hashes for // Secp256k1/Ed25519 e_0: G0::Scalar, s: [(G0::Scalar, G1::Scalar); RING_LEN] } impl< G0: PrimeGroup, G1: PrimeGroup, const RING_LEN: usize > RingSignature for ClassicAos where G0::Scalar: PrimeFieldBits, G1::Scalar: PrimeFieldBits { type Context = (); const LEN: usize = RING_LEN; fn prove( rng: &mut R, transcript: T, generators: (Generators, Generators), ring: &[(G0, G1)], actual: usize, blinding_key: (G0::Scalar, G1::Scalar) ) -> Self { // While it is possible to use larger values, it's not efficient to do so // 2 + 2 == 2^2, yet 2 + 2 + 2 < 2^3 debug_assert!((RING_LEN == 2) || (RING_LEN == 4)); let mut e_0 = G0::Scalar::zero(); let mut s = [(G0::Scalar::zero(), G1::Scalar::zero()); RING_LEN]; let r = (G0::Scalar::random(&mut *rng), G1::Scalar::random(&mut *rng)); #[allow(non_snake_case)] let original_R = (generators.0.alt * r.0, generators.1.alt * r.1); #[allow(non_snake_case)] let mut R = original_R; for i in ((actual + 1) .. (actual + RING_LEN + 1)).map(|i| i % RING_LEN) { let e = nonces(transcript.clone(), R); e_0 = G0::Scalar::conditional_select(&e_0, &e.0, usize::ct_eq(&i, &0)); // Solve for the real index if i == actual { s[i] = (r.0 + (e.0 * blinding_key.0), r.1 + (e.1 * blinding_key.1)); debug_assert_eq!(calculate_R(generators, s[i], ring[actual], e), original_R); break; // Generate a decoy response } else { s[i] = (G0::Scalar::random(&mut *rng), G1::Scalar::random(&mut *rng)); } R = calculate_R(generators, s[i], ring[i], e); } ClassicAos { e_0, s } } fn verify( &self, _rng: &mut R, transcript: T, generators: (Generators, Generators), _: &mut Self::Context, ring: &[(G0, G1)] ) -> Result<(), DLEqError> { debug_assert!((RING_LEN == 2) || (RING_LEN == 4)); let e_0 = (self.e_0, scalar_convert(self.e_0).ok_or(DLEqError::InvalidChallenge)?); let mut e = None; for i in 0 .. RING_LEN { e = Some(R_nonces(transcript.clone(), generators, self.s[i], ring[i], e.unwrap_or(e_0))); } // Will panic if the above loop is never run somehow // If e wasn't an Option, and instead initially set to e_0, it'd always pass if e_0 != e.unwrap() { Err(DLEqError::InvalidProof)?; } Ok(()) } #[cfg(feature = "serialize")] fn serialize(&self, w: &mut W) -> std::io::Result<()> { w.write_all(self.e_0.to_repr().as_ref())?; for i in 0 .. Self::LEN { w.write_all(self.s[i].0.to_repr().as_ref())?; w.write_all(self.s[i].1.to_repr().as_ref())?; } Ok(()) } #[cfg(feature = "serialize")] fn deserialize(r: &mut R) -> std::io::Result { let e_0 = read_scalar(r)?; let mut s = [(G0::Scalar::zero(), G1::Scalar::zero()); RING_LEN]; for i in 0 .. Self::LEN { s[i] = (read_scalar(r)?, read_scalar(r)?); } Ok(ClassicAos { e_0, s }) } } #[allow(non_snake_case)] #[derive(Clone, PartialEq, Eq, Debug)] pub struct MultiexpAos { R_0: (G0, G1), s: [(G0::Scalar, G1::Scalar); 2] } impl MultiexpAos { #[allow(non_snake_case)] fn R_batch( generators: (Generators, Generators), s: (G0::Scalar, G1::Scalar), A: (G0, G1), e: (G0::Scalar, G1::Scalar) ) -> (Vec<(G0::Scalar, G0)>, Vec<(G1::Scalar, G1)>) { (vec![(s.0, generators.0.alt), (-e.0, A.0)], vec![(s.1, generators.1.alt), (-e.1, A.1)]) } } impl RingSignature for MultiexpAos where G0::Scalar: PrimeFieldBits, G1::Scalar: PrimeFieldBits { type Context = (BatchVerifier<(), G0>, BatchVerifier<(), G1>); const LEN: usize = 2; fn prove( rng: &mut R, transcript: T, generators: (Generators, Generators), ring: &[(G0, G1)], actual: usize, blinding_key: (G0::Scalar, G1::Scalar) ) -> Self { #[allow(non_snake_case)] let mut R_0 = (G0::identity(), G1::identity()); let mut s = [(G0::Scalar::zero(), G1::Scalar::zero()); 2]; // Can't use Self::LEN due to 76200 let r = (G0::Scalar::random(&mut *rng), G1::Scalar::random(&mut *rng)); #[allow(non_snake_case)] let original_R = (generators.0.alt * r.0, generators.1.alt * r.1); #[allow(non_snake_case)] let mut R = original_R; for i in ((actual + 1) .. (actual + Self::LEN + 1)).map(|i| i % Self::LEN) { if i == 0 { R_0.0 = R.0; R_0.1 = R.1; } // Solve for the real index let e = nonces(transcript.clone(), R); if i == actual { s[i] = (r.0 + (e.0 * blinding_key.0), r.1 + (e.1 * blinding_key.1)); debug_assert_eq!(calculate_R(generators, s[i], ring[actual], e), original_R); break; // Generate a decoy response } else { s[i] = (G0::Scalar::random(&mut *rng), G1::Scalar::random(&mut *rng)); } R = calculate_R(generators, s[i], ring[i], e); } MultiexpAos { R_0, s } } fn verify( &self, rng: &mut R, transcript: T, generators: (Generators, Generators), batch: &mut Self::Context, ring: &[(G0, G1)] ) -> Result<(), DLEqError> { let mut e = nonces(transcript.clone(), self.R_0); for i in 0 .. (Self::LEN - 1) { e = R_nonces(transcript.clone(), generators, self.s[i], ring[i], e); } let mut statements = Self::R_batch( generators, *self.s.last().unwrap(), *ring.last().unwrap(), e ); statements.0.push((-G0::Scalar::one(), self.R_0.0)); statements.1.push((-G1::Scalar::one(), self.R_0.1)); batch.0.queue(&mut *rng, (), statements.0); batch.1.queue(&mut *rng, (), statements.1); Ok(()) } #[cfg(feature = "serialize")] fn serialize(&self, w: &mut W) -> std::io::Result<()> { w.write_all(self.R_0.0.to_bytes().as_ref())?; w.write_all(self.R_0.1.to_bytes().as_ref())?; for i in 0 .. Self::LEN { w.write_all(self.s[i].0.to_repr().as_ref())?; w.write_all(self.s[i].1.to_repr().as_ref())?; } Ok(()) } #[cfg(feature = "serialize")] fn deserialize(r: &mut R) -> std::io::Result { #[allow(non_snake_case)] let R_0 = (read_point(r)?, read_point(r)?); let mut s = [(G0::Scalar::zero(), G1::Scalar::zero()); 2]; for i in 0 .. Self::LEN { s[i] = (read_scalar(r)?, read_scalar(r)?); } Ok(MultiexpAos { R_0, s }) } }