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 response;
6pub mod r#struct;
7
8#[macro_export]
9macro_rules! tpm_bitflags {
10    (
11        $(#[$outer:meta])*
12        $vis:vis struct $name:ident($repr:ty) {
13            $(
14                $(#[$inner:meta])*
15                const $field:ident = $value:expr, $string_name:literal;
16            )*
17        }
18    ) => {
19        $(#[$outer])*
20        $vis struct $name($repr);
21
22        impl $name {
23            $(
24                $(#[$inner])*
25                pub const $field: Self = Self($value);
26            )*
27
28            #[must_use]
29            pub const fn bits(&self) -> $repr {
30                self.0
31            }
32
33            #[must_use]
34            pub const fn from_bits_truncate(bits: $repr) -> Self {
35                Self(bits)
36            }
37
38            #[must_use]
39            pub const fn empty() -> Self {
40                Self(0)
41            }
42
43            #[must_use]
44            pub const fn contains(&self, other: Self) -> bool {
45                (self.0 & other.0) == other.0
46            }
47
48            pub fn flag_names(&self) -> impl Iterator<Item = &'static str> + '_ {
49                [
50                    $(
51                        (Self::$field, $string_name),
52                    )*
53                ]
54                .into_iter()
55                .filter(move |(flag, _)| self.contains(*flag))
56                .map(|(_, name)| name)
57            }
58        }
59
60        impl core::ops::BitOr for $name {
61            type Output = Self;
62            fn bitor(self, rhs: Self) -> Self::Output {
63                Self(self.0 | rhs.0)
64            }
65        }
66
67        impl core::ops::BitOrAssign for $name {
68            fn bitor_assign(&mut self, rhs: Self) {
69                self.0 |= rhs.0;
70            }
71        }
72
73        impl $crate::TpmBuild for $name {
74            fn build(&self, writer: &mut $crate::TpmWriter) -> $crate::TpmResult<()> {
75                $crate::TpmBuild::build(&self.0, writer)
76            }
77        }
78
79        impl $crate::TpmParse for $name {
80            fn parse(buf: &[u8]) -> $crate::TpmResult<(Self, &[u8])> {
81                let (val, buf) = <$repr>::parse(buf)?;
82                Ok((Self(val), buf))
83            }
84        }
85
86        impl $crate::TpmSized for $name {
87            const SIZE: usize = core::mem::size_of::<$repr>();
88            fn len(&self) -> usize {
89                Self::SIZE
90            }
91        }
92    };
93}
94
95#[macro_export]
96macro_rules! tpm_bool {
97    (
98        $(#[$outer:meta])*
99        $vis:vis struct $name:ident(bool);
100    ) => {
101        $(#[$outer])*
102        $vis struct $name(pub bool);
103
104        impl From<bool> for $name {
105            fn from(val: bool) -> Self {
106                Self(val)
107            }
108        }
109
110        impl From<$name> for bool {
111            fn from(val: $name) -> Self {
112                val.0
113            }
114        }
115
116        impl $crate::TpmBuild for $name {
117            fn build(&self, writer: &mut $crate::TpmWriter) -> $crate::TpmResult<()> {
118                $crate::TpmBuild::build(&u8::from(self.0), writer)
119            }
120        }
121
122        impl $crate::TpmParse for $name {
123            fn parse(buf: &[u8]) -> $crate::TpmResult<(Self, &[u8])> {
124                let (val, buf) = u8::parse(buf)?;
125                match val {
126                    0 => Ok((Self(false), buf)),
127                    1 => Ok((Self(true), buf)),
128                    _ => Err($crate::TpmErrorKind::InvalidDiscriminant {
129                        type_name: stringify!($name),
130                        value: u64::from(val),
131                    }),
132                }
133            }
134        }
135
136        impl $crate::TpmSized for $name {
137            const SIZE: usize = core::mem::size_of::<u8>();
138            fn len(&self) -> usize {
139                Self::SIZE
140            }
141        }
142    };
143}
144
145#[macro_export]
146macro_rules! tpm_dispatch {
147    ( $( ($cmd:ident, $resp:ident, $variant:ident) ),* $(,)? ) => {
148        macro_rules! tpm_command_parser {
149            ($value:ty, $name:ident) => {
150                (
151                    <$value as $crate::message::TpmHeader>::COMMAND,
152                    <$value as $crate::message::TpmHeader>::NO_SESSIONS,
153                    <$value as $crate::message::TpmHeader>::WITH_SESSIONS,
154                    <$value as $crate::message::TpmHeader>::HANDLES,
155                    |buf| <$value>::parse(buf).map(|(c, r)| (TpmCommandBody::$name(c), r)),
156                )
157            };
158        }
159
160        macro_rules! tpm_response_parser {
161            ($rsp_ty:ty, $enum_variant:ident) => {
162                (
163                    <$rsp_ty as $crate::message::TpmHeader>::COMMAND,
164                    <$rsp_ty as $crate::message::TpmHeader>::WITH_SESSIONS,
165                    |buf| {
166                        <$rsp_ty>::parse(buf)
167                            .map(|(r, rest)| (TpmResponseBody::$enum_variant(r), rest))
168                    },
169                )
170            };
171        }
172
173        /// A TPM command
174        #[derive(Debug, PartialEq, Eq, Clone)]
175        pub enum TpmCommandBody {
176            $( $variant($cmd), )*
177        }
178
179        /// A TPM response body
180        #[allow(clippy::large_enum_variant)]
181        #[derive(Debug, PartialEq, Eq, Clone)]
182        pub enum TpmResponseBody {
183            $( $variant($resp), )*
184        }
185
186        impl TpmResponseBody {
187            $(
188                /// Attempts to convert the `TpmResponseBody` into a specific response type.
189                ///
190                /// # Errors
191                ///
192                /// Returns the original `TpmResponseBody` as an error if the enum variant does not match.
193                #[allow(non_snake_case, clippy::result_large_err)]
194                pub fn $variant(self) -> Result<$resp, Self> {
195                    if let Self::$variant(r) = self {
196                        Ok(r)
197                    } else {
198                        Err(self)
199                    }
200                }
201            )*
202        }
203
204        pub type TpmCommandParser = for<'a> fn(&'a [u8]) -> $crate::TpmResult<(TpmCommandBody, &'a [u8])>;
205        pub type TpmResponseParser = for<'a> fn(&'a [u8]) -> $crate::TpmResult<(TpmResponseBody, &'a [u8])>;
206
207        pub(crate) static PARSE_COMMAND_MAP: &[($crate::data::TpmCc, bool, bool, usize, TpmCommandParser)] =
208            &[$(tpm_command_parser!($cmd, $variant),)*];
209
210        pub(crate) static PARSE_RESPONSE_MAP: &[($crate::data::TpmCc, bool, TpmResponseParser)] =
211            &[$(tpm_response_parser!($resp, $variant),)*];
212
213        const _: () = {
214            let mut i = 1;
215            while i < PARSE_COMMAND_MAP.len() {
216                if PARSE_COMMAND_MAP[i - 1].0 as u32 > PARSE_COMMAND_MAP[i].0 as u32 {
217                    panic!("PARSE_COMMAND_MAP must be sorted by TpmCc.");
218                }
219                i += 1;
220            }
221        };
222
223        const _: () = {
224            let mut i = 1;
225            while i < PARSE_RESPONSE_MAP.len() {
226                if PARSE_RESPONSE_MAP[i - 1].0 as u32 > PARSE_RESPONSE_MAP[i].0 as u32 {
227                    panic!("PARSE_RESPONSE_MAP must be sorted by TpmCc.");
228                }
229                i += 1;
230            }
231        };
232    };
233}
234
235#[macro_export]
236macro_rules! tpm_enum {
237    (
238        $(#[$enum_meta:meta])*
239        $vis:vis enum $name:ident($repr:ty) {
240            $(
241                $(#[$variant_meta:meta])*
242                ($variant:ident, $value:expr, $display:literal)
243            ),* $(,)?
244        }
245    ) => {
246        $(#[$enum_meta])*
247        #[repr($repr)]
248        $vis enum $name {
249            $(
250                $(#[$variant_meta])*
251                $variant = $value
252            ),*
253        }
254
255        impl TryFrom<$repr> for $name {
256            type Error = ();
257
258            #[allow(clippy::cognitive_complexity)]
259            fn try_from(value: $repr) -> Result<Self, ()> {
260                $(
261                    if value == $value {
262                        return Ok(Self::$variant);
263                    }
264                )*
265                Err(())
266            }
267        }
268
269        impl core::fmt::Display for $name {
270            fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
271                let s = match self {
272                    $(Self::$variant => $display),*
273                };
274                write!(f, "{}", s)
275            }
276        }
277
278        impl core::str::FromStr for $name {
279            type Err = ();
280
281            fn from_str(s: &str) -> Result<Self, Self::Err> {
282                match s {
283                    $($display => Ok(Self::$variant),)*
284                    _ => Err(()),
285                }
286            }
287        }
288
289        impl $crate::TpmSized for $name {
290            const SIZE: usize = core::mem::size_of::<$repr>();
291
292            fn len(&self) -> usize {
293                Self::SIZE
294            }
295        }
296
297        impl $crate::TpmBuild for $name {
298            fn build(&self, writer: &mut $crate::TpmWriter) -> $crate::TpmResult<()> {
299                $crate::TpmBuild::build(&(*self as $repr), writer)
300            }
301        }
302
303        impl $crate::TpmParse for $name {
304            fn parse(buf: &[u8]) -> $crate::TpmResult<(Self, &[u8])> {
305                let (val, buf) = <$repr>::parse(buf)?;
306                let enum_val = Self::try_from(val).map_err(|()| $crate::TpmErrorKind::InvalidDiscriminant {
307                    type_name: stringify!($name),
308                    value: u64::from(val)
309                })?;
310                Ok((enum_val, buf))
311            }
312        }
313    };
314}
315
316#[macro_export]
317macro_rules! tpm_handle {
318    (
319        $(#[$meta:meta])*
320        $name:ident
321    ) => {
322        $(#[$meta])*
323        pub struct $name(pub u32);
324
325        impl From<u32> for $name {
326            fn from(val: u32) -> Self {
327                Self(val)
328            }
329        }
330
331        impl From<$name> for u32 {
332            fn from(val: $name) -> Self {
333                val.0
334            }
335        }
336
337        impl $crate::TpmBuild for $name {
338            fn build(&self, writer: &mut $crate::TpmWriter) -> $crate::TpmResult<()> {
339                $crate::TpmBuild::build(&self.0, writer)
340            }
341        }
342
343        impl $crate::TpmParse for $name {
344            fn parse(buf: &[u8]) -> $crate::TpmResult<(Self, &[u8])> {
345                let (val, buf) = u32::parse(buf)?;
346                Ok((Self(val), buf))
347            }
348        }
349
350        impl $crate::TpmSized for $name {
351            const SIZE: usize = core::mem::size_of::<u32>();
352            fn len(&self) -> usize {
353                Self::SIZE
354            }
355        }
356
357        impl core::fmt::Display for $name {
358            fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
359                core::fmt::Display::fmt(&self.0, f)
360            }
361        }
362
363        impl core::fmt::LowerHex for $name {
364            fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
365                core::fmt::LowerHex::fmt(&self.0, f)
366            }
367        }
368
369        impl core::fmt::UpperHex for $name {
370            fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
371                core::fmt::UpperHex::fmt(&self.0, f)
372            }
373        }
374    };
375}
376
377#[macro_export]
378macro_rules! tpm2b {
379    ($name:ident, $capacity:expr) => {
380        pub type $name = $crate::TpmBuffer<$capacity>;
381    };
382}
383
384#[macro_export]
385macro_rules! tpm2b_struct {
386    (
387        $(#[$meta:meta])*
388        $wrapper_ty:ident, $inner_ty:ty) => {
389        $(#[$meta])*
390        pub struct $wrapper_ty {
391            pub inner: $inner_ty,
392        }
393
394        impl $crate::TpmSized for $wrapper_ty {
395            const SIZE: usize = core::mem::size_of::<u16>() + <$inner_ty>::SIZE;
396            fn len(&self) -> usize {
397                core::mem::size_of::<u16>() + $crate::TpmSized::len(&self.inner)
398            }
399        }
400
401        impl $crate::TpmBuild for $wrapper_ty {
402            fn build(&self, writer: &mut $crate::TpmWriter) -> $crate::TpmResult<()> {
403                let inner_len = $crate::TpmSized::len(&self.inner);
404                u16::try_from(inner_len)
405                    .map_err(|_| $crate::TpmErrorKind::ValueTooLarge)?
406                    .build(writer)?;
407                $crate::TpmBuild::build(&self.inner, writer)
408            }
409        }
410
411        impl $crate::TpmParse for $wrapper_ty {
412            fn parse(buf: &[u8]) -> $crate::TpmResult<(Self, &[u8])> {
413                let (inner_bytes, rest) = $crate::parse_tpm2b(buf)?;
414                let (inner_val, tail) = <$inner_ty>::parse(inner_bytes)?;
415
416                if !tail.is_empty() {
417                    return Err($crate::TpmErrorKind::TrailingData);
418                }
419
420                Ok((Self { inner: inner_val }, rest))
421            }
422        }
423
424        impl From<$inner_ty> for $wrapper_ty {
425            fn from(inner: $inner_ty) -> Self {
426                Self { inner }
427            }
428        }
429
430        impl core::ops::Deref for $wrapper_ty {
431            type Target = $inner_ty;
432            fn deref(&self) -> &Self::Target {
433                &self.inner
434            }
435        }
436
437        impl core::ops::DerefMut for $wrapper_ty {
438            fn deref_mut(&mut self) -> &mut Self::Target {
439                &mut self.inner
440            }
441        }
442    };
443}
444
445#[macro_export]
446macro_rules! tpml {
447    ($name:ident, $inner_ty:ty, $capacity:expr) => {
448        pub type $name = $crate::TpmList<$inner_ty, $capacity>;
449    };
450}