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) -> TpmResult<()> {
53        TpmMarshal::marshal(&self.0, writer)
54    }
55}
56
57impl TpmUnmarshal for TpmHandle {
58    fn unmarshal(buf: &[u8]) -> TpmResult<(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/// TPM protocol data error.
90#[derive(Debug, PartialEq, Eq)]
91pub enum TpmProtocolError {
92    /// The amount data exceeds the maximum capacity of a type, as defined in
93    /// the TCG specifications.
94    CapacityExceeded,
95    /// An [`TpmAttest`](crate::data::TpmAttest) instance contains an invalid
96    /// magic value.
97    InvalidAttestMagic,
98    /// Tag is neither [`Sessions`](crate::data::TpmSt::Sessions) nor
99    /// [`NoSessions`](crate::data::TpmSt::NoSessions).
100    InvalidTag,
101    /// A value is not defined for the type.
102    InvalidValue,
103    /// An enum variant is not valid for the context of use.
104    InvalidVariant,
105    /// A cryptographic operation failed.
106    OperationFailed,
107    /// The number of list items exceeds the maximum capability of a type, as
108    /// defined in the TCG specifications.
109    TooManyItems,
110    /// Trailing data left after unmarshaling.
111    TrailingData,
112    /// Not enough bytes to unmarshal.
113    UnexpectedEnd,
114}
115
116impl core::fmt::Display for TpmProtocolError {
117    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
118        match self {
119            Self::CapacityExceeded => write!(f, "buffer capacity exceeded"),
120            Self::InvalidAttestMagic => write!(f, "invalid attestation magiC"),
121            Self::InvalidTag => write!(f, "invalid tag"),
122            Self::InvalidValue => write!(f, "invalid value"),
123            Self::InvalidVariant => write!(f, "invalid variant"),
124            Self::OperationFailed => write!(f, "operation failed"),
125            Self::TooManyItems => write!(f, "too many list items"),
126            Self::TrailingData => write!(f, "trailing data"),
127            Self::UnexpectedEnd => write!(f, "unexpected end"),
128        }
129    }
130}
131
132impl core::error::Error for TpmProtocolError {}
133
134pub type TpmResult<T> = Result<T, TpmProtocolError>;
135
136/// Writes into a mutable byte slice.
137pub struct TpmWriter<'a> {
138    buffer: &'a mut [u8],
139    cursor: usize,
140}
141
142impl<'a> TpmWriter<'a> {
143    /// Creates a new writer for the given buffer.
144    #[must_use]
145    pub fn new(buffer: &'a mut [u8]) -> Self {
146        Self { buffer, cursor: 0 }
147    }
148
149    /// Returns the number of bytes written so far.
150    #[must_use]
151    pub fn len(&self) -> usize {
152        self.cursor
153    }
154
155    /// Returns `true` if no bytes have been written.
156    #[must_use]
157    pub fn is_empty(&self) -> bool {
158        self.cursor == 0
159    }
160
161    /// Appends a slice of bytes to the writer.
162    ///
163    /// # Errors
164    ///
165    /// Returns [`CapacityExceeded`](crate::TpmProtocolError::CapacitExceeded)
166    /// when the capacity of the buffer is exceeded.
167    pub fn write_bytes(&mut self, bytes: &[u8]) -> TpmResult<()> {
168        let end = self.cursor + bytes.len();
169        if end > self.buffer.len() {
170            return Err(TpmProtocolError::CapacityExceeded);
171        }
172        self.buffer[self.cursor..end].copy_from_slice(bytes);
173        self.cursor = end;
174        Ok(())
175    }
176}
177
178/// Provides two ways to determine the size of an oBject: a compile-time maximum
179/// and a runtime exact size.
180pub trait TpmSized {
181    /// The estimated size of the object in its serialized form evaluated at
182    /// compile-time (always larger than the realized length).
183    const SIZE: usize;
184
185    /// Returns the exact serialized size of the object.
186    fn len(&self) -> usize;
187
188    /// Returns `true` if the object has a serialized length of zero.
189    fn is_empty(&self) -> bool {
190        self.len() == 0
191    }
192}
193
194pub trait TpmMarshal: TpmSized {
195    /// Marshals the object into the given writer.
196    ///
197    /// # Errors
198    ///
199    /// Returns `Err(TpmProtocolError)` on a marshal failure.
200    fn marshal(&self, writer: &mut TpmWriter) -> TpmResult<()>;
201}
202
203pub trait TpmUnmarshal: Sized + TpmSized {
204    /// Unmarshals an object from the given buffer.
205    ///
206    /// Returns the unmarshald type and the remaining portion of the buffer.
207    ///
208    /// # Errors
209    ///
210    /// Returns `Err(TpmProtocolError)` on a unmarshal failure.
211    fn unmarshal(buf: &[u8]) -> TpmResult<(Self, &[u8])>;
212}
213
214/// Types that are composed of a tag and a value e.g., a union.
215pub trait TpmTagged {
216    /// The type of the tag/discriminant.
217    type Tag: TpmUnmarshal + TpmMarshal + Copy;
218    /// The type of the value/union.
219    type Value;
220}
221
222/// Unmarshals a tagged object from a buffer.
223pub trait TpmUnmarshalTagged: Sized {
224    /// Unmarshals a tagged object from the given buffer using the provided tag.
225    ///
226    /// # Errors
227    ///
228    /// This method can return any error of the underlying type's `TpmUnmarshal` implementation,
229    /// such as a `TpmProtocolError::UnexpectedEnd` if the buffer is too small or an
230    /// `TpmProtocolError::MalformedValue` if the data is malformed.
231    fn unmarshal_tagged(tag: <Self as TpmTagged>::Tag, buf: &[u8]) -> TpmResult<(Self, &[u8])>
232    where
233        Self: TpmTagged,
234        <Self as TpmTagged>::Tag: TpmUnmarshal + TpmMarshal;
235}
236
237tpm_integer!(u8, Unsigned);
238tpm_integer!(i8, Signed);
239tpm_integer!(i32, Signed);
240tpm_integer!(u16, Unsigned);
241tpm_integer!(u32, Unsigned);
242tpm_integer!(u64, Unsigned);