#![cfg_attr(not(test), no_std)]
#![deny(unsafe_code)]
#![deny(clippy::all)]
#![deny(clippy::pedantic)]
#![recursion_limit = "256"]
pub mod buffer;
pub mod constant;
pub mod data;
pub mod list;
#[macro_use]
pub mod r#macro;
pub mod message;
use crate::data::TpmAlgId;
pub use buffer::TpmBuffer;
use core::{
convert::{From, TryFrom},
fmt,
mem::size_of,
result::Result,
};
pub use list::TpmList;
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
#[repr(transparent)]
pub struct TpmHandle(pub u32);
impl From<u32> for TpmHandle {
fn from(val: u32) -> Self {
Self(val)
}
}
impl From<TpmHandle> for u32 {
fn from(val: TpmHandle) -> Self {
val.0
}
}
impl TpmBuild for TpmHandle {
fn build(&self, writer: &mut TpmWriter) -> TpmResult<()> {
TpmBuild::build(&self.0, writer)
}
}
impl TpmParse for TpmHandle {
fn parse(buf: &[u8]) -> TpmResult<(Self, &[u8])> {
let (val, buf) = u32::parse(buf)?;
Ok((Self(val), buf))
}
}
impl TpmSized for TpmHandle {
const SIZE: usize = size_of::<u32>();
fn len(&self) -> usize {
Self::SIZE
}
}
impl fmt::Display for TpmHandle {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(&self.0, f)
}
}
impl fmt::LowerHex for TpmHandle {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::LowerHex::fmt(&self.0, f)
}
}
impl fmt::UpperHex for TpmHandle {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::UpperHex::fmt(&self.0, f)
}
}
#[derive(Debug, PartialEq, Eq)]
pub enum TpmNotDiscriminant {
Signed(i64),
Unsigned(u64),
}
impl fmt::LowerHex for TpmNotDiscriminant {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
TpmNotDiscriminant::Signed(v) => write!(f, "{v:x}"),
TpmNotDiscriminant::Unsigned(v) => write!(f, "{v:x}"),
}
}
}
#[derive(Debug, PartialEq, Eq)]
pub enum TpmErrorKind {
Capacity(usize),
InvalidValue,
NotDiscriminant(&'static str, TpmNotDiscriminant),
TrailingData,
Underflow,
Failure,
}
impl fmt::Display for TpmErrorKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Capacity(size) => write!(f, "exceeds capacity {size}"),
Self::InvalidValue => write!(f, "invalid value"),
Self::NotDiscriminant(type_name, value) => {
write!(f, "not discriminant for {type_name}: 0x{value:x}")
}
Self::TrailingData => write!(f, "trailing data"),
Self::Underflow => write!(f, "parse underflow"),
Self::Failure => write!(f, "unreachable"),
}
}
}
impl From<core::num::TryFromIntError> for TpmErrorKind {
fn from(_: core::num::TryFromIntError) -> Self {
Self::Capacity(usize::MAX)
}
}
pub type TpmResult<T> = Result<T, TpmErrorKind>;
pub struct TpmWriter<'a> {
buffer: &'a mut [u8],
cursor: usize,
}
impl<'a> TpmWriter<'a> {
#[must_use]
pub fn new(buffer: &'a mut [u8]) -> Self {
Self { buffer, cursor: 0 }
}
#[must_use]
pub fn len(&self) -> usize {
self.cursor
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.cursor == 0
}
pub fn write_bytes(&mut self, bytes: &[u8]) -> TpmResult<()> {
let end = self.cursor + bytes.len();
if end > self.buffer.len() {
return Err(TpmErrorKind::Failure);
}
self.buffer[self.cursor..end].copy_from_slice(bytes);
self.cursor = end;
Ok(())
}
}
pub trait TpmSized {
const SIZE: usize;
fn len(&self) -> usize;
fn is_empty(&self) -> bool {
self.len() == 0
}
}
pub trait TpmBuild: TpmSized {
fn build(&self, writer: &mut TpmWriter) -> TpmResult<()>;
}
pub trait TpmParse: Sized + TpmSized {
fn parse(buf: &[u8]) -> TpmResult<(Self, &[u8])>;
}
pub trait TpmTagged {
type Tag: TpmParse + TpmBuild + Copy;
type Value;
}
pub trait TpmParseTagged: Sized {
fn parse_tagged(tag: <Self as TpmTagged>::Tag, buf: &[u8]) -> TpmResult<(Self, &[u8])>
where
Self: TpmTagged,
<Self as TpmTagged>::Tag: TpmParse + TpmBuild;
}
tpm_integer!(u8, Unsigned);
tpm_integer!(i8, Signed);
tpm_integer!(i32, Signed);
tpm_integer!(u16, Unsigned);
tpm_integer!(u32, Unsigned);
tpm_integer!(u64, Unsigned);
pub fn build_tpm2b(writer: &mut TpmWriter, data: &[u8]) -> TpmResult<()> {
let len_u16 = u16::try_from(data.len()).map_err(|_| TpmErrorKind::Capacity(u16::MAX.into()))?;
TpmBuild::build(&len_u16, writer)?;
writer.write_bytes(data)
}
pub fn parse_tpm2b(buf: &[u8]) -> TpmResult<(&[u8], &[u8])> {
let (size, buf) = u16::parse(buf)?;
let size = size as usize;
if buf.len() < size {
return Err(TpmErrorKind::Underflow);
}
Ok(buf.split_at(size))
}
#[must_use]
pub const fn tpm_hash_size(alg_id: &TpmAlgId) -> Option<usize> {
match alg_id {
TpmAlgId::Sha1 => Some(20),
TpmAlgId::Sha256 | TpmAlgId::Sm3_256 => Some(32),
TpmAlgId::Sha384 => Some(48),
TpmAlgId::Sha512 => Some(64),
_ => None,
}
}