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//! ## Zero-Copy Contract
22//!
23//! Read-side protocol APIs operate on caller-owned byte slices and return
24//! borrowed wire views into those slices. Implementations must not copy payload
25//! bytes to inspect frames or nested TPM values. Scalar fields may be read by
26//! value from their big-endian wire representation.
27//!
28//! Validation must prove all exposed borrowed views are bounded by the original
29//! input slice. Any malformed length, tag, selector, or trailing byte condition
30//! must be reported as [`TpmError`] instead of panicking.
31//!
32//! The crate does not use the external `zerocopy` crate.
33
34#![cfg_attr(not(test), no_std)]
35#![deny(unsafe_op_in_unsafe_fn)]
36#![deny(clippy::all)]
37#![deny(clippy::undocumented_unsafe_blocks)]
38#![deny(clippy::pedantic)]
39#![recursion_limit = "256"]
40
41pub mod basic;
42pub mod constant;
43pub mod data;
44mod error;
45#[macro_use]
46pub mod r#macro;
47pub mod frame;
48
49pub use self::error::{TpmError, TpmResult, tpm_offset, tpm_value};
50
51/// A byte-backed TPM wire view.
52#[repr(transparent)]
53pub struct TpmWire([u8]);
54
55impl TpmWire {
56 /// Casts a byte slice into a TPM wire view.
57 #[must_use]
58 pub fn cast(buf: &[u8]) -> &Self {
59 // SAFETY: `TpmWire` accepts any byte slice as its backing storage.
60 unsafe { Self::cast_unchecked(buf) }
61 }
62
63 /// Casts a byte slice into a TPM wire view and returns no remainder.
64 #[must_use]
65 pub fn cast_prefix(buf: &[u8]) -> (&Self, &[u8]) {
66 (Self::cast(buf), &buf[buf.len()..])
67 }
68
69 /// Casts a mutable byte slice into a mutable TPM wire view.
70 #[must_use]
71 pub fn cast_mut(buf: &mut [u8]) -> &mut Self {
72 // SAFETY: `TpmWire` accepts any mutable byte slice as its backing
73 // storage. The `&mut` input provides exclusive access.
74 unsafe { Self::cast_mut_unchecked(buf) }
75 }
76
77 /// Casts a mutable byte slice into a mutable TPM wire view and returns no remainder.
78 #[must_use]
79 pub fn cast_prefix_mut(buf: &mut [u8]) -> (&mut Self, &mut [u8]) {
80 let len = buf.len();
81 let (head, tail) = buf.split_at_mut(len);
82
83 (Self::cast_mut(head), tail)
84 }
85
86 /// Returns the backing bytes.
87 #[must_use]
88 pub const fn as_bytes(&self) -> &[u8] {
89 &self.0
90 }
91
92 /// Returns the mutable backing bytes.
93 #[must_use]
94 pub fn as_bytes_mut(&mut self) -> &mut [u8] {
95 &mut self.0
96 }
97
98 /// Returns the number of backing bytes.
99 #[must_use]
100 pub const fn len(&self) -> usize {
101 self.0.len()
102 }
103
104 /// Returns `true` when the backing byte slice is empty.
105 #[must_use]
106 pub const fn is_empty(&self) -> bool {
107 self.0.is_empty()
108 }
109}
110
111crate::tpm_byte_view!(TpmWire);
112
113/// A byte-backed TPM wire view with a fixed byte length.
114#[repr(transparent)]
115pub struct TpmWireBytes<const N: usize>([u8; N]);
116
117impl<const N: usize> TpmWireBytes<N> {
118 /// Casts a byte slice into a fixed-size TPM wire view.
119 ///
120 /// # Errors
121 ///
122 /// Returns [`UnexpectedEnd`](crate::TpmError::UnexpectedEnd) when
123 /// `buf` is smaller than `N` bytes.
124 /// Returns [`TrailingData`](crate::TpmError::TrailingData) when
125 /// `buf` is larger than `N` bytes.
126 pub fn cast(buf: &[u8]) -> TpmResult<&Self> {
127 Self::validate(buf)?;
128
129 // SAFETY: The validation above guarantees that `buf` has exactly the
130 // byte length required by `TpmWireBytes<N>`.
131 Ok(unsafe { Self::cast_unchecked(buf) })
132 }
133
134 /// Validates an exact fixed-size TPM wire view.
135 ///
136 /// # Errors
137 ///
138 /// Returns [`UnexpectedEnd`](crate::TpmError::UnexpectedEnd) when
139 /// `buf` is smaller than `N` bytes.
140 /// Returns [`TrailingData`](crate::TpmError::TrailingData) when
141 /// `buf` is larger than `N` bytes.
142 pub fn validate(buf: &[u8]) -> TpmResult<()> {
143 Self::validate_prefix(buf)?;
144
145 if buf.len() > N {
146 return Err(TpmError::TrailingData {
147 offset: N,
148 actual: buf.len() - N,
149 });
150 }
151
152 Ok(())
153 }
154
155 /// Validates that `buf` starts with a fixed-size TPM wire view.
156 ///
157 /// # Errors
158 ///
159 /// Returns [`UnexpectedEnd`](crate::TpmError::UnexpectedEnd) when
160 /// `buf` is smaller than `N` bytes.
161 pub fn validate_prefix(buf: &[u8]) -> TpmResult<()> {
162 if buf.len() < N {
163 return Err(TpmError::UnexpectedEnd {
164 offset: 0,
165 needed: N,
166 available: buf.len(),
167 });
168 }
169
170 Ok(())
171 }
172
173 /// Casts the first `N` bytes into a fixed-size TPM wire view.
174 ///
175 /// # Errors
176 ///
177 /// Returns [`UnexpectedEnd`](crate::TpmError::UnexpectedEnd) when
178 /// `buf` is smaller than `N` bytes.
179 pub fn cast_prefix(buf: &[u8]) -> TpmResult<(&Self, &[u8])> {
180 Self::validate_prefix(buf)?;
181
182 let (head, tail) = buf.split_at(N);
183
184 // SAFETY: The validation above guarantees that `head` has exactly
185 // the byte length required by `TpmWireBytes<N>`.
186 Ok((unsafe { Self::cast_unchecked(head) }, tail))
187 }
188
189 /// Casts a mutable byte slice into a fixed-size mutable TPM wire view.
190 ///
191 /// # Errors
192 ///
193 /// Returns [`UnexpectedEnd`](crate::TpmError::UnexpectedEnd) when
194 /// `buf` is smaller than `N` bytes.
195 /// Returns [`TrailingData`](crate::TpmError::TrailingData) when
196 /// `buf` is larger than `N` bytes.
197 pub fn cast_mut(buf: &mut [u8]) -> TpmResult<&mut Self> {
198 Self::validate(buf)?;
199
200 // SAFETY: The validation above guarantees that `buf` has exactly the
201 // byte length required by `TpmWireBytes<N>`. The `&mut` input provides
202 // exclusive access.
203 Ok(unsafe { Self::cast_mut_unchecked(buf) })
204 }
205
206 /// Casts the first `N` mutable bytes into a fixed-size TPM wire view.
207 ///
208 /// # Errors
209 ///
210 /// Returns [`UnexpectedEnd`](crate::TpmError::UnexpectedEnd) when
211 /// `buf` is smaller than `N` bytes.
212 pub fn cast_prefix_mut(buf: &mut [u8]) -> TpmResult<(&mut Self, &mut [u8])> {
213 Self::validate_prefix(buf)?;
214
215 let (head, tail) = buf.split_at_mut(N);
216
217 // SAFETY: The validation above guarantees that `head` has exactly
218 // the byte length required by `TpmWireBytes<N>`.
219 Ok((unsafe { Self::cast_mut_unchecked(head) }, tail))
220 }
221
222 /// Returns the backing bytes.
223 #[must_use]
224 pub const fn as_bytes(&self) -> &[u8; N] {
225 &self.0
226 }
227
228 /// Returns the mutable backing bytes.
229 #[must_use]
230 pub fn as_bytes_mut(&mut self) -> &mut [u8; N] {
231 &mut self.0
232 }
233
234 /// Returns the number of backing bytes.
235 #[must_use]
236 pub const fn len(&self) -> usize {
237 N
238 }
239
240 /// Returns `true` when the backing byte array is empty.
241 #[must_use]
242 pub const fn is_empty(&self) -> bool {
243 N == 0
244 }
245}
246
247crate::tpm_byte_view!(array TpmWireBytes<const N: usize>);
248
249impl<const N: usize> AsRef<[u8]> for TpmWireBytes<N> {
250 fn as_ref(&self) -> &[u8] {
251 self.as_bytes()
252 }
253}
254
255impl<const N: usize> AsMut<[u8]> for TpmWireBytes<N> {
256 fn as_mut(&mut self) -> &mut [u8] {
257 self.as_bytes_mut()
258 }
259}
260
261/// Casts caller-owned bytes into a TPM wire view.
262pub trait TpmCast {
263 /// Casts `buf` into `Self` after validating the wire-view invariants.
264 ///
265 /// # Errors
266 ///
267 /// Returns `Err(TpmError)` when `buf` does not satisfy the
268 /// invariants for `Self`.
269 fn cast(buf: &[u8]) -> TpmResult<&Self>;
270
271 /// Casts the first wire value in `buf` into `Self` and returns the remainder.
272 ///
273 /// # Errors
274 ///
275 /// Returns `Err(TpmError)` when `buf` does not start with a valid `Self`.
276 fn cast_prefix(buf: &[u8]) -> TpmResult<(&Self, &[u8])> {
277 let value = Self::cast(buf)?;
278
279 Ok((value, &buf[buf.len()..]))
280 }
281
282 /// Casts `buf` into `Self` without validating the wire-view invariants.
283 ///
284 /// # Safety
285 ///
286 /// The caller must ensure that `buf` satisfies the same invariants checked
287 /// by [`TpmCast::cast`].
288 unsafe fn cast_unchecked(buf: &[u8]) -> &Self;
289}
290
291/// Casts caller-owned mutable bytes into a mutable TPM wire view.
292pub trait TpmCastMut: TpmCast {
293 /// Casts `buf` into mutable `Self` after validating the wire-view invariants.
294 ///
295 /// # Errors
296 ///
297 /// Returns `Err(TpmError)` when `buf` does not satisfy the
298 /// invariants for `Self`.
299 fn cast_mut(buf: &mut [u8]) -> TpmResult<&mut Self>;
300
301 /// Casts the first mutable wire value in `buf` into `Self` and returns the remainder.
302 ///
303 /// # Errors
304 ///
305 /// Returns `Err(TpmError)` when `buf` does not start with a valid `Self`.
306 fn cast_prefix_mut(buf: &mut [u8]) -> TpmResult<(&mut Self, &mut [u8])> {
307 let len = buf.len();
308 let (head, tail) = buf.split_at_mut(len);
309 let value = Self::cast_mut(head)?;
310
311 Ok((value, tail))
312 }
313
314 /// Casts `buf` into mutable `Self` without validating the wire-view invariants.
315 ///
316 /// # Safety
317 ///
318 /// The caller must ensure that `buf` satisfies the same invariants checked
319 /// by [`TpmCastMut::cast_mut`]. The returned reference inherits the
320 /// exclusive access represented by `buf`.
321 unsafe fn cast_mut_unchecked(buf: &mut [u8]) -> &mut Self;
322}
323
324/// Reads one field from a TPM wire structure.
325pub trait TpmField<'a> {
326 type View;
327
328 /// Reads the first field from `buf` and returns the remaining bytes.
329 ///
330 /// # Errors
331 ///
332 /// Returns `Err(TpmError)` when `buf` does not start with a valid field.
333 fn cast_prefix_field(buf: &'a [u8]) -> TpmResult<(Self::View, &'a [u8])>;
334}
335
336/// Reads a union field selected by a previously-read tag.
337pub trait TpmTaggedField<'a, Tag> {
338 type View;
339
340 /// Reads the tagged field from `buf` and returns the remaining bytes.
341 ///
342 /// # Errors
343 ///
344 /// Returns `Err(TpmError)` when `tag` does not select a valid variant or
345 /// `buf` does not start with a valid selected field.
346 fn cast_tagged_prefix_field(tag: Tag, buf: &'a [u8]) -> TpmResult<(Self::View, &'a [u8])>;
347}
348
349impl<'a, T: TpmCast + ?Sized + 'a> TpmField<'a> for T {
350 type View = &'a T;
351
352 fn cast_prefix_field(buf: &'a [u8]) -> TpmResult<(Self::View, &'a [u8])> {
353 T::cast_prefix(buf)
354 }
355}
356
357impl TpmCast for TpmWire {
358 fn cast(buf: &[u8]) -> TpmResult<&Self> {
359 Ok(Self::cast(buf))
360 }
361
362 fn cast_prefix(buf: &[u8]) -> TpmResult<(&Self, &[u8])> {
363 Ok(Self::cast_prefix(buf))
364 }
365
366 unsafe fn cast_unchecked(buf: &[u8]) -> &Self {
367 // SAFETY: The caller upholds the unchecked cast contract for `TpmWire`.
368 unsafe { Self::cast_unchecked(buf) }
369 }
370}
371
372impl TpmCastMut for TpmWire {
373 fn cast_mut(buf: &mut [u8]) -> TpmResult<&mut Self> {
374 Ok(Self::cast_mut(buf))
375 }
376
377 fn cast_prefix_mut(buf: &mut [u8]) -> TpmResult<(&mut Self, &mut [u8])> {
378 Ok(Self::cast_prefix_mut(buf))
379 }
380
381 unsafe fn cast_mut_unchecked(buf: &mut [u8]) -> &mut Self {
382 // SAFETY: The caller upholds the unchecked mutable cast contract for
383 // `TpmWire`.
384 unsafe { Self::cast_mut_unchecked(buf) }
385 }
386}
387
388impl<const N: usize> TpmCast for TpmWireBytes<N> {
389 fn cast(buf: &[u8]) -> TpmResult<&Self> {
390 Self::cast(buf)
391 }
392
393 fn cast_prefix(buf: &[u8]) -> TpmResult<(&Self, &[u8])> {
394 Self::cast_prefix(buf)
395 }
396
397 unsafe fn cast_unchecked(buf: &[u8]) -> &Self {
398 // SAFETY: The caller upholds the unchecked cast contract for
399 // `TpmWireBytes<N>`.
400 unsafe { Self::cast_unchecked(buf) }
401 }
402}
403
404impl<const N: usize> TpmCastMut for TpmWireBytes<N> {
405 fn cast_mut(buf: &mut [u8]) -> TpmResult<&mut Self> {
406 Self::cast_mut(buf)
407 }
408
409 fn cast_prefix_mut(buf: &mut [u8]) -> TpmResult<(&mut Self, &mut [u8])> {
410 Self::cast_prefix_mut(buf)
411 }
412
413 unsafe fn cast_mut_unchecked(buf: &mut [u8]) -> &mut Self {
414 // SAFETY: The caller upholds the unchecked mutable cast contract for
415 // `TpmWireBytes<N>`.
416 unsafe { Self::cast_mut_unchecked(buf) }
417 }
418}
419
420/// Builds TPM wire bytes into a caller-provided mutable byte slice.
421pub struct TpmWriter<'a> {
422 buffer: &'a mut [u8],
423 cursor: usize,
424}
425
426impl<'a> TpmWriter<'a> {
427 /// Creates a new writer for the given buffer.
428 #[must_use]
429 pub fn new(buffer: &'a mut [u8]) -> Self {
430 Self { buffer, cursor: 0 }
431 }
432
433 /// Returns the number of bytes written so far.
434 #[must_use]
435 pub fn len(&self) -> usize {
436 self.cursor
437 }
438
439 /// Returns `true` if no bytes have been written.
440 #[must_use]
441 pub fn is_empty(&self) -> bool {
442 self.cursor == 0
443 }
444
445 /// Returns the bytes written so far.
446 #[must_use]
447 pub fn as_bytes(&self) -> &[u8] {
448 &self.buffer[..self.cursor]
449 }
450
451 /// Appends a slice of bytes to the writer.
452 ///
453 /// # Errors
454 ///
455 /// Returns [`BufferOverflow`](crate::TpmError::BufferOverflow) when the
456 /// capacity of the buffer is exceeded.
457 pub fn write_bytes(&mut self, bytes: &[u8]) -> TpmResult<()> {
458 let end = self
459 .cursor
460 .checked_add(bytes.len())
461 .ok_or(TpmError::BufferOverflow {
462 offset: self.cursor,
463 needed: bytes.len(),
464 available: 0,
465 })?;
466
467 if end > self.buffer.len() {
468 return Err(TpmError::BufferOverflow {
469 offset: self.cursor,
470 needed: bytes.len(),
471 available: self.buffer.len().saturating_sub(self.cursor),
472 });
473 }
474 self.buffer[self.cursor..end].copy_from_slice(bytes);
475 self.cursor = end;
476 Ok(())
477 }
478}
479
480/// Provides two ways to determine the size of an oBject: a compile-time maximum
481/// and a runtime exact size.
482pub trait TpmSized {
483 /// The estimated size of the object in its serialized form evaluated at
484 /// compile-time (always larger than the realized length).
485 const SIZE: usize;
486
487 /// Returns the exact serialized size of the object.
488 fn len(&self) -> usize;
489
490 /// Returns `true` if the object has a serialized length of zero.
491 fn is_empty(&self) -> bool {
492 self.len() == 0
493 }
494}
495
496pub trait TpmMarshal {
497 /// Marshals the object into the given writer.
498 ///
499 /// # Errors
500 ///
501 /// Returns `Err(TpmError)` on a marshal failure.
502 fn marshal(&self, writer: &mut TpmWriter) -> TpmResult<()>;
503}