From 21ade9f742f97f2ac861a1906d480d31f100256a Mon Sep 17 00:00:00 2001 From: Matt DeVillier Date: Wed, 16 Sep 2020 16:56:38 -0500 Subject: [PATCH] Use a USB security key as a TPM work-alike in the absence of a TPM On machines without a TPM, we'd still like some way for the BIOS to attest that it has not been modified. With a USB security key, we can have the BIOS use its own ROM measurement converted to a SHA256sum and truncated so it fits within an HOTP secret. Like with a TPM, a malicious BIOS with access to the correct measurements can send pre-known good measurements to the USB security key. This approach provides one big drawback in that we have to truncate the SHA256sum to 20 characters so that it fits within the limitations of HOTP secrets. This means the possibility of collisions is much higher but again, an attacker could also capture and spoof an existing ROM's measurements if they have prior access to it, either with this approach or with a TPM. Signed-off-by: Matt DeVillier --- initrd/bin/flash.sh | 17 +++++- initrd/bin/seal-hotpkey | 31 +++++----- initrd/bin/seal-totp | 123 +++++++++++++++++++++------------------- initrd/bin/unseal-hotp | 31 +++++----- initrd/etc/functions | 13 +++++ 5 files changed, 129 insertions(+), 86 deletions(-) diff --git a/initrd/bin/flash.sh b/initrd/bin/flash.sh index d82d43b71..7bf3f4d35 100755 --- a/initrd/bin/flash.sh +++ b/initrd/bin/flash.sh @@ -8,7 +8,7 @@ set -e -o pipefail case "$CONFIG_FLASHROM_OPTIONS" in -* ) - echo "Board $CONFIG_BOARD detected, continuing..." + [ "$1" != "-s" ] && echo "Board $CONFIG_BOARD detected, continuing..." ;; * ) die "ERROR: No board has been configured!\n\nEach board requires specific flashrom options and it's unsafe to flash without them.\n\nAborting." @@ -30,6 +30,10 @@ flash_rom() { else die "$ROM: Read inconsistent" fi + elif [ "$SHA" -eq 1 ]; then + flashrom $CONFIG_FLASHROM_OPTIONS -r "${ROM}" 1&>2 >/dev/null \ + || die "$ROM: Read failed" + sha256sum ${ROM} | cut -f1 -d ' ' else cp "$ROM" /tmp/${CONFIG_BOARD}.rom sha256sum /tmp/${CONFIG_BOARD}.rom @@ -52,20 +56,29 @@ flash_rom() { if [ "$1" == "-c" ]; then CLEAN=1 READ=0 + SHA=0 ROM="$2" elif [ "$1" == "-r" ]; then CLEAN=0 READ=1 + SHA=0 + ROM="$2" + touch $ROM +elif [ "$1" == "-s" ]; then + CLEAN=0 + READ=0 + SHA=1 ROM="$2" touch $ROM else CLEAN=0 READ=0 + SHA=0 ROM="$1" fi if [ ! -e "$ROM" ]; then - die "Usage: $0 [-c|-r] " + die "Usage: $0 [-c|-r|-s] " fi flash_rom $ROM diff --git a/initrd/bin/seal-hotpkey b/initrd/bin/seal-hotpkey index 70b0e933f..83b8236ae 100755 --- a/initrd/bin/seal-hotpkey +++ b/initrd/bin/seal-hotpkey @@ -24,19 +24,24 @@ else HOTPKEY_BRANDING="HOTP USB Security Dongle" fi -tpm nv_readvalue \ - -in 4d47 \ - -sz 312 \ - -of "$HOTP_SEALED" \ -|| die "Unable to retrieve sealed file from TPM NV" - -tpm unsealfile \ - -hk 40000000 \ - -if "$HOTP_SEALED" \ - -of "$HOTP_SECRET" \ -|| die "Unable to unseal HOTP secret" - -shred -n 10 -z -u "$HOTP_SEALED" 2> /dev/null +if [ "$CONFIG_TPM" = "y" ]; then + tpm nv_readvalue \ + -in 4d47 \ + -sz 312 \ + -of "$HOTP_SEALED" \ + || die "Unable to retrieve sealed file from TPM NV" + + tpm unsealfile \ + -hk 40000000 \ + -if "$HOTP_SEALED" \ + -of "$HOTP_SECRET" \ + || die "Unable to unseal HOTP secret" + + shred -n 10 -z -u "$HOTP_SEALED" 2> /dev/null +else + # without a TPM, use the first 20 characters of the ROM SHA256sum + secret_from_rom_hash > $HOTP_SECRET +fi # Store counter in file instead of TPM for now, as it conflicts with Heads # config TPM counter as TPM 1.2 can only increment one counter between reboots diff --git a/initrd/bin/seal-totp b/initrd/bin/seal-totp index f33449e58..7c98480ef 100755 --- a/initrd/bin/seal-totp +++ b/initrd/bin/seal-totp @@ -17,72 +17,79 @@ fi TOTP_SECRET="/tmp/secret/totp.key" TOTP_SEALED="/tmp/secret/totp.sealed" -dd \ - if=/dev/urandom \ - of="$TOTP_SECRET" \ - count=1 \ - bs=20 \ - 2>/dev/null \ -|| die "Unable to generate 20 random bytes" - -secret="`base32 < $TOTP_SECRET`" - -# Use the current values of the PCRs, which will be read -# from the TPM as part of the sealing ("X"). -# PCR4 == 0 means that we are still in the boot process and -# not a recovery shell. -# should this read the storage root key? -if ! tpm sealfile2 \ - -if "$TOTP_SECRET" \ - -of "$TOTP_SEALED" \ - -hk 40000000 \ - -ix 0 X \ - -ix 1 X \ - -ix 2 X \ - -ix 3 X \ - -ix 4 0000000000000000000000000000000000000000 \ - -ix 7 X \ -; then +if [ "$CONFIG_TPM" = "y" ]; then + dd \ + if=/dev/urandom \ + of="$TOTP_SECRET" \ + count=1 \ + bs=20 \ + 2>/dev/null \ + || die "Unable to generate 20 random bytes" + + secret="`base32 < $TOTP_SECRET`" + + # Use the current values of the PCRs, which will be read + # from the TPM as part of the sealing ("X"). + # PCR4 == 0 means that we are still in the boot process and + # not a recovery shell. + # should this read the storage root key? + if ! tpm sealfile2 \ + -if "$TOTP_SECRET" \ + -of "$TOTP_SEALED" \ + -hk 40000000 \ + -ix 0 X \ + -ix 1 X \ + -ix 2 X \ + -ix 3 X \ + -ix 4 0000000000000000000000000000000000000000 \ + -ix 7 X \ + ; then + shred -n 10 -z -u "$TOTP_SECRET" 2> /dev/null + die "Unable to seal secret" + fi + shred -n 10 -z -u "$TOTP_SECRET" 2> /dev/null - die "Unable to seal secret" -fi -shred -n 10 -z -u "$TOTP_SECRET" 2> /dev/null + # to create an nvram space we need the TPM owner password + # and the TPM physical presence must be asserted. + # + # The permissions are 0 since there is nothing special + # about the sealed file + tpm physicalpresence -s \ + || warn "Warning: Unable to assert physical presence" -# to create an nvram space we need the TPM owner password -# and the TPM physical presence must be asserted. -# -# The permissions are 0 since there is nothing special -# about the sealed file -tpm physicalpresence -s \ -|| warn "Warning: Unable to assert physical presence" - -# Try to write it without the password first, and then create -# the NVRAM space using the owner password if it fails for some reason. -if ! tpm nv_writevalue \ - -in $TPM_NVRAM_SPACE \ - -if "$TOTP_SEALED" \ -; then - warn 'NVRAM space does not exist? Owner password is required' - read -s -p "TPM Owner password: " tpm_password - echo - - tpm nv_definespace \ - -in $TPM_NVRAM_SPACE \ - -sz 312 \ - -pwdo "$tpm_password" \ - -per 0 \ - || die "Unable to define NVRAM space" - - tpm nv_writevalue \ + # Try to write it without the password first, and then create + # the NVRAM space using the owner password if it fails for some reason. + if ! tpm nv_writevalue \ -in $TPM_NVRAM_SPACE \ -if "$TOTP_SEALED" \ - || die "Unable to write sealed secret to NVRAM" + ; then + warn 'NVRAM space does not exist? Owner password is required' + read -s -p "TPM Owner password: " tpm_password + echo + + tpm nv_definespace \ + -in $TPM_NVRAM_SPACE \ + -sz 312 \ + -pwdo "$tpm_password" \ + -per 0 \ + || die "Unable to define NVRAM space" + + tpm nv_writevalue \ + -in $TPM_NVRAM_SPACE \ + -if "$TOTP_SEALED" \ + || die "Unable to write sealed secret to NVRAM" + fi + + shred -n 10 -z -u "$TOTP_SEALED" 2> /dev/null +else + # without a TPM, use the first 20 characters of the ROM SHA256sum + secret_from_rom_hash > $TOTP_SECRET + secret="`base32 < $TOTP_SECRET`" + shred -n 10 -z -u "$TOTP_SECRET" 2> /dev/null fi -shred -n 10 -z -u "$TOTP_SEALED" 2> /dev/null - url="otpauth://totp/$HOST?secret=$secret" secret="" diff --git a/initrd/bin/unseal-hotp b/initrd/bin/unseal-hotp index 0fc3fb28e..d3a4621d3 100755 --- a/initrd/bin/unseal-hotp +++ b/initrd/bin/unseal-hotp @@ -36,19 +36,24 @@ fi #counter_value=$(printf "%d" 0x${counter_value}) -tpm nv_readvalue \ - -in 4d47 \ - -sz 312 \ - -of "$HOTP_SEALED" \ -|| die "Unable to retrieve sealed file from TPM NV" - -tpm unsealfile \ - -hk 40000000 \ - -if "$HOTP_SEALED" \ - -of "$HOTP_SECRET" \ -|| die "Unable to unseal HOTP secret" - -shred -n 10 -z -u "$HOTP_SEALED" 2> /dev/null +if [ "$CONFIG_TPM" = "y" ]; then + tpm nv_readvalue \ + -in 4d47 \ + -sz 312 \ + -of "$HOTP_SEALED" \ + || die "Unable to retrieve sealed file from TPM NV" + + tpm unsealfile \ + -hk 40000000 \ + -if "$HOTP_SEALED" \ + -of "$HOTP_SECRET" \ + || die "Unable to unseal HOTP secret" + + shred -n 10 -z -u "$HOTP_SEALED" 2> /dev/null +else + # without a TPM, use the first 20 characters of the ROM SHA256sum + secret_from_rom_hash > $HOTP_SECRET +fi if ! hotp $counter_value < "$HOTP_SECRET"; then shred -n 10 -z -u "$HOTP_SECRET" 2> /dev/null diff --git a/initrd/etc/functions b/initrd/etc/functions index a5935f61c..8f55a1087 100755 --- a/initrd/etc/functions +++ b/initrd/etc/functions @@ -261,6 +261,19 @@ combine_configs() { cat /etc/config* > /tmp/config } +# Generate secret value using first 20 chars of ROM SHA256 hash +secret_from_rom_hash() { + local ROM_IMAGE="/tmp/coreboot-notpm.rom" + + echo -e "\nTPM not detected; measuring ROM directly\n" 1>&2 + # use a previously-copied image if it exists + if [ -f ${ROM_IMAGE} ]; then + sha256sum ${ROM_IMAGE} | cut -f1 -d ' ' | cut -c 1-20 | tr -d '\n' + else + flash.sh -s ${ROM_IMAGE} | cut -c 1-20 | tr -d '\n' + fi +} + update_checksums() { # clear screen