1#![cfg_attr(not(test), no_std)]
22#![deny(unsafe_code)]
23#![deny(clippy::all)]
24#![deny(clippy::pedantic)]
25
26pub mod buffer;
27pub mod constant;
28pub mod data;
29pub mod list;
30#[macro_use]
31pub mod r#macro;
32pub mod message;
33
34use crate::data::TpmAlgId;
35pub use buffer::TpmBuffer;
36use core::{
37 convert::{From, TryFrom},
38 fmt,
39 mem::size_of,
40 result::Result,
41};
42pub use list::TpmList;
43
44tpm_handle! {
45 #[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
46 TpmTransient
47}
48tpm_handle! {
49 #[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
50 TpmSession
51}
52tpm_handle! {
53 #[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
54 TpmPersistent
55}
56
57#[derive(Debug, PartialEq, Eq)]
58pub enum TpmNotDiscriminant {
59 Signed(i64),
60 Unsigned(u64),
61}
62
63impl fmt::LowerHex for TpmNotDiscriminant {
64 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
65 match self {
66 TpmNotDiscriminant::Signed(v) => write!(f, "{v:x}"),
67 TpmNotDiscriminant::Unsigned(v) => write!(f, "{v:x}"),
68 }
69 }
70}
71
72#[derive(Debug, PartialEq, Eq)]
73pub enum TpmErrorKind {
74 Capacity(usize),
76 InvalidValue,
78 NotDiscriminant(&'static str, TpmNotDiscriminant),
80 TrailingData,
82 Underflow,
84 Failure,
86}
87
88impl fmt::Display for TpmErrorKind {
89 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
90 match self {
91 Self::Capacity(size) => write!(f, "exceeds capacity {size}"),
92 Self::InvalidValue => write!(f, "invalid value"),
93 Self::NotDiscriminant(type_name, value) => {
94 write!(f, "not discriminant for {type_name}: 0x{value:x}")
95 }
96 Self::TrailingData => write!(f, "trailing data"),
97 Self::Underflow => write!(f, "parse underflow"),
98 Self::Failure => write!(f, "unreachable"),
99 }
100 }
101}
102
103impl From<core::num::TryFromIntError> for TpmErrorKind {
104 fn from(_: core::num::TryFromIntError) -> Self {
105 Self::Failure
106 }
107}
108
109pub type TpmResult<T> = Result<T, TpmErrorKind>;
110
111pub struct TpmWriter<'a> {
113 buffer: &'a mut [u8],
114 cursor: usize,
115}
116
117impl<'a> TpmWriter<'a> {
118 #[must_use]
120 pub fn new(buffer: &'a mut [u8]) -> Self {
121 Self { buffer, cursor: 0 }
122 }
123
124 #[must_use]
126 pub fn len(&self) -> usize {
127 self.cursor
128 }
129
130 #[must_use]
132 pub fn is_empty(&self) -> bool {
133 self.cursor == 0
134 }
135
136 pub fn write_bytes(&mut self, bytes: &[u8]) -> TpmResult<()> {
143 let end = self.cursor + bytes.len();
144 if end > self.buffer.len() {
145 return Err(TpmErrorKind::Failure);
146 }
147 self.buffer[self.cursor..end].copy_from_slice(bytes);
148 self.cursor = end;
149 Ok(())
150 }
151}
152
153pub trait TpmSized {
156 const SIZE: usize;
159
160 fn len(&self) -> usize;
162
163 fn is_empty(&self) -> bool {
165 self.len() == 0
166 }
167}
168
169pub trait TpmBuild: TpmSized {
170 fn build(&self, writer: &mut TpmWriter) -> TpmResult<()>;
176}
177
178pub trait TpmParse: Sized + TpmSized {
179 fn parse(buf: &[u8]) -> TpmResult<(Self, &[u8])>;
187}
188
189pub trait TpmTagged {
191 type Tag: TpmParse + TpmBuild + Copy;
193 type Value;
195}
196
197pub trait TpmParseTagged: Sized {
199 fn parse_tagged(tag: <Self as TpmTagged>::Tag, buf: &[u8]) -> TpmResult<(Self, &[u8])>
207 where
208 Self: TpmTagged,
209 <Self as TpmTagged>::Tag: TpmParse + TpmBuild;
210}
211
212tpm_integer!(u8, Unsigned);
213tpm_integer!(i8, Signed);
214tpm_integer!(i32, Signed);
215tpm_integer!(u16, Unsigned);
216tpm_integer!(u32, Unsigned);
217tpm_integer!(u64, Unsigned);
218
219pub fn build_tpm2b(writer: &mut TpmWriter, data: &[u8]) -> TpmResult<()> {
225 let len_u16 = u16::try_from(data.len()).map_err(|_| TpmErrorKind::Capacity(u16::MAX.into()))?;
226 TpmBuild::build(&len_u16, writer)?;
227 writer.write_bytes(data)
228}
229
230pub fn parse_tpm2b(buf: &[u8]) -> TpmResult<(&[u8], &[u8])> {
237 let (size, buf) = u16::parse(buf)?;
238 let size = size as usize;
239
240 if size > crate::constant::TPM_MAX_COMMAND_SIZE {
241 return Err(TpmErrorKind::Capacity(
242 crate::constant::TPM_MAX_COMMAND_SIZE,
243 ));
244 }
245
246 if buf.len() < size {
247 return Err(TpmErrorKind::Underflow);
248 }
249 Ok(buf.split_at(size))
250}
251
252#[must_use]
254pub const fn tpm_hash_size(alg_id: &TpmAlgId) -> Option<usize> {
255 match alg_id {
256 TpmAlgId::Sha1 => Some(20),
257 TpmAlgId::Sha256 | TpmAlgId::Sm3_256 => Some(32),
258 TpmAlgId::Sha384 => Some(48),
259 TpmAlgId::Sha512 => Some(64),
260 _ => None,
261 }
262}