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::TpmBuild for $name {
75            fn build(&self, writer: &mut $crate::TpmWriter) -> $crate::TpmResult<()> {
76                $crate::TpmBuild::build(&self.0, writer)
77            }
78        }
79
80        impl $crate::TpmParse for $name {
81            fn parse(buf: &[u8]) -> $crate::TpmResult<(Self, &[u8])> {
82                let (val, buf) = <$repr>::parse(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::TpmBuild for $name {
118            fn build(&self, writer: &mut $crate::TpmWriter) -> $crate::TpmResult<()> {
119                $crate::TpmBuild::build(&u8::from(self.0), writer)
120            }
121        }
122
123        impl $crate::TpmParse for $name {
124            fn parse(buf: &[u8]) -> $crate::TpmResult<(Self, &[u8])> {
125                let (val, buf) = u8::parse(buf)?;
126                match val {
127                    0 => Ok((Self(false), buf)),
128                    1 => Ok((Self(true), buf)),
129                    _ => Err($crate::TpmErrorKind::NotDiscriminant (stringify!($name), TpmNotDiscriminant::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    ( $( ($cmd:ident, $resp:ident, $variant:ident) ),* $(,)? ) => {
146        macro_rules! tpm_command_parser {
147            ($value:ty, $name:ident) => {
148                (
149                    <$value as $crate::message::TpmHeader>::COMMAND,
150                    <$value as $crate::message::TpmHeader>::NO_SESSIONS,
151                    <$value as $crate::message::TpmHeader>::WITH_SESSIONS,
152                    <$value as $crate::message::TpmHeader>::HANDLES,
153                    |handles, params| {
154                        <$value as $crate::message::TpmCommandBodyParse>::parse_body(handles, params)
155                            .map(|(c, r)| (TpmCommandBody::$name(c), r))
156                    },
157                )
158            };
159        }
160
161        macro_rules! tpm_response_parser {
162            ($rsp_ty:ty, $enum_variant:ident) => {
163                (
164                    <$rsp_ty as $crate::message::TpmHeader>::COMMAND,
165                    <$rsp_ty as $crate::message::TpmHeader>::WITH_SESSIONS,
166                    |tag, buf| {
167                        <$rsp_ty as $crate::message::TpmResponseBodyParse>::parse_body(tag, buf)
168                            .map(|(r, rest)| (TpmResponseBody::$enum_variant(r), rest))
169                    },
170                )
171            };
172        }
173
174        /// A TPM command
175        #[allow(clippy::large_enum_variant)]
176        #[derive(Debug, PartialEq, Eq, Clone)]
177        pub enum TpmCommandBody {
178            $( $variant($cmd), )*
179        }
180
181        /// A TPM response body
182        #[allow(clippy::large_enum_variant)]
183        #[derive(Debug, PartialEq, Eq, Clone)]
184        pub enum TpmResponseBody {
185            $( $variant($resp), )*
186        }
187
188        impl TpmResponseBody {
189            $(
190                /// Attempts to convert the `TpmResponseBody` into a specific response type.
191                ///
192                /// # Errors
193                ///
194                /// Returns the original `TpmResponseBody` as an error if the enum variant does not match.
195                #[allow(non_snake_case, clippy::result_large_err)]
196                pub fn $variant(self) -> Result<$resp, Self> {
197                    if let Self::$variant(r) = self {
198                        Ok(r)
199                    } else {
200                        Err(self)
201                    }
202                }
203            )*
204        }
205
206        pub type TpmCommandParser = for<'a> fn(&'a [u8], &'a [u8]) -> $crate::TpmResult<(TpmCommandBody, &'a [u8])>;
207        pub type TpmResponseParser = for<'a> fn($crate::data::TpmSt, &'a [u8]) -> $crate::TpmResult<(TpmResponseBody, &'a [u8])>;
208
209        pub(crate) static PARSE_COMMAND_MAP: &[($crate::data::TpmCc, bool, bool, usize, TpmCommandParser)] =
210            &[$(tpm_command_parser!($cmd, $variant),)*];
211
212        pub(crate) static PARSE_RESPONSE_MAP: &[($crate::data::TpmCc, bool, TpmResponseParser)] =
213            &[$(tpm_response_parser!($resp, $variant),)*];
214
215        const _: () = {
216            let mut i = 1;
217            while i < PARSE_COMMAND_MAP.len() {
218                if PARSE_COMMAND_MAP[i - 1].0 as u32 > PARSE_COMMAND_MAP[i].0 as u32 {
219                    panic!("PARSE_COMMAND_MAP must be sorted by TpmCc.");
220                }
221                i += 1;
222            }
223        };
224
225        const _: () = {
226            let mut i = 1;
227            while i < PARSE_RESPONSE_MAP.len() {
228                if PARSE_RESPONSE_MAP[i - 1].0 as u32 > PARSE_RESPONSE_MAP[i].0 as u32 {
229                    panic!("PARSE_RESPONSE_MAP must be sorted by TpmCc.");
230                }
231                i += 1;
232            }
233        };
234    };
235}
236
237#[macro_export]
238macro_rules! tpm_handle {
239    (
240        $(#[$meta:meta])*
241        $name:ident
242    ) => {
243        $(#[$meta])*
244        pub struct $name(pub u32);
245
246        impl From<u32> for $name {
247            fn from(val: u32) -> Self {
248                Self(val)
249            }
250        }
251
252        impl From<$name> for u32 {
253            fn from(val: $name) -> Self {
254                val.0
255            }
256        }
257
258        impl $crate::TpmBuild for $name {
259            fn build(&self, writer: &mut $crate::TpmWriter) -> $crate::TpmResult<()> {
260                $crate::TpmBuild::build(&self.0, writer)
261            }
262        }
263
264        impl $crate::TpmParse for $name {
265            fn parse(buf: &[u8]) -> $crate::TpmResult<(Self, &[u8])> {
266                let (val, buf) = u32::parse(buf)?;
267                Ok((Self(val), buf))
268            }
269        }
270
271        impl $crate::TpmSized for $name {
272            const SIZE: usize = core::mem::size_of::<u32>();
273            fn len(&self) -> usize {
274                Self::SIZE
275            }
276        }
277
278        impl core::fmt::Display for $name {
279            fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
280                core::fmt::Display::fmt(&self.0, f)
281            }
282        }
283
284        impl core::fmt::LowerHex for $name {
285            fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
286                core::fmt::LowerHex::fmt(&self.0, f)
287            }
288        }
289
290        impl core::fmt::UpperHex for $name {
291            fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
292                core::fmt::UpperHex::fmt(&self.0, f)
293            }
294        }
295    };
296}
297
298#[macro_export]
299macro_rules! tpm2b {
300    ($name:ident, $capacity:expr) => {
301        pub type $name = $crate::TpmBuffer<$capacity>;
302    };
303}
304
305#[macro_export]
306macro_rules! tpm2b_struct {
307    (
308        $(#[$meta:meta])*
309        $wrapper_ty:ident, $inner_ty:ty) => {
310        $(#[$meta])*
311        pub struct $wrapper_ty {
312            pub inner: $inner_ty,
313        }
314
315        impl $crate::TpmSized for $wrapper_ty {
316            const SIZE: usize = core::mem::size_of::<u16>() + <$inner_ty>::SIZE;
317            fn len(&self) -> usize {
318                core::mem::size_of::<u16>() + $crate::TpmSized::len(&self.inner)
319            }
320        }
321
322        impl $crate::TpmBuild for $wrapper_ty {
323            fn build(&self, writer: &mut $crate::TpmWriter) -> $crate::TpmResult<()> {
324                let inner_len = $crate::TpmSized::len(&self.inner);
325                u16::try_from(inner_len)
326                    .map_err(|_| $crate::TpmErrorKind::ParseCapacity)?
327                    .build(writer)?;
328                $crate::TpmBuild::build(&self.inner, writer)
329            }
330        }
331
332        impl $crate::TpmParse for $wrapper_ty {
333            fn parse(buf: &[u8]) -> $crate::TpmResult<(Self, &[u8])> {
334                let (inner_bytes, rest) = $crate::parse_tpm2b(buf)?;
335                let (inner_val, tail) = <$inner_ty>::parse(inner_bytes)?;
336
337                if !tail.is_empty() {
338                    return Err($crate::TpmErrorKind::TrailingData);
339                }
340
341                Ok((Self { inner: inner_val }, rest))
342            }
343        }
344
345        impl From<$inner_ty> for $wrapper_ty {
346            fn from(inner: $inner_ty) -> Self {
347                Self { inner }
348            }
349        }
350
351        impl core::ops::Deref for $wrapper_ty {
352            type Target = $inner_ty;
353            fn deref(&self) -> &Self::Target {
354                &self.inner
355            }
356        }
357
358        impl core::ops::DerefMut for $wrapper_ty {
359            fn deref_mut(&mut self) -> &mut Self::Target {
360                &mut self.inner
361            }
362        }
363    };
364}
365
366#[macro_export]
367macro_rules! tpml {
368    ($name:ident, $inner_ty:ty, $capacity:expr) => {
369        pub type $name = $crate::TpmList<$inner_ty, $capacity>;
370    };
371}