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