tpm2_protocol/
macro.rs

1// SPDX-License-Identifier: MIT OR Apache-2.0
2// Copyright (c) 2025 Opinsys Oy
3// Copyright (c) 2024-2025 Jarkko Sakkinen
4
5#[macro_export]
6macro_rules! tpm_bitflags {
7    (
8        $(#[$outer:meta])*
9        $vis:vis struct $name:ident($repr:ty) {
10            $(
11                $(#[$inner:meta])*
12                const $field:ident = $value:expr;
13            )*
14        }
15    ) => {
16        $(#[$outer])*
17        $vis struct $name($repr);
18
19        impl $name {
20            $(
21                $(#[$inner])*
22                pub const $field: Self = Self($value);
23            )*
24
25            #[must_use]
26            pub const fn bits(&self) -> $repr {
27                self.0
28            }
29
30            #[must_use]
31            pub const fn from_bits_truncate(bits: $repr) -> Self {
32                Self(bits)
33            }
34
35            #[must_use]
36            pub const fn empty() -> Self {
37                Self(0)
38            }
39
40            #[must_use]
41            pub const fn contains(&self, other: Self) -> bool {
42                (self.0 & other.0) == other.0
43            }
44        }
45
46        impl core::ops::BitOr for $name {
47            type Output = Self;
48            fn bitor(self, rhs: Self) -> Self::Output {
49                Self(self.0 | rhs.0)
50            }
51        }
52
53        impl core::ops::BitOrAssign for $name {
54            fn bitor_assign(&mut self, rhs: Self) {
55                self.0 |= rhs.0;
56            }
57        }
58
59        impl $crate::TpmBuild for $name {
60            fn build(&self, writer: &mut $crate::TpmWriter) -> $crate::TpmResult<()> {
61                self.0.build(writer)
62            }
63        }
64
65        impl<'a> $crate::TpmParse<'a> for $name {
66            fn parse(buf: &'a [u8]) -> $crate::TpmResult<(Self, &'a [u8])> {
67                let (val, buf) = <$repr>::parse(buf)?;
68                Ok((Self(val), buf))
69            }
70        }
71
72        impl $crate::TpmSized for $name {
73            const SIZE: usize = core::mem::size_of::<$repr>();
74            fn len(&self) -> usize {
75                Self::SIZE
76            }
77        }
78    };
79}
80
81#[macro_export]
82macro_rules! tpm_bool {
83    (
84        $(#[$outer:meta])*
85        $vis:vis struct $name:ident(bool);
86    ) => {
87        $(#[$outer])*
88        $vis struct $name(pub bool);
89
90        impl From<bool> for $name {
91            fn from(val: bool) -> Self {
92                Self(val)
93            }
94        }
95
96        impl From<$name> for bool {
97            fn from(val: $name) -> Self {
98                val.0
99            }
100        }
101
102        impl $crate::TpmBuild for $name {
103            fn build(&self, writer: &mut $crate::TpmWriter) -> $crate::TpmResult<()> {
104                u8::from(self.0).build(writer)
105            }
106        }
107
108        impl<'a> $crate::TpmParse<'a> for $name {
109            fn parse(buf: &'a [u8]) -> $crate::TpmResult<(Self, &'a [u8])> {
110                let (val, buf) = u8::parse(buf)?;
111                match val {
112                    0 => Ok((Self(false), buf)),
113                    1 => Ok((Self(true), buf)),
114                    _ => Err($crate::TpmErrorKind::InvalidDiscriminant {
115                        type_name: stringify!($name),
116                        value: u64::from(val),
117                    }),
118                }
119            }
120        }
121
122        impl $crate::TpmSized for $name {
123            const SIZE: usize = core::mem::size_of::<u8>();
124            fn len(&self) -> usize {
125                Self::SIZE
126            }
127        }
128    };
129}
130
131#[macro_export]
132macro_rules! tpm_dispatch {
133    ( $( ($cmd:ident, $resp:ident, $variant:ident) ),* $(,)? ) => {
134        macro_rules! tpm_command_parser {
135            ($value:ty, $name:ident) => {
136                (
137                    <$value as $crate::message::TpmHeader>::COMMAND,
138                    <$value as $crate::message::TpmHeader>::NO_SESSIONS,
139                    <$value as $crate::message::TpmHeader>::WITH_SESSIONS,
140                    <$value as $crate::message::TpmHeader>::HANDLES,
141                    |buf| <$value>::parse(buf).map(|(c, r)| (TpmCommandBody::$name(c), r)),
142                )
143            };
144        }
145
146        macro_rules! tpm_response_parser {
147            ($rsp_ty:ty, $enum_variant:ident) => {
148                (
149                    <$rsp_ty as $crate::message::TpmHeader>::COMMAND,
150                    <$rsp_ty as $crate::message::TpmHeader>::WITH_SESSIONS,
151                    |buf| {
152                        <$rsp_ty>::parse(buf)
153                            .map(|(r, rest)| (TpmResponseBody::$enum_variant(r), rest))
154                    },
155                )
156            };
157        }
158
159        /// A TPM command
160        #[derive(Debug, PartialEq, Eq, Clone)]
161        pub enum TpmCommandBody {
162            $( $variant($cmd), )*
163        }
164
165        /// A TPM response body
166        #[allow(clippy::large_enum_variant)]
167        #[derive(Debug, PartialEq, Eq, Clone)]
168        pub enum TpmResponseBody {
169            $( $variant($resp), )*
170        }
171
172        impl TpmResponseBody {
173            $(
174                /// Attempts to convert the `TpmResponseBody` into a specific response type.
175                ///
176                /// # Errors
177                ///
178                /// Returns the original `TpmResponseBody` as an error if the enum variant does not match.
179                #[allow(non_snake_case, clippy::result_large_err)]
180                pub fn $variant(self) -> Result<$resp, Self> {
181                    if let Self::$variant(r) = self {
182                        Ok(r)
183                    } else {
184                        Err(self)
185                    }
186                }
187            )*
188        }
189
190        pub type TpmCommandParser = for<'a> fn(&'a [u8]) -> $crate::TpmResult<(TpmCommandBody, &'a [u8])>;
191        pub type TpmResponseParser = for<'a> fn(&'a [u8]) -> $crate::TpmResult<(TpmResponseBody, &'a [u8])>;
192
193        pub(crate) static PARSE_COMMAND_MAP: &[(
194            $crate::data::TpmCc,
195            bool,
196            bool,
197            usize,
198            TpmCommandParser,
199        )] = &[
200            $( tpm_command_parser!($cmd, $variant), )*
201        ];
202
203        pub(crate) static PARSE_RESPONSE_MAP: &[(
204            $crate::data::TpmCc,
205            bool,
206            TpmResponseParser,
207        )] = &[
208            $( tpm_response_parser!($resp, $variant), )*
209        ];
210
211        const _: () = {
212            let mut i = 1;
213            while i < PARSE_COMMAND_MAP.len() {
214                if PARSE_COMMAND_MAP[i - 1].0 as u32 > PARSE_COMMAND_MAP[i].0 as u32 {
215                    panic!("PARSE_COMMAND_MAP must be sorted by TpmCc.");
216                }
217                i += 1;
218            }
219        };
220
221        const _: () = {
222            let mut i = 1;
223            while i < PARSE_RESPONSE_MAP.len() {
224                if PARSE_RESPONSE_MAP[i - 1].0 as u32 > PARSE_RESPONSE_MAP[i].0 as u32 {
225                    panic!("PARSE_RESPONSE_MAP must be sorted by TpmCc.");
226                }
227                i += 1;
228            }
229        };
230    };
231}
232
233#[macro_export]
234macro_rules! tpm_enum {
235    (
236        $(#[$enum_meta:meta])*
237        $vis:vis enum $name:ident($repr:ty) {
238            $(
239                $(#[$variant_meta:meta])*
240                ($variant:ident, $value:expr, $display:literal)
241            ),* $(,)?
242        }
243    ) => {
244        $(#[$enum_meta])*
245        #[repr($repr)]
246        $vis enum $name {
247            $(
248                $(#[$variant_meta])*
249                $variant = $value
250            ),*
251        }
252
253        impl TryFrom<$repr> for $name {
254            type Error = ();
255
256            #[allow(clippy::cognitive_complexity)]
257            fn try_from(value: $repr) -> Result<Self, ()> {
258                $(
259                    if value == $value {
260                        return Ok(Self::$variant);
261                    }
262                )*
263                Err(())
264            }
265        }
266
267        impl core::fmt::Display for $name {
268            fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
269                let s = match self {
270                    $(Self::$variant => $display),*
271                };
272                write!(f, "{}", s)
273            }
274        }
275
276        impl core::str::FromStr for $name {
277            type Err = ();
278
279            fn from_str(s: &str) -> Result<Self, Self::Err> {
280                match s {
281                    $($display => Ok(Self::$variant),)*
282                    _ => Err(()),
283                }
284            }
285        }
286
287        impl $crate::TpmSized for $name {
288            const SIZE: usize = core::mem::size_of::<$repr>();
289
290            fn len(&self) -> usize {
291                Self::SIZE
292            }
293        }
294
295        impl $crate::TpmBuild for $name {
296            fn build(&self, writer: &mut $crate::TpmWriter) -> $crate::TpmResult<()> {
297                (*self as $repr).build(writer)
298            }
299        }
300
301        impl<'a> $crate::TpmParse<'a> for $name {
302            fn parse(buf: &'a [u8]) -> $crate::TpmResult<(Self, &'a [u8])> {
303                let (val, buf) = <$repr>::parse(buf)?;
304                let enum_val = Self::try_from(val).map_err(|()| $crate::TpmErrorKind::InvalidDiscriminant {
305                    type_name: stringify!($name),
306                    value: u64::from(val)
307                })?;
308                Ok((enum_val, buf))
309            }
310        }
311    };
312}
313
314#[macro_export]
315macro_rules! tpm_handle {
316    (
317        $(#[$meta:meta])*
318        $name:ident
319    ) => {
320        $(#[$meta])*
321        pub struct $name(pub u32);
322
323        impl From<u32> for $name {
324            fn from(val: u32) -> Self {
325                Self(val)
326            }
327        }
328
329        impl From<$name> for u32 {
330            fn from(val: $name) -> Self {
331                val.0
332            }
333        }
334
335        impl $crate::TpmBuild for $name {
336            fn build(&self, writer: &mut TpmWriter) -> $crate::TpmResult<()> {
337                self.0.build(writer)
338            }
339        }
340
341        impl<'a> $crate::TpmParse<'a> for $name {
342            fn parse(buf: &'a [u8]) -> $crate::TpmResult<(Self, &'a [u8])> {
343                let (val, buf) = u32::parse(buf)?;
344                Ok((Self(val), buf))
345            }
346        }
347
348        impl $crate::TpmSized for $name {
349            const SIZE: usize = core::mem::size_of::<u32>();
350            fn len(&self) -> usize {
351                Self::SIZE
352            }
353        }
354
355        impl core::fmt::Display for $name {
356            fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
357                core::fmt::Display::fmt(&self.0, f)
358            }
359        }
360
361        impl core::fmt::LowerHex for $name {
362            fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
363                core::fmt::LowerHex::fmt(&self.0, f)
364            }
365        }
366
367        impl core::fmt::UpperHex for $name {
368            fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
369                core::fmt::UpperHex::fmt(&self.0, f)
370            }
371        }
372    };
373}
374
375#[macro_export]
376macro_rules! tpm_response {
377    (
378        $(#[$meta:meta])*
379        $name:ident,
380        $cc:expr,
381        $no_sessions:expr,
382        $with_sessions:expr,
383        $(pub $handle_field:ident: $handle_type:ty,)*
384        {
385            $(pub $param_field:ident: $param_type:ty),*
386            $(,)?
387        }
388    ) => {
389        $(#[$meta])*
390        pub struct $name {
391            $(pub $handle_field: $handle_type,)*
392            $(pub $param_field: $param_type,)*
393        }
394
395        impl $crate::message::TpmHeader<'_> for $name {
396            const COMMAND: $crate::data::TpmCc = $cc;
397            const NO_SESSIONS: bool = $no_sessions;
398            const WITH_SESSIONS: bool = $with_sessions;
399            const HANDLES: usize = 0 $(+ {let _ = stringify!($handle_field); 1})*;
400        }
401
402        impl $crate::TpmSized for $name {
403            const SIZE: usize = 0 $(+ <$handle_type>::SIZE)* $(+ <$param_type>::SIZE)*;
404            fn len(&self) -> usize {
405                let params_len: usize = 0 $(+ self.$param_field.len())*;
406                let handles_len: usize = 0 $(+ self.$handle_field.len())*;
407                let parameter_area_size_field_len: usize = core::mem::size_of::<u32>();
408                handles_len + parameter_area_size_field_len + params_len
409            }
410        }
411
412        impl $crate::TpmBuild for $name {
413            fn build(&self, writer: &mut $crate::TpmWriter) -> $crate::TpmResult<()> {
414                let params_len: usize = 0 $(+ self.$param_field.len())*;
415                let params_len_u32 = u32::try_from(params_len)
416                    .map_err(|_| $crate::TpmErrorKind::ValueTooLarge)?;
417
418                $(self.$handle_field.build(writer)?;)*
419                params_len_u32.build(writer)?;
420                $(self.$param_field.build(writer)?;)*
421
422                Ok(())
423            }
424        }
425
426        impl<'a> $crate::TpmParse<'a> for $name {
427            fn parse(buf: &'a [u8]) -> $crate::TpmResult<(Self, &'a [u8])> {
428                #[allow(unused_mut)]
429                let mut cursor = buf;
430                $(
431                    let ($handle_field, tail) = <$handle_type>::parse(cursor)?;
432                    cursor = tail;
433                )*
434
435                let (mut params, tail) = $crate::TpmParameters::new(cursor)?;
436                $(
437                    let $param_field = params.parse::<$param_type>()?;
438                )*
439                if !params.is_empty() {
440                    return Err($crate::TpmErrorKind::TrailingData);
441                }
442
443                Ok((
444                    Self {
445                        $( $handle_field, )*
446                        $( $param_field, )*
447                    },
448                    tail,
449                ))
450            }
451        }
452    };
453}
454
455#[macro_export]
456macro_rules! tpm_struct {
457    (
458        $(#[$meta:meta])*
459        $name:ident,
460        $cc:expr,
461        $no_sessions:expr,
462        $with_sessions:expr,
463        $handles:expr,
464        {
465            $(pub $field_name:ident: $field_type:ty),*
466            $(,)?
467        }
468    ) => {
469        $crate::tpm_struct! {
470            $(#[$meta])*
471            pub struct $name {
472                $(pub $field_name: $field_type,)*
473            }
474        }
475
476        impl $crate::message::TpmHeader<'_> for $name {
477            const COMMAND: $crate::data::TpmCc = $cc;
478            const NO_SESSIONS: bool = $no_sessions;
479            const WITH_SESSIONS: bool = $with_sessions;
480            const HANDLES: usize = $handles;
481        }
482    };
483
484    (
485        $(#[$meta:meta])*
486        $vis:vis struct $name:ident {
487            $(pub $field_name:ident: $field_type:ty),*
488            $(,)?
489        }
490    ) => {
491        $(#[$meta])*
492        $vis struct $name {
493            $(pub $field_name: $field_type,)*
494        }
495
496        impl $crate::TpmSized for $name {
497            const SIZE: usize = 0 $(+ <$field_type>::SIZE)*;
498            fn len(&self) -> usize {
499                0 $(+ self.$field_name.len())*
500            }
501        }
502
503        impl $crate::TpmBuild for $name {
504            #[allow(unused_variables)]
505            fn build(&self, writer: &mut $crate::TpmWriter) -> $crate::TpmResult<()> {
506                $(self.$field_name.build(writer)?;)*
507                Ok(())
508            }
509        }
510
511        impl<'a> $crate::TpmParse<'a> for $name {
512            fn parse(buf: &'a [u8]) -> $crate::TpmResult<(Self, &'a [u8])> {
513                $(let ($field_name, buf) = <$field_type>::parse(buf)?;)*
514                Ok((
515                    Self {
516                        $($field_name,)*
517                    },
518                    buf,
519                ))
520            }
521        }
522    };
523}
524
525#[macro_export]
526macro_rules! tpm2b {
527    ($name:ident, $capacity:expr) => {
528        pub type $name = $crate::TpmBuffer<$capacity>;
529    };
530}
531
532#[macro_export]
533macro_rules! tpm2b_struct {
534    (
535        $(#[$meta:meta])*
536        $wrapper_ty:ident, $inner_ty:ty) => {
537        $(#[$meta])*
538        pub struct $wrapper_ty {
539            pub inner: $inner_ty,
540        }
541
542        impl $crate::TpmSized for $wrapper_ty {
543            const SIZE: usize = core::mem::size_of::<u16>() + <$inner_ty>::SIZE;
544            fn len(&self) -> usize {
545                core::mem::size_of::<u16>() + self.inner.len()
546            }
547        }
548
549        impl $crate::TpmBuild for $wrapper_ty {
550            fn build(&self, writer: &mut $crate::TpmWriter) -> $crate::TpmResult<()> {
551                let inner_len = self.inner.len();
552                u16::try_from(inner_len)
553                    .map_err(|_| $crate::TpmErrorKind::ValueTooLarge)?
554                    .build(writer)?;
555                self.inner.build(writer)
556            }
557        }
558
559        impl<'a> $crate::TpmParse<'a> for $wrapper_ty {
560            fn parse(buf: &'a [u8]) -> $crate::TpmResult<(Self, &'a [u8])> {
561                let (inner_bytes, rest) = $crate::parse_tpm2b(buf)?;
562                let (inner_val, tail) = <$inner_ty>::parse(inner_bytes)?;
563
564                if !tail.is_empty() {
565                    return Err($crate::TpmErrorKind::TrailingData);
566                }
567
568                Ok((Self { inner: inner_val }, rest))
569            }
570        }
571
572        impl From<$inner_ty> for $wrapper_ty {
573            fn from(inner: $inner_ty) -> Self {
574                Self { inner }
575            }
576        }
577
578        impl core::ops::Deref for $wrapper_ty {
579            type Target = $inner_ty;
580            fn deref(&self) -> &Self::Target {
581                &self.inner
582            }
583        }
584
585        impl core::ops::DerefMut for $wrapper_ty {
586            fn deref_mut(&mut self) -> &mut Self::Target {
587                &mut self.inner
588            }
589        }
590    };
591}
592
593#[macro_export]
594macro_rules! tpml {
595    ($name:ident, $inner_ty:ty, $capacity:expr) => {
596        pub type $name = $crate::TpmList<$inner_ty, $capacity>;
597    };
598}