Add issuance and issuance verification - but one of the tests is failing

This commit is contained in:
aniampio
2022-04-28 13:05:54 +01:00
committed by durch
parent 7632e524c0
commit 0fc5b97dfb
8 changed files with 721 additions and 17 deletions
@@ -1 +1,63 @@
use std::borrow::Borrow;
use std::convert::TryFrom;
use std::convert::TryInto;
use bls12_381::{G1Affine, G1Projective, Scalar};
use digest::Digest;
use digest::generic_array::typenum::Unsigned;
use group::GroupEncoding;
use sha2::Sha256;
use crate::error::{DivisibleEcashError, Result};
use crate::scheme::keygen::PublicKeyUser;
use crate::scheme::setup::GroupParameters;
use crate::utils::try_deserialize_g1_projective;
pub mod proof_withdrawal;
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]>,
{
let mut h = D::new();
for point_representation in iter {
h.update(point_representation);
}
let digest = h.finalize();
// TODO: I don't like the 0 padding here (though it's what we've been using before,
// but we never had a security audit anyway...)
// instead we could maybe use the `from_bytes` variant and adding some suffix
// when computing the digest until we produce a valid scalar.
let mut bytes = [0u8; 64];
let pad_size = 64usize
.checked_sub(D::OutputSize::to_usize())
.unwrap_or_default();
bytes[pad_size..].copy_from_slice(&digest);
Scalar::from_bytes_wide(&bytes)
}
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>,
{
debug_assert_eq!(witnesses.len(), secrets.len());
witnesses
.iter()
.zip(secrets.iter())
.map(|(w, x)| produce_response(w, challenge, x.borrow()))
.collect()
}
@@ -0,0 +1,4 @@
pub struct SpendInstance {}
pub struct SpendWitness {}
@@ -0,0 +1,335 @@
use std::convert::{TryFrom, TryInto};
use bls12_381::{G1Projective, Scalar};
use group::GroupEncoding;
use itertools::izip;
use crate::error::{DivisibleEcashError, Result};
use crate::proofs::{ChallengeDigest, compute_challenge, produce_response, produce_responses};
use crate::scheme::keygen::PublicKeyUser;
use crate::scheme::setup::{GroupParameters, Parameters};
use crate::utils::try_deserialize_g1_projective;
#[derive(Debug)]
#[cfg_attr(test, derive(PartialEq))]
// instance: g, gamma1, gamma2, gamma3, com, h, com1, com2, com3, pkUser
pub struct WithdrawalReqInstance {
// 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 = DivisibleEcashError;
fn try_from(bytes: &[u8]) -> Result<WithdrawalReqInstance> {
if bytes.len() < 48 * 4 + 8 || (bytes.len() - 8) % 48 != 0 {
return Err(DivisibleEcashError::DeserializationInvalidLength {
actual: bytes.len(),
modulus_target: bytes.len() - 8,
target: 48 * 4 + 8,
modulus: 48,
object: "withdrawal request zkp instance".to_string(),
});
}
let com_bytes: [u8; 48] = bytes[..48].try_into().unwrap();
let com = try_deserialize_g1_projective(
&com_bytes,
DivisibleEcashError::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,
DivisibleEcashError::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(DivisibleEcashError::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,
DivisibleEcashError::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,
DivisibleEcashError::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>,
// Opening for the joined commitment com
pub com_opening: Scalar,
// Openings for the pedersen commitments
pub pc_coms_openings: Vec<Scalar>,
}
pub struct WithdrawalReqProof {
challenge: Scalar,
response_opening: Scalar,
response_openings: Vec<Scalar>,
response_attributes: Vec<Scalar>,
}
impl WithdrawalReqProof {
pub(crate) fn construct(
params: &Parameters,
instance: &WithdrawalReqInstance,
witness: &WithdrawalReqWitness,
) -> Self {
let grp = params.get_grp();
let g1 = grp.gen1();
let paramsU = params.get_paramsUser();
// generate random values to replace the witnesses
let r_com_opening = grp.random_scalar();
let r_pedcom_openings = grp.n_random_scalars(witness.pc_coms_openings.len());
let r_attributes = grp.n_random_scalars(witness.attributes.len());
// compute zkp commitments for each instance
let zkcm_com = g1 * r_com_opening
+ r_attributes
.iter()
.zip(paramsU.get_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)| g1 * o_j + instance.h * m_j)
.collect::<Vec<_>>();
let zkcm_user_sk = g1 * r_attributes[0];
// covert to bytes
let gammas_bytes = paramsU
.get_gammas()
.iter()
.map(|gamma| gamma.to_bytes())
.collect::<Vec<_>>();
let zkcm_pedcom_bytes = zkcm_pedcom
.iter()
.map(|cm| cm.to_bytes())
.collect::<Vec<_>>();
// compute zkp challenge using g1, gammas, c, h, c1, c2, c3, zk commitments
let challenge = compute_challenge::<ChallengeDigest, _, _>(
std::iter::once(g1.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_com_opening, &challenge, &witness.com_opening);
let response_openings = produce_responses(
&r_pedcom_openings,
&challenge,
&witness.pc_coms_openings.iter().collect::<Vec<_>>(),
);
let response_attributes = produce_responses(
&r_attributes,
&challenge,
&witness.attributes.iter().collect::<Vec<_>>(),
);
WithdrawalReqProof {
challenge,
response_opening,
response_openings,
response_attributes,
}
}
pub(crate) fn verify(
&self,
params: &Parameters,
instance: &WithdrawalReqInstance,
) -> bool {
let grp = params.get_grp();
let g1 = grp.gen1();
let paramsU = params.get_paramsUser();
// recompute zk commitments for each instance
let zkcm_com = instance.com * self.challenge
+ g1 * self.response_opening
+ self
.response_attributes
.iter()
.zip(paramsU.get_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 + g1 * resp_o_j + instance.h * resp_m_j
})
.collect::<Vec<_>>();
let zk_commitment_user_sk =
instance.pk_user.pk * self.challenge + g1 * self.response_attributes[0];
// covert to bytes
let gammas_bytes = paramsU
.get_gammas()
.iter()
.map(|gamma| gamma.to_bytes())
.collect::<Vec<_>>();
let zkcm_pedcom_bytes = zkcm_pedcom
.iter()
.map(|cm| cm.to_bytes())
.collect::<Vec<_>>();
// recompute zkp challenge
let challenge = compute_challenge::<ChallengeDigest, _, _>(
std::iter::once(g1.to_bytes().as_ref())
.chain(gammas_bytes.iter().map(|hs| hs.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())),
);
challenge == self.challenge
}
}
#[cfg(test)]
mod tests {
use group::Group;
use rand::thread_rng;
use crate::scheme::setup::Parameters;
use crate::utils::hash_g1;
use super::*;
#[test]
fn withdrawal_request_instance_roundtrip() {
let mut rng = thread_rng();
let params = GroupParameters::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 rng = thread_rng();
let grp = GroupParameters::new().unwrap();
let params = Parameters::new(grp.clone());
let sk = grp.random_scalar();
let pk_user = PublicKeyUser {
pk: grp.gen1() * sk,
};
let v = grp.random_scalar();
let t = grp.random_scalar();
let attr = vec![sk, v, t];
let com_opening = grp.random_scalar();
let com = grp.gen1() * com_opening
+ attr
.iter()
.zip(params.get_paramsUser().get_gammas())
.map(|(&m, gamma)| gamma * m)
.sum::<G1Projective>();
let h = hash_g1(com.to_bytes());
let pc_openings = grp.n_random_scalars(attr.len());
let pc_coms = pc_openings
.iter()
.zip(attr.iter())
.map(|(o_j, m_j)| grp.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))
}
}
@@ -365,7 +365,7 @@ impl KeyPairUser {
pub fn ttp_keygen_authorities(
params: Parameters,
params: &Parameters,
threshold: u64,
num_authorities: u64) -> Result<Vec<KeyPairAuth>> {
if threshold == 0 {
@@ -381,10 +381,11 @@ pub fn ttp_keygen_authorities(
));
}
let grp = params.get_grp();
// generate polynomials
let v = Polynomial::new_random(&params.get_grp(), threshold - 1);
let v = Polynomial::new_random(&grp, threshold - 1);
let ws = (0..2)
.map(|_| Polynomial::new_random(&params.get_grp(), threshold - 1))
.map(|_| Polynomial::new_random(&grp, threshold - 1))
.collect::<Vec<_>>();
let polynomial_indices = (1..=num_authorities).collect::<Vec<_>>();
@@ -404,7 +405,7 @@ pub fn ttp_keygen_authorities(
let keypairs = secret_keys
.zip(polynomial_indices.iter())
.map(|(secret_key, index)| {
let verification_key = secret_key.verification_key(&params.get_grp());
let verification_key = secret_key.verification_key(&grp);
KeyPairAuth {
secret_key,
verification_key,
@@ -416,7 +417,7 @@ pub fn ttp_keygen_authorities(
Ok(keypairs)
}
pub fn ttp_keygen_users(params: Parameters) -> KeyPairUser {
pub fn ttp_keygen_users(params: &Parameters) -> KeyPairUser {
let grp = params.get_grp();
let sk_user = SecretKeyUser { sk: grp.random_scalar() };
let pk_user = PublicKeyUser { pk: grp.gen1() * sk_user.sk };
@@ -1,6 +1,119 @@
use std::cell::Cell;
use bls12_381::{G2Projective, Scalar};
use crate::Attribute;
use crate::constants::L;
use crate::error::{DivisibleEcashError, Result};
use crate::scheme::keygen::{SecretKeyUser, VerificationKeyAuth};
use crate::scheme::setup::{GroupParameters, Parameters};
use crate::utils::{hash_to_scalar, Signature, SignerIndex};
pub mod aggregation;
pub mod identify;
pub mod keygen;
pub mod setup;
pub mod structure_preserving_signature;
pub mod withdrawal;
pub fn compute_kappa(
params: &GroupParameters,
verification_key: &VerificationKeyAuth,
attributes: &[Attribute],
blinding_factor: Scalar,
) -> G2Projective {
params.gen2() * blinding_factor
+ verification_key.alpha
+ attributes
.iter()
.zip(verification_key.beta_g2.iter())
.map(|(priv_attr, beta_i)| beta_i * priv_attr)
.sum::<G2Projective>()
}
pub struct PayInfo {
pub info: [u8; 32],
}
#[derive(Debug, Clone)]
pub struct Payment {}
pub struct PartialWallet {
sig: Signature,
v: Scalar,
idx: Option<SignerIndex>,
}
pub struct Wallet {
sig: Signature,
v: Scalar,
l: Cell<u64>,
}
impl Wallet {
pub fn signature(&self) -> &Signature {
&self.sig
}
pub fn v(&self) -> Scalar {
self.v
}
pub fn l(&self) -> u64 {
self.l.get()
}
fn up(&self) {
self.l.set(self.l.get() + 1);
}
fn down(&self) {
self.l.set(self.l.get() - 1);
}
pub(crate) fn spend(
&self,
params: &Parameters,
verification_key: &VerificationKeyAuth,
sk_user: &SecretKeyUser,
pay_info: &PayInfo,
V: u64,
) -> Result<(Payment, &Self)> {
if self.l() + V > L {
return Err(DivisibleEcashError::Spend(
"The counter l is higher than max L".to_string(),
));
}
let grp = params.get_grp();
let paramsU = params.get_paramsUser();
// randomize signature in the wallet
let (signature_prime, sign_blinding_factor) = self.signature().randomise(grp);
// construct kappa i.e., blinded attributes for show
let attributes = vec![sk_user.sk, self.v()];
// compute kappa
let kappa = compute_kappa(
&grp,
&verification_key,
&attributes,
sign_blinding_factor,
);
let r1 = grp.random_scalar();
let r2 = grp.random_scalar();
let phi1 = grp.gen1() * r1;
let phi2 = paramsU.get_ith_sigma(self.l.get() as usize) * self.v + paramsU.get_ith_eta(V as usize) * r1;
// compute hash of the payment info
let rr = hash_to_scalar(pay_info.info);
let rho1 = grp.gen1() * r2;
let rho2 = (grp.gen1() * rr) * sk_user.sk + paramsU.get_ith_theta(self.l.get() as usize) * self.v + paramsU.get_ith_eta(V as usize) * r2;
// compute the zk proof
// output pay and updated wallet
let pay = Payment {};
Ok((pay, self))
}
}
@@ -57,7 +57,7 @@ pub struct Parameters {
}
impl Parameters {
pub(crate) fn get_grp(&self) -> GroupParameters { self.grp.clone() }
pub(crate) fn get_grp(&self) -> &GroupParameters { &self.grp }
pub(crate) fn get_paramsUser(&self) -> &ParametersUser { &self.paramsUser }
@@ -92,7 +92,7 @@ impl Parameters {
let etasUser: Vec<G1Projective> = vec_a.iter().map(|x| g1 * x).collect();
let mut etasAuth: Vec<G2Projective> = Default::default();
for l in 1..=L {
for l in 1..=L + 1 {
for k in 0..=l - 1 {
etasAuth.push(g2 * (vec_a[l as usize].neg() * (y * Scalar::from(k))));
}
@@ -109,10 +109,7 @@ impl Parameters {
// Compute signature for each pair sigma, theta
let paramsUser = ParametersUser {
g1: *g1,
g2: *g2,
gamma1,
gamma2,
gammas: vec![gamma1, gamma2],
eta,
omega,
etas: etasUser,
@@ -137,12 +134,7 @@ impl Parameters {
#[derive(Debug, Clone)]
pub struct ParametersUser {
/// Generator of the G1 group
g1: G1Affine,
/// Generator of the G2 group
g2: G2Affine,
gamma1: G1Projective,
gamma2: G1Projective,
gammas: Vec<G1Projective>,
eta: G1Projective,
omega: G1Projective,
etas: Vec<G1Projective>,
@@ -152,8 +144,40 @@ pub struct ParametersUser {
sps_pk: SPSVerificationKey,
}
impl ParametersUser {
pub(crate) fn get_gammas(&self) -> &Vec<G1Projective> { &self.gammas }
pub(crate) fn get_eta(&self) -> &G1Projective { &self.eta }
pub(crate) fn get_omega(&self) -> &G1Projective { &self.omega }
pub(crate) fn get_etas(&self) -> &[G1Projective] { &self.etas }
pub(crate) fn get_ith_eta(&self, idx: usize) -> &G1Projective { self.etas.get(idx).unwrap() }
pub(crate) fn get_sigmas(&self) -> &[G1Projective] { &self.sigmas }
pub(crate) fn get_ith_sigma(&self, idx: usize) -> &G1Projective { self.sigmas.get(idx).unwrap() }
pub(crate) fn get_thetas(&self) -> &[G1Projective] { &self.thetas }
pub(crate) fn get_ith_theta(&self, idx: usize) -> &G1Projective { self.thetas.get(idx).unwrap() }
pub(crate) fn get_sps_signs(&self) -> &[SPSSignature] { &self.sps_signatures }
pub(crate) fn get_ith_sps_sign(&self, idx: usize) -> &SPSSignature { &self.sps_signatures.get(idx).unwrap() }
pub(crate) fn get_sps_pk(&self) -> &SPSVerificationKey { &self.sps_pk }
}
#[derive(Debug, Clone)]
pub struct ParametersAuthority {
deltas: Vec<G2Projective>,
etas: Vec<G2Projective>,
}
impl ParametersAuthority {
pub(crate) fn get_deltas(&self) -> &[G2Projective] { &self.deltas }
pub(crate) fn get_etas(&self) -> &[G2Projective] { &self.etas }
}
@@ -1 +1,166 @@
use bls12_381::{G1Projective, G2Prepared, G2Projective, Scalar};
use group::{Curve, GroupEncoding};
use crate::error::{DivisibleEcashError, Result};
use crate::proofs::proof_withdrawal::{WithdrawalReqInstance, WithdrawalReqProof, WithdrawalReqWitness};
use crate::scheme::keygen::{PublicKeyUser, SecretKeyAuth, SecretKeyUser, VerificationKeyAuth};
use crate::scheme::PartialWallet;
use crate::scheme::setup::{GroupParameters, Parameters};
use crate::utils::{BlindedSignature, check_bilinear_pairing, hash_g1, Signature};
pub struct WithdrawalRequest {
com_hash: G1Projective,
com: G1Projective,
pc_coms: Vec<G1Projective>,
zk_proof: WithdrawalReqProof,
}
pub struct RequestInfo {
com_hash: G1Projective,
pc_coms_openings: Vec<Scalar>,
v: Scalar,
}
pub fn withdrawal_request(params: Parameters, sk_user: SecretKeyUser) -> Result<(WithdrawalRequest, RequestInfo)> {
let grp = params.get_grp();
let g1 = grp.gen1();
let paramsU = params.get_paramsUser();
let v = grp.random_scalar();
let attributes = vec![sk_user.sk, v];
let com_opening = grp.random_scalar();
let commitment = g1 * com_opening
+ attributes
.iter()
.zip(paramsU.get_gammas())
.map(|(&m, gamma)| gamma * m)
.sum::<G1Projective>();
// Value h in the paper
let com_hash = hash_g1(commitment.to_bytes());
let pc_coms_openings = grp.n_random_scalars(attributes.len());
// Compute Pedersen commitment for each attribute
let pc_coms = pc_coms_openings
.iter()
.zip(attributes.iter())
.map(|(o_j, m_j)| g1 * o_j + com_hash * m_j)
.collect::<Vec<_>>();
// construct a zk proof of knowledge proving possession of m1, m2, o, o1, o2
let instance = WithdrawalReqInstance {
com: commitment,
h: com_hash,
pc_coms: pc_coms.clone(),
pk_user: sk_user.public_key(&params.get_grp()),
};
let witness = WithdrawalReqWitness {
attributes,
com_opening,
pc_coms_openings: pc_coms_openings.clone(),
};
let zk_proof = WithdrawalReqProof::construct(&params, &instance, &witness);
let req = WithdrawalRequest {
com_hash,
com: commitment,
pc_coms: pc_coms.clone(),
zk_proof,
};
let req_info = RequestInfo {
com_hash,
pc_coms_openings,
v,
};
Ok((req, req_info))
}
pub(crate) fn issue(params: &Parameters, req: WithdrawalRequest, pkU: PublicKeyUser, skA: SecretKeyAuth) -> Result<BlindedSignature> {
let h = hash_g1(req.com.to_bytes());
if !(h == req.com_hash) {
return Err(DivisibleEcashError::WithdrawalRequestVerification(
"Failed to verify the commitment hash".to_string(),
));
}
// verify zk proof
let instance = WithdrawalReqInstance {
com: req.com,
h: req.com_hash,
pc_coms: req.pc_coms.clone(),
pk_user: pkU,
};
if !req.zk_proof.verify(&params, &instance) {
return Err(DivisibleEcashError::WithdrawalRequestVerification(
"Failed to verify the proof of knowledge".to_string(),
));
}
let sig = req
.pc_coms
.iter()
.zip(skA.ys.iter())
.map(|(pc, yi)| pc * yi)
.chain(std::iter::once(h * skA.x))
.sum();
Ok(BlindedSignature(h, sig))
}
pub(crate) fn issue_verify(
params: &GroupParameters,
vk_auth: &VerificationKeyAuth,
sk_user: &SecretKeyUser,
blind_signature: &BlindedSignature,
req_info: &RequestInfo) -> Result<PartialWallet> {
// Parse the blinded signature
let h = blind_signature.0;
let c = blind_signature.1;
// Verify the integrity of the response from the authority
if !(req_info.com_hash == h) {
return Err(DivisibleEcashError::IssuanceVfy(
"Integrity verification failed".to_string(),
));
}
// Unblind the blinded signature on the partial wallet
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;
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>();
// Verify the signature correctness on the wallet share
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(DivisibleEcashError::IssuanceVfy(
"Verification of wallet share failed".to_string(),
));
}
Ok(PartialWallet {
sig: Signature(h, unblinded_c),
v: req_info.v,
idx: None,
})
}