Skip to main content

tpm2_protocol/
lib.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//! # TPM 2.0 Protocol
6//!
7//! A library for marshaling and unmarshaling TCG TPM 2.0 protocol messages.
8//!
9//! ## Constraints
10//!
11//! * `alloc` is disallowed.
12//! * Dependencies are disallowed.
13//! * Developer dependencies are disallowed.
14//! * Panics are disallowed.
15//!
16//! ## Design Goals
17//!
18//! * The crate must compile with GNU make and rustc without any external
19//!   dependencies.
20
21#![cfg_attr(not(test), no_std)]
22#![deny(unsafe_op_in_unsafe_fn)]
23#![deny(clippy::all)]
24#![deny(clippy::undocumented_unsafe_blocks)]
25#![deny(clippy::pedantic)]
26#![recursion_limit = "256"]
27
28pub mod basic;
29pub mod constant;
30pub mod data;
31mod error;
32#[macro_use]
33pub mod r#macro;
34pub mod frame;
35
36pub use self::error::{TpmError, TpmErrorValue, TpmResult};
37
38/// A byte-backed TPM wire view.
39#[repr(transparent)]
40pub struct TpmWire([u8]);
41
42impl TpmWire {
43    /// Casts a byte slice into a TPM wire view.
44    #[must_use]
45    pub fn cast(buf: &[u8]) -> &Self {
46        // SAFETY: `TpmWire` accepts any byte slice as its backing storage.
47        unsafe { Self::cast_unchecked(buf) }
48    }
49
50    /// Casts a byte slice into a TPM wire view without validation.
51    ///
52    /// # Safety
53    ///
54    /// `TpmWire` has no additional validity requirements beyond the validity
55    /// of `buf`. Callers must still ensure any higher-level protocol
56    /// invariants required by later typed accessors have been validated.
57    #[must_use]
58    pub unsafe fn cast_unchecked(buf: &[u8]) -> &Self {
59        let ptr = core::ptr::from_ref(buf) as *const Self;
60
61        // SAFETY: `TpmWire` is `repr(transparent)` over `[u8]`, so it has the
62        // same layout, metadata, and alignment as the referenced slice.
63        unsafe { &*ptr }
64    }
65
66    /// Casts a mutable byte slice into a mutable TPM wire view.
67    #[must_use]
68    pub fn cast_mut(buf: &mut [u8]) -> &mut Self {
69        // SAFETY: `TpmWire` accepts any mutable byte slice as its backing
70        // storage. The `&mut` input provides exclusive access.
71        unsafe { Self::cast_mut_unchecked(buf) }
72    }
73
74    /// Casts a mutable byte slice into a mutable TPM wire view without validation.
75    ///
76    /// # Safety
77    ///
78    /// `TpmWire` has no additional validity requirements beyond the validity
79    /// of `buf`. Callers must still ensure any higher-level protocol
80    /// invariants required by later typed accessors have been validated. The
81    /// returned reference inherits the exclusive access represented by `buf`.
82    #[must_use]
83    pub unsafe fn cast_mut_unchecked(buf: &mut [u8]) -> &mut Self {
84        let ptr = core::ptr::from_mut(buf) as *mut Self;
85
86        // SAFETY: `TpmWire` is `repr(transparent)` over `[u8]`, so it has the
87        // same layout, metadata, and alignment as the referenced slice.
88        unsafe { &mut *ptr }
89    }
90
91    /// Returns the backing bytes.
92    #[must_use]
93    pub const fn as_bytes(&self) -> &[u8] {
94        &self.0
95    }
96
97    /// Returns the mutable backing bytes.
98    #[must_use]
99    pub fn as_bytes_mut(&mut self) -> &mut [u8] {
100        &mut self.0
101    }
102
103    /// Returns the number of backing bytes.
104    #[must_use]
105    pub const fn len(&self) -> usize {
106        self.0.len()
107    }
108
109    /// Returns `true` when the backing byte slice is empty.
110    #[must_use]
111    pub const fn is_empty(&self) -> bool {
112        self.0.is_empty()
113    }
114}
115
116impl AsRef<[u8]> for TpmWire {
117    fn as_ref(&self) -> &[u8] {
118        self.as_bytes()
119    }
120}
121
122impl AsMut<[u8]> for TpmWire {
123    fn as_mut(&mut self) -> &mut [u8] {
124        self.as_bytes_mut()
125    }
126}
127
128/// A byte-backed TPM wire view with a fixed byte length.
129#[repr(transparent)]
130pub struct TpmWireBytes<const N: usize>([u8; N]);
131
132impl<const N: usize> TpmWireBytes<N> {
133    /// Casts a byte slice into a fixed-size TPM wire view.
134    ///
135    /// # Errors
136    ///
137    /// Returns [`UnexpectedEnd`](crate::TpmError::UnexpectedEnd) when
138    /// `buf` is smaller than `N` bytes.
139    /// Returns [`TrailingData`](crate::TpmError::TrailingData) when
140    /// `buf` is larger than `N` bytes.
141    pub fn cast(buf: &[u8]) -> TpmResult<&Self> {
142        if buf.len() < N {
143            return Err(TpmError::UnexpectedEnd(
144                crate::TpmErrorValue::new(0).size(N, buf.len()),
145            ));
146        }
147
148        if buf.len() > N {
149            return Err(TpmError::TrailingData(
150                crate::TpmErrorValue::new(N).actual(buf.len() - N),
151            ));
152        }
153
154        // SAFETY: The length check above guarantees that `buf` has exactly the
155        // byte length required by `TpmWireBytes<N>`.
156        Ok(unsafe { Self::cast_unchecked(buf) })
157    }
158
159    /// Casts a byte slice into a fixed-size TPM wire view without validation.
160    ///
161    /// # Safety
162    ///
163    /// The caller must ensure that `buf.len() == N`. Callers must also ensure
164    /// any higher-level protocol invariants required by later typed accessors
165    /// have been validated.
166    #[must_use]
167    pub unsafe fn cast_unchecked(buf: &[u8]) -> &Self {
168        let ptr = buf.as_ptr().cast::<Self>();
169
170        // SAFETY: `TpmWireBytes<N>` is `repr(transparent)` over `[u8; N]`, so it
171        // has the same layout and alignment. The caller guarantees exact size.
172        unsafe { &*ptr }
173    }
174
175    /// Casts a mutable byte slice into a fixed-size mutable TPM wire view.
176    ///
177    /// # Errors
178    ///
179    /// Returns [`UnexpectedEnd`](crate::TpmError::UnexpectedEnd) when
180    /// `buf` is smaller than `N` bytes.
181    /// Returns [`TrailingData`](crate::TpmError::TrailingData) when
182    /// `buf` is larger than `N` bytes.
183    pub fn cast_mut(buf: &mut [u8]) -> TpmResult<&mut Self> {
184        if buf.len() < N {
185            return Err(TpmError::UnexpectedEnd(
186                crate::TpmErrorValue::new(0).size(N, buf.len()),
187            ));
188        }
189
190        if buf.len() > N {
191            return Err(TpmError::TrailingData(
192                crate::TpmErrorValue::new(N).actual(buf.len() - N),
193            ));
194        }
195
196        // SAFETY: The length check above guarantees that `buf` has exactly the
197        // byte length required by `TpmWireBytes<N>`. The `&mut` input provides
198        // exclusive access.
199        Ok(unsafe { Self::cast_mut_unchecked(buf) })
200    }
201
202    /// Casts a mutable byte slice into a fixed-size mutable TPM wire view without validation.
203    ///
204    /// # Safety
205    ///
206    /// The caller must ensure that `buf.len() == N`. Callers must also ensure
207    /// any higher-level protocol invariants required by later typed accessors
208    /// have been validated. The returned reference inherits the exclusive
209    /// access represented by `buf`.
210    #[must_use]
211    pub unsafe fn cast_mut_unchecked(buf: &mut [u8]) -> &mut Self {
212        let ptr = buf.as_mut_ptr().cast::<Self>();
213
214        // SAFETY: `TpmWireBytes<N>` is `repr(transparent)` over `[u8; N]`, so it
215        // has the same layout and alignment. The caller guarantees exact size.
216        unsafe { &mut *ptr }
217    }
218
219    /// Returns the backing bytes.
220    #[must_use]
221    pub const fn as_bytes(&self) -> &[u8; N] {
222        &self.0
223    }
224
225    /// Returns the mutable backing bytes.
226    #[must_use]
227    pub fn as_bytes_mut(&mut self) -> &mut [u8; N] {
228        &mut self.0
229    }
230
231    /// Returns the number of backing bytes.
232    #[must_use]
233    pub const fn len(&self) -> usize {
234        N
235    }
236
237    /// Returns `true` when the backing byte array is empty.
238    #[must_use]
239    pub const fn is_empty(&self) -> bool {
240        N == 0
241    }
242}
243
244impl<const N: usize> AsRef<[u8]> for TpmWireBytes<N> {
245    fn as_ref(&self) -> &[u8] {
246        self.as_bytes()
247    }
248}
249
250impl<const N: usize> AsMut<[u8]> for TpmWireBytes<N> {
251    fn as_mut(&mut self) -> &mut [u8] {
252        self.as_bytes_mut()
253    }
254}
255
256/// Casts caller-owned bytes into a TPM wire view.
257pub trait TpmCast {
258    /// Casts `buf` into `Self` after validating the wire-view invariants.
259    ///
260    /// # Errors
261    ///
262    /// Returns `Err(TpmError)` when `buf` does not satisfy the
263    /// invariants for `Self`.
264    fn cast(buf: &[u8]) -> TpmResult<&Self>;
265
266    /// Casts `buf` into `Self` without validating the wire-view invariants.
267    ///
268    /// # Safety
269    ///
270    /// The caller must ensure that `buf` satisfies the same invariants checked
271    /// by [`TpmCast::cast`].
272    unsafe fn cast_unchecked(buf: &[u8]) -> &Self;
273}
274
275/// Casts caller-owned mutable bytes into a mutable TPM wire view.
276pub trait TpmCastMut: TpmCast {
277    /// Casts `buf` into mutable `Self` after validating the wire-view invariants.
278    ///
279    /// # Errors
280    ///
281    /// Returns `Err(TpmError)` when `buf` does not satisfy the
282    /// invariants for `Self`.
283    fn cast_mut(buf: &mut [u8]) -> TpmResult<&mut Self>;
284
285    /// Casts `buf` into mutable `Self` without validating the wire-view invariants.
286    ///
287    /// # Safety
288    ///
289    /// The caller must ensure that `buf` satisfies the same invariants checked
290    /// by [`TpmCastMut::cast_mut`]. The returned reference inherits the
291    /// exclusive access represented by `buf`.
292    unsafe fn cast_mut_unchecked(buf: &mut [u8]) -> &mut Self;
293}
294
295impl TpmCast for TpmWire {
296    fn cast(buf: &[u8]) -> TpmResult<&Self> {
297        Ok(Self::cast(buf))
298    }
299
300    unsafe fn cast_unchecked(buf: &[u8]) -> &Self {
301        // SAFETY: The caller upholds the unchecked cast contract for `TpmWire`.
302        unsafe { Self::cast_unchecked(buf) }
303    }
304}
305
306impl TpmCastMut for TpmWire {
307    fn cast_mut(buf: &mut [u8]) -> TpmResult<&mut Self> {
308        Ok(Self::cast_mut(buf))
309    }
310
311    unsafe fn cast_mut_unchecked(buf: &mut [u8]) -> &mut Self {
312        // SAFETY: The caller upholds the unchecked mutable cast contract for
313        // `TpmWire`.
314        unsafe { Self::cast_mut_unchecked(buf) }
315    }
316}
317
318impl<const N: usize> TpmCast for TpmWireBytes<N> {
319    fn cast(buf: &[u8]) -> TpmResult<&Self> {
320        Self::cast(buf)
321    }
322
323    unsafe fn cast_unchecked(buf: &[u8]) -> &Self {
324        // SAFETY: The caller upholds the unchecked cast contract for
325        // `TpmWireBytes<N>`.
326        unsafe { Self::cast_unchecked(buf) }
327    }
328}
329
330impl<const N: usize> TpmCastMut for TpmWireBytes<N> {
331    fn cast_mut(buf: &mut [u8]) -> TpmResult<&mut Self> {
332        Self::cast_mut(buf)
333    }
334
335    unsafe fn cast_mut_unchecked(buf: &mut [u8]) -> &mut Self {
336        // SAFETY: The caller upholds the unchecked mutable cast contract for
337        // `TpmWireBytes<N>`.
338        unsafe { Self::cast_mut_unchecked(buf) }
339    }
340}
341
342/// Builds TPM wire bytes into a caller-provided mutable byte slice.
343pub struct TpmWriter<'a> {
344    buffer: &'a mut [u8],
345    cursor: usize,
346}
347
348impl<'a> TpmWriter<'a> {
349    /// Creates a new writer for the given buffer.
350    #[must_use]
351    pub fn new(buffer: &'a mut [u8]) -> Self {
352        Self { buffer, cursor: 0 }
353    }
354
355    /// Returns the number of bytes written so far.
356    #[must_use]
357    pub fn len(&self) -> usize {
358        self.cursor
359    }
360
361    /// Returns `true` if no bytes have been written.
362    #[must_use]
363    pub fn is_empty(&self) -> bool {
364        self.cursor == 0
365    }
366
367    /// Returns the bytes written so far.
368    #[must_use]
369    pub fn as_bytes(&self) -> &[u8] {
370        &self.buffer[..self.cursor]
371    }
372
373    /// Appends a slice of bytes to the writer.
374    ///
375    /// # Errors
376    ///
377    /// Returns [`BufferOverflow`](crate::TpmError::BufferOverflow) when the
378    /// capacity of the buffer is exceeded.
379    pub fn write_bytes(&mut self, bytes: &[u8]) -> TpmResult<()> {
380        let end = self
381            .cursor
382            .checked_add(bytes.len())
383            .ok_or(TpmError::BufferOverflow(
384                crate::TpmErrorValue::new(self.cursor).size(bytes.len(), 0),
385            ))?;
386
387        if end > self.buffer.len() {
388            return Err(TpmError::BufferOverflow(
389                crate::TpmErrorValue::new(self.cursor)
390                    .size(bytes.len(), self.buffer.len().saturating_sub(self.cursor)),
391            ));
392        }
393        self.buffer[self.cursor..end].copy_from_slice(bytes);
394        self.cursor = end;
395        Ok(())
396    }
397}
398
399/// Provides two ways to determine the size of an oBject: a compile-time maximum
400/// and a runtime exact size.
401pub trait TpmSized {
402    /// The estimated size of the object in its serialized form evaluated at
403    /// compile-time (always larger than the realized length).
404    const SIZE: usize;
405
406    /// Returns the exact serialized size of the object.
407    fn len(&self) -> usize;
408
409    /// Returns `true` if the object has a serialized length of zero.
410    fn is_empty(&self) -> bool {
411        self.len() == 0
412    }
413}
414
415pub trait TpmMarshal {
416    /// Marshals the object into the given writer.
417    ///
418    /// # Errors
419    ///
420    /// Returns `Err(TpmError)` on a marshal failure.
421    fn marshal(&self, writer: &mut TpmWriter) -> TpmResult<()>;
422}
423
424pub(crate) trait TpmUnmarshal: Sized + TpmSized {
425    /// Unmarshals an object from the given buffer.
426    ///
427    /// Returns the unmarshald type and the remaining portion of the buffer.
428    ///
429    /// # Errors
430    ///
431    /// Returns `Err(TpmError)` on a unmarshal failure.
432    fn unmarshal(buf: &[u8]) -> TpmResult<(Self, &[u8])>;
433}
434
435/// Types that are composed of a tag and a value e.g., a union.
436pub(crate) trait TpmTagged {
437    /// The type of the tag/discriminant.
438    type Tag: TpmUnmarshal + TpmMarshal + Copy;
439    /// The type of the value/union.
440    type Value;
441}
442
443/// Unmarshals a tagged object from a buffer.
444pub(crate) trait TpmUnmarshalTagged: Sized {
445    /// Unmarshals a tagged object from the given buffer using the provided tag.
446    ///
447    /// # Errors
448    ///
449    /// This method can return any error of the underlying type's `TpmUnmarshal` implementation,
450    /// such as a `TpmError::UnexpectedEnd` if the buffer is too small or a
451    /// `TpmError::VariantNotAvailable` if a tagged variant is unavailable.
452    fn unmarshal_tagged(tag: <Self as TpmTagged>::Tag, buf: &[u8]) -> TpmResult<(Self, &[u8])>
453    where
454        Self: TpmTagged,
455        <Self as TpmTagged>::Tag: TpmUnmarshal + TpmMarshal;
456}