arkeddsa/src/constraints.rs
src/constraints.rsRaw
1/// This file implements the EdDSA verification in-circuit.
2use ark_crypto_primitives::sponge::{
3 constraints::CryptographicSpongeVar,
4 poseidon::{constraints::PoseidonSpongeVar, PoseidonConfig},
5};
6use ark_ec::CurveGroup;
7use ark_ff::Field;
8use ark_r1cs_std::{
9 boolean::Boolean, fields::fp::FpVar, fields::nonnative::NonNativeFieldVar, groups::CurveVar,
10 ToBitsGadget, ToConstraintFieldGadget,
11};
12use ark_relations::r1cs::ConstraintSystemRef;
13
14/// CF stands for ConstraintField
15pub type CF<C> = <<C as CurveGroup>::BaseField as Field>::BasePrimeField;
16
17/// gadget to compute the EdDSA verification in-circuit
18pub fn verify<C, GC>(
19 cs: ConstraintSystemRef<CF<C>>,
20 poseidon_config: PoseidonConfig<CF<C>>,
21 pk: GC,
22 sig: (GC, NonNativeFieldVar<C::ScalarField, CF<C>>),
23 msg: FpVar<CF<C>>,
24) -> ark_relations::r1cs::Result<Boolean<CF<C>>>
25where
26 C: CurveGroup,
27 GC: CurveVar<C, CF<C>> + ToConstraintFieldGadget<CF<C>>,
28{
29 let (r, s): (GC, NonNativeFieldVar<C::ScalarField, CF<C>>) = sig;
30
31 let r_xy = r.to_constraint_field()?;
32 let pk_xy = pk.to_constraint_field()?;
33
34 let mut poseidon = PoseidonSpongeVar::new(cs.clone(), &poseidon_config);
35 poseidon.absorb(&r_xy)?;
36 poseidon.absorb(&pk_xy)?;
37 poseidon.absorb(&msg)?;
38 let k = poseidon.squeeze_field_elements(1)?;
39 let k = k
40 .first()
41 .ok_or(ark_relations::r1cs::SynthesisError::Unsatisfiable)?;
42
43 let kx_b = pk.scalar_mul_le(k.to_bits_le()?.iter())?;
44
45 let g = GC::new_constant(cs.clone(), C::generator())?;
46 let s_b = g.scalar_mul_le(s.to_bits_le()?.iter())?;
47 let r_rec: GC = s_b - kx_b;
48 Ok(r_rec.is_eq(&r)?)
49}
50
51#[cfg(test)]
52mod tests {
53 use ark_ff::PrimeField;
54 use ark_r1cs_std::{alloc::AllocVar, eq::EqGadget, fields::nonnative::NonNativeFieldVar};
55 use ark_relations::r1cs::ConstraintSystem;
56 use rand_core::OsRng;
57
58 use super::*;
59 use crate::ed_on_bn254_twist::{
60 constraints::EdwardsVar as GVar, BaseField as Fq, EdwardsConfig, EdwardsProjective as G,
61 ScalarField as Fr,
62 };
63 use crate::{poseidon_config, SigningKey};
64
65 #[test]
66 fn gadget_verify() {
67 let poseidon_config = poseidon_config::<Fq>(4, 8, 60);
68 let sk = SigningKey::<EdwardsConfig>::generate::<blake2::Blake2b512>(&mut OsRng).unwrap();
69 let msg_raw = b"xxx yyy <<< zzz >>> bunny";
70 let msg = Fq::from_le_bytes_mod_order(msg_raw);
71 let sig = sk
72 .sign::<blake2::Blake2b512>(&poseidon_config, &msg)
73 .unwrap();
74 let pk = sk.public_key();
75 pk.verify(&poseidon_config, &msg, &sig).unwrap();
76
77 let cs = ConstraintSystem::<Fq>::new_ref();
78 let pk_var: GVar = GVar::new_witness(cs.clone(), || Ok(*pk.as_ref())).unwrap();
79 let r_var: GVar = GVar::new_witness(cs.clone(), || Ok(*sig.r())).unwrap();
80 let s_var = NonNativeFieldVar::<Fr, Fq>::new_witness(cs.clone(), || Ok(sig.s())).unwrap();
81 let msg_var = FpVar::<Fq>::new_witness(cs.clone(), || Ok(msg)).unwrap();
82
83 let res = verify::<G, GVar>(cs.clone(), poseidon_config, pk_var, (r_var, s_var), msg_var)
84 .unwrap();
85 res.enforce_equal(&Boolean::<Fq>::TRUE).unwrap();
86
87 dbg!(cs.num_constraints());
88 assert!(cs.is_satisfied().unwrap());
89 }
90}