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