From 6e408d03a67a1d4137399483b8844b8f0b9b0454 Mon Sep 17 00:00:00 2001 From: Srinath Setty Date: Tue, 16 Aug 2022 14:06:08 -0700 Subject: [PATCH] simplify signature gadget (#109) --- examples/ecdsa/circuit.rs | 229 --------------------------- examples/ecdsa/main.rs | 291 ----------------------------------- examples/ecdsa/utils.rs | 29 ---- examples/signature.rs | 314 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 314 insertions(+), 549 deletions(-) delete mode 100644 examples/ecdsa/circuit.rs delete mode 100644 examples/ecdsa/main.rs delete mode 100644 examples/ecdsa/utils.rs create mode 100644 examples/signature.rs diff --git a/examples/ecdsa/circuit.rs b/examples/ecdsa/circuit.rs deleted file mode 100644 index 211007e..0000000 --- a/examples/ecdsa/circuit.rs +++ /dev/null @@ -1,229 +0,0 @@ -use bellperson::{ - gadgets::{boolean::AllocatedBit, num::AllocatedNum}, - ConstraintSystem, SynthesisError, -}; -use ff::{PrimeField, PrimeFieldBits}; -use nova_snark::{gadgets::ecc::AllocatedPoint, traits::circuit::StepCircuit}; -use subtle::Choice; - -// An affine point coordinate that is on the curve. -#[derive(Clone, Copy, Debug)] -pub struct Coordinate -where - F: PrimeField, -{ - pub x: F, - pub y: F, - pub is_infinity: bool, -} - -impl Coordinate -where - F: PrimeField, -{ - // New affine point coordiante on the curve so is_infinity = false. - pub fn new(x: F, y: F) -> Self { - Self { - x, - y, - is_infinity: false, - } - } -} - -// An ECDSA signature -#[derive(Clone, Debug)] -pub struct EcdsaSignature -where - Fb: PrimeField, - Fs: PrimeField + PrimeFieldBits, -{ - pk: Coordinate, // public key - r: Coordinate, // (r, s) is the ECDSA signature - s: Fs, - c: Fs, // hash of the message - g: Coordinate, // generator of the group; could be omitted if Nova's traits allow accessing the generator -} - -impl EcdsaSignature -where - Fb: PrimeField, - Fs: PrimeField + PrimeFieldBits, -{ - pub fn new(pk: Coordinate, r: Coordinate, s: Fs, c: Fs, g: Coordinate) -> Self { - Self { pk, r, s, c, g } - } -} - -// An ECDSA signature proof that we will use on the primary curve -#[derive(Clone, Debug)] -pub struct EcdsaCircuit -where - F: PrimeField, -{ - pub r: Coordinate, - pub g: Coordinate, - pub pk: Coordinate, - pub c: F, - pub s: F, - pub c_bits: Vec, - pub s_bits: Vec, -} - -impl EcdsaCircuit -where - F: PrimeField, -{ - // Creates a new [`EcdsaCircuit`]. The base and scalar field elements from the curve - // field used by the signature are converted to scalar field elements from the cyclic curve - // field used by the circuit. - pub fn new(num_steps: usize, signatures: &[EcdsaSignature]) -> (Vec, Vec) - where - Fb: PrimeField, - Fs: PrimeField + PrimeFieldBits, - { - let mut z0 = Vec::new(); - let mut circuits = Vec::new(); - for (i, signature) in signatures.iter().enumerate().take(num_steps) { - let r = Coordinate::new( - F::from_repr(signature.r.x.to_repr()).unwrap(), - F::from_repr(signature.r.y.to_repr()).unwrap(), - ); - - let g = Coordinate::new( - F::from_repr(signature.g.x.to_repr()).unwrap(), - F::from_repr(signature.g.y.to_repr()).unwrap(), - ); - - let pk = Coordinate::new( - F::from_repr(signature.pk.x.to_repr()).unwrap(), - F::from_repr(signature.pk.y.to_repr()).unwrap(), - ); - - let c_bits = Self::to_le_bits(&signature.c); - let s_bits = Self::to_le_bits(&signature.s); - let c = F::from_repr(signature.c.to_repr()).unwrap(); - let s = F::from_repr(signature.s.to_repr()).unwrap(); - - let circuit = EcdsaCircuit { - r, - g, - pk, - c, - s, - c_bits, - s_bits, - }; - circuits.push(circuit); - - if i == 0 { - z0 = vec![r.x, r.y, g.x, g.y, pk.x, pk.y, c, s]; - } - } - - (z0, circuits) - } - - // Converts the scalar field element from the curve used by the signature to a bit represenation - // for later use in scalar multiplication using the cyclic curve used by the circuit. - fn to_le_bits(fs: &Fs) -> Vec - where - Fs: PrimeField + PrimeFieldBits, - { - let bits = fs - .to_repr() - .iter() - .flat_map(|byte| (0..8).map(move |i| Choice::from((byte >> i) & 1u8))) - .collect::>(); - bits - } - - // Synthesize a bit representation into circuit gadgets. - fn synthesize_bits>( - cs: &mut CS, - bits: &[Choice], - ) -> Result, SynthesisError> { - let alloc_bits: Vec = bits - .iter() - .enumerate() - .map(|(i, bit)| { - AllocatedBit::alloc( - cs.namespace(|| format!("bit {}", i)), - Some(bit.unwrap_u8() == 1u8), - ) - }) - .collect::, SynthesisError>>() - .unwrap(); - Ok(alloc_bits) - } -} - -impl StepCircuit for EcdsaCircuit -where - F: PrimeField + PrimeFieldBits, -{ - fn arity(&self) -> usize { - 8 - } - - // Prove knowledge of the sk used to generate the Ecdsa signature (R,s) - // with public key PK and message commitment c. - // [s]G == R + [c]PK - fn synthesize>( - &self, - cs: &mut CS, - _z: &[AllocatedNum], - ) -> Result>, SynthesisError> { - let g = AllocatedPoint::alloc( - cs.namespace(|| "G"), - Some((self.g.x, self.g.y, self.g.is_infinity)), - )?; - let s_bits = Self::synthesize_bits(&mut cs.namespace(|| "s_bits"), &self.s_bits)?; - let sg = g.scalar_mul(cs.namespace(|| "[s]G"), s_bits)?; - let r = AllocatedPoint::alloc( - cs.namespace(|| "R"), - Some((self.r.x, self.r.y, self.r.is_infinity)), - )?; - let c_bits = Self::synthesize_bits(&mut cs.namespace(|| "c_bits"), &self.c_bits)?; - let pk = AllocatedPoint::alloc( - cs.namespace(|| "PK"), - Some((self.pk.x, self.pk.y, self.pk.is_infinity)), - )?; - let cpk = pk.scalar_mul(&mut cs.namespace(|| "[c]PK"), c_bits)?; - let rcpk = cpk.add(&mut cs.namespace(|| "R + [c]PK"), &r)?; - - let (rcpk_x, rcpk_y, _) = rcpk.get_coordinates(); - let (sg_x, sg_y, _) = sg.get_coordinates(); - - cs.enforce( - || "sg_x == rcpk_x", - |lc| lc + sg_x.get_variable(), - |lc| lc + CS::one(), - |lc| lc + rcpk_x.get_variable(), - ); - - cs.enforce( - || "sg_y == rcpk_y", - |lc| lc + sg_y.get_variable(), - |lc| lc + CS::one(), - |lc| lc + rcpk_y.get_variable(), - ); - - let rx = AllocatedNum::alloc(cs.namespace(|| "rx"), || Ok(self.r.x))?; - let ry = AllocatedNum::alloc(cs.namespace(|| "ry"), || Ok(self.r.y))?; - let gx = AllocatedNum::alloc(cs.namespace(|| "gx"), || Ok(self.g.x))?; - let gy = AllocatedNum::alloc(cs.namespace(|| "gy"), || Ok(self.g.y))?; - let pkx = AllocatedNum::alloc(cs.namespace(|| "pkx"), || Ok(self.pk.x))?; - let pky = AllocatedNum::alloc(cs.namespace(|| "pky"), || Ok(self.pk.y))?; - let c = AllocatedNum::alloc(cs.namespace(|| "c"), || Ok(self.c))?; - let s = AllocatedNum::alloc(cs.namespace(|| "s"), || Ok(self.s))?; - - Ok(vec![rx, ry, gx, gy, pkx, pky, c, s]) - } - - fn output(&self, _z: &[F]) -> Vec { - vec![ - self.r.x, self.r.y, self.g.x, self.g.y, self.pk.x, self.pk.y, self.c, self.s, - ] - } -} diff --git a/examples/ecdsa/main.rs b/examples/ecdsa/main.rs deleted file mode 100644 index b2c47e8..0000000 --- a/examples/ecdsa/main.rs +++ /dev/null @@ -1,291 +0,0 @@ -//! Demonstrates how to use Nova to produce a recursive proof of an ECDSA signature. -//! This example proves the knowledge of a sequence of ECDSA signatures with different public keys on different messages, -//! but the example can be adapted to other settings (e.g., proving the validity of the certificate chain with a well-known root public key) -//! Scheme borrowed from https://github.com/filecoin-project/bellperson-gadgets/blob/main/src/eddsa.rs -//! Sign using G1 curve, and prove using G2 curve. - -use core::ops::{Add, AddAssign, Mul, MulAssign, Neg}; -use ff::{ - derive::byteorder::{ByteOrder, LittleEndian}, - Field, PrimeField, PrimeFieldBits, -}; -use nova_snark::{ - traits::{circuit::TrivialTestCircuit, Group as Nova_Group}, - CompressedSNARK, PublicParams, RecursiveSNARK, -}; -use num_bigint::BigUint; -use pasta_curves::{ - arithmetic::CurveAffine, - group::{Curve, Group}, -}; -use rand::{rngs::OsRng, RngCore}; -use sha3::{Digest, Sha3_512}; -use subtle::Choice; - -mod circuit; -mod utils; - -use crate::circuit::{Coordinate, EcdsaCircuit, EcdsaSignature}; -use crate::utils::BitIterator; - -type G1 = pasta_curves::pallas::Point; -type G2 = pasta_curves::vesta::Point; -type S1 = nova_snark::spartan_with_ipa_pc::RelaxedR1CSSNARK; -type S2 = nova_snark::spartan_with_ipa_pc::RelaxedR1CSSNARK; - -#[derive(Debug, Clone, Copy)] -pub struct SecretKey(pub ::Scalar); - -impl SecretKey { - pub fn random(mut rng: impl RngCore) -> Self { - let secret = ::Scalar::random(&mut rng); - Self(secret) - } -} - -#[derive(Debug, Clone, Copy)] -pub struct PublicKey(pub G1); - -impl PublicKey { - pub fn from_secret_key(s: &SecretKey) -> Self { - let point = G1::generator() * s.0; - Self(point) - } -} - -#[derive(Clone)] -pub struct Signature { - pub r: G1, - pub s: ::Scalar, -} - -impl SecretKey { - pub fn sign(self, c: ::Scalar, mut rng: impl RngCore) -> Signature { - // T - let mut t = [0u8; 80]; - rng.fill_bytes(&mut t[..]); - - // h = H*(T || M) - let h = Self::hash_to_scalar(b"Nova_Ecdsa_Hash", &t[..], &c.to_repr()); - - // R = [h]G - let r = G1::generator().mul(h); - - // s = h + c * sk - let mut s = c; - - s.mul_assign(&self.0); - s.add_assign(&h); - - Signature { r, s } - } - - fn mul_bits>( - s: &::Scalar, - bits: BitIterator, - ) -> ::Scalar { - let mut x = ::Scalar::zero(); - for bit in bits { - x.double(); - - if bit { - x.add_assign(s) - } - } - x - } - - fn to_uniform(digest: &[u8]) -> ::Scalar { - assert_eq!(digest.len(), 64); - let mut bits: [u64; 8] = [0; 8]; - LittleEndian::read_u64_into(digest, &mut bits); - Self::mul_bits(&::Scalar::one(), BitIterator::new(bits)) - } - - pub fn to_uniform_32(digest: &[u8]) -> ::Scalar { - assert_eq!(digest.len(), 32); - let mut bits: [u64; 4] = [0; 4]; - LittleEndian::read_u64_into(digest, &mut bits); - Self::mul_bits(&::Scalar::one(), BitIterator::new(bits)) - } - - pub fn hash_to_scalar(persona: &[u8], a: &[u8], b: &[u8]) -> ::Scalar { - let mut hasher = Sha3_512::new(); - hasher.input(persona); - hasher.input(a); - hasher.input(b); - let digest = hasher.result(); - Self::to_uniform(digest.as_ref()) - } -} - -impl PublicKey { - pub fn verify(&self, c: ::Scalar, signature: &Signature) -> bool { - let modulus = Self::modulus_as_scalar(); - let order_check_pk = self.0.mul(modulus); - if !order_check_pk.eq(&G1::identity()) { - return false; - } - - let order_check_r = signature.r.mul(modulus); - if !order_check_r.eq(&G1::identity()) { - return false; - } - - // 0 = [-s]G + R + [c]PK - self - .0 - .mul(c) - .add(&signature.r) - .add(G1::generator().mul(signature.s).neg()) - .eq(&G1::identity()) - } - - fn modulus_as_scalar() -> ::Scalar { - let mut bits = ::Scalar::char_le_bits().to_bitvec(); - let mut acc = BigUint::new(Vec::::new()); - while let Some(b) = bits.pop() { - acc <<= 1_i32; - acc += b as u8; - } - let modulus = acc.to_str_radix(10); - ::Scalar::from_str_vartime(&modulus).unwrap() - } -} - -fn main() { - // In a VERY LIMITED case of messages known to be unique due to application level - // and being less than the group order when interpreted as integer, one can sign - // the message directly without hashing - pub const MAX_MESSAGE_LEN: usize = 16; - assert!(MAX_MESSAGE_LEN * 8 <= ::Scalar::CAPACITY as usize); - - // produce public parameters - println!("Generating public parameters..."); - - let circuit_primary = EcdsaCircuit::<::Scalar> { - r: Coordinate::new( - ::Scalar::zero(), - ::Scalar::zero(), - ), - g: Coordinate::new( - ::Scalar::zero(), - ::Scalar::zero(), - ), - pk: Coordinate::new( - ::Scalar::zero(), - ::Scalar::zero(), - ), - c: ::Scalar::zero(), - s: ::Scalar::zero(), - c_bits: vec![Choice::from(0u8); 256], - s_bits: vec![Choice::from(0u8); 256], - }; - - let circuit_secondary = TrivialTestCircuit::default(); - - let pp = PublicParams::< - G2, - G1, - EcdsaCircuit<::Scalar>, - TrivialTestCircuit<::Scalar>, - >::setup(circuit_primary, circuit_secondary.clone()); - - // produce non-deterministic advice - println!("Generating non-deterministic advice..."); - - let num_steps = 3; - - let signatures = || { - let mut signatures = Vec::new(); - for i in 0..num_steps { - let sk = SecretKey::random(&mut OsRng); - let pk = PublicKey::from_secret_key(&sk); - - let message = format!("MESSAGE{}", i).as_bytes().to_owned(); - assert!(message.len() <= MAX_MESSAGE_LEN); - - let mut digest: Vec = message.to_vec(); - for _ in 0..(32 - message.len() as u32) { - digest.extend(&[0u8; 1]); - } - - let c = SecretKey::to_uniform_32(digest.as_ref()); - - let signature_primary = sk.sign(c, &mut OsRng); - let result = pk.verify(c, &signature_primary); - assert!(result); - - // Affine coordinates guaranteed to be on the curve - let rxy = signature_primary.r.to_affine().coordinates().unwrap(); - let gxy = G1::generator().to_affine().coordinates().unwrap(); - let pkxy = pk.0.to_affine().coordinates().unwrap(); - - let s = signature_primary.s; - - signatures.push(EcdsaSignature::< - ::Base, - ::Scalar, - >::new( - Coordinate::<::Base>::new(*pkxy.x(), *pkxy.y()), - Coordinate::<::Base>::new(*rxy.x(), *rxy.y()), - s, - c, - Coordinate::<::Base>::new(*gxy.x(), *gxy.y()), - )); - } - signatures - }; - - let (z0_primary, circuits_primary) = EcdsaCircuit::<::Scalar>::new::< - ::Base, - ::Scalar, - >(num_steps, &signatures()); - - // Secondary circuit - let z0_secondary = vec![::Scalar::zero()]; - - // produce a recursive SNARK - println!("Generating a RecursiveSNARK..."); - - type C1 = EcdsaCircuit<::Scalar>; - type C2 = TrivialTestCircuit<::Scalar>; - - let mut recursive_snark: Option> = None; - - for (i, circuit_primary) in circuits_primary.iter().take(num_steps).enumerate() { - let result = RecursiveSNARK::prove_step( - &pp, - recursive_snark, - circuit_primary.clone(), - circuit_secondary.clone(), - z0_primary.clone(), - z0_secondary.clone(), - ); - assert!(result.is_ok()); - println!("RecursiveSNARK::prove_step {}: {:?}", i, result.is_ok()); - recursive_snark = Some(result.unwrap()); - } - - assert!(recursive_snark.is_some()); - let recursive_snark = recursive_snark.unwrap(); - - // verify the recursive SNARK - println!("Verifying the RecursiveSNARK..."); - let res = recursive_snark.verify(&pp, num_steps, z0_primary.clone(), z0_secondary.clone()); - println!("RecursiveSNARK::verify: {:?}", res.is_ok()); - assert!(res.is_ok()); - - // produce a compressed SNARK - println!("Generating a CompressedSNARK..."); - let res = CompressedSNARK::<_, _, _, _, S1, S2>::prove(&pp, &recursive_snark); - println!("CompressedSNARK::prove: {:?}", res.is_ok()); - assert!(res.is_ok()); - let compressed_snark = res.unwrap(); - - // verify the compressed SNARK - println!("Verifying a CompressedSNARK..."); - let res = compressed_snark.verify(&pp, num_steps, z0_primary, z0_secondary); - println!("CompressedSNARK::verify: {:?}", res.is_ok()); - assert!(res.is_ok()); -} diff --git a/examples/ecdsa/utils.rs b/examples/ecdsa/utils.rs deleted file mode 100644 index 0a5c89e..0000000 --- a/examples/ecdsa/utils.rs +++ /dev/null @@ -1,29 +0,0 @@ -#[derive(Debug)] -pub struct BitIterator { - t: E, - n: usize, -} - -impl> BitIterator { - pub fn new(t: E) -> Self { - let n = t.as_ref().len() * 64; - - BitIterator { t, n } - } -} - -impl> Iterator for BitIterator { - type Item = bool; - - fn next(&mut self) -> Option { - if self.n == 0 { - None - } else { - self.n -= 1; - let part = self.n / 64; - let bit = self.n - (64 * part); - - Some(self.t.as_ref()[part] & (1 << bit) > 0) - } - } -} diff --git a/examples/signature.rs b/examples/signature.rs new file mode 100644 index 0000000..6516712 --- /dev/null +++ b/examples/signature.rs @@ -0,0 +1,314 @@ +use bellperson::{ + gadgets::{boolean::AllocatedBit, test::TestConstraintSystem}, + ConstraintSystem, SynthesisError, +}; +use core::ops::{AddAssign, MulAssign}; +use ff::{ + derive::byteorder::{ByteOrder, LittleEndian}, + Field, PrimeField, PrimeFieldBits, +}; +use nova_snark::gadgets::ecc::AllocatedPoint; +use num_bigint::BigUint; +use pasta_curves::{ + arithmetic::CurveAffine, + group::{Curve, Group}, +}; +use rand::{rngs::OsRng, RngCore}; +use sha3::{Digest, Sha3_512}; + +#[derive(Debug, Clone, Copy)] +pub struct SecretKey(G::Scalar); + +impl SecretKey +where + G: Group, +{ + pub fn random(mut rng: impl RngCore) -> Self { + let secret = G::Scalar::random(&mut rng); + Self(secret) + } +} + +#[derive(Debug, Clone, Copy)] +pub struct PublicKey(G); + +impl PublicKey +where + G: Group, +{ + pub fn from_secret_key(s: &SecretKey) -> Self { + let point = G::generator() * s.0; + Self(point) + } +} + +#[derive(Clone)] +pub struct Signature { + pub r: G, + pub s: G::Scalar, +} + +impl SecretKey +where + G: Group, +{ + pub fn sign(self, c: G::Scalar, mut rng: impl RngCore) -> Signature { + // T + let mut t = [0u8; 80]; + rng.fill_bytes(&mut t[..]); + + // h = H*(T || M) + let h = Self::hash_to_scalar(b"Nova_Ecdsa_Hash", &t[..], c.to_repr().as_mut()); + + // R = [h]G + let r = G::generator().mul(h); + + // s = h + c * sk + let mut s = c; + + s.mul_assign(&self.0); + s.add_assign(&h); + + Signature { r, s } + } + + fn mul_bits>(s: &G::Scalar, bits: BitIterator) -> G::Scalar { + let mut x = G::Scalar::zero(); + for bit in bits { + x = x.double(); + + if bit { + x.add_assign(s) + } + } + x + } + + fn to_uniform(digest: &[u8]) -> G::Scalar { + assert_eq!(digest.len(), 64); + let mut bits: [u64; 8] = [0; 8]; + LittleEndian::read_u64_into(digest, &mut bits); + Self::mul_bits(&G::Scalar::one(), BitIterator::new(bits)) + } + + pub fn to_uniform_32(digest: &[u8]) -> G::Scalar { + assert_eq!(digest.len(), 32); + let mut bits: [u64; 4] = [0; 4]; + LittleEndian::read_u64_into(digest, &mut bits); + Self::mul_bits(&G::Scalar::one(), BitIterator::new(bits)) + } + + pub fn hash_to_scalar(persona: &[u8], a: &[u8], b: &[u8]) -> G::Scalar { + let mut hasher = Sha3_512::new(); + hasher.input(persona); + hasher.input(a); + hasher.input(b); + let digest = hasher.result(); + Self::to_uniform(digest.as_ref()) + } +} + +impl PublicKey +where + G: Group, + G::Scalar: PrimeFieldBits, +{ + pub fn verify(&self, c: G::Scalar, signature: &Signature) -> bool { + let modulus = Self::modulus_as_scalar(); + let order_check_pk = self.0.mul(modulus); + if !order_check_pk.eq(&G::identity()) { + return false; + } + + let order_check_r = signature.r.mul(modulus); + if !order_check_r.eq(&G::identity()) { + return false; + } + + // 0 = [-s]G + R + [c]PK + self + .0 + .mul(c) + .add(&signature.r) + .add(G::generator().mul(signature.s).neg()) + .eq(&G::identity()) + } + + fn modulus_as_scalar() -> G::Scalar { + let mut bits = G::Scalar::char_le_bits().to_bitvec(); + let mut acc = BigUint::new(Vec::::new()); + while let Some(b) = bits.pop() { + acc <<= 1_i32; + acc += b as u8; + } + let modulus = acc.to_str_radix(10); + G::Scalar::from_str_vartime(&modulus).unwrap() + } +} + +#[derive(Debug)] +pub struct BitIterator { + t: E, + n: usize, +} + +impl> BitIterator { + pub fn new(t: E) -> Self { + let n = t.as_ref().len() * 64; + + BitIterator { t, n } + } +} + +impl> Iterator for BitIterator { + type Item = bool; + + fn next(&mut self) -> Option { + if self.n == 0 { + None + } else { + self.n -= 1; + let part = self.n / 64; + let bit = self.n - (64 * part); + + Some(self.t.as_ref()[part] & (1 << bit) > 0) + } + } +} + +// Synthesize a bit representation into circuit gadgets. +pub fn synthesize_bits>( + cs: &mut CS, + bits: Option>, +) -> Result, SynthesisError> { + (0..F::NUM_BITS) + .into_iter() + .map(|i| { + AllocatedBit::alloc( + cs.namespace(|| format!("bit {}", i)), + Some(bits.as_ref().unwrap()[i as usize]), + ) + }) + .collect::, SynthesisError>>() +} + +pub fn verify_signature>( + cs: &mut CS, + pk: AllocatedPoint, + r: AllocatedPoint, + s_bits: Vec, + c_bits: Vec, +) -> Result<(), SynthesisError> { + let g = AllocatedPoint::alloc( + cs.namespace(|| "g"), + Some(( + F::from_str_vartime( + "28948022309329048855892746252171976963363056481941647379679742748393362948096", + ) + .unwrap(), + F::from_str_vartime("2").unwrap(), + false, + )), + ) + .unwrap(); + + cs.enforce( + || "gx is vesta curve", + |lc| lc + g.get_coordinates().0.get_variable(), + |lc| lc + CS::one(), + |lc| { + lc + ( + F::from_str_vartime( + "28948022309329048855892746252171976963363056481941647379679742748393362948096", + ) + .unwrap(), + CS::one(), + ) + }, + ); + + cs.enforce( + || "gy is vesta curve", + |lc| lc + g.get_coordinates().1.get_variable(), + |lc| lc + CS::one(), + |lc| lc + (F::from_str_vartime("2").unwrap(), CS::one()), + ); + + let sg = g.scalar_mul(cs.namespace(|| "[s]G"), s_bits)?; + let cpk = pk.scalar_mul(&mut cs.namespace(|| "[c]PK"), c_bits)?; + let rcpk = cpk.add(&mut cs.namespace(|| "R + [c]PK"), &r)?; + + let (rcpk_x, rcpk_y, _) = rcpk.get_coordinates(); + let (sg_x, sg_y, _) = sg.get_coordinates(); + + cs.enforce( + || "sg_x == rcpk_x", + |lc| lc + sg_x.get_variable(), + |lc| lc + CS::one(), + |lc| lc + rcpk_x.get_variable(), + ); + + cs.enforce( + || "sg_y == rcpk_y", + |lc| lc + sg_y.get_variable(), + |lc| lc + CS::one(), + |lc| lc + rcpk_y.get_variable(), + ); + + Ok(()) +} + +type G1 = pasta_curves::pallas::Point; +type G2 = pasta_curves::vesta::Point; + +fn main() { + let mut cs = TestConstraintSystem::<::Scalar>::new(); + assert!(cs.is_satisfied()); + assert_eq!(cs.num_constraints(), 0); + + let sk = SecretKey::::random(&mut OsRng); + let pk = PublicKey::from_secret_key(&sk); + + // generate a random message to sign + let c = ::Scalar::random(&mut OsRng); + + // sign and verify + let signature = sk.sign(c, &mut OsRng); + let result = pk.verify(c, &signature); + assert!(result); + + // prepare inputs to the circuit gadget + let pk = { + let pkxy = pk.0.to_affine().coordinates().unwrap(); + + AllocatedPoint::alloc( + cs.namespace(|| "pub key"), + Some((*pkxy.x(), *pkxy.y(), false)), + ) + .unwrap() + }; + let r = { + let rxy = signature.r.to_affine().coordinates().unwrap(); + AllocatedPoint::alloc(cs.namespace(|| "r"), Some((*rxy.x(), *rxy.y(), false))).unwrap() + }; + let s = { + let s_bits = signature + .s + .to_le_bits() + .iter() + .map(|b| *b) + .collect::>(); + + synthesize_bits(&mut cs.namespace(|| "s bits"), Some(s_bits)).unwrap() + }; + let c = { + let c_bits = c.to_le_bits().iter().map(|b| *b).collect::>(); + + synthesize_bits(&mut cs.namespace(|| "c bits"), Some(c_bits)).unwrap() + }; + + // Check the signature was signed by the correct sk using the pk + verify_signature(&mut cs, pk, r, s, c).unwrap(); + + assert!(cs.is_satisfied()); +}