UEFI TPM2 examples

I’ve fallen way behind in writing up the highlights from the last few years spent building the TSS2 libraries and plumbing. Recently though I’ve had a few people reach out asking about accessing various bits of data exposed by the TCG2 UEFI protocol driver. Back when I first got involved in the TPM2 effort at Intel I wrote a few tools for the Grub2 shell (never upstreamed) that did just this and wrote about it here.

Having these as UEFI executables sounds useful but I never had an existing repo where they would fit well and I didn’t want to create a new one. I’ve made a lot of progress on the TCTI for UEFI that I presented at the Linux Security Summit last year and since it’s the only UEFI specific repo in the tpm2-software org, it’s kinda the natural place for them.

I’ve added these UEFI shell executables to the ‘examples’ directory of the tpm2-tcti-uefi repo. These are very much example applications and not something I’d call utilities. They take no command line options and are intended only as examples of how to use a specific interface, either the TCG2 UEFI protocol driver or a TSS2 API. I’m not against adding features to these examples but I think this work should be weighed against the cost of making the existing tpm2-tools usable from the UEFI shell. Till then these aren’t part of a supported interface in the tpm2-tcti-uefi (see the README).

But before we get into the example applications we’ll take a look at the test harness and the script we use to execute the examples. This script integrates all of the stuff necessary to automate the execution of the UEFI executables (qemu, OVMF and swtpm) in the autotools test harness.  Better yet, we can also use it to run these examples and capture the output.

test harness

Autotools has a very simple but very powerful test harness. For the tests executables listed in the TESTS variable, those with the.efi suffix will be invoked by running the script at $(srcdir)/lib/efi-test-setup.sh, passing it the path to the executable file. When make check is executed, autotools does this in parallel for each test listed in the TESTS variable.

What exactly this script does is beyond the scope of this post, but it’s still really interesting. Simply put: it sets up everything qemu needs to execute the OVMF firmware with TPM2 support and then automates the execution of the test executable in the UEFI shell.

To make this concrete by way of example: if we execute the make check target with TESTS=test/hello-world.efi set in the environment:make check TESTS=test/hello-world.efi,autotools will invoke the test script like so:

$(srcdir)/lib/efi-test-setup.sh --startup-template=$(srcdir)/lib/startup.nsh.template test/hello-world.efi

The executables under the example directory are not executed by the test harness when make check is run by default. Using the trick above we can cause them to be executed like so:

make check TESTS=example/tcg2-get-pcr-banks.efi

The remaining sections will rely on this method to invoke each example executable.

tcg2-get-pcr-banks.efi

This example demonstrates how to query the TCG2 UEFI protocol driver for the currently active PCR banks. This can be discovered by querying the TPM2 device directly using the TSS2 APIs however the UEFI protocol driver makes this available through a much more simple interface. Execute the example code with the following command:

make check TESTS=example/tcg2-get-pcr-banks.efi

The output from this “test” will end up in the file example/tcg2-get-pcr-banks.log and should look something like:

GetActivePcrBanks: 0x00000007
    EFI_TCG2_BOOT_HASH_ALG_SHA1: true
    EFI_TCG2_BOOT_HASH_ALG_SHA256: true
    EFI_TCG2_BOOT_HASH_ALG_SHA384: true
    EFI_TCG2_BOOT_HASH_ALG_SHA512: false
    EFI_TCG2_BOOT_HASH_ALG_SM3_256: false

tpm2-get-pcrs.efi

The next logical thing to do after we’ve queried for the active PCR banks is to get their contents. The TCG2 UEFI protocol does not expose this data to us and so we use the UEFI TCTI and the TSS2 System API (libtss2-sys). Again we use the check target, this time passing in the path to versions of the TSS2 libraries built for UEFI (see the section titled Dependencies in doc/examples.md for more info):

make check -L$(pwd)/../$(pwd)/../tss2uefi_root/usr/local/lib check TESTS=example/tpm2-get-pcrs.efi

The output will end up in example/tcg2-get-pcr-banks.log and should show output from the EFI executable for each bank. Below is example output from a single PCR bank:

