mirror of
https://github.com/arnaucube/ark-ec-blind-signatures.git
synced 2026-01-12 00:21:29 +01:00
Impl blind sigs batch verifiction r1cs constraints
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
# ark-ec-blind-signatures [](https://github.com/aragonzkresearch/ark-ec-blind-signatures/actions?query=workflow%3ATest) [](https://github.com/aragonzkresearch/ark-ec-blind-signatures/actions?query=workflow%3AClippy)
|
# ark-ec-blind-signatures [](https://github.com/aragonzkresearch/ark-ec-blind-signatures/actions?query=workflow%3ATest) [](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.
|
[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.
|
||||||
|
|
||||||
|
|||||||
@@ -168,11 +168,9 @@ where
|
|||||||
s: &SignatureVar<C, GC>,
|
s: &SignatureVar<C, GC>,
|
||||||
q: &PublicKeyVar<C, GC>,
|
q: &PublicKeyVar<C, GC>,
|
||||||
) -> Result<Boolean<ConstraintF<C>>, SynthesisError> {
|
) -> Result<Boolean<ConstraintF<C>>, SynthesisError> {
|
||||||
let s_s = s.s.clone();
|
|
||||||
|
|
||||||
let sG = parameters
|
let sG = parameters
|
||||||
.generator
|
.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
|
// 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,
|
// 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)]
|
#[derive(Clone)]
|
||||||
pub struct BlindSigVerifyCircuit<C: ProjectiveCurve, GC: CurveVar<C, ConstraintF<C>>>
|
pub struct BlindSigVerifyCircuit<C: ProjectiveCurve, GC: CurveVar<C, ConstraintF<C>>>
|
||||||
where
|
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(
|
||||||
|
¶meters,
|
||||||
|
&poseidon_hash,
|
||||||
|
m,
|
||||||
|
&signatures,
|
||||||
|
&pub_key,
|
||||||
|
)?;
|
||||||
|
v.enforce_equal(&Boolean::TRUE)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use super::*;
|
use super::*;
|
||||||
@@ -271,7 +401,7 @@ mod test {
|
|||||||
// type Fr = <BabyJubJub as ProjectiveCurve>::ScalarField;
|
// type Fr = <BabyJubJub as ProjectiveCurve>::ScalarField;
|
||||||
type S = BlindSigScheme<BabyJubJub>;
|
type S = BlindSigScheme<BabyJubJub>;
|
||||||
|
|
||||||
fn generate_native_data(
|
fn generate_single_sig_native_data(
|
||||||
poseidon_hash: &poseidon::Poseidon<Fq>,
|
poseidon_hash: &poseidon::Poseidon<Fq>,
|
||||||
) -> (
|
) -> (
|
||||||
Parameters<BabyJubJub>,
|
Parameters<BabyJubJub>,
|
||||||
@@ -292,13 +422,39 @@ mod test {
|
|||||||
(params, pk, m, s)
|
(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(¶ms, &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(¶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]
|
#[test]
|
||||||
fn test_verify() {
|
fn test_single_verify() {
|
||||||
let poseidon_params = poseidon_setup_params::<Fq>(Curve::Bn254, 5, 3);
|
let poseidon_params = poseidon_setup_params::<Fq>(Curve::Bn254, 5, 3);
|
||||||
let poseidon_hash = poseidon::Poseidon::new(poseidon_params);
|
let poseidon_hash = poseidon::Poseidon::new(poseidon_params);
|
||||||
|
|
||||||
// create signature using native-rust lib
|
// 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
|
// use the constraint system to verify the signature
|
||||||
type SG = BlindSigVerifyGadget<BabyJubJub, BabyJubJubVar>;
|
type SG = BlindSigVerifyGadget<BabyJubJub, BabyJubJubVar>;
|
||||||
@@ -327,12 +483,12 @@ mod test {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[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_params = poseidon_setup_params::<Fq>(Curve::Bn254, 5, 3);
|
||||||
let poseidon_hash = poseidon::Poseidon::new(poseidon_params);
|
let poseidon_hash = poseidon::Poseidon::new(poseidon_params);
|
||||||
|
|
||||||
// create signature using native-rust lib
|
// 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
|
// use the constraint system to verify the signature
|
||||||
let circuit = BlindSigVerifyCircuit::<BabyJubJub, BabyJubJubVar> {
|
let circuit = BlindSigVerifyCircuit::<BabyJubJub, BabyJubJubVar> {
|
||||||
@@ -349,4 +505,29 @@ mod test {
|
|||||||
assert!(is_satisfied);
|
assert!(is_satisfied);
|
||||||
println!("num_cnstraints={:?}", cs.num_constraints());
|
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());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user