|
|
@@ -400,6 +400,13 @@ pub fn cmz_core(
|
|
|
// only because it carries that extra tag.
|
|
|
let mut cli_proof_generator_points: Vec<Ident> = Vec::new();
|
|
|
let mut iss_proof_generator_points: Vec<Ident> = Vec::new();
|
|
|
+ // The issuer public key component `X` for a given (credential type,
|
|
|
+ // attribute) is one value, but the show scope and the issue scope mint
|
|
|
+ // separate identifiers for it. The first one declared owns the element;
|
|
|
+ // later ones are tagged `dedup` and share it whenever the values agree
|
|
|
+ // at run time (they can differ -- the key may have rotated).
|
|
|
+ let mut x_owner: HashMap<(String, String), Ident> = HashMap::new();
|
|
|
+ let mut cli_proof_dedup_points: Vec<(Ident, Ident)> = Vec::new();
|
|
|
let d_ident = format_ident!("d_privkey");
|
|
|
let D_ident = format_ident!("D_pubkey");
|
|
|
let iss_proof_sessid_ident = format_ident!("iss_proof_sessid");
|
|
|
@@ -687,7 +694,15 @@ pub fn cmz_core(
|
|
|
#C_statement + #scoped_attr * #X_attr
|
|
|
};
|
|
|
cli_proof_priv_scalars.push(scoped_attr.clone());
|
|
|
- cli_proof_cind_points.push(X_attr.clone());
|
|
|
+ match x_owner.entry((iss_cred_type.to_string(), attr_str.clone())) {
|
|
|
+ std::collections::hash_map::Entry::Occupied(owner) => {
|
|
|
+ cli_proof_dedup_points.push((X_attr.clone(), owner.get().clone()));
|
|
|
+ }
|
|
|
+ std::collections::hash_map::Entry::Vacant(slot) => {
|
|
|
+ slot.insert(X_attr.clone());
|
|
|
+ cli_proof_cind_points.push(X_attr.clone());
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
/* For each Reveal attribute: include attr in Request (client will
|
|
|
@@ -1216,7 +1231,15 @@ pub fn cmz_core(
|
|
|
cli_proof_priv_scalars.push(scoped_attr.clone());
|
|
|
cli_proof_rand_scalars.push(z_attr.clone());
|
|
|
cli_proof_pub_points.push(C_attr.clone());
|
|
|
- cli_proof_cind_points.push(X_attr.clone());
|
|
|
+ match x_owner.entry((show_cred_type.to_string(), attr_str.clone())) {
|
|
|
+ std::collections::hash_map::Entry::Occupied(owner) => {
|
|
|
+ cli_proof_dedup_points.push((X_attr.clone(), owner.get().clone()));
|
|
|
+ }
|
|
|
+ std::collections::hash_map::Entry::Vacant(slot) => {
|
|
|
+ slot.insert(X_attr.clone());
|
|
|
+ cli_proof_cind_points.push(X_attr.clone());
|
|
|
+ }
|
|
|
+ }
|
|
|
cli_proof_statements.push(quote! {
|
|
|
#C_attr = #scoped_attr * #P_cred + #z_attr * #A_ident,
|
|
|
});
|
|
|
@@ -1325,11 +1348,20 @@ pub fn cmz_core(
|
|
|
// The client will create a zero-knowledge proof
|
|
|
let cli_proof_ident = format_ident!("cli_proof");
|
|
|
request_fields.push_bytevec(&cli_proof_ident);
|
|
|
+ let cli_dedup_decls = cli_proof_dedup_points
|
|
|
+ .iter()
|
|
|
+ .map(|(id, peer)| quote! { cind dedup(#peer) #id })
|
|
|
+ .collect::<Vec<_>>();
|
|
|
+ let cli_proof_dedup_ids = cli_proof_dedup_points
|
|
|
+ .iter()
|
|
|
+ .map(|(id, _)| id.clone())
|
|
|
+ .collect::<Vec<_>>();
|
|
|
let cli_instance_fields = cli_proof_pub_points
|
|
|
.iter()
|
|
|
.chain(cli_proof_generator_points.iter())
|
|
|
.chain(cli_proof_const_points.iter())
|
|
|
.chain(cli_proof_cind_points.iter())
|
|
|
+ .chain(cli_proof_dedup_ids.iter())
|
|
|
.chain(cli_proof_pub_scalars.iter());
|
|
|
let cli_witness_fields = cli_proof_rand_scalars
|
|
|
.iter()
|
|
|
@@ -1351,6 +1383,7 @@ pub fn cmz_core(
|
|
|
.chain(cli_proof_generator_points.iter())
|
|
|
.chain(cli_proof_const_points.iter())
|
|
|
.chain(cli_proof_cind_points.iter())
|
|
|
+ .chain(cli_proof_dedup_ids.iter())
|
|
|
.chain(cli_proof_pub_scalars.iter());
|
|
|
handle_code_post_fill = quote! {
|
|
|
#handle_code_post_fill
|
|
|
@@ -1568,6 +1601,7 @@ pub fn cmz_core(
|
|
|
#(#cli_proof_priv_scalars,)*
|
|
|
#(pub #cli_proof_pub_scalars,)*),
|
|
|
(#(cind #cli_proof_cind_points,)*
|
|
|
+ #(#cli_dedup_decls,)*
|
|
|
#(#cli_proof_pub_points,)*
|
|
|
#(generator cind const #cli_proof_generator_points,)*
|
|
|
#(cind const #cli_proof_const_points,)*),
|