소스 검색

Parser for the CMZProtocol macros

The format is:

let proto = CMZProtocol! { proto_name,
 [ A: Cred {
     attr1: H,
     attr2: R,
   },
   B: Cred2 {
     attr3: H,
     attr4: I,
   } ],
 C: Cred3 {
   attr5: J,
   attr6: R,
   attr7: H,
   attr8: I,
   attr9: S,
 },
 A.attr1 == B.attr3,
 A.attr1 == C.attr7,
};

The parameters are:
- an identifier for the protocol
- a list of zero or more specifications for credentials that will be shown
- a list of zero or more specifications for credentials that will be issued
- zero or more statements relating the attributes in the credentials

Each credential specification list can be:
- empty
- a single credential specification
- a square-bracketed list of credential specifications

Each credential specification is:
- an identifier for the credential
- a type for the credential, previously defined with the CMZ! macro
- a braced list of the attributes of the credential (as defined in
 the CMZ! macro), annotated with the attribute specification

An attribute specification for a credential to be shown is one of:
- H (hide)
- R (reveal)
- I (implicit)

An attribute specification for a credential to be issued is one of:
- H (hide)
- R (reveal)
- I (implicit)
- S (set by issuer)
- J (joint creation)
Ian Goldberg 6 달 전
부모
커밋
7da83329b9
2개의 변경된 파일272개의 추가작업 그리고 2개의 파일을 삭제
  1. 271 1
      cmzcred_derive/src/lib.rs
  2. 1 1
      src/lib.rs

+ 271 - 1
cmzcred_derive/src/lib.rs

