tpm2_protocol/macro/
mod.rs

1// SPDX-License-Identifier: MIT OR Apache-2.0
2// Copyright (c) 2025 Opinsys Oy
3// Copyright (c) 2024-2025 Jarkko Sakkinen
4
5pub mod r#enum;
6pub mod integer;
7pub mod r#struct;
8
9#[macro_export]
10macro_rules! tpm_bitflags {
11    (
12        $(#[$outer:meta])*
13        $vis:vis struct $name:ident($repr:ty) {
14            $(
15                $(#[$inner:meta])*
16                const $field:ident = $value:expr, $string_name:literal;
17            )*
18        }
19    ) => {
20        $(#[$outer])*
21        $vis struct $name($repr);
22
23        impl $name {
24            $(
25                $(#[$inner])*
26                pub const $field: Self = Self($value);
27            )*
28
29            #[must_use]
30            pub const fn bits(&self) -> $repr {
31                self.0
32            }
33
34            #[must_use]
35            pub const fn from_bits_truncate(bits: $repr) -> Self {
36                Self(bits)
37            }
38
39            #[must_use]
40            pub const fn empty() -> Self {
41                Self(0)
42            }
43
44            #[must_use]
45            pub const fn contains(&self, other: Self) -> bool {
46                (self.0 & other.0) == other.0
47            }
48
49            pub fn flag_names(&self) -> impl Iterator<Item = &'static str> + '_ {
50                [
51                    $(
52                        (Self::$field, $string_name),
53                    )*
54                ]
55                .into_iter()
56                .filter(move |(flag, _)| self.contains(*flag))
57                .map(|(_, name)| name)
58            }
59        }
60
61        impl core::ops::BitOr for $name {
62            type Output = Self;
63            fn bitor(self, rhs: Self) -> Self::Output {
64                Self(self.0 | rhs.0)
65            }
66        }
67
68        impl core::ops::BitOrAssign for $name {
69            fn bitor_assign(&mut self, rhs: Self) {
70                self.0 |= rhs.0;
71            }
72        }
73
74        impl $crate::TpmMarshal for $name {
75            fn marshal(&self, writer: &mut $crate::TpmWriter) -> $crate::TpmResult<()> {
76                $crate::TpmMarshal::marshal(&self.0, writer)
77            }
78        }
79
80        impl $crate::TpmUnmarshal for $name {
81            fn unmarshal(buf: &[u8]) -> $crate::TpmResult<(Self, &[u8])> {
82                let (val, buf) = <$repr>::unmarshal(buf)?;
83                Ok((Self(val), buf))
84            }
85        }
86
87        impl $crate::TpmSized for $name {
88            const SIZE: usize = core::mem::size_of::<$repr>();
89            fn len(&self) -> usize {
90                Self::SIZE
91            }
92        }
93    };
94}
95
96#[macro_export]
97macro_rules! tpm_bool {
98    (
99        $(#[$outer:meta])*
100        $vis:vis struct $name:ident(bool);
101    ) => {
102        $(#[$outer])*
103        $vis struct $name(pub bool);
104
105        impl From<bool> for $name {
106            fn from(val: bool) -> Self {
107                Self(val)
108            }
109        }
110
111        impl From<$name> for bool {
112            fn from(val: $name) -> Self {
113                val.0
114            }
115        }
116
117        impl $crate::TpmMarshal for $name {
118            fn marshal(&self, writer: &mut $crate::TpmWriter) -> $crate::TpmResult<()> {
119                $crate::TpmMarshal::marshal(&u8::from(self.0), writer)
120            }
121        }
122
123        impl $crate::TpmUnmarshal for $name {
124            fn unmarshal(buf: &[u8]) -> $crate::TpmResult<(Self, &[u8])> {
125                let (val, buf) = u8::unmarshal(buf)?;
126                match val {
127                    0 => Ok((Self(false), buf)),
128                    1 => Ok((Self(true), buf)),
129                    _ => Err($crate::TpmProtocolError::InvalidDiscriminant (stringify!($name), $crate::TpmDiscriminant::Unsigned(u64::from(val)))),
130                }
131            }
132        }
133
134        impl $crate::TpmSized for $name {
135            const SIZE: usize = core::mem::size_of::<u8>();
136            fn len(&self) -> usize {
137                Self::SIZE
138            }
139        }
140    };
141}
142
143#[macro_export]
144macro_rules! tpm_dispatch {
145    (@const_check_sorted) => {};
146    (@const_check_sorted $prev_cmd:ident, $( $rest_cmd:ident, )*) => {
147        $crate::tpm_dispatch!(@const_check_sorted_impl $prev_cmd, $( $rest_cmd, )*);
148    };
149    (@const_check_sorted_impl $prev_cmd:ident,) => {};
150    (@const_check_sorted_impl $prev_cmd:ident, $current_cmd:ident, $( $rest_cmd:ident, )* ) => {
151        const _: () = assert!(
152            <$crate::frame::data::$prev_cmd as $crate::frame::TpmHeader>::CC as u32 <= <$crate::frame::data::$current_cmd as $crate::frame::TpmHeader>::CC as u32,
153            "TPM_DISPATCH_TABLE must be sorted by TpmCc."
154        );
155        $crate::tpm_dispatch!(@const_check_sorted_impl $current_cmd, $( $rest_cmd, )*);
156    };
157
158    ( $( ($cmd:ident, $resp:ident, $variant:ident) ),* $(,)? ) => {
159        /// A TPM command
160        #[allow(clippy::large_enum_variant)]
161        #[derive(Debug, PartialEq, Eq, Clone)]
162        pub enum TpmCommand {
163            $( $variant($crate::frame::data::$cmd), )*
164        }
165
166        impl $crate::TpmSized for TpmCommand {
167            const SIZE: usize = $crate::constant::TPM_MAX_COMMAND_SIZE as usize;
168            fn len(&self) -> usize {
169                match self {
170                    $( Self::$variant(c) => $crate::TpmSized::len(c), )*
171                }
172            }
173        }
174
175        impl $crate::frame::TpmMarshalBody for TpmCommand {
176             fn marshal_handles(&self, writer: &mut $crate::TpmWriter) -> $crate::TpmResult<()> {
177                 match self {
178                     $( Self::$variant(c) => $crate::frame::TpmMarshalBody::marshal_handles(c, writer), )*
179                 }
180             }
181             fn marshal_parameters(&self, writer: &mut $crate::TpmWriter) -> $crate::TpmResult<()> {
182                 match self {
183                     $( Self::$variant(c) => $crate::frame::TpmMarshalBody::marshal_parameters(c, writer), )*
184                 }
185             }
186        }
187
188        impl $crate::TpmMarshal for TpmCommand {
189             fn marshal(&self, writer: &mut $crate::TpmWriter) -> $crate::TpmResult<()> {
190                 match self {
191                     $( Self::$variant(c) => $crate::TpmMarshal::marshal(c, writer), )*
192                 }
193             }
194        }
195
196        impl $crate::frame::TpmFrame for TpmCommand {
197            fn cc(&self) -> $crate::data::TpmCc {
198                match self {
199                    $( Self::$variant(c) => $crate::frame::TpmFrame::cc(c), )*
200                }
201            }
202            fn handles(&self) -> usize {
203                match self {
204                    $( Self::$variant(c) => $crate::frame::TpmFrame::handles(c), )*
205                }
206            }
207        }
208
209        impl TpmCommand {
210            /// Marshals a command body into a writer.
211            ///
212            /// # Errors
213            ///
214            /// Returns `Err(TpmProtocolError)` on a marshal failure.
215            pub fn marshal_frame(
216                &self,
217                tag: $crate::data::TpmSt,
218                sessions: &$crate::frame::TpmAuthCommands,
219                writer: &mut $crate::TpmWriter,
220            ) -> $crate::TpmResult<()> {
221                match self {
222                    $( Self::$variant(c) => $crate::frame::tpm_marshal_command(c, tag, sessions, writer), )*
223                }
224            }
225        }
226
227        /// A TPM response body
228        #[allow(clippy::large_enum_variant)]
229        #[derive(Debug, PartialEq, Eq, Clone)]
230        pub enum TpmResponse {
231            $( $variant($crate::frame::data::$resp), )*
232        }
233
234        impl $crate::TpmSized for TpmResponse {
235            const SIZE: usize = $crate::constant::TPM_MAX_COMMAND_SIZE as usize;
236            fn len(&self) -> usize {
237                match self {
238                    $( Self::$variant(r) => $crate::TpmSized::len(r), )*
239                }
240            }
241        }
242
243        impl $crate::frame::TpmMarshalBody for TpmResponse {
244             fn marshal_handles(&self, writer: &mut $crate::TpmWriter) -> $crate::TpmResult<()> {
245                 match self {
246                     $( Self::$variant(r) => $crate::frame::TpmMarshalBody::marshal_handles(r, writer), )*
247                 }
248             }
249             fn marshal_parameters(&self, writer: &mut $crate::TpmWriter) -> $crate::TpmResult<()> {
250                 match self {
251                     $( Self::$variant(r) => $crate::frame::TpmMarshalBody::marshal_parameters(r, writer), )*
252                 }
253             }
254        }
255
256        impl $crate::TpmMarshal for TpmResponse {
257             fn marshal(&self, writer: &mut $crate::TpmWriter) -> $crate::TpmResult<()> {
258                 match self {
259                     $( Self::$variant(r) => $crate::TpmMarshal::marshal(r, writer), )*
260                 }
261             }
262        }
263
264        impl $crate::frame::TpmFrame for TpmResponse {
265            fn cc(&self) -> $crate::data::TpmCc {
266                match self {
267                    $( Self::$variant(r) => $crate::frame::TpmFrame::cc(r), )*
268                }
269            }
270            fn handles(&self) -> usize {
271                match self {
272                    $( Self::$variant(r) => $crate::frame::TpmFrame::handles(r), )*
273                }
274            }
275        }
276
277        impl TpmResponse {
278            $(
279                /// Attempts to convert the `TpmResponse` into a specific response type.
280                ///
281                /// # Errors
282                ///
283                /// Returns the original `TpmResponse` as an error if the enum variant does not match.
284                #[allow(non_snake_case, clippy::result_large_err)]
285                pub fn $variant(self) -> Result<$crate::frame::data::$resp, Self> {
286                    if let Self::$variant(r) = self {
287                        Ok(r)
288                    } else {
289                        Err(self)
290                    }
291                }
292            )*
293
294            /// Marshals a response body into a writer.
295            ///
296            /// # Errors
297            ///
298            /// Returns `Err(TpmProtocolError)` on a marshal failure.
299            pub fn marshal_frame(
300                &self,
301                rc: $crate::data::TpmRc,
302                sessions: &$crate::frame::TpmAuthResponses,
303                writer: &mut $crate::TpmWriter,
304            ) -> $crate::TpmResult<()> {
305                match self {
306                    $( Self::$variant(r) => $crate::frame::tpm_marshal_response(r, sessions, rc, writer), )*
307                }
308            }
309        }
310
311        pub(crate) static TPM_DISPATCH_TABLE: &[$crate::frame::TpmDispatch] = &[
312            $(
313                $crate::frame::TpmDispatch {
314                    cc: <$crate::frame::data::$cmd as $crate::frame::TpmHeader>::CC,
315                    handles: <$crate::frame::data::$cmd as $crate::frame::TpmHeader>::HANDLES,
316                    command_unmarshaler: |handles, params| {
317                        <$crate::frame::data::$cmd as $crate::frame::TpmUnmarshalCommand>::unmarshal_body(handles, params)
318                            .map(|(c, r)| (TpmCommand::$variant(c), r))
319                    },
320                    response_unmarshaler: |tag, buf| {
321                        <$crate::frame::data::$resp as $crate::frame::TpmUnmarshalResponse>::unmarshal_body(tag, buf)
322                            .map(|(r, rest)| (TpmResponse::$variant(r), rest))
323                    },
324                },
325            )*
326        ];
327
328        $crate::tpm_dispatch!(@const_check_sorted $( $cmd, )*);
329    };
330}
331
332#[macro_export]
333macro_rules! tpm2b {
334    ($name:ident, $capacity:expr) => {
335        pub type $name = $crate::basic::TpmBuffer<$capacity>;
336    };
337}
338
339#[macro_export]
340macro_rules! tpm2b_struct {
341    (
342        $(#[$meta:meta])*
343        $wrapper_ty:ident, $inner_ty:ty) => {
344        $(#[$meta])*
345        pub struct $wrapper_ty {
346            pub inner: $inner_ty,
347        }
348
349        impl $crate::TpmSized for $wrapper_ty {
350            const SIZE: usize = core::mem::size_of::<u16>() + <$inner_ty>::SIZE;
351            fn len(&self) -> usize {
352                core::mem::size_of::<u16>() + $crate::TpmSized::len(&self.inner)
353            }
354        }
355
356        impl $crate::TpmMarshal for $wrapper_ty {
357            fn marshal(&self, writer: &mut $crate::TpmWriter) -> $crate::TpmResult<()> {
358                let inner_len = $crate::TpmSized::len(&self.inner);
359                u16::try_from(inner_len)
360                    .map_err(|_| $crate::TpmProtocolError::CapacityExceeded)?
361                    .marshal(writer)?;
362                $crate::TpmMarshal::marshal(&self.inner, writer)
363            }
364        }
365
366        impl $crate::TpmUnmarshal for $wrapper_ty {
367            fn unmarshal(buf: &[u8]) -> $crate::TpmResult<(Self, &[u8])> {
368                let (size, buf_after_size) = u16::unmarshal(buf)?;
369                let size = size as usize;
370
371                if buf_after_size.len() < size {
372                    return Err($crate::TpmProtocolError::UnexpectedEof);
373                }
374                let (inner_bytes, rest) = buf_after_size.split_at(size);
375
376                let (inner_val, tail) = <$inner_ty>::unmarshal(inner_bytes)?;
377
378                if !tail.is_empty() {
379                    return Err($crate::TpmProtocolError::TrailingData);
380                }
381
382                Ok((Self { inner: inner_val }, rest))
383            }
384        }
385
386        impl From<$inner_ty> for $wrapper_ty {
387            fn from(inner: $inner_ty) -> Self {
388                Self { inner }
389            }
390        }
391
392        impl core::ops::Deref for $wrapper_ty {
393            type Target = $inner_ty;
394            fn deref(&self) -> &Self::Target {
395                &self.inner
396            }
397        }
398
399        impl core::ops::DerefMut for $wrapper_ty {
400            fn deref_mut(&mut self) -> &mut Self::Target {
401                &mut self.inner
402            }
403        }
404    };
405}
406
407#[macro_export]
408macro_rules! tpml {
409    ($name:ident, $inner_ty:ty, $capacity:expr) => {
410        pub type $name = $crate::basic::TpmList<$inner_ty, $capacity>;
411    };
412}