浏览代码

Generate the issuer proof statement for muCMZ

Ian Goldberg 5 月之前
父节点
当前提交
c5ab17a0d8
共有 3 个文件被更改,包括 155 次插入12 次删除
  1. 2 0
      Cargo.toml
  2. 134 4
      cmzcred_derive/src/lib.rs
  3. 19 8
      src/lib.rs

+ 2 - 0
Cargo.toml

@@ -13,7 +13,9 @@ hex = { version = "0.4", features = [ "serde" ] }
 lazy_static = "1"
 rand_core = "0.6"
 serde = { version = "1", features = [ "derive" ] }
+serde_bytes = "0.11"
 serde_with = "3"
+sigma_compiler = { path = "../sigma_compiler" }
 thiserror = "2"
 
 [dev-dependencies]

+ 134 - 4
cmzcred_derive/src/lib.rs

@@ -429,6 +429,7 @@ enum StructField {
     Point(Ident),
     EncPoint(Ident),
     Pubkey(Ident),
+    ByteVec(Ident),
 }
 
 // Convenience functions to create StructField items
@@ -445,6 +446,9 @@ impl StructField {
     pub fn pubkey(s: &str) -> Self {
         Self::Pubkey(Ident::new(s, Span::call_site().into()))
     }
+    pub fn bytevec(s: &str) -> Self {
+        Self::ByteVec(Ident::new(s, Span::call_site().into()))
+    }
 }
 
 // A list of StructField items
@@ -466,6 +470,9 @@ impl StructFieldList {
     pub fn push_pubkey(&mut self, s: &str) {
         self.fields.push(StructField::pubkey(s));
     }
+    pub fn push_bytevec(&mut self, s: &str) {
+        self.fields.push(StructField::bytevec(s));
+    }
     /// Output an iterator consisting of the field names
     pub fn field_iter<'a>(&'a self) -> impl Iterator<Item = &'a Ident> {
         self.fields.iter().map(|f| match f {
@@ -473,6 +480,7 @@ impl StructFieldList {
             StructField::Point(id) => id,
             StructField::EncPoint(id) => id,
             StructField::Pubkey(id) => id,
+            StructField::ByteVec(id) => id,
         })
     }
     /// Output a ToTokens of the fields as they would appear in a struct
@@ -494,6 +502,10 @@ impl StructFieldList {
             StructField::Pubkey(id) => quote! {
                 pub #id: CMZPubkey<Point>,
             },
+            StructField::ByteVec(id) => quote! {
+                #[serde(with = "serde_bytes")]
+                pub #id: Vec<u8>,
+            },
         });
         quote! { #(#decls)* }
     }
@@ -531,6 +543,16 @@ fn protocol_macro(
         quote! {}
     };
 
