#![allow(non_snake_case)]
use proc_macro2::TokenStream;
use quote::{format_ident, quote, ToTokens};
use std::collections::{BTreeMap, HashMap};
use syn::parse::{Parse, ParseStream, Result};
use syn::punctuated::Punctuated;
use syn::visit_mut::{self, VisitMut};
use syn::{braced, bracketed, parse_quote, token, Expr, Ident, Member, Token};
#[derive(Copy, Clone, Debug, PartialEq)]
enum ShowSpec {
Hide,
Reveal,
Implicit,
}
impl Parse for ShowSpec {
fn parse(input: ParseStream) -> Result<Self> {
let spec: Ident = input.parse()?;
match spec.to_string().to_uppercase().as_str() {
"H" | "HIDE" => Ok(Self::Hide),
"R" | "REVEAL" => Ok(Self::Reveal),
"I" | "IMPLICIT" => Ok(Self::Implicit),
_ => Err(input.error("Unknown attribute spec for shown credential")),
}
}
}
impl ShowSpec {
fn abbr(&self) -> &'static str {
match self {
Self::Hide => "H",
Self::Reveal => "R",
Self::Implicit => "I",
}
}
}
#[derive(Copy, Clone, Debug, PartialEq)]
enum IssueSpec {
Hide,
Reveal,
Implicit,
Set,
Joint,
}
impl Parse for IssueSpec {
fn parse(input: ParseStream) -> Result<Self> {
let spec: Ident = input.parse()?;
match spec.to_string().to_uppercase().as_str() {
"H" | "HIDE" => Ok(Self::Hide),
"R" | "REVEAL" => Ok(Self::Reveal),
"I" | "IMPLICIT" => Ok(Self::Implicit),
"S" | "SET" => Ok(Self::Set),
"J" | "JOINT" => Ok(Self::Joint),
_ => Err(input.error("Unknown attribute spec for issued credential")),
}
}
}
impl IssueSpec {
fn abbr(&self) -> &'static str {
match self {
Self::Hide => "H",
Self::Reveal => "R",
Self::Implicit => "I",
Self::Set => "S",
Self::Joint => "J",
}
}
}
#[derive(Clone)]
struct AttrSpec<ShowOrIssue: Parse> {
attr: Ident,
spec: ShowOrIssue,
}
impl<ShowOrIssue: Parse> Parse for AttrSpec<ShowOrIssue> {
fn parse(input: ParseStream) -> Result<Self> {
let attr: Ident = input.parse()?;
input.parse::<Token![:]>()?;
let spec: ShowOrIssue = input.parse()?;
Ok(Self { attr, spec })
}
}
#[derive(Debug)]
struct CredSpec<ShowOrIssue: Parse, const VALID_OPTIONAL: bool> {
id: Ident,
cred_type: Ident,
valid_optional: bool,
attrs: BTreeMap<Ident, ShowOrIssue>,
}
impl<ShowOrIssue: Parse + Copy, const VALID_OPTIONAL: bool> Parse
for CredSpec<ShowOrIssue, VALID_OPTIONAL>
{
fn parse(input: ParseStream) -> Result<Self> {
let id: Ident = input.parse()?;
let valid_optional = if VALID_OPTIONAL && input.peek(Token![?]) {
input.parse::<Token![?]>()?;
true
} else {
false
};
input.parse::<Token![:]>()?;
let cred_type: Ident = input.parse()?;
let content;
braced!(content in input);
let attrspecs: Punctuated<AttrSpec<ShowOrIssue>, Token![,]> =
content.parse_terminated(AttrSpec::<ShowOrIssue>::parse, Token![,])?;
let mut attrs: BTreeMap<Ident, ShowOrIssue> = BTreeMap::new();
for attrspec in attrspecs.iter() {
attrs.insert(attrspec.attr.clone(), attrspec.spec);
}
Ok(Self {
id,
cred_type,
valid_optional,
attrs,
})
}
}
struct CredSpecVec<ShowOrIssue: Parse, const VALID_OPTIONAL: bool>(
Vec<CredSpec<ShowOrIssue, VALID_OPTIONAL>>,
);
impl<ShowOrIssue: Parse + Copy, const VALID_OPTIONAL: bool> Parse
for CredSpecVec<ShowOrIssue, VALID_OPTIONAL>
{
fn parse(input: ParseStream) -> Result<Self> {
let specvec: Vec<CredSpec<ShowOrIssue, VALID_OPTIONAL>> = if input.peek(Token![,]) {
Vec::new()
} else if input.peek(token::Bracket) {
let content;
bracketed!(content in input);
let specs: Punctuated<CredSpec<ShowOrIssue, VALID_OPTIONAL>, Token![,]> = content
.parse_terminated(CredSpec::<ShowOrIssue, VALID_OPTIONAL>::parse, Token![,])?;
specs.into_iter().collect()
} else {
let spec: CredSpec<ShowOrIssue, VALID_OPTIONAL> = input.parse()?;
vec![spec]
};
Ok(Self(specvec))
}
}
#[derive(Debug)]
pub struct ProtoSpec {
proto_name: Ident,
params: Vec<Ident>,
point_params: Vec<Ident>,
show_creds: Vec<CredSpec<ShowSpec, true>>,
issue_creds: Vec<CredSpec<IssueSpec, false>>,
statements: Vec<Expr>,
}
impl Parse for ProtoSpec {
fn parse(input: ParseStream) -> Result<Self> {
let mut params: Vec<Ident> = Vec::new();
let mut point_params: Vec<Ident> = Vec::new();
let proto_name: Ident = input.parse()?;
if input.peek(Token![<]) {
input.parse::<Token![<]>()?;
loop {
if input.peek(Token![>]) {
break;
}
if input.peek(Token![@]) {
input.parse::<Token![@]>()?;
let param: Ident = input.parse()?;
point_params.push(param);
} else {
let param: Ident = input.parse()?;
params.push(param);
}
if input.peek(Token![>]) {
break;
}
input.parse::<Token![,]>()?;
}
input.parse::<Token![>]>()?;
}
input.parse::<Token![,]>()?;
let showvec: CredSpecVec<ShowSpec, true> = input.parse()?;
input.parse::<Token![,]>()?;
let issuevec: CredSpecVec<IssueSpec, false> = input.parse()?;
input.parse::<Token![,]>()?;
let statementpunc: Punctuated<Expr, Token![,]> =
input.parse_terminated(Expr::parse, Token![,])?;
let statements: Vec<Expr> = statementpunc.into_iter().collect();
Ok(ProtoSpec {
proto_name,
params,
point_params,
show_creds: showvec.0,
issue_creds: issuevec.0,
statements,
})
}
}
enum StructField {
Scalar(Ident),
Point(Ident),
EncPoint(Ident),
Pubkey(Ident),
ByteVec(Ident),
}
#[derive(Default)]
struct StructFieldList {
fields: Vec<StructField>,
}
impl StructFieldList {
pub fn push_scalar(&mut self, s: &Ident) {
self.fields.push(StructField::Scalar(s.clone()));
}
pub fn push_point(&mut self, s: &Ident) {
self.fields.push(StructField::Point(s.clone()));
}
pub fn push_encpoint(&mut self, s: &Ident) {
self.fields.push(StructField::EncPoint(s.clone()));
}
pub fn push_pubkey(&mut self, s: &Ident) {
self.fields.push(StructField::Pubkey(s.clone()));
}
pub fn push_bytevec(&mut self, s: &Ident) {
self.fields.push(StructField::ByteVec(s.clone()));
}
pub fn field_iter(&self) -> impl Iterator<Item = &Ident> {
self.fields.iter().map(|f| match f {
StructField::Scalar(id) => id,
StructField::Point(id) => id,
StructField::EncPoint(id) => id,
StructField::Pubkey(id) => id,
StructField::ByteVec(id) => id,
})
}
pub fn field_decls(&self) -> impl ToTokens {
let decls = self.fields.iter().map(|f| match f {
StructField::Scalar(id) => quote! {
#[serde_as(as = "SerdeScalar")]
pub #id: Scalar,
},
StructField::Point(id) => quote! {
#[serde_as(as = "SerdePoint")]
pub #id: Point,
},
StructField::EncPoint(id) => quote! {
#[serde_as(as = "(SerdePoint, SerdePoint)")]
pub #id: (Point, Point),
},
StructField::Pubkey(id) => quote! {
pub #id: CMZPubkey<Point>,
},
StructField::ByteVec(id) => quote! {
#[serde(with = "serde_bytes")]
pub #id: Vec<u8>,
},
});
quote! { #(#decls)* }
}
}
pub fn cmz_core(
proto_spec: &ProtoSpec,
use_muCMZ: bool,
emit_client: bool,
emit_issuer: bool,
) -> TokenStream {
let proto_name = &proto_spec.proto_name;
let has_params = !proto_spec.params.is_empty() || !proto_spec.point_params.is_empty();
let tot_num_creds = proto_spec.show_creds.len() + proto_spec.issue_creds.len();
let group_types = if !proto_spec.show_creds.is_empty() {
let first_cred_type = &proto_spec.show_creds[0].cred_type;
quote! {
pub type Scalar = <#first_cred_type as CMZCredential>::Scalar;
pub type Point = <#first_cred_type as CMZCredential>::Point;
}
} else if !proto_spec.issue_creds.is_empty() {
let first_cred_type = &proto_spec.issue_creds[0].cred_type;
quote! {
pub type Scalar = <#first_cred_type as CMZCredential>::Scalar;
pub type Point = <#first_cred_type as CMZCredential>::Point;
}
} else {
quote! {}
};
let mut cli_proof_rand_scalars = Vec::<Ident>::default();
let mut cli_proof_priv_scalars = Vec::<Ident>::default();
let mut cli_proof_pub_scalars = Vec::<Ident>::default();
let mut cli_proof_cind_points = Vec::<Ident>::default();
let mut cli_proof_pub_points = Vec::<Ident>::default();
let mut cli_proof_const_points = Vec::<Ident>::default();
let mut cli_proof_statements = Vec::<TokenStream>::default();
let mut cli_proof_idmap = HashMap::<(String, String), Ident>::default();
let mut iss_proof_rand_scalars = Vec::<Ident>::default();
let mut iss_proof_priv_scalars = Vec::<Ident>::default();
let mut iss_proof_pub_scalars = Vec::<Ident>::default();
let mut iss_proof_pub_points = Vec::<Ident>::default();
let mut iss_proof_const_points = Vec::<Ident>::default();
let mut iss_proof_statements = Vec::<TokenStream>::default();
let mut clientstate_fields = StructFieldList::default();
let mut request_fields = StructFieldList::default();
let mut reply_fields = StructFieldList::default();
let mut prepare_code = quote! {};
let mut handle_code_pre_fill = quote! {};
let mut handle_code_post_fill = quote! {};
let mut handle_code_post_auth = quote! {};
let mut finalize_code = quote! {};
let mut any_hide_joint = false;
let A_ident = format_ident!("A_generator");
let B_ident = format_ident!("B_generator");
let d_ident = format_ident!("d_privkey");
let D_ident = format_ident!("D_pubkey");
let iss_proof_sessid_ident = format_ident!("iss_proof_sessid");
prepare_code = quote! {
#prepare_code
let #A_ident = bp.A();
};
handle_code_pre_fill = quote! {
#handle_code_pre_fill
let #A_ident = bp.A();
};
finalize_code = quote! {
#finalize_code
let #A_ident = bp.A();
};
iss_proof_const_points.push(A_ident.clone());
prepare_code = quote! {
#prepare_code
let #B_ident = bp.B();
};
handle_code_pre_fill = quote! {
#handle_code_pre_fill
let #B_ident = bp.B();
};
if !use_muCMZ || !proto_spec.issue_creds.is_empty() {
finalize_code = quote! {
#finalize_code
let #B_ident = bp.B();
};
iss_proof_const_points.push(B_ident.clone());
}
clientstate_fields.push_bytevec(&iss_proof_sessid_ident);
for iss_cred in proto_spec.issue_creds.iter() {
let mut cred_hide_joint = false;
let iss_cred_id = format_ident!("iss_cred_{}", iss_cred.id);
let pubkey_cred = format_ident!("pubkey_iss_cred_{}", iss_cred.id);
let b_cred = format_ident!("b_iss_cred_{}", iss_cred.id);
let P_cred = format_ident!("P_iss_cred_{}", iss_cred.id);
let Q_cred = format_ident!("Q_iss_cred_{}", iss_cred.id);
let EQ_cred = format_ident!("EQ_iss_cred_{}", iss_cred.id);
let EQ0_cred = format_ident!("EQ0_iss_cred_{}", iss_cred.id);
let EQ1_cred = format_ident!("EQ1_iss_cred_{}", iss_cred.id);
let mut eq0_statement = quote! {};
let mut eq1_statement = quote! {};
let C_cred = format_ident!("C_iss_cred_{}", iss_cred.id);
let K_cred = format_ident!("K_iss_cred_{}", iss_cred.id);
let mut C_statement = quote! {};
let iss_cred_type = &iss_cred.cred_type;
let cred_str = iss_cred.id.to_string();
handle_code_post_fill = quote! {
#handle_code_post_fill
if #iss_cred_id.get_privkey().x.len() != #iss_cred_type::num_attrs() {
return Err(CMZError::PrivkeyMissing(#cred_str));
}
if #iss_cred_id.get_privkey().muCMZ != #use_muCMZ {
return Err(CMZError::WrongProtocol(#cred_str));
}
};
prepare_code = quote! {
#prepare_code
if #iss_cred_id.get_pubkey().X.len() != #iss_cred_type::num_attrs() {
return Err(CMZError::PubkeyMissing(#cred_str));
}
if #iss_cred_id.get_pubkey().Xr.is_some() != #use_muCMZ {
return Err(CMZError::WrongProtocol(#cred_str));
}
};
clientstate_fields.push_pubkey(&pubkey_cred);
prepare_code = quote! {
#prepare_code
let #pubkey_cred = #iss_cred_id.get_pubkey().clone();
};
finalize_code = quote! {
#finalize_code
#iss_cred_id.set_pubkey(&self.#pubkey_cred);
};
for (attr, &spec) in iss_cred.attrs.iter() {
let attr_str = attr.to_string();
let scoped_attr = format_ident!("iss_{}attr_{}_{}", spec.abbr(), iss_cred.id, attr);
cli_proof_idmap.insert(
(iss_cred.id.to_string(), attr.to_string()),
scoped_attr.clone(),
);
let x_attr = format_ident!("x_{}", scoped_attr);
let X_attr = format_ident!("X_{}", scoped_attr);
if spec == IssueSpec::Hide || spec == IssueSpec::Joint {
cred_hide_joint = true;
}
if !use_muCMZ {
handle_code_post_auth = quote! {
#handle_code_post_auth
let #x_attr = #iss_cred_id.privkey_x(#attr_str);
let #X_attr = #iss_cred_id.pubkey_X(#attr_str);
};
finalize_code = quote! {
#finalize_code
let #X_attr = #iss_cred_id.pubkey_X(#attr_str);
};
iss_proof_priv_scalars.push(x_attr.clone());
iss_proof_pub_points.push(X_attr.clone());
iss_proof_statements.push(quote! {
#X_attr = #x_attr * #A_ident,
});
}
if spec == IssueSpec::Hide {
clientstate_fields.push_scalar(&scoped_attr);
prepare_code = quote! {
#prepare_code
let #scoped_attr =
#iss_cred_id.#attr.ok_or(CMZError::HideAttrMissing(#cred_str,
#attr_str))?;
};
finalize_code = quote! {
#finalize_code
#iss_cred_id.#attr = Some(self.#scoped_attr);
}
}
if spec == IssueSpec::Joint {
clientstate_fields.push_scalar(&scoped_attr);
reply_fields.push_scalar(&scoped_attr);
prepare_code = quote! {
#prepare_code
let #scoped_attr = <Scalar as ff::Field>::random(&mut *rng);
};
handle_code_pre_fill = quote! {
#handle_code_pre_fill
let #scoped_attr = <Scalar as ff::Field>::random(&mut *rng);
};
finalize_code = quote! {
#finalize_code
let #scoped_attr = reply.#scoped_attr;
#iss_cred_id.#attr = Some(self.#scoped_attr + reply.#scoped_attr);
};
}
if !use_muCMZ && (spec == IssueSpec::Hide || spec == IssueSpec::Joint) {
let enc_attr = format_ident!("E_{}", scoped_attr);
let enc0_attr = format_ident!("E0_{}", scoped_attr);
let enc1_attr = format_ident!("E1_{}", scoped_attr);
let r_attr = format_ident!("r_{}", scoped_attr);
let t_attr = format_ident!("t_{}", scoped_attr);
let T_attr = format_ident!("T_{}", scoped_attr);
request_fields.push_encpoint(&enc_attr);
clientstate_fields.push_encpoint(&enc_attr);
reply_fields.push_point(&T_attr);
iss_proof_priv_scalars.push(t_attr.clone());
iss_proof_pub_points.push(T_attr.clone());
iss_proof_pub_points.push(enc0_attr.clone());
iss_proof_pub_points.push(enc1_attr.clone());
iss_proof_statements.push(quote! {
#T_attr = #t_attr * #A_ident,
#T_attr = #b_cred * #X_attr,
});
eq0_statement = quote! {
#eq0_statement + #t_attr * #enc0_attr
};
eq1_statement = quote! {
#eq1_statement + #t_attr * #enc1_attr
};
cli_proof_priv_scalars.push(scoped_attr.clone());
cli_proof_rand_scalars.push(r_attr.clone());
cli_proof_pub_points.push(enc0_attr.clone());
cli_proof_pub_points.push(enc1_attr.clone());
cli_proof_statements.push(quote! {
#enc0_attr = #r_attr * #B_ident,
#enc1_attr = #scoped_attr * #B_ident + #r_attr * #D_ident,
});
prepare_code = quote! {
#prepare_code
let #r_attr = <Scalar as ff::Field>::random(&mut *rng);
let #enc0_attr = bp.mulB(&#r_attr);
let #enc1_attr = bp.mulB(&#scoped_attr) +
#r_attr * #D_ident;
let #enc_attr = (#enc0_attr, #enc1_attr);
};
handle_code_post_fill = quote! {
#handle_code_post_fill
let #enc0_attr = request.#enc_attr.0;
let #enc1_attr = request.#enc_attr.1;
};
handle_code_post_auth = quote! {
#handle_code_post_auth
let #t_attr = #b_cred * #x_attr;
#EQ_cred.0 += #t_attr * #enc0_attr;
#EQ_cred.1 += #t_attr * #enc1_attr;
let #T_attr = bp.mulA(&#t_attr);
};
finalize_code = quote! {
#finalize_code
let #T_attr = reply.#T_attr;
let #enc0_attr = self.#enc_attr.0;
let #enc1_attr = self.#enc_attr.1;
};
}
if use_muCMZ && (spec == IssueSpec::Hide || spec == IssueSpec::Joint) {
prepare_code = quote! {
#prepare_code
let #X_attr = #pubkey_cred.X[#iss_cred_type::attr_num(#attr_str)];
#C_cred += #scoped_attr * #X_attr;
};
handle_code_post_fill = quote! {
#handle_code_post_fill
let #X_attr = #iss_cred_id.pubkey_X(#attr_str);
};
C_statement = quote! {
#C_statement + #scoped_attr * #X_attr
};
cli_proof_priv_scalars.push(scoped_attr.clone());
cli_proof_cind_points.push(X_attr.clone());
}
if spec == IssueSpec::Reveal {
request_fields.push_scalar(&scoped_attr);
clientstate_fields.push_scalar(&scoped_attr);
cli_proof_pub_scalars.push(scoped_attr.clone());
prepare_code = quote! {
#prepare_code
let #scoped_attr =
#iss_cred_id.#attr.ok_or(CMZError::RevealAttrMissing(#cred_str,
#attr_str))?;
};
handle_code_pre_fill = quote! {
#handle_code_pre_fill
let #scoped_attr = request.#scoped_attr;
#iss_cred_id.#attr = Some(#scoped_attr);
};
finalize_code = quote! {
#finalize_code
#iss_cred_id.#attr = Some(self.#scoped_attr);
};
}
if spec == IssueSpec::Implicit {
clientstate_fields.push_scalar(&scoped_attr);
cli_proof_pub_scalars.push(scoped_attr.clone());
prepare_code = quote! {
#prepare_code
let #scoped_attr =
#iss_cred_id.#attr.ok_or(CMZError::ImplicitAttrCliMissing(#cred_str,
#attr_str))?;
};
handle_code_post_fill = quote! {
#handle_code_post_fill
let #scoped_attr =
#iss_cred_id.#attr.ok_or(CMZError::ImplicitAttrIssMissing(#cred_str,
#attr_str))?;
};
finalize_code = quote! {
#finalize_code
#iss_cred_id.#attr = Some(self.#scoped_attr);
};
}
if spec == IssueSpec::Set {
reply_fields.push_scalar(&scoped_attr);
handle_code_post_fill = quote! {
#handle_code_post_fill
let #scoped_attr =
#iss_cred_id.#attr.ok_or(CMZError::SetAttrMissing(#cred_str,
#attr_str))?;
};
finalize_code = quote! {
#finalize_code
#iss_cred_id.#attr = Some(reply.#scoped_attr);
}
}
if spec == IssueSpec::Reveal
|| spec == IssueSpec::Implicit
|| spec == IssueSpec::Set
|| spec == IssueSpec::Joint
{
if use_muCMZ {
handle_code_post_fill = quote! {
#handle_code_post_fill
#K_cred += (#scoped_attr *
#iss_cred_id.pubkey_X(#attr_str));
};
let use_attr = if spec == IssueSpec::Joint {
quote! { #scoped_attr }
} else {
quote! { #iss_cred_id.#attr.unwrap() }
};
finalize_code = quote! {
#finalize_code
#K_cred += (#use_attr *
#iss_cred_id.pubkey_X(#attr_str));
};
} else {
handle_code_post_auth = quote! {
#handle_code_post_auth
#Q_cred += (#scoped_attr * #x_attr) * #P_cred;
};
if spec != IssueSpec::Joint {
finalize_code = quote! {
#finalize_code
let #scoped_attr = #iss_cred_id.#attr.unwrap();
};
}
eq1_statement = quote! {
#eq1_statement + #x_attr * ( #scoped_attr * #P_cred )
};
iss_proof_pub_scalars.push(scoped_attr.clone());
}
}
}
if !use_muCMZ {
let s_cred = format_ident!("s_iss_cred_{}", iss_cred.id);
let x0_cred = format_ident!("x0_iss_cred_{}", iss_cred.id);
let xr_cred = format_ident!("xr_cred{}", iss_cred.id);
let X0_cred = format_ident!("X0_iss_cred_{}", iss_cred.id);
reply_fields.push_point(&P_cred);
if cred_hide_joint {
reply_fields.push_encpoint(&EQ_cred);
} else {
reply_fields.push_point(&Q_cred);
}
let EQ_cred_code_pre = if cred_hide_joint {
quote! {
let #s_cred = <Scalar as ff::Field>::random(&mut *rng);
let mut #EQ_cred = (bp.mulB(&#s_cred), #s_cred * #D_ident);
}
} else {
quote! {}
};
let EQ_cred_code_post = if cred_hide_joint {
quote! {
#EQ_cred.1 += #Q_cred;
let #EQ0_cred = #EQ_cred.0;
let #EQ1_cred = #EQ_cred.1;
}
} else {
quote! {}
};
if cred_hide_joint {
iss_proof_pub_points.push(EQ0_cred.clone());
iss_proof_pub_points.push(EQ1_cred.clone());
iss_proof_statements.push(quote! {
#EQ0_cred = #s_cred * #B_ident #eq0_statement,
#EQ1_cred = #s_cred * #D_ident + #x0_cred * #P_cred #eq1_statement,
});
}
handle_code_post_auth = quote! {
let #b_cred = <Scalar as ff::Field>::random(&mut *rng);
let #P_cred = bp.mulB(&#b_cred);
#iss_cred_id.MAC.P = #P_cred;
let #x0_cred = #iss_cred_id.get_privkey().x0;
let #xr_cred = #iss_cred_id.get_privkey().xr;
let #X0_cred = #iss_cred_id.get_pubkey().X0.unwrap();
let mut #Q_cred = bp.mulB(&(#b_cred * #iss_cred_id.get_privkey().x0));
#EQ_cred_code_pre
#handle_code_post_auth
#EQ_cred_code_post
};
let finalize_Q_code = if cred_hide_joint {
quote! {
let #EQ0_cred = reply.#EQ_cred.0;
let #EQ1_cred = reply.#EQ_cred.1;
#iss_cred_id.MAC.Q = #EQ1_cred - self.#d_ident * #EQ0_cred;
}
} else {
quote! {
#iss_cred_id.MAC.Q = reply.#Q_cred;
}
};
finalize_code = quote! {
#finalize_code
let #P_cred = reply.#P_cred;
let #X0_cred = #iss_cred_id.get_pubkey().X0.unwrap();
#iss_cred_id.MAC.P = #P_cred;
#finalize_Q_code
};
if cred_hide_joint {
iss_proof_rand_scalars.push(s_cred.clone());
}
iss_proof_pub_points.push(P_cred.clone());
iss_proof_priv_scalars.push(x0_cred.clone());
iss_proof_rand_scalars.push(xr_cred.clone());
iss_proof_pub_points.push(X0_cred.clone());
iss_proof_rand_scalars.push(b_cred.clone());
iss_proof_statements.push(quote! {
#X0_cred = #x0_cred * #B_ident + #xr_cred * #A_ident,
});
}
if use_muCMZ {
let R_cred = format_ident!("R_iss_cred_{}", iss_cred.id);
let s_cred = format_ident!("s_iss_cred_{}", iss_cred.id);
let x0_cred = format_ident!("x0_iss_cred_{}", iss_cred.id);
let X0_cred = format_ident!("X0_iss_cred_{}", iss_cred.id);
reply_fields.push_point(&P_cred);
reply_fields.push_point(&R_cred);
if cred_hide_joint {
clientstate_fields.push_scalar(&s_cred);
clientstate_fields.push_point(&C_cred);
request_fields.push_point(&C_cred);
cli_proof_pub_points.push(C_cred.clone());
cli_proof_rand_scalars.push(s_cred.clone());
prepare_code = quote! {
let #s_cred = <Scalar as ff::Field>::random(&mut *rng);
let mut #C_cred = bp.mulA(&#s_cred);
#prepare_code
};
handle_code_post_fill = quote! {
let #C_cred = request.#C_cred;
let mut #K_cred = #C_cred + #iss_cred_id.get_pubkey().Xr.unwrap();
#handle_code_post_fill
};
finalize_code = quote! {
let mut #K_cred = self.#C_cred + self.#pubkey_cred.Xr.unwrap();
#finalize_code
};
cli_proof_statements.push(quote! {
#C_cred = #s_cred * #A_ident #C_statement,
});
} else {
handle_code_post_fill = quote! {
let mut #K_cred = #iss_cred_id.get_pubkey().Xr.unwrap();
#handle_code_post_fill
};
finalize_code = quote! {
let mut #K_cred = self.#pubkey_cred.Xr.unwrap();
#finalize_code
};
}
handle_code_post_auth = quote! {
#handle_code_post_auth
let #b_cred = <Scalar as ff::Field>::random(&mut *rng);
let #P_cred = bp.mulA(&#b_cred);
#iss_cred_id.MAC.P = #P_cred;
let #x0_cred = #iss_cred_id.get_privkey().x0;
let #X0_cred = #iss_cred_id.get_pubkey().X0.unwrap();
let #R_cred = #b_cred * (bp.mulA(�_cred) + #K_cred);
};
let finalize_Q_code = if cred_hide_joint {
quote! {
#iss_cred_id.MAC.Q = reply.#R_cred - self.#s_cred * reply.#P_cred;
}
} else {
quote! {
#iss_cred_id.MAC.Q = reply.#R_cred;
}
};
finalize_code = quote! {
#finalize_code
let #P_cred = reply.#P_cred;
let #X0_cred = #iss_cred_id.get_pubkey().X0.unwrap();
let #R_cred = reply.#R_cred;
#iss_cred_id.MAC.P = #P_cred;
#finalize_Q_code
};
iss_proof_priv_scalars.push(x0_cred.clone());
iss_proof_rand_scalars.push(b_cred.clone());
iss_proof_pub_points.push(P_cred.clone());
iss_proof_pub_points.push(X0_cred.clone());
iss_proof_pub_points.push(K_cred.clone());
iss_proof_pub_points.push(R_cred.clone());
iss_proof_statements.push(quote! {
#P_cred = #b_cred * #A_ident,
#X0_cred = #x0_cred * #B_ident,
#R_cred = #x0_cred * #P_cred + #b_cred * #K_cred,
});
}
any_hide_joint |= cred_hide_joint;
}
if any_hide_joint && !use_muCMZ {
clientstate_fields.push_scalar(&d_ident);
clientstate_fields.push_point(&D_ident);
cli_proof_rand_scalars.push(d_ident.clone());
cli_proof_pub_points.push(D_ident.clone());
request_fields.push_point(&D_ident);
cli_proof_statements.push(quote! {
#D_ident = #d_ident * #B_ident,
});
prepare_code = quote! {
let (#d_ident,#D_ident) = bp.keypairB(&mut *rng);
#prepare_code
};
handle_code_post_fill = quote! {
let #D_ident = request.#D_ident;
#handle_code_post_fill
};
finalize_code = quote! {
#finalize_code
let #D_ident = self.#D_ident;
};
iss_proof_pub_points.push(D_ident.clone());
}
if !proto_spec.issue_creds.is_empty() {
let iss_proof_ident = format_ident!("iss_proof");
reply_fields.push_bytevec(&iss_proof_ident);
let iss_instance_fields = iss_proof_pub_points
.iter()
.chain(iss_proof_const_points.iter())
.chain(iss_proof_pub_scalars.iter());
let iss_witness_fields = iss_proof_rand_scalars
.iter()
.chain(iss_proof_priv_scalars.iter());
handle_code_post_auth = quote! {
#handle_code_post_auth
let iss_proof_instance = issuer_proof::Instance {
#(#iss_instance_fields,)*
};
let iss_proof_witness = issuer_proof::Witness {
#(#iss_witness_fields,)*
};
let #iss_proof_ident =
issuer_proof::prove(&iss_proof_instance,
&iss_proof_witness, &iss_proof_sessid, rng).unwrap();
};
let cli_iss_instance_fields = iss_proof_pub_points
.iter()
.chain(iss_proof_const_points.iter())
.chain(iss_proof_pub_scalars.iter());
finalize_code = quote! {
#finalize_code
let iss_proof_instance = issuer_proof::Instance {
#(#cli_iss_instance_fields,)*
};
if issuer_proof::verify(&iss_proof_instance,
&reply.#iss_proof_ident, &self.iss_proof_sessid).is_err() {
return Err((CMZError::IssProofFailed, self));
}
};
}
let mut validity_proofs: HashMap<String, TokenStream> = HashMap::new();
for show_cred in proto_spec.show_creds.iter() {
let show_cred_id = format_ident!("show_cred_{}", show_cred.id);
let t_cred = format_ident!("t_show_cred_{}", show_cred.id);
let P_cred = format_ident!("P_show_cred_{}", show_cred.id);
let Q_cred = format_ident!("Q_show_cred_{}", show_cred.id);
let zQ_cred = format_ident!("zQ_show_cred_{}", show_cred.id);
let CQ_cred = format_ident!("CQ_show_cred_{}", show_cred.id);
let V_cred = format_ident!("V_show_cred_{}", show_cred.id);
let q_cred = format_ident!("q_show_cred_{}", show_cred.id);
let show_cred_type = &show_cred.cred_type;
let cred_str = show_cred.id.to_string();
handle_code_post_fill = quote! {
#handle_code_post_fill
if #show_cred_id.get_privkey().x.len() != #show_cred_type::num_attrs() {
return Err(CMZError::PrivkeyMissing(#cred_str));
}
if #show_cred_id.get_privkey().muCMZ != #use_muCMZ {
return Err(CMZError::WrongProtocol(#cred_str));
}
};
prepare_code = quote! {
#prepare_code
if #show_cred_id.get_pubkey().X.len() != #show_cred_type::num_attrs() {
return Err(CMZError::PubkeyMissing(#cred_str));
}
if #show_cred_id.get_pubkey().Xr.is_some() != #use_muCMZ {
return Err(CMZError::WrongProtocol(#cred_str));
}
};
prepare_code = quote! {
#prepare_code
let #t_cred = <Scalar as ff::Field>::random(&mut *rng);
let #P_cred = #t_cred * #show_cred_id.MAC.P;
let #Q_cred = #t_cred * #show_cred_id.MAC.Q;
let #zQ_cred = <Scalar as ff::Field>::random(&mut *rng);
let #CQ_cred = #Q_cred - bp.mulB(&#zQ_cred);
let mut #V_cred = bp.mulB(&#zQ_cred);
};
handle_code_post_fill = quote! {
#handle_code_post_fill
let #P_cred = request.#P_cred;
if bool::from(#P_cred.is_identity()) {
return Err(CMZError::CliProofFailed);
}
};
request_fields.push_point(&P_cred);
request_fields.push_point(&CQ_cred);
cli_proof_rand_scalars.push(zQ_cred.clone());
cli_proof_cind_points.push(P_cred.clone());
cli_proof_pub_points.push(V_cred.clone());
let mut V_statement = quote! {
#V_cred = #zQ_cred * #B_ident
};
let q_init = if use_muCMZ {
quote! { #show_cred_id.get_privkey().x0 + #show_cred_id.get_privkey().xr }
} else {
quote! { #show_cred_id.get_privkey().x0 }
};
handle_code_post_fill = quote! {
#handle_code_post_fill
let mut #V_cred = -request.#CQ_cred;
let mut #q_cred = #q_init;
};
for (attr, &spec) in show_cred.attrs.iter() {
let attr_str = attr.to_string();
let scoped_attr = format_ident!("show_{}attr_{}_{}", spec.abbr(), show_cred.id, attr);
let X_attr = format_ident!("X_{}", scoped_attr);
cli_proof_idmap.insert(
(show_cred.id.to_string(), attr.to_string()),
scoped_attr.clone(),
);
if spec == ShowSpec::Hide {
prepare_code = quote! {
#prepare_code
let #scoped_attr =
#show_cred_id.#attr.ok_or(CMZError::HideAttrMissing(#cred_str,
#attr_str))?;
};
let z_attr = format_ident!("z_{}", scoped_attr);
let C_attr = format_ident!("C_{}", scoped_attr);
request_fields.push_point(&C_attr);
prepare_code = quote! {
#prepare_code
let #z_attr = <Scalar as ff::Field>::random(&mut *rng);
let #C_attr = #scoped_attr * #P_cred + bp.mulA(&#z_attr);
let #X_attr = #show_cred_id.pubkey_X(#attr_str);
#V_cred += #z_attr * #X_attr;
};
handle_code_post_fill = quote! {
#handle_code_post_fill
let #C_attr = request.#C_attr;
let #X_attr = #show_cred_id.pubkey_X(#attr_str);
#V_cred += #show_cred_id.privkey_x(#attr_str)
* #C_attr;
};
cli_proof_priv_scalars.push(scoped_attr.clone());
cli_proof_rand_scalars.push(z_attr.clone());
cli_proof_pub_points.push(C_attr.clone());
cli_proof_cind_points.push(X_attr.clone());
cli_proof_statements.push(quote! {
#C_attr = #scoped_attr * #P_cred + #z_attr * #A_ident,
});
V_statement = quote! {
#V_statement + #z_attr * #X_attr
};
}
if spec == ShowSpec::Reveal {
request_fields.push_scalar(&scoped_attr);
prepare_code = quote! {
#prepare_code
let #scoped_attr =
#show_cred_id.#attr.ok_or(CMZError::RevealAttrMissing(#cred_str,
#attr_str))?;
};
handle_code_pre_fill = quote! {
#handle_code_pre_fill
let #scoped_attr = request.#scoped_attr;
#show_cred_id.#attr = Some(#scoped_attr);
};
handle_code_post_fill = quote! {
#handle_code_post_fill
#q_cred += #scoped_attr *
#show_cred_id.privkey_x(#attr_str);
};
cli_proof_pub_scalars.push(scoped_attr.clone());
}
if spec == ShowSpec::Implicit {
prepare_code = quote! {
#prepare_code
let #scoped_attr =
#show_cred_id.#attr.ok_or(CMZError::ImplicitAttrCliMissing(#cred_str,
#attr_str))?;
};
handle_code_post_fill = quote! {
#handle_code_post_fill
let #scoped_attr =
#show_cred_id.#attr.ok_or(CMZError::ImplicitAttrIssMissing(#cred_str,
#attr_str))?;
};
handle_code_post_fill = quote! {
#handle_code_post_fill
#q_cred += #scoped_attr *
#show_cred_id.privkey_x(#attr_str);
};
cli_proof_pub_scalars.push(scoped_attr.clone());
}
}
handle_code_post_fill = quote! {
#handle_code_post_fill
#V_cred += #q_cred * #P_cred;
};
if show_cred.valid_optional {
validity_proofs.insert(
cred_str,
quote! {
#V_statement
},
);
} else {
cli_proof_statements.push(quote! {
#V_statement,
});
}
}
cli_proof_const_points.push(A_ident.clone());
cli_proof_const_points.push(B_ident.clone());
for paramid in proto_spec.params.iter() {
let scoped_param = format_ident!("param_{}", paramid);
prepare_code = quote! {
#prepare_code
let #scoped_param = params.#paramid;
};
handle_code_post_fill = quote! {
#handle_code_post_fill
let #scoped_param = params.#paramid;
};
cli_proof_pub_scalars.push(scoped_param.clone());
cli_proof_idmap.insert(("".to_string(), paramid.to_string()), scoped_param.clone());
}
for paramid in proto_spec.point_params.iter() {
let scoped_param = format_ident!("param_{}", paramid);
prepare_code = quote! {
#prepare_code
let #scoped_param = params.#paramid;
};
handle_code_post_fill = quote! {
#handle_code_post_fill
let #scoped_param = params.#paramid;
};
cli_proof_pub_points.push(scoped_param.clone());
cli_proof_idmap.insert(("".to_string(), paramid.to_string()), scoped_param.clone());
}
let cli_proof_ident = format_ident!("cli_proof");
request_fields.push_bytevec(&cli_proof_ident);
let cli_instance_fields = cli_proof_pub_points
.iter()
.chain(cli_proof_const_points.iter())
.chain(cli_proof_cind_points.iter())
.chain(cli_proof_pub_scalars.iter());
let cli_witness_fields = cli_proof_rand_scalars
.iter()
.chain(cli_proof_priv_scalars.iter());
prepare_code = quote! {
#prepare_code
let cli_proof_instance = client_proof::Instance {
#(#cli_instance_fields,)*
};
let cli_proof_witness = client_proof::Witness {
#(#cli_witness_fields,)*
};
let #cli_proof_ident = client_proof::prove(&cli_proof_instance,
&cli_proof_witness, &cli_proof_sessid, rng).unwrap();
};
let iss_cli_instance_fields = cli_proof_pub_points
.iter()
.chain(cli_proof_const_points.iter())
.chain(cli_proof_cind_points.iter())
.chain(cli_proof_pub_scalars.iter());
handle_code_post_fill = quote! {
#handle_code_post_fill
let cli_proof_instance = client_proof::Instance {
#(#iss_cli_instance_fields,)*
};
if client_proof::verify(&cli_proof_instance,
&request.#cli_proof_ident, &cli_proof_sessid).is_err() {
return Err(CMZError::CliProofFailed);
}
};
let params_struct = if has_params {
let param_list = &proto_spec.params;
let point_param_list = &proto_spec.point_params;
quote! {
pub struct Params {
#( pub #param_list: Scalar, )*
#( pub #point_param_list: Point, )*
}
}
} else {
quote! {}
};
let client_state = {
let decls = clientstate_fields.field_decls();
quote! {
#[serde_as]
#[derive(Clone,Debug,serde::Serialize,serde::Deserialize)]
pub struct ClientState {
#decls
}
impl TryFrom<&[u8]> for ClientState {
type Error = postcard::Error;
fn try_from(bytes: &[u8]) -> postcard::Result<ClientState> {
postcard::from_bytes::<ClientState>(bytes)
}
}
impl From<&ClientState> for Vec<u8> {
fn from(req: &ClientState) -> Vec<u8> {
postcard::to_allocvec(req).unwrap()
}
}
impl ClientState {
pub fn as_bytes(&self) -> Vec<u8> {
self.into()
}
}
}
};
let messages = {
let reqdecls = request_fields.field_decls();
let repdecls = reply_fields.field_decls();
quote! {
#[serde_as]
#[derive(Clone,Debug,serde::Serialize,serde::Deserialize)]
pub struct Request {
#reqdecls
}
impl TryFrom<&[u8]> for Request {
type Error = postcard::Error;
fn try_from(bytes: &[u8]) -> postcard::Result<Request> {
postcard::from_bytes::<Request>(bytes)
}
}
impl From<&Request> for Vec<u8> {
fn from(req: &Request) -> Vec<u8> {
postcard::to_allocvec(req).unwrap()
}
}
impl Request {
pub fn as_bytes(&self) -> Vec<u8> {
self.into()
}
}
#[serde_as]
#[derive(Clone,Debug,serde::Serialize,serde::Deserialize)]
pub struct Reply {
#repdecls
}
impl TryFrom<&[u8]> for Reply {
type Error = postcard::Error;
fn try_from(bytes: &[u8]) -> postcard::Result<Reply> {
postcard::from_bytes::<Reply>(bytes)
}
}
impl From<&Reply> for Vec<u8> {
fn from(rep: &Reply) -> Vec<u8> {
postcard::to_allocvec(rep).unwrap()
}
}
impl Reply {
pub fn as_bytes(&self) -> Vec<u8> {
self.into()
}
}
}
};
struct StatementScoper<'a> {
idmap: &'a HashMap<(String, String), Ident>,
validity_proofs: &'a HashMap<String, TokenStream>,
}
impl<'a> VisitMut for StatementScoper<'a> {
fn visit_expr_mut(&mut self, node: &mut Expr) {
if let Expr::Field(exfld) = node {
let base = *exfld.base.clone();
if let Expr::Path(basepath) = base {
if let Member::Named(attrid) = &exfld.member {
if let Some(credid) = basepath.path.get_ident() {
if let Some(scopedid) =
self.idmap.get(&(credid.to_string(), attrid.to_string()))
{
*node = parse_quote! { #scopedid };
return;
}
}
}
}
}
if let Expr::Path(expath) = node {
if let Some(id) = expath.path.get_ident() {
if let Some(scopedparam) = self.idmap.get(&("".to_string(), id.to_string())) {
*node = parse_quote! { #scopedparam };
return;
}
}
}
if let Expr::Call(excall) = node {
let base = *excall.func.clone();
if let Expr::Path(basepath) = base {
if let Some(id) = basepath.path.get_ident() {
if *id == "valid" && excall.args.len() == 1 {
let mut validity_statement = quote! {};
let argexpr = excall.args.first().unwrap();
if let Expr::Path(argpath) = argexpr {
if let Some(credid) = argpath.path.get_ident() {
let credstr = credid.to_string();
match self.validity_proofs.get(&credstr) {
Some(tokens) => {
validity_statement = tokens.clone();
},
None => panic!("{} is not a shown credential with optional validity proof", credstr),
}
}
}
*node = parse_quote! { #validity_statement };
return;
}
}
}
}
visit_mut::visit_expr_mut(self, node);
}
}
let mut statement_scoper = StatementScoper {
idmap: &cli_proof_idmap,
validity_proofs: &validity_proofs,
};
let mut cli_proof_scoped_statements = proto_spec.statements.clone();
cli_proof_scoped_statements
.iter_mut()
.for_each(|expr| statement_scoper.visit_expr_mut(expr));
let cli_sigma_compiler_macro = if emit_client && emit_issuer {
quote! { sigma_compiler }
} else if emit_client {
quote! { sigma_compiler_prover }
} else {
quote! { sigma_compiler_verifier }
};
let cli_proof = {
quote! {
#cli_sigma_compiler_macro! { client_proof<Point>,
(#(rand #cli_proof_rand_scalars,)*
#(#cli_proof_priv_scalars,)*
#(pub #cli_proof_pub_scalars,)*),
(#(cind #cli_proof_cind_points,)*
#(#cli_proof_pub_points,)*
#(cind const #cli_proof_const_points,)*),
#(#cli_proof_scoped_statements,)*
#(#cli_proof_statements)*
}
}
};
let iss_sigma_compiler_macro = if emit_client && emit_issuer {
quote! { sigma_compiler }
} else if emit_issuer {
quote! { sigma_compiler_prover }
} else {
quote! { sigma_compiler_verifier }
};
let iss_proof = {
quote! {
#iss_sigma_compiler_macro! { issuer_proof<Point>,
(#(rand #iss_proof_rand_scalars,)*
#(#iss_proof_priv_scalars,)*
#(pub #iss_proof_pub_scalars,)*),
(#(#iss_proof_pub_points,)*
#(cind const #iss_proof_const_points,)*),
#(#iss_proof_statements)*
}
}
};
let client_show_args = proto_spec.show_creds.iter().map(|c| {
let id = format_ident!("show_cred_{}", c.id);
let cred_type = &c.cred_type;
quote! { #id: &#cred_type, }
});
let client_issue_args = proto_spec.issue_creds.iter().map(|c| {
let id = format_ident!("iss_cred_{}", c.id);
let cred_type = &c.cred_type;
quote! { #id: #cred_type, }
});
let client_params_arg = if has_params {
quote! { params: &Params, }
} else {
quote! {}
};
let client_func = {
let reqf = request_fields.field_iter();
let csf = clientstate_fields.field_iter();
quote! {
pub fn prepare(rng: &mut (impl CryptoRng + RngCore),
session_id: &[u8],
#(#client_show_args)* #(#client_issue_args)* #client_params_arg)
-> Result<(Request, ClientState),CMZError> {
let bp = cmz_basepoints::<Point>();
let mut cli_proof_sessid: Vec<u8> = Vec::new();
cli_proof_sessid.extend(b"cli_");
cli_proof_sessid.extend(session_id);
let mut iss_proof_sessid: Vec<u8> = Vec::new();
iss_proof_sessid.extend(b"iss_");
iss_proof_sessid.extend(session_id);
#prepare_code
Ok((Request{#(#reqf,)*}, ClientState{#(#csf,)*}))
}
}
};
let issuer_func = {
let cred_decls = proto_spec
.show_creds
.iter()
.map(|c| {
let id = format_ident!("show_cred_{}", c.id);
let cred_type = &c.cred_type;
quote! { let mut #id = #cred_type::default(); }
})
.chain(proto_spec.issue_creds.iter().map(|c| {
let id = format_ident!("iss_cred_{}", c.id);
let cred_type = &c.cred_type;
quote! { let mut #id = #cred_type::default(); }
}));
let cred_rettypes = proto_spec
.show_creds
.iter()
.map(|c| {
let cred_type = &c.cred_type;
quote! { #cred_type }
})
.chain(proto_spec.issue_creds.iter().map(|c| {
let cred_type = &c.cred_type;
quote! { #cred_type }
}));
let rettype = match tot_num_creds {
0 => quote! { Result<Reply,CMZError> },
1 => quote! { Result<(Reply, #(#cred_rettypes)*),CMZError> },
_ => quote! { Result<(Reply, (#(#cred_rettypes),*)),CMZError> },
};
let cred_retvals = proto_spec
.show_creds
.iter()
.map(|c| {
let id = format_ident!("show_cred_{}", c.id);
quote! { #id }
})
.chain(proto_spec.issue_creds.iter().map(|c| {
let id = format_ident!("iss_cred_{}", c.id);
quote! { #id }
}));
let fill_creds_args = proto_spec
.show_creds
.iter()
.map(|c| {
let cred_type = &c.cred_type;
quote! { &mut #cred_type, }
})
.chain(proto_spec.issue_creds.iter().map(|c| {
let cred_type = &c.cred_type;
quote! { &mut #cred_type, }
}));
let fill_creds_params = proto_spec
.show_creds
.iter()
.map(|c| {
let id = format_ident!("show_cred_{}", c.id);
quote! { &mut #id, }
})
.chain(proto_spec.issue_creds.iter().map(|c| {
let id = format_ident!("iss_cred_{}", c.id);
quote! { &mut #id, }
}));
let fill_creds_params_ret = if has_params {
quote! { Params }
} else {
quote! { () }
};
let fill_creds_assign = if has_params {
quote! { let params = }
} else {
quote! {}
};
let authorize_args = proto_spec
.show_creds
.iter()
.map(|c| {
let cred_type = &c.cred_type;
quote! { &#cred_type, }
})
.chain(proto_spec.issue_creds.iter().map(|c| {
let cred_type = &c.cred_type;
quote! { &#cred_type, }
}));
let authorize_params = proto_spec
.show_creds
.iter()
.map(|c| {
let id = format_ident!("show_cred_{}", c.id);
quote! { &#id, }
})
.chain(proto_spec.issue_creds.iter().map(|c| {
let id = format_ident!("iss_cred_{}", c.id);
quote! { &#id, }
}));
let repf = reply_fields.field_iter();
let retval = match tot_num_creds {
0 => quote! { Ok(Reply{#(#repf,)*}) },
1 => quote! { Ok((Reply{#(#repf,)*}, #(#cred_retvals)*)) },
_ => quote! { Ok((Reply{#(#repf,)*}, (#(#cred_retvals),*))) },
};
quote! {
pub fn handle<F,A>(rng: &mut (impl CryptoRng + RngCore),
session_id: &[u8],
request: Request, fill_creds: F, authorize: A)
-> #rettype
where
F: FnOnce(#(#fill_creds_args)*) ->
Result<#fill_creds_params_ret, CMZError>,
A: FnOnce(#(#authorize_args)*) ->
Result<(),CMZError>
{
let bp = cmz_basepoints::<Point>();
let mut cli_proof_sessid: Vec<u8> = Vec::new();
cli_proof_sessid.extend(b"cli_");
cli_proof_sessid.extend(session_id);
let mut iss_proof_sessid: Vec<u8> = Vec::new();
iss_proof_sessid.extend(b"iss_");
iss_proof_sessid.extend(session_id);
#(#cred_decls)*
#handle_code_pre_fill
#fill_creds_assign fill_creds(#(#fill_creds_params)*)?;
#handle_code_post_fill
authorize(#(#authorize_params)*)?;
#handle_code_post_auth
#retval
}
}
};
let clientstate_finalize_func = {
let cred_decls = proto_spec.issue_creds.iter().map(|c| {
let id = format_ident!("iss_cred_{}", c.id);
let cred_type = &c.cred_type;
quote! { let mut #id = #cred_type::default(); }
});
let cred_rettypes = proto_spec.issue_creds.iter().map(|c| {
let cred_type = &c.cred_type;
quote! { #cred_type }
});
let rettype = match proto_spec.issue_creds.len() {
0 => quote! { Result<(),(CMZError,Self)> },
1 => quote! { Result<#(#cred_rettypes)*,(CMZError,Self)> },
_ => quote! { Result<(#(#cred_rettypes),*),(CMZError,Self)> },
};
let cred_retvals = proto_spec.issue_creds.iter().map(|c| {
let id = format_ident!("iss_cred_{}", c.id);
quote! { #id }
});
let retval = match proto_spec.issue_creds.len() {
0 => quote! { Ok(()) },
1 => quote! { Ok(#(#cred_retvals)*) },
_ => quote! { Ok((#(#cred_retvals),*)) },
};
quote! {
impl ClientState {
pub fn finalize(
self,
reply: Reply,
) -> #rettype {
let bp = cmz_basepoints::<Point>();
#(#cred_decls)*
#finalize_code
#retval
}
}
}
};
let client_side = if emit_client {
quote! { #client_state #client_func #clientstate_finalize_func }
} else {
quote! {}
};
let issuer_side = if emit_issuer {
issuer_func
} else {
quote! {}
};
quote! {
#[allow(non_snake_case)]
pub mod #proto_name {
use super::*;
use sigma_compiler::*;
use group::GroupEncoding;
#group_types
#params_struct
#messages
#cli_proof
#iss_proof
#client_side
#issuer_side
}
}
}