1 //! [TCG] (Trusted Computing Group) protocol for [TPM] (Trusted Platform 2 //! Module) 1.1 and 1.2. 3 //! 4 //! This protocol is defined in the [TCG EFI Protocol Specification _for 5 //! TPM Family 1.1 or 1.2_][spec]. 6 //! 7 //! [spec]: https://trustedcomputinggroup.org/resource/tcg-efi-protocol-specification/ 8 //! [TCG]: https://trustedcomputinggroup.org/ 9 //! [TPM]: https://en.wikipedia.org/wiki/Trusted_Platform_Module 10 11 use super::{AlgorithmId, EventType, HashAlgorithm, PcrIndex}; 12 use crate::data_types::{Align, PhysicalAddress}; 13 use crate::proto::unsafe_protocol; 14 use crate::util::{ptr_write_unaligned_and_add, usize_from_u32}; 15 use crate::{Error, Result, Status, StatusExt}; 16 use core::fmt::{self, Debug, Formatter}; 17 use core::marker::PhantomData; 18 use core::{mem, ptr}; 19 use ptr_meta::Pointee; 20 21 #[cfg(feature = "alloc")] 22 use {crate::mem::make_boxed, alloc::boxed::Box}; 23 24 #[cfg(all(feature = "unstable", feature = "alloc"))] 25 use alloc::alloc::Global; 26 27 /// 20-byte SHA-1 digest. 28 pub type Sha1Digest = [u8; 20]; 29 30 /// This corresponds to the `AlgorithmId` enum, but in the v1 spec it's `u32` 31 /// instead of `u16`. 32 #[allow(non_camel_case_types)] 33 type TCG_ALGORITHM_ID = u32; 34 35 /// Information about the protocol and the TPM device. 36 /// 37 /// Layout compatible with the C type `TCG_EFI_BOOT_SERVICE_CAPABILITY`. 38 #[repr(C)] 39 #[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Ord, PartialOrd)] 40 pub struct BootServiceCapability { 41 size: u8, 42 structure_version: Version, 43 protocol_spec_version: Version, 44 hash_algorithm_bitmap: u8, 45 tpm_present_flag: u8, 46 tpm_deactivated_flag: u8, 47 } 48 49 impl BootServiceCapability { 50 /// Version of the `BootServiceCapability` structure. 51 #[must_use] structure_version(&self) -> Version52 pub const fn structure_version(&self) -> Version { 53 self.structure_version 54 } 55 56 /// Version of the `Tcg` protocol. 57 #[must_use] protocol_spec_version(&self) -> Version58 pub const fn protocol_spec_version(&self) -> Version { 59 self.protocol_spec_version 60 } 61 62 /// Supported hash algorithms. 63 #[must_use] hash_algorithm(&self) -> HashAlgorithm64 pub fn hash_algorithm(&self) -> HashAlgorithm { 65 // Safety: the value should always be 0x1 (indicating SHA-1), but 66 // we don't care if it's some unexpected value. 67 HashAlgorithm::from_bits_retain(u32::from(self.hash_algorithm_bitmap)) 68 } 69 70 /// Whether the TPM device is present. 71 #[must_use] tpm_present(&self) -> bool72 pub const fn tpm_present(&self) -> bool { 73 self.tpm_present_flag != 0 74 } 75 76 /// Whether the TPM device is deactivated. 77 #[must_use] tpm_deactivated(&self) -> bool78 pub const fn tpm_deactivated(&self) -> bool { 79 self.tpm_deactivated_flag != 0 80 } 81 } 82 83 /// Version information. 84 /// 85 /// Layout compatible with the C type `TCG_VERSION`. 86 #[repr(C)] 87 #[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Ord, PartialOrd)] 88 pub struct Version { 89 /// Major version. 90 pub major: u8, 91 /// Minor version. 92 pub minor: u8, 93 94 // Leave these two fields undocumented since it's not clear what 95 // they are for. The spec doesn't say, and they were removed in the 96 // v2 spec. 97 #[allow(missing_docs)] 98 pub rev_major: u8, 99 #[allow(missing_docs)] 100 pub rev_minor: u8, 101 } 102 103 /// Entry in the [`EventLog`]. 104 /// 105 /// Layout compatible with the C type `TCG_PCR_EVENT`. 106 /// 107 /// Naming note: the spec refers to "event data" in two conflicting 108 /// ways: the `event_data` field and the data hashed in the digest 109 /// field. These two are independent; although the event data _can_ be 110 /// what is hashed in the digest field, it doesn't have to be. 111 #[repr(C, packed)] 112 #[derive(Eq, Pointee)] 113 pub struct PcrEvent { 114 pcr_index: PcrIndex, 115 event_type: EventType, 116 digest: Sha1Digest, 117 event_data_size: u32, 118 event_data: [u8], 119 } 120 121 impl PcrEvent { from_ptr<'a>(ptr: *const u8) -> &'a Self122 pub(super) unsafe fn from_ptr<'a>(ptr: *const u8) -> &'a Self { 123 // Get the `event_size` field. 124 let ptr_u32: *const u32 = ptr.cast(); 125 let event_size = ptr_u32.add(7).read_unaligned(); 126 let event_size = usize_from_u32(event_size); 127 unsafe { &*ptr_meta::from_raw_parts(ptr.cast(), event_size) } 128 } 129 130 /// Create a new `PcrEvent` using a byte buffer for storage. 131 /// 132 /// # Errors 133 /// 134 /// Returns [`Status::BUFFER_TOO_SMALL`] if the `buffer` is not large 135 /// enough. The required size will be returned in the error data. 136 /// 137 /// Returns [`Status::INVALID_PARAMETER`] if the `event_data` size is too 138 /// large. 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>>139 pub fn new_in_buffer<'buf>( 140 buffer: &'buf mut [u8], 141 pcr_index: PcrIndex, 142 event_type: EventType, 143 digest: Sha1Digest, 144 event_data: &[u8], 145 ) -> Result<&'buf mut Self, Option<usize>> { 146 let event_data_size = u32::try_from(event_data.len()) 147 .map_err(|_| Error::new(Status::INVALID_PARAMETER, None))?; 148 149 let required_size = mem::size_of::<PcrIndex>() 150 + mem::size_of::<EventType>() 151 + mem::size_of::<Sha1Digest>() 152 + mem::size_of::<u32>() 153 + event_data.len(); 154 155 if buffer.len() < required_size { 156 return Err(Error::new(Status::BUFFER_TOO_SMALL, Some(required_size))); 157 } 158 159 let mut ptr: *mut u8 = buffer.as_mut_ptr().cast(); 160 161 unsafe { 162 ptr_write_unaligned_and_add(&mut ptr, pcr_index); 163 ptr_write_unaligned_and_add(&mut ptr, event_type); 164 ptr_write_unaligned_and_add(&mut ptr, digest); 165 ptr_write_unaligned_and_add(&mut ptr, event_data_size); 166 ptr::copy(event_data.as_ptr(), ptr, event_data.len()); 167 168 let ptr: *mut Self = 169 ptr_meta::from_raw_parts_mut(buffer.as_mut_ptr().cast(), event_data.len()); 170 Ok(&mut *ptr) 171 } 172 } 173 174 /// Create a new `PcrEvent` in a [`Box`]. 175 /// 176 /// # Errors 177 /// 178 /// Returns [`Status::INVALID_PARAMETER`] if the `event_data` size is too 179 /// large. 180 #[cfg(feature = "alloc")] new_in_box( pcr_index: PcrIndex, event_type: EventType, digest: Sha1Digest, event_data: &[u8], ) -> Result<Box<Self>>181 pub fn new_in_box( 182 pcr_index: PcrIndex, 183 event_type: EventType, 184 digest: Sha1Digest, 185 event_data: &[u8], 186 ) -> Result<Box<Self>> { 187 #[cfg(not(feature = "unstable"))] 188 { 189 make_boxed(|buf| Self::new_in_buffer(buf, pcr_index, event_type, digest, event_data)) 190 } 191 #[cfg(feature = "unstable")] 192 { 193 make_boxed( 194 |buf| Self::new_in_buffer(buf, pcr_index, event_type, digest, event_data), 195 Global, 196 ) 197 } 198 } 199 200 /// PCR index for the event. 201 #[must_use] pcr_index(&self) -> PcrIndex202 pub const fn pcr_index(&self) -> PcrIndex { 203 self.pcr_index 204 } 205 206 /// Type of event, indicating what type of data is stored in [`event_data`]. 207 /// 208 /// [`event_data`]: Self::event_data 209 #[must_use] event_type(&self) -> EventType210 pub const fn event_type(&self) -> EventType { 211 self.event_type 212 } 213 214 /// Raw event data. The meaning of this data can be determined from 215 /// the [`event_type`]. 216 /// 217 /// Note that this data is independent of what is hashed [`digest`]. 218 /// 219 /// [`digest`]: Self::digest 220 /// [`event_type`]: Self::event_type 221 #[must_use] event_data(&self) -> &[u8]222 pub const fn event_data(&self) -> &[u8] { 223 &self.event_data 224 } 225 226 /// SHA-1 digest of the data hashed for this event. 227 #[must_use] digest(&self) -> Sha1Digest228 pub const fn digest(&self) -> Sha1Digest { 229 self.digest 230 } 231 } 232 233 impl Align for PcrEvent { alignment() -> usize234 fn alignment() -> usize { 235 1 236 } 237 } 238 239 // Manual `Debug` implementation since it can't be derived for a packed DST. 240 impl Debug for PcrEvent { fmt(&self, f: &mut Formatter<'_>) -> fmt::Result241 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { 242 f.debug_struct("PcrEvent") 243 .field("pcr_index", &{ self.pcr_index }) 244 .field("event_type", &{ self.event_type }) 245 .field("digest", &self.digest) 246 .field("event_data_size", &{ self.event_data_size }) 247 .field("event_data", &&self.event_data) 248 .finish() 249 } 250 } 251 252 // Manual `PartialEq` implementation since it can't be derived for a packed DST. 253 impl PartialEq for PcrEvent { eq(&self, rhs: &Self) -> bool254 fn eq(&self, rhs: &Self) -> bool { 255 self.pcr_index() == rhs.pcr_index() 256 && self.event_type() == rhs.event_type() 257 && self.digest == rhs.digest 258 && self.event_data_size == rhs.event_data_size 259 && self.event_data == rhs.event_data 260 } 261 } 262 263 opaque_type! { 264 /// Opaque type that should be used to represent a pointer to a [`PcrEvent`] in 265 /// foreign function interfaces. This type produces a thin pointer, unlike 266 /// [`PcrEvent`]. 267 pub struct FfiPcrEvent; 268 } 269 270 /// TPM event log. 271 /// 272 /// This type of event log always uses SHA-1 hashes. The [`v1::Tcg`] 273 /// protocol always uses this type of event log, but it can also be 274 /// provided by the [`v2::Tcg`] protocol via [`get_event_log_v2`]. 275 /// 276 /// [`v1::Tcg`]: Tcg 277 /// [`v2::Tcg`]: super::v2::Tcg 278 /// [`get_event_log_v2`]: super::v2::Tcg::get_event_log_v2 279 #[derive(Debug)] 280 pub struct EventLog<'a> { 281 // Tie the lifetime to the protocol, and by extension, boot services. 282 _lifetime: PhantomData<&'a Tcg>, 283 284 location: *const u8, 285 last_entry: *const u8, 286 287 is_truncated: bool, 288 } 289 290 impl<'a> EventLog<'a> { new( location: *const u8, last_entry: *const u8, is_truncated: bool, ) -> Self291 pub(super) const unsafe fn new( 292 location: *const u8, 293 last_entry: *const u8, 294 is_truncated: bool, 295 ) -> Self { 296 Self { 297 _lifetime: PhantomData, 298 location, 299 last_entry, 300 is_truncated, 301 } 302 } 303 304 /// Iterator of events in the log. 305 #[must_use] iter(&self) -> EventLogIter306 pub const fn iter(&self) -> EventLogIter { 307 EventLogIter { 308 log: self, 309 location: self.location, 310 } 311 } 312 313 /// If true, the event log is missing one or more entries because 314 /// additional events would have exceeded the space allocated for 315 /// the log. 316 /// 317 /// This value is not reported for the [`v1::Tcg`] protocol, so it 318 /// is always `false` in that case. 319 /// 320 /// [`v1::Tcg`]: Tcg 321 #[must_use] is_truncated(&self) -> bool322 pub const fn is_truncated(&self) -> bool { 323 self.is_truncated 324 } 325 } 326 327 /// Iterator for events in [`EventLog`]. 328 #[derive(Debug)] 329 pub struct EventLogIter<'a> { 330 log: &'a EventLog<'a>, 331 location: *const u8, 332 } 333 334 impl<'a> Iterator for EventLogIter<'a> { 335 type Item = &'a PcrEvent; 336 next(&mut self) -> Option<Self::Item>337 fn next(&mut self) -> Option<Self::Item> { 338 // The spec says that `last_entry` will be null if there are no 339 // events. Presumably `location` will be null as well, but check 340 // both just to be safe. 341 if self.location.is_null() || self.log.last_entry.is_null() { 342 return None; 343 } 344 345 // Safety: we trust that the protocol has given us a valid range 346 // of memory to read from. 347 let event = unsafe { PcrEvent::from_ptr(self.location) }; 348 349 // If this is the last entry, set the location to null so that 350 // future calls to `next()` return `None`. 351 if self.location == self.log.last_entry { 352 self.location = ptr::null(); 353 } else { 354 self.location = unsafe { self.location.add(mem::size_of_val(event)) }; 355 } 356 357 Some(event) 358 } 359 } 360 361 /// Protocol for interacting with TPM 1.1 and 1.2 devices. 362 /// 363 /// The corresponding C type is `EFI_TCG_PROTOCOL`. 364 #[derive(Debug)] 365 #[repr(C)] 366 #[unsafe_protocol("f541796d-a62e-4954-a775-9584f61b9cdd")] 367 pub struct Tcg { 368 status_check: unsafe extern "efiapi" fn( 369 this: *mut Tcg, 370 protocol_capability: *mut BootServiceCapability, 371 feature_flags: *mut u32, 372 event_log_location: *mut PhysicalAddress, 373 event_log_last_entry: *mut PhysicalAddress, 374 ) -> Status, 375 376 // Note: we do not currently expose this function because the spec 377 // for this is not well written. The function allocates memory, but 378 // the spec doesn't say how to free it. Most likely 379 // `EFI_BOOT_SERVICES.FreePool` would work, but this is not 380 // mentioned in the spec so it is unsafe to rely on. 381 // 382 // Also, this function is not that useful in practice for a couple 383 // reasons. First, it takes an algorithm ID, but only SHA-1 is 384 // supported with TPM v1. Second, TPMs are not cryptographic 385 // accelerators, so it is very likely faster to calculate the hash 386 // on the CPU, e.g. with the `sha1` crate. 387 hash_all: unsafe extern "efiapi" fn() -> Status, 388 389 log_event: unsafe extern "efiapi" fn( 390 this: *mut Tcg, 391 // The spec does not guarantee that the `event` will not be mutated 392 // through the pointer, but it seems reasonable to assume and makes the 393 // public interface clearer, so use a const pointer. 394 event: *const FfiPcrEvent, 395 event_number: *mut u32, 396 flags: u32, 397 ) -> Status, 398 399 pass_through_to_tpm: unsafe extern "efiapi" fn( 400 this: *mut Tcg, 401 tpm_input_parameter_block_size: u32, 402 tpm_input_parameter_block: *const u8, 403 tpm_output_parameter_block_size: u32, 404 tpm_output_parameter_block: *mut u8, 405 ) -> Status, 406 407 hash_log_extend_event: unsafe extern "efiapi" fn( 408 this: *mut Tcg, 409 hash_data: PhysicalAddress, 410 hash_data_len: u64, 411 algorithm_id: TCG_ALGORITHM_ID, 412 event: *mut FfiPcrEvent, 413 event_number: *mut u32, 414 event_log_last_entry: *mut PhysicalAddress, 415 ) -> Status, 416 } 417 418 /// Return type of [`Tcg::status_check`]. 419 #[derive(Debug)] 420 pub struct StatusCheck<'a> { 421 /// Information about the protocol and the TPM device. 422 pub protocol_capability: BootServiceCapability, 423 424 /// Feature flags. The spec does not define any feature flags, so 425 /// this is always expected to be zero. 426 pub feature_flags: u32, 427 428 /// TPM event log. 429 pub event_log: EventLog<'a>, 430 } 431 432 impl Tcg { 433 /// Get information about the protocol and TPM device, as well as 434 /// the TPM event log. status_check(&mut self) -> Result<StatusCheck>435 pub fn status_check(&mut self) -> Result<StatusCheck> { 436 let mut protocol_capability = BootServiceCapability::default(); 437 let mut feature_flags = 0; 438 let mut event_log_location = 0; 439 let mut event_log_last_entry = 0; 440 441 let status = unsafe { 442 (self.status_check)( 443 self, 444 &mut protocol_capability, 445 &mut feature_flags, 446 &mut event_log_location, 447 &mut event_log_last_entry, 448 ) 449 }; 450 451 if status.is_success() { 452 // The truncated field is just there for the v2 protocol; 453 // always set it to false for v1. 454 let truncated = false; 455 let event_log = unsafe { 456 EventLog::new( 457 event_log_location as *const u8, 458 event_log_last_entry as *const u8, 459 truncated, 460 ) 461 }; 462 463 Ok(StatusCheck { 464 protocol_capability, 465 feature_flags, 466 event_log, 467 }) 468 } else { 469 Err(status.into()) 470 } 471 } 472 473 /// Add an entry to the event log without extending a PCR. 474 /// 475 /// Usually [`hash_log_extend_event`] should be used instead. An 476 /// entry added via `log_event` cannot be verified, so it is mainly 477 /// intended for adding an informational entry. 478 /// 479 /// [`hash_log_extend_event`]: Self::hash_log_extend_event log_event(&mut self, event: &PcrEvent) -> Result480 pub fn log_event(&mut self, event: &PcrEvent) -> Result { 481 // This is the only valid value; it indicates that the extend 482 // operation should not be performed. 483 let flags = 0x1; 484 485 // Don't bother returning this, it's not very useful info. 486 let mut event_number = 0; 487 488 let event_ptr: *const PcrEvent = event; 489 490 unsafe { (self.log_event)(self, event_ptr.cast(), &mut event_number, flags).to_result() } 491 } 492 493 /// Extend a PCR and add an entry to the event log. 494 /// 495 /// If `data_to_hash` is `None` then the `digest` field of the `event` 496 /// should be used as-is. Otherwise, the `digest` field will be overwritten 497 /// with the SHA-1 hash of the data. hash_log_extend_event( &mut self, event: &mut PcrEvent, data_to_hash: Option<&[u8]>, ) -> Result498 pub fn hash_log_extend_event( 499 &mut self, 500 event: &mut PcrEvent, 501 data_to_hash: Option<&[u8]>, 502 ) -> Result { 503 let hash_data; 504 let hash_data_len; 505 if let Some(data_to_hash) = data_to_hash { 506 hash_data = data_to_hash.as_ptr() as PhysicalAddress; 507 hash_data_len = u64::try_from(data_to_hash.len()).unwrap(); 508 } else { 509 hash_data = 0; 510 hash_data_len = 0; 511 } 512 513 // Don't bother returning these, it's not very useful info. 514 let mut event_number = 0; 515 let mut event_log_last_entry = 0; 516 517 let event_ptr: *mut PcrEvent = event; 518 519 unsafe { 520 (self.hash_log_extend_event)( 521 self, 522 hash_data, 523 hash_data_len, 524 AlgorithmId::SHA1.0.into(), 525 event_ptr.cast(), 526 &mut event_number, 527 &mut event_log_last_entry, 528 ) 529 .to_result() 530 } 531 } 532 533 /// Send a command directly to the TPM. 534 /// 535 /// Constructing the input block and parsing the output block are outside 536 /// the scope of this crate. See the [TPM 1.2 Main Specification][spec] 537 /// documents for details of these blocks, in particular Part 3, Commands. 538 /// 539 /// Note that TPM structures are big endian. 540 /// 541 /// [spec]: https://trustedcomputinggroup.org/resource/tpm-main-specification/ pass_through_to_tpm( &mut self, input_parameter_block: &[u8], output_parameter_block: &mut [u8], ) -> Result542 pub fn pass_through_to_tpm( 543 &mut self, 544 input_parameter_block: &[u8], 545 output_parameter_block: &mut [u8], 546 ) -> Result { 547 let input_parameter_block_len = u32::try_from(input_parameter_block.len()) 548 .map_err(|_| Error::from(Status::BAD_BUFFER_SIZE))?; 549 let output_parameter_block_len = u32::try_from(output_parameter_block.len()) 550 .map_err(|_| Error::from(Status::BAD_BUFFER_SIZE))?; 551 552 unsafe { 553 (self.pass_through_to_tpm)( 554 self, 555 input_parameter_block_len, 556 input_parameter_block.as_ptr(), 557 output_parameter_block_len, 558 output_parameter_block.as_mut_ptr(), 559 ) 560 .to_result() 561 } 562 } 563 } 564 565 #[cfg(test)] 566 mod tests { 567 use super::*; 568 use core::slice; 569 570 #[test] test_new_pcr_event()571 fn test_new_pcr_event() { 572 let mut event_buf = [0; 256]; 573 #[rustfmt::skip] 574 let digest = [ 575 0x00, 0x01, 0x02, 0x03, 576 0x04, 0x05, 0x06, 0x07, 577 0x08, 0x09, 0x0a, 0x0b, 578 0x0c, 0x0d, 0x0e, 0x0f, 579 0x10, 0x11, 0x12, 0x13, 580 ]; 581 let data = [0x14, 0x15, 0x16, 0x17]; 582 let event = 583 PcrEvent::new_in_buffer(&mut event_buf, PcrIndex(4), EventType::IPL, digest, &data) 584 .unwrap(); 585 assert_eq!(event.pcr_index(), PcrIndex(4)); 586 assert_eq!(event.event_type(), EventType::IPL); 587 assert_eq!(event.digest(), digest); 588 assert_eq!(event.event_data(), data); 589 590 let event_ptr: *const PcrEvent = event; 591 let bytes = 592 unsafe { slice::from_raw_parts(event_ptr.cast::<u8>(), mem::size_of_val(event)) }; 593 #[rustfmt::skip] 594 assert_eq!(bytes, [ 595 // PCR index 596 0x04, 0x00, 0x00, 0x00, 597 // Event type 598 0x0d, 0x00, 0x00, 0x00, 599 // Digest 600 0x00, 0x01, 0x02, 0x03, 601 0x04, 0x05, 0x06, 0x07, 602 0x08, 0x09, 0x0a, 0x0b, 603 0x0c, 0x0d, 0x0e, 0x0f, 604 0x10, 0x11, 0x12, 0x13, 605 // Event data len 606 0x04, 0x00, 0x00, 0x00, 607 // Event data 608 0x14, 0x15, 0x16, 0x17, 609 ]); 610 611 // Check that `new_in_box` gives the same value. 612 assert_eq!( 613 event, 614 &*PcrEvent::new_in_box(PcrIndex(4), EventType::IPL, digest, &data).unwrap() 615 ); 616 } 617 618 #[test] test_event_log_v1()619 fn test_event_log_v1() { 620 // This data comes from dumping the TPM event log in a VM 621 // (truncated to just two entries). 622 #[rustfmt::skip] 623 let bytes = [ 624 // Event 1 625 // PCR index 626 0x00, 0x00, 0x00, 0x00, 627 // Event type 628 0x08, 0x00, 0x00, 0x00, 629 // SHA1 digest 630 0x14, 0x89, 0xf9, 0x23, 0xc4, 0xdc, 0xa7, 0x29, 0x17, 0x8b, 631 0x3e, 0x32, 0x33, 0x45, 0x85, 0x50, 0xd8, 0xdd, 0xdf, 0x29, 632 // Event data size 633 0x02, 0x00, 0x00, 0x00, 634 // Event data 635 0x00, 0x00, 636 637 // Event 2 638 // PCR index 639 0x00, 0x00, 0x00, 0x00, 640 // Event type 641 0x08, 0x00, 0x00, 0x80, 642 // SHA1 digest 643 0xc7, 0x06, 0xe7, 0xdd, 0x36, 0x39, 0x29, 0x84, 0xeb, 0x06, 644 0xaa, 0xa0, 0x8f, 0xf3, 0x36, 0x84, 0x40, 0x77, 0xb3, 0xed, 645 // Event data size 646 0x10, 0x00, 0x00, 0x00, 647 // Event data 648 0x00, 0x00, 0x82, 0x00, 0x00, 0x00, 0x00, 0x00, 649 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 650 ]; 651 652 let log = unsafe { EventLog::new(bytes.as_ptr(), bytes.as_ptr().add(34), false) }; 653 let mut iter = log.iter(); 654 655 // Entry 1 656 let entry = iter.next().unwrap(); 657 assert_eq!(entry.pcr_index(), PcrIndex(0)); 658 assert_eq!(entry.event_type(), EventType::CRTM_VERSION); 659 #[rustfmt::skip] 660 assert_eq!( 661 entry.digest(), 662 [ 663 0x14, 0x89, 0xf9, 0x23, 0xc4, 0xdc, 0xa7, 0x29, 0x17, 0x8b, 664 0x3e, 0x32, 0x33, 0x45, 0x85, 0x50, 0xd8, 0xdd, 0xdf, 0x29, 665 ] 666 ); 667 assert_eq!(entry.event_data(), [0x00, 0x00]); 668 669 // Entry 2 670 let entry = iter.next().unwrap(); 671 assert_eq!(entry.pcr_index(), PcrIndex(0)); 672 assert_eq!(entry.event_type(), EventType::EFI_PLATFORM_FIRMWARE_BLOB); 673 #[rustfmt::skip] 674 assert_eq!( 675 entry.digest(), 676 [ 677 0xc7, 0x06, 0xe7, 0xdd, 0x36, 0x39, 0x29, 0x84, 0xeb, 0x06, 678 0xaa, 0xa0, 0x8f, 0xf3, 0x36, 0x84, 0x40, 0x77, 0xb3, 0xed, 679 ] 680 ); 681 #[rustfmt::skip] 682 assert_eq!( 683 entry.event_data(), 684 [ 685 0x00, 0x00, 0x82, 0x00, 0x00, 0x00, 0x00, 0x00, 686 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 687 ] 688 ); 689 } 690 } 691