Browse Source

Initial commit to extract crypto-primitives to new crate

master
Pratyush Mishra 5 years ago
parent
commit
a244e719d1
33 changed files with 4746 additions and 1 deletions
  1. +1
    -1
      Cargo.toml
  2. +29
    -0
      crypto-primitives/Cargo.toml
  3. +169
    -0
      crypto-primitives/src/commitment/blake2s/constraints.rs
  4. +30
    -0
      crypto-primitives/src/commitment/blake2s/mod.rs
  5. +23
    -0
      crypto-primitives/src/commitment/constraints.rs
  6. +59
    -0
      crypto-primitives/src/commitment/injective_map/constraints.rs
  7. +47
    -0
      crypto-primitives/src/commitment/injective_map/mod.rs
  8. +30
    -0
      crypto-primitives/src/commitment/mod.rs
  9. +244
    -0
      crypto-primitives/src/commitment/pedersen/constraints.rs
  10. +123
    -0
      crypto-primitives/src/commitment/pedersen/mod.rs
  11. +25
    -0
      crypto-primitives/src/crh/constraints.rs
  12. +115
    -0
      crypto-primitives/src/crh/injective_map/constraints.rs
  13. +76
    -0
      crypto-primitives/src/crh/injective_map/mod.rs
  14. +24
    -0
      crypto-primitives/src/crh/mod.rs
  15. +193
    -0
      crypto-primitives/src/crh/pedersen/constraints.rs
  16. +141
    -0
      crypto-primitives/src/crh/pedersen/mod.rs
  17. +46
    -0
      crypto-primitives/src/lib.rs
  18. +512
    -0
      crypto-primitives/src/mht/constraints-old.rs
  19. +353
    -0
      crypto-primitives/src/mht/constraints.rs
  20. +437
    -0
      crypto-primitives/src/mht/mod.rs
  21. +22
    -0
      crypto-primitives/src/nizk/constraints.rs
  22. +543
    -0
      crypto-primitives/src/nizk/gm17/constraints.rs
  23. +81
    -0
      crypto-primitives/src/nizk/gm17/mod.rs
  24. +112
    -0
      crypto-primitives/src/nizk/mod.rs
  25. +650
    -0
      crypto-primitives/src/prf/blake2s/constraints.rs
  26. +28
    -0
      crypto-primitives/src/prf/blake2s/mod.rs
  27. +19
    -0
      crypto-primitives/src/prf/constraints.rs
  28. +20
    -0
      crypto-primitives/src/prf/mod.rs
  29. +18
    -0
      crypto-primitives/src/signature/constraints.rs
  30. +106
    -0
      crypto-primitives/src/signature/mod.rs
  31. +210
    -0
      crypto-primitives/src/signature/schnorr/constraints.rs
  32. +223
    -0
      crypto-primitives/src/signature/schnorr/mod.rs
  33. +37
    -0
      r1cs-std/src/bits/mod.rs

+ 1
- 1
Cargo.toml

@ -1,6 +1,6 @@
[workspace]
members = [ "algebra", "ff-fft", "r1cs-core", "r1cs-std", "gm17", "dpc", "bench-utils" ]
members = [ "algebra", "ff-fft", "r1cs-core", "r1cs-std", "gm17", "crypto-primitives", "dpc", "bench-utils" ]
[profile.release]
opt-level = 3

+ 29
- 0
crypto-primitives/Cargo.toml

@ -0,0 +1,29 @@
[package]
name = "crypto-primitives"
version = "0.1.0"
authors = ["Pratyush Mishra <pratyushmishra@berkeley.edu>"]
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
algebra = { path = "../algebra" }
r1cs-core = { path = "../r1cs-core", optional = true }
r1cs-std = { path = "../r1cs-std", optional = true }
gm17 = { path = "../gm17", optional = true }
bench-utils = { path = "../bench-utils" }
digest = "0.7"
blake2 = "0.7"
rand = { version = "0.7" }
derivative = "1"
rayon = "1"
[features]
r1cs = [ "r1cs-core", "r1cs-std" ]
[dev-dependencies]
criterion = "0.2"
rand_xorshift = { version = "0.2" }

+ 169
- 0
crypto-primitives/src/commitment/blake2s/constraints.rs

@ -0,0 +1,169 @@
use crate::crypto_primitives::commitment::blake2s::Blake2sCommitment;
use r1cs_core::{ConstraintSystem, SynthesisError};
use crate::gadgets::{
prf::blake2s::{blake2s_gadget, Blake2sOutputGadget},
CommitmentGadget,
};
use algebra::{PrimeField, Field};
use r1cs_std::prelude::*;
use std::borrow::Borrow;
#[derive(Clone)]
pub struct Blake2sParametersGadget;
#[derive(Clone)]
pub struct Blake2sRandomnessGadget(pub Vec<UInt8>);
pub struct Blake2sCommitmentGadget;
impl<ConstraintF: PrimeField> CommitmentGadget<Blake2sCommitment, ConstraintF> for Blake2sCommitmentGadget {
type OutputGadget = Blake2sOutputGadget;
type ParametersGadget = Blake2sParametersGadget;
type RandomnessGadget = Blake2sRandomnessGadget;
fn check_commitment_gadget<CS: ConstraintSystem<ConstraintF>>(
mut cs: CS,
_: &Self::ParametersGadget,
input: &[UInt8],
r: &Self::RandomnessGadget,
) -> Result<Self::OutputGadget, SynthesisError> {
let mut input_bits = Vec::with_capacity(512);
for byte in input.into_iter().chain(r.0.iter()) {
input_bits.extend_from_slice(&byte.into_bits_le());
}
let mut result = Vec::new();
for (i, int) in blake2s_gadget(cs.ns(|| "Blake2s Eval"), &input_bits)?
.into_iter()
.enumerate()
{
let chunk = int.to_bytes(&mut cs.ns(|| format!("Result ToBytes {}", i)))?;
result.extend_from_slice(&chunk);
}
Ok(Blake2sOutputGadget(result))
}
}
impl<ConstraintF: Field> AllocGadget<(), ConstraintF> for Blake2sParametersGadget {
fn alloc<F, T, CS: ConstraintSystem<ConstraintF>>(_: CS, _: F) -> Result<Self, SynthesisError>
where
F: FnOnce() -> Result<T, SynthesisError>,
T: Borrow<()>,
{
Ok(Blake2sParametersGadget)
}
fn alloc_input<F, T, CS: ConstraintSystem<ConstraintF>>(_: CS, _: F) -> Result<Self, SynthesisError>
where
F: FnOnce() -> Result<T, SynthesisError>,
T: Borrow<()>,
{
Ok(Blake2sParametersGadget)
}
}
impl<ConstraintF: PrimeField> AllocGadget<[u8; 32], ConstraintF> for Blake2sRandomnessGadget {
#[inline]
fn alloc<F, T, CS: ConstraintSystem<ConstraintF>>(cs: CS, value_gen: F) -> Result<Self, SynthesisError>
where
F: FnOnce() -> Result<T, SynthesisError>,
T: Borrow<[u8; 32]>,
{
let zeros = [0u8; 32];
let value = match value_gen() {
Ok(val) => *(val.borrow()),
Err(_) => zeros,
};
let bytes = <UInt8>::alloc_vec(cs, &value)?;
Ok(Blake2sRandomnessGadget(bytes))
}
#[inline]
fn alloc_input<F, T, CS: ConstraintSystem<ConstraintF>>(
cs: CS,
value_gen: F,
) -> Result<Self, SynthesisError>
where
F: FnOnce() -> Result<T, SynthesisError>,
T: Borrow<[u8; 32]>,
{
let zeros = [0u8; 32];
let value = match value_gen() {
Ok(val) => *(val.borrow()),
Err(_) => zeros,
};
let bytes = <UInt8>::alloc_input_vec(cs, &value)?;
Ok(Blake2sRandomnessGadget(bytes))
}
}
#[cfg(test)]
mod test {
use algebra::fields::bls12_381::Fr;
use rand::{thread_rng, Rng};
use crate::{
crypto_primitives::commitment::{blake2s::Blake2sCommitment, CommitmentScheme},
gadgets::commitment::{
blake2s::{Blake2sCommitmentGadget, Blake2sRandomnessGadget},
CommitmentGadget,
},
};
use r1cs_core::ConstraintSystem;
use r1cs_std::prelude::*;
use r1cs_std::test_constraint_system::TestConstraintSystem;
#[test]
fn commitment_gadget_test() {
let mut cs = TestConstraintSystem::<Fr>::new();
let input = [1u8; 32];
let rng = &mut thread_rng();
type TestCOMM = Blake2sCommitment;
type TestCOMMGadget = Blake2sCommitmentGadget;
let mut randomness = [0u8; 32];
rng.fill(&mut randomness);
let parameters = ();
let primitive_result = Blake2sCommitment::commit(&parameters, &input, &randomness).unwrap();
let mut input_bytes = vec![];
for (byte_i, input_byte) in input.into_iter().enumerate() {
let cs = cs.ns(|| format!("input_byte_gadget_{}", byte_i));
input_bytes.push(UInt8::alloc(cs, || Ok(*input_byte)).unwrap());
}
let mut randomness_bytes = vec![];
for (byte_i, random_byte) in randomness.into_iter().enumerate() {
let cs = cs.ns(|| format!("randomness_byte_gadget_{}", byte_i));
randomness_bytes.push(UInt8::alloc(cs, || Ok(*random_byte)).unwrap());
}
let randomness_bytes = Blake2sRandomnessGadget(randomness_bytes);
let gadget_parameters =
<TestCOMMGadget as CommitmentGadget<TestCOMM, Fr>>::ParametersGadget::alloc(
&mut cs.ns(|| "gadget_parameters"),
|| Ok(&parameters),
)
.unwrap();
let gadget_result =
<TestCOMMGadget as CommitmentGadget<TestCOMM, Fr>>::check_commitment_gadget(
&mut cs.ns(|| "gadget_evaluation"),
&gadget_parameters,
&input_bytes,
&randomness_bytes,
)
.unwrap();
for i in 0..32 {
assert_eq!(primitive_result[i], gadget_result.0[i].get_value().unwrap());
}
assert!(cs.is_satisfied());
}
}

+ 30
- 0
crypto-primitives/src/commitment/blake2s/mod.rs

@ -0,0 +1,30 @@
use super::CommitmentScheme;
use blake2::Blake2s as b2s;
use digest::Digest;
use crate::Error;
use rand::Rng;
pub struct Blake2sCommitment;
impl CommitmentScheme for Blake2sCommitment {
type Parameters = ();
type Randomness = [u8; 32];
type Output = [u8; 32];
fn setup<R: Rng>(_: &mut R) -> Result<Self::Parameters, Error> {
Ok(())
}
fn commit(
_: &Self::Parameters,
input: &[u8],
randomness: &Self::Randomness,
) -> Result<Self::Output, Error> {
let mut h = b2s::new();
h.input(input);
h.input(randomness.as_ref());
let mut result = [0u8; 32];
result.copy_from_slice(&h.result());
Ok(result)
}
}

+ 23
- 0
crypto-primitives/src/commitment/constraints.rs

@ -0,0 +1,23 @@
use crate::CommitmentScheme;
use algebra::Field;
use r1cs_core::{ConstraintSystem, SynthesisError};
use r1cs_std::prelude::*;
use std::fmt::Debug;
pub trait CommitmentGadget<C: CommitmentScheme, ConstraintF: Field> {
type OutputGadget: EqGadget<ConstraintF>
+ ToBytesGadget<ConstraintF>
+ AllocGadget<C::Output, ConstraintF>
+ Clone
+ Sized
+ Debug;
type ParametersGadget: AllocGadget<C::Parameters, ConstraintF> + Clone;
type RandomnessGadget: AllocGadget<C::Randomness, ConstraintF> + Clone;
fn check_commitment_gadget<CS: ConstraintSystem<ConstraintF>>(
cs: CS,
parameters: &Self::ParametersGadget,
input: &[UInt8],
r: &Self::RandomnessGadget,
) -> Result<Self::OutputGadget, SynthesisError>;
}

+ 59
- 0
crypto-primitives/src/commitment/injective_map/constraints.rs

@ -0,0 +1,59 @@
use algebra::{Field, PrimeField};
use crate::commitment::{
injective_map::{InjectiveMap, PedersenCommCompressor},
pedersen::PedersenWindow,
pedersen::constraints::{
PedersenCommitmentGadget, PedersenCommitmentGadgetParameters, PedersenRandomnessGadget,
},
CommitmentGadget,
};
pub use crate::crh::injective_map::constraints::InjectiveMapGadget;
use algebra::groups::Group;
use r1cs_core::{ConstraintSystem, SynthesisError};
use r1cs_std::{groups::GroupGadget, uint8::UInt8};
use std::marker::PhantomData;
pub struct PedersenCommitmentCompressorGadget<
G: Group,
I: InjectiveMap<G>,
ConstraintF: Field,
GG: GroupGadget<G, ConstraintF>,
IG: InjectiveMapGadget<G, I, ConstraintF, GG>,
> {
_compressor: PhantomData<I>,
_compressor_gadget: PhantomData<IG>,
_crh: PedersenCommitmentGadget<G, ConstraintF, GG>,
}
impl<G, I, ConstraintF, GG, IG, W> CommitmentGadget<PedersenCommCompressor<G, I, W>, ConstraintF>
for PedersenCommitmentCompressorGadget<G, I, ConstraintF, GG, IG>
where
G: Group,
I: InjectiveMap<G>,
ConstraintF: PrimeField,
GG: GroupGadget<G, ConstraintF>,
IG: InjectiveMapGadget<G, I, ConstraintF, GG>,
W: PedersenWindow,
{
type OutputGadget = IG::OutputGadget;
type ParametersGadget = PedersenCommitmentGadgetParameters<G, W, ConstraintF>;
type RandomnessGadget = PedersenRandomnessGadget;
fn check_commitment_gadget<CS: ConstraintSystem<ConstraintF>>(
mut cs: CS,
parameters: &Self::ParametersGadget,
input: &[UInt8],
r: &Self::RandomnessGadget,
) -> Result<Self::OutputGadget, SynthesisError> {
let result = PedersenCommitmentGadget::<G, ConstraintF, GG>::check_commitment_gadget(
cs.ns(|| "PedersenComm"),
parameters,
input,
r,
)?;
IG::evaluate_map(cs.ns(|| "InjectiveMap"), &result)
}
}

+ 47
- 0
crypto-primitives/src/commitment/injective_map/mod.rs

@ -0,0 +1,47 @@
use crate::Error;
use rand::Rng;
use std::marker::PhantomData;
use super::{
pedersen::{PedersenCommitment, PedersenParameters, PedersenRandomness, PedersenWindow},
CommitmentScheme,
};
pub use crate::crh::injective_map::InjectiveMap;
use algebra::groups::Group;
#[cfg(feature = "r1cs")]
pub mod constraints;
pub struct PedersenCommCompressor<G: Group, I: InjectiveMap<G>, W: PedersenWindow> {
_group: PhantomData<G>,
_compressor: PhantomData<I>,
_comm: PedersenCommitment<G, W>,
}
impl<G: Group, I: InjectiveMap<G>, W: PedersenWindow> CommitmentScheme
for PedersenCommCompressor<G, I, W>
{
type Output = I::Output;
type Parameters = PedersenParameters<G>;
type Randomness = PedersenRandomness<G>;
fn setup<R: Rng>(rng: &mut R) -> Result<Self::Parameters, Error> {
let time = start_timer!(|| format!("PedersenCompressor::Setup"));
let params = PedersenCommitment::<G, W>::setup(rng);
end_timer!(time);
params
}
fn commit(
parameters: &Self::Parameters,
input: &[u8],
randomness: &Self::Randomness,
) -> Result<Self::Output, Error> {
let eval_time = start_timer!(|| "PedersenCompressor::Eval");
let result = I::injective_map(&PedersenCommitment::<G, W>::commit(
parameters, input, randomness,
)?)?;
end_timer!(eval_time);
Ok(result)
}
}

+ 30
- 0
crypto-primitives/src/commitment/mod.rs

@ -0,0 +1,30 @@
use rand::Rng;
use algebra::UniformRand;
use std::{fmt::Debug, hash::Hash};
use algebra::bytes::ToBytes;
pub mod blake2s;
pub mod injective_map;
pub mod pedersen;
#[cfg(feature = "r1cs")]
pub mod constraints;
#[cfg(feature = "r1cs")]
pub use constraints::*;
use crate::Error;
pub trait CommitmentScheme {
type Output: ToBytes + Clone + Default + Eq + Hash + Debug;
type Parameters: Clone;
type Randomness: Clone + ToBytes + Default + Eq + UniformRand + Debug;
fn setup<R: Rng>(r: &mut R) -> Result<Self::Parameters, Error>;
fn commit(
parameters: &Self::Parameters,
input: &[u8],
r: &Self::Randomness,
) -> Result<Self::Output, Error>;
}

+ 244
- 0
crypto-primitives/src/commitment/pedersen/constraints.rs

