linux_bootloader/
tpm.rs

1use alloc::vec;
2use core::mem::{self, MaybeUninit};
3use log::warn;
4use uefi::{
5    prelude::BootServices,
6    proto::tcg::{
7        v1::{self, Sha1Digest},
8        v2, EventType, PcrIndex,
9    },
10    table::boot::ScopedProtocol,
11};
12
13fn open_capable_tpm2(boot_services: &BootServices) -> uefi::Result<ScopedProtocol<v2::Tcg>> {
14    let tpm_handle = boot_services.get_handle_for_protocol::<v2::Tcg>()?;
15    let mut tpm_protocol = boot_services.open_protocol_exclusive::<v2::Tcg>(tpm_handle)?;
16
17    let capabilities = tpm_protocol.get_capability()?;
18
19    /*
20     * Here's systemd-stub perform a cast to EFI_TCG_BOOT_SERVICE_CAPABILITY
21     * indicating there could be some quirks to workaround.
22     * It should probably go to uefi-rs?
23    if capabilities.structure_version.major == 1 && capabilities.structure_version.minor == 0 {
24
25    }*/
26
27    if !capabilities.tpm_present() {
28        warn!("Capability `TPM present` is not there for the existing TPM TCGv2 protocol");
29        return Err(uefi::Status::UNSUPPORTED.into());
30    }
31
32    Ok(tpm_protocol)
33}
34
35fn open_capable_tpm1(boot_services: &BootServices) -> uefi::Result<ScopedProtocol<v1::Tcg>> {
36    let tpm_handle = boot_services.get_handle_for_protocol::<v1::Tcg>()?;
37    let mut tpm_protocol = boot_services.open_protocol_exclusive::<v1::Tcg>(tpm_handle)?;
38
39    let status_check = tpm_protocol.status_check()?;
40
41    if status_check.protocol_capability.tpm_deactivated()
42        || !status_check.protocol_capability.tpm_present()
43    {
44        warn!("Capability `TPM present` is not there or `TPM deactivated` is there for the existing TPM TCGv1 protocol");
45        return Err(uefi::Status::UNSUPPORTED.into());
46    }
47
48    Ok(tpm_protocol)
49}
50
51pub fn tpm_available(boot_services: &BootServices) -> bool {
52    open_capable_tpm2(boot_services).is_ok() || open_capable_tpm1(boot_services).is_ok()
53}
54
55/// Log an event in the TPM with `buffer` as data.
56/// Returns a boolean whether the measurement has been done or not in case of success.
57pub fn tpm_log_event_ascii(
58    boot_services: &BootServices,
59    pcr_index: PcrIndex,
60    buffer: &[u8],
61    description: &str,
62) -> uefi::Result<bool> {
63    if pcr_index.0 == u32::MAX {
64        return Ok(false);
65    }
66    if let Ok(mut tpm2) = open_capable_tpm2(boot_services) {
67        let required_size = mem::size_of::<u32>()
68            // EventHeader is private…
69            + mem::size_of::<u32>() + mem::size_of::<u16>() + mem::size_of::<PcrIndex>() + mem::size_of::<EventType>()
70            + description.len();
71
72        let mut event_buffer = vec![MaybeUninit::<u8>::uninit(); required_size];
73        let event = v2::PcrEventInputs::new_in_buffer(
74            event_buffer.as_mut_slice(),
75            pcr_index,
76            EventType::IPL,
77            description.as_bytes(),
78        )?;
79        // FIXME: what do we want as flags here?
80        tpm2.hash_log_extend_event(Default::default(), buffer, event)?;
81    } else if let Ok(mut tpm1) = open_capable_tpm1(boot_services) {
82        let required_size = mem::size_of::<PcrIndex>()
83            + mem::size_of::<EventType>()
84            + mem::size_of::<Sha1Digest>()
85            + mem::size_of::<u32>()
86            + description.len();
87
88        let mut event_buffer = vec![MaybeUninit::<u8>::uninit(); required_size];
89
90        // Compute sha1 of the event data
91        let mut m = sha1_smol::Sha1::new();
92        m.update(description.as_bytes());
93
94        let event = v1::PcrEvent::new_in_buffer(
95            event_buffer.as_mut_slice(),
96            pcr_index,
97            EventType::IPL,
98            m.digest().bytes(),
99            description.as_bytes(),
100        )?;
101
102        tpm1.hash_log_extend_event(event, Some(buffer))?;
103    }
104
105    Ok(true)
106}