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