1#![allow(non_snake_case)]
3
4use 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#[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#[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#[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#[derive(Debug)]
102struct CredSpec<ShowOrIssue: Parse, const VALID_OPTIONAL: bool> {
103 id: Ident,
104 cred_type: Ident,
105 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
143struct 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 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#[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 if input.peek(Token![<]) {
194 input.parse::<Token![<]>()?;
195 loop {
196 if input.peek(Token![>]) {
197 break;
198 }
199 if input.peek(Token![@]) {
200 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
236enum StructField {
238 Scalar(Ident),
239 Point(Ident),
240 EncPoint(Ident),
241 Pubkey(Ident),
242 ByteVec(Ident),
243}
244
245#[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 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 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
305pub 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 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 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 let mut cli_proof_idmap = HashMap::<(String, String), Ident>::default();
349
350 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 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 let mut clientstate_fields = StructFieldList::default();
366
367 let mut request_fields = StructFieldList::default();
369
370 let mut reply_fields = StructFieldList::default();
372
373 let mut prepare_code = quote! {};
375
376 let mut handle_code_pre_fill = quote! {};
379
380 let mut handle_code_post_fill = quote! {};
383
384 let mut handle_code_post_auth = quote! {};
387
388 let mut finalize_code = quote! {};
390
391 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 clientstate_fields.push_bytevec(&iss_proof_sessid_ident);
434
435 for iss_cred in proto_spec.issue_creds.iter() {
436 let mut cred_hide_joint = false;
439
440 let iss_cred_id = format_ident!("iss_cred_{}", iss_cred.id);
442 let pubkey_cred = format_ident!("pubkey_iss_cred_{}", iss_cred.id);
444 let b_cred = format_ident!("b_iss_cred_{}", iss_cred.id);
446 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 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 let mut eq0_statement = quote! {};
458 let mut eq1_statement = quote! {};
459
460 let C_cred = format_ident!("C_iss_cred_{}", iss_cred.id);
464 let K_cred = format_ident!("K_iss_cred_{}", iss_cred.id);
467 let mut C_statement = quote! {};
469
470 let iss_cred_type = &iss_cred.cred_type;
471
472 let cred_str = iss_cred.id.to_string();
474
475 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 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 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 let attr_str = attr.to_string();
515
516 let scoped_attr = format_ident!("iss_{}attr_{}_{}", spec.abbr(), iss_cred.id, attr);
518
519 cli_proof_idmap.insert(
522 (iss_cred.id.to_string(), attr.to_string()),
523 scoped_attr.clone(),
524 );
525
526 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 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 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 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 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 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 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 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 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 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 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 handle_code_post_auth = quote! {
787 #handle_code_post_auth
788 #Q_cred += (#scoped_attr * #x_attr) * #P_cred;
789 };
790 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 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 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 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(�_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 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 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 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 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 let mut validity_proofs: HashMap<String, TokenStream> = HashMap::new();
1069
1070 for show_cred in proto_spec.show_creds.iter() {
1071 let show_cred_id = format_ident!("show_cred_{}", show_cred.id);
1073 let t_cred = format_ident!("t_show_cred_{}", show_cred.id);
1075 let P_cred = format_ident!("P_show_cred_{}", show_cred.id);
1077 let Q_cred = format_ident!("Q_show_cred_{}", show_cred.id);
1078 let zQ_cred = format_ident!("zQ_show_cred_{}", show_cred.id);
1080 let CQ_cred = format_ident!("CQ_show_cred_{}", show_cred.id);
1082 let V_cred = format_ident!("V_show_cred_{}", show_cred.id);
1084 let q_cred = format_ident!("q_show_cred_{}", show_cred.id);
1087
1088 let show_cred_type = &show_cred.cred_type;
1089
1090 let cred_str = show_cred.id.to_string();
1092
1093 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 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 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 let mut V_statement = quote! {
1146 #V_cred = #zQ_cred * #B_ident
1147 };
1148
1149 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 let attr_str = attr.to_string();
1168
1169 let scoped_attr = format_ident!("show_{}attr_{}_{}", spec.abbr(), show_cred.id, attr);
1171
1172 let X_attr = format_ident!("X_{}", scoped_attr);
1174
1175 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 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 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 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 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 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 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 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 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 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 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 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 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 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 (#(#iss_proof_pub_points,)*
1586 #(cind const #iss_proof_const_points,)*),
1587 #(#iss_proof_statements)*
1588 }
1589 }
1590 };
1591
1592 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 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 let issuer_func = {
1637 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 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 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 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 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 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 let fill_creds_params_ret = if has_params {
1713 quote! { Params }
1714 } else {
1715 quote! { () }
1716 };
1717
1718 let fill_creds_assign = if has_params {
1720 quote! { let params = }
1721 } else {
1722 quote! {}
1723 };
1724
1725 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 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 let clientstate_finalize_func = {
1789 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 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 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 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}