Skip to main content

tpm2_protocol/macro/
integer.rs

1// SPDX-License-Identifier: MIT OR Apache-2.0
2// Copyright (c) 2025 Opinsys Oy
3// Copyright (c) 2024-2025 Jarkko Sakkinen
4
5#[macro_export]
6macro_rules! integer {
7    ($name:ident, $raw:ty, $bytes:expr) => {
8        #[derive(Default, Clone, Copy, PartialEq, Eq, Hash)]
9        #[repr(transparent)]
10        pub struct $name([u8; $bytes]);
11
12        impl $name {
13            #[must_use]
14            pub const fn new(value: $raw) -> Self {
15                Self(value.to_be_bytes())
16            }
17
18            #[must_use]
19            pub const fn value(self) -> $raw {
20                <$raw>::from_be_bytes(self.0)
21            }
22
23            #[must_use]
24            pub const fn get(&self) -> $raw {
25                <$raw>::from_be_bytes(self.0)
26            }
27
28            pub const fn set(&mut self, value: $raw) {
29                self.0 = value.to_be_bytes();
30            }
31
32            #[must_use]
33            pub const fn as_bytes(&self) -> &[u8; $bytes] {
34                &self.0
35            }
36
37            #[must_use]
38            pub fn as_bytes_mut(&mut self) -> &mut [u8; $bytes] {
39                &mut self.0
40            }
41
42            #[must_use]
43            pub fn to_be_bytes(self) -> [u8; $bytes] {
44                self.0
45            }
46
47            #[must_use]
48            pub const fn from_be_bytes(bytes: [u8; $bytes]) -> Self {
49                Self(bytes)
50            }
51
52            /// Casts a byte slice into a TPM integer wire view.
53            ///
54            /// # Errors
55            ///
56            /// Returns [`UnexpectedEnd`](crate::TpmError::UnexpectedEnd) when
57            /// `buf` is smaller than this integer's wire size.
58            /// Returns [`TrailingData`](crate::TpmError::TrailingData) when
59            /// `buf` is larger than this integer's wire size.
60            pub fn cast(buf: &[u8]) -> $crate::TpmResult<&Self> {
61                let _ = $crate::TpmWireBytes::<$bytes>::cast(buf)?;
62
63                // SAFETY: `TpmWireBytes::<$bytes>::cast` validated the exact
64                // byte length required by this transparent integer view.
65                Ok(unsafe { Self::cast_unchecked(buf) })
66            }
67
68            /// Casts a byte slice into a TPM integer wire view without validation.
69            ///
70            /// # Safety
71            ///
72            /// The caller must ensure that `buf.len()` equals this integer's
73            /// wire size and that any containing protocol structure has been
74            /// validated as needed.
75            #[must_use]
76            pub unsafe fn cast_unchecked(buf: &[u8]) -> &Self {
77                let ptr = buf.as_ptr().cast::<Self>();
78
79                // SAFETY: `$name` is `repr(transparent)` over `[u8; $bytes]`.
80                // The caller guarantees exact size.
81                unsafe { &*ptr }
82            }
83
84            /// Casts a mutable byte slice into a mutable TPM integer wire view.
85            ///
86            /// # Errors
87            ///
88            /// Returns [`UnexpectedEnd`](crate::TpmError::UnexpectedEnd) when
89            /// `buf` is smaller than this integer's wire size.
90            /// Returns [`TrailingData`](crate::TpmError::TrailingData) when
91            /// `buf` is larger than this integer's wire size.
92            pub fn cast_mut(buf: &mut [u8]) -> $crate::TpmResult<&mut Self> {
93                let _ = $crate::TpmWireBytes::<$bytes>::cast_mut(buf)?;
94
95                // SAFETY: `TpmWireBytes::<$bytes>::cast_mut` validated the exact
96                // byte length required by this transparent integer view.
97                Ok(unsafe { Self::cast_mut_unchecked(buf) })
98            }
99
100            /// Casts a mutable byte slice into a mutable TPM integer wire view without validation.
101            ///
102            /// # Safety
103            ///
104            /// The caller must ensure that `buf.len()` equals this integer's
105            /// wire size and that any containing protocol structure has been
106            /// validated as needed. The returned reference inherits the
107            /// exclusive access represented by `buf`.
108            #[must_use]
109            pub unsafe fn cast_mut_unchecked(buf: &mut [u8]) -> &mut Self {
110                let ptr = buf.as_mut_ptr().cast::<Self>();
111
112                // SAFETY: `$name` is `repr(transparent)` over `[u8; $bytes]`.
113                // The caller guarantees exact size.
114                unsafe { &mut *ptr }
115            }
116        }
117
118        impl core::fmt::Debug for $name {
119            fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
120                f.debug_tuple(stringify!($name)).field(&self.get()).finish()
121            }
122        }
123
124        impl core::cmp::PartialOrd for $name {
125            fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
126                Some(self.cmp(other))
127            }
128        }
129
130        impl core::cmp::Ord for $name {
131            fn cmp(&self, other: &Self) -> core::cmp::Ordering {
132                self.get().cmp(&other.get())
133            }
134        }
135
136        impl From<$raw> for $name {
137            fn from(value: $raw) -> Self {
138                Self::new(value)
139            }
140        }
141
142        impl From<$name> for $raw {
143            fn from(value: $name) -> $raw {
144                value.value()
145            }
146        }
147
148        impl core::fmt::Display for $name {
149            fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
150                core::fmt::Display::fmt(&self.get(), f)
151            }
152        }
153
154        impl core::fmt::LowerHex for $name {
155            fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
156                core::fmt::LowerHex::fmt(&self.get(), f)
157            }
158        }
159
160        impl core::fmt::UpperHex for $name {
161            fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
162                core::fmt::UpperHex::fmt(&self.get(), f)
163            }
164        }
165
166        impl $crate::TpmSized for $name {
167            const SIZE: usize = $bytes;
168            fn len(&self) -> usize {
169                Self::SIZE
170            }
171        }
172
173        impl $crate::TpmMarshal for $name {
174            fn marshal(&self, writer: &mut $crate::TpmWriter) -> $crate::TpmResult<()> {
175                writer.write_bytes(self.as_bytes())
176            }
177        }
178
179        impl $crate::TpmUnmarshal for $name {
180            fn unmarshal(buf: &[u8]) -> $crate::TpmResult<(Self, &[u8])> {
181                let size = core::mem::size_of::<$raw>();
182                let bytes = buf.get(..size).ok_or($crate::TpmError::UnexpectedEnd(
183                    $crate::TpmErrorValue::new(0).size(size, buf.len()),
184                ))?;
185                let array = bytes.try_into().map_err(|_| {
186                    $crate::TpmError::UnexpectedEnd(
187                        $crate::TpmErrorValue::new(0).size(size, buf.len()),
188                    )
189                })?;
190                let val = Self::from_be_bytes(array);
191                Ok((val, &buf[size..]))
192            }
193        }
194
195        impl $crate::TpmCast for $name {
196            fn cast(buf: &[u8]) -> $crate::TpmResult<&Self> {
197                Self::cast(buf)
198            }
199
200            unsafe fn cast_unchecked(buf: &[u8]) -> &Self {
201                // SAFETY: The caller upholds the unchecked cast contract for `$name`.
202                unsafe { Self::cast_unchecked(buf) }
203            }
204        }
205
206        impl $crate::TpmCastMut for $name {
207            fn cast_mut(buf: &mut [u8]) -> $crate::TpmResult<&mut Self> {
208                Self::cast_mut(buf)
209            }
210
211            unsafe fn cast_mut_unchecked(buf: &mut [u8]) -> &mut Self {
212                // SAFETY: The caller upholds the unchecked mutable cast contract for `$name`.
213                unsafe { Self::cast_mut_unchecked(buf) }
214            }
215        }
216
217        impl TryFrom<usize> for $name
218        where
219            $raw: TryFrom<usize>,
220        {
221            type Error = <$raw as TryFrom<usize>>::Error;
222            fn try_from(value: usize) -> Result<Self, Self::Error> {
223                <$raw>::try_from(value).map(Self::new)
224            }
225        }
226    };
227}