Fix a bug in the commitment computation

This commit is contained in:
aniampio
2022-03-23 15:50:30 +00:00
committed by durch
parent a0c81c982c
commit d00bef2ddf
7 changed files with 389 additions and 142 deletions
+3
View File
@@ -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,
+236 -94
View File
@@ -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(&params, &instance, &witness);
assert!(zk_proof.verify(&params, &instance))
}
}
+11 -1
View File
@@ -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>,
}
+9 -1
View File
@@ -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(&params, &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(&params, &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::*;
+27 -3
View File
@@ -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(&params);
let (req, req_info) = withdrawal_request(&params, &user_keypair.secret_key()).unwrap();
let mut authorities_keypairs = ttp_keygen(&params, 1, 1).unwrap();
let authorities_keypairs = ttp_keygen(&params, 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(&params, 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(&params,
// vk,
// &user_keypair.secret_key(),
// w,
// &req_info)
// .unwrap()
// })
// .collect();
Ok(())
}
+14 -1
View File
@@ -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 {