From e53e5e95a449e4bc95a1ce338a34fb0b92e3755d Mon Sep 17 00:00:00 2001 From: arnaucube Date: Thu, 20 Oct 2022 11:23:00 +0200 Subject: [PATCH] Impl blind sigs batch verifiction r1cs constraints --- README.md | 2 +- src/constraints.rs | 199 +++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 191 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index e357421..c4bbaf9 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # ark-ec-blind-signatures [![Test](https://github.com/aragonzkresearch/ark-ec-blind-signatures/workflows/Test/badge.svg)](https://github.com/aragonzkresearch/ark-ec-blind-signatures/actions?query=workflow%3ATest) [![Clippy](https://github.com/aragonzkresearch/ark-ec-blind-signatures/workflows/Clippy/badge.svg)](https://github.com/aragonzkresearch/ark-ec-blind-signatures/actions?query=workflow%3AClippy) -Blind signatures over elliptic curve implementation (native & r1cs constraints) using arkworks. +Blind signatures over elliptic curve implementation (native & r1cs constraints) using [arkworks](https://github.com/arkworks-rs). [Blind signature](https://en.wikipedia.org/wiki/Blind_signature) over elliptic curves, based on *"[New Blind Signature Schemes Based on the (Elliptic Curve) Discrete Logarithm Problem](https://sci-hub.st/10.1109/iccke.2013.6682844)"* paper by Hamid Mala & Nafiseh Nezhadansari. diff --git a/src/constraints.rs b/src/constraints.rs index 3abac5e..b52c6c1 100644 --- a/src/constraints.rs +++ b/src/constraints.rs @@ -168,11 +168,9 @@ where s: &SignatureVar, q: &PublicKeyVar, ) -> Result>, SynthesisError> { - let s_s = s.s.clone(); - let sG = parameters .generator - .scalar_mul_le(s_s.to_bits_le()?.iter())?; + .scalar_mul_le(s.s.to_bits_le()?.iter())?; // Note: in a circuit that aggregates multiple verifications, the hashing step could be // done outside the signature verification, once for all 1 votes and once for all 0 votes, @@ -190,7 +188,63 @@ where } } -// example of circuit using BlindSigVerifyGadget +pub struct BlindSigBatchVerifyGadget< + C: ProjectiveCurve, + GC: CurveVar>, + const NUM_SIGS: usize, +> where + for<'a> &'a GC: GroupOpsBounds<'a, C, GC>, +{ + _params: Parameters, // TODO review if needed, maybe delete + _gc: PhantomData, +} + +impl>, const NUM_SIGS: usize> + BlindSigBatchVerifyGadget +where + C: ProjectiveCurve, + GC: CurveVar>, + for<'a> &'a GC: GroupOpsBounds<'a, C, GC>, + ark_r1cs_std::groups::curves::twisted_edwards::AffineVar< + EdwardsParameters, + FpVar>, + >: From, + ::BaseField: PrimeField, + FpVar<::BaseField>: Mul>>, + FpVar<::BaseField>: From>>, +{ + fn batch_verify( + parameters: &ParametersVar, + poseidon_hash: &PoseidonGadget>, + m: FpVar>, + sigs: &[SignatureVar], + q: &PublicKeyVar, + ) -> Result>, SynthesisError> { + // Note: in a circuit that aggregates multiple verifications, the hashing step could be + // done outside the signature verification, once for all 1 votes and once for all 0 votes, + // saving lots of constraints + let hm = poseidon_hash.hash(&[m])?; + + #[allow(clippy::needless_range_loop)] + for i in 0..NUM_SIGS { + let sG = parameters + .generator + .scalar_mul_le(sigs[i].s.to_bits_le()?.iter())?; + + let r = EdwardsVar::from(sigs[i].r.clone()); // WIP + let rx_fpvar: FpVar> = r.x.into(); + + // G * s == R + Q * (R.x * H(m)) + let Q_rx_hm_0 = q.pub_key.scalar_mul_le(rx_fpvar.to_bits_le()?.iter())?; + let Q_rx_hm = Q_rx_hm_0.scalar_mul_le(hm.to_bits_le()?.iter())?; + let RHS = sigs[i].r.clone() + Q_rx_hm; + sG.enforce_equal(&RHS)?; + } + Ok(Boolean::TRUE) + } +} + +// example of circuit using BlindSigVerifyGadget to verify a single blind signature #[derive(Clone)] pub struct BlindSigVerifyCircuit>> where @@ -255,6 +309,82 @@ where } } +// example of circuit using BlindSigVerifyGadget to verify a batch of blind signatures +#[derive(Clone)] +pub struct BlindSigBatchVerifyCircuit< + C: ProjectiveCurve, + GC: CurveVar>, + const NUM_SIGS: usize, +> where + ::BaseField: PrimeField, +{ + _group: PhantomData<*const GC>, + pub params: Parameters, + pub poseidon_hash_native: poseidon_native::Poseidon>, + pub signatures: Option>>, + pub pub_key: Option>, + pub message: Option>, +} + +impl>, const NUM_SIGS: usize> + ConstraintSynthesizer> for BlindSigBatchVerifyCircuit +where + C: ProjectiveCurve, + GC: CurveVar>, + for<'a> &'a GC: GroupOpsBounds<'a, C, GC>, + ark_r1cs_std::groups::curves::twisted_edwards::AffineVar< + EdwardsParameters, + FpVar>, + >: From, + ::BaseField: PrimeField, + FpVar<::BaseField>: Mul>>, + FpVar<::BaseField>: From>>, +{ + #[tracing::instrument(target = "r1cs", skip(self, cs))] + fn generate_constraints( + self, + cs: ConstraintSystemRef>, + ) -> Result<(), SynthesisError> { + let parameters = + ParametersVar::new_constant(ark_relations::ns!(cs, "parameters"), &self.params)?; + + let pub_key = + PublicKeyVar::::new_input(ark_relations::ns!(cs, "public key"), || { + self.pub_key.ok_or(SynthesisError::AssignmentMissing) + })?; + let m = FpVar::>::new_input(ark_relations::ns!(cs, "message"), || { + self.message.ok_or(SynthesisError::AssignmentMissing) + })?; + + let mut signatures: Vec> = Vec::new(); + for i in 0..NUM_SIGS { + let signature = self.signatures.as_ref().and_then(|s| s.get(i)); + + let signature = + SignatureVar::::new_witness(ark_relations::ns!(cs, "signature"), || { + signature.ok_or(SynthesisError::AssignmentMissing) + })?; + signatures.push(signature); + } + + #[allow(clippy::redundant_clone)] + let poseidon_hash = PoseidonGadget::>::from_native( + &mut cs.clone(), + self.poseidon_hash_native, + ) + .unwrap(); + + let v = BlindSigBatchVerifyGadget::::batch_verify( + ¶meters, + &poseidon_hash, + m, + &signatures, + &pub_key, + )?; + v.enforce_equal(&Boolean::TRUE) + } +} + #[cfg(test)] mod test { use super::*; @@ -271,7 +401,7 @@ mod test { // type Fr = ::ScalarField; type S = BlindSigScheme; - fn generate_native_data( + fn generate_single_sig_native_data( poseidon_hash: &poseidon::Poseidon, ) -> ( Parameters, @@ -292,13 +422,39 @@ mod test { (params, pk, m, s) } + fn generate_batch_sig_native_data( + poseidon_hash: &poseidon::Poseidon, + n: usize, + ) -> ( + Parameters, + PublicKey, + Fq, + Vec>, + ) { + let mut rng = ark_std::test_rng(); + let params = S::setup(); + let (pk, sk) = S::keygen(¶ms, &mut rng); + let m = Fq::from(1234); + let mut signatures: Vec> = Vec::new(); + for _ in 0..n { + let (k, signer_r) = S::new_request_params(¶ms, &mut rng); + let (m_blinded, u) = S::blind(¶ms, &mut rng, &poseidon_hash, m, signer_r).unwrap(); + let s_blinded = S::blind_sign(sk, k, m_blinded); + let s = S::unblind(s_blinded, u); + let verified = S::verify(¶ms, &poseidon_hash, m, s.clone(), pk); + assert!(verified); + signatures.push(s); + } + (params, pk, m, signatures) + } + #[test] - fn test_verify() { + fn test_single_verify() { let poseidon_params = poseidon_setup_params::(Curve::Bn254, 5, 3); let poseidon_hash = poseidon::Poseidon::new(poseidon_params); // create signature using native-rust lib - let (params, pk, m, s) = generate_native_data(&poseidon_hash); + let (params, pk, m, s) = generate_single_sig_native_data(&poseidon_hash); // use the constraint system to verify the signature type SG = BlindSigVerifyGadget; @@ -327,12 +483,12 @@ mod test { } #[test] - fn test_constraint_system() { + fn test_single_verify_constraint_system() { let poseidon_params = poseidon_setup_params::(Curve::Bn254, 5, 3); let poseidon_hash = poseidon::Poseidon::new(poseidon_params); // create signature using native-rust lib - let (params, pk, m, s) = generate_native_data(&poseidon_hash); + let (params, pk, m, s) = generate_single_sig_native_data(&poseidon_hash); // use the constraint system to verify the signature let circuit = BlindSigVerifyCircuit:: { @@ -349,4 +505,29 @@ mod test { assert!(is_satisfied); println!("num_cnstraints={:?}", cs.num_constraints()); } + + #[test] + fn test_batch_verify_constraint_system() { + let poseidon_params = poseidon_setup_params::(Curve::Bn254, 5, 3); + let poseidon_hash = poseidon::Poseidon::new(poseidon_params); + + // create signatures using native-rust lib + const NUM_SIGS: usize = 5; + let (params, pk, m, sigs) = generate_batch_sig_native_data(&poseidon_hash, NUM_SIGS); + + // use the constraint system to verify the batch of signatures + let circuit = BlindSigBatchVerifyCircuit:: { + params, + poseidon_hash_native: poseidon_hash, + signatures: Some(sigs), + pub_key: Some(pk), + message: Some(m), + _group: PhantomData, + }; + let cs = ConstraintSystem::::new_ref(); + circuit.generate_constraints(cs.clone()).unwrap(); + let is_satisfied = cs.is_satisfied().unwrap(); + assert!(is_satisfied); + println!("num_cnstraints={:?}", cs.num_constraints()); + } }