@ -0,0 +1,244 @@
use crate::{
commitment::pedersen::{PedersenCommitment, PedersenParameters, PedersenRandomness},
crh::pedersen::PedersenWindow,
};
use algebra::{to_bytes, Group, ToBytes};
use r1cs_core::{ConstraintSystem, SynthesisError};
use crate::commitment::CommitmentGadget;
use algebra::fields::{Field, PrimeField};
use r1cs_std::prelude::*;
use std::{borrow::Borrow, marker::PhantomData};
#[derive(Derivative)]
#[derivative(Clone(bound = "G: Group, W: PedersenWindow, ConstraintF: Field"))]
pub struct PedersenCommitmentGadgetParameters<G: Group, W: PedersenWindow, ConstraintF: Field> {
params: PedersenParameters<G>,
#[doc(hidden)]
_group: PhantomData<G>,
#[doc(hidden)]
_engine: PhantomData<ConstraintF>,
#[doc(hidden)]
_window: PhantomData<W>,
}
#[derive(Clone, Debug)]
pub struct PedersenRandomnessGadget(Vec<UInt8>);
pub struct PedersenCommitmentGadget<G: Group, ConstraintF: Field, GG: GroupGadget<G, ConstraintF>>(
#[doc(hidden)]
PhantomData<*const G>,
#[doc(hidden)]
PhantomData<*const GG>,
PhantomData<ConstraintF>,
);
impl<ConstraintF, G, GG, W> CommitmentGadget<PedersenCommitment<G, W>, ConstraintF>
for PedersenCommitmentGadget<G, ConstraintF, GG>
where
ConstraintF: PrimeField,
G: Group,
GG: GroupGadget<G, ConstraintF>,
W: PedersenWindow,
{
type OutputGadget = GG;
type ParametersGadget = PedersenCommitmentGadgetParameters<G, W, ConstraintF>;
type RandomnessGadget = PedersenRandomnessGadget;
fn check_commitment_gadget<CS: ConstraintSystem<ConstraintF>>(
mut cs: CS,
parameters: &Self::ParametersGadget,
input: &[UInt8],
r: &Self::RandomnessGadget,
) -> Result<Self::OutputGadget, SynthesisError> {
assert!((input.len() * 8) <= (W::WINDOW_SIZE * W::NUM_WINDOWS));
let mut padded_input = input.to_vec();
// Pad if input length is less than `W::WINDOW_SIZE * W::NUM_WINDOWS`.
if (input.len() * 8) < W::WINDOW_SIZE * W::NUM_WINDOWS {
let current_length = input.len();
for _ in current_length..((W::WINDOW_SIZE * W::NUM_WINDOWS) / 8) {
padded_input.push(UInt8::constant(0u8));
}
}
assert_eq!(padded_input.len() * 8, W::WINDOW_SIZE * W::NUM_WINDOWS);
assert_eq!(parameters.params.generators.len(), W::NUM_WINDOWS);
// Allocate new variable for commitment output.
let input_in_bits: Vec<_> = padded_input
.iter()
.flat_map(|byte| byte.into_bits_le())
.collect();
let input_in_bits = input_in_bits.chunks(W::WINDOW_SIZE);
let mut result = GG::precomputed_base_multiscalar_mul(
cs.ns(|| "multiexp"),
&parameters.params.generators,
input_in_bits,
)?;
// Compute h^r
let rand_bits: Vec<_> = r.0.iter().flat_map(|byte| byte.into_bits_le()).collect();
result.precomputed_base_scalar_mul(
cs.ns(|| "Randomizer"),
rand_bits
.iter()
.zip(&parameters.params.randomness_generator),
)?;
Ok(result)
}
}
impl<G, W, ConstraintF> AllocGadget<PedersenParameters<G>, ConstraintF> for PedersenCommitmentGadgetParameters<G, W, ConstraintF>
where
G: Group,
W: PedersenWindow,
ConstraintF: PrimeField,
{
fn alloc<F, T, CS: ConstraintSystem<ConstraintF>>(_cs: CS, value_gen: F) -> Result<Self, SynthesisError>
where
F: FnOnce() -> Result<T, SynthesisError>,
T: Borrow<PedersenParameters<G>>,
{
let temp = value_gen()?;
let parameters = temp.borrow().clone();
Ok(PedersenCommitmentGadgetParameters {
params: parameters,
_group: PhantomData,
_engine: PhantomData,
_window: PhantomData,
})
}
fn alloc_input<F, T, CS: ConstraintSystem<ConstraintF>>(
_cs: CS,
value_gen: F,
) -> Result<Self, SynthesisError>
where
F: FnOnce() -> Result<T, SynthesisError>,
T: Borrow<PedersenParameters<G>>,
{
let temp = value_gen()?;
let parameters = temp.borrow().clone();
Ok(PedersenCommitmentGadgetParameters {
params: parameters,
_group: PhantomData,
_engine: PhantomData,
_window: PhantomData,
})
}
}
impl<G, ConstraintF> AllocGadget<PedersenRandomness<G>, ConstraintF> for PedersenRandomnessGadget
where
G: Group,
ConstraintF: PrimeField,
{
fn alloc<F, T, CS: ConstraintSystem<ConstraintF>>(cs: CS, value_gen: F) -> Result<Self, SynthesisError>
where
F: FnOnce() -> Result<T, SynthesisError>,
T: Borrow<PedersenRandomness<G>>,
{
let temp = value_gen()?;
let randomness = to_bytes![temp.borrow().0].unwrap();
Ok(PedersenRandomnessGadget(UInt8::alloc_vec(cs, &randomness)?))
}
fn alloc_input<F, T, CS: ConstraintSystem<ConstraintF>>(
cs: CS,
value_gen: F,
) -> Result<Self, SynthesisError>
where
F: FnOnce() -> Result<T, SynthesisError>,
T: Borrow<PedersenRandomness<G>>,
{
let temp = value_gen()?;
let randomness = to_bytes![temp.borrow().0].unwrap();
Ok(PedersenRandomnessGadget(UInt8::alloc_input_vec(
cs,
&randomness,
)?))
}
}
#[cfg(test)]
mod test {
use algebra::{fields::jubjub::{fq::Fq, fr::Fr}};
use rand::thread_rng;
use algebra::UniformRand;
use crate::{
commitment::{
pedersen::{PedersenCommitment, PedersenRandomness, constraints::PedersenCommitmentGadget},
CommitmentScheme,
CommitmentGadget,
},
crh::pedersen::PedersenWindow,
};
use algebra::curves::{jubjub::JubJubProjective as JubJub, ProjectiveCurve};
use r1cs_core::ConstraintSystem;
use r1cs_std::{
groups::jubjub::JubJubGadget, test_constraint_system::TestConstraintSystem, prelude::*,
};
#[test]
fn commitment_gadget_test() {
let mut cs = TestConstraintSystem::<Fq>::new();
#[derive(Clone, PartialEq, Eq, Hash)]
pub(super) struct Window;
impl PedersenWindow for Window {
const WINDOW_SIZE: usize = 4;
const NUM_WINDOWS: usize = 8;
}
let input = [1u8; 4];
let rng = &mut thread_rng();
type TestCOMM = PedersenCommitment<JubJub, Window>;
type TestCOMMGadget = PedersenCommitmentGadget<JubJub, Fq, JubJubGadget>;
let randomness = PedersenRandomness(Fr::rand(rng));
let parameters = PedersenCommitment::<JubJub, Window>::setup(rng).unwrap();
let primitive_result =
PedersenCommitment::<JubJub, Window>::commit(&parameters, &input, &randomness).unwrap();
let mut input_bytes = vec![];
for (byte_i, input_byte) in input.into_iter().enumerate() {
let cs = cs.ns(|| format!("input_byte_gadget_{}", byte_i));
input_bytes.push(UInt8::alloc(cs, || Ok(*input_byte)).unwrap());
}
let randomness =
<TestCOMMGadget as CommitmentGadget<TestCOMM, Fq>>::RandomnessGadget::alloc(
&mut cs.ns(|| "gadget_randomness"),
|| Ok(&randomness),
)
.unwrap();
let gadget_parameters =
<TestCOMMGadget as CommitmentGadget<TestCOMM, Fq>>::ParametersGadget::alloc(
&mut cs.ns(|| "gadget_parameters"),
|| Ok(&parameters),
)
.unwrap();
let gadget_result =
<TestCOMMGadget as CommitmentGadget<TestCOMM, Fq>>::check_commitment_gadget(
&mut cs.ns(|| "gadget_evaluation"),
&gadget_parameters,
&input_bytes,
&randomness,
)
.unwrap();
let primitive_result = primitive_result.into_affine();
assert_eq!(primitive_result.x, gadget_result.x.value.unwrap());
assert_eq!(primitive_result.y, gadget_result.y.value.unwrap());
assert!(cs.is_satisfied());
}
}

+ 123
- 0
crypto-primitives/src/commitment/pedersen/mod.rs

@ -0,0 +1,123 @@
use crate::Error;
use algebra::UniformRand;
use rand::Rng;
use std::marker::PhantomData;
use super::CommitmentScheme;
use algebra::{bytes::ToBytes, groups::Group, BitIterator, FpParameters, PrimeField};
use std::io::{Result as IoResult, Write};
pub use crate::crh::pedersen::PedersenWindow;
use crate::crh::{
pedersen::{PedersenCRH, PedersenParameters as PedersenCRHParameters},
FixedLengthCRH,
};
#[cfg(feature = "r1cs")]
pub mod constraints;
#[derive(Clone)]
pub struct PedersenParameters<G: Group> {
pub randomness_generator: Vec<G>,
pub generators: Vec<Vec<G>>,
}
pub struct PedersenCommitment<G: Group, W: PedersenWindow> {
group: PhantomData<G>,
window: PhantomData<W>,
}
#[derive(Derivative)]
#[derivative(
Clone(bound = "G: Group"),
PartialEq(bound = "G: Group"),
Debug(bound = "G: Group"),
Eq(bound = "G: Group"),
Default(bound = "G: Group")
)]
pub struct PedersenRandomness<G: Group>(pub G::ScalarField);
impl<G: Group> UniformRand for PedersenRandomness<G> {
#[inline]
fn rand<R: Rng + ?Sized>(rng: &mut R) -> Self {
PedersenRandomness(UniformRand::rand(rng))
}
}
impl<G: Group> ToBytes for PedersenRandomness<G> {
fn write<W: Write>(&self, writer: W) -> IoResult<()> {
self.0.write(writer)
}
}
impl<G: Group, W: PedersenWindow> CommitmentScheme for PedersenCommitment<G, W> {
type Parameters = PedersenParameters<G>;
type Randomness = PedersenRandomness<G>;
type Output = G;
fn setup<R: Rng>(rng: &mut R) -> Result<Self::Parameters, Error> {
let time = start_timer!(|| format!(
"PedersenCOMM::Setup: {} {}-bit windows; {{0,1}}^{{{}}} -> G",
W::NUM_WINDOWS,
W::WINDOW_SIZE,
W::NUM_WINDOWS * W::WINDOW_SIZE
));
let num_powers = <G::ScalarField as PrimeField>::Params::MODULUS_BITS as usize;
let randomness_generator = PedersenCRH::<_, W>::generator_powers(num_powers, rng);
let generators = PedersenCRH::<_, W>::create_generators(rng);
end_timer!(time);
Ok(Self::Parameters {
randomness_generator,
generators,
})
}
fn commit(
parameters: &Self::Parameters,
input: &[u8],
randomness: &Self::Randomness,
) -> Result<Self::Output, Error> {
let commit_time = start_timer!(|| "PedersenCOMM::Commit");
// If the input is too long, return an error.
if input.len() > W::WINDOW_SIZE * W::NUM_WINDOWS {
panic!("incorrect input length: {:?}", input.len());
}
// Pad the input to the necessary length.
let mut padded_input = Vec::with_capacity(input.len());
let mut input = input;
if (input.len() * 8) < W::WINDOW_SIZE * W::NUM_WINDOWS {
let current_length = input.len();
padded_input.extend_from_slice(input);
for _ in current_length..((W::WINDOW_SIZE * W::NUM_WINDOWS) / 8) {
padded_input.push(0u8);
}
input = padded_input.as_slice();
}
assert_eq!(parameters.generators.len(), W::NUM_WINDOWS);
// Invoke Pedersen CRH here, to prevent code duplication.
let crh_parameters = PedersenCRHParameters {
generators: parameters.generators.clone(),
};
let mut result = PedersenCRH::<_, W>::evaluate(&crh_parameters, &input)?;
let randomize_time = start_timer!(|| "Randomize");
// Compute h^r.
let mut scalar_bits = BitIterator::new(randomness.0.into_repr()).collect::<Vec<_>>();
scalar_bits.reverse();
for (bit, power) in scalar_bits
.into_iter()
.zip(&parameters.randomness_generator)
{
if bit {
result += power
}
}
end_timer!(randomize_time);
end_timer!(commit_time);
Ok(result)
}
}

+ 25
- 0
crypto-primitives/src/crh/constraints.rs

@ -0,0 +1,25 @@
use algebra::Field;
use std::fmt::Debug;
use crate::crh::FixedLengthCRH;
use r1cs_core::{ConstraintSystem, SynthesisError};
use r1cs_std::prelude::*;
pub trait FixedLengthCRHGadget<H: FixedLengthCRH, ConstraintF: Field>: Sized {
type OutputGadget: ConditionalEqGadget<ConstraintF>
+ EqGadget<ConstraintF>
+ ToBytesGadget<ConstraintF>
+ CondSelectGadget<ConstraintF>
+ AllocGadget<H::Output, ConstraintF>
+ Debug
+ Clone
+ Sized;
type ParametersGadget: AllocGadget<H::Parameters, ConstraintF> + Clone;
fn check_evaluation_gadget<CS: ConstraintSystem<ConstraintF>>(
cs: CS,
parameters: &Self::ParametersGadget,
input: &[UInt8],
) -> Result<Self::OutputGadget, SynthesisError>;
}

+ 115
- 0
crypto-primitives/src/crh/injective_map/constraints.rs

@ -0,0 +1,115 @@
use std::{fmt::Debug, marker::PhantomData};
use crate::crh::{
FixedLengthCRHGadget,
injective_map::{InjectiveMap, PedersenCRHCompressor, TECompressor},
pedersen::{
PedersenWindow,
constraints::{PedersenCRHGadget, PedersenCRHGadgetParameters},
}
};
use algebra::{
curves::{
models::{ModelParameters, TEModelParameters},
twisted_edwards_extended::{GroupAffine as TEAffine, GroupProjective as TEProjective},
},
fields::{Field, PrimeField, SquareRootField},
groups::Group,
};
use r1cs_core::{ConstraintSystem, SynthesisError};
use r1cs_std::{
fields::fp::FpGadget,
groups::{curves::twisted_edwards::AffineGadget as TwistedEdwardsGadget, GroupGadget},
prelude::*,
};
pub trait InjectiveMapGadget<G: Group, I: InjectiveMap<G>, ConstraintF: Field, GG: GroupGadget<G, ConstraintF>>
{
type OutputGadget: EqGadget<ConstraintF>
+ ToBytesGadget<ConstraintF>
+ CondSelectGadget<ConstraintF>
+ AllocGadget<I::Output, ConstraintF>
+ Debug
+ Clone
+ Sized;
fn evaluate_map<CS: ConstraintSystem<ConstraintF>>(
cs: CS,
ge: &GG,
) -> Result<Self::OutputGadget, SynthesisError>;
}
pub struct TECompressorGadget;
impl<ConstraintF, P> InjectiveMapGadget<TEAffine<P>, TECompressor, ConstraintF, TwistedEdwardsGadget<P, ConstraintF, FpGadget<ConstraintF>>>
for TECompressorGadget
where
ConstraintF: PrimeField + SquareRootField,
P: TEModelParameters + ModelParameters<BaseField = ConstraintF>,
{
type OutputGadget = FpGadget<ConstraintF>;
fn evaluate_map<CS: ConstraintSystem<ConstraintF>>(
_cs: CS,
ge: &TwistedEdwardsGadget<P, ConstraintF, FpGadget<ConstraintF>>,
) -> Result<Self::OutputGadget, SynthesisError> {
Ok(ge.x.clone())
}
}
impl<ConstraintF, P>
InjectiveMapGadget<TEProjective<P>, TECompressor, ConstraintF, TwistedEdwardsGadget<P, ConstraintF, FpGadget<ConstraintF>>>
for TECompressorGadget
where
ConstraintF: PrimeField + SquareRootField,
P: TEModelParameters + ModelParameters<BaseField = ConstraintF>,
{
type OutputGadget = FpGadget<ConstraintF>;
fn evaluate_map<CS: ConstraintSystem<ConstraintF>>(
_cs: CS,
ge: &TwistedEdwardsGadget<P, ConstraintF, FpGadget<ConstraintF>>,
) -> Result<Self::OutputGadget, SynthesisError> {
Ok(ge.x.clone())
}
}
pub struct PedersenCRHCompressorGadget<
G: Group,
I: InjectiveMap<G>,
ConstraintF: Field,
GG: GroupGadget<G, ConstraintF>,
IG: InjectiveMapGadget<G, I, ConstraintF, GG>,
> {
_compressor: PhantomData<I>,
_compressor_gadget: PhantomData<IG>,
_crh: PedersenCRHGadget<G, ConstraintF, GG>,
}
impl<G, I, ConstraintF, GG, IG, W> FixedLengthCRHGadget<PedersenCRHCompressor<G, I, W>, ConstraintF>
for PedersenCRHCompressorGadget<G, I, ConstraintF, GG, IG>
where
G: Group,
I: InjectiveMap<G>,
ConstraintF: Field,
GG: GroupGadget<G, ConstraintF>,
IG: InjectiveMapGadget<G, I, ConstraintF, GG>,
W: PedersenWindow,
{
type OutputGadget = IG::OutputGadget;
type ParametersGadget = PedersenCRHGadgetParameters<G, W, ConstraintF, GG>;
fn check_evaluation_gadget<CS: ConstraintSystem<ConstraintF>>(
mut cs: CS,
parameters: &Self::ParametersGadget,
input: &[UInt8],
) -> Result<Self::OutputGadget, SynthesisError> {
let result = PedersenCRHGadget::<G, ConstraintF, GG>::check_evaluation_gadget(
cs.ns(|| "PedCRH"),
parameters,
input,
)?;
IG::evaluate_map(cs.ns(|| "InjectiveMap"), &result)
}
}

+ 76
- 0
crypto-primitives/src/crh/injective_map/mod.rs

@ -0,0 +1,76 @@
use crate::CryptoError;
use algebra::bytes::ToBytes;
use crate::Error;
use rand::Rng;
use std::{fmt::Debug, hash::Hash, marker::PhantomData};
use super::{
pedersen::{PedersenCRH, PedersenParameters, PedersenWindow},
FixedLengthCRH,
};
use algebra::{
curves::{
models::{ModelParameters, TEModelParameters},
twisted_edwards_extended::{GroupAffine as TEAffine, GroupProjective as TEProjective},
ProjectiveCurve,
},
groups::Group,
};
#[cfg(feature = "r1cs")]
pub mod constraints;
pub trait InjectiveMap<G: Group> {
type Output: ToBytes + Clone + Eq + Hash + Default + Debug;
fn injective_map(ge: &G) -> Result<Self::Output, CryptoError>;
}
pub struct TECompressor;
impl<P: TEModelParameters> InjectiveMap<TEAffine<P>> for TECompressor {
type Output = <P as ModelParameters>::BaseField;
fn injective_map(ge: &TEAffine<P>) -> Result<Self::Output, CryptoError> {
debug_assert!(ge.is_in_correct_subgroup_assuming_on_curve());
Ok(ge.x)
}
}
impl<P: TEModelParameters> InjectiveMap<TEProjective<P>> for TECompressor {
type Output = <P as ModelParameters>::BaseField;
fn injective_map(ge: &TEProjective<P>) -> Result<Self::Output, CryptoError> {
let ge = ge.into_affine();
debug_assert!(ge.is_in_correct_subgroup_assuming_on_curve());
Ok(ge.x)
}
}
pub struct PedersenCRHCompressor<G: Group, I: InjectiveMap<G>, W: PedersenWindow> {
_group: PhantomData<G>,
_compressor: PhantomData<I>,
_crh: PedersenCRH<G, W>,
}
impl<G: Group, I: InjectiveMap<G>, W: PedersenWindow> FixedLengthCRH
for PedersenCRHCompressor<G, I, W>
{
const INPUT_SIZE_BITS: usize = PedersenCRH::<G, W>::INPUT_SIZE_BITS;
type Output = I::Output;
type Parameters = PedersenParameters<G>;
fn setup<R: Rng>(rng: &mut R) -> Result<Self::Parameters, Error> {
let time = start_timer!(|| format!("PedersenCRHCompressor::Setup"));
let params = PedersenCRH::<G, W>::setup(rng);
end_timer!(time);
params
}
fn evaluate(parameters: &Self::Parameters, input: &[u8]) -> Result<Self::Output, Error> {
let eval_time = start_timer!(|| "PedersenCRHCompressor::Eval");
let result = I::injective_map(&PedersenCRH::<G, W>::evaluate(parameters, input)?)?;
end_timer!(eval_time);
Ok(result)
}
}

+ 24
- 0
crypto-primitives/src/crh/mod.rs

@ -0,0 +1,24 @@
use algebra::bytes::ToBytes;
use rand::Rng;
use std::hash::Hash;
pub mod injective_map;
pub mod pedersen;
use crate::Error;
#[cfg(feature = "r1cs")]
pub mod constraints;
#[cfg(feature = "r1cs")]
pub use constraints::*;
pub trait FixedLengthCRH {
const INPUT_SIZE_BITS: usize;
type Output: ToBytes + Clone + Eq + Hash + Default;
type Parameters: Clone + Default;
fn setup<R: Rng>(r: &mut R) -> Result<Self::Parameters, Error>;
fn evaluate(parameters: &Self::Parameters, input: &[u8]) -> Result<Self::Output, Error>;
}

+ 193
- 0
crypto-primitives/src/crh/pedersen/constraints.rs

