nizk: extract proof of knowledge of a commitment

This commit is contained in:
Gijs Van Laer 2019-08-27 16:58:30 -04:00
parent c947a446ea
commit cc921e5786
4 changed files with 154 additions and 145 deletions

View File

@ -14,14 +14,14 @@ use pairing::{Engine, CurveProjective};
use pairing::bls12_381::{Bls12};
use ff::PrimeField;
use cl::{BlindKeyPair, KeyPair, Signature, PublicParams, setup};
use ped92::{CSParams, Commitment, CSMultiParams};
use ped92::{CSParams, Commitment, CSMultiParams, CommitmentProof};
use util::{hash_pubkey_to_fr, convert_int_to_fr, hash_to_fr, RevokedMessage, hash_to_slice};
use rand::Rng;
use std::collections::HashMap;
use std::fmt::Display;
use serde::{Serialize, Deserialize};
use std::ptr::hash;
use nizk::{NIZKPublicParams, CommitmentProof, Proof};
use nizk::{NIZKPublicParams, NIZKProof};
use wallet::Wallet;
use std::error::Error;
use std::fmt;
@ -330,7 +330,7 @@ impl<E: Engine> CustomerState<E> {
}
// for channel pay
pub fn generate_payment<R: Rng>(&self, csprng: &mut R, channel: &ChannelState<E>, amount: i32) -> (Proof<E>, Commitment<E>, secp256k1::PublicKey, CustomerState<E>) {
pub fn generate_payment<R: Rng>(&self, csprng: &mut R, channel: &ChannelState<E>, amount: i32) -> (NIZKProof<E>, Commitment<E>, secp256k1::PublicKey, CustomerState<E>) {
// 1 - chooose new wpk/wsk pair
let mut kp = secp256k1::Secp256k1::new();
kp.randomize(csprng);
@ -557,7 +557,7 @@ impl<E: Engine> MerchantState<E> {
return self.pay_tokens.get(&wpk_str).unwrap().clone();
}
pub fn verify_payment<R: Rng>(&mut self, csprng: &mut R, channel: &ChannelState<E>, proof: &Proof<E>, com: &Commitment<E>, wpk: &secp256k1::PublicKey, amount: i32) -> ResultBoltSig<Signature<E>> {
pub fn verify_payment<R: Rng>(&mut self, csprng: &mut R, channel: &ChannelState<E>, proof: &NIZKProof<E>, com: &Commitment<E>, wpk: &secp256k1::PublicKey, amount: i32) -> ResultBoltSig<Signature<E>> {
let cp = channel.cp.as_ref().unwrap();
let pay_proof = proof.clone();
let prev_wpk = hash_pubkey_to_fr::<E>(&wpk);

View File

@ -130,9 +130,10 @@ pub mod bidirectional {
pub use BoltResult;
pub use channels::{ChannelState, ChannelToken, CustomerState, MerchantState, ChannelcloseM,
PubKeyMap, ChannelParams, BoltError, ResultBoltSig};
pub use nizk::{CommitmentProof, Proof};
pub use nizk::NIZKProof;
pub use wallet::Wallet;
pub use cl::PublicParams;
pub use ped92::CommitmentProof;
#[derive(Clone, Serialize, Deserialize)]
#[serde(bound(serialize = "<E as ff::ScalarEngine>::Fr: serde::Serialize, \
@ -157,7 +158,7 @@ pub mod bidirectional {
<E as pairing::Engine>::Fqk: serde::Deserialize<'de>"
))]
pub struct Payment<E: Engine> {
proof: Proof<E>,
proof: NIZKProof<E>,
com: Commitment<E>,
wpk: secp256k1::PublicKey,
amount: i32,

View File

@ -4,14 +4,16 @@ extern crate rand;
use super::*;
use rand::Rng;
use cl::{KeyPair, Signature, PublicParams, setup, BlindKeyPair, ProofState, SignatureProof, BlindPublicKey};
use ped92::{CSParams, Commitment, CSMultiParams};
use ped92::{CSParams, Commitment, CSMultiParams, CommitmentProof};
use pairing::{Engine, CurveProjective};
use ff::PrimeField;
use wallet::Wallet;
use ccs08::{RPPublicParams, RangeProof};
use serde::{Serialize, Deserialize};
use util;
use std::borrow::BorrowMut;
/// NIZKProof is the object that represents the NIZK Proof of Knowledge during the payment and closing protocol
#[derive(Clone, Serialize, Deserialize)]
#[serde(bound(serialize = "<E as ff::ScalarEngine>::Fr: serde::Serialize, \
<E as pairing::Engine>::G1: serde::Serialize, \
@ -23,15 +25,15 @@ use util;
<E as pairing::Engine>::G2: serde::Deserialize<'de>, \
<E as pairing::Engine>::Fqk: serde::Deserialize<'de>"
))]
pub struct Proof<E: Engine> {
pub struct NIZKProof<E: Engine> {
pub sig: Signature<E>,
pub sigProof: SignatureProof<E>,
pub D: E::G1,
pub z: Vec<E::Fr>,
pub comProof: CommitmentProof<E>,
pub rpBC: RangeProof<E>,
pub rpBM: RangeProof<E>,
}
/// NIZKPublicParams are public parameters to perform a NIZK Proof of Knowledge during the payment and closing protocol
#[derive(Clone, Serialize, Deserialize)]
#[serde(bound(serialize = "<E as ff::ScalarEngine>::Fr: serde::Serialize, \
<E as pairing::Engine>::G1: serde::Serialize, \
@ -50,6 +52,8 @@ pub struct NIZKPublicParams<E: Engine> {
}
impl<E: Engine> NIZKPublicParams<E> {
/// Basic setup for the NIZKPublicParams
/// Takes as input a random generator and the length of the message which should be 4 during payment protocol and 5 for the closing protocol
pub fn setup<R: Rng>(rng: &mut R, messageLength: usize) -> Self {
let mpk = setup(rng);
let keypair = BlindKeyPair::<E>::generate(rng, &mpk, messageLength);
@ -57,14 +61,25 @@ impl<E: Engine> NIZKPublicParams<E> {
let rpParamsBC = RPPublicParams::setup(rng, 0, std::i16::MAX as i32, comParams.clone());
let rpParamsBM = RPPublicParams::setup(rng, 0, std::i16::MAX as i32, comParams.clone());
NIZKPublicParams{mpk, keypair, comParams, rpParamsBC, rpParamsBM}
NIZKPublicParams { mpk, keypair, comParams, rpParamsBC, rpParamsBM }
}
/** This method can be called to create the proof during the payment and closing protocol
Input:
rng: random generator
r: randomness of commitment of old wallet (TODO: still necessary?)
oldWallet: This is the wallet before payment occurs
newWallet: This is the new state of the wallet after payment
newWalletCom: A commitment of the new wallet
rPrime: randomness of commitment of new wallet
paymentToken: A blind signature on the old wallet
Output:
NIZKProof: a proof that can be verified by the merchant during payment or closing protocol
*/
pub fn prove<R: Rng>(&self, rng: &mut R, r: E::Fr, oldWallet: Wallet<E>, newWallet: Wallet<E>,
newWalletCom: Commitment<E>, rPrime: E::Fr, paymentToken: &Signature<E>) -> Proof<E> {
newWalletCom: Commitment<E>, rPrime: E::Fr, paymentToken: &Signature<E>) -> NIZKProof<E> {
//Commitment phase
//commit commitment
let mut D = E::G1::zero();
let w_len = newWallet.as_fr_vec().len();
let diff = self.comParams.pub_bases.len() - w_len;
let max = match diff > 1 {
@ -72,14 +87,7 @@ impl<E: Engine> NIZKPublicParams<E> {
false => self.comParams.pub_bases.len()
};
let mut t = Vec::<E::Fr>::with_capacity(max );
for i in 0 .. max {
let ti = E::Fr::rand(rng);
t.push(ti);
let mut gt = self.comParams.pub_bases[i].clone();
gt.mul_assign(ti.into_repr());
D.add_assign(&gt);
}
let (D, t, rt, mut reveal_wallet) = CommitmentProof::<E>::prove_commitment(rng, &self.comParams, &newWallet.as_fr_vec(), &vec! {});
//commit signature
let zero = E::Fr::zero();
@ -102,18 +110,8 @@ impl<E: Engine> NIZKPublicParams<E> {
let sigProof = self.keypair.prove_response(&proofState, challenge, &mut oldWalletVec.clone());
//response commitment
let mut z = Vec::<E::Fr>::with_capacity(t.len());
let mut z0 = rPrime.clone();
z0.mul_assign(&challenge);
z0.add_assign(&t[0]);
z.push(z0);
let newWalletVec = newWallet.as_fr_vec();
for i in 1..t.len() {
let mut zi = newWalletVec[i - 1].clone();
zi.mul_assign(&challenge);
zi.add_assign(&t[i]);
z.push(zi);
}
let comProof = CommitmentProof::<E>::prove_response(&newWalletVec, &rPrime, &vec! {}, D, &t, rt, reveal_wallet.borrow_mut(), &challenge);
//response range proof
let mut vec01 = newWalletVec[0..2].to_vec();
@ -121,7 +119,7 @@ impl<E: Engine> NIZKPublicParams<E> {
let mut vec3 = newWalletVec[3..].to_vec();
vecWithout2.append(&mut vec3);
let vec2 = newWalletVec[2].clone();
vec01.push( vec2);
vec01.push(vec2);
if newWalletVec.len() > 4 {
let mut vec4 = newWalletVec[4..].to_vec();
vec01.append(&mut vec4);
@ -129,15 +127,23 @@ impl<E: Engine> NIZKPublicParams<E> {
let rpBC = self.rpParamsBC.prove_response(rPrime.clone(), &rpStateBC, challenge.clone(), 3, vecWithout2.to_vec());
let rpBM = self.rpParamsBM.prove_response(rPrime.clone(), &rpStateBM, challenge.clone(), 4, vec01.to_vec());
Proof { sig: proofState.blindSig, sigProof, D, z, rpBC, rpBM }
NIZKProof { sig: proofState.blindSig, sigProof, comProof, rpBC, rpBM }
}
pub fn verify(&self, proof: Proof<E>, epsilon: E::Fr, com2: &Commitment<E>, wpk: E::Fr) -> bool {
/**
Verify a NIZK Proof of Knowledge during payment or closing protocl
Input:
proof: A NIZK proof created by the Customer
epsilon: The transaction amount of the payment
com: Commitment of the new wallet that needs to be signed
wpk: reveal of wallet public key of the old wallet.
*/
pub fn verify(&self, proof: NIZKProof<E>, epsilon: E::Fr, com: &Commitment<E>, wpk: E::Fr) -> bool {
//verify signature is not the identity
let r0 = proof.sig.h != E::G1::one();
//compute challenge
let challenge = NIZKPublicParams::<E>::hash(proof.sigProof.a, vec! {proof.D, proof.rpBC.p1.D, proof.rpBC.p2.D, proof.rpBM.p1.D, proof.rpBM.p2.D});
let challenge = NIZKPublicParams::<E>::hash(proof.sigProof.a, vec! {proof.comProof.T, proof.rpBC.p1.D, proof.rpBC.p2.D, proof.rpBM.p1.D, proof.rpBM.p2.D});
//verify knowledge of signature
let mut r1 = self.keypair.public.verify_proof(&self.mpk, proof.sig, proof.sigProof.clone(), challenge);
@ -146,31 +152,22 @@ impl<E: Engine> NIZKPublicParams<E> {
r1 = r1 && proof.sigProof.zsig[1] == wpkc;
//verify knowledge of commitment
let mut comc = com2.c.clone();
comc.mul_assign(challenge.into_repr());
comc.add_assign(&proof.D.clone());
let mut x = E::G1::zero();
for i in 0..proof.z.len() {
let mut base = self.comParams.pub_bases[i].clone();
base.mul_assign(proof.z[i].into_repr());
x.add_assign(&base);
}
let r2 = x == comc;
let r2 = proof.comProof.verify_proof(&self.comParams, &com.c.clone(), &challenge);
//verify range proofs
let r3 = self.rpParamsBC.verify(proof.rpBC.clone(), challenge.clone(), 3);
let r4 = self.rpParamsBM.verify(proof.rpBM.clone(), challenge.clone(), 4);
//verify linear relationship
let mut r5 = proof.z[1] == proof.sigProof.zsig[0];
let mut r5 = proof.comProof.z[1] == proof.sigProof.zsig[0];
let mut zsig2 = proof.sigProof.zsig[2].clone();
let mut epsC = epsilon.clone();
epsC.mul_assign(&challenge.clone());
zsig2.sub_assign(&epsC.clone());
r5 = r5 && proof.z[3] == zsig2;
r5 = r5 && proof.comProof.z[3] == zsig2;
let mut zsig3 = proof.sigProof.zsig[3].clone();
zsig3.add_assign(&epsC.clone());
r5 = r5 && proof.z[4] == zsig3;
r5 = r5 && proof.comProof.z[4] == zsig3;
r0 && r1 && r2 && r3 && r4 && r5
}
@ -186,104 +183,17 @@ impl<E: Engine> NIZKPublicParams<E> {
}
}
#[derive(Clone, Serialize, Deserialize, Debug)]
#[serde(bound(serialize = "<E as ff::ScalarEngine>::Fr: serde::Serialize, \
<E as pairing::Engine>::G1: serde::Serialize, \
<E as pairing::Engine>::G2: serde::Serialize"
))]
#[serde(bound(deserialize = "<E as ff::ScalarEngine>::Fr: serde::Deserialize<'de>, \
<E as pairing::Engine>::G1: serde::Deserialize<'de>, \
<E as pairing::Engine>::G2: serde::Deserialize<'de>"
))]
pub struct CommitmentProof<E: Engine> {
pub T: E::G1,
pub z: Vec<E::Fr>,
pub t: Vec<E::Fr>,
pub index: Vec<usize>,
pub reveal: Vec<E::Fr>
}
impl<E: Engine> CommitmentProof<E> {
pub fn new<R: Rng>(csprng: &mut R, com_params: &CSMultiParams<E>, com: &E::G1, wallet: &Vec<E::Fr>, r: &E::Fr, reveal_index: &Vec<usize>) -> Self {
let mut Tvals = E::G1::zero();
assert!(wallet.len() <= com_params.pub_bases.len());
let mut t = Vec::<E::Fr>::with_capacity( wallet.len()+1 );
let mut rt: Vec<E::Fr> = Vec::new(); // t values that will be revealed
let mut reveal_wallet: Vec<E::Fr> = Vec::new(); // aspects of wallet being revealed
for i in 0..wallet.len()+1 {
let ti = E::Fr::rand(csprng);
t.push(ti);
// check if we are revealing this index
if (reveal_index.contains(&i)) {
rt.push(ti);
} else {
rt.push( E::Fr::zero());
}
let mut gt = com_params.pub_bases[i].clone();
gt.mul_assign(ti.into_repr());
Tvals.add_assign(&gt);
}
// compute the challenge
let x: Vec<E::G1> = vec![Tvals, com.clone()];
let challenge = util::hash_g1_to_fr::<E>(&x);
// compute the response
let mut z: Vec<E::Fr> = Vec::new();
let mut z0 = r.clone();
z0.mul_assign(&challenge);
z0.add_assign(&t[0]);
z.push(z0);
reveal_wallet.push( E::Fr::zero());
for i in 1..t.len() {
let mut zi = wallet[i-1].clone();
zi.mul_assign(&challenge);
zi.add_assign(&t[i]);
z.push(zi);
// check if we are revealing this index
if (reveal_index.contains(&i)) {
reveal_wallet.push(wallet[i-1].clone() );
} else {
reveal_wallet.push( E::Fr::zero());
}
}
CommitmentProof {
T: Tvals, // commitment challenge
z: z, // response values
t: rt, // randomness for verifying partial reveals
index: reveal_index.clone(),
reveal: reveal_wallet
}
}
}
///
/// Verify PoK for the opening of a commitment
/// Verify PoK for the opening of a commitment during the establishment protocol
///
pub fn verify_opening<E: Engine>(com_params: &CSMultiParams<E>, com: &E::G1, proof: &CommitmentProof<E>, init_cust: i32, init_merch: i32) -> bool {
let mut comc = com.clone();
let T = proof.T.clone();
let xvec: Vec<E::G1> = vec![T, comc];
let xvec: Vec<E::G1> = vec![proof.T.clone(), com.clone()];
let challenge = util::hash_g1_to_fr::<E>(&xvec);
// compute the
comc.mul_assign(challenge.into_repr());
comc.add_assign(&T);
let com_equal = proof.verify_proof(com_params, com, &challenge);
let mut x = E::G1::zero();
for i in 0..proof.z.len() {
let mut base = com_params.pub_bases[i].clone();
base.mul_assign(proof.z[i].into_repr());
x.add_assign(&base);
}
if (proof.index.len() == 0) {
if proof.index.len() == 0 {
println!("verify_opening - doing any partial reveals?");
return false;
}
@ -309,7 +219,7 @@ pub fn verify_opening<E: Engine>(com_params: &CSMultiParams<E>, com: &E::G1, pro
let init_m = util::convert_int_to_fr::<E>(init_merch);
let bm_equal = (s4 == proof.z[4]) && (proof.reveal[4] == init_m);
return comc == x && pkc_equal && bc_equal && bm_equal;
return com_equal && pkc_equal && bc_equal && bm_equal;
}
@ -344,7 +254,7 @@ mod tests {
let paymentToken = pubParams.keypair.unblind(&r, &blindPaymentToken);
let proof = pubParams.prove(rng, r, wallet1, wallet2,
commitment2.clone(), rprime, &paymentToken);
commitment2.clone(), rprime, &paymentToken);
let fr = convert_int_to_fr::<Bls12>(*epsilon);
assert_eq!(pubParams.verify(proof, fr, &commitment2, wpk), true);
}
@ -499,7 +409,7 @@ mod tests {
let com2 = pubParams.comParams.commit(&wallet2.as_fr_vec().clone(), &t);
let com1_proof = CommitmentProof::<Bls12>::new(rng, &pubParams.comParams,
&com1.c, &wallet1.as_fr_vec(), &t, &vec![1, 3, 4]);
&com1.c, &wallet1.as_fr_vec(), &t, &vec![1, 3, 4]);
assert!(verify_opening(&pubParams.comParams, &com1.c, &com1_proof, bc, bm));
assert!(!verify_opening(&pubParams.comParams, &com2.c, &com1_proof, bc2, bm));
@ -524,5 +434,4 @@ mod tests {
// deserialize
}
}

View File

@ -1,10 +1,12 @@
// ped92.rs
use rand::{thread_rng, Rng};
use pairing::{Engine, CurveProjective};
use ff::Rand;
use ff::{Rand, Field, PrimeField};
use std::fmt;
use util::is_vec_g1_equal;
use serde::{Serialize, Deserialize};
use util;
use std::borrow::BorrowMut;
#[derive(Clone)]
pub struct CSParams<E: Engine> {
@ -173,6 +175,103 @@ impl<E: Engine> CSMultiParams<E> {
}
}
#[derive(Clone, Serialize, Deserialize, Debug)]
#[serde(bound(serialize = "<E as ff::ScalarEngine>::Fr: serde::Serialize, \
<E as pairing::Engine>::G1: serde::Serialize, \
<E as pairing::Engine>::G2: serde::Serialize"
))]
#[serde(bound(deserialize = "<E as ff::ScalarEngine>::Fr: serde::Deserialize<'de>, \
<E as pairing::Engine>::G1: serde::Deserialize<'de>, \
<E as pairing::Engine>::G2: serde::Deserialize<'de>"
))]
pub struct CommitmentProof<E: Engine> {
pub T: E::G1,
pub z: Vec<E::Fr>,
pub t: Vec<E::Fr>,
pub index: Vec<usize>,
pub reveal: Vec<E::Fr>
}
impl<E: Engine> CommitmentProof<E> {
pub fn new<R: Rng>(csprng: &mut R, com_params: &CSMultiParams<E>, com: &E::G1, wallet: &Vec<E::Fr>, r: &E::Fr, reveal_index: &Vec<usize>) -> Self {
let (Tvals, t, rt, mut reveal_wallet) = CommitmentProof::<E>::prove_commitment::<R>(csprng, com_params, wallet, reveal_index);
// compute the challenge
let x: Vec<E::G1> = vec![Tvals, com.clone()];
let challenge = util::hash_g1_to_fr::<E>(&x);
// compute the response
CommitmentProof::<E>::prove_response(wallet, r, reveal_index, Tvals, &t, rt, reveal_wallet.borrow_mut(), &challenge)
}
pub fn prove_commitment<R: Rng>(csprng: &mut R, com_params: &CSMultiParams<E>, wallet: &Vec<E::Fr>, reveal_index: &Vec<usize>) -> (E::G1, Vec<E::Fr>, Vec<E::Fr>, Vec<E::Fr>) {
let mut Tvals = E::G1::zero();
assert!(wallet.len() <= com_params.pub_bases.len());
let mut t = Vec::<E::Fr>::with_capacity(wallet.len() + 1);
let mut rt: Vec<E::Fr> = Vec::new();
// t values that will be revealed
let mut reveal_wallet: Vec<E::Fr> = Vec::new();
// aspects of wallet being revealed
for i in 0..wallet.len() + 1 {
let ti = E::Fr::rand(csprng);
t.push(ti);
// check if we are revealing this index
if reveal_index.contains(&i) {
rt.push(ti);
} else {
rt.push(E::Fr::zero());
}
let mut gt = com_params.pub_bases[i].clone();
gt.mul_assign(ti.into_repr());
Tvals.add_assign(&gt);
}
(Tvals, t, rt, reveal_wallet)
}
pub fn prove_response(wallet: &Vec<E::Fr>, r: &E::Fr, reveal_index: &Vec<usize>, Tvals: E::G1, t: &Vec<E::Fr>, rt: Vec<E::Fr>, reveal_wallet: &mut Vec<E::Fr>, challenge: &E::Fr) -> CommitmentProof<E> {
let mut z: Vec<E::Fr> = Vec::new();
let mut z0 = r.clone();
z0.mul_assign(&challenge);
z0.add_assign(&t[0]);
z.push(z0);
reveal_wallet.push(E::Fr::zero());
for i in 1..t.len() {
let mut zi = wallet[i - 1].clone();
zi.mul_assign(&challenge);
zi.add_assign(&t[i]);
z.push(zi);
// check if we are revealing this index
if reveal_index.contains(&i) {
reveal_wallet.push(wallet[i - 1].clone());
} else {
reveal_wallet.push(E::Fr::zero());
}
}
CommitmentProof {
T: Tvals, // commitment challenge
z: z, // response values
t: rt, // randomness for verifying partial reveals
index: reveal_index.clone(),
reveal: reveal_wallet.clone()
}
}
pub fn verify_proof(&self, com_params: &CSMultiParams<E>, com: &<E as Engine>::G1, challenge: &E::Fr) -> bool {
let mut comc = com.clone();
let T = self.T.clone();
comc.mul_assign(challenge.into_repr());
comc.add_assign(&T);
let mut x = E::G1::zero();
for i in 0..self.z.len() {
let mut base = com_params.pub_bases[i].clone();
base.mul_assign(self.z[i].into_repr());
x.add_assign(&base);
}
comc == x
}
}
#[cfg(test)]
mod tests {
use super::*;