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