1//! [TCG] (Trusted Computing Group) protocol for [TPM] (Trusted Platform
2//! Module) 2.0.
3//!
4//! This protocol is defined in the [TCG EFI Protocol Specification _TPM
5//! Family 2.0_][spec]. It is generally implemented only for TPM 2.0
6//! devices, but the spec indicates it can also be used for older TPM
7//! devices.
8//!
9//! [spec]: https://trustedcomputinggroup.org/resource/tcg-efi-protocol-specification/
10//! [TCG]: https://trustedcomputinggroup.org/
11//! [TPM]: https://en.wikipedia.org/wiki/Trusted_Platform_Module
12
13use super::{v1, AlgorithmId, EventType, HashAlgorithm, PcrIndex};
14use crate::data_types::{PhysicalAddress, UnalignedSlice};
15use crate::proto::unsafe_protocol;
16use crate::util::{ptr_write_unaligned_and_add, usize_from_u32};
17use crate::{Error, Result, Status};
18use bitflags::bitflags;
19use core::fmt::{self, Debug, Formatter};
20use core::marker::PhantomData;
21use core::mem::MaybeUninit;
22use core::{mem, ptr, slice};
23use ptr_meta::{Pointee, PtrExt};
24
25/// Version information.
26///
27/// Layout compatible with the C type `EFI_TG2_VERSION`.
28#[repr(C)]
29#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Ord, PartialOrd)]
30pub struct Version {
31 /// Major version.
32 pub major: u8,
33 /// Minor version.
34 pub minor: u8,
35}
36
37bitflags! {
38 /// Event log formats supported by the firmware.
39 ///
40 /// Corresponds to the C typedef `EFI_TCG2_EVENT_ALGORITHM_BITMAP`.
41 #[derive(Default)]
42 #[repr(transparent)]
43 pub struct EventLogFormat: u32 {
44 /// Firmware supports the SHA-1 log format.
45 const TCG_1_2 = 0x0000_0001;
46
47 /// Firmware supports the crypto-agile log format.
48 const TCG_2 = 0x0000_0002;
49 }
50}
51
52/// Information about the protocol and the TPM device.
53///
54/// Layout compatible with the C type `EFI_TCG2_BOOT_SERVICE_CAPABILITY`.
55#[repr(C)]
56#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd)]
57pub struct BootServiceCapability {
58 size: u8,
59
60 /// Version of the EFI TCG2 protocol.
61 pub structure_version: Version,
62
63 /// Version of the EFI TCG2 protocol.
64 pub protocol_version: Version,
65
66 /// Bitmap of supported hash algorithms.
67 pub hash_algorithm_bitmap: HashAlgorithm,
68
69 /// Event log formats supported by the firmware.
70 pub supported_event_logs: EventLogFormat,
71
72 present_flag: u8,
73
74 /// Maximum size (in bytes) of a command that can be sent to the TPM.
75 pub max_command_size: u16,
76
77 /// Maximum size (in bytes) of a response that can be provided by the TPM.
78 pub max_response_size: u16,
79
80 /// Manufacturer ID.
81 ///
82 /// See the [TCG Vendor ID registry].
83 ///
84 /// [TCG Vendor ID registry]: https://trustedcomputinggroup.org/resource/vendor-id-registry/
85 pub manufacturer_id: u32,
86
87 /// Maximum number of supported PCR banks (hashing algorithms).
88 pub number_of_pcr_banks: u32,
89
90 /// Bitmap of currently-active PCR banks (hashing algorithms). This
91 /// is a subset of the supported algorithms in [`hash_algorithm_bitmap`].
92 ///
93 /// [`hash_algorithm_bitmap`]: Self::hash_algorithm_bitmap
94 pub active_pcr_banks: HashAlgorithm,
95}
96
97impl Default for BootServiceCapability {
98 fn default() -> Self {
99 // OK to unwrap, the size is less than u8.
100 let struct_size: u8 = u8::try_from(mem::size_of::<BootServiceCapability>()).unwrap();
101
102 Self {
103 size: struct_size,
104 structure_version: Version::default(),
105 protocol_version: Version::default(),
106 hash_algorithm_bitmap: HashAlgorithm::default(),
107 supported_event_logs: EventLogFormat::default(),
108 present_flag: 0,
109 max_command_size: 0,
110 max_response_size: 0,
111 manufacturer_id: 0,
112 number_of_pcr_banks: 0,
113 active_pcr_banks: HashAlgorithm::default(),
114 }
115 }
116}
117
118impl BootServiceCapability {
119 /// Whether the TPM device is present.
120 #[must_use]
121 pub fn tpm_present(&self) -> bool {
122 self.present_flag != 0
123 }
124}
125
126bitflags! {
127 /// Flags for the [`Tcg::hash_log_extend_event`] function.
128 #[derive(Default)]
129 #[repr(transparent)]
130 pub struct HashLogExtendEventFlags: u64 {
131 /// Extend an event but don't log it.
132 const EFI_TCG2_EXTEND_ONLY = 0x0000_0000_0000_0001;
133
134 /// Use when measuring a PE/COFF image.
135 const PE_COFF_IMAGE = 0x0000_0000_0000_0010;
136 }
137}
138
139/// Header used in [`PcrEventInputs`].
140///
141/// Layout compatible with the C type `EFI_TCG2_EVENT_HEADER`.
142#[derive(Clone, Copy, Debug, Eq, PartialEq)]
143#[repr(C, packed)]
144struct EventHeader {
145 header_size: u32,
146 header_version: u16,
147 pcr_index: PcrIndex,
148 event_type: EventType,
149}
150
151/// Event type passed to [`Tcg::hash_log_extend_event`].
152///
153/// Layout compatible with the C type `EFI_TCG2_EVENT`.
154///
155/// The TPM v1 spec uses a single generic event type for both creating a
156/// new event and reading an event from the log. The v2 spec splits this
157/// into two structs: `EFI_TCG2_EVENT` for creating events, and
158/// `TCG_PCR_EVENT2` for reading events. To help clarify the usage, our
159/// API renames these types to `PcrEventInputs` and `PcrEvent`,
160/// respectively.
161#[derive(Pointee)]
162#[repr(C, packed)]
163pub struct PcrEventInputs {
164 size: u32,
165 event_header: EventHeader,
166 event: [u8],
167}
168
169impl PcrEventInputs {
170 /// Create a new `PcrEventInputs` using a byte buffer for storage.
171 ///
172 /// # Errors
173 ///
174 /// Returns [`Status::BUFFER_TOO_SMALL`] if the `buffer` is not large
175 /// enough.
176 ///
177 /// Returns [`Status::INVALID_PARAMETER`] if the `event_data` size is too
178 /// large.
179 pub fn new_in_buffer<'buf>(
180 buffer: &'buf mut [MaybeUninit<u8>],
181 pcr_index: PcrIndex,
182 event_type: EventType,
183 event_data: &[u8],
184 ) -> Result<&'buf Self> {
185 let required_size =
186 mem::size_of::<u32>() + mem::size_of::<EventHeader>() + event_data.len();
187
188 if buffer.len() < required_size {
189 return Err(Status::BUFFER_TOO_SMALL.into());
190 }
191 let size_field =
192 u32::try_from(required_size).map_err(|_| Error::from(Status::INVALID_PARAMETER))?;
193
194 let mut ptr: *mut u8 = buffer.as_mut_ptr().cast();
195
196 unsafe {
197 ptr_write_unaligned_and_add(&mut ptr, size_field);
198 ptr_write_unaligned_and_add(
199 &mut ptr,
200 EventHeader {
201 header_size: u32::try_from(mem::size_of::<EventHeader>()).unwrap(),
202 header_version: 1,
203 pcr_index,
204 event_type,
205 },
206 );
207 ptr::copy(event_data.as_ptr(), ptr, event_data.len());
208
209 let ptr: *const PcrEventInputs =
210 ptr_meta::from_raw_parts(buffer.as_ptr().cast(), event_data.len());
211 Ok(&*ptr)
212 }
213 }
214}
215
216#[repr(C, packed)]
217#[derive(Clone, Copy, Debug, PartialEq, Eq)]
218struct AlgorithmDigestSize {
219 algorithm_id: AlgorithmId,
220 digest_size: u16,
221}
222
223#[derive(Clone, Debug)]
224struct AlgorithmDigestSizes<'a>(UnalignedSlice<'a, AlgorithmDigestSize>);
225
226impl<'a> AlgorithmDigestSizes<'a> {
227 fn get_size(&self, alg: AlgorithmId) -> Option<u16> {
228 self.0.iter().find_map(|elem: AlgorithmDigestSize| {
229 if { elem.algorithm_id } == alg {
230 Some(elem.digest_size)
231 } else {
232 None
233 }
234 })
235 }
236}
237
238fn u32_le_from_bytes_at_offset(bytes: &[u8], offset: usize) -> Option<u32> {
239 let bytes: &[u8] = bytes.get(index:offset..offset + 4)?;
240 // OK to unwrap: we know `bytes` is now of length 4.
241 let val: u32 = u32::from_le_bytes(bytes.try_into().unwrap());
242 Some(val)
243}
244
245/// Header stored at the beginning of the event log.
246///
247/// Layout compatible with the C type `TCG_EfiSpecIDEventStruct`.
248#[derive(Clone, Debug)]
249#[allow(unused)] // We don't current access most of the fields.
250struct EventLogHeader<'a> {
251 platform_class: u32,
252 // major, minor, errata
253 spec_version: (u8, u8, u8),
254 uintn_size: u8,
255 algorithm_digest_sizes: AlgorithmDigestSizes<'a>,
256 vendor_info: &'a [u8],
257 // Size of the whole header event, in bytes.
258 size_in_bytes: usize,
259}
260
261impl<'a> EventLogHeader<'a> {
262 fn new(event: &'a v1::PcrEvent) -> Option<Self> {
263 if event.pcr_index() != PcrIndex(0) {
264 return None;
265 }
266 if { event.event_type() } != EventType::NO_ACTION {
267 return None;
268 }
269 if event.digest() != [0; 20] {
270 return None;
271 }
272
273 let event = &event.event_data();
274 if event.get(..16)? != *b"Spec ID Event03\0" {
275 return None;
276 }
277 let platform_class = u32_le_from_bytes_at_offset(event, 16)?;
278 let version_minor = *event.get(20)?;
279 let version_major = *event.get(21)?;
280 let version_errata = *event.get(22)?;
281 let uintn_size = *event.get(23)?;
282 let number_of_algorithms = usize_from_u32(u32_le_from_bytes_at_offset(event, 24)?);
283 let vendor_info_size_byte_offset =
284 28 + (number_of_algorithms * mem::size_of::<AlgorithmDigestSize>());
285 let vendor_info_size = usize::from(*event.get(vendor_info_size_byte_offset)?);
286
287 // Safety: we know the slice is big enough because we just
288 // safely got the field after the slice (`vendor_info_size`).
289 let algorithm_digest_sizes = unsafe {
290 let ptr: *const AlgorithmDigestSize = event.as_ptr().add(28).cast();
291 AlgorithmDigestSizes(UnalignedSlice::new(ptr, number_of_algorithms))
292 };
293
294 let vendor_info_byte_offset = vendor_info_size_byte_offset + 1;
295 let vendor_info =
296 event.get(vendor_info_byte_offset..vendor_info_byte_offset + vendor_info_size)?;
297
298 // 32 is the size of PcrEventV1 excluding the event data.
299 let size_in_bytes = 32 + vendor_info_byte_offset + vendor_info_size;
300
301 Some(Self {
302 platform_class,
303 spec_version: (version_major, version_minor, version_errata),
304 uintn_size,
305 algorithm_digest_sizes,
306 vendor_info,
307 size_in_bytes,
308 })
309 }
310}
311
312/// TPM event log as returned by [`Tcg::get_event_log_v2`].
313///
314/// This type of event log can contain multiple hash types (e.g. SHA-1, SHA-256,
315/// SHA-512, etc).
316pub struct EventLog<'a> {
317 // Tie the lifetime to the protocol, and by extension, boot services.
318 _lifetime: PhantomData<&'a Tcg>,
319
320 location: *const u8,
321 last_entry: *const u8,
322
323 is_truncated: bool,
324}
325
326impl<'a> EventLog<'a> {
327 /// Iterator of events in the log.
328 #[must_use]
329 pub fn iter(&self) -> EventLogIter {
330 if let Some(header) = self.header() {
331 // Advance past the header
332 let location = unsafe { self.location.add(header.size_in_bytes) };
333
334 EventLogIter {
335 log: self,
336 location,
337 header: self.header(),
338 }
339 } else {
340 EventLogIter {
341 log: self,
342 location: ptr::null(),
343 header: None,
344 }
345 }
346 }
347
348 /// Header at the beginning of the event log.
349 fn header(&self) -> Option<EventLogHeader> {
350 // The spec is unclear if the header is present when there are
351 // no entries, so lets assume that `self.location` will be null
352 // if there's no header, and otherwise valid.
353 if self.location.is_null() {
354 None
355 } else {
356 // Safety: we trust that the protocol has given us a valid range
357 // of memory to read from.
358 let event = unsafe { v1::PcrEvent::from_ptr(self.location) };
359 EventLogHeader::new(event)
360 }
361 }
362
363 /// Whether the event log is truncated due to not enough space in the log to
364 /// contain some events.
365 #[must_use]
366 pub fn is_truncated(&self) -> bool {
367 self.is_truncated
368 }
369}
370
371/// Digests in a PCR event.
372#[derive(Clone)]
373pub struct PcrEventDigests<'a> {
374 data: &'a [u8],
375 algorithm_digest_sizes: AlgorithmDigestSizes<'a>,
376}
377
378impl<'a> Debug for PcrEventDigests<'a> {
379 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
380 f.debug_list().entries(self.clone()).finish()
381 }
382}
383
384impl<'a> IntoIterator for PcrEventDigests<'a> {
385 type Item = (AlgorithmId, &'a [u8]);
386 type IntoIter = PcrEventDigestIter<'a>;
387
388 fn into_iter(self) -> Self::IntoIter {
389 PcrEventDigestIter {
390 digests: self,
391 offset: 0,
392 }
393 }
394}
395
396/// Iterator over a list of digests.
397pub struct PcrEventDigestIter<'a> {
398 digests: PcrEventDigests<'a>,
399 offset: usize,
400}
401
402impl<'a> Iterator for PcrEventDigestIter<'a> {
403 type Item = (AlgorithmId, &'a [u8]);
404
405 fn next(&mut self) -> Option<Self::Item> {
406 let data: &[u8] = &self.digests.data[self.offset..];
407 let alg: &[u8] = data.get(..2)?;
408 let alg: AlgorithmId = AlgorithmId(u16::from_le_bytes([alg[0], alg[1]]));
409 let digest_size: usize = usize::from(self.digests.algorithm_digest_sizes.get_size(alg)?);
410 let digest: &[u8] = data.get(index:2..2 + digest_size)?;
411 self.offset += 2 + digest_size;
412 Some((alg, digest))
413 }
414}
415
416/// PCR event from an [`EventLog`].
417///
418/// This roughly corresponds to the C type `TCG_PCR_EVENT2`, but is not layout
419/// compatible.
420///
421/// The TPM v1 spec uses a single generic event type for both creating a
422/// new event and reading an event from the log. The v2 spec splits this
423/// into two structs: `EFI_TCG2_EVENT` for creating events, and
424/// `TCG_PCR_EVENT2` for reading events. To help clarify the usage, our
425/// API renames these types to `PcrEventInputs` and `PcrEvent`,
426/// respectively.
427#[derive(Debug)]
428pub struct PcrEvent<'a> {
429 pcr_index: PcrIndex,
430 event_type: EventType,
431 digests: &'a [u8],
432 event_data: &'a [u8],
433
434 // Precalculate the pointer to the next event.
435 next: *const u8,
436
437 // This data from the v2 log header is needed to parse the digest data.
438 algorithm_digest_sizes: AlgorithmDigestSizes<'a>,
439}
440
441impl<'a> PcrEvent<'a> {
442 unsafe fn from_ptr(ptr: *const u8, header: EventLogHeader<'a>) -> Option<Self> {
443 let ptr_u32: *const u32 = ptr.cast();
444 let pcr_index = PcrIndex(ptr_u32.read_unaligned());
445 let event_type = EventType(ptr_u32.add(1).read_unaligned());
446 let digests_count = ptr_u32.add(2).read_unaligned();
447 let digests_ptr: *const u8 = ptr.add(12);
448
449 // Get the byte size of the digests so that the digests iterator
450 // can be safe code.
451 let mut digests_byte_size = 0;
452 let mut elem_ptr = digests_ptr;
453 for _ in 0..digests_count {
454 let algorithm_id = AlgorithmId(elem_ptr.cast::<u16>().read_unaligned());
455 let alg_and_digest_size = mem::size_of::<AlgorithmId>()
456 + usize::from(header.algorithm_digest_sizes.get_size(algorithm_id)?);
457 digests_byte_size += alg_and_digest_size;
458 elem_ptr = elem_ptr.add(alg_and_digest_size);
459 }
460
461 let digests = slice::from_raw_parts(digests_ptr, digests_byte_size);
462 let event_size_ptr = digests_ptr.add(digests_byte_size);
463 let event_size = usize_from_u32(event_size_ptr.cast::<u32>().read_unaligned());
464 let event_data_ptr = event_size_ptr.add(4);
465 let event_data = slice::from_raw_parts(event_data_ptr, event_size);
466
467 Some(Self {
468 pcr_index,
469 event_type,
470 digests,
471 event_data,
472 next: event_data_ptr.add(event_size),
473 algorithm_digest_sizes: header.algorithm_digest_sizes,
474 })
475 }
476
477 /// PCR index for the event.
478 #[must_use]
479 pub fn pcr_index(&self) -> PcrIndex {
480 self.pcr_index
481 }
482
483 /// Type of event, indicating what type of data is stored in [`event_data`].
484 ///
485 /// [`event_data`]: Self::event_data
486 #[must_use]
487 pub fn event_type(&self) -> EventType {
488 self.event_type
489 }
490
491 /// Raw event data. The meaning of this data can be determined from
492 /// the [`event_type`].
493 ///
494 /// Note that this data is independent of what is hashed in [`digests`].
495 ///
496 /// [`digests`]: Self::digests
497 /// [`event_type`]: Self::event_type
498 #[must_use]
499 pub fn event_data(&self) -> &[u8] {
500 self.event_data
501 }
502
503 /// Digests of the data hashed for this event.
504 #[must_use]
505 pub fn digests(&self) -> PcrEventDigests {
506 PcrEventDigests {
507 data: self.digests,
508 algorithm_digest_sizes: self.algorithm_digest_sizes.clone(),
509 }
510 }
511}
512
513/// Iterator for events in [`EventLog`].
514pub struct EventLogIter<'a> {
515 log: &'a EventLog<'a>,
516 header: Option<EventLogHeader<'a>>,
517 location: *const u8,
518}
519
520impl<'a> Iterator for EventLogIter<'a> {
521 type Item = PcrEvent<'a>;
522
523 fn next(&mut self) -> Option<Self::Item> {
524 // The spec says that `last_entry` will be null if there are no
525 // events. Presumably `location` will be null as well, but check
526 // both just to be safe.
527 if self.location.is_null() || self.log.last_entry.is_null() {
528 return None;
529 }
530
531 // Safety: we trust that the protocol has given us a valid range
532 // of memory to read from.
533 let event = unsafe { PcrEvent::from_ptr(self.location, self.header.clone()?)? };
534
535 // If this is the last entry, set the location to null so that
536 // future calls to `next()` return `None`.
537 if self.location == self.log.last_entry {
538 self.location = ptr::null();
539 } else {
540 self.location = event.next;
541 }
542
543 Some(event)
544 }
545}
546
547/// Protocol for interacting with TPM devices.
548///
549/// This protocol can be used for interacting with older TPM 1.1/1.2
550/// devices, but most firmware only uses it for TPM 2.0.
551///
552/// The corresponding C type is `EFI_TCG2_PROTOCOL`.
553#[repr(C)]
554#[unsafe_protocol("607f766c-7455-42be-930b-e4d76db2720f")]
555pub struct Tcg {
556 get_capability: unsafe extern "efiapi" fn(
557 this: *mut Tcg,
558 protocol_capability: *mut BootServiceCapability,
559 ) -> Status,
560
561 get_event_log: unsafe extern "efiapi" fn(
562 this: *mut Tcg,
563 event_log_format: EventLogFormat,
564 event_log_location: *mut PhysicalAddress,
565 event_log_last_entry: *mut PhysicalAddress,
566 event_log_truncated: *mut u8,
567 ) -> Status,
568
569 hash_log_extend_event: unsafe extern "efiapi" fn(
570 this: *mut Tcg,
571 flags: HashLogExtendEventFlags,
572 data_to_hash: PhysicalAddress,
573 data_to_hash_len: u64,
574 // Use `()` here rather than `PcrEventInputs` so that it's a
575 // thin pointer.
576 event: *const (),
577 ) -> Status,
578
579 submit_command: unsafe extern "efiapi" fn(
580 this: *mut Tcg,
581 input_parameter_block_size: u32,
582 input_parameter_block: *const u8,
583 output_parameter_block_size: u32,
584 output_parameter_block: *mut u8,
585 ) -> Status,
586
587 get_active_pcr_banks:
588 unsafe extern "efiapi" fn(this: *mut Tcg, active_pcr_banks: *mut HashAlgorithm) -> Status,
589
590 set_active_pcr_banks:
591 unsafe extern "efiapi" fn(this: *mut Tcg, active_pcr_banks: HashAlgorithm) -> Status,
592
593 get_result_of_set_active_pcr_banks: unsafe extern "efiapi" fn(
594 this: *mut Tcg,
595 operation_present: *mut u32,
596 response: *mut u32,
597 ) -> Status,
598}
599
600impl Tcg {
601 /// Get information about the protocol and TPM device.
602 pub fn get_capability(&mut self) -> Result<BootServiceCapability> {
603 let mut capability = BootServiceCapability::default();
604 unsafe { (self.get_capability)(self, &mut capability).into_with_val(|| capability) }
605 }
606
607 /// Get the V1 event log. This provides events in the same format as a V1
608 /// TPM, so all events use SHA-1 hashes.
609 pub fn get_event_log_v1(&mut self) -> Result<v1::EventLog> {
610 let mut location = 0;
611 let mut last_entry = 0;
612 let mut truncated = 0;
613
614 let status = unsafe {
615 (self.get_event_log)(
616 self,
617 EventLogFormat::TCG_1_2,
618 &mut location,
619 &mut last_entry,
620 &mut truncated,
621 )
622 };
623
624 if status.is_success() {
625 let is_truncated = truncated != 0;
626
627 let log = unsafe {
628 v1::EventLog::new(location as *const u8, last_entry as *const u8, is_truncated)
629 };
630
631 Ok(log)
632 } else {
633 Err(status.into())
634 }
635 }
636
637 /// Get the V2 event log. This format allows for a flexible list of hash types.
638 pub fn get_event_log_v2(&mut self) -> Result<EventLog> {
639 let mut location = 0;
640 let mut last_entry = 0;
641 let mut truncated = 0;
642
643 let status = unsafe {
644 (self.get_event_log)(
645 self,
646 EventLogFormat::TCG_2,
647 &mut location,
648 &mut last_entry,
649 &mut truncated,
650 )
651 };
652
653 if status.is_success() {
654 let is_truncated = truncated != 0;
655
656 let log = EventLog {
657 _lifetime: PhantomData,
658 location: location as *const u8,
659 last_entry: last_entry as *const u8,
660 is_truncated,
661 };
662
663 Ok(log)
664 } else {
665 Err(status.into())
666 }
667 }
668
669 /// Extend a PCR and add an entry to the event log.
670 pub fn hash_log_extend_event(
671 &mut self,
672 flags: HashLogExtendEventFlags,
673 data_to_hash: &[u8],
674 event: &PcrEventInputs,
675 ) -> Result {
676 let event: *const PcrEventInputs = event;
677 let (event, _event_size) = PtrExt::to_raw_parts(event);
678 unsafe {
679 (self.hash_log_extend_event)(
680 self,
681 flags,
682 data_to_hash.as_ptr() as PhysicalAddress,
683 // OK to unwrap, usize fits in u64.
684 u64::try_from(data_to_hash.len()).unwrap(),
685 event,
686 )
687 .into()
688 }
689 }
690
691 /// Send a command directly to the TPM.
692 ///
693 /// Constructing the input block and parsing the output block are outside
694 /// the scope of this crate. See the [TPM 2.0 Specification][spec], in
695 /// particular Part 2 (Structures) and Part 3 (Commands).
696 ///
697 /// Note that TPM structures are big endian.
698 ///
699 /// [spec]: https://trustedcomputinggroup.org/resource/tpm-library-specification/
700 pub fn submit_command(
701 &mut self,
702 input_parameter_block: &[u8],
703 output_parameter_block: &mut [u8],
704 ) -> Result {
705 let input_parameter_block_len = u32::try_from(input_parameter_block.len())
706 .map_err(|_| Error::from(Status::BAD_BUFFER_SIZE))?;
707 let output_parameter_block_len = u32::try_from(output_parameter_block.len())
708 .map_err(|_| Error::from(Status::BAD_BUFFER_SIZE))?;
709
710 unsafe {
711 (self.submit_command)(
712 self,
713 input_parameter_block_len,
714 input_parameter_block.as_ptr(),
715 output_parameter_block_len,
716 output_parameter_block.as_mut_ptr(),
717 )
718 .into()
719 }
720 }
721
722 /// Get a bitmap of the active PCR banks. Each bank corresponds to a hash
723 /// algorithm.
724 pub fn get_active_pcr_banks(&mut self) -> Result<HashAlgorithm> {
725 let mut active_pcr_banks = HashAlgorithm::empty();
726
727 let status = unsafe { (self.get_active_pcr_banks)(self, &mut active_pcr_banks) };
728
729 status.into_with_val(|| active_pcr_banks)
730 }
731
732 /// Set the active PCR banks. Each bank corresponds to a hash
733 /// algorithm. This change will not take effect until the system is
734 /// rebooted twice.
735 pub fn set_active_pcr_banks(&mut self, active_pcr_banks: HashAlgorithm) -> Result {
736 unsafe { (self.set_active_pcr_banks)(self, active_pcr_banks) }.into()
737 }
738
739 /// Get the stored result of calling [`Tcg::set_active_pcr_banks`] in a
740 /// previous boot.
741 ///
742 /// If there was no attempt to set the active PCR banks in a previous boot,
743 /// this returns `None`. Otherwise, it returns a numeric response code:
744 /// * `0x00000000`: Success
745 /// * `0x00000001..=0x00000FFF`: TPM error code
746 /// * `0xfffffff0`: The operation was canceled by the user or timed out
747 /// * `0xfffffff1`: Firmware error
748 pub fn get_result_of_set_active_pcr_banks(&mut self) -> Result<Option<u32>> {
749 let mut operation_present = 0;
750 let mut response = 0;
751
752 let status = unsafe {
753 (self.get_result_of_set_active_pcr_banks)(self, &mut operation_present, &mut response)
754 };
755
756 status.into_with_val(|| {
757 if operation_present == 0 {
758 None
759 } else {
760 Some(response)
761 }
762 })
763 }
764}
765
766#[cfg(test)]
767mod tests {
768 use super::*;
769 use alloc::vec::Vec;
770 use core::slice;
771
772 #[test]
773 fn test_new_event() {
774 let mut buf = [MaybeUninit::uninit(); 22];
775 let event_data = [0x12, 0x13, 0x14, 0x15];
776 let event =
777 PcrEventInputs::new_in_buffer(&mut buf, PcrIndex(4), EventType::IPL, &event_data)
778 .unwrap();
779
780 assert_eq!({ event.size }, 22);
781 assert_eq!(
782 event.event_header,
783 EventHeader {
784 header_size: 14,
785 header_version: 1,
786 pcr_index: PcrIndex(4),
787 event_type: EventType::IPL,
788 }
789 );
790
791 // Cast to a byte slice to check the data is exactly as expected.
792 let event_ptr: *const PcrEventInputs = event;
793 let event_ptr: *const u8 = event_ptr.cast();
794 let event_bytes = unsafe { slice::from_raw_parts(event_ptr, mem::size_of_val(event)) };
795
796 #[rustfmt::skip]
797 assert_eq!(event_bytes, [
798 // Size
799 0x16, 0x00, 0x00, 0x00,
800
801 // Header
802 // Header size
803 0x0e, 0x00, 0x00, 0x00,
804 // Header version
805 0x01, 0x00,
806 // PCR index
807 0x04, 0x00, 0x00, 0x00,
808 // Event type
809 0x0d, 0x00, 0x00, 0x00,
810 // Event data
811 0x12, 0x13, 0x14, 0x15,
812 ]);
813 }
814
815 #[test]
816 fn test_event_log_v2() {
817 // This data comes from dumping the TPM event log in a VM
818 // (truncated to just two entries after the header).
819 #[rustfmt::skip]
820 let bytes = [
821 // Header event
822 // PCR index
823 0x00, 0x00, 0x00, 0x00,
824 // Event type
825 0x03, 0x00, 0x00, 0x00,
826 // SHA1 digest
827 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
828 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
829 // Event data size
830 0x2d, 0x00, 0x00, 0x00,
831 // Spec ID event data
832 // Signature
833 0x53, 0x70, 0x65, 0x63,
834 0x20, 0x49, 0x44, 0x20,
835 0x45, 0x76, 0x65, 0x6e,
836 0x74, 0x30, 0x33, 0x00,
837 // Platform class
838 0x00, 0x00, 0x00, 0x00,
839 // Spec version (minor, major, errata) (yes the order is weird)
840 0x00, 0x02, 0x00,
841 // Uintn size
842 0x02,
843 // Number of algorithms
844 0x04, 0x00, 0x00, 0x00,
845 // Digest sizes
846 // SHA1, size
847 0x04, 0x00,
848 0x14, 0x00,
849 // SHA256, size
850 0x0b, 0x00,
851 0x20, 0x00,
852 // SHA384, size
853 0x0c, 0x00,
854 0x30, 0x00,
855 // SHA512, size
856 0x0d, 0x00,
857 0x40, 0x00,
858 // Vendor info size
859 0x00,
860
861 // Event 1
862 // PCR index
863 0x00, 0x00, 0x00, 0x00,
864 // Event type
865 0x08, 0x00, 0x00, 0x00,
866 // Digest count
867 0x04, 0x00, 0x00, 0x00,
868 // Digests
869 // SHA1
870 0x04, 0x00,
871 0x14, 0x89, 0xf9, 0x23, 0xc4, 0xdc, 0xa7, 0x29, 0x17, 0x8b,
872 0x3e, 0x32, 0x33, 0x45, 0x85, 0x50, 0xd8, 0xdd, 0xdf, 0x29,
873 // SHA256
874 0x0b, 0x00,
875 0x96, 0xa2, 0x96, 0xd2, 0x24, 0xf2, 0x85, 0xc6,
876 0x7b, 0xee, 0x93, 0xc3, 0x0f, 0x8a, 0x30, 0x91,
877 0x57, 0xf0, 0xda, 0xa3, 0x5d, 0xc5, 0xb8, 0x7e,
878 0x41, 0x0b, 0x78, 0x63, 0x0a, 0x09, 0xcf, 0xc7,
879 // SHA384
880 0x0c, 0x00,
881 0x1d, 0xd6, 0xf7, 0xb4, 0x57, 0xad, 0x88, 0x0d,
882 0x84, 0x0d, 0x41, 0xc9, 0x61, 0x28, 0x3b, 0xab,
883 0x68, 0x8e, 0x94, 0xe4, 0xb5, 0x93, 0x59, 0xea,
884 0x45, 0x68, 0x65, 0x81, 0xe9, 0x0f, 0xec, 0xce,
885 0xa3, 0xc6, 0x24, 0xb1, 0x22, 0x61, 0x13, 0xf8,
886 0x24, 0xf3, 0x15, 0xeb, 0x60, 0xae, 0x0a, 0x7c,
887 // SHA512
888 0x0d, 0x00,
889 0x5e, 0xa7, 0x1d, 0xc6, 0xd0, 0xb4, 0xf5, 0x7b,
890 0xf3, 0x9a, 0xad, 0xd0, 0x7c, 0x20, 0x8c, 0x35,
891 0xf0, 0x6c, 0xd2, 0xba, 0xc5, 0xfd, 0xe2, 0x10,
892 0x39, 0x7f, 0x70, 0xde, 0x11, 0xd4, 0x39, 0xc6,
893 0x2e, 0xc1, 0xcd, 0xf3, 0x18, 0x37, 0x58, 0x86,
894 0x5f, 0xd3, 0x87, 0xfc, 0xea, 0x0b, 0xad, 0xa2,
895 0xf6, 0xc3, 0x7a, 0x4a, 0x17, 0x85, 0x1d, 0xd1,
896 0xd7, 0x8f, 0xef, 0xe6, 0xf2, 0x04, 0xee, 0x54,
897 // Event size
898 0x02, 0x00, 0x00, 0x00,
899 // Event data
900 0x00, 0x00,
901
902 // Event 2
903 // PCR index
904 0x00, 0x00, 0x00, 0x00,
905 // Event type
906 0x08, 0x00, 0x00, 0x80,
907 // Digest count
908 0x04, 0x00, 0x00, 0x00,
909 // SHA1
910 0x04, 0x00,
911 0xc7, 0x06, 0xe7, 0xdd, 0x36, 0x39, 0x29, 0x84, 0xeb, 0x06,
912 0xaa, 0xa0, 0x8f, 0xf3, 0x36, 0x84, 0x40, 0x77, 0xb3, 0xed,
913 // SHA256
914 0x0b, 0x00,
915 0x3a, 0x30, 0x8e, 0x95, 0x87, 0x84, 0xbf, 0xd0,
916 0xf6, 0xe3, 0xf1, 0xbd, 0x4d, 0x42, 0x14, 0xd3,
917 0x0a, 0x4c, 0x55, 0x00, 0xa4, 0x5b, 0x06, 0xda,
918 0x96, 0xfc, 0x90, 0x33, 0x8f, 0x69, 0xb3, 0x61,
919 // SHA384
920 0x0c, 0x00,
921 0xc0, 0xd0, 0x75, 0x96, 0xc5, 0x9a, 0x90, 0x7b,
922 0x79, 0x71, 0x6f, 0xc9, 0xf3, 0x6a, 0xad, 0x8f,
923 0x0f, 0x26, 0xf2, 0x02, 0x67, 0x5b, 0x42, 0x5a,
924 0x52, 0x3f, 0x72, 0xec, 0xb6, 0xf2, 0x53, 0x99,
925 0x57, 0xf0, 0xd9, 0x2c, 0x0a, 0x7d, 0xce, 0xaa,
926 0xf9, 0x9e, 0x60, 0x0e, 0x54, 0x18, 0xf1, 0xdc,
927 // SHA512
928 0x0d, 0x00,
929 0x9a, 0xe9, 0x25, 0xdc, 0x9c, 0xd2, 0x9d, 0xf0,
930 0xe5, 0x80, 0x54, 0x35, 0xa5, 0x99, 0x06, 0x1f,
931 0xcf, 0x32, 0x98, 0xcc, 0x2a, 0x15, 0xe4, 0x87,
932 0x99, 0xa2, 0x0c, 0x9c, 0xe5, 0x6c, 0x8f, 0xe5,
933 0x84, 0x09, 0x75, 0xaf, 0xf0, 0xe1, 0xb6, 0x98,
934 0x20, 0x07, 0x5e, 0xe4, 0x29, 0x79, 0x8b, 0x5d,
935 0xbb, 0xe5, 0xd1, 0xa2, 0x74, 0x36, 0xab, 0x49,
936 0xf1, 0x9b, 0x7a, 0x04, 0x11, 0xd2, 0x96, 0x2c,
937 // Event size
938 0x10, 0x00, 0x00, 0x00,
939 // Event data
940 0x00, 0x00, 0x82, 0x00, 0x00, 0x00, 0x00, 0x00,
941 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00,
942 ];
943
944 let log = EventLog {
945 _lifetime: PhantomData,
946 location: bytes.as_ptr(),
947 last_entry: unsafe { bytes.as_ptr().add(267) },
948 is_truncated: false,
949 };
950
951 let header = log.header().unwrap();
952 assert_eq!(header.platform_class, 0);
953 assert_eq!(header.spec_version, (2, 0, 0));
954 assert_eq!(header.uintn_size, 2);
955 assert_eq!(
956 header.algorithm_digest_sizes.0.to_vec(),
957 [
958 AlgorithmDigestSize {
959 algorithm_id: AlgorithmId::SHA1,
960 digest_size: 20,
961 },
962 AlgorithmDigestSize {
963 algorithm_id: AlgorithmId::SHA256,
964 digest_size: 32,
965 },
966 AlgorithmDigestSize {
967 algorithm_id: AlgorithmId::SHA384,
968 digest_size: 48,
969 },
970 AlgorithmDigestSize {
971 algorithm_id: AlgorithmId::SHA512,
972 digest_size: 64,
973 },
974 ]
975 );
976 assert_eq!(header.vendor_info, []);
977
978 let mut iter = log.iter();
979
980 // Entry 1
981 let entry = iter.next().unwrap();
982 assert_eq!(entry.pcr_index, PcrIndex(0));
983 assert_eq!(entry.event_type, EventType::CRTM_VERSION);
984 #[rustfmt::skip]
985 assert_eq!(
986 entry.digests().into_iter().collect::<Vec<_>>(),
987 [
988 (AlgorithmId::SHA1, [
989 0x14, 0x89, 0xf9, 0x23, 0xc4, 0xdc, 0xa7, 0x29, 0x17, 0x8b,
990 0x3e, 0x32, 0x33, 0x45, 0x85, 0x50, 0xd8, 0xdd, 0xdf, 0x29,
991 ].as_slice()),
992 (AlgorithmId::SHA256, [
993 0x96, 0xa2, 0x96, 0xd2, 0x24, 0xf2, 0x85, 0xc6,
994 0x7b, 0xee, 0x93, 0xc3, 0x0f, 0x8a, 0x30, 0x91,
995 0x57, 0xf0, 0xda, 0xa3, 0x5d, 0xc5, 0xb8, 0x7e,
996 0x41, 0x0b, 0x78, 0x63, 0x0a, 0x09, 0xcf, 0xc7,
997 ].as_slice()),
998 (AlgorithmId::SHA384, [
999 0x1d, 0xd6, 0xf7, 0xb4, 0x57, 0xad, 0x88, 0x0d,
1000 0x84, 0x0d, 0x41, 0xc9, 0x61, 0x28, 0x3b, 0xab,
1001 0x68, 0x8e, 0x94, 0xe4, 0xb5, 0x93, 0x59, 0xea,
1002 0x45, 0x68, 0x65, 0x81, 0xe9, 0x0f, 0xec, 0xce,
1003 0xa3, 0xc6, 0x24, 0xb1, 0x22, 0x61, 0x13, 0xf8,
1004 0x24, 0xf3, 0x15, 0xeb, 0x60, 0xae, 0x0a, 0x7c,
1005 ].as_slice()),
1006 (AlgorithmId::SHA512, [
1007 0x5e, 0xa7, 0x1d, 0xc6, 0xd0, 0xb4, 0xf5, 0x7b,
1008 0xf3, 0x9a, 0xad, 0xd0, 0x7c, 0x20, 0x8c, 0x35,
1009 0xf0, 0x6c, 0xd2, 0xba, 0xc5, 0xfd, 0xe2, 0x10,
1010 0x39, 0x7f, 0x70, 0xde, 0x11, 0xd4, 0x39, 0xc6,
1011 0x2e, 0xc1, 0xcd, 0xf3, 0x18, 0x37, 0x58, 0x86,
1012 0x5f, 0xd3, 0x87, 0xfc, 0xea, 0x0b, 0xad, 0xa2,
1013 0xf6, 0xc3, 0x7a, 0x4a, 0x17, 0x85, 0x1d, 0xd1,
1014 0xd7, 0x8f, 0xef, 0xe6, 0xf2, 0x04, 0xee, 0x54,
1015 ].as_slice()),
1016 ]
1017 );
1018 assert_eq!(entry.event_data, [0, 0]);
1019
1020 // Entry 2
1021 let entry = iter.next().unwrap();
1022 assert_eq!(entry.pcr_index, PcrIndex(0));
1023 assert_eq!(entry.event_type, EventType::EFI_PLATFORM_FIRMWARE_BLOB);
1024 #[rustfmt::skip]
1025 assert_eq!(
1026 entry.digests().into_iter().collect::<Vec<_>>(),
1027 [
1028 (AlgorithmId::SHA1, [
1029 0xc7, 0x06, 0xe7, 0xdd, 0x36, 0x39, 0x29, 0x84, 0xeb, 0x06,
1030 0xaa, 0xa0, 0x8f, 0xf3, 0x36, 0x84, 0x40, 0x77, 0xb3, 0xed,
1031 ].as_slice()),
1032 (AlgorithmId::SHA256, [
1033 0x3a, 0x30, 0x8e, 0x95, 0x87, 0x84, 0xbf, 0xd0,
1034 0xf6, 0xe3, 0xf1, 0xbd, 0x4d, 0x42, 0x14, 0xd3,
1035 0x0a, 0x4c, 0x55, 0x00, 0xa4, 0x5b, 0x06, 0xda,
1036 0x96, 0xfc, 0x90, 0x33, 0x8f, 0x69, 0xb3, 0x61,
1037 ].as_slice()),
1038 (AlgorithmId::SHA384, [
1039 0xc0, 0xd0, 0x75, 0x96, 0xc5, 0x9a, 0x90, 0x7b,
1040 0x79, 0x71, 0x6f, 0xc9, 0xf3, 0x6a, 0xad, 0x8f,
1041 0x0f, 0x26, 0xf2, 0x02, 0x67, 0x5b, 0x42, 0x5a,
1042 0x52, 0x3f, 0x72, 0xec, 0xb6, 0xf2, 0x53, 0x99,
1043 0x57, 0xf0, 0xd9, 0x2c, 0x0a, 0x7d, 0xce, 0xaa,
1044 0xf9, 0x9e, 0x60, 0x0e, 0x54, 0x18, 0xf1, 0xdc,
1045 ].as_slice()),
1046 (AlgorithmId::SHA512, [
1047 0x9a, 0xe9, 0x25, 0xdc, 0x9c, 0xd2, 0x9d, 0xf0,
1048 0xe5, 0x80, 0x54, 0x35, 0xa5, 0x99, 0x06, 0x1f,
1049 0xcf, 0x32, 0x98, 0xcc, 0x2a, 0x15, 0xe4, 0x87,
1050 0x99, 0xa2, 0x0c, 0x9c, 0xe5, 0x6c, 0x8f, 0xe5,
1051 0x84, 0x09, 0x75, 0xaf, 0xf0, 0xe1, 0xb6, 0x98,
1052 0x20, 0x07, 0x5e, 0xe4, 0x29, 0x79, 0x8b, 0x5d,
1053 0xbb, 0xe5, 0xd1, 0xa2, 0x74, 0x36, 0xab, 0x49,
1054 0xf1, 0x9b, 0x7a, 0x04, 0x11, 0xd2, 0x96, 0x2c,
1055 ].as_slice()),
1056 ]
1057 );
1058 #[rustfmt::skip]
1059 assert_eq!(entry.event_data, [
1060 0x00, 0x00, 0x82, 0x00, 0x00, 0x00, 0x00, 0x00,
1061 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00,
1062 ]);
1063
1064 assert!(iter.next().is_none());
1065 }
1066}
1067