cmz_core/
lib.rs

1// We want to allow Point variables to be uppercase
2#![allow(non_snake_case)]
3
4//! The core functionality of the CMZ protocol macros
5//!
6//! You should not need to use this crate directly.  The [`cmz_core`]
7//! function is called by the `cmz_derive` crate to implement the
8//! `CMZProtocol` family of macros.
9
10use proc_macro2::TokenStream;
11use quote::{format_ident, quote, ToTokens};
12use std::collections::HashMap;
13use syn::parse::{Parse, ParseStream, Result};
14use syn::punctuated::Punctuated;
15use syn::visit_mut::{self, VisitMut};
16use syn::{braced, bracketed, parse_quote, token, Expr, Ident, Member, Token};
17
18// The possible attribute specifications for a credential to be shown
19#[derive(Copy, Clone, Debug, PartialEq)]
20enum ShowSpec {
21    Hide,
22    Reveal,
23    Implicit,
24}
25
26impl Parse for ShowSpec {
27    fn parse(input: ParseStream) -> Result<Self> {
28        let spec: Ident = input.parse()?;
29        match spec.to_string().to_uppercase().as_str() {
30            "H" | "HIDE" => Ok(Self::Hide),
31            "R" | "REVEAL" => Ok(Self::Reveal),
32            "I" | "IMPLICIT" => Ok(Self::Implicit),
33            _ => Err(input.error("Unknown attribute spec for shown credential")),
34        }
35    }
36}
37
38impl ShowSpec {
39    fn abbr(&self) -> &'static str {
40        match self {
41            Self::Hide => "H",
42            Self::Reveal => "R",
43            Self::Implicit => "I",
44        }
45    }
46}
47
48// The possible attribute specifications for a credential to be issued
49#[derive(Copy, Clone, Debug, PartialEq)]
50enum IssueSpec {
51    Hide,
52    Reveal,
53    Implicit,
54    Set,
55    Joint,
56}
57
58impl Parse for IssueSpec {
59    fn parse(input: ParseStream) -> Result<Self> {
60        let spec: Ident = input.parse()?;
61        match spec.to_string().to_uppercase().as_str() {
62            "H" | "HIDE" => Ok(Self::Hide),
63            "R" | "REVEAL" => Ok(Self::Reveal),
64            "I" | "IMPLICIT" => Ok(Self::Implicit),
65            "S" | "SET" => Ok(Self::Set),
66            "J" | "JOINT" => Ok(Self::Joint),
67            _ => Err(input.error("Unknown attribute spec for issued credential")),
68        }
69    }
70}
71
72impl IssueSpec {
73    fn abbr(&self) -> &'static str {
74        match self {
75            Self::Hide => "H",
76            Self::Reveal => "R",
77            Self::Implicit => "I",
78            Self::Set => "S",
79            Self::Joint => "J",
80        }
81    }
82}
83
84// An attribute specification like "attr1: Reveal"
85#[derive(Clone)]
86struct AttrSpec<ShowOrIssue: Parse> {
87    attr: Ident,
88    spec: ShowOrIssue,
89}
90
91impl<ShowOrIssue: Parse> Parse for AttrSpec<ShowOrIssue> {
92    fn parse(input: ParseStream) -> Result<Self> {
93        let attr: Ident = input.parse()?;
94        input.parse::<Token![:]>()?;
95        let spec: ShowOrIssue = input.parse()?;
96        Ok(Self { attr, spec })
97    }
98}
99
100// A specification of a credential, either to be shown or issued
101#[derive(Debug)]
102struct CredSpec<ShowOrIssue: Parse, const VALID_OPTIONAL: bool> {
103    id: Ident,
104    cred_type: Ident,
105    // For shown credentials only (not issued credentials): set to true
106    // if we want to only optionally (for example, in an "OR" clause)
107    // show that this credential is valid.  The default state of false
108    // means we should always show that the credential is valid.
109    valid_optional: bool,
110    attrs: HashMap<Ident, ShowOrIssue>,
111}
112
113impl<ShowOrIssue: Parse + Copy, const VALID_OPTIONAL: bool> Parse
114    for CredSpec<ShowOrIssue, VALID_OPTIONAL>
115{
116    fn parse(input: ParseStream) -> Result<Self> {
117        let id: Ident = input.parse()?;
118        let valid_optional = if VALID_OPTIONAL && input.peek(Token![?]) {
119            input.parse::<Token![?]>()?;
120            true
121        } else {
122            false
123        };
124        input.parse::<Token![:]>()?;
125        let cred_type: Ident = input.parse()?;
126        let content;
127        braced!(content in input);
128        let attrspecs: Punctuated<AttrSpec<ShowOrIssue>, Token![,]> =
129            content.parse_terminated(AttrSpec::<ShowOrIssue>::parse, Token![,])?;
130        let mut attrs: HashMap<Ident, ShowOrIssue> = HashMap::new();
131        for attrspec in attrspecs.iter() {
132            attrs.insert(attrspec.attr.clone(), attrspec.spec);
133        }
134        Ok(Self {
135            id,
136            cred_type,
137            valid_optional,
138            attrs,
139        })
140    }
141}
142
143// A vector of credential specifications, which could be empty, a single
144// credential specification, or a bracketed list of credential
145// specifications.  We need a newtype here and not just a Vec so that we
146// can implement the Parse trait for it.
147struct CredSpecVec<ShowOrIssue: Parse, const VALID_OPTIONAL: bool>(
148    Vec<CredSpec<ShowOrIssue, VALID_OPTIONAL>>,
149);
150
151impl<ShowOrIssue: Parse + Copy, const VALID_OPTIONAL: bool> Parse
152    for CredSpecVec<ShowOrIssue, VALID_OPTIONAL>
153{
154    fn parse(input: ParseStream) -> Result<Self> {
155        let specvec: Vec<CredSpec<ShowOrIssue, VALID_OPTIONAL>> = if input.peek(Token![,]) {
156            // The list is empty
157            Vec::new()
158        } else if input.peek(token::Bracket) {
159            let content;
160            bracketed!(content in input);
161            let specs: Punctuated<CredSpec<ShowOrIssue, VALID_OPTIONAL>, Token![,]> = content
162                .parse_terminated(CredSpec::<ShowOrIssue, VALID_OPTIONAL>::parse, Token![,])?;
163            specs.into_iter().collect()
164        } else {
165            let spec: CredSpec<ShowOrIssue, VALID_OPTIONAL> = input.parse()?;
166            vec![spec]
167        };
168
169        Ok(Self(specvec))
170    }
171}
172
173/// A parsed protocol specification, following the syntax described in
174/// the documentation for the `muCMZProtocol!` macro in the `cmz` crate.
175#[derive(Debug)]
176pub struct ProtoSpec {
177    proto_name: Ident,
178    params: Vec<Ident>,
179    point_params: Vec<Ident>,
180    show_creds: Vec<CredSpec<ShowSpec, true>>,
181    issue_creds: Vec<CredSpec<IssueSpec, false>>,
182    statements: Vec<Expr>,
183}
184
185impl Parse for ProtoSpec {
186    fn parse(input: ParseStream) -> Result<Self> {
187        let mut params: Vec<Ident> = Vec::new();
188        let mut point_params: Vec<Ident> = Vec::new();
189        let proto_name: Ident = input.parse()?;
190        // See if there are optional parameters; Rust does not provide a
191        // convenient angle-bracket parser like it does parens, square
192        // brackets, and braces, so we just roll our own.
193        if input.peek(Token![<]) {
194            input.parse::<Token![<]>()?;
195            loop {
196                if input.peek(Token![>]) {
197                    break;
198                }
199                if input.peek(Token![@]) {
200                    // Param identifiers starting with @ are Points
201                    // rather than Scalars.
202                    input.parse::<Token![@]>()?;
203                    let param: Ident = input.parse()?;
204                    point_params.push(param);
205                } else {
206                    let param: Ident = input.parse()?;
207                    params.push(param);
208                }
209                if input.peek(Token![>]) {
210                    break;
211                }
212                input.parse::<Token![,]>()?;
213            }
214            input.parse::<Token![>]>()?;
215        }
216        input.parse::<Token![,]>()?;
217        let showvec: CredSpecVec<ShowSpec, true> = input.parse()?;
218        input.parse::<Token![,]>()?;
219        let issuevec: CredSpecVec<IssueSpec, false> = input.parse()?;
220        input.parse::<Token![,]>()?;
221        let statementpunc: Punctuated<Expr, Token![,]> =
222            input.parse_terminated(Expr::parse, Token![,])?;
223        let statements: Vec<Expr> = statementpunc.into_iter().collect();
224
225        Ok(ProtoSpec {
226            proto_name,
227            params,
228            point_params,
229            show_creds: showvec.0,
230            issue_creds: issuevec.0,
231            statements,
232        })
233    }
234}
235
236// Names and types of fields that might end up in a generated struct
237enum StructField {
238    Scalar(Ident),
239    Point(Ident),
240    EncPoint(Ident),
241    Pubkey(Ident),
242    ByteVec(Ident),
243}
244
245// A list of StructField items
246#[derive(Default)]
247struct StructFieldList {
248    fields: Vec<StructField>,
249}
250
251impl StructFieldList {
252    pub fn push_scalar(&mut self, s: &Ident) {
253        self.fields.push(StructField::Scalar(s.clone()));
254    }
255    pub fn push_point(&mut self, s: &Ident) {
256        self.fields.push(StructField::Point(s.clone()));
257    }
258    pub fn push_encpoint(&mut self, s: &Ident) {
259        self.fields.push(StructField::EncPoint(s.clone()));
260    }
261    pub fn push_pubkey(&mut self, s: &Ident) {
262        self.fields.push(StructField::Pubkey(s.clone()));
263    }
264    pub fn push_bytevec(&mut self, s: &Ident) {
265        self.fields.push(StructField::ByteVec(s.clone()));
266    }
267    /// Output an iterator consisting of the field names
268    pub fn field_iter(&self) -> impl Iterator<Item = &Ident> {
269        self.fields.iter().map(|f| match f {
270            StructField::Scalar(id) => id,
271            StructField::Point(id) => id,
272            StructField::EncPoint(id) => id,
273            StructField::Pubkey(id) => id,
274            StructField::ByteVec(id) => id,
275        })
276    }
277    /// Output a ToTokens of the fields as they would appear in a struct
278    /// definition (including the serde_as annotations)
279    pub fn field_decls(&self) -> impl ToTokens {
280        let decls = self.fields.iter().map(|f| match f {
281            StructField::Scalar(id) => quote! {
282                #[serde_as(as = "SerdeScalar")]
283                pub #id: Scalar,
284            },
285            StructField::Point(id) => quote! {
286                #[serde_as(as = "SerdePoint")]
287                pub #id: Point,
288            },
289            StructField::EncPoint(id) => quote! {
290                #[serde_as(as = "(SerdePoint, SerdePoint)")]
291                pub #id: (Point, Point),
292            },
293            StructField::Pubkey(id) => quote! {
294                pub #id: CMZPubkey<Point>,
295            },
296            StructField::ByteVec(id) => quote! {
297                #[serde(with = "serde_bytes")]
298                pub #id: Vec<u8>,
299            },
300        });
301        quote! { #(#decls)* }
302    }
303}
304
305/// Produce the expansion of the `CMZProtocol` family of macros.
306///
307/// The six macros in the `CMZProtocol` macro family all call this
308/// function, with different values for the bools.
309pub fn cmz_core(
310    proto_spec: &ProtoSpec,
311    use_muCMZ: bool,
312    emit_client: bool,
313    emit_issuer: bool,
314) -> TokenStream {
315    let proto_name = &proto_spec.proto_name;
316    let has_params = !proto_spec.params.is_empty() || !proto_spec.point_params.is_empty();
317    let tot_num_creds = proto_spec.show_creds.len() + proto_spec.issue_creds.len();
318
319    // Use the group of the first named credential type
320    let group_types = if !proto_spec.show_creds.is_empty() {
321        let first_cred_type = &proto_spec.show_creds[0].cred_type;
322        quote! {
323            pub type Scalar = <#first_cred_type as CMZCredential>::Scalar;
324            pub type Point = <#first_cred_type as CMZCredential>::Point;
325        }
326    } else if !proto_spec.issue_creds.is_empty() {
327        let first_cred_type = &proto_spec.issue_creds[0].cred_type;
328        quote! {
329            pub type Scalar = <#first_cred_type as CMZCredential>::Scalar;
330            pub type Point = <#first_cred_type as CMZCredential>::Point;
331        }
332    } else {
333        quote! {}
334    };
335
336    // The structure of the client's ZKP
337    let mut cli_proof_rand_scalars = Vec::<Ident>::default();
338    let mut cli_proof_priv_scalars = Vec::<Ident>::default();
339    let mut cli_proof_pub_scalars = Vec::<Ident>::default();
340    let mut cli_proof_cind_points = Vec::<Ident>::default();
341    let mut cli_proof_pub_points = Vec::<Ident>::default();
342    let mut cli_proof_const_points = Vec::<Ident>::default();
343    let mut cli_proof_statements = Vec::<TokenStream>::default();
344    // A map from the credential name and attribute name (as Strings) to
345    // the scoped attribute identifier.  This map is used to translate
346    // expressions like `L.id` in the user-provided statements into the
347    // appropriate identifier.
348    let mut cli_proof_idmap = HashMap::<(String, String), Ident>::default();
349
350    // The structure of the issuer's ZKP
351    let mut iss_proof_rand_scalars = Vec::<Ident>::default();
352    let mut iss_proof_priv_scalars = Vec::<Ident>::default();
353    let mut iss_proof_pub_scalars = Vec::<Ident>::default();
354    // The issuer has no cind_points
355    let mut iss_proof_pub_points = Vec::<Ident>::default();
356    let mut iss_proof_const_points = Vec::<Ident>::default();
357    let mut iss_proof_statements = Vec::<TokenStream>::default();
358
359    /* Credential issuing
360
361       For each attribute of each credential to be issued, handle it
362       according to its IssueSpec:
363    */
364    // The fields that will end up in the ClientState
365    let mut clientstate_fields = StructFieldList::default();
366
367    // The fields that will end up in the Request
368    let mut request_fields = StructFieldList::default();
369
370    // The fields that will end up in the Reply
371    let mut reply_fields = StructFieldList::default();
372
373    // The code that will end up in prepare
374    let mut prepare_code = quote! {};
375
376    // The code that will end up in handle, before the call to
377    // fill_creds
378    let mut handle_code_pre_fill = quote! {};
379
380    // The code that will end up in handle, after the call to
381    // fill_creds but before the call to authorize
382    let mut handle_code_post_fill = quote! {};
383
384    // The code that will end up in handle, after the call to
385    // authorize
386    let mut handle_code_post_auth = quote! {};
387
388    // The code that will end up in finalize
389    let mut finalize_code = quote! {};
390
391    // Are there any Hide or Joint attributes in _any_ credential to be
392    // issued?
393    let mut any_hide_joint = false;
394
395    let A_ident = format_ident!("A_generator");
396    let B_ident = format_ident!("B_generator");
397    let d_ident = format_ident!("d_privkey");
398    let D_ident = format_ident!("D_pubkey");
399    let iss_proof_sessid_ident = format_ident!("iss_proof_sessid");
400
401    prepare_code = quote! {
402        #prepare_code
403        let #A_ident = bp.A();
404    };
405    handle_code_pre_fill = quote! {
406        #handle_code_pre_fill
407        let #A_ident = bp.A();
408    };
409    finalize_code = quote! {
410        #finalize_code
411        let #A_ident = bp.A();
412    };
413    iss_proof_const_points.push(A_ident.clone());
414
415    prepare_code = quote! {
416        #prepare_code
417        let #B_ident = bp.B();
418    };
419    handle_code_pre_fill = quote! {
420        #handle_code_pre_fill
421        let #B_ident = bp.B();
422    };
423    if !use_muCMZ || !proto_spec.issue_creds.is_empty() {
424        finalize_code = quote! {
425            #finalize_code
426            let #B_ident = bp.B();
427        };
428        iss_proof_const_points.push(B_ident.clone());
429    }
430
431    // Stash the issue proof session id in prepare so that it can be
432    // used in finalize
433    clientstate_fields.push_bytevec(&iss_proof_sessid_ident);
434
435    for iss_cred in proto_spec.issue_creds.iter() {
436        // Are there any Hide or Joint attributes in this particular
437        // credential to be issued?
438        let mut cred_hide_joint = false;
439
440        // The credential being issued
441        let iss_cred_id = format_ident!("iss_cred_{}", iss_cred.id);
442        // The public key for the credential
443        let pubkey_cred = format_ident!("pubkey_iss_cred_{}", iss_cred.id);
444        // The randomizing factor to generate P
445        let b_cred = format_ident!("b_iss_cred_{}", iss_cred.id);
446        // The (revealed part of) the MAC
447        let P_cred = format_ident!("P_iss_cred_{}", iss_cred.id);
448        let Q_cred = format_ident!("Q_iss_cred_{}", iss_cred.id);
449
450        // Only for CMZ14, not µCMZ:
451
452        // The encrypted form of the hidden part of the MAC
453        let EQ_cred = format_ident!("EQ_iss_cred_{}", iss_cred.id);
454        let EQ0_cred = format_ident!("EQ0_iss_cred_{}", iss_cred.id);
455        let EQ1_cred = format_ident!("EQ1_iss_cred_{}", iss_cred.id);
456        // The ZKP statements that prove the format of EQ_cred
457        let mut eq0_statement = quote! {};
458        let mut eq1_statement = quote! {};
459
460        // Only for µCMZ, not CMZ14:
461
462        // The Pedersen commitment to only the Hide and Joint attributes
463        let C_cred = format_ident!("C_iss_cred_{}", iss_cred.id);
464        // The completed Pedersen commitment to the attributes
465        // (including all kinds of attributes)
466        let K_cred = format_ident!("K_iss_cred_{}", iss_cred.id);
467        // The ZKP statement that proves the format of C
468        let mut C_statement = quote! {};
469
470        let iss_cred_type = &iss_cred.cred_type;
471
472        // String version of the credential name
473        let cred_str = iss_cred.id.to_string();
474
475        // Check that fill_creds filled in the private key for this
476        // credential and that it's for the right protocol (CMZ14 or
477        // µCMZ)
478        handle_code_post_fill = quote! {
479            #handle_code_post_fill
480            if #iss_cred_id.get_privkey().x.len() != #iss_cred_type::num_attrs() {
481                return Err(CMZError::PrivkeyMissing(#cred_str));
482            }
483            if #iss_cred_id.get_privkey().muCMZ != #use_muCMZ {
484                return Err(CMZError::WrongProtocol(#cred_str));
485            }
486        };
487
488        // Check that the credential passed to prepare has its public
489        // key set and that it's for the right protocol (CMZ14 or µCMZ)
490        prepare_code = quote! {
491            #prepare_code
492            if #iss_cred_id.get_pubkey().X.len() != #iss_cred_type::num_attrs() {
493                return Err(CMZError::PubkeyMissing(#cred_str));
494            }
495            if #iss_cred_id.get_pubkey().Xr.is_some() != #use_muCMZ {
496                return Err(CMZError::WrongProtocol(#cred_str));
497            }
498        };
499
500        // Stash the public key in prepare and use it to fill in the
501        // public key of the completed credential in finalize
502        clientstate_fields.push_pubkey(&pubkey_cred);
503        prepare_code = quote! {
504            #prepare_code
505            let #pubkey_cred = #iss_cred_id.get_pubkey().clone();
506        };
507        finalize_code = quote! {
508            #finalize_code
509            #iss_cred_id.set_pubkey(&self.#pubkey_cred);
510        };
511
512        for (attr, &spec) in iss_cred.attrs.iter() {
513            // String version of the attribute name
514            let attr_str = attr.to_string();
515
516            // The scoped attribute name
517            let scoped_attr = format_ident!("iss_{}attr_{}_{}", spec.abbr(), iss_cred.id, attr);
518
519            // Remember the mapping from the credential and attribute
520            // name to the scoped attribute
521            cli_proof_idmap.insert(
522                (iss_cred.id.to_string(), attr.to_string()),
523                scoped_attr.clone(),
524            );
525
526            // The private and public key for this attribute
527            let x_attr = format_ident!("x_{}", scoped_attr);
528            let X_attr = format_ident!("X_{}", scoped_attr);
529
530            if spec == IssueSpec::Hide || spec == IssueSpec::Joint {
531                cred_hide_joint = true;
532            }
533
534            if !use_muCMZ {
535                // For CMZ14, we prove that the encrypted MAC is
536                // consistent with all components of the credential's
537                // public key
538                handle_code_post_auth = quote! {
539                    #handle_code_post_auth
540                    let #x_attr = #iss_cred_id.privkey_x(#attr_str);
541                    let #X_attr = #iss_cred_id.pubkey_X(#attr_str);
542                };
543                finalize_code = quote! {
544                    #finalize_code
545                    let #X_attr = #iss_cred_id.pubkey_X(#attr_str);
546                };
547                iss_proof_priv_scalars.push(x_attr.clone());
548                iss_proof_pub_points.push(X_attr.clone());
549                iss_proof_statements.push(quote! {
550                    #X_attr = #x_attr * #A_ident,
551                });
552            }
553
554            if spec == IssueSpec::Hide {
555                /* For each Hide attribute, the attribute (passed in the
556                   prepare) goes in the ClientState, and from there to
557                   to the generated credential in finalize.
558                */
559                clientstate_fields.push_scalar(&scoped_attr);
560                prepare_code = quote! {
561                    #prepare_code
562                    let #scoped_attr =
563                    #iss_cred_id.#attr.ok_or(CMZError::HideAttrMissing(#cred_str,
564                    #attr_str))?;
565                };
566                finalize_code = quote! {
567                    #finalize_code
568                    #iss_cred_id.#attr = Some(self.#scoped_attr);
569                }
570            }
571
572            if spec == IssueSpec::Joint {
573                /* For each Joint attribute, the client's part of the
574                   attribute (randomly generated) goes in the
575                   ClientState, and the issuer's part of the attribute
576                   (randomly generated) goes in the Reply.
577                */
578                clientstate_fields.push_scalar(&scoped_attr);
579                reply_fields.push_scalar(&scoped_attr);
580                prepare_code = quote! {
581                    #prepare_code
582                    let #scoped_attr = <Scalar as ff::Field>::random(&mut *rng);
583                };
584                handle_code_pre_fill = quote! {
585                    #handle_code_pre_fill
586                    let #scoped_attr = <Scalar as ff::Field>::random(&mut *rng);
587                };
588                finalize_code = quote! {
589                    #finalize_code
590                    let #scoped_attr = reply.#scoped_attr;
591                    #iss_cred_id.#attr = Some(self.#scoped_attr + reply.#scoped_attr);
592                };
593            }
594
595            if !use_muCMZ && (spec == IssueSpec::Hide || spec == IssueSpec::Joint) {
596                /* For each Hide and Joint attribute (for CMZ14): Compute an
597                   exponential El Gamal encryption (of the attribute) E_attr =
598                   (r_attr*B, attr*B + r_attr*D) for random r_attr.  Include E_attr
599                   in the Request, attr in the ClientState, and attr,
600                   r_attr, and E_attr in the CliProof.  Add
601                   b*x_attr*E_attr to E_Q in handle, for the b chosen on
602                   a per-issued-credential basis below.  Include x_attr,
603                   X_attr, and t_attr = b*x_attr in IssProof and T_attr
604                   = b*X_attr = t_attr*A in Reply and IssProof.
605                */
606                let enc_attr = format_ident!("E_{}", scoped_attr);
607                let enc0_attr = format_ident!("E0_{}", scoped_attr);
608                let enc1_attr = format_ident!("E1_{}", scoped_attr);
609                let r_attr = format_ident!("r_{}", scoped_attr);
610                let t_attr = format_ident!("t_{}", scoped_attr);
611                let T_attr = format_ident!("T_{}", scoped_attr);
612                request_fields.push_encpoint(&enc_attr);
613                clientstate_fields.push_encpoint(&enc_attr);
614                reply_fields.push_point(&T_attr);
615                iss_proof_priv_scalars.push(t_attr.clone());
616                iss_proof_pub_points.push(T_attr.clone());
617                iss_proof_pub_points.push(enc0_attr.clone());
618                iss_proof_pub_points.push(enc1_attr.clone());
619                iss_proof_statements.push(quote! {
620                    #T_attr = #t_attr * #A_ident,
621                    #T_attr = #b_cred * #X_attr,
622                });
623                eq0_statement = quote! {
624                    #eq0_statement + #t_attr * #enc0_attr
625                };
626                eq1_statement = quote! {
627                    #eq1_statement + #t_attr * #enc1_attr
628                };
629                cli_proof_priv_scalars.push(scoped_attr.clone());
630                cli_proof_rand_scalars.push(r_attr.clone());
631                cli_proof_pub_points.push(enc0_attr.clone());
632                cli_proof_pub_points.push(enc1_attr.clone());
633                cli_proof_statements.push(quote! {
634                    #enc0_attr = #r_attr * #B_ident,
635                    #enc1_attr = #scoped_attr * #B_ident + #r_attr * #D_ident,
636                });
637                prepare_code = quote! {
638                    #prepare_code
639                    let #r_attr = <Scalar as ff::Field>::random(&mut *rng);
640                    let #enc0_attr = bp.mulB(&#r_attr);
641                    let #enc1_attr = bp.mulB(&#scoped_attr) +
642                        #r_attr * #D_ident;
643                    let #enc_attr = (#enc0_attr, #enc1_attr);
644                };
645                handle_code_post_fill = quote! {
646                    #handle_code_post_fill
647
648                    let #enc0_attr = request.#enc_attr.0;
649                    let #enc1_attr = request.#enc_attr.1;
650                };
651                handle_code_post_auth = quote! {
652                    #handle_code_post_auth
653
654                    let #t_attr = #b_cred * #x_attr;
655                    #EQ_cred.0 += #t_attr * #enc0_attr;
656                    #EQ_cred.1 += #t_attr * #enc1_attr;
657                    let #T_attr = bp.mulA(&#t_attr);
658                };
659                finalize_code = quote! {
660                    #finalize_code
661                    let #T_attr = reply.#T_attr;
662                    let #enc0_attr = self.#enc_attr.0;
663                    let #enc1_attr = self.#enc_attr.1;
664                };
665            }
666
667            if use_muCMZ && (spec == IssueSpec::Hide || spec == IssueSpec::Joint) {
668                /* For each Hide and Joint attribute (for µCMZ): add
669                   attr*X_attr to C.
670                */
671                prepare_code = quote! {
672                    #prepare_code
673                    let #X_attr = #pubkey_cred.X[#iss_cred_type::attr_num(#attr_str)];
674                    #C_cred += #scoped_attr * #X_attr;
675                };
676                handle_code_post_fill = quote! {
677                    #handle_code_post_fill
678                    let #X_attr = #iss_cred_id.pubkey_X(#attr_str);
679                };
680                C_statement = quote! {
681                    #C_statement + #scoped_attr * #X_attr
682                };
683                cli_proof_priv_scalars.push(scoped_attr.clone());
684                cli_proof_cind_points.push(X_attr.clone());
685            }
686
687            /* For each Reveal attribute: include attr in Request (client will
688               pass the value into prepare).  Also store it in the
689               ClientState.
690            */
691            if spec == IssueSpec::Reveal {
692                request_fields.push_scalar(&scoped_attr);
693                clientstate_fields.push_scalar(&scoped_attr);
694                cli_proof_pub_scalars.push(scoped_attr.clone());
695                prepare_code = quote! {
696                    #prepare_code
697                    let #scoped_attr =
698                    #iss_cred_id.#attr.ok_or(CMZError::RevealAttrMissing(#cred_str,
699                    #attr_str))?;
700                };
701                handle_code_pre_fill = quote! {
702                    #handle_code_pre_fill
703                    let #scoped_attr = request.#scoped_attr;
704                    #iss_cred_id.#attr = Some(#scoped_attr);
705                };
706                finalize_code = quote! {
707                    #finalize_code
708                    #iss_cred_id.#attr = Some(self.#scoped_attr);
709                };
710            }
711
712            /* For each Implicit attribute: store it in ClientState
713               (will be passed into prepare) on the client side, and
714               will be filled in by fill_creds on the issuer side.
715            */
716            if spec == IssueSpec::Implicit {
717                clientstate_fields.push_scalar(&scoped_attr);
718                cli_proof_pub_scalars.push(scoped_attr.clone());
719                prepare_code = quote! {
720                    #prepare_code
721                    let #scoped_attr =
722                    #iss_cred_id.#attr.ok_or(CMZError::ImplicitAttrCliMissing(#cred_str,
723                    #attr_str))?;
724                };
725                handle_code_post_fill = quote! {
726                    #handle_code_post_fill
727                    let #scoped_attr =
728                    #iss_cred_id.#attr.ok_or(CMZError::ImplicitAttrIssMissing(#cred_str,
729                    #attr_str))?;
730                };
731                finalize_code = quote! {
732                    #finalize_code
733                    #iss_cred_id.#attr = Some(self.#scoped_attr);
734                };
735            }
736
737            /* For each Set attribute: the issuer's value will be set
738              by fill_creds.  Include the value in Reply.
739            */
740            if spec == IssueSpec::Set {
741                reply_fields.push_scalar(&scoped_attr);
742                handle_code_post_fill = quote! {
743                    #handle_code_post_fill
744                    let #scoped_attr =
745                    #iss_cred_id.#attr.ok_or(CMZError::SetAttrMissing(#cred_str,
746                    #attr_str))?;
747                };
748                finalize_code = quote! {
749                    #finalize_code
750                    #iss_cred_id.#attr = Some(reply.#scoped_attr);
751                }
752            }
753
754            if spec == IssueSpec::Reveal
755                || spec == IssueSpec::Implicit
756                || spec == IssueSpec::Set
757                || spec == IssueSpec::Joint
758            {
759                if use_muCMZ {
760                    /* For each Reveal, Implicit, Set, or Joint attribute, add
761                       attr*X_attr to K in handle and finalize.
762                    */
763                    handle_code_post_fill = quote! {
764                        #handle_code_post_fill
765                        #K_cred += (#scoped_attr *
766                            #iss_cred_id.pubkey_X(#attr_str));
767                    };
768                    // For a Joint attribute, we only want to use the
769                    // issuer's contribution (which is in #scoped_attr),
770                    // not #iss_cred_id.#attr, which is the sum of the
771                    // client's and issuer's contributions
772                    let use_attr = if spec == IssueSpec::Joint {
773                        quote! { #scoped_attr }
774                    } else {
775                        quote! { #iss_cred_id.#attr.unwrap() }
776                    };
777                    finalize_code = quote! {
778                        #finalize_code
779                        #K_cred += (#use_attr *
780                            #iss_cred_id.pubkey_X(#attr_str));
781                    };
782                } else {
783                    /* For each Reveal, Implicit, Set, or Joint attribute, add
784                       attr*x_attr*P to Q in handle.
785                    */
786                    handle_code_post_auth = quote! {
787                        #handle_code_post_auth
788                        #Q_cred += (#scoped_attr * #x_attr) * #P_cred;
789                    };
790                    // For Joint attributes, we only want to use the
791                    // issuer's contribution.  We already set
792                    // #scoped_attr to the issuer's contribution earlier
793                    // on.
794                    if spec != IssueSpec::Joint {
795                        finalize_code = quote! {
796                            #finalize_code
797                            let #scoped_attr = #iss_cred_id.#attr.unwrap();
798                        };
799                    }
800                    eq1_statement = quote! {
801                        #eq1_statement + #x_attr * ( #scoped_attr * #P_cred )
802                    };
803                    iss_proof_pub_scalars.push(scoped_attr.clone());
804                }
805            }
806        }
807
808        if !use_muCMZ {
809            /* For all Hide and Joint attributes of a single credential to be
810               issued (for CMZ14): the issuer chooses random b and s, computes
811               P = b*B, E_Q = (s*B,s*D+b*x_0*B) + \sum_{hide,joint}
812               b*x_attr*E_attr + (0,\sum_{implicit,reveal,set,joint}
813               b*x_attr*attr*B) (note that E_Q and each E_attr are all
814               pairs of Points; the scalar multiplication is
815               componentwise).  Include P, E_Q in Reply. The client will
816               compute Q = E_Q[1] - d*E_Q[0].
817            */
818            let s_cred = format_ident!("s_iss_cred_{}", iss_cred.id);
819            let x0_cred = format_ident!("x0_iss_cred_{}", iss_cred.id);
820            let xr_cred = format_ident!("xr_cred{}", iss_cred.id);
821            let X0_cred = format_ident!("X0_iss_cred_{}", iss_cred.id);
822            reply_fields.push_point(&P_cred);
823            if cred_hide_joint {
824                reply_fields.push_encpoint(&EQ_cred);
825            } else {
826                reply_fields.push_point(&Q_cred);
827            }
828            let EQ_cred_code_pre = if cred_hide_joint {
829                quote! {
830                    let #s_cred = <Scalar as ff::Field>::random(&mut *rng);
831                    let mut #EQ_cred = (bp.mulB(&#s_cred), #s_cred * #D_ident);
832                }
833            } else {
834                quote! {}
835            };
836            let EQ_cred_code_post = if cred_hide_joint {
837                quote! {
838                    #EQ_cred.1 += #Q_cred;
839                    let #EQ0_cred = #EQ_cred.0;
840                    let #EQ1_cred = #EQ_cred.1;
841                }
842            } else {
843                quote! {}
844            };
845            if cred_hide_joint {
846                iss_proof_pub_points.push(EQ0_cred.clone());
847                iss_proof_pub_points.push(EQ1_cred.clone());
848                iss_proof_statements.push(quote! {
849                    #EQ0_cred = #s_cred * #B_ident #eq0_statement,
850                    #EQ1_cred = #s_cred * #D_ident + #x0_cred * #P_cred #eq1_statement,
851                });
852            }
853            handle_code_post_auth = quote! {
854                let #b_cred = <Scalar as ff::Field>::random(&mut *rng);
855                let #P_cred = bp.mulB(&#b_cred);
856                #iss_cred_id.MAC.P = #P_cred;
857                let #x0_cred = #iss_cred_id.get_privkey().x0;
858                let #xr_cred = #iss_cred_id.get_privkey().xr;
859                let #X0_cred = #iss_cred_id.get_pubkey().X0.unwrap();
860                let mut #Q_cred = bp.mulB(&(#b_cred * #iss_cred_id.get_privkey().x0));
861                #EQ_cred_code_pre
862
863                #handle_code_post_auth
864
865                #EQ_cred_code_post
866            };
867            let finalize_Q_code = if cred_hide_joint {
868                quote! {
869                    let #EQ0_cred = reply.#EQ_cred.0;
870                    let #EQ1_cred = reply.#EQ_cred.1;
871                    #iss_cred_id.MAC.Q = #EQ1_cred - self.#d_ident * #EQ0_cred;
872                }
873            } else {
874                quote! {
875                    #iss_cred_id.MAC.Q = reply.#Q_cred;
876                }
877            };
878            finalize_code = quote! {
879                #finalize_code
880                let #P_cred = reply.#P_cred;
881                let #X0_cred = #iss_cred_id.get_pubkey().X0.unwrap();
882                #iss_cred_id.MAC.P = #P_cred;
883                #finalize_Q_code
884            };
885            if cred_hide_joint {
886                iss_proof_rand_scalars.push(s_cred.clone());
887            }
888            iss_proof_pub_points.push(P_cred.clone());
889            iss_proof_priv_scalars.push(x0_cred.clone());
890            iss_proof_rand_scalars.push(xr_cred.clone());
891            iss_proof_pub_points.push(X0_cred.clone());
892            iss_proof_rand_scalars.push(b_cred.clone());
893            iss_proof_statements.push(quote! {
894                #X0_cred = #x0_cred * #B_ident + #xr_cred * #A_ident,
895            });
896        }
897
898        if use_muCMZ {
899            /* For all Hide and Joint attributes of a single credential to be
900               issued (for µCMZ): The client chooses a random s, computes C =
901               (\sum_{hide,joint} attr*X_attr) + s*A, where X_attr is the
902               public key for that attribute.  Include s and C in the
903               ClientState, C in the Request, and the attributes, s, and
904               C in the CliProof.  Hide attributes will be passed into
905               prepare on the client side; Joint attributes (client
906               contribution) will be generated randomly by prepare on
907               the client side.  On the issuer side, handle will pick a
908               random b, compute P = b*A, K = C + X_r +
909               \sum_{implicit,reveal,set,joint} attr*X_attr, R =
910               b*(x_0*A + K).  Include P and R in Reply, and x_0, b, P,
911               R, K in IssProof.  For each implicit,reveal,set,joint
912               attribute, include x_attr and P_attr = attr*P in
913               IssProof.  The client will compute K as above, and Q = R
914               - s*P.
915            */
916            let R_cred = format_ident!("R_iss_cred_{}", iss_cred.id);
917            let s_cred = format_ident!("s_iss_cred_{}", iss_cred.id);
918            let x0_cred = format_ident!("x0_iss_cred_{}", iss_cred.id);
919            let X0_cred = format_ident!("X0_iss_cred_{}", iss_cred.id);
920            reply_fields.push_point(&P_cred);
921            reply_fields.push_point(&R_cred);
922            if cred_hide_joint {
923                clientstate_fields.push_scalar(&s_cred);
924                clientstate_fields.push_point(&C_cred);
925                request_fields.push_point(&C_cred);
926                cli_proof_pub_points.push(C_cred.clone());
927                cli_proof_rand_scalars.push(s_cred.clone());
928                prepare_code = quote! {
929                    let #s_cred = <Scalar as ff::Field>::random(&mut *rng);
930                    let mut #C_cred = bp.mulA(&#s_cred);
931                    #prepare_code
932                };
933                handle_code_post_fill = quote! {
934                    let #C_cred = request.#C_cred;
935                    let mut #K_cred = #C_cred + #iss_cred_id.get_pubkey().Xr.unwrap();
936                    #handle_code_post_fill
937                };
938                finalize_code = quote! {
939                    let mut #K_cred = self.#C_cred + self.#pubkey_cred.Xr.unwrap();
940                    #finalize_code
941                };
942                // Construct the client proof for this credential
943                cli_proof_statements.push(quote! {
944                    #C_cred = #s_cred * #A_ident #C_statement,
945                });
946            } else {
947                handle_code_post_fill = quote! {
948                    let mut #K_cred = #iss_cred_id.get_pubkey().Xr.unwrap();
949                    #handle_code_post_fill
950                };
951                finalize_code = quote! {
952                    let mut #K_cred = self.#pubkey_cred.Xr.unwrap();
953                    #finalize_code
954                };
955            }
956            handle_code_post_auth = quote! {
957                #handle_code_post_auth
958                let #b_cred = <Scalar as ff::Field>::random(&mut *rng);
959                let #P_cred = bp.mulA(&#b_cred);
960                #iss_cred_id.MAC.P = #P_cred;
961                let #x0_cred = #iss_cred_id.get_privkey().x0;
962                let #X0_cred = #iss_cred_id.get_pubkey().X0.unwrap();
963                let #R_cred = #b_cred * (bp.mulA(&#x0_cred) + #K_cred);
964            };
965            let finalize_Q_code = if cred_hide_joint {
966                quote! {
967                    #iss_cred_id.MAC.Q = reply.#R_cred - self.#s_cred * reply.#P_cred;
968                }
969            } else {
970                quote! {
971                    #iss_cred_id.MAC.Q = reply.#R_cred;
972                }
973            };
974            finalize_code = quote! {
975                #finalize_code
976                let #P_cred = reply.#P_cred;
977                let #X0_cred = #iss_cred_id.get_pubkey().X0.unwrap();
978                let #R_cred = reply.#R_cred;
979                #iss_cred_id.MAC.P = #P_cred;
980                #finalize_Q_code
981            };
982            // Construct the issuer proof for this credential
983            iss_proof_priv_scalars.push(x0_cred.clone());
984            iss_proof_rand_scalars.push(b_cred.clone());
985            iss_proof_pub_points.push(P_cred.clone());
986            iss_proof_pub_points.push(X0_cred.clone());
987            iss_proof_pub_points.push(K_cred.clone());
988            iss_proof_pub_points.push(R_cred.clone());
989            iss_proof_statements.push(quote! {
990                #P_cred = #b_cred * #A_ident,
991                #X0_cred = #x0_cred * #B_ident,
992                #R_cred = #x0_cred * #P_cred + #b_cred * #K_cred,
993            });
994        }
995
996        any_hide_joint |= cred_hide_joint;
997    }
998
999    /* If there are _any_ Hide or Joint attributes in CMZ14 (as opposed
1000       to µCMZ), the client generates an El Gamal keypair (d, D=d*B).
1001       Include d in the ClientState and D in the Request.
1002    */
1003    if any_hide_joint && !use_muCMZ {
1004        clientstate_fields.push_scalar(&d_ident);
1005        clientstate_fields.push_point(&D_ident);
1006        cli_proof_rand_scalars.push(d_ident.clone());
1007        cli_proof_pub_points.push(D_ident.clone());
1008        request_fields.push_point(&D_ident);
1009        cli_proof_statements.push(quote! {
1010            #D_ident = #d_ident * #B_ident,
1011        });
1012        prepare_code = quote! {
1013            let (#d_ident,#D_ident) = bp.keypairB(&mut *rng);
1014            #prepare_code
1015        };
1016        handle_code_post_fill = quote! {
1017            let #D_ident = request.#D_ident;
1018            #handle_code_post_fill
1019        };
1020        finalize_code = quote! {
1021            #finalize_code
1022            let #D_ident = self.#D_ident;
1023        };
1024        iss_proof_pub_points.push(D_ident.clone());
1025    }
1026
1027    if !proto_spec.issue_creds.is_empty() {
1028        // The issuer will create a zero-knowledge proof
1029        let iss_proof_ident = format_ident!("iss_proof");
1030        reply_fields.push_bytevec(&iss_proof_ident);
1031        let iss_instance_fields = iss_proof_pub_points
1032            .iter()
1033            .chain(iss_proof_const_points.iter())
1034            .chain(iss_proof_pub_scalars.iter());
1035        let iss_witness_fields = iss_proof_rand_scalars
1036            .iter()
1037            .chain(iss_proof_priv_scalars.iter());
1038        handle_code_post_auth = quote! {
1039            #handle_code_post_auth
1040            let iss_proof_instance = issuer_proof::Instance {
1041                #(#iss_instance_fields,)*
1042            };
1043            let iss_proof_witness = issuer_proof::Witness {
1044                #(#iss_witness_fields,)*
1045            };
1046            // If prove returns Err here, there's an actual bug.
1047            let #iss_proof_ident =
1048            issuer_proof::prove(&iss_proof_instance,
1049                &iss_proof_witness, &iss_proof_sessid, rng).unwrap();
1050        };
1051        let cli_iss_instance_fields = iss_proof_pub_points
1052            .iter()
1053            .chain(iss_proof_const_points.iter())
1054            .chain(iss_proof_pub_scalars.iter());
1055        finalize_code = quote! {
1056            #finalize_code
1057            let iss_proof_instance = issuer_proof::Instance {
1058                #(#cli_iss_instance_fields,)*
1059            };
1060            if issuer_proof::verify(&iss_proof_instance,
1061                &reply.#iss_proof_ident, &self.iss_proof_sessid).is_err() {
1062                return Err((CMZError::IssProofFailed, self));
1063            }
1064        };
1065    }
1066
1067    // Validity proofs for shown credentials with valid_optional go here
1068    let mut validity_proofs: HashMap<String, TokenStream> = HashMap::new();
1069
1070    for show_cred in proto_spec.show_creds.iter() {
1071        // The credential being shown
1072        let show_cred_id = format_ident!("show_cred_{}", show_cred.id);
1073        // The rerandomizing factor for the MAC
1074        let t_cred = format_ident!("t_show_cred_{}", show_cred.id);
1075        // The rerandomized MAC
1076        let P_cred = format_ident!("P_show_cred_{}", show_cred.id);
1077        let Q_cred = format_ident!("Q_show_cred_{}", show_cred.id);
1078        // The randomness for the Pedersen commitment to Q
1079        let zQ_cred = format_ident!("zQ_show_cred_{}", show_cred.id);
1080        // The Pedersen commitment to Q
1081        let CQ_cred = format_ident!("CQ_show_cred_{}", show_cred.id);
1082        // The verification point
1083        let V_cred = format_ident!("V_show_cred_{}", show_cred.id);
1084        // The coefficient (on P) of the MAC on the Reveal and Implicit
1085        // attributes, computed by the issuer
1086        let q_cred = format_ident!("q_show_cred_{}", show_cred.id);
1087
1088        let show_cred_type = &show_cred.cred_type;
1089
1090        // String version of the credential name
1091        let cred_str = show_cred.id.to_string();
1092
1093        // Check that fill_creds filled in the private key for this
1094        // credential and that it's for the right protocol (CMZ14 or
1095        // µCMZ)
1096        handle_code_post_fill = quote! {
1097            #handle_code_post_fill
1098            if #show_cred_id.get_privkey().x.len() != #show_cred_type::num_attrs() {
1099                return Err(CMZError::PrivkeyMissing(#cred_str));
1100            }
1101            if #show_cred_id.get_privkey().muCMZ != #use_muCMZ {
1102                return Err(CMZError::WrongProtocol(#cred_str));
1103            }
1104        };
1105
1106        // Check that the credential passed to prepare has its public
1107        // key set and that it's for the right protocol (CMZ14 or µCMZ)
1108        prepare_code = quote! {
1109            #prepare_code
1110            if #show_cred_id.get_pubkey().X.len() != #show_cred_type::num_attrs() {
1111                return Err(CMZError::PubkeyMissing(#cred_str));
1112            }
1113            if #show_cred_id.get_pubkey().Xr.is_some() != #use_muCMZ {
1114                return Err(CMZError::WrongProtocol(#cred_str));
1115            }
1116        };
1117
1118        // Rerandomize the MAC and construct a Pedersen commitment to Q
1119        // Also start constructing the client's version of the
1120        // verification point V (which will be updated with each Hide
1121        // attribute below)
1122        prepare_code = quote! {
1123            #prepare_code
1124            let #t_cred = <Scalar as ff::Field>::random(&mut *rng);
1125            let #P_cred = #t_cred * #show_cred_id.MAC.P;
1126            let #Q_cred = #t_cred * #show_cred_id.MAC.Q;
1127            let #zQ_cred = <Scalar as ff::Field>::random(&mut *rng);
1128            let #CQ_cred = #Q_cred - bp.mulB(&#zQ_cred);
1129            let mut #V_cred = bp.mulB(&#zQ_cred);
1130        };
1131        handle_code_post_fill = quote! {
1132            #handle_code_post_fill
1133            let #P_cred = request.#P_cred;
1134            if bool::from(#P_cred.is_identity()) {
1135                return Err(CMZError::CliProofFailed);
1136            }
1137        };
1138        request_fields.push_point(&P_cred);
1139        request_fields.push_point(&CQ_cred);
1140        cli_proof_rand_scalars.push(zQ_cred.clone());
1141        cli_proof_cind_points.push(P_cred.clone());
1142        cli_proof_pub_points.push(V_cred.clone());
1143
1144        // The ZKP statement that proves the format of V
1145        let mut V_statement = quote! {
1146            #V_cred = #zQ_cred * #B_ident
1147        };
1148
1149        // Start constructing the issuer's version of the verification
1150        // point Vi (which will be updated with each Hide attribute below)
1151        // and the MAC on the Reveal and Implicit attributes
1152
1153        // µCMZ has the extra xr to add in here
1154        let q_init = if use_muCMZ {
1155            quote! { #show_cred_id.get_privkey().x0 + #show_cred_id.get_privkey().xr }
1156        } else {
1157            quote! { #show_cred_id.get_privkey().x0 }
1158        };
1159        handle_code_post_fill = quote! {
1160            #handle_code_post_fill
1161            let mut #V_cred = -request.#CQ_cred;
1162            let mut #q_cred = #q_init;
1163        };
1164
1165        for (attr, &spec) in show_cred.attrs.iter() {
1166            // String version of the attribute name
1167            let attr_str = attr.to_string();
1168
1169            // The scoped attribute name
1170            let scoped_attr = format_ident!("show_{}attr_{}_{}", spec.abbr(), show_cred.id, attr);
1171
1172            // The public key for this attribute
1173            let X_attr = format_ident!("X_{}", scoped_attr);
1174
1175            // Remember the mapping from the credential and attribute
1176            // name to the scoped attribute
1177            cli_proof_idmap.insert(
1178                (show_cred.id.to_string(), attr.to_string()),
1179                scoped_attr.clone(),
1180            );
1181
1182            if spec == ShowSpec::Hide {
1183                prepare_code = quote! {
1184                    #prepare_code
1185                    let #scoped_attr =
1186                    #show_cred_id.#attr.ok_or(CMZError::HideAttrMissing(#cred_str,
1187                    #attr_str))?;
1188                };
1189                // Construct a Pedersen commitment to the Hide attribute
1190                // and update the verification point
1191                let z_attr = format_ident!("z_{}", scoped_attr);
1192                let C_attr = format_ident!("C_{}", scoped_attr);
1193                request_fields.push_point(&C_attr);
1194                prepare_code = quote! {
1195                    #prepare_code
1196                    let #z_attr = <Scalar as ff::Field>::random(&mut *rng);
1197                    let #C_attr = #scoped_attr * #P_cred + bp.mulA(&#z_attr);
1198                    let #X_attr = #show_cred_id.pubkey_X(#attr_str);
1199                    #V_cred += #z_attr * #X_attr;
1200                };
1201                handle_code_post_fill = quote! {
1202                    #handle_code_post_fill
1203                    let #C_attr = request.#C_attr;
1204                    let #X_attr = #show_cred_id.pubkey_X(#attr_str);
1205                    #V_cred += #show_cred_id.privkey_x(#attr_str)
1206                        * #C_attr;
1207                };
1208                cli_proof_priv_scalars.push(scoped_attr.clone());
1209                cli_proof_rand_scalars.push(z_attr.clone());
1210                cli_proof_pub_points.push(C_attr.clone());
1211                cli_proof_cind_points.push(X_attr.clone());
1212                cli_proof_statements.push(quote! {
1213                    #C_attr = #scoped_attr * #P_cred + #z_attr * #A_ident,
1214                });
1215                V_statement = quote! {
1216                    #V_statement + #z_attr * #X_attr
1217                };
1218            }
1219
1220            if spec == ShowSpec::Reveal {
1221                request_fields.push_scalar(&scoped_attr);
1222                prepare_code = quote! {
1223                    #prepare_code
1224                    let #scoped_attr =
1225                    #show_cred_id.#attr.ok_or(CMZError::RevealAttrMissing(#cred_str,
1226                    #attr_str))?;
1227                };
1228                handle_code_pre_fill = quote! {
1229                    #handle_code_pre_fill
1230                    let #scoped_attr = request.#scoped_attr;
1231                    #show_cred_id.#attr = Some(#scoped_attr);
1232                };
1233                // Accumulate the coefficient (of P) on the component of
1234                // Q due to this attribute
1235                handle_code_post_fill = quote! {
1236                    #handle_code_post_fill
1237                    #q_cred += #scoped_attr *
1238                        #show_cred_id.privkey_x(#attr_str);
1239                };
1240                cli_proof_pub_scalars.push(scoped_attr.clone());
1241            }
1242
1243            if spec == ShowSpec::Implicit {
1244                prepare_code = quote! {
1245                    #prepare_code
1246                    let #scoped_attr =
1247                    #show_cred_id.#attr.ok_or(CMZError::ImplicitAttrCliMissing(#cred_str,
1248                    #attr_str))?;
1249                };
1250                handle_code_post_fill = quote! {
1251                    #handle_code_post_fill
1252                    let #scoped_attr =
1253                    #show_cred_id.#attr.ok_or(CMZError::ImplicitAttrIssMissing(#cred_str,
1254                    #attr_str))?;
1255                };
1256                // Accumulate the coefficient (of P) on the component of
1257                // Q due to this attribute
1258                handle_code_post_fill = quote! {
1259                    #handle_code_post_fill
1260                    #q_cred += #scoped_attr *
1261                        #show_cred_id.privkey_x(#attr_str);
1262                };
1263                cli_proof_pub_scalars.push(scoped_attr.clone());
1264            }
1265        }
1266        // Compute the computation of the issuer's version of the
1267        // Verification point Vi
1268        handle_code_post_fill = quote! {
1269            #handle_code_post_fill
1270            #V_cred += #q_cred * #P_cred;
1271        };
1272
1273        if show_cred.valid_optional {
1274            validity_proofs.insert(
1275                cred_str,
1276                quote! {
1277                    #V_statement
1278                },
1279            );
1280        } else {
1281            cli_proof_statements.push(quote! {
1282                #V_statement,
1283            });
1284        }
1285    }
1286    cli_proof_const_points.push(A_ident.clone());
1287    cli_proof_const_points.push(B_ident.clone());
1288
1289    for paramid in proto_spec.params.iter() {
1290        let scoped_param = format_ident!("param_{}", paramid);
1291        prepare_code = quote! {
1292            #prepare_code
1293            let #scoped_param = params.#paramid;
1294        };
1295        handle_code_post_fill = quote! {
1296            #handle_code_post_fill
1297            let #scoped_param = params.#paramid;
1298        };
1299        cli_proof_pub_scalars.push(scoped_param.clone());
1300        cli_proof_idmap.insert(("".to_string(), paramid.to_string()), scoped_param.clone());
1301    }
1302
1303    for paramid in proto_spec.point_params.iter() {
1304        let scoped_param = format_ident!("param_{}", paramid);
1305        prepare_code = quote! {
1306            #prepare_code
1307            let #scoped_param = params.#paramid;
1308        };
1309        handle_code_post_fill = quote! {
1310            #handle_code_post_fill
1311            let #scoped_param = params.#paramid;
1312        };
1313        cli_proof_pub_points.push(scoped_param.clone());
1314        cli_proof_idmap.insert(("".to_string(), paramid.to_string()), scoped_param.clone());
1315    }
1316
1317    // The client will create a zero-knowledge proof
1318    let cli_proof_ident = format_ident!("cli_proof");
1319    request_fields.push_bytevec(&cli_proof_ident);
1320    let cli_instance_fields = cli_proof_pub_points
1321        .iter()
1322        .chain(cli_proof_const_points.iter())
1323        .chain(cli_proof_cind_points.iter())
1324        .chain(cli_proof_pub_scalars.iter());
1325    let cli_witness_fields = cli_proof_rand_scalars
1326        .iter()
1327        .chain(cli_proof_priv_scalars.iter());
1328    prepare_code = quote! {
1329        #prepare_code
1330        let cli_proof_instance = client_proof::Instance {
1331            #(#cli_instance_fields,)*
1332        };
1333        let cli_proof_witness = client_proof::Witness {
1334            #(#cli_witness_fields,)*
1335        };
1336        // If prove returns Err here, there's an actual bug.
1337        let #cli_proof_ident = client_proof::prove(&cli_proof_instance,
1338            &cli_proof_witness, &cli_proof_sessid, rng).unwrap();
1339    };
1340    let iss_cli_instance_fields = cli_proof_pub_points
1341        .iter()
1342        .chain(cli_proof_const_points.iter())
1343        .chain(cli_proof_cind_points.iter())
1344        .chain(cli_proof_pub_scalars.iter());
1345    handle_code_post_fill = quote! {
1346        #handle_code_post_fill
1347        let cli_proof_instance = client_proof::Instance {
1348            #(#iss_cli_instance_fields,)*
1349        };
1350        if client_proof::verify(&cli_proof_instance,
1351            &request.#cli_proof_ident, &cli_proof_sessid).is_err() {
1352            return Err(CMZError::CliProofFailed);
1353        }
1354    };
1355
1356    // Build the Params struct, if we have params
1357    let params_struct = if has_params {
1358        let param_list = &proto_spec.params;
1359        let point_param_list = &proto_spec.point_params;
1360        quote! {
1361            pub struct Params {
1362                #( pub #param_list: Scalar, )*
1363                #( pub #point_param_list: Point, )*
1364            }
1365        }
1366    } else {
1367        quote! {}
1368    };
1369
1370    // Build the ClientState struct
1371    let client_state = {
1372        let decls = clientstate_fields.field_decls();
1373        quote! {
1374            #[serde_as]
1375            #[derive(Clone,Debug,serde::Serialize,serde::Deserialize)]
1376            pub struct ClientState {
1377                #decls
1378            }
1379
1380            impl TryFrom<&[u8]> for ClientState {
1381                type Error = bincode::Error;
1382
1383                fn try_from(bytes: &[u8]) -> bincode::Result<ClientState> {
1384                    bincode::deserialize::<ClientState>(bytes)
1385                }
1386            }
1387
1388            impl From<&ClientState> for Vec<u8> {
1389                fn from(req: &ClientState) -> Vec<u8> {
1390                    bincode::serialize(req).unwrap()
1391                }
1392            }
1393
1394            impl ClientState {
1395                pub fn as_bytes(&self) -> Vec<u8> {
1396                    self.into()
1397                }
1398            }
1399        }
1400    };
1401
1402    // Build the Request and Reply structs
1403    let messages = {
1404        let reqdecls = request_fields.field_decls();
1405        let repdecls = reply_fields.field_decls();
1406        quote! {
1407            #[serde_as]
1408            #[derive(Clone,Debug,serde::Serialize,serde::Deserialize)]
1409            pub struct Request {
1410                #reqdecls
1411            }
1412
1413            impl TryFrom<&[u8]> for Request {
1414                type Error = bincode::Error;
1415
1416                fn try_from(bytes: &[u8]) -> bincode::Result<Request> {
1417                    bincode::deserialize::<Request>(bytes)
1418                }
1419            }
1420
1421            impl From<&Request> for Vec<u8> {
1422                fn from(req: &Request) -> Vec<u8> {
1423                    bincode::serialize(req).unwrap()
1424                }
1425            }
1426
1427            impl Request {
1428                pub fn as_bytes(&self) -> Vec<u8> {
1429                    self.into()
1430                }
1431            }
1432
1433            #[serde_as]
1434            #[derive(Clone,Debug,serde::Serialize,serde::Deserialize)]
1435            pub struct Reply {
1436                #repdecls
1437            }
1438
1439            impl TryFrom<&[u8]> for Reply {
1440                type Error = bincode::Error;
1441
1442                fn try_from(bytes: &[u8]) -> bincode::Result<Reply> {
1443                    bincode::deserialize::<Reply>(bytes)
1444                }
1445            }
1446
1447            impl From<&Reply> for Vec<u8> {
1448                fn from(rep: &Reply) -> Vec<u8> {
1449                    bincode::serialize(rep).unwrap()
1450                }
1451            }
1452
1453            impl Reply {
1454                pub fn as_bytes(&self) -> Vec<u8> {
1455                    self.into()
1456                }
1457            }
1458        }
1459    };
1460
1461    // Massage the statements provided in the protocol spec to change
1462    // any expression of the form "L.id" (a credential name and an
1463    // attribute name) into the corresponding scoped attribute.
1464    // Bare identifiers that are protocol parameter names also get
1465    // modified into the corresponding scoped attribute.  These names
1466    // are stored in the idmap with an empty string for the credential
1467    // name.
1468    //
1469    // The expression "valid(A)" for a shown credential A with
1470    // valid_optional set expands to the proof of validity for that
1471    // credential.
1472
1473    struct StatementScoper<'a> {
1474        idmap: &'a HashMap<(String, String), Ident>,
1475        validity_proofs: &'a HashMap<String, TokenStream>,
1476    }
1477
1478    impl<'a> VisitMut for StatementScoper<'a> {
1479        fn visit_expr_mut(&mut self, node: &mut Expr) {
1480            if let Expr::Field(exfld) = node {
1481                let base = *exfld.base.clone();
1482                if let Expr::Path(basepath) = base {
1483                    if let Member::Named(attrid) = &exfld.member {
1484                        if let Some(credid) = basepath.path.get_ident() {
1485                            if let Some(scopedid) =
1486                                self.idmap.get(&(credid.to_string(), attrid.to_string()))
1487                            {
1488                                *node = parse_quote! { #scopedid };
1489                                return;
1490                            }
1491                        }
1492                    }
1493                }
1494            }
1495
1496            if let Expr::Path(expath) = node {
1497                if let Some(id) = expath.path.get_ident() {
1498                    if let Some(scopedparam) = self.idmap.get(&("".to_string(), id.to_string())) {
1499                        *node = parse_quote! { #scopedparam };
1500                        return;
1501                    }
1502                }
1503            }
1504
1505            if let Expr::Call(excall) = node {
1506                let base = *excall.func.clone();
1507                if let Expr::Path(basepath) = base {
1508                    if let Some(id) = basepath.path.get_ident() {
1509                        if *id == "valid" && excall.args.len() == 1 {
1510                            let mut validity_statement = quote! {};
1511                            let argexpr = excall.args.first().unwrap();
1512                            if let Expr::Path(argpath) = argexpr {
1513                                if let Some(credid) = argpath.path.get_ident() {
1514                                    let credstr = credid.to_string();
1515                                    match self.validity_proofs.get(&credstr) {
1516                                        Some(tokens) => {
1517                                            validity_statement = tokens.clone();
1518                                        },
1519                                        None => panic!("{} is not a shown credential with optional validity proof", credstr),
1520                                    }
1521                                }
1522                            }
1523                            *node = parse_quote! { #validity_statement };
1524                            return;
1525                        }
1526                    }
1527                }
1528            }
1529
1530            // Unless we bailed out above, continue with the default
1531            // traversal
1532            visit_mut::visit_expr_mut(self, node);
1533        }
1534    }
1535
1536    let mut statement_scoper = StatementScoper {
1537        idmap: &cli_proof_idmap,
1538        validity_proofs: &validity_proofs,
1539    };
1540    let mut cli_proof_scoped_statements = proto_spec.statements.clone();
1541    cli_proof_scoped_statements
1542        .iter_mut()
1543        .for_each(|expr| statement_scoper.visit_expr_mut(expr));
1544
1545    // The client's zero-knowledge proof
1546    let cli_sigma_compiler_macro = if emit_client && emit_issuer {
1547        quote! { sigma_compiler }
1548    } else if emit_client {
1549        quote! { sigma_compiler_prover }
1550    } else {
1551        quote! { sigma_compiler_verifier }
1552    };
1553
1554    let cli_proof = {
1555        quote! {
1556            #cli_sigma_compiler_macro! { client_proof<Point>,
1557                (#(rand #cli_proof_rand_scalars,)*
1558                 #(#cli_proof_priv_scalars,)*
1559                 #(pub #cli_proof_pub_scalars,)*),
1560                (#(cind #cli_proof_cind_points,)*
1561                 #(#cli_proof_pub_points,)*
1562                 #(cind const #cli_proof_const_points,)*),
1563                #(#cli_proof_scoped_statements,)*
1564                #(#cli_proof_statements)*
1565            }
1566        }
1567    };
1568
1569    // The issuer's zero-knowledge proof
1570    let iss_sigma_compiler_macro = if emit_client && emit_issuer {
1571        quote! { sigma_compiler }
1572    } else if emit_issuer {
1573        quote! { sigma_compiler_prover }
1574    } else {
1575        quote! { sigma_compiler_verifier }
1576    };
1577
1578    let iss_proof = {
1579        quote! {
1580            #iss_sigma_compiler_macro! { issuer_proof<Point>,
1581                (#(rand #iss_proof_rand_scalars,)*
1582                 #(#iss_proof_priv_scalars,)*
1583                 #(pub #iss_proof_pub_scalars,)*),
1584                // no cind_points
1585                (#(#iss_proof_pub_points,)*
1586                 #(cind const #iss_proof_const_points,)*),
1587                #(#iss_proof_statements)*
1588            }
1589        }
1590    };
1591
1592    // The argument list for the client's prepare function.  There is an
1593    // immutable reference for each credential to be shown, and an owned
1594    // value for each credential to be issued.
1595    let client_show_args = proto_spec.show_creds.iter().map(|c| {
1596        let id = format_ident!("show_cred_{}", c.id);
1597        let cred_type = &c.cred_type;
1598        quote! { #id: &#cred_type, }
1599    });
1600
1601    let client_issue_args = proto_spec.issue_creds.iter().map(|c| {
1602        let id = format_ident!("iss_cred_{}", c.id);
1603        let cred_type = &c.cred_type;
1604        quote! { #id: #cred_type, }
1605    });
1606
1607    let client_params_arg = if has_params {
1608        quote! { params: &Params, }
1609    } else {
1610        quote! {}
1611    };
1612
1613    // Build the client's prepare function
1614    let client_func = {
1615        let reqf = request_fields.field_iter();
1616        let csf = clientstate_fields.field_iter();
1617        quote! {
1618            pub fn prepare(rng: &mut (impl CryptoRng + RngCore),
1619                session_id: &[u8],
1620                #(#client_show_args)* #(#client_issue_args)* #client_params_arg)
1621                    -> Result<(Request, ClientState),CMZError> {
1622                let bp = cmz_basepoints::<Point>();
1623                let mut cli_proof_sessid: Vec<u8> = Vec::new();
1624                cli_proof_sessid.extend(b"cli_");
1625                cli_proof_sessid.extend(session_id);
1626                let mut iss_proof_sessid: Vec<u8> = Vec::new();
1627                iss_proof_sessid.extend(b"iss_");
1628                iss_proof_sessid.extend(session_id);
1629                #prepare_code
1630                Ok((Request{#(#reqf,)*}, ClientState{#(#csf,)*}))
1631            }
1632        }
1633    };
1634
1635    // Build the issuer's handle function
1636    let issuer_func = {
1637        // The credential declarations for the issuer's handle function
1638        let cred_decls = proto_spec
1639            .show_creds
1640            .iter()
1641            .map(|c| {
1642                let id = format_ident!("show_cred_{}", c.id);
1643                let cred_type = &c.cred_type;
1644                quote! { let mut #id = #cred_type::default(); }
1645            })
1646            .chain(proto_spec.issue_creds.iter().map(|c| {
1647                let id = format_ident!("iss_cred_{}", c.id);
1648                let cred_type = &c.cred_type;
1649                quote! { let mut #id = #cred_type::default(); }
1650            }));
1651
1652        // The type of the returned credentials from handle
1653        let cred_rettypes = proto_spec
1654            .show_creds
1655            .iter()
1656            .map(|c| {
1657                let cred_type = &c.cred_type;
1658                quote! { #cred_type }
1659            })
1660            .chain(proto_spec.issue_creds.iter().map(|c| {
1661                let cred_type = &c.cred_type;
1662                quote! { #cred_type }
1663            }));
1664
1665        // The return type
1666        let rettype = match tot_num_creds {
1667            0 => quote! { Result<Reply,CMZError> },
1668            1 => quote! { Result<(Reply, #(#cred_rettypes)*),CMZError> },
1669            _ => quote! { Result<(Reply, (#(#cred_rettypes),*)),CMZError> },
1670        };
1671
1672        // The return value
1673        let cred_retvals = proto_spec
1674            .show_creds
1675            .iter()
1676            .map(|c| {
1677                let id = format_ident!("show_cred_{}", c.id);
1678                quote! { #id }
1679            })
1680            .chain(proto_spec.issue_creds.iter().map(|c| {
1681                let id = format_ident!("iss_cred_{}", c.id);
1682                quote! { #id }
1683            }));
1684
1685        // The argument list for the issuer's fill_creds callback
1686        let fill_creds_args = proto_spec
1687            .show_creds
1688            .iter()
1689            .map(|c| {
1690                let cred_type = &c.cred_type;
1691                quote! { &mut #cred_type, }
1692            })
1693            .chain(proto_spec.issue_creds.iter().map(|c| {
1694                let cred_type = &c.cred_type;
1695                quote! { &mut #cred_type, }
1696            }));
1697
1698        // The parameters for the fill_creds callback
1699        let fill_creds_params = proto_spec
1700            .show_creds
1701            .iter()
1702            .map(|c| {
1703                let id = format_ident!("show_cred_{}", c.id);
1704                quote! { &mut #id, }
1705            })
1706            .chain(proto_spec.issue_creds.iter().map(|c| {
1707                let id = format_ident!("iss_cred_{}", c.id);
1708                quote! { &mut #id, }
1709            }));
1710
1711        // The return value of the callback
1712        let fill_creds_params_ret = if has_params {
1713            quote! { Params }
1714        } else {
1715            quote! { () }
1716        };
1717
1718        // The assignment of the return value of the callback
1719        let fill_creds_assign = if has_params {
1720            quote! { let params = }
1721        } else {
1722            quote! {}
1723        };
1724
1725        // The argument list for the issuer's authorize callback
1726        let authorize_args = proto_spec
1727            .show_creds
1728            .iter()
1729            .map(|c| {
1730                let cred_type = &c.cred_type;
1731                quote! { &#cred_type, }
1732            })
1733            .chain(proto_spec.issue_creds.iter().map(|c| {
1734                let cred_type = &c.cred_type;
1735                quote! { &#cred_type, }
1736            }));
1737
1738        // The parameters for the authorize callback
1739        let authorize_params = proto_spec
1740            .show_creds
1741            .iter()
1742            .map(|c| {
1743                let id = format_ident!("show_cred_{}", c.id);
1744                quote! { &#id, }
1745            })
1746            .chain(proto_spec.issue_creds.iter().map(|c| {
1747                let id = format_ident!("iss_cred_{}", c.id);
1748                quote! { &#id, }
1749            }));
1750
1751        let repf = reply_fields.field_iter();
1752        let retval = match tot_num_creds {
1753            0 => quote! { Ok(Reply{#(#repf,)*}) },
1754            1 => quote! { Ok((Reply{#(#repf,)*}, #(#cred_retvals)*)) },
1755            _ => quote! { Ok((Reply{#(#repf,)*}, (#(#cred_retvals),*))) },
1756        };
1757
1758        quote! {
1759            pub fn handle<F,A>(rng: &mut (impl CryptoRng + RngCore),
1760                session_id: &[u8],
1761                request: Request, fill_creds: F, authorize: A)
1762                -> #rettype
1763            where
1764                F: FnOnce(#(#fill_creds_args)*) ->
1765                    Result<#fill_creds_params_ret, CMZError>,
1766                A: FnOnce(#(#authorize_args)*) ->
1767                    Result<(),CMZError>
1768            {
1769                let bp = cmz_basepoints::<Point>();
1770                let mut cli_proof_sessid: Vec<u8> = Vec::new();
1771                cli_proof_sessid.extend(b"cli_");
1772                cli_proof_sessid.extend(session_id);
1773                let mut iss_proof_sessid: Vec<u8> = Vec::new();
1774                iss_proof_sessid.extend(b"iss_");
1775                iss_proof_sessid.extend(session_id);
1776                #(#cred_decls)*
1777                #handle_code_pre_fill
1778                #fill_creds_assign fill_creds(#(#fill_creds_params)*)?;
1779                #handle_code_post_fill
1780                authorize(#(#authorize_params)*)?;
1781                #handle_code_post_auth
1782                #retval
1783            }
1784        }
1785    };
1786
1787    // Build the ClientState's finalize function
1788    let clientstate_finalize_func = {
1789        // The credential declarations for the client's finalize function
1790        let cred_decls = proto_spec.issue_creds.iter().map(|c| {
1791            let id = format_ident!("iss_cred_{}", c.id);
1792            let cred_type = &c.cred_type;
1793            quote! { let mut #id = #cred_type::default(); }
1794        });
1795
1796        // The type of the returned credentials from finalize
1797        let cred_rettypes = proto_spec.issue_creds.iter().map(|c| {
1798            let cred_type = &c.cred_type;
1799            quote! { #cred_type }
1800        });
1801
1802        let rettype = match proto_spec.issue_creds.len() {
1803            0 => quote! { Result<(),(CMZError,Self)> },
1804            1 => quote! { Result<#(#cred_rettypes)*,(CMZError,Self)> },
1805            _ => quote! { Result<(#(#cred_rettypes),*),(CMZError,Self)> },
1806        };
1807
1808        // Return value for ClientState's finalize function
1809        let cred_retvals = proto_spec.issue_creds.iter().map(|c| {
1810            let id = format_ident!("iss_cred_{}", c.id);
1811            quote! { #id }
1812        });
1813
1814        let retval = match proto_spec.issue_creds.len() {
1815            0 => quote! { Ok(()) },
1816            1 => quote! { Ok(#(#cred_retvals)*) },
1817            _ => quote! { Ok((#(#cred_retvals),*)) },
1818        };
1819
1820        quote! {
1821            impl ClientState {
1822                pub fn finalize(
1823                    self,
1824                    reply: Reply,
1825                ) -> #rettype {
1826                    let bp = cmz_basepoints::<Point>();
1827                    #(#cred_decls)*
1828                    #finalize_code
1829                    #retval
1830                }
1831            }
1832        }
1833    };
1834
1835    let client_side = if emit_client {
1836        quote! { #client_state #client_func #clientstate_finalize_func }
1837    } else {
1838        quote! {}
1839    };
1840
1841    let issuer_side = if emit_issuer {
1842        issuer_func
1843    } else {
1844        quote! {}
1845    };
1846
1847    // Output the generated module for this protocol
1848    quote! {
1849        #[allow(non_snake_case)]
1850        pub mod #proto_name {
1851            use super::*;
1852            use sigma_compiler::*;
1853            use group::GroupEncoding;
1854
1855            #group_types
1856            #params_struct
1857            #messages
1858            #cli_proof
1859            #iss_proof
1860            #client_side
1861            #issuer_side
1862        }
1863    }
1864}