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