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