PCR bank: EFI_TCG2_BOOT_HASH_ALG_SHA384
  PCR: 0
    00000000: C5 A5 4D 51 F2 3E C4 10-F8 CF DE E8 83 D7 46 8E  *..MQ.>........F.*
    00000010: A3 C4 A9 EB C1 1F 32 90-0A 7E 24 22 89 55 C1 B7  *......2...$".U..*
    00000020: 4F 0F 50 0F D1 7D 67 76-D9 20 E3 BD D6 4F A7 AC  *O.P...gv. ...O..*
  PCR: 1
    00000000: 61 AB A4 CC CC 39 D1 A6-94 F1 DF 3C C8 B2 17 8F  *a....9.....<....*
    00000010: 85 59 33 76 13 DE 05 09-1B 40 7A BF D4 36 58 2D  *.Y3v.....@z..6X-*
    00000020: 7C 81 E9 68 A1 7F F5 C2-56 FD 48 76 86 06 82 D9  *...h....V.Hv....*
  PCR: 2
    00000000: 51 89 23 B0 F9 55 D0 8D-A0 77 C9 6A AB A5 22 B9  *Q.#..U...w.j..".*
    00000010: DE CE DE 61 C5 99 CE A6-C4 18 89 CF BE A4 AE 4D  *...a...........M*
    00000020: 50 52 9D 96 FE 4D 1A FD-AF B6 5E 7F 95 BF 23 C4  *PR...M....^...#.*
  PCR: 3
    00000000: 51 89 23 B0 F9 55 D0 8D-A0 77 C9 6A AB A5 22 B9  *Q.#..U...w.j..".*
    00000010: DE CE DE 61 C5 99 CE A6-C4 18 89 CF BE A4 AE 4D  *...a...........M*
    00000020: 50 52 9D 96 FE 4D 1A FD-AF B6 5E 7F 95 BF 23 C4  *PR...M....^...#.*
  PCR: 4
    00000000: 2E D8 40 1D 9A 80 E5 07-D8 B6 C9 0D 6E 14 2E A9  *..@.........n...*
    00000010: 96 89 44 C7 79 2D AC C4-FA 14 4E 24 68 6B 3B DC  *..D.y-....N$hk;.*
    00000020: 31 B0 CA EB CD 26 54 6B-82 34 59 D6 0E DA 9F A0  *1....&Tk.4Y.....*
  PCR: 5
    00000000: 51 89 23 B0 F9 55 D0 8D-A0 77 C9 6A AB A5 22 B9  *Q.#..U...w.j..".*
    00000010: DE CE DE 61 C5 99 CE A6-C4 18 89 CF BE A4 AE 4D  *...a...........M*
    00000020: 50 52 9D 96 FE 4D 1A FD-AF B6 5E 7F 95 BF 23 C4  *PR...M....^...#.*
  PCR: 6
    00000000: 51 89 23 B0 F9 55 D0 8D-A0 77 C9 6A AB A5 22 B9  *Q.#..U...w.j..".*
    00000010: DE CE DE 61 C5 99 CE A6-C4 18 89 CF BE A4 AE 4D  *...a...........M*
    00000020: 50 52 9D 96 FE 4D 1A FD-AF B6 5E 7F 95 BF 23 C4  *PR...M....^...#.*
  PCR: 7
    00000000: 98 44 1C 7F 76 25 D1 00-58 C4 76 83 AE C4 86 CE  *.D..v%..X.v.....*
    00000010: 31 1C 63 32 35 EB 55 55-93 A7 EE 79 11 21 E3 57  *1.c25.UU...y.!.W*
    00000020: 8A E7 2D 04 EC EF 66 1F-27 2D 59 05 8B 77 AF 35  *..-...f.'-Y..w.5*
  PCR: 8
    00000000: 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  *................*
    00000010: 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  *................*
    00000020: 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  *................*
  PCR: 9
    00000000: 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  *................*
    00000010: 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  *................*
    00000020: 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  *................*
  PCR: 10
    00000000: 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  *................*
    00000010: 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  *................*
    00000020: 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  *................*
  PCR: 11
    00000000: 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  *................*
    00000010: 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  *................*
    00000020: 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  *................*
  PCR: 12
    00000000: 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  *................*
    00000010: 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  *................*
    00000020: 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  *................*
  PCR: 13
    00000000: 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  *................*
    00000010: 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  *................*
    00000020: 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  *................*
  PCR: 14
    00000000: 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  *................*
    00000010: 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  *................*
    00000020: 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  *................*
  PCR: 15
    00000000: 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  *................*
    00000010: 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  *................*
    00000020: 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  *................*
  PCR: 16
    00000000: 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  *................*
    00000010: 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  *................*
    00000020: 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  *................*
  PCR: 17
    00000000: FF FF FF FF FF FF FF FF-FF FF FF FF FF FF FF FF  *................*
    00000010: FF FF FF FF FF FF FF FF-FF FF FF FF FF FF FF FF  *................*
    00000020: FF FF FF FF FF FF FF FF-FF FF FF FF FF FF FF FF  *................*
  PCR: 18
    00000000: FF FF FF FF FF FF FF FF-FF FF FF FF FF FF FF FF  *................*
    00000010: FF FF FF FF FF FF FF FF-FF FF FF FF FF FF FF FF  *................*
    00000020: FF FF FF FF FF FF FF FF-FF FF FF FF FF FF FF FF  *................*
  PCR: 19
    00000000: FF FF FF FF FF FF FF FF-FF FF FF FF FF FF FF FF  *................*
    00000010: FF FF FF FF FF FF FF FF-FF FF FF FF FF FF FF FF  *................*
    00000020: FF FF FF FF FF FF FF FF-FF FF FF FF FF FF FF FF  *................*
  PCR: 20
    00000000: FF FF FF FF FF FF FF FF-FF FF FF FF FF FF FF FF  *................*
    00000010: FF FF FF FF FF FF FF FF-FF FF FF FF FF FF FF FF  *................*
    00000020: FF FF FF FF FF FF FF FF-FF FF FF FF FF FF FF FF  *................*
  PCR: 21
    00000000: FF FF FF FF FF FF FF FF-FF FF FF FF FF FF FF FF  *................*
    00000010: FF FF FF FF FF FF FF FF-FF FF FF FF FF FF FF FF  *................*
    00000020: FF FF FF FF FF FF FF FF-FF FF FF FF FF FF FF FF  *................*
  PCR: 22
    00000000: FF FF FF FF FF FF FF FF-FF FF FF FF FF FF FF FF  *................*
    00000010: FF FF FF FF FF FF FF FF-FF FF FF FF FF FF FF FF  *................*
    00000020: FF FF FF FF FF FF FF FF-FF FF FF FF FF FF FF FF  *................*
  PCR: 23
    00000000: 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  *................*
    00000010: 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  *................*
    00000020: 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  *................*

