Skip to main content

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    (@impl $(#[$outer:meta])* $vis:vis struct $name:ident($wrapper:ty, $repr:ty) {
12        $(
13            $(#[$inner:meta])*
14            const $field:ident = $value:expr, $string_name:literal;
15        )*
16    }) => {
17        $(#[$outer])*
18        $vis struct $name($repr);
19
20        impl $name {
21            $(
22                $(#[$inner])*
23                pub const $field: Self = Self($value);
24            )*
25
26            #[must_use]
27            pub const fn bits(&self) -> $repr {
28                self.0
29            }
30
31            #[must_use]
32            pub const fn from_bits_truncate(bits: $repr) -> Self {
33                Self(bits)
34            }
35
36            pub const fn set_bits(&mut self, bits: $repr) {
37                self.0 = bits;
38            }
39
40            #[must_use]
41            pub const fn empty() -> Self {
42                Self(0)
43            }
44
45            #[must_use]
46            pub const fn contains(&self, other: Self) -> bool {
47                (self.0 & other.0) == other.0
48            }
49        }
50
51        impl core::ops::BitOr for $name {
52            type Output = Self;
53            fn bitor(self, rhs: Self) -> Self::Output {
54                Self(self.0 | rhs.0)
55            }
56        }
57
58        impl core::ops::BitOrAssign for $name {
59            fn bitor_assign(&mut self, rhs: Self) {
60                self.0 |= rhs.0;
61            }
62        }
63
64        impl core::ops::BitAnd for $name {
65            type Output = Self;
66            fn bitand(self, rhs: Self) -> Self::Output {
67                Self(self.0 & rhs.0)
68            }
69        }
70
71        impl core::ops::BitAndAssign for $name {
72            fn bitand_assign(&mut self, rhs: Self) {
73                self.0 &= rhs.0;
74            }
75        }
76
77        impl core::ops::BitXor for $name {
78            type Output = Self;
79            fn bitxor(self, rhs: Self) -> Self::Output {
80                Self(self.0 ^ rhs.0)
81            }
82        }
83
84        impl core::ops::BitXorAssign for $name {
85            fn bitxor_assign(&mut self, rhs: Self) {
86                self.0 ^= rhs.0;
87            }
88        }
89
90        impl core::ops::Not for $name {
91            type Output = Self;
92            fn not(self) -> Self::Output {
93                Self(!self.0)
94            }
95        }
96
97        impl $crate::TpmMarshal for $name {
98            fn marshal(&self, writer: &mut $crate::TpmWriter) -> $crate::TpmResult<()> {
99                let value = <$wrapper>::from(self.0);
100                $crate::TpmMarshal::marshal(&value, writer)
101            }
102        }
103
104        impl<'a> $crate::TpmField<'a> for $name {
105            type View = Self;
106
107            fn cast_prefix_field(buf: &'a [u8]) -> $crate::TpmResult<(Self::View, &'a [u8])> {
108                let (value, buf) = <$wrapper as $crate::TpmCast>::cast_prefix(buf)?;
109
110                Ok((Self(value.get()), buf))
111            }
112        }
113
114        impl $crate::TpmSized for $name {
115            const SIZE: usize = core::mem::size_of::<$repr>();
116            fn len(&self) -> usize {
117                Self::SIZE
118            }
119        }
120    };
121
122    ($(#[$meta:meta])* $vis:vis struct $name:ident(TpmUint8) { $($rest:tt)* }) => {
123        tpm_bitflags!(@impl $(#[$meta])* $vis struct $name($crate::basic::TpmUint8, u8) { $($rest)* });
124    };
125    ($(#[$meta:meta])* $vis:vis struct $name:ident(TpmUint16) { $($rest:tt)* }) => {
126        tpm_bitflags!(@impl $(#[$meta])* $vis struct $name($crate::basic::TpmUint16, u16) { $($rest)* });
127    };
128    ($(#[$meta:meta])* $vis:vis struct $name:ident(TpmUint32) { $($rest:tt)* }) => {
129        tpm_bitflags!(@impl $(#[$meta])* $vis struct $name($crate::basic::TpmUint32, u32) { $($rest)* });
130    };
131    ($(#[$meta:meta])* $vis:vis struct $name:ident(TpmUint64) { $($rest:tt)* }) => {
132        tpm_bitflags!(@impl $(#[$meta])* $vis struct $name($crate::basic::TpmUint64, u64) { $($rest)* });
133    };
134}
135
136#[macro_export]
137macro_rules! tpm_bool {
138    (
139        $(#[$outer:meta])*
140        $vis:vis struct $name:ident(bool);
141    ) => {
142        $(#[$outer])*
143        $vis struct $name(pub bool);
144
145        impl From<bool> for $name {
146            fn from(val: bool) -> Self {
147                Self(val)
148            }
149        }
150
151        impl From<$name> for bool {
152            fn from(val: $name) -> Self {
153                val.0
154            }
155        }
156
157        impl $crate::TpmMarshal for $name {
158            fn marshal(&self, writer: &mut $crate::TpmWriter) -> $crate::TpmResult<()> {
159                let value = if self.0 { 1 } else { 0 };
160                $crate::basic::TpmUint8::from(value).marshal(writer)
161            }
162        }
163
164        impl<'a> $crate::TpmField<'a> for $name {
165            type View = Self;
166
167            fn cast_prefix_field(buf: &'a [u8]) -> $crate::TpmResult<(Self::View, &'a [u8])> {
168                let (value, buf) = <$crate::basic::TpmUint8 as $crate::TpmCast>::cast_prefix(buf)?;
169                match value.get() {
170                    0 => Ok((Self(false), buf)),
171                    1 => Ok((Self(true), buf)),
172                    raw => Err($crate::TpmError::InvalidBoolean(
173                        $crate::TpmErrorValue::new(0).value(u64::from(raw)),
174                    )),
175                }
176            }
177        }
178
179        impl $crate::TpmSized for $name {
180            const SIZE: usize = core::mem::size_of::<$crate::basic::TpmUint8>();
181            fn len(&self) -> usize {
182                Self::SIZE
183            }
184        }
185    };
186}
187
188#[macro_export]
189macro_rules! tpm_dispatch {
190    (@const_check_sorted) => {};
191    (@const_check_sorted $prev_cmd:ident, $( $rest_cmd:ident, )*) => {
192        $crate::tpm_dispatch!(@const_check_sorted_impl $prev_cmd, $( $rest_cmd, )*);
193    };
194    (@const_check_sorted_impl $prev_cmd:ident,) => {};
195    (@const_check_sorted_impl $prev_cmd:ident, $current_cmd:ident, $( $rest_cmd:ident, )* ) => {
196        const _: () = assert!(
197            <$crate::frame::data::$prev_cmd as $crate::frame::TpmHeader>::CC as u32 <= <$crate::frame::data::$current_cmd as $crate::frame::TpmHeader>::CC as u32,
198            "TPM_DISPATCH_TABLE must be sorted by TpmCc."
199        );
200        $crate::tpm_dispatch!(@const_check_sorted_impl $current_cmd, $( $rest_cmd, )*);
201    };
202
203    ( $( ($cmd:ident, $resp:ident, $variant:ident) ),* $(,)? ) => {
204        /// An owned TPM command body value.
205        #[allow(clippy::large_enum_variant)]
206        #[derive(Debug, PartialEq, Eq, Clone)]
207        pub enum TpmCommandValue {
208            $( $variant($crate::frame::data::$cmd), )*
209        }
210
211        impl $crate::TpmSized for TpmCommandValue {
212            const SIZE: usize = $crate::constant::TPM_MAX_COMMAND_SIZE;
213            fn len(&self) -> usize {
214                match self {
215                    $( Self::$variant(c) => $crate::TpmSized::len(c), )*
216                }
217            }
218        }
219
220        impl $crate::frame::TpmMarshalBody for TpmCommandValue {
221             fn marshal_handles(&self, writer: &mut $crate::TpmWriter) -> $crate::TpmResult<()> {
222                 match self {
223                     $( Self::$variant(c) => $crate::frame::TpmMarshalBody::marshal_handles(c, writer), )*
224                 }
225             }
226             fn marshal_parameters(&self, writer: &mut $crate::TpmWriter) -> $crate::TpmResult<()> {
227                 match self {
228                     $( Self::$variant(c) => $crate::frame::TpmMarshalBody::marshal_parameters(c, writer), )*
229                 }
230             }
231        }
232
233        impl $crate::TpmMarshal for TpmCommandValue {
234             fn marshal(&self, writer: &mut $crate::TpmWriter) -> $crate::TpmResult<()> {
235                 match self {
236                     $( Self::$variant(c) => $crate::TpmMarshal::marshal(c, writer), )*
237                 }
238             }
239        }
240
241        impl $crate::frame::TpmFrame for TpmCommandValue {
242            fn cc(&self) -> $crate::data::TpmCc {
243                match self {
244                    $( Self::$variant(c) => $crate::frame::TpmFrame::cc(c), )*
245                }
246            }
247            fn handles(&self) -> usize {
248                match self {
249                    $( Self::$variant(c) => $crate::frame::TpmFrame::handles(c), )*
250                }
251            }
252        }
253
254        impl TpmCommandValue {
255            /// Marshals a command body into a writer.
256            ///
257            /// # Errors
258            ///
259            /// Returns `Err(TpmError)` on a marshal failure.
260            pub fn marshal_frame(
261                &self,
262                tag: $crate::data::TpmSt,
263                sessions: &$crate::frame::TpmAuthCommands,
264                writer: &mut $crate::TpmWriter,
265            ) -> $crate::TpmResult<()> {
266                match self {
267                    $( Self::$variant(c) => $crate::frame::tpm_marshal_command(c, tag, sessions, writer), )*
268                }
269            }
270        }
271
272        /// A borrowed TPM command frame selected by command code.
273        pub enum TpmCommandView<'a> {
274            $( $variant(&'a $crate::frame::TpmCommand), )*
275        }
276
277        impl<'a> TpmCommandView<'a> {
278            /// Casts bytes into a borrowed command dispatch value.
279            ///
280            /// # Errors
281            ///
282            /// Returns `Err(TpmError)` when the command frame is malformed or its
283            /// command code has no dispatch entry.
284            pub fn cast_frame(buf: &'a [u8]) -> $crate::TpmResult<Self> {
285                let command = <$crate::frame::TpmCommand>::cast(buf)?;
286
287                Self::cast(command)
288            }
289
290            /// Selects a borrowed command dispatch value from a command wire view.
291            ///
292            /// # Errors
293            ///
294            /// Returns `Err(TpmError)` when the command frame is malformed or its
295            /// command code has no dispatch entry.
296            pub fn cast(command: &'a $crate::frame::TpmCommand) -> $crate::TpmResult<Self> {
297                command.validate()?;
298
299                let cc = command.cc()?;
300                match cc {
301                    $( <$crate::frame::data::$cmd as $crate::frame::TpmHeader>::CC => Ok(Self::$variant(command)), )*
302                    _ => Err($crate::TpmError::InvalidCc(
303                        $crate::TpmErrorValue::new(6).value(u64::from(cc.value())),
304                    )),
305                }
306            }
307
308            /// Returns the selected command frame.
309            #[must_use]
310            pub fn command(&self) -> &'a $crate::frame::TpmCommand {
311                match self {
312                    $( Self::$variant(command) => command, )*
313                }
314            }
315
316            /// Returns the selected command code.
317            #[must_use]
318            pub fn cc(&self) -> $crate::data::TpmCc {
319                match self {
320                    $( Self::$variant(_) => <$crate::frame::data::$cmd as $crate::frame::TpmHeader>::CC, )*
321                }
322            }
323        }
324
325        /// An owned TPM response body value.
326        #[allow(clippy::large_enum_variant)]
327        #[derive(Debug, PartialEq, Eq, Clone)]
328        pub enum TpmResponseValue {
329            $( $variant($crate::frame::data::$resp), )*
330        }
331
332        impl $crate::TpmSized for TpmResponseValue {
333            const SIZE: usize = $crate::constant::TPM_MAX_COMMAND_SIZE;
334            fn len(&self) -> usize {
335                match self {
336                    $( Self::$variant(r) => $crate::TpmSized::len(r), )*
337                }
338            }
339        }
340
341        impl $crate::frame::TpmMarshalBody for TpmResponseValue {
342             fn marshal_handles(&self, writer: &mut $crate::TpmWriter) -> $crate::TpmResult<()> {
343                 match self {
344                     $( Self::$variant(r) => $crate::frame::TpmMarshalBody::marshal_handles(r, writer), )*
345                 }
346             }
347             fn marshal_parameters(&self, writer: &mut $crate::TpmWriter) -> $crate::TpmResult<()> {
348                 match self {
349                     $( Self::$variant(r) => $crate::frame::TpmMarshalBody::marshal_parameters(r, writer), )*
350                 }
351             }
352        }
353
354        impl $crate::TpmMarshal for TpmResponseValue {
355             fn marshal(&self, writer: &mut $crate::TpmWriter) -> $crate::TpmResult<()> {
356                 match self {
357                     $( Self::$variant(r) => $crate::TpmMarshal::marshal(r, writer), )*
358                 }
359             }
360        }
361
362        impl $crate::frame::TpmFrame for TpmResponseValue {
363            fn cc(&self) -> $crate::data::TpmCc {
364                match self {
365                    $( Self::$variant(r) => $crate::frame::TpmFrame::cc(r), )*
366                }
367            }
368            fn handles(&self) -> usize {
369                match self {
370                    $( Self::$variant(r) => $crate::frame::TpmFrame::handles(r), )*
371                }
372            }
373        }
374
375        impl TpmResponseValue {
376            $(
377                /// Attempts to convert the `TpmResponseValue` into a specific response type.
378                ///
379                /// # Errors
380                ///
381                /// Returns the original `TpmResponseValue` as an error if the enum variant does not match.
382                #[allow(non_snake_case, clippy::result_large_err)]
383                pub fn $variant(self) -> Result<$crate::frame::data::$resp, Self> {
384                    if let Self::$variant(r) = self {
385                        Ok(r)
386                    } else {
387                        Err(self)
388                    }
389                }
390            )*
391
392            /// Marshals a response body into a writer.
393            ///
394            /// # Errors
395            ///
396            /// Returns `Err(TpmError)` on a marshal failure.
397            pub fn marshal_frame(
398                &self,
399                rc: $crate::data::TpmRc,
400                sessions: &$crate::frame::TpmAuthResponses,
401                writer: &mut $crate::TpmWriter,
402            ) -> $crate::TpmResult<()> {
403                match self {
404                    $( Self::$variant(r) => $crate::frame::tpm_marshal_response(r, sessions, rc, writer), )*
405                }
406            }
407        }
408
409        /// A borrowed TPM response frame selected by command code.
410        pub enum TpmResponseView<'a> {
411            $( $variant(&'a $crate::frame::TpmResponse), )*
412        }
413
414        /// A borrowed response dispatch result or a TPM response code.
415        pub type TpmResponseViewResult<'a> = Result<TpmResponseView<'a>, $crate::data::TpmRc>;
416
417        impl<'a> TpmResponseView<'a> {
418            /// Casts bytes into a borrowed response dispatch value.
419            ///
420            /// # Errors
421            ///
422            /// Returns `Err(TpmError)` when the response frame is malformed or `cc`
423            /// has no dispatch entry.
424            pub fn cast_frame(
425                cc: $crate::data::TpmCc,
426                buf: &'a [u8],
427            ) -> $crate::TpmResult<TpmResponseViewResult<'a>> {
428                let response = <$crate::frame::TpmResponse>::cast(buf)?;
429
430                Self::cast(cc, response)
431            }
432
433            /// Selects a borrowed response dispatch value from a response wire view.
434            ///
435            /// # Errors
436            ///
437            /// Returns `Err(TpmError)` when the response frame is malformed or `cc`
438            /// has no dispatch entry.
439            pub fn cast(
440                cc: $crate::data::TpmCc,
441                response: &'a $crate::frame::TpmResponse,
442            ) -> $crate::TpmResult<TpmResponseViewResult<'a>> {
443                let rc = response.rc()?;
444                if !matches!(rc, $crate::data::TpmRc::Fmt0($crate::data::TpmRcBase::Success)) {
445                    return Ok(Err(rc));
446                }
447
448                response.validate(cc)?;
449
450                match cc {
451                    $( <$crate::frame::data::$cmd as $crate::frame::TpmHeader>::CC => Ok(Ok(Self::$variant(response))), )*
452                    _ => Err($crate::TpmError::InvalidCc(
453                        $crate::TpmErrorValue::new(0).value(u64::from(cc.value())),
454                    )),
455                }
456            }
457
458            /// Returns the selected response frame.
459            #[must_use]
460            pub fn response(&self) -> &'a $crate::frame::TpmResponse {
461                match self {
462                    $( Self::$variant(response) => response, )*
463                }
464            }
465
466            /// Returns the command code used for response dispatch.
467            #[must_use]
468            pub fn cc(&self) -> $crate::data::TpmCc {
469                match self {
470                    $( Self::$variant(_) => <$crate::frame::data::$cmd as $crate::frame::TpmHeader>::CC, )*
471                }
472            }
473        }
474
475        pub(crate) static TPM_DISPATCH_TABLE: &[$crate::frame::TpmDispatch] = &[
476            $(
477                $crate::frame::TpmDispatch {
478                    cc: <$crate::frame::data::$cmd as $crate::frame::TpmHeader>::CC,
479                    handles: <$crate::frame::data::$cmd as $crate::frame::TpmHeader>::HANDLES,
480                    response_handles: <$crate::frame::data::$resp as $crate::frame::TpmHeader>::HANDLES,
481                },
482            )*
483        ];
484
485        $crate::tpm_dispatch!(@const_check_sorted $( $cmd, )*);
486    };
487}