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