Browse Source

feat: share the issuer pubkey element between show and issue scopes

The issuer public key component for a given (credential type, attribute)
is one value, but the show scope and the issue scope mint separate
identifiers for it (`X_show_Hattr_*` and `X_iss_Hattr_*`), so a protocol
that shows and issues the same credential type carried it twice -- two
compressions and two 32-byte encodings per relation, for one value.

The first scope to declare it now owns the element and later ones are
tagged `dedup`, sharing it whenever the values agree at run time. They
need not: `handle_update` gives the shown credential the caller's old
public key and the issued one the server's current key, so a rotation
makes them genuinely different, and then each simply keeps its own
element.

For a three-attribute credential shown and reissued, this is three
elements per client relation, on both sides.

Requires the `dedup` tag from sigma-compiler, and changes the encoded
instance, hence the challenge: prover and verifier must be upgraded
together.
Michele Orrù 4 weeks ago
parent
commit
d9752fdad1
1 changed files with 36 additions and 2 deletions
  1. 36 2
      cmz-core/src/lib.rs

+ 36 - 2
cmz-core/src/lib.rs

@@ -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,)*),