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)]
26#[non_exhaustive]
27pub enum TpmError {
28 /// A write exceeded the capacity of the destination buffer.
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 byte offset associated with this error.
137 #[must_use]
138 pub const fn offset(&self) -> usize {
139 match *self {
140 Self::BufferOverflow { offset, .. }
141 | Self::IntegerTooLarge { offset, .. }
142 | Self::InvalidBoolean { offset, .. }
143 | Self::InvalidCc { offset, .. }
144 | Self::InvalidMagicNumber { offset, .. }
145 | Self::InvalidRc { offset, .. }
146 | Self::InvalidTag { offset, .. }
147 | Self::TooManyBytes { offset, .. }
148 | Self::TooManyItems { offset, .. }
149 | Self::TrailingData { offset, .. }
150 | Self::UnexpectedEnd { offset, .. }
151 | Self::VariantNotAvailable { offset, .. } => offset,
152 }
153 }
154
155 /// Shifts the byte offset by `delta` bytes.
156 #[must_use]
157 pub const fn rebase(self, delta: usize) -> Self {
158 match self {
159 Self::BufferOverflow {
160 offset,
161 needed,
162 available,
163 } => Self::BufferOverflow {
164 offset: offset.saturating_add(delta),
165 needed,
166 available,
167 },
168 Self::IntegerTooLarge { offset, value } => Self::IntegerTooLarge {
169 offset: offset.saturating_add(delta),
170 value,
171 },
172 Self::InvalidBoolean { offset, value } => Self::InvalidBoolean {
173 offset: offset.saturating_add(delta),
174 value,
175 },
176 Self::InvalidCc { offset, value } => Self::InvalidCc {
177 offset: offset.saturating_add(delta),
178 value,
179 },
180 Self::InvalidMagicNumber { offset, value } => Self::InvalidMagicNumber {
181 offset: offset.saturating_add(delta),
182 value,
183 },
184 Self::InvalidRc { offset, value } => Self::InvalidRc {
185 offset: offset.saturating_add(delta),
186 value,
187 },
188 Self::InvalidTag { offset, value } => Self::InvalidTag {
189 offset: offset.saturating_add(delta),
190 value,
191 },
192 Self::TooManyBytes {
193 offset,
194 limit,
195 actual,
196 } => Self::TooManyBytes {
197 offset: offset.saturating_add(delta),
198 limit,
199 actual,
200 },
201 Self::TooManyItems {
202 offset,
203 limit,
204 actual,
205 } => Self::TooManyItems {
206 offset: offset.saturating_add(delta),
207 limit,
208 actual,
209 },
210 Self::TrailingData { offset, actual } => Self::TrailingData {
211 offset: offset.saturating_add(delta),
212 actual,
213 },
214 Self::UnexpectedEnd {
215 offset,
216 needed,
217 available,
218 } => Self::UnexpectedEnd {
219 offset: offset.saturating_add(delta),
220 needed,
221 available,
222 },
223 Self::VariantNotAvailable { offset, value } => Self::VariantNotAvailable {
224 offset: offset.saturating_add(delta),
225 value,
226 },
227 }
228 }
229}
230
231/// Renders [`TpmError`] as its variant name in lowercase, space-separated words
232/// (e.g. [`BufferOverflow`](Self::BufferOverflow) renders as `buffer overflow`).
233///
234/// As the lowest-level crate in the stack, errors expose only the variant name
235/// here. Callers read the structured fields directly and decide how to present
236/// the diagnostic detail.
237impl core::fmt::Display for TpmError {
238 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
239 let name = match self {
240 Self::BufferOverflow { .. } => "buffer overflow",
241 Self::IntegerTooLarge { .. } => "integer too large",
242 Self::InvalidBoolean { .. } => "invalid boolean",
243 Self::InvalidCc { .. } => "invalid cc",
244 Self::InvalidMagicNumber { .. } => "invalid magic number",
245 Self::InvalidRc { .. } => "invalid rc",
246 Self::InvalidTag { .. } => "invalid tag",
247 Self::TooManyBytes { .. } => "too many bytes",
248 Self::TooManyItems { .. } => "too many items",
249 Self::TrailingData { .. } => "trailing data",
250 Self::UnexpectedEnd { .. } => "unexpected end",
251 Self::VariantNotAvailable { .. } => "variant not available",
252 };
253
254 f.write_str(name)
255 }
256}
257
258impl core::error::Error for TpmError {}
259
260pub type TpmResult<T> = Result<T, TpmError>;