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}