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, TpmResult, tpm_offset, tpm_value};
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 mutable byte slice into a mutable TPM wire view.
70    #[must_use]
71    pub fn cast_mut(buf: &mut [u8]) -> &mut Self {
72        // SAFETY: `TpmWire` accepts any mutable byte slice as its backing
73        // storage. The `&mut` input provides exclusive access.
74        unsafe { Self::cast_mut_unchecked(buf) }
75    }
76
77    /// Casts a mutable byte slice into a mutable TPM wire view and returns no remainder.
78    #[must_use]
79    pub fn cast_prefix_mut(buf: &mut [u8]) -> (&mut Self, &mut [u8]) {
80        let len = buf.len();
81        let (head, tail) = buf.split_at_mut(len);
82
83        (Self::cast_mut(head), tail)
84    }
85
86    /// Returns the backing bytes.
87    #[must_use]
88    pub const fn as_bytes(&self) -> &[u8] {
89        &self.0
90    }
91
92    /// Returns the mutable backing bytes.
93    #[must_use]
94    pub fn as_bytes_mut(&mut self) -> &mut [u8] {
95        &mut self.0
96    }
97
98    /// Returns the number of backing bytes.
99    #[must_use]
100    pub const fn len(&self) -> usize {
101        self.0.len()
102    }
103
104    /// Returns `true` when the backing byte slice is empty.
105    #[must_use]
106    pub const fn is_empty(&self) -> bool {
107        self.0.is_empty()
108    }
109}
110
111crate::tpm_byte_view!(TpmWire);
112
113/// A byte-backed TPM wire view with a fixed byte length.
114#[repr(transparent)]
115pub struct TpmWireBytes<const N: usize>([u8; N]);
116
117impl<const N: usize> TpmWireBytes<N> {
118    /// Casts a byte slice into a fixed-size TPM wire view.
119    ///
120    /// # Errors
121    ///
122    /// Returns [`UnexpectedEnd`](crate::TpmError::UnexpectedEnd) when
123    /// `buf` is smaller than `N` bytes.
124    /// Returns [`TrailingData`](crate::TpmError::TrailingData) when
125    /// `buf` is larger than `N` bytes.
126    pub fn cast(buf: &[u8]) -> TpmResult<&Self> {
127        Self::validate(buf)?;
128
129        // SAFETY: The validation above guarantees that `buf` has exactly the
130        // byte length required by `TpmWireBytes<N>`.
131        Ok(unsafe { Self::cast_unchecked(buf) })
132    }
133
134    /// Validates an exact fixed-size TPM wire view.
135    ///
136    /// # Errors
137    ///
138    /// Returns [`UnexpectedEnd`](crate::TpmError::UnexpectedEnd) when
139    /// `buf` is smaller than `N` bytes.
140    /// Returns [`TrailingData`](crate::TpmError::TrailingData) when
141    /// `buf` is larger than `N` bytes.
142    pub fn validate(buf: &[u8]) -> TpmResult<()> {
143        Self::validate_prefix(buf)?;
144
145        if buf.len() > N {
146            return Err(TpmError::TrailingData {
147                offset: N,
148                actual: buf.len() - N,
149            });
150        }
151
152        Ok(())
153    }
154
155    /// Validates that `buf` starts with a fixed-size TPM wire view.
156    ///
157    /// # Errors
158    ///
159    /// Returns [`UnexpectedEnd`](crate::TpmError::UnexpectedEnd) when
160    /// `buf` is smaller than `N` bytes.
161    pub fn validate_prefix(buf: &[u8]) -> TpmResult<()> {
162        if buf.len() < N {
163            return Err(TpmError::UnexpectedEnd {
164                offset: 0,
165                needed: N,
166                available: buf.len(),
167            });
168        }
169
170        Ok(())
171    }
172
173    /// Casts the first `N` bytes into a fixed-size TPM wire view.
174    ///
175    /// # Errors
176    ///
177    /// Returns [`UnexpectedEnd`](crate::TpmError::UnexpectedEnd) when
178    /// `buf` is smaller than `N` bytes.
179    pub fn cast_prefix(buf: &[u8]) -> TpmResult<(&Self, &[u8])> {
180        Self::validate_prefix(buf)?;
181
182        let (head, tail) = buf.split_at(N);
183
184        // SAFETY: The validation above guarantees that `head` has exactly
185        // the byte length required by `TpmWireBytes<N>`.
186        Ok((unsafe { Self::cast_unchecked(head) }, tail))
187    }
188
189    /// Casts a mutable byte slice into a fixed-size mutable TPM wire view.
190    ///
191    /// # Errors
192    ///
193    /// Returns [`UnexpectedEnd`](crate::TpmError::UnexpectedEnd) when
194    /// `buf` is smaller than `N` bytes.
195    /// Returns [`TrailingData`](crate::TpmError::TrailingData) when
196    /// `buf` is larger than `N` bytes.
197    pub fn cast_mut(buf: &mut [u8]) -> TpmResult<&mut Self> {
198        Self::validate(buf)?;
199
200        // SAFETY: The validation above guarantees that `buf` has exactly the
201        // byte length required by `TpmWireBytes<N>`. The `&mut` input provides
202        // exclusive access.
203        Ok(unsafe { Self::cast_mut_unchecked(buf) })
204    }
205
206    /// Casts the first `N` mutable bytes into a fixed-size TPM wire view.
207    ///
208    /// # Errors
209    ///
210    /// Returns [`UnexpectedEnd`](crate::TpmError::UnexpectedEnd) when
211    /// `buf` is smaller than `N` bytes.
212    pub fn cast_prefix_mut(buf: &mut [u8]) -> TpmResult<(&mut Self, &mut [u8])> {
213        Self::validate_prefix(buf)?;
214
215        let (head, tail) = buf.split_at_mut(N);
216
217        // SAFETY: The validation above guarantees that `head` has exactly
218        // the byte length required by `TpmWireBytes<N>`.
219        Ok((unsafe { Self::cast_mut_unchecked(head) }, tail))
220    }
221
222    /// Returns the backing bytes.
223    #[must_use]
224    pub const fn as_bytes(&self) -> &[u8; N] {
225        &self.0
226    }
227
228    /// Returns the mutable backing bytes.
229    #[must_use]
230    pub fn as_bytes_mut(&mut self) -> &mut [u8; N] {
231        &mut self.0
232    }
233
234    /// Returns the number of backing bytes.
235    #[must_use]
236    pub const fn len(&self) -> usize {
237        N
238    }
239
240    /// Returns `true` when the backing byte array is empty.
241    #[must_use]
242    pub const fn is_empty(&self) -> bool {
243        N == 0
244    }
245}
246
247crate::tpm_byte_view!(array TpmWireBytes<const N: usize>);
248
249impl<const N: usize> AsRef<[u8]> for TpmWireBytes<N> {
250    fn as_ref(&self) -> &[u8] {
251        self.as_bytes()
252    }
253}
254
255impl<const N: usize> AsMut<[u8]> for TpmWireBytes<N> {
256    fn as_mut(&mut self) -> &mut [u8] {
257        self.as_bytes_mut()
258    }
259}
260
261/// Casts caller-owned bytes into a TPM wire view.
262pub trait TpmCast {
263    /// Casts `buf` into `Self` after validating the wire-view invariants.
264    ///
265    /// # Errors
266    ///
267    /// Returns `Err(TpmError)` when `buf` does not satisfy the
268    /// invariants for `Self`.
269    fn cast(buf: &[u8]) -> TpmResult<&Self>;
270
271    /// Casts the first wire value in `buf` into `Self` and returns the remainder.
272    ///
273    /// # Errors
274    ///
275    /// Returns `Err(TpmError)` when `buf` does not start with a valid `Self`.
276    fn cast_prefix(buf: &[u8]) -> TpmResult<(&Self, &[u8])> {
277        let value = Self::cast(buf)?;
278
279        Ok((value, &buf[buf.len()..]))
280    }
281
282    /// Casts `buf` into `Self` without validating the wire-view invariants.
283    ///
284    /// # Safety
285    ///
286    /// The caller must ensure that `buf` satisfies the same invariants checked
287    /// by [`TpmCast::cast`].
288    unsafe fn cast_unchecked(buf: &[u8]) -> &Self;
289}
290
291/// Casts caller-owned mutable bytes into a mutable TPM wire view.
292pub trait TpmCastMut: TpmCast {
293    /// Casts `buf` into mutable `Self` after validating the wire-view invariants.
294    ///
295    /// # Errors
296    ///
297    /// Returns `Err(TpmError)` when `buf` does not satisfy the
298    /// invariants for `Self`.
299    fn cast_mut(buf: &mut [u8]) -> TpmResult<&mut Self>;
300
301    /// Casts the first mutable wire value in `buf` into `Self` and returns the remainder.
302    ///
303    /// # Errors
304    ///
305    /// Returns `Err(TpmError)` when `buf` does not start with a valid `Self`.
306    fn cast_prefix_mut(buf: &mut [u8]) -> TpmResult<(&mut Self, &mut [u8])> {
307        let len = buf.len();
308        let (head, tail) = buf.split_at_mut(len);
309        let value = Self::cast_mut(head)?;
310
311        Ok((value, tail))
312    }
313
314    /// Casts `buf` into mutable `Self` without validating the wire-view invariants.
315    ///
316    /// # Safety
317    ///
318    /// The caller must ensure that `buf` satisfies the same invariants checked
319    /// by [`TpmCastMut::cast_mut`]. The returned reference inherits the
320    /// exclusive access represented by `buf`.
321    unsafe fn cast_mut_unchecked(buf: &mut [u8]) -> &mut Self;
322}
323
324/// Reads one field from a TPM wire structure.
325pub trait TpmField<'a> {
326    type View;
327
328    /// Reads the first field from `buf` and returns the remaining bytes.
329    ///
330    /// # Errors
331    ///
332    /// Returns `Err(TpmError)` when `buf` does not start with a valid field.
333    fn cast_prefix_field(buf: &'a [u8]) -> TpmResult<(Self::View, &'a [u8])>;
334}
335
336/// Reads a union field selected by a previously-read tag.
337pub trait TpmTaggedField<'a, Tag> {
338    type View;
339
340    /// Reads the tagged field from `buf` and returns the remaining bytes.
341    ///
342    /// # Errors
343    ///
344    /// Returns `Err(TpmError)` when `tag` does not select a valid variant or
345    /// `buf` does not start with a valid selected field.
346    fn cast_tagged_prefix_field(tag: Tag, buf: &'a [u8]) -> TpmResult<(Self::View, &'a [u8])>;
347}
348
349impl<'a, T: TpmCast + ?Sized + 'a> TpmField<'a> for T {
350    type View = &'a T;
351
352    fn cast_prefix_field(buf: &'a [u8]) -> TpmResult<(Self::View, &'a [u8])> {
353        T::cast_prefix(buf)
354    }
355}
356
357impl TpmCast for TpmWire {
358    fn cast(buf: &[u8]) -> TpmResult<&Self> {
359        Ok(Self::cast(buf))
360    }
361
362    fn cast_prefix(buf: &[u8]) -> TpmResult<(&Self, &[u8])> {
363        Ok(Self::cast_prefix(buf))
364    }
365
366    unsafe fn cast_unchecked(buf: &[u8]) -> &Self {
367        // SAFETY: The caller upholds the unchecked cast contract for `TpmWire`.
368        unsafe { Self::cast_unchecked(buf) }
369    }
370}
371
372impl TpmCastMut for TpmWire {
373    fn cast_mut(buf: &mut [u8]) -> TpmResult<&mut Self> {
374        Ok(Self::cast_mut(buf))
375    }
376
377    fn cast_prefix_mut(buf: &mut [u8]) -> TpmResult<(&mut Self, &mut [u8])> {
378        Ok(Self::cast_prefix_mut(buf))
379    }
380
381    unsafe fn cast_mut_unchecked(buf: &mut [u8]) -> &mut Self {
382        // SAFETY: The caller upholds the unchecked mutable cast contract for
383        // `TpmWire`.
384        unsafe { Self::cast_mut_unchecked(buf) }
385    }
386}
387
388impl<const N: usize> TpmCast for TpmWireBytes<N> {
389    fn cast(buf: &[u8]) -> TpmResult<&Self> {
390        Self::cast(buf)
391    }
392
393    fn cast_prefix(buf: &[u8]) -> TpmResult<(&Self, &[u8])> {
394        Self::cast_prefix(buf)
395    }
396
397    unsafe fn cast_unchecked(buf: &[u8]) -> &Self {
398        // SAFETY: The caller upholds the unchecked cast contract for
399        // `TpmWireBytes<N>`.
400        unsafe { Self::cast_unchecked(buf) }
401    }
402}
403
404impl<const N: usize> TpmCastMut for TpmWireBytes<N> {
405    fn cast_mut(buf: &mut [u8]) -> TpmResult<&mut Self> {
406        Self::cast_mut(buf)
407    }
408
409    fn cast_prefix_mut(buf: &mut [u8]) -> TpmResult<(&mut Self, &mut [u8])> {
410        Self::cast_prefix_mut(buf)
411    }
412
413    unsafe fn cast_mut_unchecked(buf: &mut [u8]) -> &mut Self {
414        // SAFETY: The caller upholds the unchecked mutable cast contract for
415        // `TpmWireBytes<N>`.
416        unsafe { Self::cast_mut_unchecked(buf) }
417    }
418}
419
420/// Builds TPM wire bytes into a caller-provided mutable byte slice.
421pub struct TpmWriter<'a> {
422    buffer: &'a mut [u8],
423    cursor: usize,
424}
425
426impl<'a> TpmWriter<'a> {
427    /// Creates a new writer for the given buffer.
428    #[must_use]
429    pub fn new(buffer: &'a mut [u8]) -> Self {
430        Self { buffer, cursor: 0 }
431    }
432
433    /// Returns the number of bytes written so far.
434    #[must_use]
435    pub fn len(&self) -> usize {
436        self.cursor
437    }
438
439    /// Returns `true` if no bytes have been written.
440    #[must_use]
441    pub fn is_empty(&self) -> bool {
442        self.cursor == 0
443    }
444
445    /// Returns the bytes written so far.
446    #[must_use]
447    pub fn as_bytes(&self) -> &[u8] {
448        &self.buffer[..self.cursor]
449    }
450
451    /// Appends a slice of bytes to the writer.
452    ///
453    /// # Errors
454    ///
455    /// Returns [`BufferOverflow`](crate::TpmError::BufferOverflow) when the
456    /// capacity of the buffer is exceeded.
457    pub fn write_bytes(&mut self, bytes: &[u8]) -> TpmResult<()> {
458        let end = self
459            .cursor
460            .checked_add(bytes.len())
461            .ok_or(TpmError::BufferOverflow {
462                offset: self.cursor,
463                needed: bytes.len(),
464                available: 0,
465            })?;
466
467        if end > self.buffer.len() {
468            return Err(TpmError::BufferOverflow {
469                offset: self.cursor,
470                needed: bytes.len(),
471                available: self.buffer.len().saturating_sub(self.cursor),
472            });
473        }
474        self.buffer[self.cursor..end].copy_from_slice(bytes);
475        self.cursor = end;
476        Ok(())
477    }
478}
479
480/// Provides two ways to determine the size of an oBject: a compile-time maximum
481/// and a runtime exact size.
482pub trait TpmSized {
483    /// The estimated size of the object in its serialized form evaluated at
484    /// compile-time (always larger than the realized length).
485    const SIZE: usize;
486
487    /// Returns the exact serialized size of the object.
488    fn len(&self) -> usize;
489
490    /// Returns `true` if the object has a serialized length of zero.
491    fn is_empty(&self) -> bool {
492        self.len() == 0
493    }
494}
495
496pub trait TpmMarshal {
497    /// Marshals the object into the given writer.
498    ///
499    /// # Errors
500    ///
501    /// Returns `Err(TpmError)` on a marshal failure.
502    fn marshal(&self, writer: &mut TpmWriter) -> TpmResult<()>;
503}