@ -0,0 +1,193 @@
use algebra::Field;
use crate::crh::{
FixedLengthCRHGadget,
pedersen::{PedersenCRH, PedersenParameters, PedersenWindow},
};
use algebra::groups::Group;
use r1cs_core::{ConstraintSystem, SynthesisError};
use r1cs_std::prelude::*;
use std::{borrow::Borrow, marker::PhantomData};
#[derive(Derivative)]
#[derivative(Clone(
bound = "G: Group, W: PedersenWindow, ConstraintF: Field, GG: GroupGadget<G, ConstraintF>"
))]
pub struct PedersenCRHGadgetParameters<
G: Group,
W: PedersenWindow,
ConstraintF: Field,
GG: GroupGadget<G, ConstraintF>,
> {
params: PedersenParameters<G>,
_group_g: PhantomData<GG>,
_engine: PhantomData<ConstraintF>,
_window: PhantomData<W>,
}
pub struct PedersenCRHGadget<G: Group, ConstraintF: Field, GG: GroupGadget<G, ConstraintF>> {
#[doc(hideen)]
_group: PhantomData<*const G>,
#[doc(hideen)]
_group_gadget: PhantomData<*const GG>,
#[doc(hideen)]
_engine: PhantomData<ConstraintF>,
}
impl<ConstraintF, G, GG, W> FixedLengthCRHGadget<PedersenCRH<G, W>, ConstraintF> for PedersenCRHGadget<G, ConstraintF, GG>
where
ConstraintF: Field,
G: Group,
GG: GroupGadget<G, ConstraintF>,
W: PedersenWindow,
{
type OutputGadget = GG;
type ParametersGadget = PedersenCRHGadgetParameters<G, W, ConstraintF, GG>;
fn check_evaluation_gadget<CS: ConstraintSystem<ConstraintF>>(
cs: CS,
parameters: &Self::ParametersGadget,
input: &[UInt8],
) -> Result<Self::OutputGadget, SynthesisError> {
let mut padded_input = input.to_vec();
// Pad the input if it is not the current length.
if input.len() * 8 < W::WINDOW_SIZE * W::NUM_WINDOWS {
let current_length = input.len();
for _ in current_length..(W::WINDOW_SIZE * W::NUM_WINDOWS / 8) {
padded_input.push(UInt8::constant(0u8));
}
}
assert_eq!(padded_input.len() * 8, W::WINDOW_SIZE * W::NUM_WINDOWS);
assert_eq!(parameters.params.generators.len(), W::NUM_WINDOWS);
// Allocate new variable for the result.
let input_in_bits: Vec<_> = padded_input
.iter()
.flat_map(|byte| byte.into_bits_le())
.collect();
let input_in_bits = input_in_bits.chunks(W::WINDOW_SIZE);
let result =
GG::precomputed_base_multiscalar_mul(cs, &parameters.params.generators, input_in_bits)?;
Ok(result)
}
}
impl<G: Group, W: PedersenWindow, ConstraintF: Field, GG: GroupGadget<G, ConstraintF>>
AllocGadget<PedersenParameters<G>, ConstraintF> for PedersenCRHGadgetParameters<G, W, ConstraintF, GG>
{
fn alloc<F, T, CS: ConstraintSystem<ConstraintF>>(_cs: CS, value_gen: F) -> Result<Self, SynthesisError>
where
F: FnOnce() -> Result<T, SynthesisError>,
T: Borrow<PedersenParameters<G>>,
{
let params = value_gen()?.borrow().clone();
Ok(PedersenCRHGadgetParameters {
params,
_group_g: PhantomData,
_engine: PhantomData,
_window: PhantomData,
})
}
fn alloc_input<F, T, CS: ConstraintSystem<ConstraintF>>(
_cs: CS,
value_gen: F,
) -> Result<Self, SynthesisError>
where
F: FnOnce() -> Result<T, SynthesisError>,
T: Borrow<PedersenParameters<G>>,
{
let params = value_gen()?.borrow().clone();
Ok(PedersenCRHGadgetParameters {
params,
_group_g: PhantomData,
_engine: PhantomData,
_window: PhantomData,
})
}
}
#[cfg(test)]
mod test {
use algebra::fields::bls12_381::fr::Fr;
use rand::{thread_rng, Rng};
use crate::crh::{
pedersen::{PedersenCRH, PedersenWindow},
pedersen::constraints::PedersenCRHGadget,
FixedLengthCRH,
FixedLengthCRHGadget
};
use algebra::curves::{jubjub::JubJubProjective as JubJub, ProjectiveCurve};
use r1cs_core::ConstraintSystem;
use r1cs_std::{
groups::curves::twisted_edwards::jubjub::JubJubGadget,
test_constraint_system::TestConstraintSystem, prelude::*,
};
type TestCRH = PedersenCRH<JubJub, Window>;
type TestCRHGadget = PedersenCRHGadget<JubJub, Fr, JubJubGadget>;
#[derive(Clone, PartialEq, Eq, Hash)]
pub(super) struct Window;
impl PedersenWindow for Window {
const WINDOW_SIZE: usize = 128;
const NUM_WINDOWS: usize = 8;
}
fn generate_input<CS: ConstraintSystem<Fr>, R: Rng>(
mut cs: CS,
rng: &mut R,
) -> ([u8; 128], Vec<UInt8>) {
let mut input = [1u8; 128];
rng.fill_bytes(&mut input);
let mut input_bytes = vec![];
for (byte_i, input_byte) in input.into_iter().enumerate() {
let cs = cs.ns(|| format!("input_byte_gadget_{}", byte_i));
input_bytes.push(UInt8::alloc(cs, || Ok(*input_byte)).unwrap());
}
(input, input_bytes)
}
#[test]
fn crh_primitive_gadget_test() {
let rng = &mut thread_rng();
let mut cs = TestConstraintSystem::<Fr>::new();
let (input, input_bytes) = generate_input(&mut cs, rng);
println!("number of constraints for input: {}", cs.num_constraints());
let parameters = TestCRH::setup(rng).unwrap();
let primitive_result = TestCRH::evaluate(&parameters, &input).unwrap();
let gadget_parameters =
<TestCRHGadget as FixedLengthCRHGadget<TestCRH, Fr>>::ParametersGadget::alloc(
&mut cs.ns(|| "gadget_parameters"),
|| Ok(&parameters),
)
.unwrap();
println!(
"number of constraints for input + params: {}",
cs.num_constraints()
);
let gadget_result =
<TestCRHGadget as FixedLengthCRHGadget<TestCRH, Fr>>::check_evaluation_gadget(
&mut cs.ns(|| "gadget_evaluation"),
&gadget_parameters,
&input_bytes,
)
.unwrap();
println!("number of constraints total: {}", cs.num_constraints());
let primitive_result = primitive_result.into_affine();
assert_eq!(primitive_result.x, gadget_result.x.value.unwrap());
assert_eq!(primitive_result.y, gadget_result.y.value.unwrap());
assert!(cs.is_satisfied());
}
}

+ 141
- 0
crypto-primitives/src/crh/pedersen/mod.rs

@ -0,0 +1,141 @@
use crate::Error;
use rand::Rng;
use rayon::prelude::*;
use std::{
fmt::{Debug, Formatter, Result as FmtResult},
marker::PhantomData,
};
use crate::crh::FixedLengthCRH;
use algebra::groups::Group;
#[cfg(feature = "r1cs")]
pub mod constraints;
pub trait PedersenWindow: Clone {
const WINDOW_SIZE: usize;
const NUM_WINDOWS: usize;
}
#[derive(Clone, Default)]
pub struct PedersenParameters<G: Group> {
pub generators: Vec<Vec<G>>,
}
pub struct PedersenCRH<G: Group, W: PedersenWindow> {
group: PhantomData<G>,
window: PhantomData<W>,
}
impl<G: Group, W: PedersenWindow> PedersenCRH<G, W> {
pub fn create_generators<R: Rng>(rng: &mut R) -> Vec<Vec<G>> {
let mut generators_powers = Vec::new();
for _ in 0..W::NUM_WINDOWS {
generators_powers.push(Self::generator_powers(W::WINDOW_SIZE, rng));
}
generators_powers
}
pub fn generator_powers<R: Rng>(num_powers: usize, rng: &mut R) -> Vec<G> {
let mut cur_gen_powers = Vec::with_capacity(num_powers);
let mut base = G::rand(rng);
for _ in 0..num_powers {
cur_gen_powers.push(base);
base.double_in_place();
}
cur_gen_powers
}
}
impl<G: Group, W: PedersenWindow> FixedLengthCRH for PedersenCRH<G, W> {
const INPUT_SIZE_BITS: usize = W::WINDOW_SIZE * W::NUM_WINDOWS;
type Output = G;
type Parameters = PedersenParameters<G>;
fn setup<R: Rng>(rng: &mut R) -> Result<Self::Parameters, Error> {
let time = start_timer!(|| format!(
"PedersenCRH::Setup: {} {}-bit windows; {{0,1}}^{{{}}} -> G",
W::NUM_WINDOWS,
W::WINDOW_SIZE,
W::NUM_WINDOWS * W::WINDOW_SIZE
));
let generators = Self::create_generators(rng);
end_timer!(time);
Ok(Self::Parameters { generators })
}
fn evaluate(parameters: &Self::Parameters, input: &[u8]) -> Result<Self::Output, Error> {
let eval_time = start_timer!(|| "PedersenCRH::Eval");
if (input.len() * 8) > W::WINDOW_SIZE * W::NUM_WINDOWS {
panic!(
"incorrect input length {:?} for window params {:?}x{:?}",
input.len(),
W::WINDOW_SIZE,
W::NUM_WINDOWS
);
}
let mut padded_input = Vec::with_capacity(input.len());
let mut input = input;
// Pad the input if it is not the current length.
if (input.len() * 8) < W::WINDOW_SIZE * W::NUM_WINDOWS {
let current_length = input.len();
padded_input.extend_from_slice(input);
for _ in current_length..((W::WINDOW_SIZE * W::NUM_WINDOWS) / 8) {
padded_input.push(0u8);
}
input = padded_input.as_slice();
}
assert_eq!(
parameters.generators.len(),
W::NUM_WINDOWS,
"Incorrect pp of size {:?}x{:?} for window params {:?}x{:?}",
parameters.generators[0].len(),
parameters.generators.len(),
W::WINDOW_SIZE,
W::NUM_WINDOWS
);
// Compute sum of h_i^{m_i} for all i.
let result = bytes_to_bits(input)
.par_chunks(W::WINDOW_SIZE)
.zip(&parameters.generators)
.map(|(bits, generator_powers)| {
let mut encoded = G::zero();
for (bit, base) in bits.iter().zip(generator_powers.iter()) {
if *bit {
encoded = encoded + base;
}
}
encoded
})
.reduce(|| G::zero(), |a, b| a + &b);
end_timer!(eval_time);
Ok(result)
}
}
pub fn bytes_to_bits(bytes: &[u8]) -> Vec<bool> {
let mut bits = Vec::with_capacity(bytes.len() * 8);
for byte in bytes {
for i in 0..8 {
let bit = (*byte >> i) & 1;
bits.push(bit == 1)
}
}
bits
}
impl<G: Group> Debug for PedersenParameters<G> {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
write!(f, "Pedersen Hash Parameters {{\n")?;
for (i, g) in self.generators.iter().enumerate() {
write!(f, "\t Generator {}: {:?}\n", i, g)?;
}
write!(f, "}}\n")
}
}

+ 46
- 0
crypto-primitives/src/lib.rs

@ -0,0 +1,46 @@
#[macro_use]
extern crate bench_utils;
#[macro_use]
extern crate derivative;
pub mod commitment;
pub mod crh;
pub mod mht;
pub mod nizk;
pub mod prf;
pub mod signature;
pub use self::{
commitment::CommitmentScheme,
crh::FixedLengthCRH,
mht::{HashMembershipProof, MerkleHashTree},
nizk::NIZK,
prf::PRF,
signature::SignatureScheme,
};
pub type Error = Box<dyn std::error::Error>;
#[derive(Debug)]
pub enum CryptoError {
IncorrectInputLength(usize),
NotPrimeOrder,
}
impl std::fmt::Display for CryptoError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let msg = match self {
CryptoError::IncorrectInputLength(len) => format!("input length is wrong: {}", len),
CryptoError::NotPrimeOrder => "element is not prime order".to_owned(),
};
write!(f, "{}", msg)
}
}
impl std::error::Error for CryptoError {
#[inline]
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
None
}
}

+ 512
- 0
crypto-primitives/src/mht/constraints-old.rs

@ -0,0 +1,512 @@
use algebra::Field;
use r1cs_core::{ConstraintSystem, SynthesisError};
use r1cs_std::prelude::*;
use r1cs_std::boolean::AllocatedBit;
use crate::{mht::HashMembershipProof, CommitmentScheme, FixedLengthCRH};
use crate::{commitment::CommitmentGadget, crh::FixedLengthCRHGadget};
use crate::ledger::{CommPath, Digest, LedgerDigest, LedgerWitness};
use std::{borrow::Borrow, marker::PhantomData};
pub trait LCWGadget<C: CommitmentScheme, D: LedgerDigest, CW: LedgerWitness<D>, ConstraintF: Field> {
type ParametersGadget: AllocGadget<D::Parameters, ConstraintF>;
type CommitmentGadget: AllocGadget<C::Output, ConstraintF>;
type DigestGadget: AllocGadget<D, ConstraintF>;
type WitnessGadget: AllocGadget<CW, ConstraintF>;
fn check_witness_gadget<CS: ConstraintSystem<ConstraintF>>(
cs: CS,
parameters: &Self::ParametersGadget,
ledger_state_digest: &Self::DigestGadget,
commitment: &Self::CommitmentGadget,
witness: &Self::WitnessGadget,
) -> Result<(), SynthesisError> {
Self::conditionally_check_witness_gadget(
cs,
parameters,
ledger_state_digest,
commitment,
witness,
&Boolean::Constant(true),
)
}
fn conditionally_check_witness_gadget<CS: ConstraintSystem<ConstraintF>>(
cs: CS,
parameters: &Self::ParametersGadget,
ledger_state_digest: &Self::DigestGadget,
commitment: &Self::CommitmentGadget,
witness: &Self::WitnessGadget,
should_enforce: &Boolean,
) -> Result<(), SynthesisError>;
}
pub struct IdealLedgerGadget<C, H, HGadget, CGadget> {
#[doc(hidden)]
_comm_scheme: PhantomData<C>,
#[doc(hidden)]
_hash: PhantomData<H>,
#[doc(hidden)]
_comm_gadget: PhantomData<CGadget>,
#[doc(hidden)]
_hash_gadget: PhantomData<HGadget>,
}
pub struct CommitmentWitness<
H: FixedLengthCRH,
C: CommitmentScheme,
HGadget: FixedLengthCRHGadget<H, ConstraintF>,
ConstraintF: Field,
> {
path: Vec<(HGadget::OutputGadget, HGadget::OutputGadget)>,
_crh: PhantomData<H>,
_comm: PhantomData<C>,
_engine: PhantomData<ConstraintF>,
}
pub struct DigestGadget<H: FixedLengthCRH, HGadget: FixedLengthCRHGadget<H, ConstraintF>, ConstraintF: Field> {
digest: HGadget::OutputGadget,
#[doc(hidden)]
_crh: PhantomData<H>,
#[doc(hidden)]
_engine: PhantomData<ConstraintF>,
}
impl<C, ConstraintF, H, CGadget, HGadget> LCWGadget<C, Digest<H>, CommPath<H, C::Output>, ConstraintF>
for IdealLedgerGadget<C, H, HGadget, CGadget>
where
C: CommitmentScheme,
C::Output: Eq,
ConstraintF: Field,
H: FixedLengthCRH,
CGadget: CommitmentGadget<C, ConstraintF>,
HGadget: FixedLengthCRHGadget<H, ConstraintF>,
{
type ParametersGadget = <HGadget as FixedLengthCRHGadget<H, ConstraintF>>::ParametersGadget;
type DigestGadget = DigestGadget<H, HGadget, ConstraintF>;
type CommitmentGadget = <CGadget as CommitmentGadget<C, ConstraintF>>::OutputGadget;
type WitnessGadget = CommitmentWitness<H, C, HGadget, ConstraintF>;
/// Given a `leaf` and `path`, check that the `path` is a valid
/// authentication path for the `leaf` in a Merkle tree.
/// Note: It is assumed that the root is contained in the `path`.
fn conditionally_check_witness_gadget<CS: ConstraintSystem<ConstraintF>>(
mut cs: CS,
parameters: &Self::ParametersGadget,
root_hash: &Self::DigestGadget,
commitment: &Self::CommitmentGadget,
witness: &Self::WitnessGadget,
should_enforce: &Boolean,
) -> Result<(), SynthesisError> {
assert_eq!(
witness.path.len(),
(HashMembershipProof::<H, C::Output>::MAX_HEIGHT - 1) as usize
);
// Check that the hash of the given leaf matches the leaf hash in the membership
// proof.
let commitment_bits = commitment.to_bytes(&mut cs.ns(|| "commitment_to_bytes"))?;
let commitment_hash = HGadget::check_evaluation_gadget(
cs.ns(|| "check_evaluation_gadget"),
parameters,
&commitment_bits,
)?;
// Check if leaf is one of the bottom-most siblings.
let leaf_is_left = AllocatedBit::alloc(&mut cs.ns(|| "leaf_is_left"), || {
Ok(commitment_hash == witness.path[0].0)
})?
.into();
<HGadget::OutputGadget>::conditional_enforce_equal_or(
&mut cs.ns(|| "check_leaf_is_left"),
&leaf_is_left,
&commitment_hash,
&witness.path[0].0,
&witness.path[0].1,
should_enforce,
)?;
// Check levels between leaf level and root.
let mut previous_hash = commitment_hash;
for (i, &(ref left_hash, ref right_hash)) in witness.path.iter().enumerate() {
// Check if the previous_hash matches the correct current hash.
let previous_is_left =
AllocatedBit::alloc(&mut cs.ns(|| format!("previous_is_left_{}", i)), || {
Ok(&previous_hash == left_hash)
})?
.into();
<HGadget::OutputGadget>::conditional_enforce_equal_or(
&mut cs.ns(|| format!("check_equals_which_{}", i)),
&previous_is_left,
&previous_hash,
left_hash,
right_hash,
should_enforce,
)?;
previous_hash = hash_inner_node_gadget::<H, HGadget, ConstraintF, _>(
&mut cs.ns(|| format!("hash_inner_node_{}", i)),
parameters,
left_hash,
right_hash,
)?;
}
root_hash.digest.conditional_enforce_equal(
&mut cs.ns(|| "root_is_last"),
&previous_hash,
should_enforce,
)
}
}
pub(crate) fn hash_inner_node_gadget<H, HG, ConstraintF, CS>(
mut cs: CS,
parameters: &HG::ParametersGadget,
left_child: &HG::OutputGadget,
right_child: &HG::OutputGadget,
) -> Result<HG::OutputGadget, SynthesisError>
where
ConstraintF: Field,
CS: ConstraintSystem<ConstraintF>,
H: FixedLengthCRH,
HG: FixedLengthCRHGadget<H, ConstraintF>,
{
let left_bytes = left_child.to_bytes(&mut cs.ns(|| "left_to_bytes"))?;
let right_bytes = right_child.to_bytes(&mut cs.ns(|| "right_to_bytes"))?;
let mut bytes = left_bytes;
bytes.extend_from_slice(&right_bytes);
HG::check_evaluation_gadget(cs, parameters, &bytes)
}
impl<H, HGadget, ConstraintF> AllocGadget<Digest<H>, ConstraintF> for DigestGadget<H, HGadget, ConstraintF>
where
H: FixedLengthCRH,
HGadget: FixedLengthCRHGadget<H, ConstraintF>,
ConstraintF: Field,
{
fn alloc<F, T, CS: ConstraintSystem<ConstraintF>>(
mut cs: CS,
value_gen: F,
) -> Result<Self, SynthesisError>
where
F: FnOnce() -> Result<T, SynthesisError>,
T: Borrow<Digest<H>>,
{
let digest = HGadget::OutputGadget::alloc(&mut cs.ns(|| "digest"), || {
Ok(value_gen()?.borrow().0.clone())
})?;
Ok(DigestGadget {
digest,
_crh: PhantomData,
_engine: PhantomData,
})
}
fn alloc_input<F, T, CS: ConstraintSystem<ConstraintF>>(
mut cs: CS,
value_gen: F,
) -> Result<Self, SynthesisError>
where
F: FnOnce() -> Result<T, SynthesisError>,
T: Borrow<Digest<H>>,
{
let digest = HGadget::OutputGadget::alloc_input(&mut cs.ns(|| "input_digest"), || {
Ok(value_gen()?.borrow().0.clone())
})?;
Ok(DigestGadget {
digest,
_crh: PhantomData,
_engine: PhantomData,
})
}
}
impl<H, C, HGadget, ConstraintF> AllocGadget<CommPath<H, C::Output>, ConstraintF>
for CommitmentWitness<H, C, HGadget, ConstraintF>
where
H: FixedLengthCRH,
C: CommitmentScheme,
HGadget: FixedLengthCRHGadget<H, ConstraintF>,
ConstraintF: Field,
{
fn alloc<F, T, CS: ConstraintSystem<ConstraintF>>(
mut cs: CS,
value_gen: F,
) -> Result<Self, SynthesisError>
where
F: FnOnce() -> Result<T, SynthesisError>,
T: Borrow<CommPath<H, C::Output>>,
{
let mut path = Vec::new();
for (i, &(ref l, ref r)) in value_gen()?.borrow().0.path.iter().enumerate() {
let l_hash =
HGadget::OutputGadget::alloc(&mut cs.ns(|| format!("l_child_{}", i)), || {
Ok(l.clone())
})?;
let r_hash =
HGadget::OutputGadget::alloc(&mut cs.ns(|| format!("r_child_{}", i)), || {
Ok(r.clone())
})?;
path.push((l_hash, r_hash));
}
Ok(CommitmentWitness {
path,
_crh: PhantomData,
_comm: PhantomData,
_engine: PhantomData,
})
}
fn alloc_input<F, T, CS: ConstraintSystem<ConstraintF>>(
mut cs: CS,
value_gen: F,
) -> Result<Self, SynthesisError>
where
F: FnOnce() -> Result<T, SynthesisError>,
T: Borrow<CommPath<H, C::Output>>,
{
let mut path = Vec::new();
for (i, &(ref l, ref r)) in value_gen()?.borrow().0.path.iter().enumerate() {
let l_hash = HGadget::OutputGadget::alloc_input(
&mut cs.ns(|| format!("l_child_{}", i)),
|| Ok(l.clone()),
)?;
let r_hash = HGadget::OutputGadget::alloc_input(
&mut cs.ns(|| format!("r_child_{}", i)),
|| Ok(r.clone()),
)?;
path.push((l_hash, r_hash));
}
Ok(CommitmentWitness {
path,
_crh: PhantomData,
_comm: PhantomData,
_engine: PhantomData,
})
}
}
#[cfg(test)]
mod test {
use std::rc::Rc;
use crate::crypto_primitives::{
commitment::{
pedersen::{PedersenCommitment, PedersenRandomness},
CommitmentScheme,
},
crh::{
pedersen::{PedersenCRH, PedersenWindow},
FixedLengthCRH,
},
mht::*,
};
use algebra::{
curves::jubjub::JubJubAffine as JubJub,
fields::jubjub::fr::Fr,
fields::jubjub::fq::Fq,
Group
};
use rand::SeedableRng;
use algebra::UniformRand;
use rand_xorshift::XorShiftRng;
use r1cs_core::ConstraintSystem;
use super::*;
use crate::gadgets::{
commitment::pedersen::PedersenCommitmentGadget,
crh::{pedersen::PedersenCRHGadget, FixedLengthCRHGadget},
};
use r1cs_std::{
groups::curves::twisted_edwards::jubjub::JubJubGadget,
test_constraint_system::TestConstraintSystem,
};
use crate::ledger::{CommPath, Digest};
#[derive(Clone)]
pub(super) struct Window4x256;
impl PedersenWindow for Window4x256 {
const WINDOW_SIZE: usize = 4;
const NUM_WINDOWS: usize = 256;
}
type H = PedersenCRH<JubJub, Window4x256>;
type HG = PedersenCRHGadget<JubJub, Fq, JubJubGadget>;
type C = PedersenCommitment<JubJub, Window4x256>;
type CG = PedersenCommitmentGadget<JubJub, Fq, JubJubGadget>;
type JubJubMHT = MerkleHashTree<H, <C as CommitmentScheme>::Output>;
type LG = IdealLedgerGadget<C, H, HG, CG>;
type DG = DigestGadget<H, HG, Fq>;
type LCWG = CommitmentWitness<H, C, HG, Fq>;
fn generate_merkle_tree(leaves: &[<C as CommitmentScheme>::Output]) -> () {
let mut rng = XorShiftRng::seed_from_u64(1231275789u64);
let crh_parameters = Rc::new(H::setup(&mut rng).unwrap());
let tree = JubJubMHT::new(crh_parameters.clone(), &leaves).unwrap();
let root = tree.root();
let mut satisfied = true;
for (i, leaf) in leaves.iter().enumerate() {
let mut cs = TestConstraintSystem::<Fq>::new();
let proof = tree.generate_proof(i, &leaf).unwrap();
assert!(proof.verify(&crh_parameters, &root, &leaf).unwrap());
let digest = DG::alloc(&mut cs.ns(|| format!("new_digest_{}", i)), || {
Ok(Digest(root))
})
.unwrap();
let constraints_from_digest = cs.num_constraints();
println!("constraints from digest: {}", constraints_from_digest);
let crh_parameters =
<HG as FixedLengthCRHGadget<H, Fq>>::ParametersGadget::alloc(
&mut cs.ns(|| format!("new_parameters_{}", i)),
|| Ok(crh_parameters.clone()),
)
.unwrap();
let constraints_from_parameters = cs.num_constraints() - constraints_from_digest;
println!(
"constraints from parameters: {}",
constraints_from_parameters
);
let comm = <CG as CommitmentGadget<C, Fq>>::OutputGadget::alloc(
&mut cs.ns(|| format!("new_comm_{}", i)),
|| {
let leaf: JubJub = *leaf;
Ok(leaf)
},
)
.unwrap();
let constraints_from_comm =
cs.num_constraints() - constraints_from_parameters - constraints_from_digest;
println!("constraints from comm: {}", constraints_from_comm);
let cw = LCWG::alloc(&mut cs.ns(|| format!("new_witness_{}", i)), || {
Ok(CommPath(proof))
})
.unwrap();
let constraints_from_path = cs.num_constraints()
- constraints_from_parameters
- constraints_from_digest
- constraints_from_comm;
println!("constraints from path: {}", constraints_from_path);
LG::check_witness_gadget(
&mut cs.ns(|| format!("new_witness_check_{}", i)),
&crh_parameters,
&digest,
&comm,
&cw,
)
.unwrap();
if !cs.is_satisfied() {
satisfied = false;
println!(
"Unsatisfied constraint: {}",
cs.which_is_unsatisfied().unwrap()
);
}
let setup_constraints = constraints_from_comm
+ constraints_from_digest
+ constraints_from_parameters
+ constraints_from_path;
println!(
"number of constraints: {}",
cs.num_constraints() - setup_constraints
);
}
assert!(satisfied);
}
#[test]
fn mht_gadget_test() {
let mut leaves = Vec::new();
let mut rng = XorShiftRng::seed_from_u64(1231275789u64);
let comm_parameters = C::setup(&mut rng).unwrap();
for i in 0..4u8 {
let r = PedersenRandomness(Fr::rand(&mut rng));
let input = [i, i, i, i, i, i, i, i];
leaves.push(C::commit(&comm_parameters, &input, &r).unwrap());
}
generate_merkle_tree(&leaves);
// let mut leaves = Vec::new();
// for i in 0..100u8 {
// leaves.push([i, i, i, i, i, i, i, i]);
// }
// generate_merkle_tree(&leaves);
}
fn bad_merkle_tree_verify(leaves: &[<C as CommitmentScheme>::Output]) -> () {
let mut rng = XorShiftRng::seed_from_u64(1231275789u64);
let crh_parameters = Rc::new(H::setup(&mut rng).unwrap());
let tree = JubJubMHT::new(crh_parameters.clone(), &leaves).unwrap();
let root = tree.root();
for (i, leaf) in leaves.iter().enumerate() {
let mut cs = TestConstraintSystem::<Fq>::new();
let proof = tree.generate_proof(i, &leaf).unwrap();
assert!(proof.verify(&crh_parameters, &root, &leaf).unwrap());
let digest = DG::alloc(&mut cs.ns(|| format!("new_digest_{}", i)), || {
Ok(Digest(JubJub::zero()))
})
.unwrap();
let crh_parameters =
<HG as FixedLengthCRHGadget<H, Fq>>::ParametersGadget::alloc(
&mut cs.ns(|| format!("new_parameters_{}", i)),
|| Ok(crh_parameters.clone()),
)
.unwrap();
let comm = <CG as CommitmentGadget<C, Fq>>::OutputGadget::alloc(
&mut cs.ns(|| format!("new_comm_{}", i)),
|| {
let leaf = *leaf;
Ok(leaf)
},
)
.unwrap();
let cw = LCWG::alloc(&mut cs.ns(|| format!("new_witness_{}", i)), || {
Ok(CommPath(proof))
})
.unwrap();
LG::check_witness_gadget(
&mut cs.ns(|| format!("new_witness_check_{}", i)),
&crh_parameters,
&digest,
&comm,
&cw,
)
.unwrap();
if !cs.is_satisfied() {
println!(
"Unsatisfied constraints: {}",
cs.which_is_unsatisfied().unwrap()
);
}
assert!(cs.is_satisfied());
}
}
#[should_panic]
#[test]
fn bad_root_test() {
let mut leaves = Vec::new();
let mut rng = XorShiftRng::seed_from_u64(1231275789u64);
let comm_parameters = C::setup(&mut rng).unwrap();
for i in 0..4u8 {
let r = PedersenRandomness(Fr::rand(&mut rng));
let input = [i, i, i, i, i, i, i, i];
leaves.push(C::commit(&comm_parameters, &input, &r).unwrap());
}
bad_merkle_tree_verify(&leaves);
}
}

