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