tpm2_protocol/error.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/// Returns the byte offset of a cursor slice inside a base slice.
6#[must_use]
7pub fn tpm_offset(base: &[u8], cursor: &[u8]) -> usize {
8 let base_addr = base.as_ptr() as usize;
9 let cursor_addr = cursor.as_ptr() as usize;
10
11 cursor_addr.saturating_sub(base_addr).min(base.len())
12}
13
14/// Widens a `usize` count into the `u64` carried by value-bearing errors.
15#[must_use]
16#[allow(clippy::cast_possible_truncation)]
17pub const fn tpm_value(value: usize) -> u64 {
18 value as u64
19}
20
21/// TPM frame marshaling and unmarshaling error type.
22///
23/// Every variant carries the byte `offset` from the start of the parsed buffer
24/// along with the diagnostic counts relevant to that failure.
25#[derive(Debug, PartialEq, Eq, Copy, Clone)]
26pub enum TpmError {
27 /// Trying to marshal more bytes than buffer has space. This is unexpected
28 /// situation, and should be considered possible bug in the crate itself.
29 BufferOverflow {
30 /// Byte offset from the start of the buffer.
31 offset: usize,
32 /// Required byte count.
33 needed: usize,
34 /// Available byte count.
35 available: usize,
36 },
37
38 /// Integer overflow while converting to an integer of a different size.
39 IntegerTooLarge {
40 /// Byte offset from the start of the buffer.
41 offset: usize,
42 /// Raw value that did not fit.
43 value: u64,
44 },
45
46 /// Boolean value was expected but the value is neither `0` nor `1`.
47 InvalidBoolean {
48 /// Byte offset from the start of the buffer.
49 offset: usize,
50 /// Raw value encountered.
51 value: u64,
52 },
53
54 /// Non-existent command code encountered.
55 InvalidCc {
56 /// Byte offset from the start of the buffer.
57 offset: usize,
58 /// Raw command code encountered.
59 value: u64,
60 },
61
62 /// A [`TpmsAttest`](crate::data::TpmsAttest) instance does not begin with
63 /// the [`TPM_GENERATED_VALUE`](crate::constant::TPM_GENERATED_VALUE) magic.
64 InvalidMagicNumber {
65 /// Byte offset from the start of the buffer.
66 offset: usize,
67 /// Raw magic value encountered.
68 value: u64,
69 },
70
71 /// Invalid TPM response code encountered.
72 InvalidRc {
73 /// Byte offset from the start of the buffer.
74 offset: usize,
75 /// Raw response code encountered.
76 value: u64,
77 },
78
79 /// Tag is neither [`Sessions`](crate::data::TpmSt::Sessions) nor
80 /// [`NoSessions`](crate::data::TpmSt::NoSessions).
81 InvalidTag {
82 /// Byte offset from the start of the buffer.
83 offset: usize,
84 /// Raw tag encountered.
85 value: u64,
86 },
87
88 /// Buffer contains more bytes than allowed by the TCG specifications.
89 TooManyBytes {
90 /// Byte offset from the start of the buffer.
91 offset: usize,
92 /// Maximum allowed byte count.
93 limit: usize,
94 /// Actual byte count.
95 actual: usize,
96 },
97
98 /// List contains more items than allowed by the TCG specifications.
99 TooManyItems {
100 /// Byte offset from the start of the buffer.
101 offset: usize,
102 /// Maximum allowed item count.
103 limit: usize,
104 /// Actual item count.
105 actual: usize,
106 },
107
108 /// Trailing data left after unmarshaling.
109 TrailingData {
110 /// Byte offset from the start of the buffer.
111 offset: usize,
112 /// Trailing byte or item count.
113 actual: usize,
114 },
115
116 /// Run out of bytes while unmarshaling.
117 UnexpectedEnd {
118 /// Byte offset from the start of the buffer.
119 offset: usize,
120 /// Required byte count.
121 needed: usize,
122 /// Available byte count.
123 available: usize,
124 },
125
126 /// The variant accessed is not available.
127 VariantNotAvailable {
128 /// Byte offset from the start of the buffer.
129 offset: usize,
130 /// Raw value encountered.
131 value: u64,
132 },
133}
134
135impl TpmError {
136 /// Returns the stable machine-readable name of the error variant.
137 #[must_use]
138 pub const fn kind(self) -> &'static str {
139 match self {
140 Self::BufferOverflow { .. } => "BufferOverflow",
141 Self::IntegerTooLarge { .. } => "IntegerTooLarge",
142 Self::InvalidBoolean { .. } => "InvalidBoolean",
143 Self::InvalidCc { .. } => "InvalidCc",
144 Self::InvalidMagicNumber { .. } => "InvalidMagicNumber",
145 Self::InvalidRc { .. } => "InvalidRc",
146 Self::InvalidTag { .. } => "InvalidTag",
147 Self::TooManyBytes { .. } => "TooManyBytes",
148 Self::TooManyItems { .. } => "TooManyItems",
149 Self::TrailingData { .. } => "TrailingData",
150 Self::UnexpectedEnd { .. } => "UnexpectedEnd",
151 Self::VariantNotAvailable { .. } => "VariantNotAvailable",
152 }
153 }
154}
155
156/// Renders [`TpmError`] as its bare variant name in lowercase, space-separated
157/// words (e.g. `BufferOverflow` becomes `buffer overflow`).
158///
159/// As the lowest-level crate in the stack, errors expose only the variant name
160/// here, derived from the stable [`kind`](Self::kind) discriminant. Callers read
161/// the structured fields directly and decide how to present the diagnostic
162/// detail.
163impl core::fmt::Display for TpmError {
164 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
165 use core::fmt::Write as _;
166
167 for (i, ch) in self.kind().char_indices() {
168 if ch.is_ascii_uppercase() {
169 if i != 0 {
170 f.write_char(' ')?;
171 }
172 f.write_char(ch.to_ascii_lowercase())?;
173 } else {
174 f.write_char(ch)?;
175 }
176 }
177 Ok(())
178 }
179}
180
181impl core::error::Error for TpmError {}
182
183pub type TpmResult<T> = Result<T, TpmError>;