+ 353
- 0
crypto-primitives/src/mht/constraints.rs

@ -0,0 +1,353 @@
use algebra::Field;
use r1cs_core::{ConstraintSystem, SynthesisError};
use r1cs_std::prelude::*;
use r1cs_std::boolean::AllocatedBit;
use crate::mht::*;
use crate::crh::{FixedLengthCRH, FixedLengthCRHGadget};
use std::{borrow::Borrow, marker::PhantomData};
pub struct MerklePath<P, HGadget, ConstraintF>
where
P: MHTParameters,
HGadget: FixedLengthCRHGadget<P::H, ConstraintF>,
ConstraintF: Field,
{
path: Vec<(HGadget::OutputGadget, HGadget::OutputGadget)>,
}
pub struct MerklePathVerifierGadget<P, CRHGadget, ConstraintF>
where
P: MHTParameters,
ConstraintF: Field,
CRHGadget: FixedLengthCRHGadget<P::H, ConstraintF>,
{
_p: PhantomData<CRHGadget>,
_p2: PhantomData<P>,
_f: PhantomData<ConstraintF>,
}
impl<P, CRHGadget, ConstraintF> MerklePathVerifierGadget<P, CRHGadget, ConstraintF>
where
P: MHTParameters,
ConstraintF: Field,
CRHGadget: FixedLengthCRHGadget<P::H, ConstraintF>,
{
pub fn check_membership<CS: ConstraintSystem<ConstraintF>>(
cs: CS,
parameters: &CRHGadget::ParametersGadget,
root: &CRHGadget::OutputGadget,
leaf: impl ToBytesGadget<ConstraintF>,
witness: &MerklePath<P, CRHGadget, ConstraintF>,
) -> Result<(), SynthesisError> {
Self::conditionally_check_membership(
cs,
parameters,
root,
leaf,
witness,
&Boolean::Constant(true),
)
}
pub fn conditionally_check_membership<CS: ConstraintSystem<ConstraintF>>(
mut cs: CS,
parameters: &CRHGadget::ParametersGadget,
root: &CRHGadget::OutputGadget,
leaf: impl ToBytesGadget<ConstraintF>,
witness: &MerklePath<P, CRHGadget, ConstraintF>,
should_enforce: &Boolean,
) -> Result<(), SynthesisError> {
assert_eq!(witness.path.len(), P::HEIGHT - 1);
// Check that the hash of the given leaf matches the leaf hash in the membership
// proof.
let leaf_bits = leaf.to_bytes(&mut cs.ns(|| "leaf_to_bytes"))?;
let leaf_hash = CRHGadget::check_evaluation_gadget(
cs.ns(|| "check_evaluation_gadget"),
parameters,
&leaf_bits,
)?;
// Check if leaf is one of the bottom-most siblings.
let leaf_is_left = AllocatedBit::alloc(&mut cs.ns(|| "leaf_is_left"), || {
Ok(leaf_hash == witness.path[0].0)
})?
.into();
CRHGadget::OutputGadget::conditional_enforce_equal_or(
&mut cs.ns(|| "check_leaf_is_left"),
&leaf_is_left,
&leaf_hash,
&witness.path[0].0,
&witness.path[0].1,
should_enforce,
)?;
// Check levels between leaf level and root.
let mut previous_hash = leaf_hash;
for (i, &(ref left_hash, ref right_hash)) in witness.path.iter().enumerate() {
// Check if the previous_hash matches the correct current hash.
let previous_is_left =
AllocatedBit::alloc(&mut cs.ns(|| format!("previous_is_left_{}", i)), || {
Ok(&previous_hash == left_hash)
})?
.into();
CRHGadget::OutputGadget::conditional_enforce_equal_or(
&mut cs.ns(|| format!("check_equals_which_{}", i)),
&previous_is_left,
&previous_hash,
left_hash,
right_hash,
should_enforce,
)?;
previous_hash = hash_inner_node_gadget::<P::H, CRHGadget, ConstraintF, _>(
&mut cs.ns(|| format!("hash_inner_node_{}", i)),
parameters,
left_hash,
right_hash,
)?;
}
root.conditional_enforce_equal(
&mut cs.ns(|| "root_is_last"),
&previous_hash,
should_enforce,
)
}
}
pub(crate) fn hash_inner_node_gadget<H, HG, ConstraintF, CS>(
mut cs: CS,
parameters: &HG::ParametersGadget,
left_child: &HG::OutputGadget,
right_child: &HG::OutputGadget,
) -> Result<HG::OutputGadget, SynthesisError>
where
ConstraintF: Field,
CS: ConstraintSystem<ConstraintF>,
H: FixedLengthCRH,
HG: FixedLengthCRHGadget<H, ConstraintF>,
{
let left_bytes = left_child.to_bytes(&mut cs.ns(|| "left_to_bytes"))?;
let right_bytes = right_child.to_bytes(&mut cs.ns(|| "right_to_bytes"))?;
let mut bytes = left_bytes;
bytes.extend_from_slice(&right_bytes);
HG::check_evaluation_gadget(cs, parameters, &bytes)
}
impl<P, HGadget, ConstraintF> AllocGadget<HashMembershipProof<P>, ConstraintF>
for MerklePath<P, HGadget, ConstraintF>
where
P: MHTParameters,
HGadget: FixedLengthCRHGadget<P::H, ConstraintF>,
ConstraintF: Field,
{
fn alloc<F, T, CS: ConstraintSystem<ConstraintF>>(
mut cs: CS,
value_gen: F,
) -> Result<Self, SynthesisError>
where
F: FnOnce() -> Result<T, SynthesisError>,
T: Borrow<HashMembershipProof<P>>,
{
let mut path = Vec::new();
for (i, &(ref l, ref r)) in value_gen()?.borrow().path.iter().enumerate() {
let l_hash =
HGadget::OutputGadget::alloc(&mut cs.ns(|| format!("l_child_{}", i)), || {
Ok(l.clone())
})?;
let r_hash =
HGadget::OutputGadget::alloc(&mut cs.ns(|| format!("r_child_{}", i)), || {
Ok(r.clone())
})?;
path.push((l_hash, r_hash));
}
Ok(MerklePath {
path,
})
}
fn alloc_input<F, T, CS: ConstraintSystem<ConstraintF>>(
mut cs: CS,
value_gen: F,
) -> Result<Self, SynthesisError>
where
F: FnOnce() -> Result<T, SynthesisError>,
T: Borrow<HashMembershipProof<P>>,
{
let mut path = Vec::new();
for (i, &(ref l, ref r)) in value_gen()?.borrow().path.iter().enumerate() {
let l_hash = HGadget::OutputGadget::alloc_input(
&mut cs.ns(|| format!("l_child_{}", i)),
|| Ok(l.clone()),
)?;
let r_hash = HGadget::OutputGadget::alloc_input(
&mut cs.ns(|| format!("r_child_{}", i)),
|| Ok(r.clone()),
)?;
path.push((l_hash, r_hash));
}
Ok(MerklePath {
path,
})
}
}
#[cfg(test)]
mod test {
use std::rc::Rc;
use crate::{
crh::{
pedersen::{PedersenCRH, PedersenWindow},
pedersen::constraints::PedersenCRHGadget,
FixedLengthCRH,
FixedLengthCRHGadget,
},
mht::*,
};
use algebra::{
curves::jubjub::JubJubAffine as JubJub,
fields::jubjub::fq::Fq,
};
use rand::SeedableRng;
use rand_xorshift::XorShiftRng;
use r1cs_core::ConstraintSystem;
use super::*;
use r1cs_std::{
groups::curves::twisted_edwards::jubjub::JubJubGadget,
test_constraint_system::TestConstraintSystem,
};
#[derive(Clone)]
pub(super) struct Window4x256;
impl PedersenWindow for Window4x256 {
const WINDOW_SIZE: usize = 4;
const NUM_WINDOWS: usize = 256;
}
type H = PedersenCRH<JubJub, Window4x256>;
type HG = PedersenCRHGadget<JubJub, Fq, JubJubGadget>;
struct JubJubMHTParams;
impl MHTParameters for JubJubMHTParams {
const HEIGHT: usize = 32;
type H = H;
}
type JubJubMHT = MerkleHashTree<JubJubMHTParams>;
fn generate_merkle_tree(leaves: &[[u8; 30]], use_bad_root: bool) -> () {
let mut rng = XorShiftRng::seed_from_u64(9174123u64);
let crh_parameters = Rc::new(H::setup(&mut rng).unwrap());
let tree = JubJubMHT::new(crh_parameters.clone(), leaves).unwrap();
let root = tree.root();
let mut satisfied = true;
for (i, leaf) in leaves.iter().enumerate() {
let mut cs = TestConstraintSystem::<Fq>::new();
let proof = tree.generate_proof(i, &leaf).unwrap();
assert!(proof.verify(&crh_parameters, &root, &leaf).unwrap());
// Allocate Merkle Tree Root
let root = <HG as FixedLengthCRHGadget<H, _>>::OutputGadget::alloc(&mut cs.ns(|| format!("new_digest_{}", i)), || {
if use_bad_root {
Ok(<H as FixedLengthCRH>::Output::default())
} else {
Ok(root)
}
})
.unwrap();
let constraints_from_digest = cs.num_constraints();
println!("constraints from digest: {}", constraints_from_digest);
// Allocate Parameters for CRH
let crh_parameters =
<HG as FixedLengthCRHGadget<H, Fq>>::ParametersGadget::alloc(
&mut cs.ns(|| format!("new_parameters_{}", i)),
|| Ok(crh_parameters.clone()),
)
.unwrap();
let constraints_from_parameters = cs.num_constraints() - constraints_from_digest;
println!(
"constraints from parameters: {}",
constraints_from_parameters
);
// Allocate Leaf
let leaf_g = UInt8::constant_vec(leaf);
let constraints_from_leaf =
cs.num_constraints() - constraints_from_parameters - constraints_from_digest;
println!("constraints from leaf: {}", constraints_from_leaf);
// Allocate Merkle Tree Path
let cw = MerklePath::<_, HG, _>::alloc(&mut cs.ns(|| format!("new_witness_{}", i)), || {
Ok(proof)
})
.unwrap();
let constraints_from_path = cs.num_constraints()
- constraints_from_parameters
- constraints_from_digest
- constraints_from_leaf;
println!("constraints from path: {}", constraints_from_path);
let leaf_g: &[UInt8] = leaf_g.as_slice();
MerklePathVerifierGadget::<_, HG, _>::check_membership(
&mut cs.ns(|| format!("new_witness_check_{}", i)),
&crh_parameters,
&root,
&leaf_g,
&cw,
)
.unwrap();
if !cs.is_satisfied() {
satisfied = false;
println!(
"Unsatisfied constraint: {}",
cs.which_is_unsatisfied().unwrap()
);
}
let setup_constraints = constraints_from_leaf
+ constraints_from_digest
+ constraints_from_parameters
+ constraints_from_path;
println!(
"number of constraints: {}",
cs.num_constraints() - setup_constraints
);
}
assert!(satisfied);
}
#[test]
fn mht_gadget_test() {
let mut leaves = Vec::new();
for i in 0..4u8 {
let input = [i ; 30];
leaves.push(input);
}
generate_merkle_tree(&leaves, false);
}
#[should_panic]
#[test]
fn bad_root_test() {
let mut leaves = Vec::new();
for i in 0..4u8 {
let input = [i ; 30];
leaves.push(input);
}
generate_merkle_tree(&leaves, true);
}
}

+ 437
- 0
crypto-primitives/src/mht/mod.rs

