Schnorr proofs in progress
Brian Lawrence tideofwords@gmail.com
Sun, 29 Sep 2024 14:15:39 -0700
2 files changed,
135 insertions(+),
31 deletions(-)
M
src/schnorr.rs
→
src/schnorr.rs
@@ -9,30 +9,30 @@
const BIG_GROUP_GEN: GoldilocksField = GoldilocksField(14293326489335486720); #[derive(Copy, Clone, Debug)] -struct SchnorrSigner { +pub struct SchnorrSigner { PRIME_GROUP_GEN: GoldilocksField, PRIME_GROUP_ORDER: u64, } #[derive(Copy, Clone, Debug)] -struct SchnorrSecretKey { - sk: u64, +pub struct SchnorrSecretKey { + pub sk: u64, } #[derive(Copy, Clone, Debug)] -struct SchnorrPublicKey { - pk: GoldilocksField, +pub struct SchnorrPublicKey { + pub pk: GoldilocksField, } #[derive(Copy, Clone, Debug)] -struct SchnorrSignature { - s: u64, - e: u64, +pub struct SchnorrSignature { + pub s: u64, + pub e: u64, } impl SchnorrSigner{ - fn new() -> Self { + pub fn new() -> Self { let quotient_order: u64 = (1 << 48) - (1 << 32); let PRIME_GROUP_GEN: GoldilocksField = Self::pow(BIG_GROUP_GEN, quotient_order); let PRIME_GROUP_ORDER: u64 = (1 << 16) + 1;@@ -53,7 +53,7 @@ }
res } - fn keygen(&self, sk: &SchnorrSecretKey) -> SchnorrPublicKey { + pub fn keygen(&self, sk: &SchnorrSecretKey) -> SchnorrPublicKey { let pk: GoldilocksField = Self::pow(self.PRIME_GROUP_GEN, sk.sk).inverse(); println!("{:?}", self.PRIME_GROUP_GEN); // self.PRIME_GROUP_GEN is 6612579038192137166@@ -76,13 +76,13 @@ let group_order: u64 = (1 << 16) + 1;
rng.gen_range(0..group_order) } - fn u64_into_goldilocks_vec(&self, msg: Vec<u64>) -> Vec<GoldilocksField> { + pub fn u64_into_goldilocks_vec(&self, msg: Vec<u64>) -> Vec<GoldilocksField> { msg.into_iter() .map(|x| GoldilocksField::from_noncanonical_u64(x)) .collect() } - fn sign(&self, msg: &Vec<GoldilocksField>, sk: &SchnorrSecretKey, rng: &mut rand::rngs::ThreadRng) -> SchnorrSignature { + pub fn sign(&self, msg: &Vec<GoldilocksField>, sk: &SchnorrSecretKey, rng: &mut rand::rngs::ThreadRng) -> SchnorrSignature { let k: u64 = self.rand_group_multiplier(rng); let r: GoldilocksField = Self::pow(self.PRIME_GROUP_GEN, k); let e: u64 = self.hash_insecure(&r, msg);@@ -98,7 +98,7 @@ let s: u64 = s128 as u64;
SchnorrSignature{e, s} } - fn verify(&self, sig: &SchnorrSignature, msg: &Vec<GoldilocksField>, pk: &SchnorrPublicKey) -> bool { + pub fn verify(&self, sig: &SchnorrSignature, msg: &Vec<GoldilocksField>, pk: &SchnorrPublicKey) -> bool { let r: GoldilocksField = Self::pow(self.PRIME_GROUP_GEN, sig.s) * Self::pow(pk.pk, sig.e); let e_v: u64 = self.hash_insecure(&r, msg);
M
src/schnorr_prover.rs
→
src/schnorr_prover.rs
@@ -11,13 +11,40 @@ use plonky2::plonk::circuit_data::{CircuitConfig, CircuitData, CommonCircuitData, VerifierCircuitData, VerifierOnlyCircuitData};
use plonky2::plonk::config::{GenericConfig, PoseidonGoldilocksConfig}; use plonky2::plonk::proof::ProofWithPublicInputs; +pub struct MessageTarget { + msg: Vec<Target>, +} + +impl MessageTarget { + fn new_with_size(builder: &mut CircuitBuilder<GoldilocksField, 2>, n: usize) -> Self { + Self { + msg: builder.add_virtual_targets(n), + } + } +} + pub struct SchnorrSignatureTarget { s: Target, e: Target, } +impl SchnorrSignatureTarget { + fn new_virtual(builder: &mut CircuitBuilder<GoldilocksField, 2>) -> Self { + let s = builder.add_virtual_target(); + let e = builder.add_virtual_target(); + Self{ s, e } + } +} + pub struct SchnorrPublicKeyTarget { pk: Target, +} + +#[derive(Debug, Default)] +pub struct Mod65537Generator { + a: Target, + q: Target, + r: Target, } pub struct SchnorrBuilder {@@ -25,33 +52,64 @@
} impl SchnorrBuilder { - // the output Target is constrained to equal x^a - // here we assume that + // Reduce a modulo the constant 65537 + // where a is the canonical representative for an element of the field + // (meaning: 0 \leq a < p) - // waaait, maybe I can use their built in thing - fn prove_power< - F: RichField + Extendable<D>, - C: GenericConfig<D, F = F>, - const D: usize - > (builder: &mut CircuitBuilder::<F, D>, x: Target, a: Target, num_bits: usize) -> Target { - let bits: Vec<BoolTarget> = builder.split_le(a, num_bits); - // make a sequence of targets x_i - // where x_0 = 1 - // x_{num_bits} = x^a - // and in between: - // x_i = x_{i-1}**2 * (bits[num_bits+1-i] ? 1 : x) + // To verify this, write + // a = 65537 * q + r, and do range checks to check that: + // 0 <= q <= floor(p / 65537) + // 0 <= r < 65537 + // (these first two checks guarantee that a lies in the range [0, p + 65536]) + // if q = floor(p / 65537) then r = 0 + // (note that p % 65537 == 1 so this is the only possibility) + fn mod_65537 < + C: GenericConfig<2, F = GoldilocksField>, + > ( + builder: &mut CircuitBuilder::<GoldilocksField, 2>, + a: &Target, + ) -> Target { + let q = builder.add_virtual_target(); + let r = builder.add_virtual_target(); + + // the Mod65537Generator will assign values to q and r later + builder.add_simple_generator( Mod65537Generator { a, q, r } ); + + // impose four constraints + // 1. a = 65537 * q + r + let t65537 = builder.constant(GoldilocksField::from_canonical_u64(65537)); + let a_copy = builder.mul_add(t65537, q, r); + builder.connect(*a, a_copy); + // 2. 0 <= q <= floor(p / 65537) + // max_q is 281470681743360 = floor(p / 65537) = (p-1) / 65537 = 2^48 - 2^32 + let max_q = builder.constant(GoldilocksField::from_canonical_u64(281470681743360)); + builder.range_check(q, 48); + builder.range_check(builder.sub(max_q, q), 48); + + // 3. 0 <= r < 65537 + let max_r = builder.constant(GoldilocksField::from_canonical_u64(65537)); + builder.range_check(r, 17); + builder.range_check(builder.sub(max_r, r), 17); + + // 4. if q = floor(p / 65537) then r = 0 + let q_equals_max = builder.is_equal(q, max_q); + builder.connect(builder.mul(q_equals_max.target, r), builder.zero()); + + r } fn constrain_sig < C: GenericConfig<2, F = GoldilocksField>, > ( + &self, builder: &mut CircuitBuilder::<GoldilocksField, 2>, sig: &SchnorrSignatureTarget, - msg: &Vec<Target>, + msg: &MessageTarget, pk: &SchnorrPublicKeyTarget, ) -> () { let PRIME_GROUP_GEN: Target = builder.constant(GoldilocksField::from_canonical_u64(6612579038192137166)); + let PRIME_GROUP_ORDER: Target = builder.constant(GoldilocksField::from_canonical_u64(65537)); const num_bits_exp: usize = 32; /*@@ -65,15 +123,61 @@ let pe: Target = builder.exp(pk.pk, sig.e, num_bits_exp);
let r: Target = builder.mul(gs, pe); // compute hash + // note that it's safe to clone Targets since they just contain indices let hash_input: Vec<Target> = std::iter::once(r) - .chain(msg.iter().cloned()) + .chain(msg.iter().cloned()) .collect(); let e: Target = builder.hash_n_to_hash_no_pad::<PoseidonHash>( hash_input, - ).elements[0]; + ).elements[0] // whoops have to take mod group order; - // verify equality + // enforce equality builder.connect(e, sig.e); + } +} +#[cfg(test)] +mod tests{ + use crate::schnorr::{SchnorrPublicKey, SchnorrSecretKey, SchnorrSigner, SchnorrSignature}; + use crate::schnorr_prover::SchnorrBuilder; + use plonky2::plonk::circuit_builder::CircuitBuilder; + use plonky2::plonk::circuit_data::{CircuitConfig, CircuitData, CommonCircuitData, VerifierCircuitData, VerifierOnlyCircuitData}; + use plonky2::plonk::config::{GenericConfig, PoseidonGoldilocksConfig}; + use plonky2::field::goldilocks_field::GoldilocksField; + use rand; + + #[test] + fn test_schnorr() -> () { + const D: usize = 2; + type C = PoseidonGoldilocksConfig; + type F = <C as GenericConfig<D>>::F; + + let mut rng: rand::rngs::ThreadRng = rand::thread_rng(); + + let config = CircuitConfig::standard_recursion_config(); + let mut builder = CircuitBuilder::<F, D>::new(config); + + builder.add_virtual_fri_proof(num_leaves_per_oracle, params) + + let sb: SchnorrBuilder = SchnorrBuilder{}; + + // create keypair, message, signature + let sk: SchnorrSecretKey = SchnorrSecretKey{ sk: 133 }; + let ss = SchnorrSigner::new(); + let pk: SchnorrPublicKey = ss.keygen(&sk); + let msg: Vec<GoldilocksField> = ss.u64_into_goldilocks_vec( + vec![1500, 1600, 0, 0, 0] + ); + let msg_size: usize = msg.len(); + let sig: SchnorrSignature = ss.sign(&msg, &sk, &mut rng); + + let sig_target = builder.constant(sig); + // instead of verifying we're going to prove the verification + sb.constrain_sig( + &mut builder, + &sig, + &msg, + &pk + ); } }