Przeglądaj źródła

Initial implementation of the CMZ declarative macro and CMZCred derive procedural macro

Ian Goldberg 6 miesięcy temu
commit
3dccb6e47a
4 zmienionych plików z 195 dodań i 0 usunięć
  1. 8 0
      Cargo.toml
  2. 11 0
      cmzcred_derive/Cargo.toml
  3. 85 0
      cmzcred_derive/src/lib.rs
  4. 91 0
      src/lib.rs

+ 8 - 0
Cargo.toml

@@ -0,0 +1,8 @@
+[package]
+name = "cmz"
+version = "0.1.0"
+edition = "2021"
+
+[dependencies]
+cmzcred_derive = { path = "cmzcred_derive" }
+curve25519-dalek = "4"

+ 11 - 0
cmzcred_derive/Cargo.toml

@@ -0,0 +1,11 @@
+[package]
+name = "cmzcred_derive"
+version = "0.1.0"
+edition = "2021"
+
+[lib]
+proc-macro = true
+
+[dependencies]
+syn = { version = "2.0", features = ["extra-traits"] }
+quote = "1.0"

+ 85 - 0
cmzcred_derive/src/lib.rs

@@ -0,0 +1,85 @@
+/*! The implementation of the CMZCred derive.
+
+This derive should not be explicitly used by a programmer using a CMZ
+credential.  Instead, a CMZ credential should be declared with the
+
+`CMZ!{ Name: attr1, attr2, attr3 }`
+
+macro.  That macro will internally expand to a struct annotated with
+this CMZCred derive.  This derive will output the implementation of
+the CMZCredential trait for the declared credential.
+
+*/
+
+use proc_macro::TokenStream;
+use quote::quote;
+use syn::{Data,DataStruct,Fields,FieldsNamed,Ident};
+
+fn impl_cmzcred_derive(ast: &syn::DeriveInput) -> TokenStream {
+    // Ensure that CMZCred is derived on a struct and not something else
+    // (like an enum)
+    let Data::Struct(DataStruct{struct_token: _,
+        fields: Fields::Named(FieldsNamed{brace_token: _, ref named}),
+        semi_token: _}) = ast.data else {
+        panic!("CMZCred derived on a non-struct");
+    };
+    // attrs and idents are each vectors of the names of the attributes
+    // of the credential (not including the MAC).  attrs stores the
+    // names as Strings, while idents stores them as Idents.
+    let mut attrs = Vec::<String>::new();
+    let mut idents = Vec::<&Ident>::new();
+    for n in named {
+        let Some(ref ident) = n.ident else {
+            panic!("Missing attribute name in CMZCred");
+        };
+        let id_str = ident.to_string();
+        if id_str != String::from("MAC") {
+            attrs.push(id_str);
+            idents.push(ident);
+        }
+    }
+    let num_attrs = attrs.len();
+    let name = &ast.ident;
+    let errmsg = format!("Invalid attribute name for {} CMZ credential",
+        name);
+
+    // Output the CMZCredential trail implementation
+    let gen = quote! {
+        impl CMZCredential for #name {
+            fn attrs() -> Vec<&'static str> {
+                vec![
+                    #( #attrs, )*
+                ]
+            }
+
+            fn num_attrs() -> usize {
+                return #num_attrs;
+            }
+
+            fn attr(&self, attrname: &str) -> &Option<Scalar> {
+                match attrname {
+                    #( #attrs => &self.#idents, )*
+                    _ => panic!(#errmsg),
+                }
+            }
+
+            fn attr_mut(&mut self, attrname: &str) -> &mut Option<Scalar> {
+                match attrname {
+                    #( #attrs => &mut self.#idents, )*
+                    _ => panic!(#errmsg),
+                }
+            }
+        }
+    };
+    gen.into()
+}
+
+#[proc_macro_derive(CMZCred)]
+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();
+
+    // Build the trait implementation
+    impl_cmzcred_derive(&ast)
+}

+ 91 - 0
src/lib.rs

@@ -0,0 +1,91 @@
+// We really want points to be capital letters and scalars to be
+// lowercase letters
+#![allow(non_snake_case)]
+
+use curve25519_dalek::ristretto::RistrettoPoint as Point;
+use curve25519_dalek::scalar::Scalar;
+
+/// The CMZMac struct represents a MAC on a CMZ credential.
+#[derive(Copy, Clone, Debug, Default)]
+pub struct CMZMac {
+    pub P: Point,
+    pub Q: Point,
+}
+
+/// The CMZCredential trail implemented by all CMZ credential struct types.
+pub trait CMZCredential {
+    /// Produce a vector of strings containing the names of the
+    /// attributes of this credential.  (The MAC is not included.)
+    fn attrs() -> Vec<&'static str>;
+
+    /// The number of attributes in this credential
+    fn num_attrs() -> usize;
+
+    /// Get a reference to one of the attributes, specified by name as a
+    /// string.
+    fn attr(&self, name: &str) -> &Option<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>;
+}
+
+/** The CMZ macro for declaring CMZ credentials.
+
+Use this macro to declare a CMZ credential struct type.
+
+`CMZ!{ Name: 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.
+
+*/
+#[macro_export]
+macro_rules! CMZ {
+    ( $name: ident : $( $id: ident ),+ ) => {
+        #[derive(CMZCred,Copy,Clone,Debug,Default)]
+        pub struct $name {
+        $(
+            pub $id: Option<Scalar>,
+        )+
+            pub MAC: CMZMac,
+        }
+    };
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+    use cmzcred_derive::CMZCred;
+
+    #[test]
+    fn lox_credential_test() {
+        CMZ! { Lox:
+            id,
+            bucket,
+            trust_level,
+            level_since,
+            invites_remaining,
+            blockages
+        }
+
+        println!("{:#?}", Lox::attrs());
+
+        let mut L: Lox = Lox::default();
+
+        println!("{:#?}", L);
+
+        L.bucket = Some(Scalar::ONE);
+
+        println!("{:#?}", L);
+
+        println!("{:#?}", L.attr("bucket"));
+
+        *L.attr_mut("id") = Some(Scalar::ONE);
+
+        println!("{:#?}", L);
+    }
+}