@ -0,0 +1,437 @@
use crate::crh::FixedLengthCRH;
use algebra::bytes::ToBytes;
use crate::Error;
use std::{fmt, rc::Rc};
#[cfg(feature = "r1cs")]
pub mod constraints;
pub trait MHTParameters {
const HEIGHT: usize;
type H: FixedLengthCRH;
}
/// Stores the hashes of a particular path (in order) from leaf to root.
/// Our path `is_left_child()` if the boolean in `path` is true.
#[derive(Derivative)]
#[derivative(
Clone(bound = "P: MHTParameters"),
Debug(bound = "P: MHTParameters, <P::H as FixedLengthCRH>::Output: fmt::Debug")
)]
pub struct HashMembershipProof<P: MHTParameters> {
pub(crate) path: Vec<(<P::H as FixedLengthCRH>::Output, <P::H as FixedLengthCRH>::Output)>,
}
impl<P: MHTParameters> Default for HashMembershipProof<P> {
fn default() -> Self {
let mut path = Vec::with_capacity(P::HEIGHT as usize);
for _i in 1..P::HEIGHT as usize {
path.push((<P::H as FixedLengthCRH>::Output::default(), <P::H as FixedLengthCRH>::Output::default()));
}
Self {
path,
}
}
}
impl<P: MHTParameters> HashMembershipProof<P> {
pub fn verify<L: ToBytes>(
&self,
parameters: &<P::H as FixedLengthCRH>::Parameters,
root_hash: &<P::H as FixedLengthCRH>::Output,
leaf: &L,
) -> Result<bool, Error> {
if self.path.len() != (P::HEIGHT - 1) as usize {
return Ok(false);
}
// Check that the given leaf matches the leaf in the membership proof.
let mut buffer = [0u8; 128];
if !self.path.is_empty() {
let claimed_leaf_hash = hash_leaf::<P::H, L>(parameters, leaf, &mut buffer)?;
// Check if leaf is one of the bottom-most siblings.
if claimed_leaf_hash != self.path[0].0 && claimed_leaf_hash != self.path[0].1 {
return Ok(false);
};
let mut prev = claimed_leaf_hash;
// Check levels between leaf level and root.
for &(ref hash, ref sibling_hash) in &self.path {
// Check if the previous hash matches the correct current hash.
if &prev != hash && &prev != sibling_hash {
return Ok(false);
};
prev = hash_inner_node::<P::H>(parameters, hash, sibling_hash, &mut buffer)?;
}
if root_hash != &prev {
return Ok(false);
}
Ok(true)
} else {
Ok(false)
}
}
}
pub struct MerkleHashTree<P: MHTParameters> {
tree: Vec<<P::H as FixedLengthCRH>::Output>,
padding_tree: Vec<(<P::H as FixedLengthCRH>::Output, <P::H as FixedLengthCRH>::Output)>,
parameters: Rc<<P::H as FixedLengthCRH>::Parameters>,
root: Option<<P::H as FixedLengthCRH>::Output>,
}
impl<P: MHTParameters> MerkleHashTree<P> {
pub const HEIGHT: u8 = P::HEIGHT as u8;
pub fn blank(parameters: Rc<<P::H as FixedLengthCRH>::Parameters>) -> Self {
MerkleHashTree {
tree: Vec::new(),
padding_tree: Vec::new(),
root: None,
parameters,
}
}
pub fn new<L: ToBytes>(parameters: Rc<<P::H as FixedLengthCRH>::Parameters>, leaves: &[L]) -> Result<Self, Error> {
let new_time = start_timer!(|| "MHT::New");
let last_level_size = leaves.len().next_power_of_two();
let tree_size = 2 * last_level_size - 1;
let tree_height = tree_height(tree_size);
assert!(tree_height as u8 <= Self::HEIGHT);
// Initialize the merkle tree.
let mut tree = Vec::with_capacity(tree_size);
let empty_hash = hash_empty::<P::H>(&parameters)?;
for _ in 0..tree_size {
tree.push(empty_hash.clone());
}
// Compute the starting indices for each level of the tree.
let mut index = 0;
let mut level_indices = Vec::with_capacity(tree_height);
for _ in 0..tree_height {
level_indices.push(index);
index = left_child(index);
}
// Compute and store the hash values for each leaf.
let last_level_index = level_indices.pop().unwrap();
let mut buffer = [0u8; 128];
for (i, leaf) in leaves.iter().enumerate() {
tree[last_level_index + i] = hash_leaf::<P::H, _>(&parameters, leaf, &mut buffer)?;
}
// Compute the hash values for every node in the tree.
let mut upper_bound = last_level_index;
let mut buffer = [0u8; 128];
level_indices.reverse();
for &start_index in &level_indices {
// Iterate over the current level.
for current_index in start_index..upper_bound {
let left_index = left_child(current_index);
let right_index = right_child(current_index);
// Compute Hash(left || right).
tree[current_index] = hash_inner_node::<P::H>(
&parameters,
&tree[left_index],
&tree[right_index],
&mut buffer,
)?;
}
upper_bound = start_index;
}
// Finished computing actual tree.
// Now, we compute the dummy nodes until we hit our HEIGHT goal.
let mut cur_height = tree_height;
let mut padding_tree = Vec::new();
let mut cur_hash = tree[0].clone();
while cur_height < (Self::HEIGHT - 1) as usize {
cur_hash = hash_inner_node::<P::H>(&parameters, &cur_hash, &empty_hash, &mut buffer)?;
padding_tree.push((cur_hash.clone(), empty_hash.clone()));
cur_height += 1;
}
let root_hash = hash_inner_node::<P::H>(&parameters, &cur_hash, &empty_hash, &mut buffer)?;
end_timer!(new_time);
Ok(MerkleHashTree {
tree,
padding_tree,
parameters,
root: Some(root_hash),
})
}
#[inline]
pub fn root(&self) -> <P::H as FixedLengthCRH>::Output {
self.root.clone().unwrap()
}
pub fn generate_proof<L: ToBytes>(
&self,
index: usize,
leaf: &L,
) -> Result<HashMembershipProof<P>, Error> {
let prove_time = start_timer!(|| "MHT::GenProof");
let mut path = Vec::new();
let mut buffer = [0u8; 128];
let leaf_hash = hash_leaf::<P::H, _>(&self.parameters, leaf, &mut buffer)?;
let tree_height = tree_height(self.tree.len());
let tree_index = convert_index_to_last_level(index, tree_height);
let empty_hash = hash_empty::<P::H>(&self.parameters)?;
// Check that the given index corresponds to the correct leaf.
if leaf_hash != self.tree[tree_index] {
Err(MHTError::IncorrectLeafIndex(tree_index))?
}
// Iterate from the leaf up to the root, storing all intermediate hash values.
let mut current_node = tree_index;
while !is_root(current_node) {
let sibling_node = sibling(current_node).unwrap();
let (curr_hash, sibling_hash) = (
self.tree[current_node].clone(),
self.tree[sibling_node].clone(),
);
if is_left_child(current_node) {
path.push((curr_hash, sibling_hash));
} else {
path.push((sibling_hash, curr_hash));
}
current_node = parent(current_node).unwrap();
}
// Store the root node. Set boolean as true for consistency with digest
// location.
assert!(path.len() < Self::HEIGHT as usize);
if path.len() != (Self::HEIGHT - 1) as usize {
path.push((self.tree[0].clone(), empty_hash));
for &(ref hash, ref sibling_hash) in &self.padding_tree {
path.push((hash.clone(), sibling_hash.clone()));
}
}
end_timer!(prove_time);
if path.len() != (Self::HEIGHT - 1) as usize {
Err(MHTError::IncorrectPathLength(path.len()))?
} else {
Ok(HashMembershipProof {
path,
})
}
}
}
#[derive(Debug)]
pub enum MHTError {
IncorrectLeafIndex(usize),
IncorrectPathLength(usize),
}
impl std::fmt::Display for MHTError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let msg = match self {
MHTError::IncorrectLeafIndex(index) => format!("incorrect leaf index: {}", index),
MHTError::IncorrectPathLength(len) => format!("incorrect path length: {}", len),
};
write!(f, "{}", msg)
}
}
impl std::error::Error for MHTError {
#[inline]
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
None
}
}
/// Returns the log2 value of the given number.
#[inline]
fn log2(number: usize) -> usize {
(number as f64).log2() as usize
}
/// Returns the height of the tree, given the size of the tree.
#[inline]
fn tree_height(tree_size: usize) -> usize {
log2(tree_size + 1)
}
/// Returns true iff the index represents the root.
#[inline]
fn is_root(index: usize) -> bool {
index == 0
}
/// Returns the index of the left child, given an index.
#[inline]
fn left_child(index: usize) -> usize {
2 * index + 1
}
/// Returns the index of the right child, given an index.
#[inline]
fn right_child(index: usize) -> usize {
2 * index + 2
}
/// Returns the index of the sibling, given an index.
#[inline]
fn sibling(index: usize) -> Option<usize> {
if index == 0 {
None
} else if is_left_child(index) {
Some(index + 1)
} else {
Some(index - 1)
}
}
/// Returns true iff the given index represents a left child.
#[inline]
fn is_left_child(index: usize) -> bool {
index % 2 == 1
}
/// Returns the index of the parent, given an index.
#[inline]
fn parent(index: usize) -> Option<usize> {
if index > 0 {
Some((index - 1) >> 1)
} else {
None
}
}
#[inline]
fn convert_index_to_last_level(index: usize, tree_height: usize) -> usize {
index + (1 << (tree_height - 1)) - 1
}
/// Returns the output hash, given a left and right hash value.
pub(crate) fn hash_inner_node<H: FixedLengthCRH>(
parameters: &H::Parameters,
left: &H::Output,
right: &H::Output,
buffer: &mut [u8],
) -> Result<H::Output, Error> {
use std::io::Cursor;
let mut writer = Cursor::new(buffer);
// Construct left input.
left.write(&mut writer)?;
// Construct right input.
right.write(&mut writer)?;
let buffer = writer.into_inner();
H::evaluate(parameters, &buffer[..(H::INPUT_SIZE_BITS / 8)])
}
/// Returns the hash of a leaf.
pub(crate) fn hash_leaf<H: FixedLengthCRH, L: ToBytes>(
parameters: &H::Parameters,
leaf: &L,
buffer: &mut [u8],
) -> Result<H::Output, Error> {
use std::io::Cursor;
let mut writer = Cursor::new(buffer);
leaf.write(&mut writer)?;
let buffer = writer.into_inner();
H::evaluate(parameters, &buffer[..(H::INPUT_SIZE_BITS / 8)])
}
pub(crate) fn hash_empty<H: FixedLengthCRH>(
parameters: &H::Parameters,
) -> Result<H::Output, Error> {
let empty_buffer = vec![0u8; H::INPUT_SIZE_BITS / 8];
H::evaluate(parameters, &empty_buffer)
}
#[cfg(test)]
mod test {
use crate::{crh::{pedersen::*, *}, mht::*};
use algebra::curves::jubjub::JubJubAffine as JubJub;
use rand::SeedableRng;
use rand_xorshift::XorShiftRng;
#[derive(Clone)]
pub(super) struct Window4x256;
impl PedersenWindow for Window4x256 {
const WINDOW_SIZE: usize = 4;
const NUM_WINDOWS: usize = 256;
}
type H = PedersenCRH<JubJub, Window4x256>;
struct JubJubMHTParams;
impl MHTParameters for JubJubMHTParams {
const HEIGHT: usize = 32;
type H = H;
}
type JubJubMHT = MerkleHashTree<JubJubMHTParams>;
fn generate_merkle_tree<L: ToBytes + Clone + Eq>(leaves: &[L]) -> () {
let mut rng = XorShiftRng::seed_from_u64(9174123u64);
let crh_parameters = Rc::new(H::setup(&mut rng).unwrap());
let tree = JubJubMHT::new(crh_parameters.clone(), &leaves).unwrap();
let root = tree.root();
for (i, leaf) in leaves.iter().enumerate() {
let proof = tree.generate_proof(i, &leaf).unwrap();
assert!(proof.verify(&crh_parameters, &root, &leaf).unwrap());
}
}
#[test]
fn mht_test() {
let mut leaves = Vec::new();
for i in 0..4u8 {
leaves.push([i, i, i, i, i, i, i, i]);
}
generate_merkle_tree(&leaves);
let mut leaves = Vec::new();
for i in 0..100u8 {
leaves.push([i, i, i, i, i, i, i, i]);
}
generate_merkle_tree(&leaves);
}
fn bad_merkle_tree_verify<L: ToBytes + Clone + Eq>(leaves: &[L]) -> () {
use algebra::groups::Group;
let mut rng = XorShiftRng::seed_from_u64(13423423u64);
let crh_parameters = Rc::new(H::setup(&mut rng).unwrap());
let tree = JubJubMHT::new(crh_parameters.clone(), &leaves).unwrap();
let root = JubJub::zero();
for (i, leaf) in leaves.iter().enumerate() {
let proof = tree.generate_proof(i, &leaf).unwrap();
assert!(proof.verify(&crh_parameters, &root, &leaf).unwrap());
}
}
#[should_panic]
#[test]
fn bad_root_mht_test() {
let mut leaves = Vec::new();
for i in 0..4u8 {
leaves.push([i, i, i, i, i, i, i, i]);
}
generate_merkle_tree(&leaves);
let mut leaves = Vec::new();
for i in 0..100u8 {
leaves.push([i, i, i, i, i, i, i, i]);
}
bad_merkle_tree_verify(&leaves);
}
}

+ 22
- 0
crypto-primitives/src/nizk/constraints.rs

@ -0,0 +1,22 @@
use algebra::Field;
use r1cs_core::{ConstraintSystem, SynthesisError};
use r1cs_std::prelude::*;
use crate::nizk::NIZK;
pub trait NIZKVerifierGadget<N: NIZK, ConstraintF: Field> {
type VerificationKeyGadget: AllocGadget<N::VerificationParameters, ConstraintF> + ToBytesGadget<ConstraintF>;
type ProofGadget: AllocGadget<N::Proof, ConstraintF>;
fn check_verify<'a, CS, I, T>(
cs: CS,
verification_key: &Self::VerificationKeyGadget,
input: I,
proof: &Self::ProofGadget,
) -> Result<(), SynthesisError>
where
CS: ConstraintSystem<ConstraintF>,
I: Iterator<Item = &'a T>,
T: 'a + ToBitsGadget<ConstraintF> + ?Sized;
}

+ 543
- 0
crypto-primitives/src/nizk/gm17/constraints.rs

