Browse Source

Impl blind sigs batch verifiction r1cs constraints

main
arnaucube 2 years ago
parent
commit
e53e5e95a4
2 changed files with 191 additions and 10 deletions
  1. +1
    -1
      README.md
  2. +190
    -9
      src/constraints.rs

+ 1
- 1
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.

+ 190
- 9
src/constraints.rs

@ -168,11 +168,9 @@ where
s: &SignatureVar<C, GC>,
q: &PublicKeyVar<C, GC>,
) -> Result<Boolean<ConstraintF<C>>, 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<C, ConstraintF<C>>,
const NUM_SIGS: usize,
> where
for<'a> &'a GC: GroupOpsBounds<'a, C, GC>,
{
_params: Parameters<C>, // TODO review if needed, maybe delete
_gc: PhantomData<GC>,
}
impl<C: ProjectiveCurve, GC: CurveVar<C, ConstraintF<C>>, const NUM_SIGS: usize>
BlindSigBatchVerifyGadget<C, GC, NUM_SIGS>
where
C: ProjectiveCurve,
GC: CurveVar<C, ConstraintF<C>>,
for<'a> &'a GC: GroupOpsBounds<'a, C, GC>,
ark_r1cs_std::groups::curves::twisted_edwards::AffineVar<
EdwardsParameters,
FpVar<Fp256<FqParameters>>,
>: From<GC>,
<C as ProjectiveCurve>::BaseField: PrimeField,
FpVar<<C as ProjectiveCurve>::BaseField>: Mul<FpVar<Fp256<FqParameters>>>,
FpVar<<C as ProjectiveCurve>::BaseField>: From<FpVar<Fp256<FqParameters>>>,
{
fn batch_verify(
parameters: &ParametersVar<C, GC>,
poseidon_hash: &PoseidonGadget<ConstraintF<C>>,
m: FpVar<ConstraintF<C>>,
sigs: &[SignatureVar<C, GC>],
q: &PublicKeyVar<C, GC>,
) -> Result<Boolean<ConstraintF<C>>, 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<ConstraintF<C>> = 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<C: ProjectiveCurve, GC: CurveVar<C, ConstraintF<C>>>
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<C, ConstraintF<C>>,
const NUM_SIGS: usize,
> where
<C as ProjectiveCurve>::BaseField: PrimeField,
{
_group: PhantomData<*const GC>,
pub params: Parameters<C>,
pub poseidon_hash_native: poseidon_native::Poseidon<ConstraintF<C>>,
pub signatures: Option<Vec<Signature<C>>>,
pub pub_key: Option<PublicKey<C>>,
pub message: Option<ConstraintF<C>>,
}
impl<C: ProjectiveCurve, GC: CurveVar<C, ConstraintF<C>>, const NUM_SIGS: usize>
ConstraintSynthesizer<ConstraintF<C>> for BlindSigBatchVerifyCircuit<C, GC, NUM_SIGS>
where
C: ProjectiveCurve,
GC: CurveVar<C, ConstraintF<C>>,
for<'a> &'a GC: GroupOpsBounds<'a, C, GC>,
ark_r1cs_std::groups::curves::twisted_edwards::AffineVar<
EdwardsParameters,
FpVar<Fp256<FqParameters>>,
>: From<GC>,
<C as ProjectiveCurve>::BaseField: PrimeField,
FpVar<<C as ProjectiveCurve>::BaseField>: Mul<FpVar<Fp256<FqParameters>>>,
FpVar<<C as ProjectiveCurve>::BaseField>: From<FpVar<Fp256<FqParameters>>>,
{
#[tracing::instrument(target = "r1cs", skip(self, cs))]
fn generate_constraints(
self,
cs: ConstraintSystemRef<ConstraintF<C>>,
) -> Result<(), SynthesisError> {
let parameters =
ParametersVar::new_constant(ark_relations::ns!(cs, "parameters"), &self.params)?;
let pub_key =
PublicKeyVar::<C, GC>::new_input(ark_relations::ns!(cs, "public key"), || {
self.pub_key.ok_or(SynthesisError::AssignmentMissing)
})?;
let m = FpVar::<ConstraintF<C>>::new_input(ark_relations::ns!(cs, "message"), || {
self.message.ok_or(SynthesisError::AssignmentMissing)
})?;
let mut signatures: Vec<SignatureVar<C, GC>> = Vec::new();
for i in 0..NUM_SIGS {
let signature = self.signatures.as_ref().and_then(|s| s.get(i));
let signature =
SignatureVar::<C, GC>::new_witness(ark_relations::ns!(cs, "signature"), || {
signature.ok_or(SynthesisError::AssignmentMissing)
})?;
signatures.push(signature);
}
#[allow(clippy::redundant_clone)]
let poseidon_hash = PoseidonGadget::<ConstraintF<C>>::from_native(
&mut cs.clone(),
self.poseidon_hash_native,
)
.unwrap();
let v = BlindSigBatchVerifyGadget::<C, GC, NUM_SIGS>::batch_verify(
&parameters,
&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 = <BabyJubJub as ProjectiveCurve>::ScalarField;
type S = BlindSigScheme<BabyJubJub>;
fn generate_native_data(
fn generate_single_sig_native_data(
poseidon_hash: &poseidon::Poseidon<Fq>,
) -> (
Parameters<BabyJubJub>,
@ -292,13 +422,39 @@ mod test {
(params, pk, m, s)
}
fn generate_batch_sig_native_data(
poseidon_hash: &poseidon::Poseidon<Fq>,
n: usize,
) -> (
Parameters<BabyJubJub>,
PublicKey<BabyJubJub>,
Fq,
Vec<Signature<BabyJubJub>>,
) {
let mut rng = ark_std::test_rng();
let params = S::setup();
let (pk, sk) = S::keygen(&params, &mut rng);
let m = Fq::from(1234);
let mut signatures: Vec<Signature<BabyJubJub>> = Vec::new();
for _ in 0..n {
let (k, signer_r) = S::new_request_params(&params, &mut rng);
let (m_blinded, u) = S::blind(&params, &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(&params, &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::<Fq>(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<BabyJubJub, BabyJubJubVar>;
@ -327,12 +483,12 @@ mod test {
}
#[test]
fn test_constraint_system() {
fn test_single_verify_constraint_system() {
let poseidon_params = poseidon_setup_params::<Fq>(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::<BabyJubJub, BabyJubJubVar> {
@ -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::<Fq>(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::<BabyJubJub, BabyJubJubVar, NUM_SIGS> {
params,
poseidon_hash_native: poseidon_hash,
signatures: Some(sigs),
pub_key: Some(pk),
message: Some(m),
_group: PhantomData,
};
let cs = ConstraintSystem::<Fq>::new_ref();
circuit.generate_constraints(cs.clone()).unwrap();
let is_satisfied = cs.is_satisfied().unwrap();
assert!(is_satisfied);
println!("num_cnstraints={:?}", cs.num_constraints());
}
}

Loading…
Cancel
Save