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
11use super::{AlgorithmId, EventType, HashAlgorithm, PcrIndex};
12use crate::data_types::PhysicalAddress;
13use crate::polyfill::maybe_uninit_slice_as_mut_ptr;
14use crate::proto::unsafe_protocol;
15use crate::util::{ptr_write_unaligned_and_add, usize_from_u32};
16use crate::{Error, Result, Status};
17use core::fmt::{self, Debug, Formatter};
18use core::marker::{PhantomData, PhantomPinned};
19use core::mem::{self, MaybeUninit};
20use core::ptr;
21use ptr_meta::Pointee;
22
23/// 20-byte SHA-1 digest.
24pub 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)]
29type 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)]
36pub 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
45impl 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)]
84pub 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)]
109pub struct PcrEvent {
110 pcr_index: PcrIndex,
111 event_type: EventType,
112 digest: Sha1Digest,
113 event_data_size: u32,
114 event_data: [u8],
115}
116
117impl 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.
204impl 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.
217impl 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)]
231pub 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
247pub 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
257impl<'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`].
295pub struct EventLogIter<'a> {
296 log: &'a EventLog<'a>,
297 location: *const u8,
298}
299
300impl<'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")]
332pub 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`].
384pub 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
396impl 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)]
530mod 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