//! [TCG] (Trusted Computing Group) protocol for [TPM] (Trusted Platform //! Module) 2.0. //! //! This protocol is defined in the [TCG EFI Protocol Specification _TPM //! Family 2.0_][spec]. It is generally implemented only for TPM 2.0 //! devices, but the spec indicates it can also be used for older TPM //! devices. //! //! [spec]: https://trustedcomputinggroup.org/resource/tcg-efi-protocol-specification/ //! [TCG]: https://trustedcomputinggroup.org/ //! [TPM]: https://en.wikipedia.org/wiki/Trusted_Platform_Module use super::{v1, AlgorithmId, EventType, HashAlgorithm, PcrIndex}; use crate::data_types::{Align, PhysicalAddress, UnalignedSlice}; use crate::proto::unsafe_protocol; use crate::util::{ptr_write_unaligned_and_add, usize_from_u32}; use crate::{Error, Result, Status, StatusExt}; use bitflags::bitflags; use core::fmt::{self, Debug, Formatter}; use core::marker::PhantomData; use core::{mem, ptr, slice}; use ptr_meta::{Pointee, PtrExt}; #[cfg(feature = "alloc")] use {crate::mem::make_boxed, alloc::boxed::Box}; #[cfg(all(feature = "unstable", feature = "alloc"))] use alloc::alloc::Global; /// Version information. /// /// Layout compatible with the C type `EFI_TG2_VERSION`. #[repr(C)] #[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Ord, PartialOrd)] pub struct Version { /// Major version. pub major: u8, /// Minor version. pub minor: u8, } bitflags! { /// Event log formats supported by the firmware. /// /// Corresponds to the C typedef `EFI_TCG2_EVENT_ALGORITHM_BITMAP`. #[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord)] #[repr(transparent)] pub struct EventLogFormat: u32 { /// Firmware supports the SHA-1 log format. const TCG_1_2 = 0x0000_0001; /// Firmware supports the crypto-agile log format. const TCG_2 = 0x0000_0002; } } /// Information about the protocol and the TPM device. /// /// Layout compatible with the C type `EFI_TCG2_BOOT_SERVICE_CAPABILITY`. #[repr(C)] #[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd)] pub struct BootServiceCapability { size: u8, /// Version of the EFI TCG2 protocol. pub structure_version: Version, /// Version of the EFI TCG2 protocol. pub protocol_version: Version, /// Bitmap of supported hash algorithms. pub hash_algorithm_bitmap: HashAlgorithm, /// Event log formats supported by the firmware. pub supported_event_logs: EventLogFormat, present_flag: u8, /// Maximum size (in bytes) of a command that can be sent to the TPM. pub max_command_size: u16, /// Maximum size (in bytes) of a response that can be provided by the TPM. pub max_response_size: u16, /// Manufacturer ID. /// /// See the [TCG Vendor ID registry]. /// /// [TCG Vendor ID registry]: https://trustedcomputinggroup.org/resource/vendor-id-registry/ pub manufacturer_id: u32, /// Maximum number of supported PCR banks (hashing algorithms). pub number_of_pcr_banks: u32, /// Bitmap of currently-active PCR banks (hashing algorithms). This /// is a subset of the supported algorithms in [`hash_algorithm_bitmap`]. /// /// [`hash_algorithm_bitmap`]: Self::hash_algorithm_bitmap pub active_pcr_banks: HashAlgorithm, } impl Default for BootServiceCapability { fn default() -> Self { // OK to unwrap, the size is less than u8. let struct_size = u8::try_from(mem::size_of::()).unwrap(); Self { size: struct_size, structure_version: Version::default(), protocol_version: Version::default(), hash_algorithm_bitmap: HashAlgorithm::default(), supported_event_logs: EventLogFormat::default(), present_flag: 0, max_command_size: 0, max_response_size: 0, manufacturer_id: 0, number_of_pcr_banks: 0, active_pcr_banks: HashAlgorithm::default(), } } } impl BootServiceCapability { /// Whether the TPM device is present. #[must_use] pub const fn tpm_present(&self) -> bool { self.present_flag != 0 } } bitflags! { /// Flags for the [`Tcg::hash_log_extend_event`] function. #[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord)] #[repr(transparent)] pub struct HashLogExtendEventFlags: u64 { /// Extend an event but don't log it. const EFI_TCG2_EXTEND_ONLY = 0x0000_0000_0000_0001; /// Use when measuring a PE/COFF image. const PE_COFF_IMAGE = 0x0000_0000_0000_0010; } } /// Header used in [`PcrEventInputs`]. /// /// Layout compatible with the C type `EFI_TCG2_EVENT_HEADER`. #[derive(Clone, Copy, Debug, Eq, PartialEq)] #[repr(C, packed)] struct EventHeader { header_size: u32, header_version: u16, pcr_index: PcrIndex, event_type: EventType, } /// Event type passed to [`Tcg::hash_log_extend_event`]. /// /// Layout compatible with the C type `EFI_TCG2_EVENT`. /// /// The TPM v1 spec uses a single generic event type for both creating a /// new event and reading an event from the log. The v2 spec splits this /// into two structs: `EFI_TCG2_EVENT` for creating events, and /// `TCG_PCR_EVENT2` for reading events. To help clarify the usage, our /// API renames these types to `PcrEventInputs` and `PcrEvent`, /// respectively. #[derive(Eq, Pointee)] #[repr(C, packed)] pub struct PcrEventInputs { size: u32, event_header: EventHeader, event: [u8], } impl PcrEventInputs { /// Create a new `PcrEventInputs` using a byte buffer for storage. /// /// # Errors /// /// Returns [`Status::BUFFER_TOO_SMALL`] if the `buffer` is not large /// enough. The required size will be returned in the error data. /// /// Returns [`Status::INVALID_PARAMETER`] if the `event_data` size is too /// large. pub fn new_in_buffer<'buf>( buffer: &'buf mut [u8], pcr_index: PcrIndex, event_type: EventType, event_data: &[u8], ) -> Result<&'buf mut Self, Option> { let required_size = mem::size_of::() + mem::size_of::() + event_data.len(); if buffer.len() < required_size { return Err(Error::new(Status::BUFFER_TOO_SMALL, Some(required_size))); } let size_field = u32::try_from(required_size) .map_err(|_| Error::new(Status::INVALID_PARAMETER, None))?; let mut ptr: *mut u8 = buffer.as_mut_ptr().cast(); unsafe { ptr_write_unaligned_and_add(&mut ptr, size_field); ptr_write_unaligned_and_add( &mut ptr, EventHeader { header_size: u32::try_from(mem::size_of::()).unwrap(), header_version: 1, pcr_index, event_type, }, ); 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) } } /// Create a new `PcrEventInputs` in a [`Box`]. /// /// # Errors /// /// Returns [`Status::INVALID_PARAMETER`] if the `event_data` size is too /// large. #[cfg(feature = "alloc")] pub fn new_in_box( pcr_index: PcrIndex, event_type: EventType, event_data: &[u8], ) -> Result> { #[cfg(not(feature = "unstable"))] { make_boxed(|buf| Self::new_in_buffer(buf, pcr_index, event_type, event_data)) } #[cfg(feature = "unstable")] { make_boxed( |buf| Self::new_in_buffer(buf, pcr_index, event_type, event_data), Global, ) } } } impl Align for PcrEventInputs { fn alignment() -> usize { 1 } } impl Debug for PcrEventInputs { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { f.debug_struct("PcrEventInputs") .field("size", &{ self.size }) .field("event_header", &self.event_header) .field("event", &"") .finish() } } // Manual `PartialEq` implementation since it can't be derived for a packed DST. impl PartialEq for PcrEventInputs { fn eq(&self, other: &Self) -> bool { self.size == other.size && self.event_header == other.event_header && self.event == other.event } } #[repr(C, packed)] #[derive(Clone, Copy, Debug, PartialEq, Eq)] struct AlgorithmDigestSize { algorithm_id: AlgorithmId, digest_size: u16, } #[derive(Clone, Debug)] struct AlgorithmDigestSizes<'a>(UnalignedSlice<'a, AlgorithmDigestSize>); impl<'a> AlgorithmDigestSizes<'a> { fn get_size(&self, alg: AlgorithmId) -> Option { self.0.iter().find_map(|elem| { if { elem.algorithm_id } == alg { Some(elem.digest_size) } else { None } }) } } fn u32_le_from_bytes_at_offset(bytes: &[u8], offset: usize) -> Option { let bytes = bytes.get(offset..offset + 4)?; // OK to unwrap: we know `bytes` is now of length 4. let val = u32::from_le_bytes(bytes.try_into().unwrap()); Some(val) } /// Header stored at the beginning of the event log. /// /// Layout compatible with the C type `TCG_EfiSpecIDEventStruct`. #[derive(Clone, Debug)] #[allow(unused)] // We don't current access most of the fields. struct EventLogHeader<'a> { platform_class: u32, // major, minor, errata spec_version: (u8, u8, u8), uintn_size: u8, algorithm_digest_sizes: AlgorithmDigestSizes<'a>, vendor_info: &'a [u8], // Size of the whole header event, in bytes. size_in_bytes: usize, } impl<'a> EventLogHeader<'a> { fn new(event: &'a v1::PcrEvent) -> Option { if event.pcr_index() != PcrIndex(0) { return None; } if { event.event_type() } != EventType::NO_ACTION { return None; } if event.digest() != [0; 20] { return None; } let event = &event.event_data(); if event.get(..16)? != *b"Spec ID Event03\0" { return None; } let platform_class = u32_le_from_bytes_at_offset(event, 16)?; let version_minor = *event.get(20)?; let version_major = *event.get(21)?; let version_errata = *event.get(22)?; let uintn_size = *event.get(23)?; let number_of_algorithms = usize_from_u32(u32_le_from_bytes_at_offset(event, 24)?); let vendor_info_size_byte_offset = 28 + (number_of_algorithms * mem::size_of::()); let vendor_info_size = usize::from(*event.get(vendor_info_size_byte_offset)?); // Safety: we know the slice is big enough because we just // safely got the field after the slice (`vendor_info_size`). let algorithm_digest_sizes = unsafe { let ptr: *const AlgorithmDigestSize = event.as_ptr().add(28).cast(); AlgorithmDigestSizes(UnalignedSlice::new(ptr, number_of_algorithms)) }; let vendor_info_byte_offset = vendor_info_size_byte_offset + 1; let vendor_info = event.get(vendor_info_byte_offset..vendor_info_byte_offset + vendor_info_size)?; // 32 is the size of PcrEventV1 excluding the event data. let size_in_bytes = 32 + vendor_info_byte_offset + vendor_info_size; Some(Self { platform_class, spec_version: (version_major, version_minor, version_errata), uintn_size, algorithm_digest_sizes, vendor_info, size_in_bytes, }) } } /// TPM event log as returned by [`Tcg::get_event_log_v2`]. /// /// This type of event log can contain multiple hash types (e.g. SHA-1, SHA-256, /// SHA-512, etc). #[derive(Debug)] pub struct EventLog<'a> { // Tie the lifetime to the protocol, and by extension, boot services. _lifetime: PhantomData<&'a Tcg>, location: *const u8, last_entry: *const u8, is_truncated: bool, } impl<'a> EventLog<'a> { /// Iterator of events in the log. #[must_use] pub fn iter(&self) -> EventLogIter { if let Some(header) = self.header() { // Advance past the header let location = unsafe { self.location.add(header.size_in_bytes) }; EventLogIter { log: self, location, header: self.header(), } } else { EventLogIter { log: self, location: ptr::null(), header: None, } } } /// Header at the beginning of the event log. fn header(&self) -> Option { // The spec is unclear if the header is present when there are // no entries, so lets assume that `self.location` will be null // if there's no header, and otherwise valid. if self.location.is_null() { None } else { // Safety: we trust that the protocol has given us a valid range // of memory to read from. let event = unsafe { v1::PcrEvent::from_ptr(self.location) }; EventLogHeader::new(event) } } /// Whether the event log is truncated due to not enough space in the log to /// contain some events. #[must_use] pub const fn is_truncated(&self) -> bool { self.is_truncated } } /// Digests in a PCR event. #[derive(Clone)] pub struct PcrEventDigests<'a> { data: &'a [u8], algorithm_digest_sizes: AlgorithmDigestSizes<'a>, } impl<'a> Debug for PcrEventDigests<'a> { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { f.debug_list().entries(self.clone()).finish() } } impl<'a> IntoIterator for PcrEventDigests<'a> { type Item = (AlgorithmId, &'a [u8]); type IntoIter = PcrEventDigestIter<'a>; fn into_iter(self) -> Self::IntoIter { PcrEventDigestIter { digests: self, offset: 0, } } } /// Iterator over a list of digests. #[derive(Debug)] pub struct PcrEventDigestIter<'a> { digests: PcrEventDigests<'a>, offset: usize, } impl<'a> Iterator for PcrEventDigestIter<'a> { type Item = (AlgorithmId, &'a [u8]); fn next(&mut self) -> Option { let data = &self.digests.data[self.offset..]; let alg = data.get(..2)?; let alg = AlgorithmId(u16::from_le_bytes([alg[0], alg[1]])); let digest_size = usize::from(self.digests.algorithm_digest_sizes.get_size(alg)?); let digest = data.get(2..2 + digest_size)?; self.offset += 2 + digest_size; Some((alg, digest)) } } /// PCR event from an [`EventLog`]. /// /// This roughly corresponds to the C type `TCG_PCR_EVENT2`, but is not layout /// compatible. /// /// The TPM v1 spec uses a single generic event type for both creating a /// new event and reading an event from the log. The v2 spec splits this /// into two structs: `EFI_TCG2_EVENT` for creating events, and /// `TCG_PCR_EVENT2` for reading events. To help clarify the usage, our /// API renames these types to `PcrEventInputs` and `PcrEvent`, /// respectively. #[derive(Debug)] pub struct PcrEvent<'a> { pcr_index: PcrIndex, event_type: EventType, digests: &'a [u8], event_data: &'a [u8], // Precalculate the pointer to the next event. next: *const u8, // This data from the v2 log header is needed to parse the digest data. algorithm_digest_sizes: AlgorithmDigestSizes<'a>, } impl<'a> PcrEvent<'a> { unsafe fn from_ptr(ptr: *const u8, header: EventLogHeader<'a>) -> Option { let ptr_u32: *const u32 = ptr.cast(); let pcr_index = PcrIndex(ptr_u32.read_unaligned()); let event_type = EventType(ptr_u32.add(1).read_unaligned()); let digests_count = ptr_u32.add(2).read_unaligned(); let digests_ptr: *const u8 = ptr.add(12); // Get the byte size of the digests so that the digests iterator // can be safe code. let mut digests_byte_size = 0; let mut elem_ptr = digests_ptr; for _ in 0..digests_count { let algorithm_id = AlgorithmId(elem_ptr.cast::().read_unaligned()); let alg_and_digest_size = mem::size_of::() + usize::from(header.algorithm_digest_sizes.get_size(algorithm_id)?); digests_byte_size += alg_and_digest_size; elem_ptr = elem_ptr.add(alg_and_digest_size); } let digests = slice::from_raw_parts(digests_ptr, digests_byte_size); let event_size_ptr = digests_ptr.add(digests_byte_size); let event_size = usize_from_u32(event_size_ptr.cast::().read_unaligned()); let event_data_ptr = event_size_ptr.add(4); let event_data = slice::from_raw_parts(event_data_ptr, event_size); Some(Self { pcr_index, event_type, digests, event_data, next: event_data_ptr.add(event_size), algorithm_digest_sizes: header.algorithm_digest_sizes, }) } /// PCR index for the event. #[must_use] pub const fn pcr_index(&self) -> PcrIndex { self.pcr_index } /// Type of event, indicating what type of data is stored in [`event_data`]. /// /// [`event_data`]: Self::event_data #[must_use] pub const fn event_type(&self) -> EventType { self.event_type } /// Raw event data. The meaning of this data can be determined from /// the [`event_type`]. /// /// Note that this data is independent of what is hashed in [`digests`]. /// /// [`digests`]: Self::digests /// [`event_type`]: Self::event_type #[must_use] pub const fn event_data(&self) -> &[u8] { self.event_data } /// Digests of the data hashed for this event. #[must_use] pub fn digests(&self) -> PcrEventDigests { PcrEventDigests { data: self.digests, algorithm_digest_sizes: self.algorithm_digest_sizes.clone(), } } } /// Iterator for events in [`EventLog`]. #[derive(Debug)] pub struct EventLogIter<'a> { log: &'a EventLog<'a>, header: Option>, location: *const u8, } impl<'a> Iterator for EventLogIter<'a> { type Item = PcrEvent<'a>; fn next(&mut self) -> Option { // The spec says that `last_entry` will be null if there are no // events. Presumably `location` will be null as well, but check // both just to be safe. if self.location.is_null() || self.log.last_entry.is_null() { return None; } // Safety: we trust that the protocol has given us a valid range // of memory to read from. let event = unsafe { PcrEvent::from_ptr(self.location, self.header.clone()?)? }; // If this is the last entry, set the location to null so that // future calls to `next()` return `None`. if self.location == self.log.last_entry { self.location = ptr::null(); } else { self.location = event.next; } Some(event) } } /// Protocol for interacting with TPM devices. /// /// This protocol can be used for interacting with older TPM 1.1/1.2 /// devices, but most firmware only uses it for TPM 2.0. /// /// The corresponding C type is `EFI_TCG2_PROTOCOL`. #[derive(Debug)] #[repr(C)] #[unsafe_protocol("607f766c-7455-42be-930b-e4d76db2720f")] pub struct Tcg { get_capability: unsafe extern "efiapi" fn( this: *mut Tcg, protocol_capability: *mut BootServiceCapability, ) -> Status, get_event_log: unsafe extern "efiapi" fn( this: *mut Tcg, event_log_format: EventLogFormat, event_log_location: *mut PhysicalAddress, event_log_last_entry: *mut PhysicalAddress, event_log_truncated: *mut u8, ) -> Status, hash_log_extend_event: unsafe extern "efiapi" fn( this: *mut Tcg, flags: HashLogExtendEventFlags, data_to_hash: PhysicalAddress, data_to_hash_len: u64, // Use `()` here rather than `PcrEventInputs` so that it's a // thin pointer. event: *const (), ) -> Status, submit_command: unsafe extern "efiapi" fn( this: *mut Tcg, input_parameter_block_size: u32, input_parameter_block: *const u8, output_parameter_block_size: u32, output_parameter_block: *mut u8, ) -> Status, get_active_pcr_banks: unsafe extern "efiapi" fn(this: *mut Tcg, active_pcr_banks: *mut HashAlgorithm) -> Status, set_active_pcr_banks: unsafe extern "efiapi" fn(this: *mut Tcg, active_pcr_banks: HashAlgorithm) -> Status, get_result_of_set_active_pcr_banks: unsafe extern "efiapi" fn( this: *mut Tcg, operation_present: *mut u32, response: *mut u32, ) -> Status, } impl Tcg { /// Get information about the protocol and TPM device. pub fn get_capability(&mut self) -> Result { let mut capability = BootServiceCapability::default(); unsafe { (self.get_capability)(self, &mut capability).to_result_with_val(|| capability) } } /// Get the V1 event log. This provides events in the same format as a V1 /// TPM, so all events use SHA-1 hashes. pub fn get_event_log_v1(&mut self) -> Result { let mut location = 0; let mut last_entry = 0; let mut truncated = 0; let status = unsafe { (self.get_event_log)( self, EventLogFormat::TCG_1_2, &mut location, &mut last_entry, &mut truncated, ) }; if status.is_success() { let is_truncated = truncated != 0; let log = unsafe { v1::EventLog::new(location as *const u8, last_entry as *const u8, is_truncated) }; Ok(log) } else { Err(status.into()) } } /// Get the V2 event log. This format allows for a flexible list of hash types. pub fn get_event_log_v2(&mut self) -> Result { let mut location = 0; let mut last_entry = 0; let mut truncated = 0; let status = unsafe { (self.get_event_log)( self, EventLogFormat::TCG_2, &mut location, &mut last_entry, &mut truncated, ) }; if status.is_success() { let is_truncated = truncated != 0; let log = EventLog { _lifetime: PhantomData, location: location as *const u8, last_entry: last_entry as *const u8, is_truncated, }; Ok(log) } else { Err(status.into()) } } /// Extend a PCR and add an entry to the event log. pub fn hash_log_extend_event( &mut self, flags: HashLogExtendEventFlags, data_to_hash: &[u8], event: &PcrEventInputs, ) -> Result { let event: *const PcrEventInputs = event; let (event, _event_size) = PtrExt::to_raw_parts(event); unsafe { (self.hash_log_extend_event)( self, flags, data_to_hash.as_ptr() as PhysicalAddress, // OK to unwrap, usize fits in u64. u64::try_from(data_to_hash.len()).unwrap(), event, ) .to_result() } } /// Send a command directly to the TPM. /// /// Constructing the input block and parsing the output block are outside /// the scope of this crate. See the [TPM 2.0 Specification][spec], in /// particular Part 2 (Structures) and Part 3 (Commands). /// /// Note that TPM structures are big endian. /// /// [spec]: https://trustedcomputinggroup.org/resource/tpm-library-specification/ pub fn submit_command( &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.submit_command)( self, input_parameter_block_len, input_parameter_block.as_ptr(), output_parameter_block_len, output_parameter_block.as_mut_ptr(), ) .to_result() } } /// Get a bitmap of the active PCR banks. Each bank corresponds to a hash /// algorithm. pub fn get_active_pcr_banks(&mut self) -> Result { let mut active_pcr_banks = HashAlgorithm::empty(); let status = unsafe { (self.get_active_pcr_banks)(self, &mut active_pcr_banks) }; status.to_result_with_val(|| active_pcr_banks) } /// Set the active PCR banks. Each bank corresponds to a hash /// algorithm. This change will not take effect until the system is /// rebooted twice. pub fn set_active_pcr_banks(&mut self, active_pcr_banks: HashAlgorithm) -> Result { unsafe { (self.set_active_pcr_banks)(self, active_pcr_banks) }.to_result() } /// Get the stored result of calling [`Tcg::set_active_pcr_banks`] in a /// previous boot. /// /// If there was no attempt to set the active PCR banks in a previous boot, /// this returns `None`. Otherwise, it returns a numeric response code: /// * `0x00000000`: Success /// * `0x00000001..=0x00000FFF`: TPM error code /// * `0xfffffff0`: The operation was canceled by the user or timed out /// * `0xfffffff1`: Firmware error pub fn get_result_of_set_active_pcr_banks(&mut self) -> Result> { let mut operation_present = 0; let mut response = 0; let status = unsafe { (self.get_result_of_set_active_pcr_banks)(self, &mut operation_present, &mut response) }; status.to_result_with_val(|| { if operation_present == 0 { None } else { Some(response) } }) } } #[cfg(test)] mod tests { use super::*; use alloc::vec::Vec; use core::slice; #[test] fn test_new_event() { let mut buf = [0; 22]; let event_data = [0x12, 0x13, 0x14, 0x15]; let event = PcrEventInputs::new_in_buffer(&mut buf, PcrIndex(4), EventType::IPL, &event_data) .unwrap(); assert_eq!({ event.size }, 22); assert_eq!( event.event_header, EventHeader { header_size: 14, header_version: 1, pcr_index: PcrIndex(4), event_type: EventType::IPL, } ); // Cast to a byte slice to check the data is exactly as expected. let event_ptr: *const PcrEventInputs = event; let event_ptr: *const u8 = event_ptr.cast(); let event_bytes = unsafe { slice::from_raw_parts(event_ptr, mem::size_of_val(event)) }; #[rustfmt::skip] assert_eq!(event_bytes, [ // Size 0x16, 0x00, 0x00, 0x00, // Header // Header size 0x0e, 0x00, 0x00, 0x00, // Header version 0x01, 0x00, // PCR index 0x04, 0x00, 0x00, 0x00, // Event type 0x0d, 0x00, 0x00, 0x00, // Event data 0x12, 0x13, 0x14, 0x15, ]); // Check that `new_in_box` gives the same value. assert_eq!( event, &*PcrEventInputs::new_in_box(PcrIndex(4), EventType::IPL, &event_data).unwrap() ); } #[test] fn test_event_log_v2() { // This data comes from dumping the TPM event log in a VM // (truncated to just two entries after the header). #[rustfmt::skip] let bytes = [ // Header event // PCR index 0x00, 0x00, 0x00, 0x00, // Event type 0x03, 0x00, 0x00, 0x00, // SHA1 digest 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Event data size 0x2d, 0x00, 0x00, 0x00, // Spec ID event data // Signature 0x53, 0x70, 0x65, 0x63, 0x20, 0x49, 0x44, 0x20, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x30, 0x33, 0x00, // Platform class 0x00, 0x00, 0x00, 0x00, // Spec version (minor, major, errata) (yes the order is weird) 0x00, 0x02, 0x00, // Uintn size 0x02, // Number of algorithms 0x04, 0x00, 0x00, 0x00, // Digest sizes // SHA1, size 0x04, 0x00, 0x14, 0x00, // SHA256, size 0x0b, 0x00, 0x20, 0x00, // SHA384, size 0x0c, 0x00, 0x30, 0x00, // SHA512, size 0x0d, 0x00, 0x40, 0x00, // Vendor info size 0x00, // Event 1 // PCR index 0x00, 0x00, 0x00, 0x00, // Event type 0x08, 0x00, 0x00, 0x00, // Digest count 0x04, 0x00, 0x00, 0x00, // Digests // SHA1 0x04, 0x00, 0x14, 0x89, 0xf9, 0x23, 0xc4, 0xdc, 0xa7, 0x29, 0x17, 0x8b, 0x3e, 0x32, 0x33, 0x45, 0x85, 0x50, 0xd8, 0xdd, 0xdf, 0x29, // SHA256 0x0b, 0x00, 0x96, 0xa2, 0x96, 0xd2, 0x24, 0xf2, 0x85, 0xc6, 0x7b, 0xee, 0x93, 0xc3, 0x0f, 0x8a, 0x30, 0x91, 0x57, 0xf0, 0xda, 0xa3, 0x5d, 0xc5, 0xb8, 0x7e, 0x41, 0x0b, 0x78, 0x63, 0x0a, 0x09, 0xcf, 0xc7, // SHA384 0x0c, 0x00, 0x1d, 0xd6, 0xf7, 0xb4, 0x57, 0xad, 0x88, 0x0d, 0x84, 0x0d, 0x41, 0xc9, 0x61, 0x28, 0x3b, 0xab, 0x68, 0x8e, 0x94, 0xe4, 0xb5, 0x93, 0x59, 0xea, 0x45, 0x68, 0x65, 0x81, 0xe9, 0x0f, 0xec, 0xce, 0xa3, 0xc6, 0x24, 0xb1, 0x22, 0x61, 0x13, 0xf8, 0x24, 0xf3, 0x15, 0xeb, 0x60, 0xae, 0x0a, 0x7c, // SHA512 0x0d, 0x00, 0x5e, 0xa7, 0x1d, 0xc6, 0xd0, 0xb4, 0xf5, 0x7b, 0xf3, 0x9a, 0xad, 0xd0, 0x7c, 0x20, 0x8c, 0x35, 0xf0, 0x6c, 0xd2, 0xba, 0xc5, 0xfd, 0xe2, 0x10, 0x39, 0x7f, 0x70, 0xde, 0x11, 0xd4, 0x39, 0xc6, 0x2e, 0xc1, 0xcd, 0xf3, 0x18, 0x37, 0x58, 0x86, 0x5f, 0xd3, 0x87, 0xfc, 0xea, 0x0b, 0xad, 0xa2, 0xf6, 0xc3, 0x7a, 0x4a, 0x17, 0x85, 0x1d, 0xd1, 0xd7, 0x8f, 0xef, 0xe6, 0xf2, 0x04, 0xee, 0x54, // Event size 0x02, 0x00, 0x00, 0x00, // Event data 0x00, 0x00, // Event 2 // PCR index 0x00, 0x00, 0x00, 0x00, // Event type 0x08, 0x00, 0x00, 0x80, // Digest count 0x04, 0x00, 0x00, 0x00, // SHA1 0x04, 0x00, 0xc7, 0x06, 0xe7, 0xdd, 0x36, 0x39, 0x29, 0x84, 0xeb, 0x06, 0xaa, 0xa0, 0x8f, 0xf3, 0x36, 0x84, 0x40, 0x77, 0xb3, 0xed, // SHA256 0x0b, 0x00, 0x3a, 0x30, 0x8e, 0x95, 0x87, 0x84, 0xbf, 0xd0, 0xf6, 0xe3, 0xf1, 0xbd, 0x4d, 0x42, 0x14, 0xd3, 0x0a, 0x4c, 0x55, 0x00, 0xa4, 0x5b, 0x06, 0xda, 0x96, 0xfc, 0x90, 0x33, 0x8f, 0x69, 0xb3, 0x61, // SHA384 0x0c, 0x00, 0xc0, 0xd0, 0x75, 0x96, 0xc5, 0x9a, 0x90, 0x7b, 0x79, 0x71, 0x6f, 0xc9, 0xf3, 0x6a, 0xad, 0x8f, 0x0f, 0x26, 0xf2, 0x02, 0x67, 0x5b, 0x42, 0x5a, 0x52, 0x3f, 0x72, 0xec, 0xb6, 0xf2, 0x53, 0x99, 0x57, 0xf0, 0xd9, 0x2c, 0x0a, 0x7d, 0xce, 0xaa, 0xf9, 0x9e, 0x60, 0x0e, 0x54, 0x18, 0xf1, 0xdc, // SHA512 0x0d, 0x00, 0x9a, 0xe9, 0x25, 0xdc, 0x9c, 0xd2, 0x9d, 0xf0, 0xe5, 0x80, 0x54, 0x35, 0xa5, 0x99, 0x06, 0x1f, 0xcf, 0x32, 0x98, 0xcc, 0x2a, 0x15, 0xe4, 0x87, 0x99, 0xa2, 0x0c, 0x9c, 0xe5, 0x6c, 0x8f, 0xe5, 0x84, 0x09, 0x75, 0xaf, 0xf0, 0xe1, 0xb6, 0x98, 0x20, 0x07, 0x5e, 0xe4, 0x29, 0x79, 0x8b, 0x5d, 0xbb, 0xe5, 0xd1, 0xa2, 0x74, 0x36, 0xab, 0x49, 0xf1, 0x9b, 0x7a, 0x04, 0x11, 0xd2, 0x96, 0x2c, // Event size 0x10, 0x00, 0x00, 0x00, // Event data 0x00, 0x00, 0x82, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, ]; let log = EventLog { _lifetime: PhantomData, location: bytes.as_ptr(), last_entry: unsafe { bytes.as_ptr().add(267) }, is_truncated: false, }; let header = log.header().unwrap(); assert_eq!(header.platform_class, 0); assert_eq!(header.spec_version, (2, 0, 0)); assert_eq!(header.uintn_size, 2); assert_eq!( header.algorithm_digest_sizes.0.to_vec(), [ AlgorithmDigestSize { algorithm_id: AlgorithmId::SHA1, digest_size: 20, }, AlgorithmDigestSize { algorithm_id: AlgorithmId::SHA256, digest_size: 32, }, AlgorithmDigestSize { algorithm_id: AlgorithmId::SHA384, digest_size: 48, }, AlgorithmDigestSize { algorithm_id: AlgorithmId::SHA512, digest_size: 64, }, ] ); assert_eq!(header.vendor_info, []); let mut iter = log.iter(); // Entry 1 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.digests().into_iter().collect::>(), [ (AlgorithmId::SHA1, [ 0x14, 0x89, 0xf9, 0x23, 0xc4, 0xdc, 0xa7, 0x29, 0x17, 0x8b, 0x3e, 0x32, 0x33, 0x45, 0x85, 0x50, 0xd8, 0xdd, 0xdf, 0x29, ].as_slice()), (AlgorithmId::SHA256, [ 0x96, 0xa2, 0x96, 0xd2, 0x24, 0xf2, 0x85, 0xc6, 0x7b, 0xee, 0x93, 0xc3, 0x0f, 0x8a, 0x30, 0x91, 0x57, 0xf0, 0xda, 0xa3, 0x5d, 0xc5, 0xb8, 0x7e, 0x41, 0x0b, 0x78, 0x63, 0x0a, 0x09, 0xcf, 0xc7, ].as_slice()), (AlgorithmId::SHA384, [ 0x1d, 0xd6, 0xf7, 0xb4, 0x57, 0xad, 0x88, 0x0d, 0x84, 0x0d, 0x41, 0xc9, 0x61, 0x28, 0x3b, 0xab, 0x68, 0x8e, 0x94, 0xe4, 0xb5, 0x93, 0x59, 0xea, 0x45, 0x68, 0x65, 0x81, 0xe9, 0x0f, 0xec, 0xce, 0xa3, 0xc6, 0x24, 0xb1, 0x22, 0x61, 0x13, 0xf8, 0x24, 0xf3, 0x15, 0xeb, 0x60, 0xae, 0x0a, 0x7c, ].as_slice()), (AlgorithmId::SHA512, [ 0x5e, 0xa7, 0x1d, 0xc6, 0xd0, 0xb4, 0xf5, 0x7b, 0xf3, 0x9a, 0xad, 0xd0, 0x7c, 0x20, 0x8c, 0x35, 0xf0, 0x6c, 0xd2, 0xba, 0xc5, 0xfd, 0xe2, 0x10, 0x39, 0x7f, 0x70, 0xde, 0x11, 0xd4, 0x39, 0xc6, 0x2e, 0xc1, 0xcd, 0xf3, 0x18, 0x37, 0x58, 0x86, 0x5f, 0xd3, 0x87, 0xfc, 0xea, 0x0b, 0xad, 0xa2, 0xf6, 0xc3, 0x7a, 0x4a, 0x17, 0x85, 0x1d, 0xd1, 0xd7, 0x8f, 0xef, 0xe6, 0xf2, 0x04, 0xee, 0x54, ].as_slice()), ] ); assert_eq!(entry.event_data, [0, 0]); // Entry 2 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.digests().into_iter().collect::>(), [ (AlgorithmId::SHA1, [ 0xc7, 0x06, 0xe7, 0xdd, 0x36, 0x39, 0x29, 0x84, 0xeb, 0x06, 0xaa, 0xa0, 0x8f, 0xf3, 0x36, 0x84, 0x40, 0x77, 0xb3, 0xed, ].as_slice()), (AlgorithmId::SHA256, [ 0x3a, 0x30, 0x8e, 0x95, 0x87, 0x84, 0xbf, 0xd0, 0xf6, 0xe3, 0xf1, 0xbd, 0x4d, 0x42, 0x14, 0xd3, 0x0a, 0x4c, 0x55, 0x00, 0xa4, 0x5b, 0x06, 0xda, 0x96, 0xfc, 0x90, 0x33, 0x8f, 0x69, 0xb3, 0x61, ].as_slice()), (AlgorithmId::SHA384, [ 0xc0, 0xd0, 0x75, 0x96, 0xc5, 0x9a, 0x90, 0x7b, 0x79, 0x71, 0x6f, 0xc9, 0xf3, 0x6a, 0xad, 0x8f, 0x0f, 0x26, 0xf2, 0x02, 0x67, 0x5b, 0x42, 0x5a, 0x52, 0x3f, 0x72, 0xec, 0xb6, 0xf2, 0x53, 0x99, 0x57, 0xf0, 0xd9, 0x2c, 0x0a, 0x7d, 0xce, 0xaa, 0xf9, 0x9e, 0x60, 0x0e, 0x54, 0x18, 0xf1, 0xdc, ].as_slice()), (AlgorithmId::SHA512, [ 0x9a, 0xe9, 0x25, 0xdc, 0x9c, 0xd2, 0x9d, 0xf0, 0xe5, 0x80, 0x54, 0x35, 0xa5, 0x99, 0x06, 0x1f, 0xcf, 0x32, 0x98, 0xcc, 0x2a, 0x15, 0xe4, 0x87, 0x99, 0xa2, 0x0c, 0x9c, 0xe5, 0x6c, 0x8f, 0xe5, 0x84, 0x09, 0x75, 0xaf, 0xf0, 0xe1, 0xb6, 0x98, 0x20, 0x07, 0x5e, 0xe4, 0x29, 0x79, 0x8b, 0x5d, 0xbb, 0xe5, 0xd1, 0xa2, 0x74, 0x36, 0xab, 0x49, 0xf1, 0x9b, 0x7a, 0x04, 0x11, 0xd2, 0x96, 0x2c, ].as_slice()), ] ); #[rustfmt::skip] assert_eq!(entry.event_data, [ 0x00, 0x00, 0x82, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, ]); assert!(iter.next().is_none()); } }