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