@ -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,
|
|
||||
]
|
|
||||
}
|
|
||||
}
|
|
@ -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());
|
|
||||
}
|
|
@ -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)
|
|
||||
}
|
|
||||
}
|
|
||||
}
|
|
@ -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());
|
||||
|
}
|