Skip to main content

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>;