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.
35pub type TpmHandle = crate::basic::Uint32;
36
37/// TPM frame marshaling and unmarshaling error type containing variants
38/// for all the possible error conditions.
39#[derive(Debug, PartialEq, Eq, Copy, Clone)]
40pub enum TpmProtocolError {
41    /// Trying to marshal more bytes than buffer has space. This is unexpected
42    /// situation, and should be considered possible bug in the crate itself.
43    BufferOverflow,
44
45    /// Integer overflow while converting to an integer of a different size.
46    IntegerTooLarge,
47
48    /// Boolean value was expected but the value is neither `0` nor `1`.
49    InvalidBoolean,
50
51    /// Non-existent command code encountered.
52    InvalidCc,
53
54    /// An [`TpmAttest`](crate::data::TpmAttest) instance contains an invalid
55    /// magic value.
56    InvalidMagicNumber,
57
58    /// Tag is neither [`Sessions`](crate::data::TpmSt::Sessions) nor
59    /// [`NoSessions`](crate::data::TpmSt::NoSessions).
60    InvalidTag,
61
62    /// Buffer contains more bytes than allowed by the TCG specifications.
63    TooManyBytes,
64
65    /// List contains more items than allowed by the TCG specifications.
66    TooManyItems,
67
68    /// Trailing data left after unmarshaling.
69    TrailingData,
70
71    /// Run out of bytes while unmarshaling.
72    UnexpectedEnd,
73
74    /// The variant accessed is not available.
75    VariantNotAvailable,
76}
77
78impl core::fmt::Display for TpmProtocolError {
79    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
80        match self {
81            Self::BufferOverflow => write!(f, "buffer overflow"),
82            Self::InvalidBoolean => write!(f, "invalid boolean value"),
83            Self::InvalidCc => write!(f, "invalid command code"),
84            Self::InvalidMagicNumber => write!(f, "invalid magic number"),
85            Self::InvalidTag => write!(f, "invalid tag"),
86            Self::IntegerTooLarge => write!(f, "integer overflow"),
87            Self::TooManyBytes => write!(f, "buffer capacity surpassed"),
88            Self::TooManyItems => write!(f, "list capaacity surpassed"),
89            Self::TrailingData => write!(f, "trailing data"),
90            Self::UnexpectedEnd => write!(f, "unexpected end"),
91            Self::VariantNotAvailable => write!(f, "enum variant is not available"),
92        }
93    }
94}
95
96impl core::error::Error for TpmProtocolError {}
97
98pub type TpmResult<T> = Result<T, TpmProtocolError>;
99
100/// Writes into a mutable byte slice.
101pub struct TpmWriter<'a> {
102    buffer: &'a mut [u8],
103    cursor: usize,
104}
105
106impl<'a> TpmWriter<'a> {
107    /// Creates a new writer for the given buffer.
108    #[must_use]
109    pub fn new(buffer: &'a mut [u8]) -> Self {
110        Self { buffer, cursor: 0 }
111    }
112
113    /// Returns the number of bytes written so far.
114    #[must_use]
115    pub fn len(&self) -> usize {
116        self.cursor
117    }
118
119    /// Returns `true` if no bytes have been written.
120    #[must_use]
121    pub fn is_empty(&self) -> bool {
122        self.cursor == 0
123    }
124
125    /// Appends a slice of bytes to the writer.
126    ///
127    /// # Errors
128    ///
129    /// Returns [`OutOfMemory`](crate::TpmProtocolError::OutOfMemory)
130    /// when the capacity of the buffer is exceeded.
131    pub fn write_bytes(&mut self, bytes: &[u8]) -> TpmResult<()> {
132        let end = self
133            .cursor
134            .checked_add(bytes.len())
135            .ok_or(TpmProtocolError::BufferOverflow)?;
136
137        if end > self.buffer.len() {
138            return Err(TpmProtocolError::BufferOverflow);
139        }
140        self.buffer[self.cursor..end].copy_from_slice(bytes);
141        self.cursor = end;
142        Ok(())
143    }
144}
145
146/// Provides two ways to determine the size of an oBject: a compile-time maximum
147/// and a runtime exact size.
148pub trait TpmSized {
149    /// The estimated size of the object in its serialized form evaluated at
150    /// compile-time (always larger than the realized length).
151    const SIZE: usize;
152
153    /// Returns the exact serialized size of the object.
154    fn len(&self) -> usize;
155
156    /// Returns `true` if the object has a serialized length of zero.
157    fn is_empty(&self) -> bool {
158        self.len() == 0
159    }
160}
161
162pub trait TpmMarshal {
163    /// Marshals the object into the given writer.
164    ///
165    /// # Errors
166    ///
167    /// Returns `Err(TpmProtocolError)` on a marshal failure.
168    fn marshal(&self, writer: &mut TpmWriter) -> TpmResult<()>;
169}
170
171pub trait TpmUnmarshal: Sized + TpmSized {
172    /// Unmarshals an object from the given buffer.
173    ///
174    /// Returns the unmarshald type and the remaining portion of the buffer.
175    ///
176    /// # Errors
177    ///
178    /// Returns `Err(TpmProtocolError)` on a unmarshal failure.
179    fn unmarshal(buf: &[u8]) -> TpmResult<(Self, &[u8])>;
180}
181
182/// Types that are composed of a tag and a value e.g., a union.
183pub trait TpmTagged {
184    /// The type of the tag/discriminant.
185    type Tag: TpmUnmarshal + TpmMarshal + Copy;
186    /// The type of the value/union.
187    type Value;
188}
189
190/// Unmarshals a tagged object from a buffer.
191pub trait TpmUnmarshalTagged: Sized {
192    /// Unmarshals a tagged object from the given buffer using the provided tag.
193    ///
194    /// # Errors
195    ///
196    /// This method can return any error of the underlying type's `TpmUnmarshal` implementation,
197    /// such as a `TpmProtocolError::UnexpectedEnd` if the buffer is too small or an
198    /// `TpmProtocolError::MalformedValue` if the data is malformed.
199    fn unmarshal_tagged(tag: <Self as TpmTagged>::Tag, buf: &[u8]) -> TpmResult<(Self, &[u8])>
200    where
201        Self: TpmTagged,
202        <Self as TpmTagged>::Tag: TpmUnmarshal + TpmMarshal;
203}