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
135/// Renders [`TpmError`] as its variant name in lowercase, space-separated words
136/// (e.g. [`BufferOverflow`](Self::BufferOverflow) renders as `buffer overflow`).
137///
138/// As the lowest-level crate in the stack, errors expose only the variant name
139/// here. Callers read the structured fields directly and decide how to present
140/// the diagnostic detail.
141impl core::fmt::Display for TpmError {
142 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
143 let name = match self {
144 Self::BufferOverflow { .. } => "buffer overflow",
145 Self::IntegerTooLarge { .. } => "integer too large",
146 Self::InvalidBoolean { .. } => "invalid boolean",
147 Self::InvalidCc { .. } => "invalid cc",
148 Self::InvalidMagicNumber { .. } => "invalid magic number",
149 Self::InvalidRc { .. } => "invalid rc",
150 Self::InvalidTag { .. } => "invalid tag",
151 Self::TooManyBytes { .. } => "too many bytes",
152 Self::TooManyItems { .. } => "too many items",
153 Self::TrailingData { .. } => "trailing data",
154 Self::UnexpectedEnd { .. } => "unexpected end",
155 Self::VariantNotAvailable { .. } => "variant not available",
156 };
157
158 f.write_str(name)
159 }
160}
161
162impl core::error::Error for TpmError {}
163
164pub type TpmResult<T> = Result<T, TpmError>;