use super::{AlgorithmId, EventType, HashAlgorithm, PcrIndex};
use crate::data_types::{Align, PhysicalAddress};
use crate::proto::unsafe_protocol;
use crate::util::{ptr_write_unaligned_and_add, usize_from_u32};
use crate::{Error, Result, Status, StatusExt};
use core::fmt::{self, Debug, Formatter};
use core::marker::PhantomData;
use core::ptr;
use ptr_meta::Pointee;
use uefi_raw::protocol::tcg::v1::{TcgBootServiceCapability, TcgProtocol};
#[cfg(feature = "alloc")]
use {crate::mem::make_boxed, alloc::boxed::Box};
pub use uefi_raw::protocol::tcg::v1::TcgVersion as Version;
pub type Sha1Digest = [u8; 20];
#[repr(C)]
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Ord, PartialOrd)]
pub struct BootServiceCapability(TcgBootServiceCapability);
impl BootServiceCapability {
#[must_use]
pub const fn structure_version(&self) -> Version {
self.0.structure_version
}
#[must_use]
pub const fn protocol_spec_version(&self) -> Version {
self.0.protocol_spec_version
}
#[must_use]
pub fn hash_algorithm(&self) -> HashAlgorithm {
HashAlgorithm::from_bits_retain(u32::from(self.0.hash_algorithm_bitmap))
}
#[must_use]
pub const fn tpm_present(&self) -> bool {
self.0.tpm_present_flag != 0
}
#[must_use]
pub const fn tpm_deactivated(&self) -> bool {
self.0.tpm_deactivated_flag != 0
}
}
#[repr(C, packed)]
#[derive(Eq, Pointee)]
pub struct PcrEvent {
pcr_index: PcrIndex,
event_type: EventType,
digest: Sha1Digest,
event_data_size: u32,
event_data: [u8],
}
impl PcrEvent {
pub(super) const unsafe fn from_ptr<'a>(ptr: *const u8) -> &'a Self {
let ptr_u32: *const u32 = ptr.cast();
let event_size = unsafe { ptr_u32.add(7).read_unaligned() };
let event_size = usize_from_u32(event_size);
unsafe { &*ptr_meta::from_raw_parts(ptr.cast(), event_size) }
}
pub fn new_in_buffer<'buf>(
buffer: &'buf mut [u8],
pcr_index: PcrIndex,
event_type: EventType,
digest: Sha1Digest,
event_data: &[u8],
) -> Result<&'buf mut Self, Option<usize>> {
let event_data_size = u32::try_from(event_data.len())
.map_err(|_| Error::new(Status::INVALID_PARAMETER, None))?;
let required_size = size_of::<PcrIndex>()
+ size_of::<EventType>()
+ size_of::<Sha1Digest>()
+ size_of::<u32>()
+ event_data.len();
if buffer.len() < required_size {
return Err(Error::new(Status::BUFFER_TOO_SMALL, Some(required_size)));
}
let mut ptr: *mut u8 = buffer.as_mut_ptr().cast();
unsafe {
ptr_write_unaligned_and_add(&mut ptr, pcr_index);
ptr_write_unaligned_and_add(&mut ptr, event_type);
ptr_write_unaligned_and_add(&mut ptr, digest);
ptr_write_unaligned_and_add(&mut ptr, event_data_size);
ptr::copy(event_data.as_ptr(), ptr, event_data.len());
let ptr: *mut Self =
ptr_meta::from_raw_parts_mut(buffer.as_mut_ptr().cast(), event_data.len());
Ok(&mut *ptr)
}
}
#[cfg(feature = "alloc")]
pub fn new_in_box(
pcr_index: PcrIndex,
event_type: EventType,
digest: Sha1Digest,
event_data: &[u8],
) -> Result<Box<Self>> {
make_boxed(|buf| Self::new_in_buffer(buf, pcr_index, event_type, digest, event_data))
}
#[must_use]
pub const fn pcr_index(&self) -> PcrIndex {
self.pcr_index
}
#[must_use]
pub const fn event_type(&self) -> EventType {
self.event_type
}
#[must_use]
pub const fn event_data(&self) -> &[u8] {
&self.event_data
}
#[must_use]
pub const fn digest(&self) -> Sha1Digest {
self.digest
}
}
impl Align for PcrEvent {
fn alignment() -> usize {
1
}
}
impl Debug for PcrEvent {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
f.debug_struct("PcrEvent")
.field("pcr_index", &{ self.pcr_index })
.field("event_type", &{ self.event_type })
.field("digest", &self.digest)
.field("event_data_size", &{ self.event_data_size })
.field("event_data", &&self.event_data)
.finish()
}
}
impl PartialEq for PcrEvent {
fn eq(&self, rhs: &Self) -> bool {
self.pcr_index() == rhs.pcr_index()
&& self.event_type() == rhs.event_type()
&& self.digest == rhs.digest
&& self.event_data_size == rhs.event_data_size
&& self.event_data == rhs.event_data
}
}
opaque_type! {
pub struct FfiPcrEvent;
}
#[derive(Debug)]
pub struct EventLog<'a> {
_lifetime: PhantomData<&'a Tcg>,
location: *const u8,
last_entry: *const u8,
is_truncated: bool,
}
impl EventLog<'_> {
pub(super) const unsafe fn new(
location: *const u8,
last_entry: *const u8,
is_truncated: bool,
) -> Self {
Self {
_lifetime: PhantomData,
location,
last_entry,
is_truncated,
}
}
#[must_use]
pub const fn iter(&self) -> EventLogIter<'_> {
EventLogIter {
log: self,
location: self.location,
}
}
#[must_use]
pub const fn is_truncated(&self) -> bool {
self.is_truncated
}
}
#[derive(Debug)]
pub struct EventLogIter<'a> {
log: &'a EventLog<'a>,
location: *const u8,
}
impl<'a> Iterator for EventLogIter<'a> {
type Item = &'a PcrEvent;
fn next(&mut self) -> Option<Self::Item> {
if self.location.is_null() || self.log.last_entry.is_null() {
return None;
}
let event = unsafe { PcrEvent::from_ptr(self.location) };
if self.location == self.log.last_entry {
self.location = ptr::null();
} else {
self.location = unsafe { self.location.add(size_of_val(event)) };
}
Some(event)
}
}
#[derive(Debug)]
#[repr(transparent)]
#[unsafe_protocol(TcgProtocol::GUID)]
pub struct Tcg(TcgProtocol);
#[derive(Debug)]
pub struct StatusCheck<'a> {
pub protocol_capability: BootServiceCapability,
pub feature_flags: u32,
pub event_log: EventLog<'a>,
}
impl Tcg {
pub fn status_check(&mut self) -> Result<StatusCheck<'_>> {
let mut protocol_capability = TcgBootServiceCapability::default();
let mut feature_flags = 0;
let mut event_log_location = 0;
let mut event_log_last_entry = 0;
let status = unsafe {
(self.0.status_check)(
&mut self.0,
&mut protocol_capability,
&mut feature_flags,
&mut event_log_location,
&mut event_log_last_entry,
)
};
if status.is_success() {
let truncated = false;
let event_log = unsafe {
EventLog::new(
event_log_location as *const u8,
event_log_last_entry as *const u8,
truncated,
)
};
Ok(StatusCheck {
protocol_capability: BootServiceCapability(protocol_capability),
feature_flags,
event_log,
})
} else {
Err(status.into())
}
}
pub fn log_event(&mut self, event: &PcrEvent) -> Result {
let flags = 0x1;
let mut event_number = 0;
let event_ptr: *const PcrEvent = event;
unsafe {
(self.0.log_event)(&mut self.0, event_ptr.cast(), &mut event_number, flags).to_result()
}
}
pub fn hash_log_extend_event(
&mut self,
event: &mut PcrEvent,
data_to_hash: Option<&[u8]>,
) -> Result {
let hash_data;
let hash_data_len = if let Some(data_to_hash) = data_to_hash {
hash_data = data_to_hash.as_ptr() as PhysicalAddress;
u64::try_from(data_to_hash.len()).unwrap()
} else {
hash_data = 0;
0
};
let mut event_number = 0;
let mut event_log_last_entry = 0;
let event_ptr: *mut PcrEvent = event;
unsafe {
(self.0.hash_log_extend_event)(
&mut self.0,
hash_data,
hash_data_len,
AlgorithmId::SHA1.0.into(),
event_ptr.cast(),
&mut event_number,
&mut event_log_last_entry,
)
.to_result()
}
}
pub fn pass_through_to_tpm(
&mut self,
input_parameter_block: &[u8],
output_parameter_block: &mut [u8],
) -> Result {
let input_parameter_block_len = u32::try_from(input_parameter_block.len())
.map_err(|_| Error::from(Status::BAD_BUFFER_SIZE))?;
let output_parameter_block_len = u32::try_from(output_parameter_block.len())
.map_err(|_| Error::from(Status::BAD_BUFFER_SIZE))?;
unsafe {
(self.0.pass_through_to_tpm)(
&mut self.0,
input_parameter_block_len,
input_parameter_block.as_ptr(),
output_parameter_block_len,
output_parameter_block.as_mut_ptr(),
)
.to_result()
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use core::slice;
#[test]
fn test_new_pcr_event() {
let mut event_buf = [0; 256];
#[rustfmt::skip]
let digest = [
0x00, 0x01, 0x02, 0x03,
0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0a, 0x0b,
0x0c, 0x0d, 0x0e, 0x0f,
0x10, 0x11, 0x12, 0x13,
];
let data = [0x14, 0x15, 0x16, 0x17];
let event =
PcrEvent::new_in_buffer(&mut event_buf, PcrIndex(4), EventType::IPL, digest, &data)
.unwrap();
assert_eq!(event.pcr_index(), PcrIndex(4));
assert_eq!(event.event_type(), EventType::IPL);
assert_eq!(event.digest(), digest);
assert_eq!(event.event_data(), data);
let event_ptr: *const PcrEvent = event;
let bytes = unsafe { slice::from_raw_parts(event_ptr.cast::<u8>(), size_of_val(event)) };
#[rustfmt::skip]
assert_eq!(bytes, [
0x04, 0x00, 0x00, 0x00,
0x0d, 0x00, 0x00, 0x00,
0x00, 0x01, 0x02, 0x03,
0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0a, 0x0b,
0x0c, 0x0d, 0x0e, 0x0f,
0x10, 0x11, 0x12, 0x13,
0x04, 0x00, 0x00, 0x00,
0x14, 0x15, 0x16, 0x17,
]);
assert_eq!(
event,
&*PcrEvent::new_in_box(PcrIndex(4), EventType::IPL, digest, &data).unwrap()
);
}
#[test]
fn test_event_log_v1() {
#[rustfmt::skip]
let bytes = [
0x00, 0x00, 0x00, 0x00,
0x08, 0x00, 0x00, 0x00,
0x14, 0x89, 0xf9, 0x23, 0xc4, 0xdc, 0xa7, 0x29, 0x17, 0x8b,
0x3e, 0x32, 0x33, 0x45, 0x85, 0x50, 0xd8, 0xdd, 0xdf, 0x29,
0x02, 0x00, 0x00, 0x00,
0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x08, 0x00, 0x00, 0x80,
0xc7, 0x06, 0xe7, 0xdd, 0x36, 0x39, 0x29, 0x84, 0xeb, 0x06,
0xaa, 0xa0, 0x8f, 0xf3, 0x36, 0x84, 0x40, 0x77, 0xb3, 0xed,
0x10, 0x00, 0x00, 0x00,
0x00, 0x00, 0x82, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00,
];
let log = unsafe { EventLog::new(bytes.as_ptr(), bytes.as_ptr().add(34), false) };
let mut iter = log.iter();
let entry = iter.next().unwrap();
assert_eq!(entry.pcr_index(), PcrIndex(0));
assert_eq!(entry.event_type(), EventType::CRTM_VERSION);
#[rustfmt::skip]
assert_eq!(
entry.digest(),
[
0x14, 0x89, 0xf9, 0x23, 0xc4, 0xdc, 0xa7, 0x29, 0x17, 0x8b,
0x3e, 0x32, 0x33, 0x45, 0x85, 0x50, 0xd8, 0xdd, 0xdf, 0x29,
]
);
assert_eq!(entry.event_data(), [0x00, 0x00]);
let entry = iter.next().unwrap();
assert_eq!(entry.pcr_index(), PcrIndex(0));
assert_eq!(entry.event_type(), EventType::EFI_PLATFORM_FIRMWARE_BLOB);
#[rustfmt::skip]
assert_eq!(
entry.digest(),
[
0xc7, 0x06, 0xe7, 0xdd, 0x36, 0x39, 0x29, 0x84, 0xeb, 0x06,
0xaa, 0xa0, 0x8f, 0xf3, 0x36, 0x84, 0x40, 0x77, 0xb3, 0xed,
]
);
#[rustfmt::skip]
assert_eq!(
entry.event_data(),
[
0x00, 0x00, 0x82, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00,
]
);
}
}