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
45tpm_handle! {
46 #[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
47 TpmTransient
48}
49tpm_handle! {
50 #[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
51 TpmSession
52}
53tpm_handle! {
54 #[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
55 TpmPersistent
56}
57
58#[derive(Debug, PartialEq, Eq)]
59pub enum TpmNotDiscriminant {
60 Signed(i64),
61 Unsigned(u64),
62}
63
64impl fmt::LowerHex for TpmNotDiscriminant {
65 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
66 match self {
67 TpmNotDiscriminant::Signed(v) => write!(f, "{v:x}"),
68 TpmNotDiscriminant::Unsigned(v) => write!(f, "{v:x}"),
69 }
70 }
71}
72
73#[derive(Debug, PartialEq, Eq)]
74pub enum TpmErrorKind {
75 Capacity(usize),
77 InvalidValue,
79 NotDiscriminant(&'static str, TpmNotDiscriminant),
81 TrailingData,
83 Underflow,
85 Failure,
87}
88
89impl fmt::Display for TpmErrorKind {
90 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
91 match self {
92 Self::Capacity(size) => write!(f, "exceeds capacity {size}"),
93 Self::InvalidValue => write!(f, "invalid value"),
94 Self::NotDiscriminant(type_name, value) => {
95 write!(f, "not discriminant for {type_name}: 0x{value:x}")
96 }
97 Self::TrailingData => write!(f, "trailing data"),
98 Self::Underflow => write!(f, "parse underflow"),
99 Self::Failure => write!(f, "unreachable"),
100 }
101 }
102}
103
104impl From<core::num::TryFromIntError> for TpmErrorKind {
105 fn from(_: core::num::TryFromIntError) -> Self {
106 Self::Capacity(usize::MAX)
107 }
108}
109
110pub type TpmResult<T> = Result<T, TpmErrorKind>;
111
112pub struct TpmWriter<'a> {
114 buffer: &'a mut [u8],
115 cursor: usize,
116}
117
118impl<'a> TpmWriter<'a> {
119 #[must_use]
121 pub fn new(buffer: &'a mut [u8]) -> Self {
122 Self { buffer, cursor: 0 }
123 }
124
125 #[must_use]
127 pub fn len(&self) -> usize {
128 self.cursor
129 }
130
131 #[must_use]
133 pub fn is_empty(&self) -> bool {
134 self.cursor == 0
135 }
136
137 pub fn write_bytes(&mut self, bytes: &[u8]) -> TpmResult<()> {
144 let end = self.cursor + bytes.len();
145 if end > self.buffer.len() {
146 return Err(TpmErrorKind::Failure);
147 }
148 self.buffer[self.cursor..end].copy_from_slice(bytes);
149 self.cursor = end;
150 Ok(())
151 }
152}
153
154pub trait TpmSized {
157 const SIZE: usize;
160
161 fn len(&self) -> usize;
163
164 fn is_empty(&self) -> bool {
166 self.len() == 0
167 }
168}
169
170pub trait TpmBuild: TpmSized {
171 fn build(&self, writer: &mut TpmWriter) -> TpmResult<()>;
177}
178
179pub trait TpmParse: Sized + TpmSized {
180 fn parse(buf: &[u8]) -> TpmResult<(Self, &[u8])>;
188}
189
190pub trait TpmTagged {
192 type Tag: TpmParse + TpmBuild + Copy;
194 type Value;
196}
197
198pub trait TpmParseTagged: Sized {
200 fn parse_tagged(tag: <Self as TpmTagged>::Tag, buf: &[u8]) -> TpmResult<(Self, &[u8])>
208 where
209 Self: TpmTagged,
210 <Self as TpmTagged>::Tag: TpmParse + TpmBuild;
211}
212
213tpm_integer!(u8, Unsigned);
214tpm_integer!(i8, Signed);
215tpm_integer!(i32, Signed);
216tpm_integer!(u16, Unsigned);
217tpm_integer!(u32, Unsigned);
218tpm_integer!(u64, Unsigned);
219
220pub fn build_tpm2b(writer: &mut TpmWriter, data: &[u8]) -> TpmResult<()> {
226 let len_u16 = u16::try_from(data.len()).map_err(|_| TpmErrorKind::Capacity(u16::MAX.into()))?;
227 TpmBuild::build(&len_u16, writer)?;
228 writer.write_bytes(data)
229}
230
231pub fn parse_tpm2b(buf: &[u8]) -> TpmResult<(&[u8], &[u8])> {
238 let (size, buf) = u16::parse(buf)?;
239 let size = size as usize;
240
241 if size > crate::constant::TPM_MAX_COMMAND_SIZE {
242 return Err(TpmErrorKind::Capacity(
243 crate::constant::TPM_MAX_COMMAND_SIZE,
244 ));
245 }
246
247 if buf.len() < size {
248 return Err(TpmErrorKind::Underflow);
249 }
250 Ok(buf.split_at(size))
251}
252
253#[must_use]
255pub const fn tpm_hash_size(alg_id: &TpmAlgId) -> Option<usize> {
256 match alg_id {
257 TpmAlgId::Sha1 => Some(20),
258 TpmAlgId::Sha256 | TpmAlgId::Sm3_256 => Some(32),
259 TpmAlgId::Sha384 => Some(48),
260 TpmAlgId::Sha512 => Some(64),
261 _ => None,
262 }
263}