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_code)]
23#![deny(clippy::all)]
24#![deny(clippy::pedantic)]
25#![recursion_limit = "256"]
26
27pub mod basic;
28pub mod constant;
29pub mod data;
30#[macro_use]
31pub mod r#macro;
32pub mod frame;
33
34/// A TPM handle, which is a 32-bit unsigned integer.
35#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
36#[repr(transparent)]
37pub struct TpmHandle(pub u32);
38
39impl core::convert::From<u32> for TpmHandle {
40    fn from(val: u32) -> Self {
41        Self(val)
42    }
43}
44
45impl core::convert::From<TpmHandle> for u32 {
46    fn from(val: TpmHandle) -> Self {
47        val.0
48    }
49}
50
51impl TpmMarshal for TpmHandle {
52    fn marshal(&self, writer: &mut TpmWriter) -> TpmMarshalResult<()> {
53        TpmMarshal::marshal(&self.0, writer)
54    }
55}
56
57impl TpmUnmarshal for TpmHandle {
58    fn unmarshal(buf: &[u8]) -> TpmUnmarshalResult<(Self, &[u8])> {
59        let (val, buf) = u32::unmarshal(buf)?;
60        Ok((Self(val), buf))
61    }
62}
63
64impl TpmSized for TpmHandle {
65    const SIZE: usize = size_of::<u32>();
66    fn len(&self) -> usize {
67        Self::SIZE
68    }
69}
70
71impl core::fmt::Display for TpmHandle {
72    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
73        core::fmt::Display::fmt(&self.0, f)
74    }
75}
76
77impl core::fmt::LowerHex for TpmHandle {
78    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
79        core::fmt::LowerHex::fmt(&self.0, f)
80    }
81}
82
83impl core::fmt::UpperHex for TpmHandle {
84    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
85        core::fmt::UpperHex::fmt(&self.0, f)
86    }
87}
88
89#[derive(Debug, PartialEq, Eq)]
90pub enum TpmDiscriminant {
91    Signed(i64),
92    Unsigned(u64),
93}
94
95impl core::fmt::LowerHex for TpmDiscriminant {
96    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
97        match self {
98            TpmDiscriminant::Signed(v) => write!(f, "{v:x}"),
99            TpmDiscriminant::Unsigned(v) => write!(f, "{v:x}"),
100        }
101    }
102}
103
104#[derive(Debug, PartialEq, Eq)]
105/// TPM protocol marshaling error.
106pub enum TpmMarshalError {
107    /// Capacity of a structure has been exceeded,
108    CapacityExceeded,
109    /// Input value is not valid.
110    InvalidValue,
111}
112
113impl core::fmt::Display for TpmMarshalError {
114    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
115        match self {
116            Self::CapacityExceeded => write!(f, "capacity exceeded"),
117            Self::InvalidValue => write!(f, "invalid value"),
118        }
119    }
120}
121
122#[derive(Debug, PartialEq, Eq)]
123/// TPM protocol unmarshaling error.
124pub enum TpmUnmarshalError {
125    /// Capacity of a structure has been exceeded,
126    CapacityExceeded,
127    /// Unknown discriminant.
128    InvalidDiscriminant(&'static str, TpmDiscriminant),
129    /// The frame or object is malformed.
130    MalformedValue,
131    /// `TrailingData` data left.
132    TrailingData,
133    /// Not enough bytes to unmarshal.
134    TruncatedData,
135}
136
137impl core::fmt::Display for TpmUnmarshalError {
138    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
139        match self {
140            Self::CapacityExceeded => write!(f, "capacity exceeded"),
141            Self::InvalidDiscriminant(type_name, value) => {
142                write!(f, "invalid discriminant: {type_name}: 0x{value:x}")
143            }
144            Self::MalformedValue => write!(f, "malformed value"),
145            Self::TrailingData => write!(f, "trailing data"),
146            Self::TruncatedData => write!(f, "truncated data"),
147        }
148    }
149}
150
151pub type TpmMarshalResult<T> = Result<T, TpmMarshalError>;
152pub type TpmUnmarshalResult<T> = Result<T, TpmUnmarshalError>;
153
154/// Writes into a mutable byte slice.
155pub struct TpmWriter<'a> {
156    buffer: &'a mut [u8],
157    cursor: usize,
158}
159
160impl<'a> TpmWriter<'a> {
161    /// Creates a new writer for the given buffer.
162    #[must_use]
163    pub fn new(buffer: &'a mut [u8]) -> Self {
164        Self { buffer, cursor: 0 }
165    }
166
167    /// Returns the number of bytes written so far.
168    #[must_use]
169    pub fn len(&self) -> usize {
170        self.cursor
171    }
172
173    /// Returns `true` if no bytes have been written.
174    #[must_use]
175    pub fn is_empty(&self) -> bool {
176        self.cursor == 0
177    }
178
179    /// Appends a slice of bytes to the writer.
180    ///
181    /// # Errors
182    ///
183    /// Returns `TpmError::CapacityExceeded` if the writer does not have enough
184    /// capacity to hold the new bytes.
185    pub fn write_bytes(&mut self, bytes: &[u8]) -> TpmMarshalResult<()> {
186        let end = self.cursor + bytes.len();
187        if end > self.buffer.len() {
188            return Err(TpmMarshalError::CapacityExceeded);
189        }
190        self.buffer[self.cursor..end].copy_from_slice(bytes);
191        self.cursor = end;
192        Ok(())
193    }
194}
195
196/// Provides two ways to determine the size of an object: a compile-time maximum
197/// and a runtime exact size.
198pub trait TpmSized {
199    /// The estimated size of the object in its serialized form evaluated at
200    /// compile-time (always larger than the realized length).
201    const SIZE: usize;
202
203    /// Returns the exact serialized size of the object.
204    fn len(&self) -> usize;
205
206    /// Returns `true` if the object has a serialized length of zero.
207    fn is_empty(&self) -> bool {
208        self.len() == 0
209    }
210}
211
212pub trait TpmMarshal: TpmSized {
213    /// Marshals the object into the given writer.
214    ///
215    /// # Errors
216    ///
217    /// Returns `Err(TpmError)` on a marshal failure.
218    fn marshal(&self, writer: &mut TpmWriter) -> TpmMarshalResult<()>;
219}
220
221pub trait TpmUnmarshal: Sized + TpmSized {
222    /// Unmarshals an object from the given buffer.
223    ///
224    /// Returns the unmarshald type and the remaining portion of the buffer.
225    ///
226    /// # Errors
227    ///
228    /// Returns `Err(TpmError)` on a unmarshal failure.
229    fn unmarshal(buf: &[u8]) -> TpmUnmarshalResult<(Self, &[u8])>;
230}
231
232/// Types that are composed of a tag and a value e.g., a union.
233pub trait TpmTagged {
234    /// The type of the tag/discriminant.
235    type Tag: TpmUnmarshal + TpmMarshal + Copy;
236    /// The type of the value/union.
237    type Value;
238}
239
240/// Unmarshals a tagged object from a buffer.
241pub trait TpmUnmarshalTagged: Sized {
242    /// Unmarshals a tagged object from the given buffer using the provided tag.
243    ///
244    /// # Errors
245    ///
246    /// This method can return any error of the underlying type's `TpmUnmarshal` implementation,
247    /// such as a `TpmError::TruncatedData` if the buffer is too small or an
248    /// `TpmError::MalformedValue` if the data is malformed.
249    fn unmarshal_tagged(
250        tag: <Self as TpmTagged>::Tag,
251        buf: &[u8],
252    ) -> TpmUnmarshalResult<(Self, &[u8])>
253    where
254        Self: TpmTagged,
255        <Self as TpmTagged>::Tag: TpmUnmarshal + TpmMarshal;
256}
257
258tpm_integer!(u8, Unsigned);
259tpm_integer!(i8, Signed);
260tpm_integer!(i32, Signed);
261tpm_integer!(u16, Unsigned);
262tpm_integer!(u32, Unsigned);
263tpm_integer!(u64, Unsigned);