1#![cfg_attr(not(test), no_std)]
22#![deny(unsafe_code)]
23#![deny(clippy::all)]
24#![deny(clippy::pedantic)]
25
26#[macro_use]
27pub mod r#macro;
28pub mod buffer;
29pub mod data;
30pub mod list;
31pub mod message;
32
33use crate::data::TpmAlgId;
34pub use buffer::TpmBuffer;
35use core::{
36 convert::{From, TryFrom},
37 fmt,
38 mem::size_of,
39 result::Result,
40};
41pub use list::TpmList;
42
43tpm_handle! {
44 #[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
45 TpmTransient
46}
47tpm_handle! {
48 #[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
49 TpmSession
50}
51tpm_handle! {
52 #[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
53 TpmPersistent
54}
55
56pub const TPM_MAX_COMMAND_SIZE: usize = 4096;
58
59#[derive(Debug, PartialEq, Eq)]
60pub enum TpmNotDiscriminant {
61 Signed(i64),
62 Unsigned(u64),
63}
64
65impl fmt::LowerHex for TpmNotDiscriminant {
66 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
67 match self {
68 TpmNotDiscriminant::Signed(v) => write!(f, "{v:x}"),
69 TpmNotDiscriminant::Unsigned(v) => write!(f, "{v:x}"),
70 }
71 }
72}
73
74#[derive(Debug, PartialEq, Eq)]
75pub enum TpmErrorKind {
76 AuthMissing,
78 Boundary,
80 CapacityExceeded,
82 Unreachable,
84 InvalidMagic { expected: u32, got: u32 },
86 InvalidTag {
88 type_name: &'static str,
89 expected: u16,
90 got: u16,
91 },
92 InvalidValue,
94 NotDiscriminant(&'static str, TpmNotDiscriminant),
96 TrailingData,
98 ValueTooLarge,
100}
101
102impl fmt::Display for TpmErrorKind {
103 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
104 match self {
105 Self::Boundary => write!(f, "Insufficient data in buffer"),
106 Self::TrailingData => write!(f, "Buffer has unexpected trailing data after parsing"),
107 Self::NotDiscriminant(type_name, value) => {
108 write!(f, "Invalid discriminant 0x{value:x} for type '{type_name}'")
109 }
110 Self::InvalidMagic { expected, got } => {
111 write!(
112 f,
113 "Invalid magic number: expected 0x{expected:x}, got 0x{got:x}"
114 )
115 }
116 Self::InvalidTag {
117 type_name,
118 expected,
119 got,
120 } => {
121 write!(
122 f,
123 "Invalid tag for {type_name}: expected 0x{expected:x}, got 0x{got:x}"
124 )
125 }
126 Self::InvalidValue => write!(f, "A value is invalid or out of the expected range"),
127 Self::ValueTooLarge => {
128 write!(
129 f,
130 "A size or count is larger than the maximum allowed value"
131 )
132 }
133 Self::CapacityExceeded => write!(f, "An operation would exceed a container's capacity"),
134 Self::AuthMissing => write!(f, "Command requires authorization but none was provided"),
135 Self::Unreachable => write!(f, "An unexpected internal error occurred"),
136 }
137 }
138}
139
140impl From<core::num::TryFromIntError> for TpmErrorKind {
141 fn from(_: core::num::TryFromIntError) -> Self {
142 Self::Unreachable
143 }
144}
145
146pub type TpmResult<T> = Result<T, TpmErrorKind>;
147
148pub struct TpmWriter<'a> {
150 buffer: &'a mut [u8],
151 cursor: usize,
152}
153
154impl<'a> TpmWriter<'a> {
155 #[must_use]
157 pub fn new(buffer: &'a mut [u8]) -> Self {
158 Self { buffer, cursor: 0 }
159 }
160
161 #[must_use]
163 pub fn len(&self) -> usize {
164 self.cursor
165 }
166
167 #[must_use]
169 pub fn is_empty(&self) -> bool {
170 self.cursor == 0
171 }
172
173 pub fn write_bytes(&mut self, bytes: &[u8]) -> TpmResult<()> {
180 let end = self.cursor + bytes.len();
181 if end > self.buffer.len() {
182 return Err(TpmErrorKind::Boundary);
183 }
184 self.buffer[self.cursor..end].copy_from_slice(bytes);
185 self.cursor = end;
186 Ok(())
187 }
188}
189
190pub trait TpmSized {
193 const SIZE: usize;
196
197 fn len(&self) -> usize;
199
200 fn is_empty(&self) -> bool {
202 self.len() == 0
203 }
204}
205
206pub trait TpmBuild: TpmSized {
207 fn build(&self, writer: &mut TpmWriter) -> TpmResult<()>;
214}
215
216pub trait TpmParse: Sized + TpmSized {
217 fn parse(buf: &[u8]) -> TpmResult<(Self, &[u8])>;
226}
227
228pub trait TpmTagged {
230 type Tag: TpmParse + TpmBuild + Copy;
232 type Value;
234}
235
236pub trait TpmParseTagged: Sized {
238 fn parse_tagged(tag: <Self as TpmTagged>::Tag, buf: &[u8]) -> TpmResult<(Self, &[u8])>
246 where
247 Self: TpmTagged,
248 <Self as TpmTagged>::Tag: TpmParse + TpmBuild;
249}
250
251impl TpmSized for u8 {
252 const SIZE: usize = 1;
253 fn len(&self) -> usize {
254 1
255 }
256}
257
258impl TpmBuild for u8 {
259 fn build(&self, writer: &mut TpmWriter) -> TpmResult<()> {
260 writer.write_bytes(&[*self])
261 }
262}
263
264impl TpmParse for u8 {
265 fn parse(buf: &[u8]) -> TpmResult<(Self, &[u8])> {
266 let (val, buf) = buf.split_first().ok_or(TpmErrorKind::Boundary)?;
267 Ok((*val, buf))
268 }
269}
270
271impl From<u8> for TpmNotDiscriminant {
272 fn from(value: u8) -> Self {
273 Self::Unsigned(value.into())
274 }
275}
276
277tpm_integer!(i8, Signed);
278tpm_integer!(i32, Signed);
279tpm_integer!(u16, Unsigned);
280tpm_integer!(u32, Unsigned);
281tpm_integer!(u64, Unsigned);
282
283pub fn build_tpm2b(writer: &mut TpmWriter, data: &[u8]) -> TpmResult<()> {
289 let len_u16 = u16::try_from(data.len()).map_err(|_| TpmErrorKind::ValueTooLarge)?;
290 TpmBuild::build(&len_u16, writer)?;
291 writer.write_bytes(data)
292}
293
294pub fn parse_tpm2b(buf: &[u8]) -> TpmResult<(&[u8], &[u8])> {
301 let (size, buf) = u16::parse(buf)?;
302 let size = size as usize;
303
304 if size > TPM_MAX_COMMAND_SIZE {
305 return Err(TpmErrorKind::ValueTooLarge);
306 }
307
308 if buf.len() < size {
309 return Err(TpmErrorKind::Boundary);
310 }
311 Ok(buf.split_at(size))
312}
313
314#[must_use]
316pub const fn tpm_hash_size(alg_id: &TpmAlgId) -> Option<usize> {
317 match alg_id {
318 TpmAlgId::Sha1 => Some(20),
319 TpmAlgId::Sha256 | TpmAlgId::Sm3_256 => Some(32),
320 TpmAlgId::Sha384 => Some(48),
321 TpmAlgId::Sha512 => Some(64),
322 _ => None,
323 }
324}