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 BuildCapacity,
80 BuildOverflow,
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 ParseCapacity,
98 ParseUnderflow,
100 TrailingData,
102}
103
104impl fmt::Display for TpmErrorKind {
105 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
106 match self {
107 Self::AuthMissing => write!(f, "auth value missing"),
108 Self::BuildCapacity => write!(f, "build capacity limit exceeded"),
109 Self::BuildOverflow => write!(f, "build buffer overflow"),
110 Self::InvalidMagic { expected, got } => {
111 write!(f, "invalid magic: expected 0x{expected:x}, got 0x{got:x}")
112 }
113 Self::InvalidTag {
114 type_name,
115 expected,
116 got,
117 } => {
118 write!(
119 f,
120 "invalid tag for {type_name}: expected 0x{expected:x}, got 0x{got:x}"
121 )
122 }
123 Self::InvalidValue => write!(f, "invalid value"),
124 Self::NotDiscriminant(type_name, value) => {
125 write!(f, "unknown discriminant for '{type_name}': 0x{value:x} ")
126 }
127 Self::ParseCapacity => {
128 write!(f, "parse capacity limit exceeded")
129 }
130 Self::ParseUnderflow => write!(f, "parse buffer underflow"),
131 Self::TrailingData => write!(f, "trailing data"),
132 Self::Unreachable => write!(f, "unreachable code path"),
133 }
134 }
135}
136
137impl From<core::num::TryFromIntError> for TpmErrorKind {
138 fn from(_: core::num::TryFromIntError) -> Self {
139 Self::Unreachable
140 }
141}
142
143pub type TpmResult<T> = Result<T, TpmErrorKind>;
144
145pub struct TpmWriter<'a> {
147 buffer: &'a mut [u8],
148 cursor: usize,
149}
150
151impl<'a> TpmWriter<'a> {
152 #[must_use]
154 pub fn new(buffer: &'a mut [u8]) -> Self {
155 Self { buffer, cursor: 0 }
156 }
157
158 #[must_use]
160 pub fn len(&self) -> usize {
161 self.cursor
162 }
163
164 #[must_use]
166 pub fn is_empty(&self) -> bool {
167 self.cursor == 0
168 }
169
170 pub fn write_bytes(&mut self, bytes: &[u8]) -> TpmResult<()> {
177 let end = self.cursor + bytes.len();
178 if end > self.buffer.len() {
179 return Err(TpmErrorKind::BuildOverflow);
180 }
181 self.buffer[self.cursor..end].copy_from_slice(bytes);
182 self.cursor = end;
183 Ok(())
184 }
185}
186
187pub trait TpmSized {
190 const SIZE: usize;
193
194 fn len(&self) -> usize;
196
197 fn is_empty(&self) -> bool {
199 self.len() == 0
200 }
201}
202
203pub trait TpmBuild: TpmSized {
204 fn build(&self, writer: &mut TpmWriter) -> TpmResult<()>;
211}
212
213pub trait TpmParse: Sized + TpmSized {
214 fn parse(buf: &[u8]) -> TpmResult<(Self, &[u8])>;
223}
224
225pub trait TpmTagged {
227 type Tag: TpmParse + TpmBuild + Copy;
229 type Value;
231}
232
233pub trait TpmParseTagged: Sized {
235 fn parse_tagged(tag: <Self as TpmTagged>::Tag, buf: &[u8]) -> TpmResult<(Self, &[u8])>
243 where
244 Self: TpmTagged,
245 <Self as TpmTagged>::Tag: TpmParse + TpmBuild;
246}
247
248impl TpmSized for u8 {
249 const SIZE: usize = 1;
250 fn len(&self) -> usize {
251 1
252 }
253}
254
255impl TpmBuild for u8 {
256 fn build(&self, writer: &mut TpmWriter) -> TpmResult<()> {
257 writer.write_bytes(&[*self])
258 }
259}
260
261impl TpmParse for u8 {
262 fn parse(buf: &[u8]) -> TpmResult<(Self, &[u8])> {
263 let (val, buf) = buf.split_first().ok_or(TpmErrorKind::ParseUnderflow)?;
264 Ok((*val, buf))
265 }
266}
267
268impl From<u8> for TpmNotDiscriminant {
269 fn from(value: u8) -> Self {
270 Self::Unsigned(value.into())
271 }
272}
273
274tpm_integer!(i8, Signed);
275tpm_integer!(i32, Signed);
276tpm_integer!(u16, Unsigned);
277tpm_integer!(u32, Unsigned);
278tpm_integer!(u64, Unsigned);
279
280pub fn build_tpm2b(writer: &mut TpmWriter, data: &[u8]) -> TpmResult<()> {
286 let len_u16 = u16::try_from(data.len()).map_err(|_| TpmErrorKind::BuildCapacity)?;
287 TpmBuild::build(&len_u16, writer)?;
288 writer.write_bytes(data)
289}
290
291pub fn parse_tpm2b(buf: &[u8]) -> TpmResult<(&[u8], &[u8])> {
298 let (size, buf) = u16::parse(buf)?;
299 let size = size as usize;
300
301 if size > TPM_MAX_COMMAND_SIZE {
302 return Err(TpmErrorKind::ParseCapacity);
303 }
304
305 if buf.len() < size {
306 return Err(TpmErrorKind::ParseUnderflow);
307 }
308 Ok(buf.split_at(size))
309}
310
311#[must_use]
313pub const fn tpm_hash_size(alg_id: &TpmAlgId) -> Option<usize> {
314 match alg_id {
315 TpmAlgId::Sha1 => Some(20),
316 TpmAlgId::Sha256 | TpmAlgId::Sm3_256 => Some(32),
317 TpmAlgId::Sha384 => Some(48),
318 TpmAlgId::Sha512 => Some(64),
319 _ => None,
320 }
321}