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