@@ -1,3 +1,6 @@
+// We want the macros like CMZProto to be camel case
+#![allow(non_snake_case)]
+
 /*! The implementation of the CMZCred derive.
 
 This derive should not be explicitly used by a programmer using a CMZ
@@ -14,7 +17,13 @@ the CMZCredential trait for the declared credential.
 use darling::FromDeriveInput;
 use proc_macro::TokenStream;
 use quote::quote;
-use syn::{Data, DataStruct, DeriveInput, Fields, FieldsNamed, Ident, Visibility};
+use std::collections::HashMap;
+use syn::parse::{Parse, ParseStream, Result};
+use syn::punctuated::Punctuated;
+use syn::{
+    braced, bracketed, parse_macro_input, token, Data, DataStruct, DeriveInput, Expr, Fields,
+    FieldsNamed, Ident, Token, Visibility,
+};
 
 fn impl_cmzcred_derive(ast: &syn::DeriveInput, group_ident: &Ident) -> TokenStream {
     // Ensure that CMZCred is derived on a struct and not something else
@@ -145,3 +154,264 @@ pub fn cmzcred_derive(input: TokenStream) -> TokenStream {
     // Build the trait implementation
     impl_cmzcred_derive(&ast, &group_ident.group)
 }
+
+/** The CMZ Protocol creation macros.
+
+   The format is:
+
+   let proto = CMZProtocol! { proto_name,
+     [ A: Cred {
+         attr1: H,
+         attr2: R,
+       },
+       B: Cred2 {
+         attr3: H,
+         attr4: I,
+       } ],
+     C: Cred3 {
+       attr5: J,
+       attr6: R,
+       attr7: H,
+       attr8: I,
+       attr9: S,
+     },
+     A.attr1 == B.attr3,
+     A.attr1 == C.attr7,
+   };
+
+   The parameters are:
+   - an identifier for the protocol
+   - a list of zero or more specifications for credentials that will be shown
+   - a list of zero or more specifications for credentials that will be issued
+   - zero or more statements relating the attributes in the credentials
+
+   Each credential specification list can be:
+   - empty
+   - a single credential specification
+   - a square-bracketed list of credential specifications
+
+   Each credential specification is:
+   - an identifier for the credential
+   - a type for the credential, previously defined with the CMZ! macro
+   - a braced list of the attributes of the credential (as defined in
+     the CMZ! macro), annotated with the attribute specification
+
+   An attribute specification for a credential to be shown is one of:
+   - H (hide)
+   - R (reveal)
+   - I (implicit)
+
+   An attribute specification for a credential to be issued is one of:
+   - H (hide)
+   - R (reveal)
+   - I (implicit)
+   - S (set by issuer)
+   - J (joint creation)
+*/
+
+// The possible attribute specifications for a credential to be shown
+#[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")),
+        }
+    }
+}
+
+// The possible attribute specifications for a credential to be issued
+#[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")),
+        }
+    }
+}
+
+// An attribute specification like "attr1: Reveal"
+#[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 })
+    }
+}
+
+// A specification of a credential, either to be shown or issued
+#[derive(Debug)]
+struct CredSpec<ShowOrIssue: Parse> {
+    id: Ident,
+    cred_type: Ident,
+    attrs: HashMap<String, ShowOrIssue>,
+}
+
+impl<ShowOrIssue: Parse + Copy> Parse for CredSpec<ShowOrIssue> {
+    fn parse(input: ParseStream) -> Result<Self> {
+        let id: Ident = input.parse()?;
+        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: HashMap<String, ShowOrIssue> = HashMap::new();
+        for attrspec in attrspecs.iter() {
+            attrs.insert(attrspec.attr.to_string(), attrspec.spec);
+        }
+        Ok(Self {
+            id,
+            cred_type,
+            attrs,
+        })
+    }
+}
+
+// A vector of credential specifications, which could be empty, a single
+// credential specification, or a bracketed list of credential
+// specifications.  We need a newtype here and not just a Vec so that we
+// can implement the Parse trait for it.
+struct CredSpecVec<ShowOrIssue: Parse>(Vec<CredSpec<ShowOrIssue>>);
+
+impl<ShowOrIssue: Parse + Copy> Parse for CredSpecVec<ShowOrIssue> {
+    fn parse(input: ParseStream) -> Result<Self> {
+        let mut specvec: Vec<CredSpec<ShowOrIssue>> = Vec::new();
+        if input.peek(Token![,]) {
+            // The list is empty
+        } else if input.peek(token::Bracket) {
+            let content;
+            bracketed!(content in input);
+            let specs: Punctuated<CredSpec<ShowOrIssue>, Token![,]> =
+                content.parse_terminated(CredSpec::<ShowOrIssue>::parse, Token![,])?;
+            for spec in specs.into_iter() {
+                specvec.push(spec);
+            }
+        } else {
+            let spec: CredSpec<ShowOrIssue> = input.parse()?;
+            specvec.push(spec);
+        }
+
+        Ok(Self(specvec))
+    }
+}
+
+// A protocol specification, following the syntax described above.
+#[derive(Debug)]
+struct ProtoSpec {
+    proto_name: Ident,
+    show_creds: Vec<CredSpec<ShowSpec>>,
+    issue_creds: Vec<CredSpec<IssueSpec>>,
+    statements: Vec<Expr>,
+}
+
+impl Parse for ProtoSpec {
+    fn parse(input: ParseStream) -> Result<Self> {
+        let proto_name: Ident = input.parse()?;
+        input.parse::<Token![,]>()?;
+        let showvec: CredSpecVec<ShowSpec> = input.parse()?;
+        input.parse::<Token![,]>()?;
+        let issuevec: CredSpecVec<IssueSpec> = input.parse()?;
+        input.parse::<Token![,]>()?;
+        let statementpunc: Punctuated<Expr, Token![,]> =
+            input.parse_terminated(Expr::parse, Token![,])?;
+        let mut statements: Vec<Expr> = Vec::new();
+        for statement in statementpunc.into_iter() {
+            statements.push(statement);
+        }
+
+        Ok(ProtoSpec {
+            proto_name,
+            show_creds: showvec.0,
+            issue_creds: issuevec.0,
+            statements,
+        })
+    }
+}
+
+// This is where the main work is done.  The six macros in the
+// CMZProtocol macro family (below) all call this function, with
+// different values for the bools.
+fn protocol_macro(
+    input: TokenStream,
+    use_muCMZ: bool,
+    emit_client: bool,
+    emit_issuer: bool,
+) -> TokenStream {
+    let proto_spec: ProtoSpec = parse_macro_input!(input as ProtoSpec);
+    // For now, just return a string representation of the parsed
+    // protcol spec.
+    let s = format!("{proto_spec:#?}");
+    quote! {
+        { #s }
+    }
+    .into()
+}
+
+/** There are six variants of the CMZProtocol macro.  The ones starting
+  with "CMZ" create protocol implementations using the original CMZ
+  issuing protocol.  The ones starting with "muCMZ" using the more
+  efficient µCMZ protocol.  The ones with "Cli" only create the code
+  for the client side of the protocol.  The ones with "Iss" only create
+  the code for the issuer side of the protocol.  (The ones without
+  either create the code for both sides of the protocol.)
+*/
+#[proc_macro]
+pub fn CMZProtocol(input: TokenStream) -> TokenStream {
+    protocol_macro(input, false, true, true)
+}
+
+#[proc_macro]
+pub fn CMZCliProtocol(input: TokenStream) -> TokenStream {
+    protocol_macro(input, false, true, false)
+}
+
+#[proc_macro]
+pub fn CMZIssProtocol(input: TokenStream) -> TokenStream {
+    protocol_macro(input, false, false, true)
+}
+
+#[proc_macro]
+pub fn muCMZProtocol(input: TokenStream) -> TokenStream {
+    protocol_macro(input, true, true, true)
+}
+
+#[proc_macro]
+pub fn muCMZCliProtocol(input: TokenStream) -> TokenStream {
+    protocol_macro(input, true, true, false)
+}
+
+#[proc_macro]
+pub fn muCMZIssProtocol(input: TokenStream) -> TokenStream {
+    protocol_macro(input, true, false, true)
+}

+ 1 - 1
src/lib.rs

@@ -2,7 +2,7 @@
 // lowercase letters
 #![allow(non_snake_case)]
 
-pub use cmzcred_derive::CMZCred;
+pub use cmzcred_derive::*;
 use core::any::Any;
 use ff::PrimeField;
 use generic_static::StaticTypeMap;