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/// Additional structured data for a TPM protocol error.
6#[derive(Debug, Default, PartialEq, Eq, Copy, Clone)]
7pub struct TpmErrorValue {
8    /// Byte offset from the start of the parsed buffer.
9    pub offset: usize,
10
11    /// Raw value associated with the error.
12    pub value: u64,
13
14    /// Required byte or item count.
15    pub needed: usize,
16
17    /// Available byte or item count.
18    pub available: usize,
19
20    /// Maximum allowed byte or item count.
21    pub limit: usize,
22
23    /// Actual byte or item count.
24    pub actual: usize,
25}
26
27impl TpmErrorValue {
28    /// Creates empty error data at a byte offset.
29    #[must_use]
30    pub const fn new(offset: usize) -> Self {
31        Self {
32            offset,
33            value: 0,
34            needed: 0,
35            available: 0,
36            limit: 0,
37            actual: 0,
38        }
39    }
40
41    /// Sets the raw value associated with the error.
42    #[must_use]
43    pub const fn value(mut self, value: u64) -> Self {
44        self.value = value;
45        self
46    }
47
48    /// Sets a raw `usize` value associated with the error.
49    #[allow(clippy::cast_possible_truncation)]
50    #[must_use]
51    pub const fn value_usize(mut self, value: usize) -> Self {
52        self.value = value as u64;
53        self
54    }
55
56    /// Sets an actual count without a corresponding limit.
57    #[must_use]
58    pub const fn actual(mut self, actual: usize) -> Self {
59        self.actual = actual;
60        self
61    }
62
63    /// Sets the required and available counts.
64    #[must_use]
65    pub const fn size(mut self, needed: usize, available: usize) -> Self {
66        self.needed = needed;
67        self.available = available;
68        self
69    }
70
71    /// Sets the maximum allowed and actual counts.
72    #[must_use]
73    pub const fn limit(mut self, limit: usize, actual: usize) -> Self {
74        self.limit = limit;
75        self.actual = actual;
76        self
77    }
78
79    /// Creates error data from a cursor slice inside a base slice.
80    #[must_use]
81    pub fn at(base: &[u8], cursor: &[u8]) -> Self {
82        Self::new(Self::offset(base, cursor))
83    }
84
85    /// Returns the byte offset of a cursor slice inside a base slice.
86    #[must_use]
87    pub fn offset(base: &[u8], cursor: &[u8]) -> usize {
88        let base_addr = base.as_ptr() as usize;
89        let cursor_addr = cursor.as_ptr() as usize;
90
91        cursor_addr.saturating_sub(base_addr).min(base.len())
92    }
93}
94
95/// TPM frame marshaling and unmarshaling error type.
96///
97/// Every variant carries [`TpmErrorValue`] with the byte offset and any
98/// applicable raw value, size, or limit information.
99#[derive(Debug, PartialEq, Eq, Copy, Clone)]
100pub enum TpmError {
101    /// Trying to marshal more bytes than buffer has space. This is unexpected
102    /// situation, and should be considered possible bug in the crate itself.
103    BufferOverflow(TpmErrorValue),
104
105    /// Integer overflow while converting to an integer of a different size.
106    IntegerTooLarge(TpmErrorValue),
107
108    /// Boolean value was expected but the value is neither `0` nor `1`.
109    InvalidBoolean(TpmErrorValue),
110
111    /// Non-existent command code encountered.
112    InvalidCc(TpmErrorValue),
113
114    /// An [`TpmAttest`](crate::data::TpmAttest) instance contains an invalid
115    /// magic value.
116    InvalidMagicNumber(TpmErrorValue),
117
118    /// Invalid TPM response code encountered.
119    InvalidRc(TpmErrorValue),
120
121    /// Tag is neither [`Sessions`](crate::data::TpmSt::Sessions) nor
122    /// [`NoSessions`](crate::data::TpmSt::NoSessions).
123    InvalidTag(TpmErrorValue),
124
125    /// Buffer contains more bytes than allowed by the TCG specifications.
126    TooManyBytes(TpmErrorValue),
127
128    /// List contains more items than allowed by the TCG specifications.
129    TooManyItems(TpmErrorValue),
130
131    /// Trailing data left after unmarshaling.
132    TrailingData(TpmErrorValue),
133
134    /// Run out of bytes while unmarshaling.
135    UnexpectedEnd(TpmErrorValue),
136
137    /// The variant accessed is not available.
138    VariantNotAvailable(TpmErrorValue),
139}
140
141impl TpmError {
142    /// Returns the structured value carried by the error.
143    #[must_use]
144    pub const fn value(self) -> TpmErrorValue {
145        match self {
146            Self::BufferOverflow(value)
147            | Self::IntegerTooLarge(value)
148            | Self::InvalidBoolean(value)
149            | Self::InvalidCc(value)
150            | Self::InvalidMagicNumber(value)
151            | Self::InvalidRc(value)
152            | Self::InvalidTag(value)
153            | Self::TooManyBytes(value)
154            | Self::TooManyItems(value)
155            | Self::TrailingData(value)
156            | Self::UnexpectedEnd(value)
157            | Self::VariantNotAvailable(value) => value,
158        }
159    }
160}
161
162impl core::fmt::Display for TpmError {
163    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
164        let (message, value) = match *self {
165            Self::BufferOverflow(value) => ("buffer overflow", value),
166            Self::InvalidBoolean(value) => ("invalid boolean value", value),
167            Self::InvalidCc(value) => ("invalid command code", value),
168            Self::InvalidMagicNumber(value) => ("invalid magic number", value),
169            Self::InvalidRc(value) => ("invalid response code", value),
170            Self::InvalidTag(value) => ("invalid tag", value),
171            Self::IntegerTooLarge(value) => ("integer overflow", value),
172            Self::TooManyBytes(value) => ("buffer capacity surpassed", value),
173            Self::TooManyItems(value) => ("list capacity surpassed", value),
174            Self::TrailingData(value) => ("trailing data", value),
175            Self::UnexpectedEnd(value) => ("unexpected end", value),
176            Self::VariantNotAvailable(value) => ("enum variant is not available", value),
177        };
178
179        write!(f, "{message} at offset {}", value.offset)?;
180        if value.value != 0 {
181            write!(f, ", value=0x{:x}", value.value)?;
182        }
183        if value.needed != 0 || value.available != 0 {
184            write!(
185                f,
186                ", needed={}, available={}",
187                value.needed, value.available
188            )?;
189        }
190        if value.limit != 0 || value.actual != 0 {
191            write!(f, ", limit={}, actual={}", value.limit, value.actual)?;
192        }
193
194        Ok(())
195    }
196}
197
198impl core::error::Error for TpmError {}
199
200pub type TpmResult<T> = Result<T, TpmError>;