arnaucube
arnaucube/NovaPublic Self-hosted repository
simplify signature gadget (#109)

Srinath Setty srinath@microsoft.com
Tue, 16 Aug 2022 14:06:08 -0700
commit

6e408d03a67a1d4137399483b8844b8f0b9b0454

parent

faf28e71cd2075ce38cbb84eb60af198dae51bf8

4 files changed, 314 insertions(+), 549 deletions(-)

jump to
D examples/ecdsa/circuit.rs

@@ -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<F> -where - F: PrimeField<Repr = [u8; 32]>, -{ - pub x: F, - pub y: F, - pub is_infinity: bool, -} - -impl<F> Coordinate<F> -where - F: PrimeField<Repr = [u8; 32]>, -{ - // 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<Fb, Fs> -where - Fb: PrimeField<Repr = [u8; 32]>, - Fs: PrimeField<Repr = [u8; 32]> + PrimeFieldBits, -{ - pk: Coordinate<Fb>, // public key - r: Coordinate<Fb>, // (r, s) is the ECDSA signature - s: Fs, - c: Fs, // hash of the message - g: Coordinate<Fb>, // generator of the group; could be omitted if Nova's traits allow accessing the generator -} - -impl<Fb, Fs> EcdsaSignature<Fb, Fs> -where - Fb: PrimeField<Repr = [u8; 32]>, - Fs: PrimeField<Repr = [u8; 32]> + PrimeFieldBits, -{ - pub fn new(pk: Coordinate<Fb>, r: Coordinate<Fb>, s: Fs, c: Fs, g: Coordinate<Fb>) -> 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<F> -where - F: PrimeField<Repr = [u8; 32]>, -{ - pub r: Coordinate<F>, - pub g: Coordinate<F>, - pub pk: Coordinate<F>, - pub c: F, - pub s: F, - pub c_bits: Vec<Choice>, - pub s_bits: Vec<Choice>, -} - -impl<F> EcdsaCircuit<F> -where - F: PrimeField<Repr = [u8; 32]>, -{ - // Creates a new [`EcdsaCircuit<Fb, Fs>`]. 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<Fb, Fs>(num_steps: usize, signatures: &[EcdsaSignature<Fb, Fs>]) -> (Vec<F>, Vec<Self>) - where - Fb: PrimeField<Repr = [u8; 32]>, - Fs: PrimeField<Repr = [u8; 32]> + 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: &Fs) -> Vec<Choice> - where - Fs: PrimeField<Repr = [u8; 32]> + PrimeFieldBits, - { - let bits = fs - .to_repr() - .iter() - .flat_map(|byte| (0..8).map(move |i| Choice::from((byte >> i) & 1u8))) - .collect::<Vec<Choice>>(); - bits - } - - // Synthesize a bit representation into circuit gadgets. - fn synthesize_bits<CS: ConstraintSystem<F>>( - cs: &mut CS, - bits: &[Choice], - ) -> Result<Vec<AllocatedBit>, SynthesisError> { - let alloc_bits: Vec<AllocatedBit> = bits - .iter() - .enumerate() - .map(|(i, bit)| { - AllocatedBit::alloc( - cs.namespace(|| format!("bit {}", i)), - Some(bit.unwrap_u8() == 1u8), - ) - }) - .collect::<Result<Vec<AllocatedBit>, SynthesisError>>() - .unwrap(); - Ok(alloc_bits) - } -} - -impl<F> StepCircuit<F> for EcdsaCircuit<F> -where - F: PrimeField<Repr = [u8; 32]> + 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<CS: ConstraintSystem<F>>( - &self, - cs: &mut CS, - _z: &[AllocatedNum<F>], - ) -> Result<Vec<AllocatedNum<F>>, 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<F> { - vec![ - self.r.x, self.r.y, self.g.x, self.g.y, self.pk.x, self.pk.y, self.c, self.s, - ] - } -}
D examples/ecdsa/main.rs

@@ -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<G2>; -type S2 = nova_snark::spartan_with_ipa_pc::RelaxedR1CSSNARK<G1>; - -#[derive(Debug, Clone, Copy)] -pub struct SecretKey(pub <G1 as Group>::Scalar); - -impl SecretKey { - pub fn random(mut rng: impl RngCore) -> Self { - let secret = <G1 as Group>::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: <G1 as Group>::Scalar, -} - -impl SecretKey { - pub fn sign(self, c: <G1 as Group>::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<B: AsRef<[u64]>>( - s: &<G1 as Group>::Scalar, - bits: BitIterator<B>, - ) -> <G1 as Group>::Scalar { - let mut x = <G1 as Group>::Scalar::zero(); - for bit in bits { - x.double(); - - if bit { - x.add_assign(s) - } - } - x - } - - fn to_uniform(digest: &[u8]) -> <G1 as Group>::Scalar { - assert_eq!(digest.len(), 64); - let mut bits: [u64; 8] = [0; 8]; - LittleEndian::read_u64_into(digest, &mut bits); - Self::mul_bits(&<G1 as Group>::Scalar::one(), BitIterator::new(bits)) - } - - pub fn to_uniform_32(digest: &[u8]) -> <G1 as Group>::Scalar { - assert_eq!(digest.len(), 32); - let mut bits: [u64; 4] = [0; 4]; - LittleEndian::read_u64_into(digest, &mut bits); - Self::mul_bits(&<G1 as Group>::Scalar::one(), BitIterator::new(bits)) - } - - pub fn hash_to_scalar(persona: &[u8], a: &[u8], b: &[u8]) -> <G1 as Group>::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: <G1 as Group>::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() -> <G1 as Group>::Scalar { - let mut bits = <G1 as Group>::Scalar::char_le_bits().to_bitvec(); - let mut acc = BigUint::new(Vec::<u32>::new()); - while let Some(b) = bits.pop() { - acc <<= 1_i32; - acc += b as u8; - } - let modulus = acc.to_str_radix(10); - <G1 as Group>::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 <= <G1 as Group>::Scalar::CAPACITY as usize); - - // produce public parameters - println!("Generating public parameters..."); - - let circuit_primary = EcdsaCircuit::<<G2 as Nova_Group>::Scalar> { - r: Coordinate::new( - <G2 as Nova_Group>::Scalar::zero(), - <G2 as Nova_Group>::Scalar::zero(), - ), - g: Coordinate::new( - <G2 as Nova_Group>::Scalar::zero(), - <G2 as Nova_Group>::Scalar::zero(), - ), - pk: Coordinate::new( - <G2 as Nova_Group>::Scalar::zero(), - <G2 as Nova_Group>::Scalar::zero(), - ), - c: <G2 as Nova_Group>::Scalar::zero(), - s: <G2 as Nova_Group>::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<<G2 as Group>::Scalar>, - TrivialTestCircuit<<G1 as Group>::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<u8> = 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::< - <G1 as Nova_Group>::Base, - <G1 as Nova_Group>::Scalar, - >::new( - Coordinate::<<G1 as Nova_Group>::Base>::new(*pkxy.x(), *pkxy.y()), - Coordinate::<<G1 as Nova_Group>::Base>::new(*rxy.x(), *rxy.y()), - s, - c, - Coordinate::<<G1 as Nova_Group>::Base>::new(*gxy.x(), *gxy.y()), - )); - } - signatures - }; - - let (z0_primary, circuits_primary) = EcdsaCircuit::<<G2 as Nova_Group>::Scalar>::new::< - <G1 as Nova_Group>::Base, - <G1 as Nova_Group>::Scalar, - >(num_steps, &signatures()); - - // Secondary circuit - let z0_secondary = vec![<G1 as Group>::Scalar::zero()]; - - // produce a recursive SNARK - println!("Generating a RecursiveSNARK..."); - - type C1 = EcdsaCircuit<<G2 as Nova_Group>::Scalar>; - type C2 = TrivialTestCircuit<<G1 as Nova_Group>::Scalar>; - - let mut recursive_snark: Option<RecursiveSNARK<G2, G1, C1, C2>> = 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()); -}
D examples/ecdsa/utils.rs

@@ -1,29 +0,0 @@

-#[derive(Debug)] -pub struct BitIterator<E> { - t: E, - n: usize, -} - -impl<E: AsRef<[u64]>> BitIterator<E> { - pub fn new(t: E) -> Self { - let n = t.as_ref().len() * 64; - - BitIterator { t, n } - } -} - -impl<E: AsRef<[u64]>> Iterator for BitIterator<E> { - type Item = bool; - - fn next(&mut self) -> Option<bool> { - 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) - } - } -}
A 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: Group>(G::Scalar); + +impl<G> SecretKey<G> +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: Group>(G); + +impl<G> PublicKey<G> +where + G: Group, +{ + pub fn from_secret_key(s: &SecretKey<G>) -> Self { + let point = G::generator() * s.0; + Self(point) + } +} + +#[derive(Clone)] +pub struct Signature<G: Group> { + pub r: G, + pub s: G::Scalar, +} + +impl<G> SecretKey<G> +where + G: Group, +{ + pub fn sign(self, c: G::Scalar, mut rng: impl RngCore) -> Signature<G> { + // 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<B: AsRef<[u64]>>(s: &G::Scalar, bits: BitIterator<B>) -> 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<G> PublicKey<G> +where + G: Group, + G::Scalar: PrimeFieldBits, +{ + pub fn verify(&self, c: G::Scalar, signature: &Signature<G>) -> 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::<u32>::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<E> { + t: E, + n: usize, +} + +impl<E: AsRef<[u64]>> BitIterator<E> { + pub fn new(t: E) -> Self { + let n = t.as_ref().len() * 64; + + BitIterator { t, n } + } +} + +impl<E: AsRef<[u64]>> Iterator for BitIterator<E> { + type Item = bool; + + fn next(&mut self) -> Option<bool> { + 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<F: PrimeField, CS: ConstraintSystem<F>>( + cs: &mut CS, + bits: Option<Vec<bool>>, +) -> Result<Vec<AllocatedBit>, 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::<Result<Vec<AllocatedBit>, SynthesisError>>() +} + +pub fn verify_signature<F: PrimeField + PrimeFieldBits, CS: ConstraintSystem<F>>( + cs: &mut CS, + pk: AllocatedPoint<F>, + r: AllocatedPoint<F>, + s_bits: Vec<AllocatedBit>, + c_bits: Vec<AllocatedBit>, +) -> 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::<<G1 as Group>::Scalar>::new(); + assert!(cs.is_satisfied()); + assert_eq!(cs.num_constraints(), 0); + + let sk = SecretKey::<G2>::random(&mut OsRng); + let pk = PublicKey::from_secret_key(&sk); + + // generate a random message to sign + let c = <G2 as Group>::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::<Vec<bool>>(); + + 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::<Vec<bool>>(); + + 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()); +}