arnaucube
arnaucube/ark-ec-blind-signaturesPublic Self-hosted repository
ark-ec-blind-signatures/src/constraints.rs
src/constraints.rsRaw
  1use crate::{Parameters, PublicKey, Signature};
  2
  3use ark_ec::{AffineCurve, ProjectiveCurve};
  4use ark_ed_on_bn254::{constraints::EdwardsVar, EdwardsParameters, FqParameters};
  5use ark_ff::{
  6    fields::{Field, Fp256},
  7    to_bytes, PrimeField, ToConstraintField,
  8};
  9use ark_r1cs_std::{
 10    alloc::{AllocVar, AllocationMode},
 11    bits::uint8::UInt8,
 12    boolean::Boolean,
 13    eq::EqGadget,
 14    fields::{fp::FpVar, FieldVar},
 15    groups::{curves::twisted_edwards::AffineVar, GroupOpsBounds},
 16    prelude::CurveVar,
 17    ToBitsGadget,
 18};
 19use ark_relations::r1cs::{ConstraintSynthesizer, ConstraintSystemRef, Namespace, SynthesisError};
 20use ark_std::ops::{Add, Mul};
 21
 22use core::{borrow::Borrow, marker::PhantomData};
 23use derivative::Derivative;
 24
 25// hash
 26use arkworks_native_gadgets::poseidon as poseidon_native;
 27use arkworks_r1cs_gadgets::poseidon;
 28use arkworks_r1cs_gadgets::poseidon::{FieldHasherGadget, PoseidonGadget, PoseidonParametersVar};
 29
 30use crate::ConstraintF;
 31
 32#[derive(Derivative)]
 33#[derivative(
 34    Debug(bound = "C: ProjectiveCurve, GC: CurveVar<C, ConstraintF<C>>"),
 35    Clone(bound = "C: ProjectiveCurve, GC: CurveVar<C, ConstraintF<C>>")
 36)]
 37pub struct PublicKeyVar<C: ProjectiveCurve, GC: CurveVar<C, ConstraintF<C>>>
 38where
 39    for<'a> &'a GC: GroupOpsBounds<'a, C, GC>,
 40{
 41    pub_key: GC,
 42    #[doc(hidden)]
 43    _group: PhantomData<*const C>,
 44}
 45
 46impl<C, GC> AllocVar<PublicKey<C>, ConstraintF<C>> for PublicKeyVar<C, GC>
 47where
 48    C: ProjectiveCurve,
 49    GC: CurveVar<C, ConstraintF<C>>,
 50    for<'a> &'a GC: GroupOpsBounds<'a, C, GC>,
 51{
 52    fn new_variable<T: Borrow<PublicKey<C>>>(
 53        cs: impl Into<Namespace<ConstraintF<C>>>,
 54        f: impl FnOnce() -> Result<T, SynthesisError>,
 55        mode: AllocationMode,
 56    ) -> Result<Self, SynthesisError> {
 57        let pub_key = GC::new_variable(cs, f, mode)?;
 58        Ok(Self {
 59            pub_key,
 60            _group: PhantomData,
 61        })
 62    }
 63}
 64
 65#[derive(Derivative)]
 66#[derivative(
 67    Debug(bound = "C: ProjectiveCurve, GC: CurveVar<C, ConstraintF<C>>"),
 68    Clone(bound = "C: ProjectiveCurve, GC: CurveVar<C, ConstraintF<C>>")
 69)]
 70pub struct SignatureVar<C: ProjectiveCurve, GC: CurveVar<C, ConstraintF<C>>>
 71where
 72    for<'a> &'a GC: GroupOpsBounds<'a, C, GC>,
 73{
 74    s: Vec<UInt8<ConstraintF<C>>>,
 75    r: GC,
 76    _curve: PhantomData<C>,
 77}
 78
 79impl<C, GC> AllocVar<Signature<C>, ConstraintF<C>> for SignatureVar<C, GC>
 80where
 81    C: ProjectiveCurve,
 82    GC: CurveVar<C, ConstraintF<C>>,
 83    for<'a> &'a GC: GroupOpsBounds<'a, C, GC>,
 84{
 85    fn new_variable<T: Borrow<Signature<C>>>(
 86        cs: impl Into<Namespace<ConstraintF<C>>>,
 87        f: impl FnOnce() -> Result<T, SynthesisError>,
 88        mode: AllocationMode,
 89    ) -> Result<Self, SynthesisError> {
 90        f().and_then(|val| {
 91            let cs = cs.into();
 92            // let s = val.borrow().s;
 93            let mut s = Vec::<UInt8<ConstraintF<C>>>::new();
 94            let s_bytes = to_bytes![val.borrow().s].unwrap();
 95            for i in 0..s_bytes.len() {
 96                s.push(UInt8::<ConstraintF<C>>::new_variable(
 97                    cs.clone(),
 98                    || Ok(s_bytes[i].clone()),
 99                    mode,
100                )?);
101            }
102
103            let r = GC::new_variable(cs.clone(), || Ok(val.borrow().r), mode)?;
104
105            Ok(Self {
106                s,
107                r,
108                _curve: PhantomData,
109            })
110        })
111    }
112}
113
114#[derive(Clone)]
115pub struct ParametersVar<C: ProjectiveCurve, GC: CurveVar<C, ConstraintF<C>>>
116where
117    for<'a> &'a GC: GroupOpsBounds<'a, C, GC>,
118{
119    generator: GC,
120    _curve: PhantomData<C>,
121}
122
123impl<C, GC> AllocVar<Parameters<C>, ConstraintF<C>> for ParametersVar<C, GC>
124where
125    C: ProjectiveCurve,
126    GC: CurveVar<C, ConstraintF<C>>,
127    for<'a> &'a GC: GroupOpsBounds<'a, C, GC>,
128{
129    fn new_variable<T: Borrow<Parameters<C>>>(
130        cs: impl Into<Namespace<ConstraintF<C>>>,
131        f: impl FnOnce() -> Result<T, SynthesisError>,
132        mode: AllocationMode,
133    ) -> Result<Self, SynthesisError> {
134        f().and_then(|val| {
135            let cs = cs.into();
136            let generator = GC::new_variable(cs.clone(), || Ok(val.borrow().generator), mode)?;
137            Ok(Self {
138                generator,
139                _curve: PhantomData,
140            })
141        })
142    }
143}
144
145pub struct BlindSigVerifyGadget<C: ProjectiveCurve, GC: CurveVar<C, ConstraintF<C>>>
146where
147    for<'a> &'a GC: GroupOpsBounds<'a, C, GC>,
148{
149    _params: Parameters<C>, // TODO review if needed, maybe delete
150    _gc: PhantomData<GC>,
151}
152
153impl<C: ProjectiveCurve, GC: CurveVar<C, ConstraintF<C>>> BlindSigVerifyGadget<C, GC>
154where
155    C: ProjectiveCurve,
156    GC: CurveVar<C, ConstraintF<C>>,
157    for<'a> &'a GC: GroupOpsBounds<'a, C, GC>,
158    ark_r1cs_std::groups::curves::twisted_edwards::AffineVar<
159        EdwardsParameters,
160        FpVar<Fp256<FqParameters>>,
161    >: From<GC>,
162    <C as ProjectiveCurve>::BaseField: PrimeField,
163    FpVar<<C as ProjectiveCurve>::BaseField>: Mul<FpVar<Fp256<FqParameters>>>,
164    FpVar<<C as ProjectiveCurve>::BaseField>: From<FpVar<Fp256<FqParameters>>>,
165{
166    fn verify(
167        parameters: &ParametersVar<C, GC>,
168        poseidon_hash: &PoseidonGadget<ConstraintF<C>>,
169        m: FpVar<ConstraintF<C>>,
170        s: &SignatureVar<C, GC>,
171        q: &PublicKeyVar<C, GC>,
172    ) -> Result<Boolean<ConstraintF<C>>, SynthesisError> {
173        let s_s = s.s.clone();
174
175        let sG = parameters
176            .generator
177            .scalar_mul_le(s_s.to_bits_le()?.iter())?;
178
179        // Note: in a circuit that aggregates multiple verifications, the hashing step could be
180        // done outside the signature verification, once for all 1 votes and once for all 0 votes,
181        // saving lots of constraints
182        let hm = poseidon_hash.hash(&[m])?;
183        let r = EdwardsVar::from(s.r.clone()); // WIP
184        let rx_fpvar: FpVar<ConstraintF<C>> = r.x.into();
185
186        // G * s == R + Q * (R.x * H(m))
187        let Q_rx_hm_0 = q.pub_key.scalar_mul_le(rx_fpvar.to_bits_le()?.iter())?;
188        let Q_rx_hm = Q_rx_hm_0.scalar_mul_le(hm.to_bits_le()?.iter())?;
189        let RHS = s.r.clone() + Q_rx_hm;
190
191        sG.is_eq(&RHS)
192    }
193}
194
195#[cfg(test)]
196mod test {
197    use super::*;
198    use crate::{poseidon_setup_params, BlindSigScheme};
199    use ark_ed_on_bn254::constraints::EdwardsVar as BabyJubJubVar;
200    use ark_ed_on_bn254::EdwardsProjective as BabyJubJub;
201
202    use arkworks_native_gadgets::poseidon;
203    use arkworks_utils::Curve;
204
205    use ark_relations::r1cs::ConstraintSystem;
206
207    type Fq = <BabyJubJub as ProjectiveCurve>::BaseField;
208    // type Fr = <BabyJubJub as ProjectiveCurve>::ScalarField;
209    type S = BlindSigScheme<BabyJubJub>;
210
211    #[test]
212    fn test_verify() {
213        let poseidon_params = poseidon_setup_params::<Fq>(Curve::Bn254, 5, 3);
214        let poseidon_hash = poseidon::Poseidon::new(poseidon_params);
215        let mut rng = ark_std::test_rng();
216
217        // create signature using native-rust lib
218        let params = S::setup();
219        let (pk, sk) = S::keygen(&params, &mut rng);
220        let (k, signer_r) = S::new_request_params(&params, &mut rng);
221        let m = Fq::from(1234);
222        let (m_blinded, u) = S::blind(&params, &mut rng, &poseidon_hash, m, signer_r).unwrap();
223        let s_blinded = S::blind_sign(sk, k, m_blinded);
224        let s = S::unblind(s_blinded, u);
225        let verified = S::verify(&params, &poseidon_hash, m, s.clone(), pk);
226        assert!(verified);
227
228        // use the constraint system to verify the signature
229        type SG = BlindSigVerifyGadget<BabyJubJub, BabyJubJubVar>;
230        let cs = ConstraintSystem::<Fq>::new_ref();
231
232        let params_var =
233            ParametersVar::<BabyJubJub, BabyJubJubVar>::new_constant(cs.clone(), params).unwrap();
234        let signature_var =
235            SignatureVar::<BabyJubJub, BabyJubJubVar>::new_witness(cs.clone(), || Ok(&s)).unwrap();
236        let pk_var =
237            PublicKeyVar::<BabyJubJub, BabyJubJubVar>::new_witness(cs.clone(), || Ok(&pk)).unwrap();
238        let m_var = FpVar::<Fq>::new_witness(cs.clone(), || Ok(&m)).unwrap();
239        let poseidon_hash_var =
240            PoseidonGadget::<Fq>::from_native(&mut cs.clone(), poseidon_hash).unwrap();
241
242        let valid_sig = SG::verify(
243            &params_var,
244            &poseidon_hash_var,
245            m_var,
246            &signature_var,
247            &pk_var,
248        )
249        .unwrap();
250        valid_sig.enforce_equal(&Boolean::<Fq>::TRUE).unwrap();
251        assert!(cs.is_satisfied().unwrap());
252    }
253}