Skip to main content

miden_crypto_derive/
lib.rs

1use proc_macro::TokenStream;
2use quote::quote;
3use syn::{Data, DeriveInput, Fields, PathArguments, Type, parse_macro_input};
4
5// SILENT DEBUG MACRO
6// ================================================================================================
7
8/// Derives a Debug implementation that elides secret values.
9///
10/// This macro generates a Debug implementation that outputs `<elided secret for TypeName>`
11/// instead of the actual field values, preventing accidental leakage of sensitive data
12/// in logs, error messages, or debug output.
13///
14/// # Example
15///
16/// ```ignore
17/// #[derive(SilentDebug)]
18/// pub struct SecretKey {
19///     inner: [u8; 32],
20/// }
21///
22/// let sk = SecretKey { inner: [0u8; 32] };
23/// assert_eq!(format!("{:?}", sk), "<elided secret for SecretKey>");
24/// ```
25#[proc_macro_derive(SilentDebug)]
26pub fn silent_debug(input: TokenStream) -> TokenStream {
27    let ast = parse_macro_input!(input as DeriveInput);
28    let name = &ast.ident;
29    let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl();
30
31    let expanded = quote! {
32        // In order to ensure that secrets are never leaked, Debug is elided
33        impl #impl_generics ::core::fmt::Debug for #name #ty_generics #where_clause {
34            fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
35                write!(f, "<elided secret for {}>", stringify!(#name))
36            }
37        }
38    };
39
40    TokenStream::from(expanded)
41}
42
43// SILENT DISPLAY MACRO
44// ================================================================================================
45
46/// Derives a Display implementation that elides secret values.
47///
48/// This macro generates a Display implementation that outputs `<elided secret for TypeName>`
49/// instead of the actual field values. While implementing Display for secret keys is
50/// generally discouraged (as Display implies "user-facing output"), this safe implementation
51/// prevents compilation errors in generic contexts while still protecting sensitive data.
52///
53/// # Example
54///
55/// ```ignore
56/// #[derive(SilentDisplay)]
57/// pub struct SecretKey {
58///     inner: [u8; 32],
59/// }
60///
61/// let sk = SecretKey { inner: [0u8; 32] };
62/// assert_eq!(format!("{}", sk), "<elided secret for SecretKey>");
63/// ```
64#[proc_macro_derive(SilentDisplay)]
65pub fn silent_display(input: TokenStream) -> TokenStream {
66    let ast = parse_macro_input!(input as DeriveInput);
67    let name = &ast.ident;
68    let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl();
69
70    let expanded = quote! {
71        // In order to ensure that secrets are never leaked, Display is elided
72        impl #impl_generics ::core::fmt::Display for #name #ty_generics #where_clause {
73            fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
74                write!(f, "<elided secret for {}>", stringify!(#name))
75            }
76        }
77    };
78
79    TokenStream::from(expanded)
80}
81
82// WORD WRAPPER MACRO
83// ================================================================================================
84
85/// Generates accessor methods for tuple structs wrapping a `Word` type.
86///
87/// Automatically implements:
88/// - `from_raw(Word) -> Self` - Construct without further checks
89/// - `as_elements(&self) -> &[Felt]` - Returns the elements representation
90/// - `as_bytes(&self) -> [u8; 32]` - Returns the byte representation
91/// - `to_hex(&self) -> String` - Returns a big-endian, hex-encoded string
92/// - `as_word(&self) -> Word` - Returns the underlying Word
93///
94/// Note: This macro does NOT generate `From` trait implementations. If you need conversions
95/// to/from `Word` or `[u8; 32]`, implement them manually for your type.
96///
97/// # Example
98///
99/// ```ignore
100/// #[derive(WordWrapper)]
101/// pub struct NoteId(Word);
102/// ```
103///
104/// This will generate implementations equivalent to:
105///
106/// ```ignore
107/// impl NoteId {
108///     /// Construct without further checks from a given `Word`
109///     ///
110///     /// # Warning
111///     ///
112///     /// This requires the caller to uphold the guarantees/invariants of this type (if any).
113///     /// Check the type-level documentation for guarantees/invariants.
114///     pub fn from_raw(word: Word) -> Self {
115///         Self(word)
116///     }
117///
118///     pub fn as_elements(&self) -> &[Felt] {
119///         self.0.as_elements()
120///     }
121///
122///     pub fn as_bytes(&self) -> [u8; 32] {
123///         self.0.as_bytes()
124///     }
125///
126///     pub fn to_hex(&self) -> String {
127///         self.0.to_hex()
128///     }
129///
130///     pub fn as_word(&self) -> Word {
131///         self.0
132///     }
133/// }
134/// ```
135#[proc_macro_derive(WordWrapper)]
136pub fn word_wrapper_derive(input: TokenStream) -> TokenStream {
137    let input = parse_macro_input!(input as DeriveInput);
138
139    let name = &input.ident;
140    let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
141
142    // Validate that this is a tuple struct with a single field
143    let field_type = match &input.data {
144        Data::Struct(data_struct) => match &data_struct.fields {
145            Fields::Unnamed(fields) if fields.unnamed.len() == 1 => match fields.unnamed.first() {
146                Some(field) => &field.ty,
147                None => {
148                    return syn::Error::new_spanned(
149                        &input,
150                        "WordWrapper requires exactly one field",
151                    )
152                    .to_compile_error()
153                    .into();
154                },
155            },
156            _ => {
157                return syn::Error::new_spanned(
158                    &input,
159                    "WordWrapper can only be derived for tuple structs with exactly one field",
160                )
161                .to_compile_error()
162                .into();
163            },
164        },
165        _ => {
166            return syn::Error::new_spanned(&input, "WordWrapper can only be derived for structs")
167                .to_compile_error()
168                .into();
169        },
170    };
171
172    let word_type = if let Type::Path(type_path) = field_type {
173        let Some(segment) = type_path.path.segments.last() else {
174            return syn::Error::new_spanned(
175                field_type,
176                "WordWrapper can only be derived for types wrapping a 'Word' field",
177            )
178            .to_compile_error()
179            .into();
180        };
181        if segment.ident != "Word" {
182            return syn::Error::new_spanned(
183                field_type,
184                "WordWrapper can only be derived for types wrapping a 'Word' field",
185            )
186            .to_compile_error()
187            .into();
188        }
189        if !matches!(segment.arguments, PathArguments::None) {
190            return syn::Error::new_spanned(
191                field_type,
192                "WordWrapper can only be derived for types wrapping a 'Word' field",
193            )
194            .to_compile_error()
195            .into();
196        }
197
198        field_type
199    } else {
200        return syn::Error::new_spanned(
201            field_type,
202            "WordWrapper can only be derived for types wrapping a 'Word' field",
203        )
204        .to_compile_error()
205        .into();
206    };
207
208    let expanded = quote! {
209        impl #impl_generics #name #ty_generics #where_clause {
210            /// Construct without further checks from a given `Word`.
211            ///
212            /// # Warning
213            ///
214            /// This requires the caller to uphold the guarantees/invariants of this type (if any).
215            /// Check the type-level documentation for guarantees/invariants.
216            pub fn from_raw(word: #word_type) -> Self {
217                Self(word)
218            }
219
220            /// Returns the elements representation of this value.
221            pub fn as_elements(&self) -> &[<#word_type as ::core::ops::Index<usize>>::Output] {
222                self.0.as_elements()
223            }
224
225            /// Returns the byte representation of this value.
226            pub fn as_bytes(&self) -> [u8; 32] {
227                self.0.as_bytes()
228            }
229
230            /// Returns a big-endian, hex-encoded string.
231            pub fn to_hex(&self) -> String {
232                self.0.to_hex()
233            }
234
235            /// Returns the underlying word of this value.
236            pub fn as_word(&self) -> #word_type {
237                self.0
238            }
239        }
240    };
241
242    TokenStream::from(expanded)
243}