@ -0,0 +1,543 @@
use crate::nizk::{gm17::Gm17, NIZKVerifierGadget};
use algebra::{Field, ToConstraintField, AffineCurve, PairingEngine};
use r1cs_core::{ConstraintSynthesizer, ConstraintSystem, SynthesisError};
use r1cs_std::prelude::*;
use gm17::{Proof, VerifyingKey};
use std::{borrow::Borrow, marker::PhantomData};
#[derive(Derivative)]
#[derivative(Clone(bound = "P::G1Gadget: Clone, P::G2Gadget: Clone"))]
pub struct ProofGadget<
PairingE: PairingEngine,
ConstraintF: Field,
P: PairingGadget<PairingE, ConstraintF>,
> {
pub a: P::G1Gadget,
pub b: P::G2Gadget,
pub c: P::G1Gadget,
}
#[derive(Derivative)]
#[derivative(Clone(
bound = "P::G1Gadget: Clone, P::GTGadget: Clone, P::G1PreparedGadget: Clone, \
P::G2PreparedGadget: Clone, "
))]
pub struct VerifyingKeyGadget<
PairingE: PairingEngine,
ConstraintF: Field,
P: PairingGadget<PairingE, ConstraintF>,
> {
pub h_g2: P::G2Gadget,
pub g_alpha_g1: P::G1Gadget,
pub h_beta_g2: P::G2Gadget,
pub g_gamma_g1: P::G1Gadget,
pub h_gamma_g2: P::G2Gadget,
pub query: Vec<P::G1Gadget>,
}
impl<
PairingE: PairingEngine,
ConstraintF: Field,
P: PairingGadget<PairingE, ConstraintF>,
> VerifyingKeyGadget<PairingE, ConstraintF, P>
{
pub fn prepare<CS: ConstraintSystem<ConstraintF>>(
&self,
mut cs: CS,
) -> Result<PreparedVerifyingKeyGadget<PairingE, ConstraintF, P>, SynthesisError> {
let mut cs = cs.ns(|| "Preparing verifying key");
let g_alpha_pc = P::prepare_g1(&mut cs.ns(|| "Prepare g_alpha_g1"), &self.g_alpha_g1)?;
let h_beta_pc = P::prepare_g2(&mut cs.ns(|| "Prepare h_beta_g2"), &self.h_beta_g2)?;
let g_gamma_pc = P::prepare_g1(&mut cs.ns(|| "Prepare g_gamma_pc"), &self.g_gamma_g1)?;
let h_gamma_pc = P::prepare_g2(&mut cs.ns(|| "Prepare h_gamma_pc"), &self.h_gamma_g2)?;
let h_pc = P::prepare_g2(&mut cs.ns(|| "Prepare h_pc"), &self.h_g2)?;
Ok(PreparedVerifyingKeyGadget {
g_alpha: self.g_alpha_g1.clone(),
h_beta: self.h_beta_g2.clone(),
g_alpha_pc,
h_beta_pc,
g_gamma_pc,
h_gamma_pc,
h_pc,
query: self.query.clone(),
})
}
}
#[derive(Derivative)]
#[derivative(Clone(
bound = "P::G1Gadget: Clone, P::GTGadget: Clone, P::G1PreparedGadget: Clone, \
P::G2PreparedGadget: Clone, "
))]
pub struct PreparedVerifyingKeyGadget<
PairingE: PairingEngine,
ConstraintF: Field,
P: PairingGadget<PairingE, ConstraintF>,
> {
pub g_alpha: P::G1Gadget,
pub h_beta: P::G2Gadget,
pub g_alpha_pc: P::G1PreparedGadget,
pub h_beta_pc: P::G2PreparedGadget,
pub g_gamma_pc: P::G1PreparedGadget,
pub h_gamma_pc: P::G2PreparedGadget,
pub h_pc: P::G2PreparedGadget,
pub query: Vec<P::G1Gadget>,
}
pub struct Gm17VerifierGadget<PairingE, ConstraintF, P>
where
PairingE: PairingEngine,
ConstraintF: Field,
P: PairingGadget<PairingE, ConstraintF>,
{
_pairing_engine: PhantomData<PairingE>,
_engine: PhantomData<ConstraintF>,
_pairing_gadget: PhantomData<P>,
}
impl<PairingE, ConstraintF, P, C, V> NIZKVerifierGadget<Gm17<PairingE, C, V>, ConstraintF>
for Gm17VerifierGadget<PairingE, ConstraintF, P>
where
PairingE: PairingEngine,
ConstraintF: Field,
C: ConstraintSynthesizer<PairingE::Fr>,
V: ToConstraintField<PairingE::Fr>,
P: PairingGadget<PairingE, ConstraintF>,
{
type VerificationKeyGadget = VerifyingKeyGadget<PairingE, ConstraintF, P>;
type ProofGadget = ProofGadget<PairingE, ConstraintF, P>;
fn check_verify<'a, CS, I, T>(
mut cs: CS,
vk: &Self::VerificationKeyGadget,
mut public_inputs: I,
proof: &Self::ProofGadget,
) -> Result<(), SynthesisError>
where
CS: ConstraintSystem<ConstraintF>,
I: Iterator<Item = &'a T>,
T: 'a + ToBitsGadget<ConstraintF> + ?Sized,
{
let pvk = vk.prepare(&mut cs.ns(|| "Prepare vk"))?;
// e(A*G^{alpha}, B*H^{beta}) = e(G^{alpha}, H^{beta}) * e(G^{psi}, H^{gamma}) *
// e(C, H) where psi = \sum_{i=0}^l input_i pvk.query[i]
let g_psi = {
let mut cs = cs.ns(|| "Process input");
let mut g_psi = pvk.query[0].clone();
let mut input_len = 1;
for (i, (input, b)) in public_inputs
.by_ref()
.zip(pvk.query.iter().skip(1))
.enumerate()
{
let input_bits = input.to_bits(cs.ns(|| format!("Input {}", i)))?;
g_psi = b.mul_bits(cs.ns(|| format!("Mul {}", i)), &g_psi, input_bits.iter())?;
input_len += 1;
}
// Check that the input and the query in the verification are of the
// same length.
assert!(input_len == pvk.query.len() && public_inputs.next().is_none());
g_psi
};
let mut test1_a_g_alpha = proof.a.add(cs.ns(|| "A * G^{alpha}"), &pvk.g_alpha)?;
let test1_b_h_beta = proof.b.add(cs.ns(|| "B * H^{beta}"), &pvk.h_beta)?;
let test1_exp = {
test1_a_g_alpha = test1_a_g_alpha.negate(cs.ns(|| "neg 1"))?;
let test1_a_g_alpha_prep = P::prepare_g1(cs.ns(|| "First prep"), &test1_a_g_alpha)?;
let test1_b_h_beta_prep = P::prepare_g2(cs.ns(|| "Second prep"), &test1_b_h_beta)?;
let g_psi_prep = P::prepare_g1(cs.ns(|| "Third prep"), &g_psi)?;
let c_prep = P::prepare_g1(cs.ns(|| "Fourth prep"), &proof.c)?;
P::miller_loop(
cs.ns(|| "Miller loop 1"),
&[
test1_a_g_alpha_prep,
g_psi_prep,
c_prep,
pvk.g_alpha_pc.clone(),
],
&[
test1_b_h_beta_prep,
pvk.h_gamma_pc.clone(),
pvk.h_pc.clone(),
pvk.h_beta_pc.clone(),
],
)?
};
let test1 = P::final_exponentiation(cs.ns(|| "Final Exp 1"), &test1_exp).unwrap();
// e(A, H^{gamma}) = e(G^{gamma}, B)
let test2_exp = {
let a_prep = P::prepare_g1(cs.ns(|| "Fifth prep"), &proof.a)?;
// pvk.h_gamma_pc
//&pvk.g_gamma_pc
let proof_b = proof.b.negate(cs.ns(|| "Negate b"))?;
let b_prep = P::prepare_g2(cs.ns(|| "Sixth prep"), &proof_b)?;
P::miller_loop(
cs.ns(|| "Miller loop 4"),
&[a_prep, pvk.g_gamma_pc.clone()],
&[pvk.h_gamma_pc.clone(), b_prep],
)?
};
let test2 = P::final_exponentiation(cs.ns(|| "Final Exp 2"), &test2_exp)?;
let one = P::GTGadget::one(cs.ns(|| "GT One"))?;
test1.enforce_equal(cs.ns(|| "Test 1"), &one)?;
test2.enforce_equal(cs.ns(|| "Test 2"), &one)?;
Ok(())
}
}
impl<PairingE, ConstraintF, P> AllocGadget<VerifyingKey<PairingE>, ConstraintF>
for VerifyingKeyGadget<PairingE, ConstraintF, P>
where
PairingE: PairingEngine,
ConstraintF: Field,
P: PairingGadget<PairingE, ConstraintF>,
{
#[inline]
fn alloc<FN, T, CS: ConstraintSystem<ConstraintF>>(
mut cs: CS,
value_gen: FN,
) -> Result<Self, SynthesisError>
where
FN: FnOnce() -> Result<T, SynthesisError>,
T: Borrow<VerifyingKey<PairingE>>,
{
value_gen().and_then(|vk| {
let VerifyingKey {
h_g2,
g_alpha_g1,
h_beta_g2,
g_gamma_g1,
h_gamma_g2,
query,
} = vk.borrow().clone();
let h_g2 = P::G2Gadget::alloc(cs.ns(|| "h_g2"), || Ok(h_g2.into_projective()))?;
let g_alpha_g1 =
P::G1Gadget::alloc(cs.ns(|| "g_alpha"), || Ok(g_alpha_g1.into_projective()))?;
let h_beta_g2 =
P::G2Gadget::alloc(cs.ns(|| "h_beta"), || Ok(h_beta_g2.into_projective()))?;
let g_gamma_g1 =
P::G1Gadget::alloc(cs.ns(|| "g_gamma_g1"), || Ok(g_gamma_g1.into_projective()))?;
let h_gamma_g2 =
P::G2Gadget::alloc(cs.ns(|| "h_gamma_g2"), || Ok(h_gamma_g2.into_projective()))?;
let query = query
.into_iter()
.enumerate()
.map(|(i, query_i)| {
P::G1Gadget::alloc(cs.ns(|| format!("query_{}", i)), || {
Ok(query_i.into_projective())
})
})
.collect::<Vec<_>>()
.into_iter()
.collect::<Result<_, _>>()?;
Ok(Self {
h_g2,
g_alpha_g1,
h_beta_g2,
g_gamma_g1,
h_gamma_g2,
query,
})
})
}
#[inline]
fn alloc_input<FN, T, CS: ConstraintSystem<ConstraintF>>(
mut cs: CS,
value_gen: FN,
) -> Result<Self, SynthesisError>
where
FN: FnOnce() -> Result<T, SynthesisError>,
T: Borrow<VerifyingKey<PairingE>>,
{
value_gen().and_then(|vk| {
let VerifyingKey {
h_g2,
g_alpha_g1,
h_beta_g2,
g_gamma_g1,
h_gamma_g2,
query,
} = vk.borrow().clone();
let h_g2 = P::G2Gadget::alloc_input(cs.ns(|| "h_g2"), || Ok(h_g2.into_projective()))?;
let g_alpha_g1 =
P::G1Gadget::alloc_input(cs.ns(|| "g_alpha"), || Ok(g_alpha_g1.into_projective()))?;
let h_beta_g2 =
P::G2Gadget::alloc_input(cs.ns(|| "h_beta"), || Ok(h_beta_g2.into_projective()))?;
let g_gamma_g1 = P::G1Gadget::alloc_input(cs.ns(|| "g_gamma_g1"), || {
Ok(g_gamma_g1.into_projective())
})?;
let h_gamma_g2 = P::G2Gadget::alloc_input(cs.ns(|| "h_gamma_g2"), || {
Ok(h_gamma_g2.into_projective())
})?;
let query = query
.into_iter()
.enumerate()
.map(|(i, query_i)| {
P::G1Gadget::alloc_input(cs.ns(|| format!("query_{}", i)), || {
Ok(query_i.into_projective())
})
})
.collect::<Vec<_>>()
.into_iter()
.collect::<Result<_, _>>()?;
Ok(Self {
h_g2,
g_alpha_g1,
h_beta_g2,
g_gamma_g1,
h_gamma_g2,
query,
})
})
}
}
impl<PairingE, ConstraintF, P> AllocGadget<Proof<PairingE>, ConstraintF>
for ProofGadget<PairingE, ConstraintF, P>
where
PairingE: PairingEngine,
ConstraintF: Field,
P: PairingGadget<PairingE, ConstraintF>,
{
#[inline]
fn alloc<FN, T, CS: ConstraintSystem<ConstraintF>>(
mut cs: CS,
value_gen: FN,
) -> Result<Self, SynthesisError>
where
FN: FnOnce() -> Result<T, SynthesisError>,
T: Borrow<Proof<PairingE>>,
{
value_gen().and_then(|proof| {
let Proof { a, b, c } = proof.borrow().clone();
let a = P::G1Gadget::alloc_checked(cs.ns(|| "a"), || Ok(a.into_projective()))?;
let b = P::G2Gadget::alloc_checked(cs.ns(|| "b"), || Ok(b.into_projective()))?;
let c = P::G1Gadget::alloc_checked(cs.ns(|| "c"), || Ok(c.into_projective()))?;
Ok(Self { a, b, c })
})
}
#[inline]
fn alloc_input<FN, T, CS: ConstraintSystem<ConstraintF>>(
mut cs: CS,
value_gen: FN,
) -> Result<Self, SynthesisError>
where
FN: FnOnce() -> Result<T, SynthesisError>,
T: Borrow<Proof<PairingE>>,
{
value_gen().and_then(|proof| {
let Proof { a, b, c } = proof.borrow().clone();
// We don't need to check here because the prime order check can be performed
// in plain.
let a = P::G1Gadget::alloc_input(cs.ns(|| "a"), || Ok(a.into_projective()))?;
let b = P::G2Gadget::alloc_input(cs.ns(|| "b"), || Ok(b.into_projective()))?;
let c = P::G1Gadget::alloc_input(cs.ns(|| "c"), || Ok(c.into_projective()))?;
Ok(Self { a, b, c })
})
}
}
impl<PairingE, ConstraintF, P> ToBytesGadget<ConstraintF>
for VerifyingKeyGadget<PairingE, ConstraintF, P>
where
PairingE: PairingEngine,
ConstraintF: Field,
P: PairingGadget<PairingE, ConstraintF>,
{
#[inline]
fn to_bytes<CS: ConstraintSystem<ConstraintF>>(
&self,
mut cs: CS,
) -> Result<Vec<UInt8>, SynthesisError> {
let mut bytes = Vec::new();
bytes.extend_from_slice(&self.h_g2.to_bytes(&mut cs.ns(|| "h_g2 to bytes"))?);
bytes.extend_from_slice(
&self
.g_alpha_g1
.to_bytes(&mut cs.ns(|| "g_alpha_g1 to bytes"))?,
);
bytes.extend_from_slice(
&self
.h_beta_g2
.to_bytes(&mut cs.ns(|| "h_beta_g2 to bytes"))?,
);
bytes.extend_from_slice(
&self
.g_gamma_g1
.to_bytes(&mut cs.ns(|| "g_gamma_g1 to bytes"))?,
);
bytes.extend_from_slice(
&self
.h_gamma_g2
.to_bytes(&mut cs.ns(|| "h_gamma_g2 to bytes"))?,
);
for (i, q) in self.query.iter().enumerate() {
let mut cs = cs.ns(|| format!("Iteration {}", i));
bytes.extend_from_slice(&q.to_bytes(&mut cs.ns(|| "q"))?);
}
Ok(bytes)
}
fn to_bytes_strict<CS: ConstraintSystem<ConstraintF>>(
&self,
cs: CS,
) -> Result<Vec<UInt8>, SynthesisError> {
self.to_bytes(cs)
}
}
#[cfg(test)]
mod test {
use gm17::*;
use r1cs_core::{ConstraintSynthesizer, ConstraintSystem, SynthesisError};
use super::*;
use algebra::{
curves::bls12_377::Bls12_377,
fields::bls12_377::Fr,
fields::bls12_377::Fq,
BitIterator, PrimeField,
};
use rand::{thread_rng, Rng};
use r1cs_std::{
boolean::Boolean, pairing::bls12_377::PairingGadget as Bls12_377PairingGadget,
test_constraint_system::TestConstraintSystem,
};
type TestProofSystem = Gm17<Bls12_377, Bench<Fr>, Fr>;
type TestVerifierGadget = Gm17VerifierGadget<Bls12_377, Fq, Bls12_377PairingGadget>;
type TestProofGadget = ProofGadget<Bls12_377, Fq, Bls12_377PairingGadget>;
type TestVkGadget = VerifyingKeyGadget<Bls12_377, Fq, Bls12_377PairingGadget>;
struct Bench<F: Field> {
inputs: Vec<Option<F>>,
num_constraints: usize,
}
impl<F: Field> ConstraintSynthesizer<F> for Bench<F> {
fn generate_constraints<CS: ConstraintSystem<F>>(self, cs: &mut CS) -> Result<(), SynthesisError> {
assert!(self.inputs.len() >= 2);
assert!(self.num_constraints >= self.inputs.len());
let mut variables: Vec<_> = Vec::with_capacity(self.inputs.len());
for (i, input) in self.inputs.into_iter().enumerate() {
let input_var = cs.alloc_input(
|| format!("Input {}", i),
|| input.ok_or(SynthesisError::AssignmentMissing),
)?;
variables.push((input, input_var));
}
for i in 0..self.num_constraints {
let new_entry = {
let (input_1_val, input_1_var) = variables[i];
let (input_2_val, input_2_var) = variables[i + 1];
let result_val = input_1_val
.and_then(|input_1| input_2_val.map(|input_2| input_1 * &input_2));
let result_var = cs.alloc(
|| format!("Result {}", i),
|| result_val.ok_or(SynthesisError::AssignmentMissing),
)?;
cs.enforce(
|| format!("Enforce constraint {}", i),
|lc| lc + input_1_var,
|lc| lc + input_2_var,
|lc| lc + result_var,
);
(result_val, result_var)
};
variables.push(new_entry);
}
Ok(())
}
}
#[test]
fn gm17_verifier_test() {
let num_inputs = 100;
let num_constraints = num_inputs;
let rng = &mut thread_rng();
let mut inputs: Vec<Option<Fr>> = Vec::with_capacity(num_inputs);
for _ in 0..num_inputs {
inputs.push(Some(rng.gen()));
}
let params = {
let c = Bench::<Fr> {
inputs: vec![None; num_inputs],
num_constraints,
};
generate_random_parameters(c, rng).unwrap()
};
{
let proof = {
// Create an instance of our circuit (with the
// witness)
let c = Bench {
inputs: inputs.clone(),
num_constraints,
};
// Create a gm17 proof with our parameters.
create_random_proof(c, &params, rng).unwrap()
};
// assert!(!verify_proof(&pvk, &proof, &[a]).unwrap());
let mut cs = TestConstraintSystem::<Fq>::new();
let inputs: Vec<_> = inputs.into_iter().map(|input| input.unwrap()).collect();
let mut input_gadgets = Vec::new();
{
let mut cs = cs.ns(|| "Allocate Input");
for (i, input) in inputs.into_iter().enumerate() {
let mut input_bits = BitIterator::new(input.into_repr()).collect::<Vec<_>>();
// Input must be in little-endian, but BitIterator outputs in big-endian.
input_bits.reverse();
let input_bits =
Vec::<Boolean>::alloc_input(cs.ns(|| format!("Input {}", i)), || {
Ok(input_bits)
})
.unwrap();
input_gadgets.push(input_bits);
}
}
let vk_gadget = TestVkGadget::alloc_input(cs.ns(|| "Vk"), || Ok(&params.vk)).unwrap();
let proof_gadget =
TestProofGadget::alloc(cs.ns(|| "Proof"), || Ok(proof.clone())).unwrap();
println!("Time to verify!\n\n\n\n");
<TestVerifierGadget as NIZKVerifierGadget<TestProofSystem, Fq>>::check_verify(
cs.ns(|| "Verify"),
&vk_gadget,
input_gadgets.iter(),
&proof_gadget,
)
.unwrap();
if !cs.is_satisfied() {
println!("=========================================================");
println!("Unsatisfied constraints:");
println!("{:?}", cs.which_is_unsatisfied().unwrap());
println!("=========================================================");
}
// cs.print_named_objects();
assert!(cs.is_satisfied());
}
}
}

+ 81
- 0
crypto-primitives/src/nizk/gm17/mod.rs

@ -0,0 +1,81 @@
use algebra::PairingEngine;
use crate::Error;
use rand::Rng;
use gm17::{
create_random_proof, generate_random_parameters, prepare_verifying_key, verify_proof,
Parameters, PreparedVerifyingKey, Proof, VerifyingKey,
};
use r1cs_core::ConstraintSynthesizer;
use algebra::ToConstraintField;
use std::marker::PhantomData;
use super::NIZK;
#[cfg(feature = "r1cs")]
pub mod constraints;
/// Note: V should serialize its contents to `Vec<E::Fr>` in the same order as
/// during the constraint generation.
pub struct Gm17<E: PairingEngine, C: ConstraintSynthesizer<E::Fr>, V: ToConstraintField<E::Fr> + ?Sized> {
#[doc(hidden)]
_engine: PhantomData<E>,
#[doc(hidden)]
_circuit: PhantomData<C>,
#[doc(hidden)]
_verifier_input: PhantomData<V>,
}
impl<E: PairingEngine, C: ConstraintSynthesizer<E::Fr>, V: ToConstraintField<E::Fr> + ?Sized> NIZK for Gm17<E, C, V> {
type Circuit = C;
type AssignedCircuit = C;
type ProvingParameters = Parameters<E>;
type VerificationParameters = VerifyingKey<E>;
type PreparedVerificationParameters = PreparedVerifyingKey<E>;
type VerifierInput = V;
type Proof = Proof<E>;
fn setup<R: Rng>(
circuit: Self::Circuit,
rng: &mut R,
) -> Result<
(
Self::ProvingParameters,
Self::PreparedVerificationParameters,
),
Error,
> {
let nizk_time = start_timer!(|| "{Groth-Maller 2017}::Setup");
let pp = generate_random_parameters::<E, Self::Circuit, R>(circuit, rng)?;
let vk = prepare_verifying_key(&pp.vk);
end_timer!(nizk_time);
Ok((pp, vk))
}
fn prove<R: Rng>(
pp: &Self::ProvingParameters,
input_and_witness: Self::AssignedCircuit,
rng: &mut R,
) -> Result<Self::Proof, Error> {
let proof_time = start_timer!(|| "{Groth-Maller 2017}::Prove");
let result = create_random_proof::<E, _, _>(input_and_witness, pp, rng)?;
end_timer!(proof_time);
Ok(result)
}
fn verify(
vk: &Self::PreparedVerificationParameters,
input: &Self::VerifierInput,
proof: &Self::Proof,
) -> Result<bool, Error> {
let verify_time = start_timer!(|| "{Groth-Maller 2017}::Verify");
let conversion_time = start_timer!(|| "Convert input to E::Fr");
let input = input.to_field_elements()?;
end_timer!(conversion_time);
let verification = start_timer!(|| format!("Verify proof w/ input len: {}", input.len()));
let result = verify_proof(&vk, proof, &input)?;
end_timer!(verification);
end_timer!(verify_time);
Ok(result)
}
}

+ 112
- 0
crypto-primitives/src/nizk/mod.rs

@ -0,0 +1,112 @@
use algebra::bytes::ToBytes;
use rand::Rng;
#[cfg(feature = "gm17")]
pub mod gm17;
#[cfg(feature = "gm17")]
pub use self::gm17::Gm17;
#[cfg(feature = "r1cs")]
pub mod constraints;
#[cfg(feature = "r1cs")]
pub use constraints::*;
use crate::Error;
pub trait NIZK {
type Circuit;
type AssignedCircuit;
type VerifierInput: ?Sized;
type ProvingParameters: Clone;
type VerificationParameters: Clone + Default + From<Self::PreparedVerificationParameters>;
type PreparedVerificationParameters: Clone + Default + From<Self::VerificationParameters>;
type Proof: ToBytes + Clone + Default;
fn setup<R: Rng>(
circuit: Self::Circuit,
rng: &mut R,
) -> Result<
(
Self::ProvingParameters,
Self::PreparedVerificationParameters,
),
Error,
>;
fn prove<R: Rng>(
parameter: &Self::ProvingParameters,
input_and_witness: Self::AssignedCircuit,
rng: &mut R,
) -> Result<Self::Proof, Error>;
fn verify(
verifier_key: &Self::PreparedVerificationParameters,
input: &Self::VerifierInput,
proof: &Self::Proof,
) -> Result<bool, Error>;
}
#[cfg(all(feature = "gm17", test))]
mod test {
use rand::thread_rng;
use std::ops::AddAssign;
#[test]
fn test_gm17() {
use crate::nizk::{gm17::Gm17, NIZK};
use algebra::{curves::bls12_381::Bls12_381, fields::bls12_381::Fr, Field};
use r1cs_core::{ConstraintSynthesizer, ConstraintSystem, SynthesisError};
#[derive(Copy, Clone)]
struct R1CSCircuit {
x: Option<Fr>,
sum: Option<Fr>,
w: Option<Fr>,
}
impl R1CSCircuit {
pub(super) fn new(x: Fr, sum: Fr, w: Fr) -> Self {
Self {
x: Some(x),
sum: Some(sum),
w: Some(w),
}
}
}
impl ConstraintSynthesizer<Fr> for R1CSCircuit {
fn generate_constraints<CS: ConstraintSystem<Fr>>(
self,
cs: &mut CS,
) -> Result<(), SynthesisError> {
let input = cs.alloc_input(|| "x", || Ok(self.x.unwrap()))?;
let sum = cs.alloc_input(|| "sum", || Ok(self.sum.unwrap()))?;
let witness = cs.alloc(|| "w", || Ok(self.w.unwrap()))?;
cs.enforce(
|| "check_one",
|lc| lc + sum,
|lc| lc + CS::one(),
|lc| lc + input + witness,
);
Ok(())
}
}
let mut sum = Fr::one();
sum.add_assign(&Fr::one());
let circuit = R1CSCircuit::new(Fr::one(), sum, Fr::one());
let rng = &mut thread_rng();
let parameters = Gm17::<Bls12_381, R1CSCircuit, [Fr]>::setup(circuit, rng).unwrap();
let proof =
Gm17::<Bls12_381, R1CSCircuit, [Fr]>::prove(&parameters.0, circuit, rng).unwrap();
let result =
Gm17::<Bls12_381, R1CSCircuit, [Fr]>::verify(&parameters.1, &[Fr::one(), sum], &proof)
.unwrap();
assert!(result);
}
}

+ 650
- 0
crypto-primitives/src/prf/blake2s/constraints.rs

