Fix a bug in the commitment computation
This commit is contained in:
@@ -16,6 +16,9 @@ pub enum CompactEcashError {
|
||||
#[error("Interpolation error: {0}")]
|
||||
Interpolation(String),
|
||||
|
||||
#[error("Issuance Verification related error: {0}")]
|
||||
IssuanceVfy(String),
|
||||
|
||||
#[error("Tried to deserialize {object} with bytes of invalid length. Expected {actual} < {} or {modulus_target} % {modulus} == 0")]
|
||||
DeserializationInvalidLength {
|
||||
actual: usize,
|
||||
|
||||
@@ -1,22 +1,27 @@
|
||||
use std::borrow::Borrow;
|
||||
use std::convert::TryFrom;
|
||||
use std::convert::TryInto;
|
||||
|
||||
use bls12_381::{G1Affine, G1Projective, Scalar};
|
||||
use digest::generic_array::typenum::Unsigned;
|
||||
use digest::Digest;
|
||||
use digest::generic_array::typenum::Unsigned;
|
||||
use group::GroupEncoding;
|
||||
use itertools::izip;
|
||||
use sha2::Sha256;
|
||||
|
||||
use crate::error::{CompactEcashError, Result};
|
||||
use crate::scheme::keygen::PublicKeyUser;
|
||||
use crate::scheme::setup::Parameters;
|
||||
use crate::utils::try_deserialize_g1_projective;
|
||||
|
||||
type ChallengeDigest = Sha256;
|
||||
|
||||
/// Generates a Scalar [or Fp] challenge by hashing a number of elliptic curve points.
|
||||
fn compute_challenge<D, I, B>(iter: I) -> Scalar
|
||||
where
|
||||
D: Digest,
|
||||
I: Iterator<Item = B>,
|
||||
B: AsRef<[u8]>,
|
||||
where
|
||||
D: Digest,
|
||||
I: Iterator<Item=B>,
|
||||
B: AsRef<[u8]>,
|
||||
{
|
||||
let mut h = D::new();
|
||||
for point_representation in iter {
|
||||
@@ -38,14 +43,14 @@ where
|
||||
Scalar::from_bytes_wide(&bytes)
|
||||
}
|
||||
|
||||
fn produce_response(witness: &Scalar, challenge: &Scalar, secret: &Scalar) -> Scalar {
|
||||
witness - challenge * secret
|
||||
fn produce_response(witness_replacement: &Scalar, challenge: &Scalar, secret: &Scalar) -> Scalar {
|
||||
witness_replacement - challenge * secret
|
||||
}
|
||||
|
||||
// note: it's caller's responsibility to ensure witnesses.len() = secrets.len()
|
||||
fn produce_responses<S>(witnesses: &[Scalar], challenge: &Scalar, secrets: &[S]) -> Vec<Scalar>
|
||||
where
|
||||
S: Borrow<Scalar>,
|
||||
where
|
||||
S: Borrow<Scalar>,
|
||||
{
|
||||
debug_assert_eq!(witnesses.len(), secrets.len());
|
||||
|
||||
@@ -56,21 +61,113 @@ where
|
||||
.collect()
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[cfg_attr(test, derive(PartialEq))]
|
||||
// instance: g, gamma1, gamma2, gamma3, com, h, com1, com2, com3, pkUser
|
||||
pub struct WithdrawalReqInstance {
|
||||
pub g1_gen: G1Affine,
|
||||
pub gammas: Vec<G1Affine>,
|
||||
pub attrs_commitment: G1Projective,
|
||||
pub attrs_commitment_hash: G1Projective,
|
||||
pub pc_commitments: Vec<G1Projective>,
|
||||
// Joined commitment to all attributes
|
||||
pub com: G1Projective,
|
||||
// Hash of the joined commitment com
|
||||
pub h: G1Projective,
|
||||
// Pedersen commitments to each attribute
|
||||
pub pc_coms: Vec<G1Projective>,
|
||||
// Public key of a user
|
||||
pub pk_user: PublicKeyUser,
|
||||
}
|
||||
|
||||
impl TryFrom<&[u8]> for WithdrawalReqInstance {
|
||||
type Error = CompactEcashError;
|
||||
|
||||
fn try_from(bytes: &[u8]) -> Result<WithdrawalReqInstance> {
|
||||
if bytes.len() < 48 * 4 + 8 || (bytes.len() - 8) % 48 != 0 {
|
||||
return Err(CompactEcashError::DeserializationInvalidLength {
|
||||
actual: bytes.len(),
|
||||
modulus_target: bytes.len() - 8,
|
||||
target: 48 * 4 + 8,
|
||||
modulus: 48,
|
||||
object: "secret key".to_string(),
|
||||
});
|
||||
}
|
||||
let com_bytes: [u8; 48] = bytes[..48].try_into().unwrap();
|
||||
let com = try_deserialize_g1_projective(
|
||||
&com_bytes,
|
||||
CompactEcashError::Deserialization(
|
||||
"Failed to deserialize com".to_string(),
|
||||
),
|
||||
)?;
|
||||
let h_bytes: [u8; 48] = bytes[48..96].try_into().unwrap();
|
||||
let h = try_deserialize_g1_projective(
|
||||
&h_bytes,
|
||||
CompactEcashError::Deserialization(
|
||||
"Failed to deserialize h".to_string(),
|
||||
),
|
||||
)?;
|
||||
let pc_coms_len = u64::from_le_bytes(bytes[96..104].try_into().unwrap());
|
||||
let actual_pc_coms_len = (bytes.len() - 152) / 48;
|
||||
if pc_coms_len as usize != actual_pc_coms_len {
|
||||
return Err(CompactEcashError::Deserialization(format!(
|
||||
"Tried to deserialize pedersen commitments with inconsistent pc_coms_len (expected {}, got {})",
|
||||
pc_coms_len, actual_pc_coms_len
|
||||
)));
|
||||
}
|
||||
let mut pc_coms = Vec::new();
|
||||
let mut pc_coms_end: usize = 0;
|
||||
for i in 0..pc_coms_len {
|
||||
let start = (104 + i * 48) as usize;
|
||||
let end = (start + 48) as usize;
|
||||
let pc_i_bytes = bytes[start..end].try_into().unwrap();
|
||||
let pc_i = try_deserialize_g1_projective(
|
||||
&pc_i_bytes,
|
||||
CompactEcashError::Deserialization(
|
||||
"Failed to deserialize pedersen commitment".to_string(),
|
||||
),
|
||||
)?;
|
||||
pc_coms_end = end;
|
||||
pc_coms.push(pc_i);
|
||||
}
|
||||
let pk_bytes = bytes[pc_coms_end..].try_into().unwrap();
|
||||
let pk = try_deserialize_g1_projective(
|
||||
&pk_bytes,
|
||||
CompactEcashError::Deserialization(
|
||||
"Failed to deserialize user's public key".to_string(),
|
||||
),
|
||||
)?;
|
||||
|
||||
Ok(WithdrawalReqInstance {
|
||||
com,
|
||||
h,
|
||||
pc_coms,
|
||||
pk_user: PublicKeyUser { pk },
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl WithdrawalReqInstance {
|
||||
pub(crate) fn to_bytes(&self) -> Vec<u8> {
|
||||
let pc_coms_len = self.pc_coms.len();
|
||||
let mut bytes = Vec::with_capacity(8 + (pc_coms_len + 3) as usize * 48);
|
||||
bytes.extend_from_slice(self.com.to_bytes().as_ref());
|
||||
bytes.extend_from_slice(self.h.to_bytes().as_ref());
|
||||
bytes.extend_from_slice(&pc_coms_len.to_le_bytes());
|
||||
for pc in self.pc_coms.iter() {
|
||||
bytes.extend_from_slice((pc.to_bytes()).as_ref());
|
||||
}
|
||||
bytes.extend_from_slice(self.pk_user.pk.to_bytes().as_ref());
|
||||
bytes
|
||||
}
|
||||
|
||||
pub fn from_bytes(bytes: &[u8]) -> Result<WithdrawalReqInstance> {
|
||||
WithdrawalReqInstance::try_from(bytes)
|
||||
}
|
||||
}
|
||||
|
||||
// witness: m1, m2, m3, o, o1, o2, o3,
|
||||
pub struct WithdrawalReqWitness {
|
||||
pub attributes: Vec<Scalar>,
|
||||
pub attrs_commitment_opening: Scalar,
|
||||
pub pc_openings: Vec<Scalar>,
|
||||
// Opening for the joined commitment com
|
||||
pub com_opening: Scalar,
|
||||
// Openings for the pedersen commitments
|
||||
pub pc_coms_openings: Vec<Scalar>,
|
||||
}
|
||||
|
||||
pub struct WithdrawalReqProof {
|
||||
@@ -87,75 +184,62 @@ impl WithdrawalReqProof {
|
||||
witness: &WithdrawalReqWitness,
|
||||
) -> Self {
|
||||
// generate random values to replace the witnesses
|
||||
let r_commitment_opening = params.random_scalar();
|
||||
let r_pedersen_commitments_openings = params.n_random_scalars(witness.pc_openings.len());
|
||||
let r_witness_attributes = params.n_random_scalars(witness.attributes.len());
|
||||
let r_com_opening = params.random_scalar();
|
||||
let r_pedcom_openings = params.n_random_scalars(witness.pc_coms_openings.len());
|
||||
let r_attributes = params.n_random_scalars(witness.attributes.len());
|
||||
|
||||
// compute zkp commitments
|
||||
let zk_commitment_attributes = instance.g1_gen * r_commitment_opening
|
||||
+ r_witness_attributes
|
||||
.iter()
|
||||
.zip(instance.gammas.iter())
|
||||
.map(|(wm_i, gamma_i)| gamma_i * wm_i)
|
||||
.sum::<G1Projective>();
|
||||
|
||||
let zk_pc_commitments_attributes = r_pedersen_commitments_openings
|
||||
// compute zkp commitments for each instance
|
||||
let zkcm_com = params.gen1() * r_com_opening
|
||||
+ r_attributes
|
||||
.iter()
|
||||
.zip(r_witness_attributes.iter())
|
||||
.map(|(o_j, m_j)| instance.g1_gen * o_j + instance.attrs_commitment_hash * m_j)
|
||||
.zip(params.gammas().iter())
|
||||
.map(|(rm_i, gamma_i)| gamma_i * rm_i)
|
||||
.sum::<G1Projective>();
|
||||
|
||||
let zkcm_pedcom = r_pedcom_openings
|
||||
.iter()
|
||||
.zip(r_attributes.iter())
|
||||
.map(|(o_j, m_j)| params.gen1() * o_j + instance.h * m_j)
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let zk_commitment_user_sk = instance.g1_gen * r_witness_attributes[0];
|
||||
let zkcm_user_sk = params.gen1() * r_attributes[0];
|
||||
|
||||
// covert to bytes
|
||||
let gammas_bytes = instance
|
||||
.gammas
|
||||
let gammas_bytes = params
|
||||
.gammas()
|
||||
.iter()
|
||||
.map(|gamma| gamma.to_bytes())
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let pc_commitments_bytes = instance
|
||||
.pc_commitments
|
||||
let zkcm_pedcom_bytes = zkcm_pedcom
|
||||
.iter()
|
||||
.map(|cm| cm.to_bytes())
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let zk_commitments_attributes_bytes = zk_pc_commitments_attributes
|
||||
.iter()
|
||||
.map(|cm| cm.to_bytes())
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
// compute zkp challenge
|
||||
println!("Zk commitments to com {:?}", zkcm_com.to_bytes());
|
||||
// compute zkp challenge using g1, gammas, c, h, c1, c2, c3, zk commitments
|
||||
let challenge = compute_challenge::<ChallengeDigest, _, _>(
|
||||
std::iter::once(instance.g1_gen.to_bytes().as_ref())
|
||||
.chain(gammas_bytes.iter().map(|hs| hs.as_ref()))
|
||||
.chain(std::iter::once(
|
||||
instance.attrs_commitment.to_bytes().as_ref(),
|
||||
))
|
||||
.chain(std::iter::once(
|
||||
instance.attrs_commitment_hash.to_bytes().as_ref(),
|
||||
))
|
||||
.chain(pc_commitments_bytes.iter().map(|pcm| pcm.as_ref()))
|
||||
.chain(std::iter::once(
|
||||
zk_commitment_attributes.to_bytes().as_ref(),
|
||||
))
|
||||
.chain(zk_commitments_attributes_bytes.iter().map(|c| c.as_ref()))
|
||||
.chain(std::iter::once(zk_commitment_user_sk.to_bytes().as_ref())),
|
||||
std::iter::once(params.gen1().to_bytes().as_ref())
|
||||
.chain(gammas_bytes.iter().map(|gamma| gamma.as_ref()))
|
||||
.chain(std::iter::once(instance.to_bytes().as_ref()))
|
||||
.chain(std::iter::once(zkcm_com.to_bytes().as_ref()))
|
||||
.chain(zkcm_pedcom_bytes.iter().map(|c| c.as_ref()))
|
||||
.chain(std::iter::once(zkcm_user_sk.to_bytes().as_ref())),
|
||||
);
|
||||
|
||||
// compute response
|
||||
let response_opening = produce_response(
|
||||
&r_commitment_opening,
|
||||
&r_com_opening,
|
||||
&challenge,
|
||||
&witness.attrs_commitment_opening,
|
||||
&witness.com_opening,
|
||||
);
|
||||
let response_openings = produce_responses(
|
||||
&r_pedersen_commitments_openings,
|
||||
&r_pedcom_openings,
|
||||
&challenge,
|
||||
&witness.pc_openings.iter().collect::<Vec<_>>(),
|
||||
&witness.pc_coms_openings.iter().collect::<Vec<_>>(),
|
||||
);
|
||||
let response_attributes = produce_responses(
|
||||
&r_witness_attributes,
|
||||
&r_attributes,
|
||||
&challenge,
|
||||
&witness.attributes.iter().collect::<Vec<_>>(),
|
||||
);
|
||||
@@ -168,61 +252,119 @@ impl WithdrawalReqProof {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn verify(&self, instance: &WithdrawalReqInstance) -> bool {
|
||||
// recompute zk commitments
|
||||
let zk_commitment_attributes = instance.g1_gen * self.response_opening
|
||||
pub(crate) fn verify(&self, params: &Parameters, instance: &WithdrawalReqInstance) -> bool {
|
||||
// recompute zk commitments for each instance
|
||||
let zkcm_com = instance.com * self.challenge
|
||||
+ params.gen1() * self.response_opening
|
||||
+ self
|
||||
.response_attributes
|
||||
.iter()
|
||||
.zip(instance.gammas.iter())
|
||||
.map(|(wm_i, gamma_i)| gamma_i * wm_i)
|
||||
.sum::<G1Projective>();
|
||||
|
||||
let zk_pc_commitments_attributes = self
|
||||
.response_openings
|
||||
.response_attributes
|
||||
.iter()
|
||||
.zip(self.response_attributes.iter())
|
||||
.map(|(o_j, m_j)| instance.g1_gen * o_j + instance.attrs_commitment_hash * m_j)
|
||||
.zip(params.gammas().iter())
|
||||
.map(|(m_i, gamma_i)| gamma_i * m_i)
|
||||
.sum::<G1Projective>();
|
||||
|
||||
let zkcm_pedcom = izip!(
|
||||
instance.pc_coms.iter(),
|
||||
self.response_openings.iter(),
|
||||
self.response_attributes.iter())
|
||||
.map(|(cm_j, resp_o_j, resp_m_j)| cm_j * self.challenge + params.gen1() * resp_o_j + instance.h * resp_m_j)
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let zk_commitment_user_sk = instance.g1_gen * self.response_attributes[0];
|
||||
let zk_commitment_user_sk = instance.pk_user.pk * self.challenge + params.gen1() * self.response_attributes[0];
|
||||
|
||||
// covert to bytes
|
||||
let gammas_bytes = instance
|
||||
.gammas
|
||||
let gammas_bytes = params
|
||||
.gammas()
|
||||
.iter()
|
||||
.map(|gamma| gamma.to_bytes())
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let pc_commitments_bytes = instance
|
||||
.pc_commitments
|
||||
let zkcm_pedcom_bytes = zkcm_pedcom
|
||||
.iter()
|
||||
.map(|cm| cm.to_bytes())
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let zk_commitments_attributes_bytes = zk_pc_commitments_attributes
|
||||
.iter()
|
||||
.map(|cm| cm.to_bytes())
|
||||
.collect::<Vec<_>>();
|
||||
println!("Zk commitments to com Vfy {:?}", zkcm_com.to_bytes());
|
||||
|
||||
// recompute zkp challenge
|
||||
let challenge = compute_challenge::<ChallengeDigest, _, _>(
|
||||
std::iter::once(instance.g1_gen.to_bytes().as_ref())
|
||||
std::iter::once(params.gen1().to_bytes().as_ref())
|
||||
.chain(gammas_bytes.iter().map(|hs| hs.as_ref()))
|
||||
.chain(std::iter::once(
|
||||
instance.attrs_commitment.to_bytes().as_ref(),
|
||||
))
|
||||
.chain(std::iter::once(
|
||||
instance.attrs_commitment_hash.to_bytes().as_ref(),
|
||||
))
|
||||
.chain(pc_commitments_bytes.iter().map(|pcm| pcm.as_ref()))
|
||||
.chain(std::iter::once(
|
||||
zk_commitment_attributes.to_bytes().as_ref(),
|
||||
))
|
||||
.chain(zk_commitments_attributes_bytes.iter().map(|c| c.as_ref()))
|
||||
.chain(std::iter::once(instance.to_bytes().as_ref()))
|
||||
.chain(std::iter::once(zkcm_com.to_bytes().as_ref()))
|
||||
.chain(zkcm_pedcom_bytes.iter().map(|c| c.as_ref()))
|
||||
.chain(std::iter::once(zk_commitment_user_sk.to_bytes().as_ref())),
|
||||
);
|
||||
|
||||
println!("Original challenge: {:?}", self.challenge);
|
||||
println!("Recomputed challenge: {:?}", challenge);
|
||||
challenge == self.challenge
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use group::Group;
|
||||
use rand::thread_rng;
|
||||
|
||||
use crate::utils::hash_g1;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn withdrawal_request_instance_roundtrip() {
|
||||
let mut rng = thread_rng();
|
||||
let params = Parameters::new().unwrap();
|
||||
let instance = WithdrawalReqInstance {
|
||||
com: G1Projective::random(&mut rng),
|
||||
h: G1Projective::random(&mut rng),
|
||||
pc_coms: vec![G1Projective::random(&mut rng), G1Projective::random(&mut rng), G1Projective::random(&mut rng)],
|
||||
pk_user: PublicKeyUser { pk: params.gen1() * params.random_scalar() },
|
||||
};
|
||||
|
||||
let instance_bytes = instance.to_bytes();
|
||||
let instance_p = WithdrawalReqInstance::from_bytes(&instance_bytes).unwrap();
|
||||
assert_eq!(instance, instance_p)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn withdrawal_proof_construct_and_verify() {
|
||||
let mut rng = thread_rng();
|
||||
let params = Parameters::new().unwrap();
|
||||
let sk = params.random_scalar();
|
||||
let pk_user = PublicKeyUser { pk: params.gen1() * sk };
|
||||
let v = params.random_scalar();
|
||||
let t = params.random_scalar();
|
||||
let attr = vec![sk, v, t];
|
||||
|
||||
let com_opening = params.random_scalar();
|
||||
let com = params.gen1() * com_opening + attr
|
||||
.iter()
|
||||
.zip(params.gammas())
|
||||
.map(|(&m, gamma)| gamma * m)
|
||||
.sum::<G1Projective>();
|
||||
let h = hash_g1(com.to_bytes());
|
||||
|
||||
let pc_openings = params.n_random_scalars(attr.len());
|
||||
let pc_coms = pc_openings
|
||||
.iter()
|
||||
.zip(attr.iter())
|
||||
.map(|(o_j, m_j)| params.gen1() * o_j + h * m_j)
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let instance = WithdrawalReqInstance {
|
||||
com,
|
||||
h,
|
||||
pc_coms,
|
||||
pk_user,
|
||||
};
|
||||
|
||||
let witness = WithdrawalReqWitness {
|
||||
attributes: attr,
|
||||
com_opening,
|
||||
pc_coms_openings: pc_openings,
|
||||
};
|
||||
let zk_proof = WithdrawalReqProof::construct(¶ms, &instance, &witness);
|
||||
assert!(zk_proof.verify(¶ms, &instance))
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
use bls12_381::G1Projective;
|
||||
use bls12_381::{G1Projective, Scalar};
|
||||
|
||||
pub mod aggregation;
|
||||
pub mod keygen;
|
||||
@@ -8,6 +8,16 @@ pub mod withdrawal;
|
||||
|
||||
pub type SignerIndex = u64;
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
#[cfg_attr(test, derive(PartialEq))]
|
||||
pub struct Signature(pub(crate) G1Projective, pub(crate) G1Projective);
|
||||
|
||||
#[derive(Debug)]
|
||||
#[cfg_attr(test, derive(PartialEq))]
|
||||
pub struct BlindedSignature(G1Projective, G1Projective);
|
||||
|
||||
pub struct Wallet {
|
||||
sig: Signature,
|
||||
v: Scalar,
|
||||
idx: Option<SignerIndex>,
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
use bls12_381::{G1Affine, G2Affine, Scalar};
|
||||
use bls12_381::{G1Affine, G2Affine, G2Prepared, Scalar};
|
||||
use ff::Field;
|
||||
use group::{Curve, GroupEncoding};
|
||||
use rand::thread_rng;
|
||||
@@ -18,6 +18,8 @@ pub struct Parameters {
|
||||
gammas: Vec<G1Affine>,
|
||||
/// Value of wallet
|
||||
L: usize,
|
||||
/// Precomputed G2 generator used for the miller loop
|
||||
_g2_prepared_miller: G2Prepared,
|
||||
}
|
||||
|
||||
impl Parameters {
|
||||
@@ -30,6 +32,7 @@ impl Parameters {
|
||||
g2: G2Affine::generator(),
|
||||
gammas,
|
||||
L: MAX_COIN_VALUE,
|
||||
_g2_prepared_miller: G2Prepared::from(G2Affine::generator()),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -44,6 +47,7 @@ impl Parameters {
|
||||
pub(crate) fn gammas(&self) -> &Vec<G1Affine> {
|
||||
&self.gammas
|
||||
}
|
||||
|
||||
|
||||
pub fn random_scalar(&self) -> Scalar {
|
||||
// lazily-initialized thread-local random number generator, seeded by the system
|
||||
@@ -54,6 +58,10 @@ impl Parameters {
|
||||
pub fn n_random_scalars(&self, n: usize) -> Vec<Scalar> {
|
||||
(0..n).map(|_| self.random_scalar()).collect()
|
||||
}
|
||||
|
||||
pub(crate) fn prepared_miller_g2(&self) -> &G2Prepared {
|
||||
&self._g2_prepared_miller
|
||||
}
|
||||
}
|
||||
|
||||
pub fn setup() -> Result<Parameters> {
|
||||
|
||||
@@ -1,29 +1,30 @@
|
||||
use bls12_381::{G1Projective, Scalar};
|
||||
use group::GroupEncoding;
|
||||
use bls12_381::{G1Projective, G2Prepared, G2Projective, Scalar};
|
||||
use group::{Curve, GroupEncoding};
|
||||
|
||||
use crate::error::{CompactEcashError, Result};
|
||||
use crate::proofs::{WithdrawalReqInstance, WithdrawalReqProof, WithdrawalReqWitness};
|
||||
use crate::scheme::BlindedSignature;
|
||||
use crate::scheme::keygen::{PublicKeyUser, SecretKeyAuth, SecretKeyUser};
|
||||
use crate::scheme::{BlindedSignature, Signature, Wallet};
|
||||
use crate::scheme::keygen::{PublicKeyUser, SecretKeyAuth, SecretKeyUser, VerificationKeyAuth};
|
||||
use crate::scheme::keygen::ttp_keygen;
|
||||
use crate::scheme::setup::Parameters;
|
||||
use crate::utils::hash_g1;
|
||||
use crate::utils::{check_bilinear_pairing, hash_g1};
|
||||
|
||||
pub struct WithdrawalRequest {
|
||||
commitment_hash: G1Projective,
|
||||
attrs_commitment: G1Projective,
|
||||
pc_commitments: Vec<G1Projective>,
|
||||
com_hash: G1Projective,
|
||||
com: G1Projective,
|
||||
pc_coms: Vec<G1Projective>,
|
||||
zk_proof: WithdrawalReqProof,
|
||||
}
|
||||
|
||||
pub struct RequestInfo {
|
||||
commitment_hash: G1Projective,
|
||||
attrs_commitment_opening: Scalar,
|
||||
pc_openings: Vec<Scalar>,
|
||||
com_hash: G1Projective,
|
||||
com_opening: Scalar,
|
||||
pc_coms_openings: Vec<Scalar>,
|
||||
v: Scalar,
|
||||
t: Scalar,
|
||||
}
|
||||
|
||||
|
||||
pub fn withdrawal_request(
|
||||
params: &Parameters,
|
||||
sk_user: &SecretKeyUser,
|
||||
@@ -33,55 +34,52 @@ pub fn withdrawal_request(
|
||||
|
||||
let attributes = vec![sk_user.sk, v, t];
|
||||
let gammas = params.gammas();
|
||||
let attrs_commitment_opening = params.random_scalar();
|
||||
let attrs_commitment = attributes
|
||||
let com_opening = params.random_scalar();
|
||||
let com = params.gen1() * com_opening + attributes
|
||||
.iter()
|
||||
.zip(gammas)
|
||||
.map(|(&m, gamma)| gamma * m)
|
||||
.sum::<G1Projective>();
|
||||
|
||||
let attrs_commitment_hash = hash_g1(attrs_commitment.to_bytes());
|
||||
// Value h in the paper
|
||||
let com_hash = hash_g1(com.to_bytes());
|
||||
|
||||
let pc_openings = params.n_random_scalars(attributes.len());
|
||||
let pc_coms_openings = params.n_random_scalars(attributes.len());
|
||||
|
||||
// Compute Pedersen commitment for each attribute
|
||||
let pc_commitments = pc_openings
|
||||
let pc_coms = pc_coms_openings
|
||||
.iter()
|
||||
.zip(attributes.iter())
|
||||
.map(|(o_j, m_j)| params.gen1() * o_j + attrs_commitment_hash * m_j)
|
||||
.map(|(o_j, m_j)| params.gen1() * o_j + com_hash * m_j)
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
// construct a zk proof of knowledge proving possession of m1, m2, m3, o, o1, o2, o3
|
||||
let instance = WithdrawalReqInstance {
|
||||
g1_gen: *params.gen1(),
|
||||
gammas: gammas.clone(),
|
||||
attrs_commitment,
|
||||
attrs_commitment_hash,
|
||||
pc_commitments: pc_commitments.clone(),
|
||||
pk_user: PublicKeyUser {
|
||||
pk: params.gen1() * sk_user.sk,
|
||||
},
|
||||
com,
|
||||
h: com_hash,
|
||||
pc_coms: pc_coms.clone(),
|
||||
pk_user: PublicKeyUser { pk: params.gen1() * sk_user.sk },
|
||||
};
|
||||
|
||||
let witness = WithdrawalReqWitness {
|
||||
attributes,
|
||||
attrs_commitment_opening,
|
||||
pc_openings: pc_openings.clone(),
|
||||
com_opening,
|
||||
pc_coms_openings: pc_coms_openings.clone(),
|
||||
};
|
||||
|
||||
let zk_proof = WithdrawalReqProof::construct(¶ms, &instance, &witness);
|
||||
|
||||
let req = WithdrawalRequest {
|
||||
commitment_hash: attrs_commitment_hash,
|
||||
attrs_commitment,
|
||||
pc_commitments: pc_commitments.clone(),
|
||||
com_hash,
|
||||
com,
|
||||
pc_coms: pc_coms.clone(),
|
||||
zk_proof,
|
||||
};
|
||||
|
||||
let req_info = RequestInfo {
|
||||
commitment_hash: attrs_commitment_hash,
|
||||
attrs_commitment_opening,
|
||||
pc_openings: pc_openings.clone(),
|
||||
com_hash,
|
||||
com_opening,
|
||||
pc_coms_openings: pc_coms_openings.clone(),
|
||||
v,
|
||||
t,
|
||||
};
|
||||
@@ -95,8 +93,8 @@ pub fn issue_wallet(
|
||||
pk_user: PublicKeyUser,
|
||||
withdrawal_req: &WithdrawalRequest,
|
||||
) -> Result<BlindedSignature> {
|
||||
let h = hash_g1(withdrawal_req.attrs_commitment.to_bytes());
|
||||
if !(h == withdrawal_req.commitment_hash) {
|
||||
let h = hash_g1(withdrawal_req.com.to_bytes());
|
||||
if !(h == withdrawal_req.com_hash) {
|
||||
return Err(CompactEcashError::WithdrawalRequestVerification(
|
||||
"Failed to verify the commitment hash".to_string(),
|
||||
));
|
||||
@@ -104,21 +102,19 @@ pub fn issue_wallet(
|
||||
|
||||
// verify zk proof
|
||||
let instance = WithdrawalReqInstance {
|
||||
g1_gen: *params.gen1(),
|
||||
gammas: params.gammas().clone(),
|
||||
attrs_commitment: withdrawal_req.attrs_commitment,
|
||||
attrs_commitment_hash: withdrawal_req.commitment_hash,
|
||||
pc_commitments: withdrawal_req.pc_commitments.clone(),
|
||||
com: withdrawal_req.com,
|
||||
h: withdrawal_req.com_hash,
|
||||
pc_coms: withdrawal_req.pc_coms.clone(),
|
||||
pk_user,
|
||||
};
|
||||
if !withdrawal_req.zk_proof.verify(&instance) {
|
||||
if !withdrawal_req.zk_proof.verify(¶ms, &instance) {
|
||||
return Err(CompactEcashError::WithdrawalRequestVerification(
|
||||
"Failed to verify the proof of knowledge".to_string(),
|
||||
));
|
||||
}
|
||||
|
||||
let sig = withdrawal_req
|
||||
.pc_commitments
|
||||
.pc_coms
|
||||
.iter()
|
||||
.zip(sk_auth.ys.iter())
|
||||
.map(|(pc, yi)| pc * yi)
|
||||
@@ -129,6 +125,57 @@ pub fn issue_wallet(
|
||||
}
|
||||
|
||||
|
||||
pub fn issue_verify(params: &Parameters, vk_auth: &VerificationKeyAuth, sk_user: &SecretKeyUser, blind_signature: &BlindedSignature, req_info: &RequestInfo) -> Result<Wallet> {
|
||||
// Parse the blinded signature
|
||||
let h = blind_signature.0;
|
||||
let c = blind_signature.1;
|
||||
|
||||
// Verify the integrity of the reponse from the authority
|
||||
if !(req_info.com_hash == h) {
|
||||
return Err(CompactEcashError::IssuanceVfy(
|
||||
"Failed to verify the proof of knowledge".to_string(),
|
||||
));
|
||||
}
|
||||
|
||||
// Unblind the blinded signature
|
||||
let blinding_removers = vk_auth
|
||||
.beta_g1
|
||||
.iter()
|
||||
.zip(req_info.pc_coms_openings.iter())
|
||||
.map(|(beta, opening)| beta * opening)
|
||||
.sum::<G1Projective>();
|
||||
|
||||
let unblinded_c = c - blinding_removers;
|
||||
|
||||
// Verify the signature correctness on the wallet share
|
||||
let attr = vec![sk_user.sk, req_info.v];
|
||||
|
||||
let signed_attributes = attr
|
||||
.iter()
|
||||
.zip(vk_auth.beta_g2.iter())
|
||||
.map(|(attr, beta_i)| beta_i * attr)
|
||||
.sum::<G2Projective>();
|
||||
|
||||
if !check_bilinear_pairing(
|
||||
&h.to_affine(),
|
||||
&G2Prepared::from((vk_auth.alpha + signed_attributes).to_affine()),
|
||||
&unblinded_c.to_affine(),
|
||||
params.prepared_miller_g2(),
|
||||
) {
|
||||
return Err(CompactEcashError::IssuanceVfy(
|
||||
"Verification of wallet share failed".to_string(),
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
Ok(Wallet {
|
||||
sig: Signature(h,
|
||||
unblinded_c),
|
||||
v: req_info.v,
|
||||
idx: None,
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
use itertools::izip;
|
||||
|
||||
use crate::error::CompactEcashError;
|
||||
use crate::scheme::keygen::{generate_keypair_user, PublicKeyUser, SecretKeyUser, ttp_keygen};
|
||||
use crate::scheme::keygen::{generate_keypair_user, PublicKeyUser, SecretKeyUser, ttp_keygen, VerificationKeyAuth};
|
||||
use crate::scheme::setup::Parameters;
|
||||
use crate::scheme::withdrawal::{issue_wallet, withdrawal_request};
|
||||
use crate::scheme::Wallet;
|
||||
use crate::scheme::withdrawal::{issue_verify, issue_wallet, withdrawal_request};
|
||||
use crate::VerificationKey;
|
||||
|
||||
#[test]
|
||||
fn main() -> Result<(), CompactEcashError> {
|
||||
@@ -9,10 +13,30 @@ fn main() -> Result<(), CompactEcashError> {
|
||||
let user_keypair = generate_keypair_user(¶ms);
|
||||
|
||||
let (req, req_info) = withdrawal_request(¶ms, &user_keypair.secret_key()).unwrap();
|
||||
let mut authorities_keypairs = ttp_keygen(¶ms, 1, 1).unwrap();
|
||||
let authorities_keypairs = ttp_keygen(¶ms, 2, 3).unwrap();
|
||||
|
||||
let verification_keys_auth: Vec<VerificationKeyAuth> = authorities_keypairs
|
||||
.iter()
|
||||
.map(|keypair| keypair.verification_key())
|
||||
.collect();
|
||||
|
||||
let mut wallet_blinded_signatures = Vec::new();
|
||||
for auth_keypair in authorities_keypairs {
|
||||
let blind_signature = issue_wallet(¶ms, auth_keypair.secret_key(), user_keypair.public_key(), &req);
|
||||
wallet_blinded_signatures.push(blind_signature.unwrap());
|
||||
}
|
||||
|
||||
// let unblinded_wallet_shares: Vec<Wallet> =
|
||||
// izip!(wallet_blinded_signatures.iter(), verification_keys_auth.iter())
|
||||
// .map(|(w, vk)| {
|
||||
// issue_verify(¶ms,
|
||||
// vk,
|
||||
// &user_keypair.secret_key(),
|
||||
// w,
|
||||
// &req_info)
|
||||
// .unwrap()
|
||||
// })
|
||||
// .collect();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -4,10 +4,12 @@
|
||||
use core::iter::Sum;
|
||||
use core::ops::Mul;
|
||||
use std::convert::TryInto;
|
||||
use std::ops::Neg;
|
||||
|
||||
use bls12_381::{G1Affine, G1Projective, G2Affine, G2Projective, Scalar};
|
||||
use bls12_381::{G1Affine, G1Projective, G2Affine, G2Prepared, G2Projective, multi_miller_loop, Scalar};
|
||||
use bls12_381::hash_to_curve::{ExpandMsgXmd, HashToCurve, HashToField};
|
||||
use ff::Field;
|
||||
use group::Group;
|
||||
|
||||
use crate::error::{CompactEcashError, Result};
|
||||
use crate::scheme::setup::Parameters;
|
||||
@@ -175,6 +177,17 @@ pub fn try_deserialize_g2_projective(bytes: &[u8; 96], err: CompactEcashError) -
|
||||
.map(G2Projective::from)
|
||||
}
|
||||
|
||||
/// Checks whether e(P, Q) * e(-R, S) == id
|
||||
pub fn check_bilinear_pairing(p: &G1Affine, q: &G2Prepared, r: &G1Affine, s: &G2Prepared) -> bool {
|
||||
// checking e(P, Q) * e(-R, S) == id
|
||||
// is equivalent to checking e(P, Q) == e(R, S)
|
||||
// but requires only a single final exponentiation rather than two of them
|
||||
// and therefore, as seen via benchmarks.rs, is almost 50% faster
|
||||
// (1.47ms vs 2.45ms, tested on R9 5900X)
|
||||
|
||||
let multi_miller = multi_miller_loop(&[(p, q), (&r.neg(), s)]);
|
||||
multi_miller.final_exponentiation().is_identity().into()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
Reference in New Issue
Block a user