Remote Attestation

An introduction to Remote Attestation #

This document introduces remote attestation.

Measurements vs. Signatures: Trusted Boot vs. Secure Boot #

The most common way to secure the boot process is by using digital signatures to validate the origin/integrity of software before executing it. In the UEFI context, this is called Secure Boot. Each stage, starting with SEC, verifies the signature of the subsequent stage before executing it. Secure Boot follows a model similar to the PKI system for TLS on the WWW: there is a list of trusted public keys in the form of X.509 certificates, and each piece of code coming from a 3rd party needs to be signed by one of these certificates.

However, there are several drawbacks to this approach. First, the decision whether to trust a piece of code is left to the computer manufacturer, which provides the default list of trusted certificates. Second, Secure Boot is all or nothing. If a signature is not trusted, the boot process aborts.

Trusted Boot, also known as Measured Boot, has a different approach. Instead of verifying code, it is measured i.e., its hash is recorded in a secure place (Shielded Location). All code is executed regardless of provenance. Later, after the system has booted, it’s validated using a process called attestation. Here, a signed message containing all hashes recorded during boot is sent to a remote server (remote attestation) or a dedicated chip (local attestation) on the device. The server or chip decides based on the hashes and other information whether the device is trusted. Once trusted, the remote server or local chip may release/expose sensitive information required for applications on the device to operate as intended, such as encryption keys or API tokens.

Remote attestation allows the trust decision to be made by the owner of the device, not the manufacturer. It also allows for more gradual trust policies and inclusion of 3rd party code in the boot process. However, there are some challenges to this approach. The attestation server or chip needs to infer a device’s state from the hashes of all code and data that contributed to the boot process. Also, a device needs to be isolated from sensitive data and services until it has been verified by attestation. The remainder of the document deals with the former challenge.

Integrity measurement collection and remote attestation #

Measurements (hashes) of code and data contributing to the boot process are stored in Platform Configuration Registers (PCR). Each PCR consists of multiple banks. Each bank stores one hash for one particular hash algorithm. No PCR has two banks with the same algorithm. The PCRs are part of a Trusted Platform Module (TPM). A TPM can either be a discrete chip or be implemented in firmware. Measurements are sent to the TPM as hashes by the host CPU. Each hash (i.e., measurement) is extended into a PCR like this:

$$ \mathrm{PCR}_i \leftarrow H(\mathrm{PCR}_i || m) $$

For a measurement $m$, hash function (i.e., bank) $H$, and PCR $i$. Later, the TPM can be asked to generate a signed version of all PCRs, called a Quote. In parallel to measurements, the measured boot maintains an Event Log which lists all measurements sent to the TPM plus meta-information that gives more context about the measurement. Initially conceived as a debugging aid, the event log is vital in real-life implementations of remote attestation to make correct trust decisions. Event log entries contain the hash sent to the TPM, the PCR it was extended into, a type field explaining the measurement’s content, and a type-dependent data field that – in theory – should be enough to recreate the hash. A single event log entry looks like this:

typedef struct {
    UINT32 pcrIndex;
    UINT32 eventType;
    TPML_DIGEST_VALUES digests;
    UINT32 eventSize;
    BYTE event[eventSize];
} TCG_PCR_EVENT2;

The event log is not maintained by the TPM, nor is it kept in a Shielded Location and as such, it can be manipulated by an attacker. The first step in verifying whether the event log received from a device is genuine is to replay it against the signed PCR values. This is done by extending all hashes in the event log and comparing them to the actual values. Then for each entry in the log, the recorded hash is verified by using the eventType and event fields. This is challenging as the event field rarely contains all necessary information to do so (see next section). After that, the event log can be analyzed to determine the booted state of the device.

Event log walkthrough #

The reality of event log analysis is significantly more messy than the previous section may suggest. Neither eventType nor event fields are part of the hash and thus could be manipulated. An attacker could for example change the event to a “No Action type”. This event type may be skipped in the analysis, thus hiding a measurement.

Additionally, while there is a specification for measured boot called TCG PC Client Platform Firmware Profile Specification, not all vendors implement its latest version. Even if they do, the specification allows for critical data to not be included in the event log directly, which makes analysis hard.

The following section is a walkthrough of events extracted from real machines.

PCR 0: No Action (Intel only)
First event in the log. Replaces the contents of PCR 0, not extended. Only on Intel V-Pro systems. The final byte is sent by the TPM locality that sent the Startup command. Data: StartupLocality\x00\x03.

PCR 0: S-CRTM Contents (Intel only)
Data: Boot Guard Measured S-CRTM\x00. Hash of the SRTM code and configuration. Measured by the CBnT ACM. Exact method is under NDA.

PCR 0: S-CRTM Version
Data: \x00 (Intel), UTF-16 platform name like R1CET67W on AMD. Hash of the SRTM code’s version. Measured by the CBnT ACM. Exact method unknown and likely under NDA.

PCR 1: CPU Microcode
Data: The string CPU Microcode\x00. Hash of the Microcode patch applied during boot. No indication of contents or size. May be rolled into an EFI Platform Firmware Blob event.

PCR 0: EFI Platform Firmware Blob
Data: Structure that gives you a physical address and size. Hash of some UEFI code and data used during boot. No indication of its format or purpose. Newer versions of the event have a descriptor string that gives some details. In both cases, only the blob’s address in memory during boot is in the event log. In order to analyze it, mapping to a specific flash address is required.

