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