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//! ## Zero-Copy Contract
22//!
23//! Read-side protocol APIs operate on caller-owned byte slices and return
24//! borrowed wire views into those slices. Implementations must not copy payload
25//! bytes to inspect frames or nested TPM values. Scalar fields may be read by
26//! value from their big-endian wire representation.
27//!
28//! Validation must prove all exposed borrowed views are bounded by the original
29//! input slice. Any malformed length, tag, selector, or trailing byte condition
30//! must be reported as [`TpmError`] instead of panicking.
31//!
32//! The crate does not use the external `zerocopy` crate.
33
34#![cfg_attr(not(test), no_std)]
35#![deny(unsafe_op_in_unsafe_fn)]
36#![deny(clippy::all)]
37#![deny(clippy::undocumented_unsafe_blocks)]
38#![deny(clippy::pedantic)]
39#![recursion_limit = "256"]
40
41pub mod basic;
42pub mod constant;
43pub mod data;
44mod error;
45#[macro_use]
46pub mod r#macro;
47pub mod frame;
48
49pub use self::error::{TpmError, TpmErrorValue, TpmResult};
50
51/// A byte-backed TPM wire view.
52#[repr(transparent)]
53pub struct TpmWire([u8]);
54
55impl TpmWire {
56    /// Casts a byte slice into a TPM wire view.
57    #[must_use]
58    pub fn cast(buf: &[u8]) -> &Self {
59        // SAFETY: `TpmWire` accepts any byte slice as its backing storage.
60        unsafe { Self::cast_unchecked(buf) }
61    }
62
63    /// Casts a byte slice into a TPM wire view and returns no remainder.
64    #[must_use]
65    pub fn cast_prefix(buf: &[u8]) -> (&Self, &[u8]) {
66        (Self::cast(buf), &buf[buf.len()..])
67    }
68
69    /// Casts a byte slice into a TPM wire view without validation.
70    ///
71    /// # Safety
72    ///
73    /// `TpmWire` has no additional validity requirements beyond the validity
74    /// of `buf`. Callers must still ensure any higher-level protocol
75    /// invariants required by later typed accessors have been validated.
76    #[must_use]
77    pub unsafe fn cast_unchecked(buf: &[u8]) -> &Self {
78        let ptr = core::ptr::from_ref(buf) as *const Self;
79
80        // SAFETY: `TpmWire` is `repr(transparent)` over `[u8]`, so it has the
81        // same layout, metadata, and alignment as the referenced slice.
82        unsafe { &*ptr }
83    }
84
85    /// Casts a mutable byte slice into a mutable TPM wire view.
86    #[must_use]
87    pub fn cast_mut(buf: &mut [u8]) -> &mut Self {
88        // SAFETY: `TpmWire` accepts any mutable byte slice as its backing
89        // storage. The `&mut` input provides exclusive access.
90        unsafe { Self::cast_mut_unchecked(buf) }
91    }
92
93    /// Casts a mutable byte slice into a mutable TPM wire view and returns no remainder.
94    #[must_use]
95    pub fn cast_prefix_mut(buf: &mut [u8]) -> (&mut Self, &mut [u8]) {
96        let len = buf.len();
97        let (head, tail) = buf.split_at_mut(len);
98
99        (Self::cast_mut(head), tail)
100    }
101
102    /// Casts a mutable byte slice into a mutable TPM wire view without validation.
103    ///
104    /// # Safety
105    ///
106    /// `TpmWire` has no additional validity requirements beyond the validity
107    /// of `buf`. Callers must still ensure any higher-level protocol
108    /// invariants required by later typed accessors have been validated. The
109    /// returned reference inherits the exclusive access represented by `buf`.
110    #[must_use]
111    pub unsafe fn cast_mut_unchecked(buf: &mut [u8]) -> &mut Self {
112        let ptr = core::ptr::from_mut(buf) as *mut Self;
113
114        // SAFETY: `TpmWire` is `repr(transparent)` over `[u8]`, so it has the
115        // same layout, metadata, and alignment as the referenced slice.
116        unsafe { &mut *ptr }
117    }
118
119    /// Returns the backing bytes.
120    #[must_use]
121    pub const fn as_bytes(&self) -> &[u8] {
122        &self.0
123    }
124
125    /// Returns the mutable backing bytes.
126    #[must_use]
127    pub fn as_bytes_mut(&mut self) -> &mut [u8] {
128        &mut self.0
129    }
130
131    /// Returns the number of backing bytes.
132    #[must_use]
133    pub const fn len(&self) -> usize {
134        self.0.len()
135    }
136
137    /// Returns `true` when the backing byte slice is empty.
138    #[must_use]
139    pub const fn is_empty(&self) -> bool {
140        self.0.is_empty()
141    }
142}
143
144impl AsRef<[u8]> for TpmWire {
145    fn as_ref(&self) -> &[u8] {
146        self.as_bytes()
147    }
148}
149
150impl AsMut<[u8]> for TpmWire {
151    fn as_mut(&mut self) -> &mut [u8] {
152        self.as_bytes_mut()
153    }
154}
155
156/// A byte-backed TPM wire view with a fixed byte length.
157#[repr(transparent)]
158pub struct TpmWireBytes<const N: usize>([u8; N]);
159
160impl<const N: usize> TpmWireBytes<N> {
161    /// Casts a byte slice into a fixed-size TPM wire view.
162    ///
163    /// # Errors
164    ///
165    /// Returns [`UnexpectedEnd`](crate::TpmError::UnexpectedEnd) when
166    /// `buf` is smaller than `N` bytes.
167    /// Returns [`TrailingData`](crate::TpmError::TrailingData) when
168    /// `buf` is larger than `N` bytes.
169    pub fn cast(buf: &[u8]) -> TpmResult<&Self> {
170        Self::validate(buf)?;
171
172        // SAFETY: The validation above guarantees that `buf` has exactly the
173        // byte length required by `TpmWireBytes<N>`.
174        Ok(unsafe { Self::cast_unchecked(buf) })
175    }
176
177    /// Validates an exact fixed-size TPM wire view.
178    ///
179    /// # Errors
180    ///
181    /// Returns [`UnexpectedEnd`](crate::TpmError::UnexpectedEnd) when
182    /// `buf` is smaller than `N` bytes.
183    /// Returns [`TrailingData`](crate::TpmError::TrailingData) when
184    /// `buf` is larger than `N` bytes.
185    pub fn validate(buf: &[u8]) -> TpmResult<()> {
186        Self::validate_prefix(buf)?;
187
188        if buf.len() > N {
189            return Err(TpmError::TrailingData(
190                crate::TpmErrorValue::new(N).actual(buf.len() - N),
191            ));
192        }
193
194        Ok(())
195    }
196
197    /// Validates that `buf` starts with a fixed-size TPM wire view.
198    ///
199    /// # Errors
200    ///
201    /// Returns [`UnexpectedEnd`](crate::TpmError::UnexpectedEnd) when
202    /// `buf` is smaller than `N` bytes.
203    pub fn validate_prefix(buf: &[u8]) -> TpmResult<()> {
204        if buf.len() < N {
205            return Err(TpmError::UnexpectedEnd(
206                crate::TpmErrorValue::new(0).size(N, buf.len()),
207            ));
208        }
209
210        Ok(())
211    }
212
213    /// Casts the first `N` bytes into a fixed-size TPM wire view.
214    ///
215    /// # Errors
216    ///
217    /// Returns [`UnexpectedEnd`](crate::TpmError::UnexpectedEnd) when
218    /// `buf` is smaller than `N` bytes.
219    pub fn cast_prefix(buf: &[u8]) -> TpmResult<(&Self, &[u8])> {
220        Self::validate_prefix(buf)?;
221
222        let (head, tail) = buf.split_at(N);
223
224        // SAFETY: The validation above guarantees that `head` has exactly
225        // the byte length required by `TpmWireBytes<N>`.
226        Ok((unsafe { Self::cast_unchecked(head) }, tail))
227    }
228
229    /// Casts a byte slice into a fixed-size TPM wire view without validation.
230    ///
231    /// # Safety
232    ///
233    /// The caller must ensure that `buf.len() == N`. Callers must also ensure
234    /// any higher-level protocol invariants required by later typed accessors
235    /// have been validated.
236    #[must_use]
237    pub unsafe fn cast_unchecked(buf: &[u8]) -> &Self {
238        let ptr = buf.as_ptr().cast::<Self>();
239
240        // SAFETY: `TpmWireBytes<N>` is `repr(transparent)` over `[u8; N]`, so it
241        // has the same layout and alignment. The caller guarantees exact size.
242        unsafe { &*ptr }
243    }
244
245    /// Casts a mutable byte slice into a fixed-size mutable TPM wire view.
246    ///
247    /// # Errors
248    ///
249    /// Returns [`UnexpectedEnd`](crate::TpmError::UnexpectedEnd) when
250    /// `buf` is smaller than `N` bytes.
251    /// Returns [`TrailingData`](crate::TpmError::TrailingData) when
252    /// `buf` is larger than `N` bytes.
253    pub fn cast_mut(buf: &mut [u8]) -> TpmResult<&mut Self> {
254        Self::validate(buf)?;
255
256        // SAFETY: The validation above guarantees that `buf` has exactly the
257        // byte length required by `TpmWireBytes<N>`. The `&mut` input provides
258        // exclusive access.
259        Ok(unsafe { Self::cast_mut_unchecked(buf) })
260    }
261
262    /// Casts the first `N` mutable bytes into a fixed-size TPM wire view.
263    ///
264    /// # Errors
265    ///
266    /// Returns [`UnexpectedEnd`](crate::TpmError::UnexpectedEnd) when
267    /// `buf` is smaller than `N` bytes.
268    pub fn cast_prefix_mut(buf: &mut [u8]) -> TpmResult<(&mut Self, &mut [u8])> {
269        Self::validate_prefix(buf)?;
270
271        let (head, tail) = buf.split_at_mut(N);
272
273        // SAFETY: The validation above guarantees that `head` has exactly
274        // the byte length required by `TpmWireBytes<N>`.
275        Ok((unsafe { Self::cast_mut_unchecked(head) }, tail))
276    }
277
278    /// Casts a mutable byte slice into a fixed-size mutable TPM wire view without validation.
279    ///
280    /// # Safety
281    ///
282    /// The caller must ensure that `buf.len() == N`. Callers must also ensure
283    /// any higher-level protocol invariants required by later typed accessors
284    /// have been validated. The returned reference inherits the exclusive
285    /// access represented by `buf`.
286    #[must_use]
287    pub unsafe fn cast_mut_unchecked(buf: &mut [u8]) -> &mut Self {
288        let ptr = buf.as_mut_ptr().cast::<Self>();
289
290        // SAFETY: `TpmWireBytes<N>` is `repr(transparent)` over `[u8; N]`, so it
291        // has the same layout and alignment. The caller guarantees exact size.
292        unsafe { &mut *ptr }
293    }
294
295    /// Returns the backing bytes.
296    #[must_use]
297    pub const fn as_bytes(&self) -> &[u8; N] {
298        &self.0
299    }
300
301    /// Returns the mutable backing bytes.
302    #[must_use]
303    pub fn as_bytes_mut(&mut self) -> &mut [u8; N] {
304        &mut self.0
305    }
306
307    /// Returns the number of backing bytes.
308    #[must_use]
309    pub const fn len(&self) -> usize {
310        N
311    }
312
313    /// Returns `true` when the backing byte array is empty.
314    #[must_use]
315    pub const fn is_empty(&self) -> bool {
316        N == 0
317    }
318}
319
320impl<const N: usize> AsRef<[u8]> for TpmWireBytes<N> {
321    fn as_ref(&self) -> &[u8] {
322        self.as_bytes()
323    }
324}
325
326impl<const N: usize> AsMut<[u8]> for TpmWireBytes<N> {
327    fn as_mut(&mut self) -> &mut [u8] {
328        self.as_bytes_mut()
329    }
330}
331
332/// Casts caller-owned bytes into a TPM wire view.
333pub trait TpmCast {
334    /// Casts `buf` into `Self` after validating the wire-view invariants.
335    ///
336    /// # Errors
337    ///
338    /// Returns `Err(TpmError)` when `buf` does not satisfy the
339    /// invariants for `Self`.
340    fn cast(buf: &[u8]) -> TpmResult<&Self>;
341
342    /// Casts the first wire value in `buf` into `Self` and returns the remainder.
343    ///
344    /// # Errors
345    ///
346    /// Returns `Err(TpmError)` when `buf` does not start with a valid `Self`.
347    fn cast_prefix(buf: &[u8]) -> TpmResult<(&Self, &[u8])> {
348        let value = Self::cast(buf)?;
349
350        Ok((value, &buf[buf.len()..]))
351    }
352
353    /// Casts `buf` into `Self` without validating the wire-view invariants.
354    ///
355    /// # Safety
356    ///
357    /// The caller must ensure that `buf` satisfies the same invariants checked
358    /// by [`TpmCast::cast`].
359    unsafe fn cast_unchecked(buf: &[u8]) -> &Self;
360}
361
362/// Casts caller-owned mutable bytes into a mutable TPM wire view.
363pub trait TpmCastMut: TpmCast {
364    /// Casts `buf` into mutable `Self` after validating the wire-view invariants.
365    ///
366    /// # Errors
367    ///
368    /// Returns `Err(TpmError)` when `buf` does not satisfy the
369    /// invariants for `Self`.
370    fn cast_mut(buf: &mut [u8]) -> TpmResult<&mut Self>;
371
372    /// Casts the first mutable wire value in `buf` into `Self` and returns the remainder.
373    ///
374    /// # Errors
375    ///
376    /// Returns `Err(TpmError)` when `buf` does not start with a valid `Self`.
377    fn cast_prefix_mut(buf: &mut [u8]) -> TpmResult<(&mut Self, &mut [u8])> {
378        let len = buf.len();
379        let (head, tail) = buf.split_at_mut(len);
380        let value = Self::cast_mut(head)?;
381
382        Ok((value, tail))
383    }
384
385    /// Casts `buf` into mutable `Self` without validating the wire-view invariants.
386    ///
387    /// # Safety
388    ///
389    /// The caller must ensure that `buf` satisfies the same invariants checked
390    /// by [`TpmCastMut::cast_mut`]. The returned reference inherits the
391    /// exclusive access represented by `buf`.
392    unsafe fn cast_mut_unchecked(buf: &mut [u8]) -> &mut Self;
393}
394
395/// Reads one field from a TPM wire structure.
396pub trait TpmField<'a> {
397    type View;
398
399    /// Reads the first field from `buf` and returns the remaining bytes.
400    ///
401    /// # Errors
402    ///
403    /// Returns `Err(TpmError)` when `buf` does not start with a valid field.
404    fn cast_prefix_field(buf: &'a [u8]) -> TpmResult<(Self::View, &'a [u8])>;
405}
406
407/// Reads a union field selected by a previously-read tag.
408pub trait TpmTaggedField<'a, Tag> {
409    type View;
410
411    /// Reads the tagged field from `buf` and returns the remaining bytes.
412    ///
413    /// # Errors
414    ///
415    /// Returns `Err(TpmError)` when `tag` does not select a valid variant or
416    /// `buf` does not start with a valid selected field.
417    fn cast_tagged_prefix_field(tag: Tag, buf: &'a [u8]) -> TpmResult<(Self::View, &'a [u8])>;
418}
419
420impl<'a, T: TpmCast + ?Sized + 'a> TpmField<'a> for T {
421    type View = &'a T;
422
423    fn cast_prefix_field(buf: &'a [u8]) -> TpmResult<(Self::View, &'a [u8])> {
424        T::cast_prefix(buf)
425    }
426}
427
428impl TpmCast for TpmWire {
429    fn cast(buf: &[u8]) -> TpmResult<&Self> {
430        Ok(Self::cast(buf))
431    }
432
433    fn cast_prefix(buf: &[u8]) -> TpmResult<(&Self, &[u8])> {
434        Ok(Self::cast_prefix(buf))
435    }
436
437    unsafe fn cast_unchecked(buf: &[u8]) -> &Self {
438        // SAFETY: The caller upholds the unchecked cast contract for `TpmWire`.
439        unsafe { Self::cast_unchecked(buf) }
440    }
441}
442
443impl TpmCastMut for TpmWire {
444    fn cast_mut(buf: &mut [u8]) -> TpmResult<&mut Self> {
445        Ok(Self::cast_mut(buf))
446    }
447
448    fn cast_prefix_mut(buf: &mut [u8]) -> TpmResult<(&mut Self, &mut [u8])> {
449        Ok(Self::cast_prefix_mut(buf))
450    }
451
452    unsafe fn cast_mut_unchecked(buf: &mut [u8]) -> &mut Self {
453        // SAFETY: The caller upholds the unchecked mutable cast contract for
454        // `TpmWire`.
455        unsafe { Self::cast_mut_unchecked(buf) }
456    }
457}
458
459impl<const N: usize> TpmCast for TpmWireBytes<N> {
460    fn cast(buf: &[u8]) -> TpmResult<&Self> {
461        Self::cast(buf)
462    }
463
464    fn cast_prefix(buf: &[u8]) -> TpmResult<(&Self, &[u8])> {
465        Self::cast_prefix(buf)
466    }
467
468    unsafe fn cast_unchecked(buf: &[u8]) -> &Self {
469        // SAFETY: The caller upholds the unchecked cast contract for
470        // `TpmWireBytes<N>`.
471        unsafe { Self::cast_unchecked(buf) }
472    }
473}
474
475impl<const N: usize> TpmCastMut for TpmWireBytes<N> {
476    fn cast_mut(buf: &mut [u8]) -> TpmResult<&mut Self> {
477        Self::cast_mut(buf)
478    }
479
480    fn cast_prefix_mut(buf: &mut [u8]) -> TpmResult<(&mut Self, &mut [u8])> {
481        Self::cast_prefix_mut(buf)
482    }
483
484    unsafe fn cast_mut_unchecked(buf: &mut [u8]) -> &mut Self {
485        // SAFETY: The caller upholds the unchecked mutable cast contract for
486        // `TpmWireBytes<N>`.
487        unsafe { Self::cast_mut_unchecked(buf) }
488    }
489}
490
491/// Builds TPM wire bytes into a caller-provided mutable byte slice.
492pub struct TpmWriter<'a> {
493    buffer: &'a mut [u8],
494    cursor: usize,
495}
496
497impl<'a> TpmWriter<'a> {
498    /// Creates a new writer for the given buffer.
499    #[must_use]
500    pub fn new(buffer: &'a mut [u8]) -> Self {
501        Self { buffer, cursor: 0 }
502    }
503
504    /// Returns the number of bytes written so far.
505    #[must_use]
506    pub fn len(&self) -> usize {
507        self.cursor
508    }
509
510    /// Returns `true` if no bytes have been written.
511    #[must_use]
512    pub fn is_empty(&self) -> bool {
513        self.cursor == 0
514    }
515
516    /// Returns the bytes written so far.
517    #[must_use]
518    pub fn as_bytes(&self) -> &[u8] {
519        &self.buffer[..self.cursor]
520    }
521
522    /// Appends a slice of bytes to the writer.
523    ///
524    /// # Errors
525    ///
526    /// Returns [`BufferOverflow`](crate::TpmError::BufferOverflow) when the
527    /// capacity of the buffer is exceeded.
528    pub fn write_bytes(&mut self, bytes: &[u8]) -> TpmResult<()> {
529        let end = self
530            .cursor
531            .checked_add(bytes.len())
532            .ok_or(TpmError::BufferOverflow(
533                crate::TpmErrorValue::new(self.cursor).size(bytes.len(), 0),
534            ))?;
535
536        if end > self.buffer.len() {
537            return Err(TpmError::BufferOverflow(
538                crate::TpmErrorValue::new(self.cursor)
539                    .size(bytes.len(), self.buffer.len().saturating_sub(self.cursor)),
540            ));
541        }
542        self.buffer[self.cursor..end].copy_from_slice(bytes);
543        self.cursor = end;
544        Ok(())
545    }
546}
547
548/// Provides two ways to determine the size of an oBject: a compile-time maximum
549/// and a runtime exact size.
550pub trait TpmSized {
551    /// The estimated size of the object in its serialized form evaluated at
552    /// compile-time (always larger than the realized length).
553    const SIZE: usize;
554
555    /// Returns the exact serialized size of the object.
556    fn len(&self) -> usize;
557
558    /// Returns `true` if the object has a serialized length of zero.
559    fn is_empty(&self) -> bool {
560        self.len() == 0
561    }
562}
563
564pub trait TpmMarshal {
565    /// Marshals the object into the given writer.
566    ///
567    /// # Errors
568    ///
569    /// Returns `Err(TpmError)` on a marshal failure.
570    fn marshal(&self, writer: &mut TpmWriter) -> TpmResult<()>;
571}