PCR 0: POST Code
Data: Newer UEFI versions: structure that gives you a physical address and size. Older version: a string ACPI DATA, SMM CODE, BIS CODE, POST CODE or Embedded UEFI Driver with zero indication of size or memory address. Hash of some non-UEFI code or data. Some AMD laptops have these multiple times for different ACPI tables sections. Spec: For performance reasons, some implementations may combine CPU microcode updates with a Firmware POST code image. In this situation, it is acceptable to record CPU microcode updates in PCR[0] as part of an EV_POST_CODE event.

PCR 7: EFI Variable Driver Config
Data: EFI variable name and contents. EFI variables are important for secure boot. At least SecureBoot, PK, KEK, db, and dbx are included. Often, important variables like AuditMode, DeployMode, dbr, defaultDb, etc., are missing.

PCR 6: Compact Hash
Data: Some string, some code or data relevant to the boot process. The string is supposed to give an indication of what it is. Used by Dell to measure configuration values in a proprietary format called Dell Configuration Information 1 (twice, different hashes) and Dell Configuration Information 2.

PCR 0,1,2: Non-Host Info
Data: Binary data, source dependent, but no generic type field. Often, signatures are used. Used to measure peripheral’s code. CSME runtime measurement format is under NDA. CSME code is measured into PCR 2, while the config is in PCR 0. Intel AMT config is measured into PCR 1.

PCR 1: EFI Handoff Tables
Data: Descriptor of a non-UEFI table (ACPI, SMBIOS,…) and its address and size in memory at the time of measurement. This is slightly better than the legacy event that misses a descriptor.

PCR 1: EFI Variable, EFI Variable Driver Config
Data: Name and contents (!) of boot loader variables Setup, BootOrder & BootXXXX. Some vendors also include proprietary variables like LenovoConfig, LenovoSecurityConfig, LenovoRuntimeConfig.

PCR 4: EFI Action
Data: The string Calling EFI Application from Boot Option. This marks the start of the BDS phase.

PCR 0-7: Separator
Data: 32 bit integer. This separates the DXE phase from the BDS phase where control of the TPM is passed to non-core UEFI code. A value of 0x00000001 signals a measurement error, 0x00000000 or 0xffffffff signal success. A value other than 0 has however not been observed by the original author of this documentation.

PCR 4: EFI Boot Services Application
Data: UEFI path to the application executed during boot. The hash is of the PE file before relocation. The first event is the EFI boot loader itself.

PCR 7: EFI Variable Authority
Data: Contents of the db (secure boot key database). This measures the db again after the separator. It’s supposed to tell us what db was used for the BDS phase.

PCR 5: EFI GPT Event
Data: GPT header and partition list.

PCR 4: EFI Boot Services Application
Data: UEFI path to the application executed during boot. The hash is of the PE file before relocation. Events after the EFI Variable Authority event are EFI applications loaded by the boot loader.

PCR 2: EFI Boot Services Driver
Data: EFI path, size and load address. Peripheral driver like PCI option ROMs loaded from outside the EFI image.

PCR 11: Compact Hash (Windows only)
Data: 32 bit integer of the number of boot attempts. A guess is that this represents the Windows boot manager entry to be booted.

PCR 12-14: Event Tag (Windows only)
Data: List of Windows boot manager events The Windows boot loader has its own event log, measuring early driver DLLs and boot manager configuration. All these events are collected and hashed as one blob to speed up boot.

PCR 11: Compact Hash (Windows only)
Data: 32-bit integer 0xffff0000 Windows boot manager/BitLocker thing.

PCR 12-14: Separator (Windows only)
Data: The string WBCL Separator marking the end of the Windows boot loader.

PCR 14: IPL (Shim only)
Data: The string MokList, MokListX or MokListTrusted Contents of the…

PCR 7: EFI Variable Authority (Shim only)
Data: Contents of SbatLevel, MokListTrusted, Shim Contents of security-critical Shim UEFI variables. Sbat and Shim are only measured as Authority events, not twice as with UEFI.

PCR 4: EFI Boot Services Application (Shim only)
Data: Next boot loader’s EFI path (e.g., \EFI\redhat\grubx64.efi). Normalized UEFI app as above.

PCR 8: IPL (GRUB only)
Data: A single line from the Grub configuration. Grub measures each line of its configuration script before it’s executed. Each line is prefixed with grub_cmd like this: grub_cmd linux (hd0,gpt2)/vmlinuz-4.18.0-372.19.1.el8_6.x86_64 root=/dev/mapper/rhel-root ro crashkernel=auto resume=/dev/mapper/rhel-swap rd.lvm.lv=rhel/root rd.lvm.lv=rhel/swap rhgb quiet

PCR 9: IPL (GRUB only)
Data: The string grub_linuxefi Kernel or grub_linuxefi Initrd Before a kernel, initrd, or chainloader is executed, Grub measures the file as is. The word after the grub_linuxefi prefix hints at its purpose.

PCR 4: EFI Boot Services Application (GRUB only)
Data: UEFI load event like BDS but without EFI path. Grub uses UEFI facilities to load the Linux kernel but does not supply an EFI path. The event only provides the memory address and size.

PCR 8: IPL (GRUB only)
Data: The kernel command line prefixed with grub_kernel_cmdline Grub measures the full kernel command line on top of the configuration script that generated it.

PCR 5: EFI Action
Data: The string Exit Boot Services Invocation The final step of the BDS layer in UEFI is to call ExitBootServices() to lock down all interfaces that should not be accessible to untrusted code.

PCR 5: EFI Action
Data: The string Exit Boot Services Returned with Success The call to ExitBootServices() was successful.