@ -0,0 +1,650 @@
use algebra::PrimeField;
use r1cs_core::{ConstraintSystem, SynthesisError};
use crate::prf::PRFGadget;
use r1cs_std::prelude::*;
use std::borrow::Borrow;
// 2.1. Parameters
// The following table summarizes various parameters and their ranges:
// | BLAKE2b | BLAKE2s |
// --------------+------------------+------------------+
// Bits in word | w = 64 | w = 32 |
// Rounds in F | r = 12 | r = 10 |
// Block bytes | bb = 128 | bb = 64 |
// Hash bytes | 1 <= nn <= 64 | 1 <= nn <= 32 |
// Key bytes | 0 <= kk <= 64 | 0 <= kk <= 32 |
// Input bytes | 0 <= ll < 2**128 | 0 <= ll < 2**64 |
// --------------+------------------+------------------+
// G Rotation | (R1, R2, R3, R4) | (R1, R2, R3, R4) |
// constants = | (32, 24, 16, 63) | (16, 12, 8, 7) |
// --------------+------------------+------------------+
//
const R1: usize = 16;
const R2: usize = 12;
const R3: usize = 8;
const R4: usize = 7;
// Round | 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
// ----------+-------------------------------------------------+
// SIGMA[0] | 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
// SIGMA[1] | 14 10 4 8 9 15 13 6 1 12 0 2 11 7 5 3 |
// SIGMA[2] | 11 8 12 0 5 2 15 13 10 14 3 6 7 1 9 4 |
// SIGMA[3] | 7 9 3 1 13 12 11 14 2 6 5 10 4 0 15 8 |
// SIGMA[4] | 9 0 5 7 2 4 10 15 14 1 11 12 6 8 3 13 |
// SIGMA[5] | 2 12 6 10 0 11 8 3 4 13 7 5 15 14 1 9 |
// SIGMA[6] | 12 5 1 15 14 13 4 10 0 7 6 3 9 2 8 11 |
// SIGMA[7] | 13 11 7 14 12 1 3 9 5 0 15 4 8 6 2 10 |
// SIGMA[8] | 6 15 14 9 11 3 0 8 12 2 13 7 1 4 10 5 |
// SIGMA[9] | 10 2 8 4 7 6 1 5 15 11 9 14 3 12 13 0 |
// ----------+-------------------------------------------------+
//
const SIGMA: [[usize; 16]; 10] = [
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15],
[14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3],
[11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4],
[7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8],
[9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13],
[2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9],
[12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11],
[13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10],
[6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5],
[10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0],
];
// 3.1. Mixing Function G
// The G primitive function mixes two input words, "x" and "y", into
// four words indexed by "a", "b", "c", and "d" in the working vector
// v[0..15]. The full modified vector is returned. The rotation
// constants (R1, R2, R3, R4) are given in Section 2.1.
// FUNCTION G( v[0..15], a, b, c, d, x, y )
// |
// | v[a] := (v[a] + v[b] + x) mod 2**w
// | v[d] := (v[d] ^ v[a]) >>> R1
// | v[c] := (v[c] + v[d]) mod 2**w
// | v[b] := (v[b] ^ v[c]) >>> R2
// | v[a] := (v[a] + v[b] + y) mod 2**w
// | v[d] := (v[d] ^ v[a]) >>> R3
// | v[c] := (v[c] + v[d]) mod 2**w
// | v[b] := (v[b] ^ v[c]) >>> R4
// |
// | RETURN v[0..15]
// |
// END FUNCTION.
//
fn mixing_g<ConstraintF: PrimeField, CS: ConstraintSystem<ConstraintF>>(
mut cs: CS,
v: &mut [UInt32],
a: usize,
b: usize,
c: usize,
d: usize,
x: &UInt32,
y: &UInt32,
) -> Result<(), SynthesisError> {
v[a] = UInt32::addmany(
cs.ns(|| "mixing step 1"),
&[v[a].clone(), v[b].clone(), x.clone()],
)?;
v[d] = v[d].xor(cs.ns(|| "mixing step 2"), &v[a])?.rotr(R1);
v[c] = UInt32::addmany(cs.ns(|| "mixing step 3"), &[v[c].clone(), v[d].clone()])?;
v[b] = v[b].xor(cs.ns(|| "mixing step 4"), &v[c])?.rotr(R2);
v[a] = UInt32::addmany(
cs.ns(|| "mixing step 5"),
&[v[a].clone(), v[b].clone(), y.clone()],
)?;
v[d] = v[d].xor(cs.ns(|| "mixing step 6"), &v[a])?.rotr(R3);
v[c] = UInt32::addmany(cs.ns(|| "mixing step 7"), &[v[c].clone(), v[d].clone()])?;
v[b] = v[b].xor(cs.ns(|| "mixing step 8"), &v[c])?.rotr(R4);
Ok(())
}
// 3.2. Compression Function F
// Compression function F takes as an argument the state vector "h",
// message block vector "m" (last block is padded with zeros to full
// block size, if required), 2w-bit_gadget offset counter "t", and final block
// indicator flag "f". Local vector v[0..15] is used in processing. F
// returns a new state vector. The number of rounds, "r", is 12 for
// BLAKE2b and 10 for BLAKE2s. Rounds are numbered from 0 to r - 1.
// FUNCTION F( h[0..7], m[0..15], t, f )
// |
// | // Initialize local work vector v[0..15]
// | v[0..7] := h[0..7] // First half from state.
// | v[8..15] := IV[0..7] // Second half from IV.
// |
// | v[12] := v[12] ^ (t mod 2**w) // Low word of the offset.
// | v[13] := v[13] ^ (t >> w) // High word.
// |
// | IF f = TRUE THEN // last block flag?
// | | v[14] := v[14] ^ 0xFF..FF // Invert all bits.
// | END IF.
// |
// | // Cryptographic mixing
// | FOR i = 0 TO r - 1 DO // Ten or twelve rounds.
// | |
// | | // Message word selection permutation for this round.
// | | s[0..15] := SIGMA[i mod 10][0..15]
// | |
// | | v := G( v, 0, 4, 8, 12, m[s[ 0]], m[s[ 1]] )
// | | v := G( v, 1, 5, 9, 13, m[s[ 2]], m[s[ 3]] )
// | | v := G( v, 2, 6, 10, 14, m[s[ 4]], m[s[ 5]] )
// | | v := G( v, 3, 7, 11, 15, m[s[ 6]], m[s[ 7]] )
// | |
// | | v := G( v, 0, 5, 10, 15, m[s[ 8]], m[s[ 9]] )
// | | v := G( v, 1, 6, 11, 12, m[s[10]], m[s[11]] )
// | | v := G( v, 2, 7, 8, 13, m[s[12]], m[s[13]] )
// | | v := G( v, 3, 4, 9, 14, m[s[14]], m[s[15]] )
// | |
// | END FOR
// |
// | FOR i = 0 TO 7 DO // XOR the two halves.
// | | h[i] := h[i] ^ v[i] ^ v[i + 8]
// | END FOR.
// |
// | RETURN h[0..7] // New state.
// |
// END FUNCTION.
//
fn blake2s_compression<ConstraintF: PrimeField, CS: ConstraintSystem<ConstraintF>>(
mut cs: CS,
h: &mut [UInt32],
m: &[UInt32],
t: u64,
f: bool,
) -> Result<(), SynthesisError> {
assert_eq!(h.len(), 8);
assert_eq!(m.len(), 16);
// static const uint32_t blake2s_iv[8] =
// {
// 0x6A09E667, 0xBB67AE85, 0x3C6EF372, 0xA54FF53A,
// 0x510E527F, 0x9B05688C, 0x1F83D9AB, 0x5BE0CD19
// };
//
let mut v = Vec::with_capacity(16);
v.extend_from_slice(h);
v.push(UInt32::constant(0x6A09E667));
v.push(UInt32::constant(0xBB67AE85));
v.push(UInt32::constant(0x3C6EF372));
v.push(UInt32::constant(0xA54FF53A));
v.push(UInt32::constant(0x510E527F));
v.push(UInt32::constant(0x9B05688C));
v.push(UInt32::constant(0x1F83D9AB));
v.push(UInt32::constant(0x5BE0CD19));
assert_eq!(v.len(), 16);
v[12] = v[12].xor(cs.ns(|| "first xor"), &UInt32::constant(t as u32))?;
v[13] = v[13].xor(cs.ns(|| "second xor"), &UInt32::constant((t >> 32) as u32))?;
if f {
v[14] = v[14].xor(cs.ns(|| "third xor"), &UInt32::constant(u32::max_value()))?;
}
for i in 0..10 {
let mut cs = cs.ns(|| format!("round {}", i));
let s = SIGMA[i % 10];
mixing_g(
cs.ns(|| "mixing invocation 1"),
&mut v,
0,
4,
8,
12,
&m[s[0]],
&m[s[1]],
)?;
mixing_g(
cs.ns(|| "mixing invocation 2"),
&mut v,
1,
5,
9,
13,
&m[s[2]],
&m[s[3]],
)?;
mixing_g(
cs.ns(|| "mixing invocation 3"),
&mut v,
2,
6,
10,
14,
&m[s[4]],
&m[s[5]],
)?;
mixing_g(
cs.ns(|| "mixing invocation 4"),
&mut v,
3,
7,
11,
15,
&m[s[6]],
&m[s[7]],
)?;
mixing_g(
cs.ns(|| "mixing invocation 5"),
&mut v,
0,
5,
10,
15,
&m[s[8]],
&m[s[9]],
)?;
mixing_g(
cs.ns(|| "mixing invocation 6"),
&mut v,
1,
6,
11,
12,
&m[s[10]],
&m[s[11]],
)?;
mixing_g(
cs.ns(|| "mixing invocation 7"),
&mut v,
2,
7,
8,
13,
&m[s[12]],
&m[s[13]],
)?;
mixing_g(
cs.ns(|| "mixing invocation 8"),
&mut v,
3,
4,
9,
14,
&m[s[14]],
&m[s[15]],
)?;
}
for i in 0..8 {
let mut cs = cs.ns(|| format!("h[{i}] ^ v[{i}] ^ v[{i} + 8]", i = i));
h[i] = h[i].xor(cs.ns(|| "first xor"), &v[i])?;
h[i] = h[i].xor(cs.ns(|| "second xor"), &v[i + 8])?;
}
Ok(())
}
// FUNCTION BLAKE2( d[0..dd-1], ll, kk, nn )
// |
// | h[0..7] := IV[0..7] // Initialization Vector.
// |
// | // Parameter block p[0]
// | h[0] := h[0] ^ 0x01010000 ^ (kk << 8) ^ nn
// |
// | // Process padded key and data blocks
// | IF dd > 1 THEN
// | | FOR i = 0 TO dd - 2 DO
// | | | h := F( h, d[i], (i + 1) * bb, FALSE )
// | | END FOR.
// | END IF.
// |
// | // Final block.
// | IF kk = 0 THEN
// | | h := F( h, d[dd - 1], ll, TRUE )
// | ELSE
// | | h := F( h, d[dd - 1], ll + bb, TRUE )
// | END IF.
// |
// | RETURN first "nn" bytes from little-endian word array h[].
// |
// END FUNCTION.
//
pub fn blake2s_gadget<ConstraintF: PrimeField, CS: ConstraintSystem<ConstraintF>>(
mut cs: CS,
input: &[Boolean],
) -> Result<Vec<UInt32>, SynthesisError> {
assert!(input.len() % 8 == 0);
let mut h = Vec::with_capacity(8);
h.push(UInt32::constant(0x6A09E667 ^ 0x01010000 ^ 32));
h.push(UInt32::constant(0xBB67AE85));
h.push(UInt32::constant(0x3C6EF372));
h.push(UInt32::constant(0xA54FF53A));
h.push(UInt32::constant(0x510E527F));
h.push(UInt32::constant(0x9B05688C));
h.push(UInt32::constant(0x1F83D9AB));
h.push(UInt32::constant(0x5BE0CD19));
let mut blocks: Vec<Vec<UInt32>> = vec![];
for block in input.chunks(512) {
let mut this_block = Vec::with_capacity(16);
for word in block.chunks(32) {
let mut tmp = word.to_vec();
while tmp.len() < 32 {
tmp.push(Boolean::constant(false));
}
this_block.push(UInt32::from_bits_le(&tmp));
}
while this_block.len() < 16 {
this_block.push(UInt32::constant(0));
}
blocks.push(this_block);
}
if blocks.is_empty() {
blocks.push((0..16).map(|_| UInt32::constant(0)).collect());
}
for (i, block) in blocks[0..blocks.len() - 1].iter().enumerate() {
let cs = cs.ns(|| format!("block {}", i));
blake2s_compression(cs, &mut h, block, ((i as u64) + 1) * 64, false)?;
}
{
let cs = cs.ns(|| "final block");
blake2s_compression(
cs,
&mut h,
&blocks[blocks.len() - 1],
(input.len() / 8) as u64,
true,
)?;
}
Ok(h)
}
use crate::prf::Blake2s;
pub struct Blake2sGadget;
#[derive(Clone, Debug)]
pub struct Blake2sOutputGadget(pub Vec<UInt8>);
impl PartialEq for Blake2sOutputGadget {
fn eq(&self, other: &Self) -> bool {
self.0 == other.0
}
}
impl Eq for Blake2sOutputGadget {}
impl<ConstraintF: PrimeField> EqGadget<ConstraintF> for Blake2sOutputGadget {}
impl<ConstraintF: PrimeField> ConditionalEqGadget<ConstraintF> for Blake2sOutputGadget {
#[inline]
fn conditional_enforce_equal<CS: ConstraintSystem<ConstraintF>>(
&self,
mut cs: CS,
other: &Self,
condition: &Boolean,
) -> Result<(), SynthesisError> {
for (i, (a, b)) in self.0.iter().zip(other.0.iter()).enumerate() {
a.conditional_enforce_equal(
&mut cs.ns(|| format!("blake2s_equal_{}", i)),
b,
condition,
)?;
}
Ok(())
}
fn cost() -> usize {
32 * <UInt8 as ConditionalEqGadget<ConstraintF>>::cost()
}
}
impl<ConstraintF: PrimeField> ToBytesGadget<ConstraintF> for Blake2sOutputGadget {
#[inline]
fn to_bytes<CS: ConstraintSystem<ConstraintF>>(&self, _cs: CS) -> Result<Vec<UInt8>, SynthesisError> {
Ok(self.0.clone())
}
#[inline]
fn to_bytes_strict<CS: ConstraintSystem<ConstraintF>>(
&self,
cs: CS,
) -> Result<Vec<UInt8>, SynthesisError> {
self.to_bytes(cs)
}
}
impl<ConstraintF: PrimeField> AllocGadget<[u8; 32], ConstraintF> for Blake2sOutputGadget {
#[inline]
fn alloc<F, T, CS: ConstraintSystem<ConstraintF>>(cs: CS, value_gen: F) -> Result<Self, SynthesisError>
where
F: FnOnce() -> Result<T, SynthesisError>,
T: Borrow<[u8; 32]>,
{
let zeros = [0u8; 32];
let value = match value_gen() {
Ok(val) => *(val.borrow()),
Err(_) => zeros,
};
let bytes = <UInt8>::alloc_vec(cs, &value)?;
Ok(Blake2sOutputGadget(bytes))
}
#[inline]
fn alloc_input<F, T, CS: ConstraintSystem<ConstraintF>>(
cs: CS,
value_gen: F,
) -> Result<Self, SynthesisError>
where
F: FnOnce() -> Result<T, SynthesisError>,
T: Borrow<[u8; 32]>,
{
let zeros = [0u8; 32];
let value = match value_gen() {
Ok(val) => *(val.borrow()),
Err(_) => zeros,
};
let bytes = <UInt8>::alloc_input_vec(cs, &value)?;
Ok(Blake2sOutputGadget(bytes))
}
}
impl<ConstraintF: PrimeField> PRFGadget<Blake2s, ConstraintF> for Blake2sGadget {
type OutputGadget = Blake2sOutputGadget;
fn new_seed<CS: ConstraintSystem<ConstraintF>>(mut cs: CS, seed: &[u8; 32]) -> Vec<UInt8> {
UInt8::alloc_vec(&mut cs.ns(|| "alloc_seed"), seed).unwrap()
}
fn check_evaluation_gadget<CS: ConstraintSystem<ConstraintF>>(
mut cs: CS,
seed: &[UInt8],
input: &[UInt8],
) -> Result<Self::OutputGadget, SynthesisError> {
assert_eq!(seed.len(), 32);
// assert_eq!(input.len(), 32);
let mut gadget_input = Vec::with_capacity(512);
for byte in seed.into_iter().chain(input) {
gadget_input.extend_from_slice(&byte.into_bits_le());
}
let mut result = Vec::new();
for (i, int) in blake2s_gadget(cs.ns(|| "Blake2s Eval"), &gadget_input)?
.into_iter()
.enumerate()
{
let chunk = int.to_bytes(&mut cs.ns(|| format!("Result ToBytes {}", i)))?;
result.extend_from_slice(&chunk);
}
Ok(Blake2sOutputGadget(result))
}
}
#[cfg(test)]
mod test {
use algebra::fields::bls12_377::fr::Fr;
use digest::{FixedOutput, Input};
use rand::{Rng, SeedableRng};
use rand_xorshift::XorShiftRng;
use crate::prf::blake2s::{Blake2s as B2SPRF, constraints::blake2s_gadget};
use blake2::Blake2s;
use r1cs_core::ConstraintSystem;
use super::Blake2sGadget;
use r1cs_std::{prelude::*, boolean::AllocatedBit, test_constraint_system::TestConstraintSystem};
#[test]
fn test_blake2s_constraints() {
let mut cs = TestConstraintSystem::<Fr>::new();
let input_bits: Vec<_> = (0..512)
.map(|i| {
AllocatedBit::alloc(cs.ns(|| format!("input bit_gadget {}", i)), || Ok(true))
.unwrap()
.into()
})
.collect();
blake2s_gadget(&mut cs, &input_bits).unwrap();
assert!(cs.is_satisfied());
assert_eq!(cs.num_constraints(), 21792);
}
#[test]
fn test_blake2s_prf() {
use crate::prf::{PRF, PRFGadget};
use rand::Rng;
let mut rng = XorShiftRng::seed_from_u64(1231275789u64);
let mut cs = TestConstraintSystem::<Fr>::new();
let mut seed = [0u8; 32];
rng.fill(&mut seed);
let mut input = [0u8; 32];
rng.fill(&mut input);
let seed_gadget = Blake2sGadget::new_seed(&mut cs.ns(|| "declare_seed"), &seed);
let input_gadget = UInt8::alloc_vec(&mut cs.ns(|| "declare_input"), &input).unwrap();
let out = B2SPRF::evaluate(&seed, &input).unwrap();
let actual_out_gadget = <Blake2sGadget as PRFGadget<_, Fr>>::OutputGadget::alloc(
&mut cs.ns(|| "declare_output"),
|| Ok(out),
)
.unwrap();
let output_gadget = Blake2sGadget::check_evaluation_gadget(
&mut cs.ns(|| "eval_blake2s"),
&seed_gadget,
&input_gadget,
)
.unwrap();
output_gadget
.enforce_equal(&mut cs, &actual_out_gadget)
.unwrap();
if !cs.is_satisfied() {
println!(
"which is unsatisfied: {:?}",
cs.which_is_unsatisfied().unwrap()
);
}
assert!(cs.is_satisfied());
}
#[test]
fn test_blake2s_precomp_constraints() {
// Test that 512 fixed leading bits (constants)
// doesn't result in more constraints.
let mut cs = TestConstraintSystem::<Fr>::new();
let mut rng = XorShiftRng::seed_from_u64(1231275789u64);
let input_bits: Vec<_> = (0..512)
.map(|_| Boolean::constant(rng.gen()))
.chain((0..512).map(|i| {
AllocatedBit::alloc(cs.ns(|| format!("input bit_gadget {}", i)), || Ok(true))
.unwrap()
.into()
}))
.collect();
blake2s_gadget(&mut cs, &input_bits).unwrap();
assert!(cs.is_satisfied());
assert_eq!(cs.num_constraints(), 21792);
}
#[test]
fn test_blake2s_constant_constraints() {
let mut cs = TestConstraintSystem::<Fr>::new();
let mut rng = XorShiftRng::seed_from_u64(1231275789u64);
let input_bits: Vec<_> = (0..512).map(|_| Boolean::constant(rng.gen())).collect();
blake2s_gadget(&mut cs, &input_bits).unwrap();
assert_eq!(cs.num_constraints(), 0);
}
#[test]
fn test_blake2s() {
let mut rng = XorShiftRng::seed_from_u64(1231275789u64);
for input_len in (0..32).chain((32..256).filter(|a| a % 8 == 0)) {
let mut h = Blake2s::new_keyed(&[], 32);
let data: Vec<u8> = (0..input_len).map(|_| rng.gen()).collect();
h.process(&data);
let hash_result = h.fixed_result();
let mut cs = TestConstraintSystem::<Fr>::new();
let mut input_bits = vec![];
for (byte_i, input_byte) in data.into_iter().enumerate() {
for bit_i in 0..8 {
let cs = cs.ns(|| format!("input bit_gadget {} {}", byte_i, bit_i));
input_bits.push(
AllocatedBit::alloc(cs, || Ok((input_byte >> bit_i) & 1u8 == 1u8))
.unwrap()
.into(),
);
}
}
let r = blake2s_gadget(&mut cs, &input_bits).unwrap();
assert!(cs.is_satisfied());
let mut s = hash_result
.as_ref()
.iter()
.flat_map(|&byte| (0..8).map(move |i| (byte >> i) & 1u8 == 1u8));
for chunk in r {
for b in chunk.to_bits_le() {
match b {
Boolean::Is(b) => {
assert!(s.next().unwrap() == b.get_value().unwrap());
},
Boolean::Not(b) => {
assert!(s.next().unwrap() != b.get_value().unwrap());
},
Boolean::Constant(b) => {
assert!(input_len == 0);
assert!(s.next().unwrap() == b);
},
}
}
}
}
}
}

+ 28
- 0
crypto-primitives/src/prf/blake2s/mod.rs

@ -0,0 +1,28 @@
use blake2::Blake2s as b2s;
use digest::Digest;
use super::PRF;
use crate::CryptoError;
#[cfg(feature = "r1cs")]
pub mod constraints;
#[derive(Clone)]
pub struct Blake2s;
impl PRF for Blake2s {
type Input = [u8; 32];
type Output = [u8; 32];
type Seed = [u8; 32];
fn evaluate(seed: &Self::Seed, input: &Self::Input) -> Result<Self::Output, CryptoError> {
let eval_time = start_timer!(|| "Blake2s::Eval");
let mut h = b2s::new();
h.input(seed.as_ref());
h.input(input.as_ref());
let mut result = [0u8; 32];
result.copy_from_slice(&h.result());
end_timer!(eval_time);
Ok(result)
}
}

+ 19
- 0
crypto-primitives/src/prf/constraints.rs

@ -0,0 +1,19 @@
use algebra::Field;
use std::fmt::Debug;
use crate::prf::PRF;
use r1cs_core::{ConstraintSystem, SynthesisError};
use r1cs_std::prelude::*;
pub trait PRFGadget<P: PRF, ConstraintF: Field> {
type OutputGadget: EqGadget<ConstraintF> + ToBytesGadget<ConstraintF> + AllocGadget<P::Output, ConstraintF> + Clone + Debug;
fn new_seed<CS: ConstraintSystem<ConstraintF>>(cs: CS, output: &P::Seed) -> Vec<UInt8>;
fn check_evaluation_gadget<CS: ConstraintSystem<ConstraintF>>(
cs: CS,
seed: &[UInt8],
input: &[UInt8],
) -> Result<Self::OutputGadget, SynthesisError>;
}

+ 20
- 0
crypto-primitives/src/prf/mod.rs

@ -0,0 +1,20 @@
use algebra::bytes::{FromBytes, ToBytes};
use std::{fmt::Debug, hash::Hash};
use crate::CryptoError;
#[cfg(feature = "r1cs")]
pub mod constraints;
#[cfg(feature = "r1cs")]
pub use constraints::*;
pub mod blake2s;
pub use self::blake2s::*;
pub trait PRF {
type Input: FromBytes + Default;
type Output: ToBytes + Eq + Clone + Default + Hash;
type Seed: FromBytes + ToBytes + Clone + Default + Debug;
fn evaluate(seed: &Self::Seed, input: &Self::Input) -> Result<Self::Output, CryptoError>;
}

+ 18
- 0
crypto-primitives/src/signature/constraints.rs

