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;
32pub mod parameters;
33
34use crate::data::TpmAlgId;
35use core::{convert::TryFrom, fmt, mem::size_of, result::Result};
36
37pub use buffer::TpmBuffer;
38pub use list::TpmList;
39pub use parameters::TpmParameters;
40
41tpm_handle! {
42 #[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
43 TpmTransient
44}
45tpm_handle! {
46 #[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
47 TpmSession
48}
49tpm_handle! {
50 #[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
51 TpmPersistent
52}
53
54pub const TPM_MAX_COMMAND_SIZE: usize = 4096;
56
57#[derive(Debug, PartialEq, Eq)]
58pub enum TpmErrorKind {
59 Boundary,
61 TrailingData,
63 InvalidDiscriminant { type_name: &'static str, value: u64 },
65 InvalidMagic { expected: u32, got: u32 },
67 InvalidTag {
69 type_name: &'static str,
70 expected: u16,
71 got: u16,
72 },
73 InvalidValue,
75 ValueTooLarge,
77 CapacityExceeded,
79 AuthMissing,
81 InternalError,
83}
84
85impl fmt::Display for TpmErrorKind {
86 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
87 match self {
88 Self::Boundary => write!(f, "Insufficient data in buffer"),
89 Self::TrailingData => write!(f, "Buffer has unexpected trailing data after parsing"),
90 Self::InvalidDiscriminant { type_name, value } => {
91 write!(f, "Invalid discriminant 0x{value:x} for type '{type_name}'")
92 }
93 Self::InvalidMagic { expected, got } => {
94 write!(
95 f,
96 "Invalid magic number: expected 0x{expected:x}, got 0x{got:x}"
97 )
98 }
99 Self::InvalidTag {
100 type_name,
101 expected,
102 got,
103 } => {
104 write!(
105 f,
106 "Invalid tag for {type_name}: expected 0x{expected:x}, got 0x{got:x}"
107 )
108 }
109 Self::InvalidValue => write!(f, "A value is invalid or out of the expected range"),
110 Self::ValueTooLarge => {
111 write!(
112 f,
113 "A size or count is larger than the maximum allowed value"
114 )
115 }
116 Self::CapacityExceeded => write!(f, "An operation would exceed a container's capacity"),
117 Self::AuthMissing => write!(f, "Command requires authorization but none was provided"),
118 Self::InternalError => write!(f, "An unexpected internal error occurred"),
119 }
120 }
121}
122
123impl From<core::num::TryFromIntError> for TpmErrorKind {
124 fn from(_: core::num::TryFromIntError) -> Self {
125 Self::InternalError
126 }
127}
128
129pub type TpmResult<T> = Result<T, TpmErrorKind>;
130
131pub struct TpmWriter<'a> {
133 buffer: &'a mut [u8],
134 cursor: usize,
135}
136
137impl<'a> TpmWriter<'a> {
138 #[must_use]
140 pub fn new(buffer: &'a mut [u8]) -> Self {
141 Self { buffer, cursor: 0 }
142 }
143
144 #[must_use]
146 pub fn len(&self) -> usize {
147 self.cursor
148 }
149
150 #[must_use]
152 pub fn is_empty(&self) -> bool {
153 self.cursor == 0
154 }
155
156 pub fn write_bytes(&mut self, bytes: &[u8]) -> TpmResult<()> {
163 let end = self.cursor + bytes.len();
164 if end > self.buffer.len() {
165 return Err(TpmErrorKind::Boundary);
166 }
167 self.buffer[self.cursor..end].copy_from_slice(bytes);
168 self.cursor = end;
169 Ok(())
170 }
171}
172
173pub trait TpmSized {
176 const SIZE: usize;
179
180 fn len(&self) -> usize;
182
183 fn is_empty(&self) -> bool {
185 self.len() == 0
186 }
187}
188
189pub trait TpmBuild: TpmSized {
190 fn build(&self, writer: &mut TpmWriter) -> TpmResult<()>;
197}
198
199pub trait TpmParse: Sized + TpmSized {
200 fn parse(buf: &[u8]) -> TpmResult<(Self, &[u8])>;
209}
210
211pub trait TpmTagged {
213 type Tag: TpmParse + TpmBuild + Copy;
215 type Value;
217}
218
219pub trait TpmParseTagged: Sized {
221 fn parse_tagged(tag: <Self as TpmTagged>::Tag, buf: &[u8]) -> TpmResult<(Self, &[u8])>
229 where
230 Self: TpmTagged,
231 <Self as TpmTagged>::Tag: TpmParse + TpmBuild;
232}
233
234impl TpmSized for u8 {
235 const SIZE: usize = 1;
236 fn len(&self) -> usize {
237 1
238 }
239}
240
241impl TpmBuild for u8 {
242 fn build(&self, writer: &mut TpmWriter) -> TpmResult<()> {
243 writer.write_bytes(&[*self])
244 }
245}
246
247impl TpmParse for u8 {
248 fn parse(buf: &[u8]) -> TpmResult<(Self, &[u8])> {
249 let (val, buf) = buf.split_first().ok_or(TpmErrorKind::Boundary)?;
250 Ok((*val, buf))
251 }
252}
253
254macro_rules! tpm_integer {
255 ($ty:ty) => {
256 impl TpmParse for $ty {
257 fn parse(buf: &[u8]) -> TpmResult<(Self, &[u8])> {
258 let size = size_of::<$ty>();
259 if buf.len() < size {
260 return Err(TpmErrorKind::Boundary);
261 }
262 let (bytes, buf) = buf.split_at(size);
263 let array = bytes.try_into().map_err(|_| TpmErrorKind::InternalError)?;
264 let val = <$ty>::from_be_bytes(array);
265 Ok((val, buf))
266 }
267 }
268
269 impl TpmBuild for $ty {
270 fn build(&self, writer: &mut TpmWriter) -> TpmResult<()> {
271 writer.write_bytes(&self.to_be_bytes())
272 }
273 }
274
275 impl TpmSized for $ty {
276 const SIZE: usize = size_of::<$ty>();
277 fn len(&self) -> usize {
278 Self::SIZE
279 }
280 }
281 };
282}
283
284tpm_integer!(i32);
285tpm_integer!(u16);
286tpm_integer!(u32);
287tpm_integer!(u64);
288
289pub fn build_tpm2b(writer: &mut TpmWriter, data: &[u8]) -> TpmResult<()> {
295 let len_u16 = u16::try_from(data.len()).map_err(|_| TpmErrorKind::ValueTooLarge)?;
296 TpmBuild::build(&len_u16, writer)?;
297 writer.write_bytes(data)
298}
299
300pub fn parse_tpm2b(buf: &[u8]) -> TpmResult<(&[u8], &[u8])> {
307 let (size, buf) = u16::parse(buf)?;
308 let size = size as usize;
309
310 if size > TPM_MAX_COMMAND_SIZE {
311 return Err(TpmErrorKind::ValueTooLarge);
312 }
313
314 if buf.len() < size {
315 return Err(TpmErrorKind::Boundary);
316 }
317 Ok(buf.split_at(size))
318}
319
320#[must_use]
322pub const fn tpm_hash_size(alg_id: &TpmAlgId) -> Option<usize> {
323 match alg_id {
324 TpmAlgId::Sha1 => Some(20),
325 TpmAlgId::Sha256 | TpmAlgId::Sm3_256 => Some(32),
326 TpmAlgId::Sha384 => Some(48),
327 TpmAlgId::Sha512 => Some(64),
328 _ => None,
329 }
330}