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