Browse Source

Make it possible to specify the mathematical group to use for a CMZ credential

/** The CMZ macro for declaring CMZ credentials.

Use this macro to declare a CMZ credential struct type.

`CMZ!{ Name<Group>: attr1, attr2, attr3 }`

will declare a struct type called `Name`, containing one field for each
of the listed attributes.  The attribute fields will be of type
`Option<Scalar>`.  It will also automatically add a field called `MAC`
of type `CMZMac`, and an implementation (via the `CMZCred` derive) of
the `CMZCredential` trait.  The mathematical group used (the field for
the values of the attributes and the group elements for the commitments
and MAC components) is Group (which must satisfy the group::Group
trait).  If "<Group>" is omitted, the macro will default to using a
group called "G", which you can define, for example, as:

use curve25519_dalek::ristretto::RistrettoPoint as G;

or:

use curve25519_dalek::ristretto::RistrettoPoint;
type G = RistrettoPoint;

*/
Ian Goldberg 6 months ago
parent
commit
adb4472396
4 changed files with 71 additions and 24 deletions
  1. 3 1
      Cargo.toml
  2. 1 0
      cmzcred_derive/Cargo.toml
  3. 22 8
      cmzcred_derive/src/lib.rs
  4. 45 15
      src/lib.rs

+ 3 - 1
Cargo.toml

@@ -5,4 +5,6 @@ edition = "2021"
 
 [dependencies]
 cmzcred_derive = { path = "cmzcred_derive" }
-curve25519-dalek = "4"
+curve25519-dalek = { version = "4", features = [ "group" ] }
+ff = "0.13"
+group = "0.13"

+ 1 - 0
cmzcred_derive/Cargo.toml

@@ -9,3 +9,4 @@ proc-macro = true
 [dependencies]
 syn = { version = "2.0", features = ["extra-traits"] }
 quote = "1.0"
+darling = "0.20"

+ 22 - 8
cmzcred_derive/src/lib.rs

@@ -13,9 +13,10 @@ the CMZCredential trait for the declared credential.
 
 use proc_macro::TokenStream;
 use quote::quote;
-use syn::{Data,DataStruct,Fields,FieldsNamed,Ident};
+use syn::{Data,DataStruct,DeriveInput,Fields,FieldsNamed,Ident};
+use darling::FromDeriveInput;
 