tcg2-get-eventlog.efi

Another interesting thing UEFI does for us is maintain a log of the data extended into the TPM2 PCRs. The structure and format of this log is documented in the TCG UEFI specs. This example code doesn’t do anything other than query the TCG2 UEFI protocol driver for the log, parse it and then print it to the console formatted for human eyes.

The log format is pretty simple but it’s binary so extracting individual fields and mapping them to C data types takes a bit of effort. The logic that parses the log is integrated with the formatting code so it’s not particularly useful outside of this context but it may be a useful example. The output of the log is quite verbose but a few select events are shown here:

EVENT: 2
  PCRIndex: 0
  EventType: EV_S_CRTM_VERSION (0x8)
  DigestCount: 3
  TCG_DIGEST2: 0
    AlgorithmId: EFI_TCG2_BOOT_HASH_ALG_SHA1 (0x4)
    Digest: 20 bytes
    00000000: 14 89 F9 23 C4 DC A7 29-17 8B 3E 32 33 45 85 50  *...#...)..>23E.P*
    00000010: D8 DD DF 29                                      *...)*
  TCG_DIGEST2: 1
    AlgorithmId: EFI_TCG2_BOOT_HASH_ALG_SHA256 (0xB)
    Digest: 32 bytes
    00000000: 96 A2 96 D2 24 F2 85 C6-7B EE 93 C3 0F 8A 30 91  *....$.........0.*
    00000010: 57 F0 DA A3 5D C5 B8 7E-41 0B 78 63 0A 09 CF C7  *W...]...A.xc....*
  TCG_DIGEST2: 2
    AlgorithmId: EFI_TCG2_BOOT_HASH_ALG_SHA384 (0xC)
    Digest: 48 bytes
    00000000: 1D D6 F7 B4 57 AD 88 0D-84 0D 41 C9 61 28 3B AB  *....W.....A.a(;.*
    00000010: 68 8E 94 E4 B5 93 59 EA-45 68 65 81 E9 0F EC CE  *h.....Y.Ehe.....*
    00000020: A3 C6 24 B1 22 61 13 F8-24 F3 15 EB 60 AE 0A 7C  *..$."a..$...`...*
  Event: 2 bytes
  00000000: 00 00                                            *..*

This is a pretty good example of the TPM2 log format since it clearly shows multiple and variable length hashes. Further, the same three hashes are shown as enabled by the tcg2-get-pcr-banks.efiexample as all were executed under the same OVMF image. The format from the 1.2 TPM is much easier by comparison since only the fixed width SHA1 hash is supported.

Conclusion

Upstreaming my original work for the Grub2 shell was blocked on the merge of mjg59’s patches using the verifiers framework. Now that his work has gone into upstream I’m hoping to port these over to the Grub2 shell and send them upstream too (eventually). For the short term they’re available as reference and example code from the tpm2-tcti-uefi repo.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s