mc_sgx_core_types/
macros.rs

1// Copyright (c) 2018-2024 The MobileCoin Foundation
2
3#[cfg(feature = "alloc")]
4pub(crate) use alloc::vec::Vec;
5
6pub(crate) use subtle::ConstantTimeEq;
7
8/// Boilerplate macro to fill in any trait implementations required by
9/// an SgxWrapperType that don't depend on the contents of the inner
10/// type.
11#[macro_export]
12macro_rules! newtype_accessors_impls {
13    ($($wrapper:ident, $inner:ty;)*) => {$(
14        impl AsMut<$inner> for $wrapper {
15            fn as_mut(&mut self) -> &mut $inner {
16                &mut self.0
17            }
18        }
19
20        impl AsRef<$inner> for $wrapper {
21            fn as_ref(&self) -> &$inner {
22                &self.0
23            }
24        }
25
26        impl From<$inner> for $wrapper {
27            fn from(src: $inner) -> Self {
28                Self(src)
29            }
30        }
31
32        impl<'src> From<&'src $inner> for $wrapper {
33            fn from(src: &$inner) -> Self {
34                Self(src.clone())
35            }
36        }
37
38        impl From<$wrapper> for $inner {
39            fn from(src: $wrapper) -> $inner {
40                src.0
41            }
42        }
43
44    )*}
45}
46
47#[macro_export]
48/// Newtype wrapper without a display implementation.
49/// TODO: Remove once every type has a Display impl.
50macro_rules! impl_newtype_no_display {
51    ($($wrapper:ident, $inner:ty;)*) => {$(
52        $crate::newtype_accessors_impls! {
53            $wrapper, $inner;
54        }
55    )*}
56}
57/// Newtype wrapper for a primitive or struct type
58#[macro_export]
59macro_rules! impl_newtype {
60    ($($wrapper:ident, $inner:ty;)*) => {$(
61        $crate::newtype_accessors_impls! {
62            $wrapper, $inner;
63        }
64
65        impl ::core::fmt::Display for $wrapper {
66            fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
67                ::core::fmt::Debug::fmt(&self.0, f)
68            }
69        }
70    )*}
71}
72
73/// This macro provides common byte-handling operations when the type being
74/// wrapped is a struct containing a single fixed-size array of bytes.
75///
76/// This should be called from within a private submodule.
77#[macro_export]
78macro_rules! impl_newtype_for_bytestruct {
79    ($($wrapper:ident, $inner:ident, $size:ident, $fieldname:ident;)*) => {$(
80
81        $crate::newtype_accessors_impls! {
82            $wrapper, $inner;
83        }
84
85        impl $wrapper {
86            #[doc="Size of the internal array"]
87            pub const SIZE: usize = $size;
88        }
89
90        impl AsRef<[u8]> for $wrapper {
91            fn as_ref(&self) -> &[u8] {
92                &(self.0).$fieldname[..]
93            }
94        }
95
96        impl AsMut<[u8]> for $wrapper {
97            fn as_mut(&mut self) -> &mut [u8] {
98                &mut (self.0).$fieldname[..]
99            }
100        }
101
102        impl $crate::macros::ConstantTimeEq for $wrapper {
103            fn ct_eq(&self, other: &Self) -> subtle::Choice {
104                (self.0).$fieldname[..].ct_eq(&(other.0).$fieldname[..])
105            }
106        }
107
108        impl Ord for $wrapper {
109            fn cmp(&self, other: &$wrapper) -> core::cmp::Ordering {
110                self.0.$fieldname.cmp(&other.0.$fieldname)
111            }
112        }
113
114        impl PartialOrd for $wrapper {
115            fn partial_cmp(&self, other: &$wrapper) -> Option<core::cmp::Ordering> {
116                Some(self.0.$fieldname.cmp(&other.0.$fieldname))
117            }
118        }
119
120        impl<'bytes> TryFrom<&'bytes [u8]> for $wrapper {
121            type Error = $crate::FfiError;
122
123            fn try_from(src: &[u8]) -> ::core::result::Result<Self, Self::Error> {
124                if src.len() < $size {
125                    return Err($crate::FfiError::InvalidInputLength);
126                }
127
128                let mut retval = $wrapper::default();
129                (retval.0).$fieldname[..].copy_from_slice(&src[..$size]);
130                Ok(retval)
131            }
132        }
133
134        #[cfg(feature="alloc")]
135        impl TryFrom<$crate::macros::Vec<u8>> for $wrapper {
136            type Error = $crate::FfiError;
137
138            fn try_from(src: $crate::macros::Vec<u8>) -> ::core::result::Result<Self, Self::Error> {
139                Self::try_from(src.as_slice())
140            }
141        }
142
143        impl From<[u8; $size]> for $wrapper {
144            fn from($fieldname: [u8; $size]) -> Self {
145                Self($inner { $fieldname })
146            }
147        }
148
149    )*}
150}
151
152/// Derive [UpperHex] and [LowerHex] from [AsRef<T>] to render as a hexadecimal
153/// string.
154///
155/// This is not connected to [ReprBytes] but it is a macro like the above macros
156/// that is often needed for structs holding bytes.
157#[macro_export]
158macro_rules! derive_measurement_hex_from_as_ref {
159    ($mytype:ty) => {
160        $crate::derive_measurement_hex_from_as_ref!($mytype, [u8]);
161    };
162    ($mytype:ty, $asref:ty) => {
163        impl core::fmt::LowerHex for $mytype {
164            fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
165                let data: &$asref = self.as_ref();
166                for d in data {
167                    write!(f, "{:02x}", d)?;
168                }
169                Ok(())
170            }
171        }
172
173        impl core::fmt::UpperHex for $mytype {
174            fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
175                let data: &$asref = self.as_ref();
176                for d in data {
177                    write!(f, "{:02X}", d)?;
178                }
179                Ok(())
180            }
181        }
182
183        impl hex::FromHex for $mytype {
184            type Error = hex::FromHexError;
185            fn from_hex<T: AsRef<[u8]>>(hex: T) -> Result<Self, Self::Error> {
186                let bytes = <[u8; <$mytype>::SIZE]>::from_hex(hex)?;
187                Ok(bytes.into())
188            }
189        }
190    };
191}
192
193/// Implement [Debug] and [Display] for enclave measurement types.
194/// Measurement types are usually stored/shared a lowercase hex strings with no byte
195/// delimiters and no leading '0x'
196/// This implementation differs from other bytestruct types.
197#[macro_export]
198macro_rules! impl_display_and_debug_for_measurement {
199    ($($wrapper:ident),*) => {$(
200        $crate::derive_measurement_hex_from_as_ref!($wrapper);
201        impl core::fmt::Debug for $wrapper {
202            fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
203                write!(f, "{}({})", stringify!($wrapper), self)
204            }
205        }
206
207        impl core::fmt::Display for $wrapper {
208            fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
209                write!(f, "{:x}", self)
210            }
211        }
212    )*}
213}
214
215/// Implement [Display] for bytestruct types.
216/// bytestruct types should display as an uppercase, two-byte underscore-delimited
217/// hex string prefixed with a '0x'
218/// Non-measurement bytestruct types derive [Debug], so it is not implemented here.
219#[macro_export]
220macro_rules! impl_display_for_bytestruct {
221    ($($wrapper:ident)*) => {$(
222        impl ::core::fmt::UpperHex for $wrapper {
223            fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
224                let inner: &[u8] = self.as_ref();
225                mc_sgx_util::fmt_hex(inner, f)
226            }
227        }
228        impl ::core::fmt::Display for $wrapper {
229            fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
230                write!(f, "{:#X}", self)
231            }
232        }
233    )*}
234}
235
236#[cfg(test)]
237mod test {
238    extern crate std;
239
240    use crate::FfiError;
241    use std::format;
242    use std::string::ToString;
243    use yare::parameterized;
244
245    const FIELD_SIZE: usize = 24;
246
247    #[derive(Debug, Default, Eq, Clone, Copy, PartialEq)]
248    struct Inner {
249        field: [u8; FIELD_SIZE],
250    }
251
252    #[derive(Debug, Default, Eq, Clone, PartialEq)]
253    #[repr(transparent)]
254    struct Outer(Inner);
255
256    impl_newtype_for_bytestruct! {
257    Outer, Inner, FIELD_SIZE, field;
258    }
259    impl_display_for_bytestruct!(Outer);
260
261    #[test]
262    fn outer_from_inner() {
263        let inner = Inner {
264            field: [4u8; Outer::SIZE],
265        };
266        let outer: Outer = inner.into();
267        assert_eq!(outer.0, inner);
268    }
269
270    #[parameterized(
271    correct_size = { FIELD_SIZE, Ok(Outer::default()) },
272    extra_large = { FIELD_SIZE + 1, Ok(Outer::default()) },
273    too_small = { FIELD_SIZE - 1, Err(FfiError::InvalidInputLength) },
274    )]
275    fn try_from(size: usize, result: Result<Outer, FfiError>) {
276        let buffer = [0; FIELD_SIZE * 2];
277        let slice = &buffer[..size];
278        assert_eq!(Outer::try_from(slice), result);
279
280        #[cfg(feature = "alloc")]
281        {
282            use alloc::vec;
283            let zero_vec = vec![0; size];
284            assert_eq!(Outer::try_from(zero_vec), result);
285        }
286    }
287
288    #[test]
289    fn from_array() {
290        let raw_array = [5u8; Outer::SIZE];
291        let outer: Outer = raw_array.into();
292        assert_eq!(outer.0.field, raw_array);
293    }
294
295    #[test]
296    fn as_ref() {
297        let raw_array = [9u8; Outer::SIZE];
298        let outer: Outer = raw_array.into();
299        assert_eq!(outer.as_ref(), raw_array);
300    }
301
302    #[test]
303    fn as_mut() {
304        let mut outer = Outer::default();
305        let replacement = [11u8; Outer::SIZE];
306        let mut_ref: &mut [u8] = outer.as_mut();
307        mut_ref.copy_from_slice(&replacement);
308        assert_eq!(outer.0.field, replacement);
309    }
310
311    #[test]
312    fn default() {
313        let outer = Outer::default();
314        assert_eq!(outer.0.field, [0u8; Outer::SIZE]);
315    }
316
317    #[test]
318    fn newtype_byte_array_display() {
319        let outer = Outer::from([
320            0xABu8, 0x00, 0xcd, 0x12, 0xfe, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0x0a,
321            0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13,
322        ]);
323        assert_eq!(
324            outer.to_string(),
325            "0xAB00_CD12_FE01_0203_0405_0607_0809_0A0B_0C0D_0E0F_1011_1213"
326        );
327    }
328
329    #[derive(Debug, Clone, Copy, PartialEq)]
330    struct StructInner {
331        field: u32,
332    }
333
334    #[repr(transparent)]
335    struct StructOuter(StructInner);
336    impl_newtype! {
337        StructOuter, StructInner;
338    }
339
340    #[repr(transparent)]
341    struct PrimitiveOuter(u32);
342    impl_newtype! {
343        PrimitiveOuter, u32;
344    }
345
346    #[test]
347    fn newtype_for_struct() {
348        let inner = StructInner { field: 30 };
349        let outer: StructOuter = inner.into();
350        assert_eq!(outer.0, inner);
351    }
352
353    #[test]
354    fn display_newtype_for_struct() {
355        let inner = StructInner { field: 20 };
356        let outer: StructOuter = inner.into();
357        assert_eq!(outer.to_string(), "StructInner { field: 20 }");
358    }
359
360    #[test]
361    fn display_newtype_for_struct_alternate() {
362        let inner = StructInner { field: 20 };
363        let outer: StructOuter = inner.into();
364        let expected = r#"
365            StructInner {
366                field: 20,
367            }"#;
368        assert_eq!(format!("\n{outer:#}"), textwrap::dedent(expected));
369    }
370
371    #[test]
372    fn display_newtype_for_primitive() {
373        let inner = 42;
374        let outer: PrimitiveOuter = inner.into();
375        assert_eq!(outer.to_string(), "42");
376    }
377}