-fn impl_cmzcred_derive(ast: &syn::DeriveInput) -> TokenStream {
+fn impl_cmzcred_derive(ast: &syn::DeriveInput, group_ident: &Ident) -> TokenStream {
     // Ensure that CMZCred is derived on a struct and not something else
     // (like an enum)
     let Data::Struct(DataStruct{struct_token: _,
@@ -45,7 +46,10 @@ fn impl_cmzcred_derive(ast: &syn::DeriveInput) -> TokenStream {
 
     // Output the CMZCredential trail implementation
     let gen = quote! {
-        impl CMZCredential for #name {
+        impl CMZCredential<#group_ident> for #name {
+            type Scalar = <#group_ident as Group>::Scalar;
+            type Point = #group_ident;
+
             fn attrs() -> Vec<&'static str> {
                 vec![
                     #( #attrs, )*
@@ -56,14 +60,14 @@ fn impl_cmzcred_derive(ast: &syn::DeriveInput) -> TokenStream {
                 return #num_attrs;
             }
 
-            fn attr(&self, attrname: &str) -> &Option<Scalar> {
+            fn attr(&self, attrname: &str) -> &Option<Self::Scalar> {
                 match attrname {
                     #( #attrs => &self.#idents, )*
                     _ => panic!(#errmsg),
                 }
             }
 
-            fn attr_mut(&mut self, attrname: &str) -> &mut Option<Scalar> {
+            fn attr_mut(&mut self, attrname: &str) -> &mut Option<Self::Scalar> {
                 match attrname {
                     #( #attrs => &mut self.#idents, )*
                     _ => panic!(#errmsg),
@@ -74,12 +78,22 @@ fn impl_cmzcred_derive(ast: &syn::DeriveInput) -> TokenStream {
     gen.into()
 }
 
-#[proc_macro_derive(CMZCred)]
+#[derive(FromDeriveInput)]
+#[darling(attributes(cmzcred_group))]
+struct GroupIdent {
+    group: Ident,
+}
+
+#[proc_macro_derive(CMZCred, attributes(cmzcred_group))]
 pub fn cmzcred_derive(input: TokenStream) -> TokenStream {
     // Construct a representation of Rust code as a syntax tree
     // that we can manipulate
-    let ast = syn::parse(input).unwrap();
+    let ast: DeriveInput = syn::parse(input).unwrap();
+
+    // Get the cmzcred_group(group = G) attribute
+    let group_ident = GroupIdent::from_derive_input(&ast)
+        .expect("missing group parameter to cmzcred_group attribute");
 
     // Build the trait implementation
-    impl_cmzcred_derive(&ast)
+    impl_cmzcred_derive(&ast, &group_ident.group)
 }

+ 45 - 15
src/lib.rs

@@ -2,18 +2,24 @@
 // lowercase letters
 #![allow(non_snake_case)]
 
-use curve25519_dalek::ristretto::RistrettoPoint as Point;
-use curve25519_dalek::scalar::Scalar;
+use ff::PrimeField;
+use group::Group;
 
 /// The CMZMac struct represents a MAC on a CMZ credential.
 #[derive(Copy, Clone, Debug, Default)]
-pub struct CMZMac {
-    pub P: Point,
-    pub Q: Point,
+pub struct CMZMac<G: Group> {
+    pub P: G,
+    pub Q: G,
 }
 
-/// The CMZCredential trail implemented by all CMZ credential struct types.
-pub trait CMZCredential {
+/// The CMZCredential trait implemented by all CMZ credential struct types.
+pub trait CMZCredential<G: Group> {
+    /// The type of attributes for this credential
+    type Scalar: PrimeField;
+
+    /// The type of the coordinates of the MAC for this credential
+    type Point: Group;
+
     /// Produce a vector of strings containing the names of the
     /// attributes of this credential.  (The MAC is not included.)
     fn attrs() -> Vec<&'static str>;
@@ -23,35 +29,57 @@ pub trait CMZCredential {
 
     /// Get a reference to one of the attributes, specified by name as a
     /// string.
-    fn attr(&self, name: &str) -> &Option<Scalar>;
+    fn attr(&self, name: &str) -> &Option<Self::Scalar>;
 
     /// Get a mutable reference to one of the attributes, specified by
     /// name as a string.
-    fn attr_mut(&mut self, name: &str) -> &mut Option<Scalar>;
+    fn attr_mut(&mut self, name: &str) -> &mut Option<Self::Scalar>;
 }
 
 /** The CMZ macro for declaring CMZ credentials.
 
 Use this macro to declare a CMZ credential struct type.
 
-`CMZ!{ Name: attr1, attr2, attr3 }`
+`CMZ!{ Name<Group>: attr1, attr2, attr3 }`
 
 will declare a struct type called `Name`, containing one field for each
 of the listed attributes.  The attribute fields will be of type
 `Option<Scalar>`.  It will also automatically add a field called `MAC`
 of type `CMZMac`, and an implementation (via the `CMZCred` derive) of
-the `CMZCredential` trait.
+the `CMZCredential` trait.  The mathematical group used (the field for
+the values of the attributes and the group elements for the commitments
+and MAC components) is Group (which must satisfy the group::Group
+trait).  If "<Group>" is omitted, the macro will default to using a
+group called "G", which you can define, for example, as:
+
+use curve25519_dalek::ristretto::RistrettoPoint as G;
+
+or:
+
+use curve25519_dalek::ristretto::RistrettoPoint;
+type G = RistrettoPoint;
 
 */
 #[macro_export]
 macro_rules! CMZ {
+    ( $name: ident < $G: ident > : $( $id: ident ),+ ) => {
+        #[derive(CMZCred,Copy,Clone,Debug,Default)]
+        #[cmzcred_group(group = $G)]
+        pub struct $name {
+        $(
+            pub $id: Option<<$G as Group>::Scalar>,
+        )+
+            pub MAC: CMZMac<$G>,
+        }
+    };
     ( $name: ident : $( $id: ident ),+ ) => {
         #[derive(CMZCred,Copy,Clone,Debug,Default)]
+        #[cmzcred_group(group = G)]
         pub struct $name {
         $(
-            pub $id: Option<Scalar>,
+            pub $id: Option<<G as Group>::Scalar>,
         )+
-            pub MAC: CMZMac,
+            pub MAC: CMZMac<G>,
         }
     };
 }
@@ -63,6 +91,8 @@ mod tests {
 
     #[test]
     fn lox_credential_test() {
+        use curve25519_dalek::ristretto::RistrettoPoint as G;
+
         CMZ! { Lox:
             id,
             bucket,
@@ -78,13 +108,13 @@ mod tests {
 
         println!("{:#?}", L);
 
-        L.bucket = Some(Scalar::ONE);
+        L.bucket = Some(<G as Group>::Scalar::ONE);
 
         println!("{:#?}", L);
 
         println!("{:#?}", L.attr("bucket"));
 
-        *L.attr_mut("id") = Some(Scalar::ONE);
+        *L.attr_mut("id") = Some(<G as Group>::Scalar::ONE);
 
         println!("{:#?}", L);
     }