@ -0,0 +1,18 @@
use algebra::Field;
use r1cs_core::{ConstraintSystem, SynthesisError};
use r1cs_std::prelude::*;
use crate::signature::SignatureScheme;
pub trait SigRandomizePkGadget<S: SignatureScheme, ConstraintF: Field> {
type ParametersGadget: AllocGadget<S::Parameters, ConstraintF> + Clone;
type PublicKeyGadget: ToBytesGadget<ConstraintF> + EqGadget<ConstraintF> + AllocGadget<S::PublicKey, ConstraintF> + Clone;
fn check_randomization_gadget<CS: ConstraintSystem<ConstraintF>>(
cs: CS,
parameters: &Self::ParametersGadget,
public_key: &Self::PublicKeyGadget,
randomness: &[UInt8],
) -> Result<Self::PublicKeyGadget, SynthesisError>;
}

+ 106
- 0
crypto-primitives/src/signature/mod.rs

@ -0,0 +1,106 @@
use algebra::bytes::ToBytes;
use crate::Error;
use rand::Rng;
use std::hash::Hash;
#[cfg(feature = "r1cs")]
pub mod constraints;
#[cfg(feature = "r1cs")]
pub use constraints::*;
pub mod schnorr;
pub trait SignatureScheme {
type Parameters: Clone + Send + Sync;
type PublicKey: ToBytes + Hash + Eq + Clone + Default + Send + Sync;
type SecretKey: ToBytes + Clone + Default;
type Signature: Clone + Default + Send + Sync;
fn setup<R: Rng>(rng: &mut R) -> Result<Self::Parameters, Error>;
fn keygen<R: Rng>(
pp: &Self::Parameters,
rng: &mut R,
) -> Result<(Self::PublicKey, Self::SecretKey), Error>;
fn sign<R: Rng>(
pp: &Self::Parameters,
sk: &Self::SecretKey,
message: &[u8],
rng: &mut R,
) -> Result<Self::Signature, Error>;
fn verify(
pp: &Self::Parameters,
pk: &Self::PublicKey,
message: &[u8],
signature: &Self::Signature,
) -> Result<bool, Error>;
fn randomize_public_key(
pp: &Self::Parameters,
public_key: &Self::PublicKey,
randomness: &[u8],
) -> Result<Self::PublicKey, Error>;
fn randomize_signature(
pp: &Self::Parameters,
signature: &Self::Signature,
randomness: &[u8],
) -> Result<Self::Signature, Error>;
}
#[cfg(test)]
mod test {
use crate::{signature::schnorr::SchnorrSignature, SignatureScheme};
use algebra::{
curves::edwards_sw6::EdwardsAffine as Edwards, groups::Group, to_bytes, ToBytes,
};
use blake2::Blake2s;
use rand::thread_rng;
use algebra::UniformRand;
fn sign_and_verify<S: SignatureScheme>(message: &[u8]) {
let rng = &mut thread_rng();
let parameters = S::setup::<_>(rng).unwrap();
let (pk, sk) = S::keygen(&parameters, rng).unwrap();
let sig = S::sign(&parameters, &sk, &message, rng).unwrap();
assert!(S::verify(&parameters, &pk, &message, &sig).unwrap());
}
fn failed_verification<S: SignatureScheme>(message: &[u8], bad_message: &[u8]) {
let rng = &mut thread_rng();
let parameters = S::setup::<_>(rng).unwrap();
let (pk, sk) = S::keygen(&parameters, rng).unwrap();
let sig = S::sign(&parameters, &sk, message, rng).unwrap();
assert!(!S::verify(&parameters, &pk, bad_message, &sig).unwrap());
}
fn randomize_and_verify<S: SignatureScheme>(message: &[u8], randomness: &[u8]) {
let rng = &mut thread_rng();
let parameters = S::setup::<_>(rng).unwrap();
let (pk, sk) = S::keygen(&parameters, rng).unwrap();
let sig = S::sign(&parameters, &sk, message, rng).unwrap();
assert!(S::verify(&parameters, &pk, message, &sig).unwrap());
let randomized_pk = S::randomize_public_key(&parameters, &pk, randomness).unwrap();
let randomized_sig = S::randomize_signature(&parameters, &sig, randomness).unwrap();
assert!(S::verify(&parameters, &randomized_pk, &message, &randomized_sig).unwrap());
}
#[test]
fn schnorr_signature_test() {
let message = "Hi, I am a Schnorr signature!";
let rng = &mut thread_rng();
sign_and_verify::<SchnorrSignature<Edwards, Blake2s>>(message.as_bytes());
failed_verification::<SchnorrSignature<Edwards, Blake2s>>(
message.as_bytes(),
"Bad message".as_bytes(),
);
let random_scalar = to_bytes!(<Edwards as Group>::ScalarField::rand(rng)).unwrap();
randomize_and_verify::<SchnorrSignature<Edwards, Blake2s>>(
message.as_bytes(),
&random_scalar.as_slice(),
);
}
}

+ 210
- 0
crypto-primitives/src/signature/schnorr/constraints.rs

@ -0,0 +1,210 @@
use algebra::{groups::Group, Field};
use r1cs_core::{ConstraintSystem, SynthesisError};
use r1cs_std::prelude::*;
use crate::signature::SigRandomizePkGadget;
use std::{borrow::Borrow, marker::PhantomData};
use crate::signature::schnorr::{
SchnorrPublicKey, SchnorrSigParameters, SchnorrSignature,
};
use digest::Digest;
pub struct SchnorrSigGadgetParameters<G: Group, ConstraintF: Field, GG: GroupGadget<G, ConstraintF>> {
generator: GG,
_group: PhantomData<*const G>,
_engine: PhantomData<*const ConstraintF>,
}
impl<G: Group, ConstraintF: Field, GG: GroupGadget<G, ConstraintF>> Clone
for SchnorrSigGadgetParameters<G, ConstraintF, GG>
{
fn clone(&self) -> Self {
Self {
generator: self.generator.clone(),
_group: PhantomData,
_engine: PhantomData,
}
}
}
#[derive(Derivative)]
#[derivative(
Debug(bound = "G: Group, ConstraintF: Field, GG: GroupGadget<G, ConstraintF>"),
Clone(bound = "G: Group, ConstraintF: Field, GG: GroupGadget<G, ConstraintF>"),
PartialEq(bound = "G: Group, ConstraintF: Field, GG: GroupGadget<G, ConstraintF>"),
Eq(bound = "G: Group, ConstraintF: Field, GG: GroupGadget<G, ConstraintF>")
)]
pub struct SchnorrSigGadgetPk<G: Group, ConstraintF: Field, GG: GroupGadget<G, ConstraintF>> {
pub_key: GG,
#[doc(hidden)]
_group: PhantomData<*const G>,
#[doc(hidden)]
_engine: PhantomData<*const ConstraintF>,
}
pub struct SchnorrRandomizePkGadget<G: Group, ConstraintF: Field, GG: GroupGadget<G, ConstraintF>> {
#[doc(hidden)]
_group: PhantomData<*const G>,
#[doc(hidden)]
_group_gadget: PhantomData<*const GG>,
#[doc(hidden)]
_engine: PhantomData<*const ConstraintF>,
}
impl<G, GG, D, ConstraintF> SigRandomizePkGadget<SchnorrSignature<G, D>, ConstraintF>
for SchnorrRandomizePkGadget<G, ConstraintF, GG>
where
G: Group,
GG: GroupGadget<G, ConstraintF>,
D: Digest + Send + Sync,
ConstraintF: Field,
{
type ParametersGadget = SchnorrSigGadgetParameters<G, ConstraintF, GG>;
type PublicKeyGadget = SchnorrSigGadgetPk<G, ConstraintF, GG>;
fn check_randomization_gadget<CS: ConstraintSystem<ConstraintF>>(
mut cs: CS,
parameters: &Self::ParametersGadget,
public_key: &Self::PublicKeyGadget,
randomness: &[UInt8],
) -> Result<Self::PublicKeyGadget, SynthesisError> {
let base = parameters.generator.clone();
let randomness = randomness
.iter()
.flat_map(|b| b.into_bits_le())
.collect::<Vec<_>>();
let rand_pk = base.mul_bits(
&mut cs.ns(|| "Compute Randomizer"),
&public_key.pub_key,
randomness.iter(),
)?;
Ok(SchnorrSigGadgetPk {
pub_key: rand_pk,
_group: PhantomData,
_engine: PhantomData,
})
}
}
impl<G, ConstraintF, GG, D> AllocGadget<SchnorrSigParameters<G, D>, ConstraintF>
for SchnorrSigGadgetParameters<G, ConstraintF, GG>
where
G: Group,
ConstraintF: Field,
GG: GroupGadget<G, ConstraintF>,
D: Digest,
{
fn alloc<F, T, CS: ConstraintSystem<ConstraintF>>(cs: CS, f: F) -> Result<Self, SynthesisError>
where
F: FnOnce() -> Result<T, SynthesisError>,
T: Borrow<SchnorrSigParameters<G, D>>,
{
let generator = GG::alloc_checked(cs, || f().map(|pp| pp.borrow().generator))?;
Ok(Self {
generator,
_engine: PhantomData,
_group: PhantomData,
})
}
fn alloc_input<F, T, CS: ConstraintSystem<ConstraintF>>(cs: CS, f: F) -> Result<Self, SynthesisError>
where
F: FnOnce() -> Result<T, SynthesisError>,
T: Borrow<SchnorrSigParameters<G, D>>,
{
let generator = GG::alloc_input(cs, || f().map(|pp| pp.borrow().generator))?;
Ok(Self {
generator,
_engine: PhantomData,
_group: PhantomData,
})
}
}
impl<G, ConstraintF, GG> AllocGadget<SchnorrPublicKey<G>, ConstraintF> for SchnorrSigGadgetPk<G, ConstraintF, GG>
where
G: Group,
ConstraintF: Field,
GG: GroupGadget<G, ConstraintF>,
{
fn alloc<F, T, CS: ConstraintSystem<ConstraintF>>(cs: CS, f: F) -> Result<Self, SynthesisError>
where
F: FnOnce() -> Result<T, SynthesisError>,
T: Borrow<SchnorrPublicKey<G>>,
{
let pub_key = GG::alloc_input(cs, || f().map(|pk| *pk.borrow()))?;
Ok(Self {
pub_key,
_engine: PhantomData,
_group: PhantomData,
})
}
fn alloc_input<F, T, CS: ConstraintSystem<ConstraintF>>(cs: CS, f: F) -> Result<Self, SynthesisError>
where
F: FnOnce() -> Result<T, SynthesisError>,
T: Borrow<SchnorrPublicKey<G>>,
{
let pub_key = GG::alloc_input(cs, || f().map(|pk| *pk.borrow()))?;
Ok(Self {
pub_key,
_engine: PhantomData,
_group: PhantomData,
})
}
}
impl<G, ConstraintF, GG> ConditionalEqGadget<ConstraintF> for SchnorrSigGadgetPk<G, ConstraintF, GG>
where
G: Group,
ConstraintF: Field,
GG: GroupGadget<G, ConstraintF>,
{
#[inline]
fn conditional_enforce_equal<CS: ConstraintSystem<ConstraintF>>(
&self,
mut cs: CS,
other: &Self,
condition: &Boolean,
) -> Result<(), SynthesisError> {
self.pub_key.conditional_enforce_equal(
&mut cs.ns(|| "PubKey equality"),
&other.pub_key,
condition,
)?;
Ok(())
}
fn cost() -> usize {
<GG as ConditionalEqGadget<ConstraintF>>::cost()
}
}
impl<G, ConstraintF, GG> EqGadget<ConstraintF> for SchnorrSigGadgetPk<G, ConstraintF, GG>
where
G: Group,
ConstraintF: Field,
GG: GroupGadget<G, ConstraintF>,
{
}
impl<G, ConstraintF, GG> ToBytesGadget<ConstraintF> for SchnorrSigGadgetPk<G, ConstraintF, GG>
where
G: Group,
ConstraintF: Field,
GG: GroupGadget<G, ConstraintF>,
{
fn to_bytes<CS: ConstraintSystem<ConstraintF>>(&self, mut cs: CS) -> Result<Vec<UInt8>, SynthesisError> {
self.pub_key.to_bytes(&mut cs.ns(|| "PubKey To Bytes"))
}
fn to_bytes_strict<CS: ConstraintSystem<ConstraintF>>(
&self,
mut cs: CS,
) -> Result<Vec<UInt8>, SynthesisError> {
self.pub_key
.to_bytes_strict(&mut cs.ns(|| "PubKey To Bytes"))
}
}

+ 223
- 0
crypto-primitives/src/signature/schnorr/mod.rs

@ -0,0 +1,223 @@
use crate::SignatureScheme;
use algebra::{
bytes::ToBytes,
fields::{Field, PrimeField},
groups::Group,
to_bytes,
};
use digest::Digest;
use crate::Error;
use algebra::UniformRand;
use rand::Rng;
use std::{
hash::Hash,
io::{Result as IoResult, Write},
marker::PhantomData,
};
#[cfg(feature = "r1cs")]
pub mod constraints;
pub struct SchnorrSignature<G: Group, D: Digest> {
_group: PhantomData<G>,
_hash: PhantomData<D>,
}
#[derive(Derivative)]
#[derivative(Clone(bound = "G: Group, H: Digest"))]
pub struct SchnorrSigParameters<G: Group, H: Digest> {
_hash: PhantomData<H>,
pub generator: G,
pub salt: [u8; 32],
}
pub type SchnorrPublicKey<G> = G;
#[derive(Derivative)]
#[derivative(Clone(bound = "G: Group"), Default(bound = "G: Group"))]
pub struct SchnorrSecretKey<G: Group>(pub G::ScalarField);
impl<G: Group> ToBytes for SchnorrSecretKey<G> {
#[inline]
fn write<W: Write>(&self, writer: W) -> IoResult<()> {
self.0.write(writer)
}
}
#[derive(Derivative)]
#[derivative(Clone(bound = "G: Group"), Default(bound = "G: Group"))]
pub struct SchnorrSig<G: Group> {
pub prover_response: G::ScalarField,
pub verifier_challenge: G::ScalarField,
}
impl<G: Group + Hash, D: Digest + Send + Sync> SignatureScheme for SchnorrSignature<G, D>
where
G::ScalarField: PrimeField,
{
type Parameters = SchnorrSigParameters<G, D>;
type PublicKey = G;
type SecretKey = SchnorrSecretKey<G>;
type Signature = SchnorrSig<G>;
fn setup<R: Rng>(rng: &mut R) -> Result<Self::Parameters, Error> {
let setup_time = start_timer!(|| "SchnorrSig::Setup");
let mut salt = [0u8; 32];
rng.fill_bytes(&mut salt);
let generator = G::rand(rng);
end_timer!(setup_time);
Ok(SchnorrSigParameters {
_hash: PhantomData,
generator,
salt,
})
}
fn keygen<R: Rng>(
parameters: &Self::Parameters,
rng: &mut R,
) -> Result<(Self::PublicKey, Self::SecretKey), Error> {
let keygen_time = start_timer!(|| "SchnorrSig::KeyGen");
let secret_key = G::ScalarField::rand(rng);
let public_key = parameters.generator.mul(&secret_key);
end_timer!(keygen_time);
Ok((public_key, SchnorrSecretKey(secret_key)))
}
fn sign<R: Rng>(
parameters: &Self::Parameters,
sk: &Self::SecretKey,
message: &[u8],
rng: &mut R,
) -> Result<Self::Signature, Error> {
let sign_time = start_timer!(|| "SchnorrSig::Sign");
// (k, e);
let (random_scalar, verifier_challenge) = loop {
// Sample a random scalar `k` from the prime scalar field.
let random_scalar: G::ScalarField = G::ScalarField::rand(rng);
// Commit to the random scalar via r := k · g.
// This is the prover's first msg in the Sigma protocol.
let prover_commitment: G = parameters.generator.mul(&random_scalar);
// Hash everything to get verifier challenge.
let mut hash_input = Vec::new();
hash_input.extend_from_slice(&parameters.salt);
hash_input.extend_from_slice(&to_bytes![prover_commitment]?);
hash_input.extend_from_slice(message);
// Compute the supposed verifier response: e := H(salt || r || msg);
if let Some(verifier_challenge) =
G::ScalarField::from_random_bytes(&D::digest(&hash_input))
{
break (random_scalar, verifier_challenge);
};
};
// k - xe;
let prover_response = random_scalar - &(verifier_challenge * &sk.0);
let signature = SchnorrSig {
prover_response,
verifier_challenge,
};
end_timer!(sign_time);
Ok(signature)
}
fn verify(
parameters: &Self::Parameters,
pk: &Self::PublicKey,
message: &[u8],
signature: &Self::Signature,
) -> Result<bool, Error> {
let verify_time = start_timer!(|| "SchnorrSig::Verify");
let SchnorrSig {
prover_response,
verifier_challenge,
} = signature;
let mut claimed_prover_commitment = parameters.generator.mul(prover_response);
let public_key_times_verifier_challenge = pk.mul(verifier_challenge);
claimed_prover_commitment += &public_key_times_verifier_challenge;
let mut hash_input = Vec::new();
hash_input.extend_from_slice(&parameters.salt);
hash_input.extend_from_slice(&to_bytes![claimed_prover_commitment]?);
hash_input.extend_from_slice(&message);
let obtained_verifier_challenge = if let Some(obtained_verifier_challenge) =
G::ScalarField::from_random_bytes(&D::digest(&hash_input))
{
obtained_verifier_challenge
} else {
return Ok(false);
};
end_timer!(verify_time);
Ok(verifier_challenge == &obtained_verifier_challenge)
}
fn randomize_public_key(
parameters: &Self::Parameters,
public_key: &Self::PublicKey,
randomness: &[u8],
) -> Result<Self::PublicKey, Error> {
let rand_pk_time = start_timer!(|| "SchnorrSig::RandomizePubKey");
let mut randomized_pk = *public_key;
let mut base = parameters.generator;
let mut encoded = G::zero();
for bit in bytes_to_bits(randomness) {
if bit {
encoded += &base;
}
base.double_in_place();
}
randomized_pk += &encoded;
end_timer!(rand_pk_time);
Ok(randomized_pk)
}
fn randomize_signature(
_parameter: &Self::Parameters,
signature: &Self::Signature,
randomness: &[u8],
) -> Result<Self::Signature, Error> {
let rand_signature_time = start_timer!(|| "SchnorrSig::RandomizeSig");
let SchnorrSig {
prover_response,
verifier_challenge,
} = signature;
let mut base = G::ScalarField::one();
let mut multiplier = G::ScalarField::zero();
for bit in bytes_to_bits(randomness) {
if bit {
multiplier += &base;
}
base.double_in_place();
}
let new_sig = SchnorrSig {
prover_response: *prover_response - &(*verifier_challenge * &multiplier),
verifier_challenge: *verifier_challenge,
};
end_timer!(rand_signature_time);
Ok(new_sig)
}
}
pub fn bytes_to_bits(bytes: &[u8]) -> Vec<bool> {
let mut bits = Vec::with_capacity(bytes.len() * 8);
for byte in bytes {
for i in 0..8 {
let bit = (*byte >> (8 - i - 1)) & 1;
bits.push(bit == 1);
}
}
bits
}

+ 37
- 0
r1cs-std/src/bits/mod.rs

@ -81,4 +81,41 @@ pub trait ToBytesGadget {
) -> Result<Vec<UInt8>, SynthesisError>;
}
impl<ConstraintF: Field> ToBytesGadget<ConstraintF> for [UInt8] {
fn to_bytes<CS: ConstraintSystem<ConstraintF>>(&self, _cs: CS) -> Result<Vec<UInt8>, SynthesisError> {
Ok(self.to_vec())
}
fn to_bytes_strict<CS: ConstraintSystem<ConstraintF>>(
&self,
cs: CS,
) -> Result<Vec<UInt8>, SynthesisError> {
self.to_bytes(cs)
}
}
impl<'a, ConstraintF: Field, T: 'a + ToBytesGadget<ConstraintF>> ToBytesGadget<ConstraintF> for &'a T {
fn to_bytes<CS: ConstraintSystem<ConstraintF>>(&self, cs: CS) -> Result<Vec<UInt8>, SynthesisError> {
(*self).to_bytes(cs)
}
fn to_bytes_strict<CS: ConstraintSystem<ConstraintF>>(
&self,
cs: CS,
) -> Result<Vec<UInt8>, SynthesisError> {
self.to_bytes(cs)
}
}
impl<'a, ConstraintF: Field> ToBytesGadget<ConstraintF> for &'a [UInt8] {
fn to_bytes<CS: ConstraintSystem<ConstraintF>>(&self, _cs: CS) -> Result<Vec<UInt8>, SynthesisError> {
Ok(self.to_vec())
}
fn to_bytes_strict<CS: ConstraintSystem<ConstraintF>>(
&self,
cs: CS,
) -> Result<Vec<UInt8>, SynthesisError> {
self.to_bytes(cs)
}
}

Loading…
Cancel
Save