From 61da563388b1bc45d25a2b216ba2a814154afec0 Mon Sep 17 00:00:00 2001 From: Krystian Hebel Date: Mon, 11 Dec 2023 17:06:25 +0100 Subject: [PATCH] tpm2-evt-log-parser.awk: add parser for TPM 2.0 event log This was tested both with TXT and firmware event logs. Log must be passed as a regular file, so neither piping it as stdin nor supplying /sys/kernel/security/tpm0/binary_bios_measurements directly works. Signed-off-by: Krystian Hebel --- anti-evil-maid.spec.in | 2 + sbin/anti-evil-maid-dump-evt-log | 22 +++++ sbin/tpm2-evt-log-parser.awk | 135 +++++++++++++++++++++++++++++++ 3 files changed, 159 insertions(+) create mode 100755 sbin/anti-evil-maid-dump-evt-log create mode 100755 sbin/tpm2-evt-log-parser.awk diff --git a/anti-evil-maid.spec.in b/anti-evil-maid.spec.in index 88835bf..9c3ef46 100644 --- a/anti-evil-maid.spec.in +++ b/anti-evil-maid.spec.in @@ -42,12 +42,14 @@ mkdir -p $RPM_BUILD_ROOT/usr/lib cp -r systemd $RPM_BUILD_ROOT/usr/lib %files +/usr/sbin/anti-evil-maid-dump-evt-log /usr/sbin/anti-evil-maid-install /usr/sbin/anti-evil-maid-lib /usr/sbin/anti-evil-maid-lib-tpm1 /usr/sbin/anti-evil-maid-lib-tpm2 /usr/sbin/anti-evil-maid-seal /usr/sbin/anti-evil-maid-tpm-setup +/usr/sbin/tpm2-evt-log-parser.awk /usr/share/doc/anti-evil-maid/README /usr/lib/systemd/system/anti-evil-maid-seal.service /usr/lib/systemd/system/tcsd.service.d/anti-evil-maid-seal.conf diff --git a/sbin/anti-evil-maid-dump-evt-log b/sbin/anti-evil-maid-dump-evt-log new file mode 100755 index 0000000..f723735 --- /dev/null +++ b/sbin/anti-evil-maid-dump-evt-log @@ -0,0 +1,22 @@ +#!/bin/bash +set -euo pipefail -o errtrace + +source anti-evil-maid-lib + +range=$(xl dmesg | grep "SLAUNCH: reserving event log" | sed "s#.*(\(.*\))#\1#") +base=$(($(echo "$range" | cut -d " " -f 1))) +size=$(($(echo "$range" | cut -d " " -f 3) - base)) + +printf "Found event log at %#x, size %#x (%d) bytes\n" $base $size $size + +validatetpm || exit 1 + +dd if=/dev/mem bs=1 skip=$base count=$size of=/tmp/log.bin + +if [ "$_tpm_version" -eq 2 ]; then + /usr/sbin/tpm2-evt-log-parser.awk /tmp/log.bin +else + echo "TPM 1.2 event log format not supported yet" +fi + +rm /tmp/log.bin diff --git a/sbin/tpm2-evt-log-parser.awk b/sbin/tpm2-evt-log-parser.awk new file mode 100755 index 0000000..8a20f36 --- /dev/null +++ b/sbin/tpm2-evt-log-parser.awk @@ -0,0 +1,135 @@ +#!/usr/bin/gawk -bf +@load "readfile" + +function assert(condition, string) +{ + if (!condition) { + print string + exit 1 + } +} + +function ord_init() +{ + for (_i = 0; _i < 256; _i++) { + ord[sprintf("%c", _i)] = _i + } +} + +function x2n(hex, width) +{ + mult = 1 + num = 0 + for (_i = 0; _i < width; _i++) { + num += ord[substr(hex, _i+1, 1)] * mult + mult *= 256 + } + return num +} + +function hexdump(hex, len) +{ + for (_i = 0; _i < len; _i++) { + printf("%02x", ord[substr(hex, _i+1, 1)]) + } +} + +function alg_name(id) +{ + switch (id) { + case 0x0004: return "SHA1" + case 0x000b: return "SHA256" + case 0x000c: return "SHA384" + case 0x000d: return "SHA512" + case 0x0012: return "SM3-256" + case 0x0027: return "SHA3-256" + case 0x0028: return "SHA3-384" + case 0x0029: return "SHA3-512" + default: return sprintf("unknown (%#06x)", id) + } +} + +function string_or_hex(str, len) +{ + _len = len + if (_len > 128) + _len = 128 + # String must start with a series of printable characters ... + if (match(str, "[[:graph:][:blank:]]*", a) != 1) { + hexdump(str, _len) + # ... long until the end, with "optional" (i.e. bad implementation) \0. + } else if (len != a[0, "length"] && + (len != a[0, "length"] + 1 || index(str, "\0") != len)) { + hexdump(str, _len) + } else + printf("%.*s", _len, a[0]) + if (_len != len) + printf("... (event truncated to %d first bytes, was %d)", _len, len) +} + +BEGIN { + PROCINFO["readfile"] + FIELDWIDTHS = "4 4 20 4 16 4 1 1 1 1 4 *" + ord_init() +} +{ + # Header sanity checks + assert($1 == "\0\0\0\0", "Bad PCR index for log header") + assert($2 == "\3\0\0\0", "Bad event type for log header") + assert(match($3, "\0{20}"), "Bad digest for log header") + assert(x2n($4, 4) >= (16+4+1+1+1+1+4+2+2+1), "Bad SpecIDEvent length") + assert($5 == "Spec ID Event03\0", "Bad SpecIDEvent signature") + assert($6 == "\1\0\0\0" || $6 == "\0\0\0\0", "Bad platform class") + assert($7 == "\0", "Bad spec minor version") + assert($8 == "\2", "Bad spec major version") + # So far there were no functional changes between revisions, only field + # names were changed. Uncomment the following if it any compatibility + # breaking change is made. + #assert(x2n($9, 1) <= 106, "Bad spec errata") + assert($10 == "\1" || $10 == "\2", "Bad UINTN size") + num_algo = x2n($11, 4) + assert(num_algo > 0, "No algorithms specified") + FIELDWIDTHS="2 2 *" + $0 = $12 +} +{ + # Iterate over algorithm sizes, save for later + print "Found " num_algo " algorithms:" + # num_algo IDs, 2 bytes each + digests_size = 2*num_algo + for (i = 0; i < num_algo; i++) { + alg[i] = x2n($1, 2) SUBSEP x2n($2, 2) + printf(" ID %#06x size = %#x\n", x2n($1, 2), x2n($2, 2)) + digests_size += x2n($2, 2) + $0 = $3 + } + vendorInfoSize = x2n($0, 1) + print "vendorInfoSize = " vendorInfoSize + FIELDWIDTHS=sprintf("4 4 4 %d 4 *", digests_size) + $0 = substr($0, vendorInfoSize+2) +} +{ + entry = 0 + printf("\n") + while (NF > 0) { + if ($3 == "\0\0\0\0") break + printf("Entry %d:\n", ++entry) + printf(" PCR: %d\n", x2n($1, 4)) + printf(" Event Type: %#x\n", x2n($2, 4)) + printf(" Digests:\n") + assert(x2n($3, 4) == num_algo, "Bad number of algorithms") + for (i = 0; i < num_algo; i++) { + split(alg[i], a, SUBSEP) + assert(x2n($4, 2) == a[1], "Bad digest algorithm") + $4 = substr($4, 3) + printf(" %s: ", alg_name(a[1])) + hexdump($4, a[2]) + printf("\n") + $4 = substr($4, a[2]+1) + } + printf(" Event: ") + string_or_hex($6, x2n($5, 4)) + printf("\n\n") + $0 = substr($6, x2n($5, 4) + 1) + } +}