1#![cfg_attr(not(test), no_std)]
22#![deny(unsafe_code)]
23#![deny(clippy::all)]
24#![deny(clippy::pedantic)]
25#![recursion_limit = "256"]
26
27pub mod buffer;
28pub mod constant;
29pub mod data;
30pub mod list;
31#[macro_use]
32pub mod r#macro;
33pub mod message;
34
35use crate::data::TpmAlgId;
36pub use buffer::TpmBuffer;
37use core::{
38 convert::{From, TryFrom},
39 fmt,
40 mem::size_of,
41 result::Result,
42};
43pub use list::TpmList;
44
45#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
47#[repr(transparent)]
48pub struct TpmHandle(pub u32);
49
50impl From<u32> for TpmHandle {
51 fn from(val: u32) -> Self {
52 Self(val)
53 }
54}
55
56impl From<TpmHandle> for u32 {
57 fn from(val: TpmHandle) -> Self {
58 val.0
59 }
60}
61
62impl TpmBuild for TpmHandle {
63 fn build(&self, writer: &mut TpmWriter) -> TpmResult<()> {
64 TpmBuild::build(&self.0, writer)
65 }
66}
67
68impl TpmParse for TpmHandle {
69 fn parse(buf: &[u8]) -> TpmResult<(Self, &[u8])> {
70 let (val, buf) = u32::parse(buf)?;
71 Ok((Self(val), buf))
72 }
73}
74
75impl TpmSized for TpmHandle {
76 const SIZE: usize = size_of::<u32>();
77 fn len(&self) -> usize {
78 Self::SIZE
79 }
80}
81
82impl fmt::Display for TpmHandle {
83 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
84 fmt::Display::fmt(&self.0, f)
85 }
86}
87
88impl fmt::LowerHex for TpmHandle {
89 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
90 fmt::LowerHex::fmt(&self.0, f)
91 }
92}
93
94impl fmt::UpperHex for TpmHandle {
95 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
96 fmt::UpperHex::fmt(&self.0, f)
97 }
98}
99
100#[derive(Debug, PartialEq, Eq)]
101pub enum TpmNotDiscriminant {
102 Signed(i64),
103 Unsigned(u64),
104}
105
106impl fmt::LowerHex for TpmNotDiscriminant {
107 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
108 match self {
109 TpmNotDiscriminant::Signed(v) => write!(f, "{v:x}"),
110 TpmNotDiscriminant::Unsigned(v) => write!(f, "{v:x}"),
111 }
112 }
113}
114
115#[derive(Debug, PartialEq, Eq)]
116pub enum TpmErrorKind {
117 Capacity(usize),
119 InvalidValue,
121 NotDiscriminant(&'static str, TpmNotDiscriminant),
123 TrailingData,
125 Underflow,
127 Failure,
129}
130
131impl fmt::Display for TpmErrorKind {
132 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
133 match self {
134 Self::Capacity(size) => write!(f, "exceeds capacity {size}"),
135 Self::InvalidValue => write!(f, "invalid value"),
136 Self::NotDiscriminant(type_name, value) => {
137 write!(f, "not discriminant for {type_name}: 0x{value:x}")
138 }
139 Self::TrailingData => write!(f, "trailing data"),
140 Self::Underflow => write!(f, "parse underflow"),
141 Self::Failure => write!(f, "unreachable"),
142 }
143 }
144}
145
146impl From<core::num::TryFromIntError> for TpmErrorKind {
147 fn from(_: core::num::TryFromIntError) -> Self {
148 Self::Capacity(usize::MAX)
149 }
150}
151
152pub type TpmResult<T> = Result<T, TpmErrorKind>;
153
154pub struct TpmWriter<'a> {
156 buffer: &'a mut [u8],
157 cursor: usize,
158}
159
160impl<'a> TpmWriter<'a> {
161 #[must_use]
163 pub fn new(buffer: &'a mut [u8]) -> Self {
164 Self { buffer, cursor: 0 }
165 }
166
167 #[must_use]
169 pub fn len(&self) -> usize {
170 self.cursor
171 }
172
173 #[must_use]
175 pub fn is_empty(&self) -> bool {
176 self.cursor == 0
177 }
178
179 pub fn write_bytes(&mut self, bytes: &[u8]) -> TpmResult<()> {
186 let end = self.cursor + bytes.len();
187 if end > self.buffer.len() {
188 return Err(TpmErrorKind::Failure);
189 }
190 self.buffer[self.cursor..end].copy_from_slice(bytes);
191 self.cursor = end;
192 Ok(())
193 }
194}
195
196pub trait TpmSized {
199 const SIZE: usize;
202
203 fn len(&self) -> usize;
205
206 fn is_empty(&self) -> bool {
208 self.len() == 0
209 }
210}
211
212pub trait TpmBuild: TpmSized {
213 fn build(&self, writer: &mut TpmWriter) -> TpmResult<()>;
219}
220
221pub trait TpmParse: Sized + TpmSized {
222 fn parse(buf: &[u8]) -> TpmResult<(Self, &[u8])>;
230}
231
232pub trait TpmTagged {
234 type Tag: TpmParse + TpmBuild + Copy;
236 type Value;
238}
239
240pub trait TpmParseTagged: Sized {
242 fn parse_tagged(tag: <Self as TpmTagged>::Tag, buf: &[u8]) -> TpmResult<(Self, &[u8])>
250 where
251 Self: TpmTagged,
252 <Self as TpmTagged>::Tag: TpmParse + TpmBuild;
253}
254
255tpm_integer!(u8, Unsigned);
256tpm_integer!(i8, Signed);
257tpm_integer!(i32, Signed);
258tpm_integer!(u16, Unsigned);
259tpm_integer!(u32, Unsigned);
260tpm_integer!(u64, Unsigned);
261
262pub fn build_tpm2b(writer: &mut TpmWriter, data: &[u8]) -> TpmResult<()> {
268 let len_u16 = u16::try_from(data.len()).map_err(|_| TpmErrorKind::Capacity(u16::MAX.into()))?;
269 TpmBuild::build(&len_u16, writer)?;
270 writer.write_bytes(data)
271}
272
273pub fn parse_tpm2b(buf: &[u8]) -> TpmResult<(&[u8], &[u8])> {
279 let (size, buf) = u16::parse(buf)?;
280 let size = size as usize;
281
282 if buf.len() < size {
283 return Err(TpmErrorKind::Underflow);
284 }
285 Ok(buf.split_at(size))
286}
287
288#[must_use]
290pub const fn tpm_hash_size(alg_id: &TpmAlgId) -> Option<usize> {
291 match alg_id {
292 TpmAlgId::Sha1 => Some(20),
293 TpmAlgId::Sha256 | TpmAlgId::Sm3_256 => Some(32),
294 TpmAlgId::Sha384 => Some(48),
295 TpmAlgId::Sha512 => Some(64),
296 _ => None,
297 }
298}