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