Skip to main content

tpm2_protocol/macro/
struct.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_struct {
7    (@wire_field_methods [$($prev_type:ty,)*],) => {};
8
9    (@wire_field_methods [$($prev_type:ty,)*], pub $field_name:ident: $field_type:ty $(, pub $rest_field:ident: $rest_type:ty)* $(,)?) => {
10        /// Returns a borrowed field view.
11        ///
12        /// # Errors
13        ///
14        /// Returns `Err(TpmError)` when preceding fields or this field are malformed.
15        #[allow(unused_mut)]
16        pub fn $field_name(&self) -> $crate::TpmResult<&$field_type>
17        where
18            $($prev_type: for<'a> $crate::TpmField<'a>,)*
19            $field_type: for<'a> $crate::TpmField<'a, View = &'a $field_type>,
20        {
21            let mut cursor = &self.0;
22            $(
23                let (_, tail) = <$prev_type as $crate::TpmField>::cast_prefix_field(cursor)?;
24                cursor = tail;
25            )*
26
27            let (value, _) = <$field_type as $crate::TpmField>::cast_prefix_field(cursor)?;
28
29            Ok(value)
30        }
31
32        $crate::tpm_struct!(@wire_field_methods [$($prev_type,)* $field_type,], $(pub $rest_field: $rest_type),*);
33    };
34
35    (
36        $(#[$meta:meta])*
37        kind: Command,
38        name: $name:ident,
39        cc: $cc:expr,
40        handles: $count:literal,
41        parameters: {
42            $(pub $param_field:ident: $param_type:ty),*
43            $(,)?
44        }
45    ) => {
46        $(#[$meta])*
47        pub struct $name {
48            pub handles: [$crate::basic::TpmHandle; $count],
49            $(pub $param_field: $param_type,)*
50        }
51
52        impl $crate::frame::TpmHeader for $name {
53            const CC: $crate::data::TpmCc = $cc;
54            const HANDLES: usize = $count;
55        }
56
57        impl $name {
58            /// Casts a command frame into a typed wire view for this command.
59            ///
60            /// # Errors
61            ///
62            /// Returns `Err(TpmError)` when the frame is malformed or
63            /// has a different command code.
64            pub fn cast_frame(buf: &[u8]) -> $crate::TpmResult<&$crate::frame::TpmCommand> {
65                let command = <$crate::frame::TpmCommand>::cast(buf)?;
66
67                let cc = command.cc()?;
68                if cc != Self::CC {
69                    return Err($crate::TpmError::InvalidCc(
70                        $crate::TpmErrorValue::new(6).value(u64::from(cc.value())),
71                    ));
72                }
73
74                Ok(command)
75            }
76
77            /// Casts a mutable command frame into a typed mutable wire view for this command.
78            ///
79            /// # Errors
80            ///
81            /// Returns `Err(TpmError)` when the frame is malformed or
82            /// has a different command code.
83            pub fn cast_frame_mut(
84                buf: &mut [u8],
85            ) -> $crate::TpmResult<&mut $crate::frame::TpmCommand> {
86                let command = <$crate::frame::TpmCommand>::cast_mut(buf)?;
87
88                let cc = command.cc()?;
89                if cc != Self::CC {
90                    return Err($crate::TpmError::InvalidCc(
91                        $crate::TpmErrorValue::new(6).value(u64::from(cc.value())),
92                    ));
93                }
94
95                Ok(command)
96            }
97        }
98
99        impl $crate::frame::TpmFrame for $name {
100            fn cc(&self) -> $crate::data::TpmCc {
101                Self::CC
102            }
103            fn handles(&self) -> usize {
104                Self::HANDLES
105            }
106        }
107
108        impl $crate::TpmSized for $name {
109            const SIZE: usize = (Self::HANDLES * <$crate::basic::TpmHandle>::SIZE) $(+ <$param_type>::SIZE)*;
110            fn len(&self) -> usize {
111                (self.handles.len() * <$crate::basic::TpmHandle>::SIZE) $(+ $crate::TpmSized::len(&self.$param_field))*
112            }
113        }
114
115        impl $crate::TpmMarshal for $name {
116            #[allow(unused_variables)]
117            fn marshal(&self, writer: &mut $crate::TpmWriter) -> $crate::TpmResult<()> {
118                <Self as $crate::frame::TpmMarshalBody>::marshal_handles(self, writer)?;
119                <Self as $crate::frame::TpmMarshalBody>::marshal_parameters(self, writer)
120            }
121        }
122
123        impl $crate::frame::TpmMarshalBody for $name {
124            #[allow(unused_variables)]
125            fn marshal_handles(&self, writer: &mut $crate::TpmWriter) -> $crate::TpmResult<()> {
126                for handle in &self.handles {
127                    $crate::TpmMarshal::marshal(handle, writer)?;
128                }
129                Ok(())
130            }
131
132            #[allow(unused_variables)]
133            fn marshal_parameters(&self, writer: &mut $crate::TpmWriter) -> $crate::TpmResult<()> {
134                $($crate::TpmMarshal::marshal(&self.$param_field, writer)?;)*
135                Ok(())
136            }
137        }
138    };
139
140    (
141        $(#[$meta:meta])*
142        kind: Response,
143        name: $name:ident,
144        cc: $cc:expr,
145        handles: $count:literal,
146        parameters: {
147            $(pub $param_field:ident: $param_type:ty),*
148            $(,)?
149        }
150    ) => {
151        $(#[$meta])*
152        pub struct $name {
153            pub handles: [$crate::basic::TpmHandle; $count],
154            $(pub $param_field: $param_type,)*
155        }
156
157        impl $crate::frame::TpmHeader for $name {
158            const CC: $crate::data::TpmCc = $cc;
159            const HANDLES: usize = $count;
160        }
161
162        impl $name {
163            /// Casts a response frame into a typed wire view for this response.
164            ///
165            /// # Errors
166            ///
167            /// Returns `Err(TpmError)` when the frame envelope is malformed.
168            pub fn cast_frame(buf: &[u8]) -> $crate::TpmResult<&$crate::frame::TpmResponse> {
169                <$crate::frame::TpmResponse>::cast(buf)
170            }
171
172            /// Casts a mutable response frame into a typed mutable wire view for this response.
173            ///
174            /// # Errors
175            ///
176            /// Returns `Err(TpmError)` when the frame envelope is malformed.
177            pub fn cast_frame_mut(
178                buf: &mut [u8],
179            ) -> $crate::TpmResult<&mut $crate::frame::TpmResponse> {
180                <$crate::frame::TpmResponse>::cast_mut(buf)
181            }
182        }
183
184        impl $crate::frame::TpmFrame for $name {
185            fn cc(&self) -> $crate::data::TpmCc {
186                Self::CC
187            }
188            fn handles(&self) -> usize {
189                Self::HANDLES
190            }
191        }
192
193        impl $crate::frame::TpmMarshalBody for $name {
194            #[allow(unused_variables)]
195            fn marshal_handles(&self, writer: &mut $crate::TpmWriter) -> $crate::TpmResult<()> {
196                for handle in &self.handles {
197                    $crate::TpmMarshal::marshal(handle, writer)?;
198                }
199                Ok(())
200            }
201            #[allow(unused_variables)]
202            fn marshal_parameters(&self, writer: &mut $crate::TpmWriter) -> $crate::TpmResult<()> {
203                $($crate::TpmMarshal::marshal(&self.$param_field, writer)?;)*
204                Ok(())
205            }
206        }
207
208        impl $crate::TpmSized for $name {
209            const SIZE: usize = (Self::HANDLES * <$crate::basic::TpmHandle>::SIZE) $(+ <$param_type>::SIZE)*;
210            fn len(&self) -> usize {
211                (self.handles.len() * <$crate::basic::TpmHandle>::SIZE) $(+ $crate::TpmSized::len(&self.$param_field))*
212            }
213        }
214
215        impl $crate::TpmMarshal for $name {
216            fn marshal(&self, writer: &mut $crate::TpmWriter) -> $crate::TpmResult<()> {
217                <Self as $crate::frame::TpmMarshalBody>::marshal_handles(self, writer)?;
218                <Self as $crate::frame::TpmMarshalBody>::marshal_parameters(self, writer)
219            }
220        }
221    };
222
223    (
224        $(#[$meta:meta])*
225        wire: $wire:ident,
226        $vis:vis struct $name:ident {
227            $(pub $field_name:ident: $field_type:ty),*
228            $(,)?
229        }
230    ) => {
231        $(#[$meta])*
232        $vis struct $name {
233            $(pub $field_name: $field_type,)*
234        }
235
236        #[repr(transparent)]
237        $vis struct $wire([u8]);
238
239        impl $wire {
240            /// Casts bytes into a typed wire structure view.
241            ///
242            /// # Errors
243            ///
244            /// Returns `Err(TpmError)` when `buf` is not exactly one valid wire
245            /// structure.
246            pub fn cast(buf: &[u8]) -> $crate::TpmResult<&Self>
247            where
248                $($field_type: for<'a> $crate::TpmField<'a>,)*
249            {
250                Self::validate(buf)?;
251
252                // SAFETY: `validate` checked the complete wire structure.
253                Ok(unsafe { Self::cast_unchecked(buf) })
254            }
255
256            /// Casts the first typed wire structure from `buf` and returns the remainder.
257            ///
258            /// # Errors
259            ///
260            /// Returns `Err(TpmError)` when `buf` does not start with a valid wire
261            /// structure.
262            pub fn cast_prefix(buf: &[u8]) -> $crate::TpmResult<(&Self, &[u8])>
263            where
264                $($field_type: for<'a> $crate::TpmField<'a>,)*
265            {
266                let wire_len = Self::validate_prefix(buf)?;
267                if buf.len() < wire_len {
268                    return Err($crate::TpmError::UnexpectedEnd(
269                        $crate::TpmErrorValue::new(0).size(wire_len, buf.len()),
270                    ));
271                }
272
273                let (head, tail) = buf.split_at(wire_len);
274
275                // SAFETY: `validate_prefix` checked the complete wire structure.
276                Ok((unsafe { Self::cast_unchecked(head) }, tail))
277            }
278
279            /// Casts bytes into a typed wire structure view without validation.
280            ///
281            /// # Safety
282            ///
283            /// The caller must ensure `buf` is exactly one valid wire structure.
284            #[must_use]
285            pub unsafe fn cast_unchecked(buf: &[u8]) -> &Self {
286                let ptr = core::ptr::from_ref(buf) as *const Self;
287
288                // SAFETY: `$wire` is `repr(transparent)` over `[u8]`, so it has
289                // the same layout, metadata, and alignment as the referenced slice.
290                unsafe { &*ptr }
291            }
292
293            /// Returns the complete wire structure bytes.
294            #[must_use]
295            pub const fn as_bytes(&self) -> &[u8] {
296                &self.0
297            }
298
299            /// Validates an exact typed wire structure view.
300            ///
301            /// # Errors
302            ///
303            /// Returns `Err(TpmError)` when `buf` is not exactly one valid wire
304            /// structure.
305            pub fn validate(buf: &[u8]) -> $crate::TpmResult<()>
306            where
307                $($field_type: for<'a> $crate::TpmField<'a>,)*
308            {
309                let wire_len = Self::validate_prefix(buf)?;
310
311                if buf.len() > wire_len {
312                    return Err($crate::TpmError::TrailingData(
313                        $crate::TpmErrorValue::new(wire_len).actual(buf.len() - wire_len),
314                    ));
315                }
316
317                Ok(())
318            }
319
320            #[allow(unused_assignments, unused_mut, unused_variables)]
321            /// Validates a typed wire structure prefix.
322            ///
323            /// # Errors
324            ///
325            /// Returns `Err(TpmError)` when `buf` does not start with a valid wire
326            /// structure.
327            pub fn validate_prefix(buf: &[u8]) -> $crate::TpmResult<usize>
328            where
329                $($field_type: for<'a> $crate::TpmField<'a>,)*
330            {
331                let mut cursor = buf;
332                let mut consumed = 0usize;
333
334                $(
335                    let before = cursor.len();
336                    let (_, tail) = <$field_type as $crate::TpmField>::cast_prefix_field(cursor)?;
337                    let field_len = before.checked_sub(tail.len()).ok_or(
338                        $crate::TpmError::IntegerTooLarge(
339                            $crate::TpmErrorValue::new(consumed).value_usize(tail.len()),
340                        ),
341                    )?;
342                    consumed = consumed.checked_add(field_len).ok_or(
343                        $crate::TpmError::IntegerTooLarge(
344                            $crate::TpmErrorValue::new(consumed).value_usize(before),
345                        ),
346                    )?;
347                    cursor = tail;
348                )*
349
350                Ok(consumed)
351            }
352
353            $crate::tpm_struct!(@wire_field_methods [], $(pub $field_name: $field_type),*);
354        }
355
356        impl AsRef<[u8]> for $wire {
357            fn as_ref(&self) -> &[u8] {
358                self.as_bytes()
359            }
360        }
361
362        impl<'a> $crate::TpmField<'a> for $name
363        where
364            $($field_type: for<'b> $crate::TpmField<'b>,)*
365        {
366            type View = &'a $wire;
367
368            fn cast_prefix_field(buf: &'a [u8]) -> $crate::TpmResult<(Self::View, &'a [u8])> {
369                $wire::cast_prefix(buf)
370            }
371        }
372
373        impl $crate::TpmSized for $name {
374            const SIZE: usize = 0 $(+ <$field_type>::SIZE)*;
375            fn len(&self) -> usize {
376                0 $(+ $crate::TpmSized::len(&self.$field_name))*
377            }
378        }
379
380        impl $crate::TpmMarshal for $name {
381            #[allow(unused_variables)]
382            fn marshal(&self, writer: &mut $crate::TpmWriter) -> $crate::TpmResult<()> {
383                $( $crate::TpmMarshal::marshal(&self.$field_name, writer)?; )*
384                Ok(())
385            }
386        }
387
388    };
389}
390
391#[macro_export]
392macro_rules! tpm2b {
393    ($name:ident, $capacity:expr) => {
394        pub type $name = $crate::basic::TpmBuffer<$capacity>;
395    };
396}
397
398#[macro_export]
399macro_rules! tpm2b_struct {
400    (
401        $(#[$meta:meta])*
402        wire: $wire_ty:ident,
403        $wrapper_ty:ident, $inner_ty:ty) => {
404        $(#[$meta])*
405        pub struct $wrapper_ty {
406            pub inner: $inner_ty,
407        }
408
409        #[repr(transparent)]
410        pub struct $wire_ty([u8]);
411
412        impl $wire_ty {
413            /// Casts bytes into a typed TPM2B wire view.
414            ///
415            /// # Errors
416            ///
417            /// Returns `Err(TpmError)` when `buf` is not exactly one valid TPM2B
418            /// wrapper for the inner type.
419            pub fn cast(buf: &[u8]) -> $crate::TpmResult<&Self>
420            where
421                $inner_ty: for<'a> $crate::TpmField<'a>,
422            {
423                Self::validate(buf)?;
424
425                // SAFETY: `validate` checked the complete TPM2B wrapper.
426                Ok(unsafe { Self::cast_unchecked(buf) })
427            }
428
429            /// Casts the first TPM2B wire view from `buf` and returns the remainder.
430            ///
431            /// # Errors
432            ///
433            /// Returns `Err(TpmError)` when `buf` does not start with a valid TPM2B
434            /// wrapper for the inner type.
435            pub fn cast_prefix(buf: &[u8]) -> $crate::TpmResult<(&Self, &[u8])>
436            where
437                $inner_ty: for<'a> $crate::TpmField<'a>,
438            {
439                let wire_len = Self::validate_prefix(buf)?;
440                let (head, tail) = buf.split_at(wire_len);
441
442                // SAFETY: `validate_prefix` checked the complete TPM2B wrapper.
443                Ok((unsafe { Self::cast_unchecked(head) }, tail))
444            }
445
446            /// Casts bytes into a typed TPM2B wire view without validation.
447            ///
448            /// # Safety
449            ///
450            /// The caller must ensure `buf` is exactly one valid TPM2B wrapper for
451            /// the inner type.
452            #[must_use]
453            pub unsafe fn cast_unchecked(buf: &[u8]) -> &Self {
454                let ptr = core::ptr::from_ref(buf) as *const Self;
455
456                // SAFETY: `$wire_ty` is `repr(transparent)` over `[u8]`, so it has
457                // the same layout, metadata, and alignment as the referenced slice.
458                unsafe { &*ptr }
459            }
460
461            /// Returns the complete TPM2B wrapper bytes.
462            #[must_use]
463            pub const fn as_bytes(&self) -> &[u8] {
464                &self.0
465            }
466
467            /// Returns the borrowed inner structure view.
468            ///
469            /// # Errors
470            ///
471            /// Returns `Err(TpmError)` when the TPM2B payload is not exactly one
472            /// valid inner structure.
473            pub fn inner(&self) -> $crate::TpmResult<<$inner_ty as $crate::TpmField<'_>>::View>
474            where
475                $inner_ty: for<'a> $crate::TpmField<'a>,
476            {
477                let inner_bytes = &self.0[<$crate::basic::TpmUint16 as $crate::TpmSized>::SIZE..];
478                let (inner, tail) = <$inner_ty as $crate::TpmField>::cast_prefix_field(inner_bytes)?;
479
480                if !tail.is_empty() {
481                    return Err($crate::TpmError::TrailingData(
482                        $crate::TpmErrorValue::at(&self.0, tail).actual(tail.len()),
483                    ));
484                }
485
486                Ok(inner)
487            }
488
489            /// Validates an exact typed TPM2B wire view.
490            ///
491            /// # Errors
492            ///
493            /// Returns `Err(TpmError)` when `buf` is not exactly one valid TPM2B
494            /// wrapper for the inner type.
495            pub fn validate(buf: &[u8]) -> $crate::TpmResult<()>
496            where
497                $inner_ty: for<'a> $crate::TpmField<'a>,
498            {
499                let wire_len = Self::validate_prefix(buf)?;
500
501                if buf.len() > wire_len {
502                    return Err($crate::TpmError::TrailingData(
503                        $crate::TpmErrorValue::new(wire_len).actual(buf.len() - wire_len),
504                    ));
505                }
506
507                Ok(())
508            }
509
510            /// Validates a typed TPM2B wire view prefix.
511            ///
512            /// # Errors
513            ///
514            /// Returns `Err(TpmError)` when `buf` does not start with a valid TPM2B
515            /// wrapper for the inner type.
516            pub fn validate_prefix(buf: &[u8]) -> $crate::TpmResult<usize>
517            where
518                $inner_ty: for<'a> $crate::TpmField<'a>,
519            {
520                let (size_field, payload) = <$crate::basic::TpmUint16 as $crate::TpmCast>::cast_prefix(buf)?;
521                let payload_len = size_field.get() as usize;
522
523                if payload.len() < payload_len {
524                    return Err($crate::TpmError::UnexpectedEnd(
525                        $crate::TpmErrorValue::at(buf, payload).size(payload_len, payload.len()),
526                    ));
527                }
528
529                let (inner_bytes, _) = payload.split_at(payload_len);
530                let (_, tail) = <$inner_ty as $crate::TpmField>::cast_prefix_field(inner_bytes)?;
531
532                if !tail.is_empty() {
533                    return Err($crate::TpmError::TrailingData(
534                        $crate::TpmErrorValue::at(buf, tail).actual(tail.len()),
535                    ));
536                }
537
538                Ok(<$crate::basic::TpmUint16 as $crate::TpmSized>::SIZE + payload_len)
539            }
540        }
541
542        impl AsRef<[u8]> for $wire_ty {
543            fn as_ref(&self) -> &[u8] {
544                self.as_bytes()
545            }
546        }
547
548        impl<'a> $crate::TpmField<'a> for $wrapper_ty
549        where
550            $inner_ty: for<'b> $crate::TpmField<'b>,
551        {
552            type View = &'a $wire_ty;
553
554            fn cast_prefix_field(buf: &'a [u8]) -> $crate::TpmResult<(Self::View, &'a [u8])> {
555                $wire_ty::cast_prefix(buf)
556            }
557        }
558
559        impl $crate::TpmSized for $wrapper_ty {
560            const SIZE: usize = $crate::basic::TpmUint16::SIZE + <$inner_ty>::SIZE;
561            fn len(&self) -> usize {
562                $crate::basic::TpmUint16::SIZE + $crate::TpmSized::len(&self.inner)
563            }
564        }
565
566        impl $crate::TpmMarshal for $wrapper_ty
567        where
568            $inner_ty: $crate::TpmSized,
569        {
570            fn marshal(&self, writer: &mut $crate::TpmWriter) -> $crate::TpmResult<()> {
571                let inner_len = $crate::TpmSized::len(&self.inner);
572                let len_field = <$crate::basic::TpmUint16>::try_from(inner_len)
573                    .map_err(|_| $crate::TpmError::IntegerTooLarge(
574                        $crate::TpmErrorValue::new(writer.len()).value_usize(inner_len),
575                    ))?;
576                len_field.marshal(writer)?;
577                $crate::TpmMarshal::marshal(&self.inner, writer)
578            }
579        }
580
581        impl From<$inner_ty> for $wrapper_ty {
582            fn from(inner: $inner_ty) -> Self {
583                Self { inner }
584            }
585        }
586
587        impl core::ops::Deref for $wrapper_ty {
588            type Target = $inner_ty;
589            fn deref(&self) -> &Self::Target {
590                &self.inner
591            }
592        }
593
594        impl core::ops::DerefMut for $wrapper_ty {
595            fn deref_mut(&mut self) -> &mut Self::Target {
596                &mut self.inner
597            }
598        }
599    };
600}
601
602#[macro_export]
603macro_rules! tpml {
604    ($name:ident, $inner_ty:ty, $capacity:expr) => {
605        pub type $name = $crate::basic::TpmList<$inner_ty, $capacity>;
606    };
607}