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