+    // The structure of the issuer's ZKP
+    let mut iss_proof_rand_scalars = Vec::<Ident>::default();
+    let mut iss_proof_priv_scalars = Vec::<Ident>::default();
+    let mut iss_proof_pub_points = Vec::<Ident>::default();
+    let mut iss_proof_const_points = Vec::<Ident>::default();
+    // Use quote! {} to enforce the correct type for
+    // iss_proof_statements, but then drop that entry
+    let mut iss_proof_statements = vec![quote! {}];
+    iss_proof_statements.clear();
+
     /* Credential issuing
 
        For each attribute of each credential to be issued, handle it
@@ -567,6 +589,37 @@ fn protocol_macro(
     // issued?
     let mut any_hide_joint = false;
 
+    let A_ident = format_ident!("A_generator");
+    let B_ident = format_ident!("B_generator");
+
+    prepare_code = quote! {
+        #prepare_code
+        let #A_ident = bp.A();
+    };
+    handle_code_pre_fill = quote! {
+        #handle_code_pre_fill
+        let #A_ident = bp.A();
+    };
+    finalize_code = quote! {
+        #finalize_code
+        let #A_ident = bp.A();
+    };
+
+    if proto_spec.issue_creds.len() > 0 {
+        prepare_code = quote! {
+            #prepare_code
+            let #B_ident = bp.B();
+        };
+        handle_code_pre_fill = quote! {
+            #handle_code_pre_fill
+            let #B_ident = bp.B();
+        };
+        finalize_code = quote! {
+            #finalize_code
+            let #B_ident = bp.B();
+        };
+    }
+
     for iss_cred in proto_spec.issue_creds.iter() {
         // Are there any Hide or Joint attributes in this particular
         // credential to be issued?
@@ -634,7 +687,7 @@ fn protocol_macro(
         };
         finalize_code = quote! {
             #finalize_code
-            #iss_cred_id.pubkey = self.#pubkey_cred;
+            #iss_cred_id.pubkey = self.#pubkey_cred.clone();
         };
 
         for (attr, &spec) in iss_cred.attrs.iter() {
@@ -879,7 +932,8 @@ fn protocol_macro(
             };
             finalize_code = quote! {
                 #finalize_code
-                #iss_cred_id.MAC.P = reply.#P_cred;
+                let #P_cred = reply.#P_cred;
+                #iss_cred_id.MAC.P = #P_cred;
                 #finalize_Q_code
             };
         }
@@ -933,11 +987,15 @@ fn protocol_macro(
                     #finalize_code
                 };
             }
+            let x0_cred = format_ident!("x0_iss_cred_{}", iss_cred.id);
+            let X0_cred = format_ident!("X0_iss_cred_{}", iss_cred.id);
             handle_code_post_auth = quote! {
                 #handle_code_post_auth
                 let #b_cred = <Scalar as ff::Field>::random(&mut *rng);
                 let #P_cred = bp.mulA(&#b_cred);
-                let #R_cred = #b_cred * (bp.mulA(&#iss_cred_id.privkey.x0) + #K_cred);
+                let #x0_cred = #iss_cred_id.privkey.x0;
+                let #X0_cred = #iss_cred_id.pubkey.X0.unwrap();
+                let #R_cred = #b_cred * (bp.mulA(&#x0_cred) + #K_cred);
             };
             let finalize_Q_code = if cred_hide_joint {
                 quote! {
@@ -950,9 +1008,26 @@ fn protocol_macro(
             };
             finalize_code = quote! {
                 #finalize_code
-                #iss_cred_id.MAC.P = reply.#P_cred;
+                let #P_cred = reply.#P_cred;
+                let #X0_cred = #iss_cred_id.pubkey.X0.unwrap();
+                let #R_cred = reply.#R_cred;
+                #iss_cred_id.MAC.P = #P_cred;
                 #finalize_Q_code
             };
+            // Construct the issuer proof for this credential
+            iss_proof_priv_scalars.push(x0_cred.clone());
+            iss_proof_rand_scalars.push(b_cred.clone());
+            iss_proof_pub_points.push(P_cred.clone());
+            iss_proof_pub_points.push(X0_cred.clone());
+            iss_proof_pub_points.push(K_cred.clone());
+            iss_proof_pub_points.push(R_cred.clone());
+            iss_proof_const_points.push(A_ident.clone());
+            iss_proof_const_points.push(B_ident.clone());
+            iss_proof_statements.push(quote! {
+                #P_cred = #b_cred * #A_ident,
+                #X0_cred = #x0_cred * #B_ident,
+                #R_cred = #x0_cred * #P_cred + #b_cred * #K_cred
+            });
         }
 
         any_hide_joint |= cred_hide_joint;
@@ -971,6 +1046,43 @@ fn protocol_macro(
         }
     }
 
+    if proto_spec.issue_creds.len() > 0 {
+        // The issuer will create a zero-knowledge proof
+        let iss_proof_ident = format_ident!("iss_proof");
+        reply_fields.push_bytevec(&iss_proof_ident.to_string());
+        let iss_params_fields = iss_proof_pub_points
+            .iter()
+            .chain(iss_proof_const_points.iter());
+        let iss_witness_fields = iss_proof_rand_scalars
+            .iter()
+            .chain(iss_proof_priv_scalars.iter());
+        handle_code_post_auth = quote! {
+            #handle_code_post_auth
+            let iss_proof_params = issuer_proof::Params {
+                #(#iss_params_fields,)*
+            };
+            let iss_proof_witness = issuer_proof::Witness {
+                #(#iss_witness_fields,)*
+            };
+            // If prove returns Err here, there's an actual bug.
+            let #iss_proof_ident = issuer_proof::prove(&iss_proof_params,
+                &iss_proof_witness).unwrap();
+        };
+        let cli_params_fields = iss_proof_pub_points
+            .iter()
+            .chain(iss_proof_const_points.iter());
+        finalize_code = quote! {
+            #finalize_code
+            let iss_proof_params = issuer_proof::Params {
+                #(#cli_params_fields,)*
+            };
+            if issuer_proof::verify(&iss_proof_params,
+                &reply.#iss_proof_ident).is_err() {
+                return Err((CMZError::CliProofFailed, self));
+            }
+        };
+    }
+
     for show_cred in proto_spec.show_creds.iter() {
         // The credential being shown
         let show_cred_id = format_ident!("show_cred_{}", show_cred.id);
@@ -1251,6 +1363,22 @@ fn protocol_macro(
         }
     };
 
+    // The issuer's zero-knowledge proof
+    let iss_proof = {
+        quote! {
+            sigma_compiler! { issuer_proof<Point>,
+                (#(#iss_proof_rand_scalars),*),
+                (#(#iss_proof_priv_scalars),*),
+                (), // pub_scalars
+                (), // cind_points
+                (#(#iss_proof_pub_points),*),
+                (#(#iss_proof_const_points),*),
+                #(#iss_proof_statements),*
+            }
+        }
+    };
+    println!("iss_proof = {iss_proof}");
+
     // The argument list for the client's prepare function.  There is an
     // immutable reference for each credential to be shown, and an owned
     // value for each credential to be issued.
@@ -1470,6 +1598,7 @@ fn protocol_macro(
             impl ClientState {
                 pub fn finalize(self, reply: Reply)
                     -> #rettype {
+                    let bp = cmz_basepoints::<Point>();
                     #(#cred_decls)*
                     #finalize_code
                     #retval
@@ -1500,6 +1629,7 @@ fn protocol_macro(
             #group_types
             #params_struct
             #messages
+            #iss_proof
             #client_side
             #issuer_side
         }

+ 19 - 8
src/lib.rs

@@ -12,6 +12,7 @@ use lazy_static::lazy_static;
 use rand_core::RngCore;
 pub use serde::{Deserialize, Deserializer, Serialize, Serializer};
 pub use serde_with::{serde_as, DeserializeAs, SerializeAs};
+pub use sigma_compiler::*;
 use thiserror::Error;
 
 // We need wrappers for group::Group and ff::PrimeField elements to be
@@ -110,21 +111,21 @@ const WNAF_SIZE: usize = 6;
 /// basepoint tables
 #[derive(Clone)]
 pub struct CMZBasepoints<G: Group> {
-    A: G,
-    B: G,
+    A_: G,
+    B_: G,
     A_TABLE: WnafBase<G, WNAF_SIZE>,
     B_TABLE: WnafBase<G, WNAF_SIZE>,
 }
 
 impl<G: Group> CMZBasepoints<G> {
     pub fn init(generator_A: G) -> Self {
-        let A = generator_A;
-        let B = G::generator();
-        let A_TABLE = WnafBase::new(A);
-        let B_TABLE = WnafBase::new(B);
+        let A_ = generator_A;
+        let B_ = G::generator();
+        let A_TABLE = WnafBase::new(A_);
+        let B_TABLE = WnafBase::new(B_);
         CMZBasepoints {
-            A,
-            B,
+            A_,
+            B_,
             A_TABLE,
             B_TABLE,
         }
@@ -149,6 +150,12 @@ impl<G: Group> CMZBasepoints<G> {
         let x = G::Scalar::random(&mut *rng);
         (x, self.mulB(&x))
     }
+    pub fn A(&self) -> G {
+        self.A_
+    }
+    pub fn B(&self) -> G {
+        self.B_
+    }
 }
 
 // What's going on here needs some explanation.  For each group G, we
@@ -432,6 +439,10 @@ pub enum CMZError {
     PubkeyMissing(&'static str),
     #[error("credential initialized with wrong protocol")]
     WrongProtocol(&'static str),
+    #[error("client proof did not verify")]
+    CliProofFailed,
+    #[error("issuer proof did not verify")]
+    IssProofFailed,
     #[error("unknown CMZ proof error")]
     Unknown,
 }