From 31cf85b7074a6e9a9da93faf0d5c4d1beaacb97e Mon Sep 17 00:00:00 2001 From: Kyle Rankin Date: Tue, 19 Jun 2018 12:27:27 -0700 Subject: [PATCH 01/10] Add Librem Key support to Heads The Librem Key is a custom device USB-based security token Nitrokey is producing for Purism and among other things it has custom firmware created for use with Heads. In particular, when a board is configured with CONFIG_LIBREMKEY, this custom firmware allows Heads to use the sealed TOTP secret to also send an HOTP authentication to the Librem Key. If the HOTP code is successful, the Librem Key will blink a green LED, if unsuccessful it will blink red, thereby informing the user that Heads has been tampered with without requiring them to use a phone to validate the TOTP secret. Heads will still use and show the TOTP secret, in case the user wants to validate both codes (in case the Librem Key was lost or is no longer trusted). It will also show the result of the HOTP verification (but not the code itself), even though the user should trust only what the Librem Key displays, so the user can confirm that both the device and Heads are in sync. If HOTP is enabled, Heads will maintain a new TPM counter separate from the Heads TPM counter that will increment each time HOTP codes are checked. This change also modifies the routines that update TOTP so that if the Librem Key executables are present it will also update HOTP codes and synchronize them with a Librem Key. --- Makefile | 2 +- boards/librem13v2/librem13v2.config | 1 + boards/librem15v3/librem15v3.config | 1 + initrd/bin/gui-init | 90 +++++++++++++++-------- initrd/bin/seal-libremkey | 84 +++++++++++++++++++++ initrd/bin/unseal-hotp | 63 ++++++++++++++++ initrd/etc/functions | 9 +-- modules/libremkey-hotp-verification | 19 +++++ patches/libremkey-hotp-verification.patch | 21 ++++++ 9 files changed, 253 insertions(+), 37 deletions(-) create mode 100755 initrd/bin/seal-libremkey create mode 100755 initrd/bin/unseal-hotp create mode 100644 modules/libremkey-hotp-verification create mode 100644 patches/libremkey-hotp-verification.patch diff --git a/Makefile b/Makefile index e77ecc56b..b1049b9e8 100644 --- a/Makefile +++ b/Makefile @@ -395,7 +395,7 @@ bin_modules-$(CONFIG_FLASHTOOLS) += flashtools bin_modules-$(CONFIG_NEWT) += newt bin_modules-$(CONFIG_CAIRO) += cairo bin_modules-$(CONFIG_FBWHIPTAIL) += fbwhiptail -bin_modules-$(CONFIG_NITROKEY) += nitrokey-hotp-verification +bin_modules-$(CONFIG_LIBREMKEY) += libremkey-hotp-verification $(foreach m, $(bin_modules-y), \ $(call map,initrd_bin_add,$(call bins,$m)) \ diff --git a/boards/librem13v2/librem13v2.config b/boards/librem13v2/librem13v2.config index 616a94724..699d591b8 100644 --- a/boards/librem13v2/librem13v2.config +++ b/boards/librem13v2/librem13v2.config @@ -20,6 +20,7 @@ CONFIG_TPMTOTP=y #CONFIG_NEWT=y CONFIG_CAIRO=y CONFIG_FBWHIPTAIL=y +CONFIG_LIBREMKEY=y CONFIG_LINUX_USB=y diff --git a/boards/librem15v3/librem15v3.config b/boards/librem15v3/librem15v3.config index a9a194be3..61c17042c 100644 --- a/boards/librem15v3/librem15v3.config +++ b/boards/librem15v3/librem15v3.config @@ -22,6 +22,7 @@ CONFIG_TPMTOTP=y #CONFIG_NEWT=y CONFIG_CAIRO=y CONFIG_FBWHIPTAIL=y +CONFIG_LIBREMKEY=y CONFIG_LINUX_USB=y diff --git a/initrd/bin/gui-init b/initrd/bin/gui-init index 4577dd635..27654cdc5 100755 --- a/initrd/bin/gui-init +++ b/initrd/bin/gui-init @@ -75,12 +75,7 @@ update_checksums() # We don't need them after the user decides to sign rm -f /boot/kexec_package_trigger* - # sign and auto-roll config counter - extparam= - if [ "$CONFIG_TPM" = "y" ]; then - extparam=-u - fi - kexec-sign-config -p /boot $extparam \ + kexec-sign-config -p /boot \ || die "Failed to sign default config" # switch back to ro mode @@ -89,6 +84,20 @@ update_checksums() echo "Returning to the main menu" fi } +update_totp() +{ + echo "Scan the QR code to add the new TOTP secret" + /bin/seal-totp + if [ -x /bin/libremkey_hotp_verification ]; then + echo "Once you have scanned the QR code, hit Enter to configure your Librem Key" + read + /bin/seal-libremkey + else + echo "Once you have scanned the QR code, hit Enter to reboot" + read + fi + /bin/reboot +} last_half=X while true; do @@ -106,7 +115,7 @@ while true; do if [ $? -ne 0 ]; then whiptail $CONFIG_ERROR_BG_COLOR --clear --title "ERROR: TOTP Generation Failed!" \ --menu "ERROR: Heads couldn't generate the TOTP code.\n\nIf you just reflashed your BIOS, you'll need to generate a new TOTP secret.\n\nIf you have not just reflashed your BIOS, THIS COULD INDICATE TAMPERING!\n\nIf this is the first time the system has booted, you should reset the TPM\nand set your own password\n\nHow would you like to proceed?" 30 90 4 \ - 'g' ' Generate new TOTP secret' \ + 'g' ' Generate new TOTP/HOTP secret' \ 'i' ' Ignore error and continue to default boot menu' \ 'p' ' Reset the TPM' \ 'x' ' Exit to recovery shell' \ @@ -117,11 +126,30 @@ while true; do fi if [ "$totp_confirm" = "i" -o -z "$totp_confirm" ]; then + if [ -x /bin/libremkey_hotp_verification ]; then + HOTP=`unseal-hotp` + enable_usb + # Don't output HOTP codes to screen, so as to make replay attacks harder + libremkey_hotp_verification check $HOTP + case "$?" in + 0 ) + HOTP="success" + ;; + 4 ) + HOTP="invalid code" + ;; + * ) + HOTP="error checking code" + ;; + esac + else + HOTP='N/A' + fi + whiptail --clear --title "$CONFIG_BOOT_GUI_MENU_NAME" \ - --menu "$date\nTOTP code: $TOTP" 20 90 10 \ + --menu "$date\nTOTP: $TOTP | HOTP: $HOTP" 20 90 10 \ 'y' ' Default boot' \ - 'r' ' TOTP does not match, refresh code' \ - 'n' ' TOTP does not match after refresh, troubleshoot' \ + 'r' ' TOTP/HOTP does not match, refresh code' \ 'o' ' Other Boot Options -->' \ 'a' ' Advanced Settings -->' \ 'x' ' Exit to recovery shell' \ @@ -145,10 +173,11 @@ while true; do if [ "$totp_confirm" = "a" ]; then whiptail --clear --title "Advanced Settings" \ --menu "Configure Advanced Settings" 20 90 10 \ - 'g' ' Generate new TOTP secret' \ - 'p' ' Reset the TPM' \ + 'g' ' Generate new TOTP/HOTP secret' \ 's' ' Update checksums and sign all files in /boot' \ 'f' ' Flash/Update the BIOS -->' \ + 'p' ' Reset the TPM' \ + 'n' ' TOTP/HOTP does not match after refresh, troubleshoot' \ 'r' ' <-- Return to main menu' \ 2>/tmp/whiptail || recovery "GUI menu failed" @@ -164,14 +193,14 @@ while true; do fi if [ "$totp_confirm" = "n" ]; then - if (whiptail $CONFIG_WARNING_BG_COLOR --title "TOTP code mismatched" \ - --yesno "TOTP code mismatches could indicate either TPM tampering or clock drift:\n\nTo correct clock drift: 'date -s HH:MM:SS'\nand save it to the RTC: 'hwclock -w'\nthen reboot and try again.\n\nWould you like to exit to a recovery console?" 30 90) then + if (whiptail $CONFIG_WARNING_BG_COLOR --title "TOTP/HOTP code mismatched" \ + --yesno "TOTP/HOTP code mismatches could indicate either TPM tampering or clock drift:\n\nTo correct clock drift: 'date -s HH:MM:SS'\nand save it to the RTC: 'hwclock -w'\nthen reboot and try again.\n\nWould you like to exit to a recovery console?" 30 90) then echo "" echo "To correct clock drift: 'date -s HH:MM:SS'" echo "and save it to the RTC: 'hwclock -w'" echo "then reboot and try again" echo "" - recovery "TOTP mismatch" + recovery "TOTP/HOTP mismatch" else continue fi @@ -183,13 +212,9 @@ while true; do fi if [ "$totp_confirm" = "g" ]; then - if (whiptail --title 'Generate new TOTP secret' \ + if (whiptail --title 'Generate new TOTP/HOTP secret' \ --yesno "This will erase your old secret and replace it with a new one!\n\nDo you want to proceed?" 16 90) then - echo "Scan the QR code to add the new TOTP secret" - /bin/seal-totp - echo "Once you have scanned the QR code, hit Enter to reboot" - read - /bin/reboot + update_totp else echo "Returning to the main menu" fi @@ -198,20 +223,27 @@ while true; do if [ "$totp_confirm" = "p" ]; then if (whiptail --title 'Reset the TPM' \ - --yesno "This will clear the TPM, erase the old TPM password and replace it with a new one!\n\nDo you want to proceed?" 16 90) then + --yesno "This will clear the TPM and TPM password, replace them with new ones!\n\nDo you want to proceed?" 16 90) then /bin/tpm-reset - # now that the TPM is reset, remove invalid kexec_rollback.txt file + # now that the TPM is reset, remove invalid TPM counter files mount_boot mount -o rw,remount /boot - rm -f /boot/kexec_rollback.txt + rm -f /boot/kexec_rollback.txt /boot/kexec_hotp_counter + + # create Heads TPM counter before any others + check_tpm_counter /boot/kexec_rollback.txt \ + || die "Unable to find/create tpm counter" + counter="$TPM_COUNTER" + + increment_tpm_counter $counter \ + || die "Unable to increment tpm counter" + + sha256sum /tmp/counter-$counter > /boot/kexec_rollback.txt \ + || die "Unable to create rollback file" mount -o ro,remount /boot - echo "Scan the QR code to add the new TOTP secret" - /bin/seal-totp - echo "Once you have scanned the QR code, hit Enter to reboot" - read - /bin/reboot + update_totp else echo "Returning to the main menu" fi diff --git a/initrd/bin/seal-libremkey b/initrd/bin/seal-libremkey new file mode 100755 index 000000000..2b7da4005 --- /dev/null +++ b/initrd/bin/seal-libremkey @@ -0,0 +1,84 @@ +#!/bin/sh +# Retrieve the sealed TOTP secret and initialize a Librem Key with it + +. /etc/functions + +HOTP_SEALED="/tmp/secret/hotp.sealed" +HOTP_SECRET="/tmp/secret/hotp.key" +HOTP_COUNTER="/boot/kexec_hotp_counter" + +mount_boot() +{ + # Mount local disk if it is not already mounted + if ! grep -q /boot /proc/mounts ; then + mount -o ro /boot \ + || recovery "Unable to mount /boot" + 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" + +rm -f "$HOTP_SEALED" +secret="`cat $HOTP_SECRET`" +rm -f "$HOTP_SECRET" + +# get current value of HOTP counter in TPM, create if absent +mount_boot + +check_tpm_counter $HOTP_COUNTER hotp \ +|| die "Unable to find/create TPM counter" +counter="$TPM_COUNTER" + +counter_value=$(read_tpm_counter $counter | cut -f2 -d ' ' | awk 'gsub("^000e","")') + +if [ "$counter_value" == "" ]; then + die "Unable to read HOTP TPM counter" +fi + +counter_value=$(printf "%d" 0x${counter_value}) + +enable_usb +if ! libremkey_hotp_verification info ; then + echo "Insert your Librem Key and press Enter to configure it" + read + libremkey_hotp_verification info \ + || die "Unable to find Librem Key" +fi + +read -s -p "Enter your Librem Key Admin PIN" admin_pin +echo + +libremkey_hotp_initialize $admin_pin $secret $counter_value +if [ $? -ne 0 ]; then + read -s -p "Error setting HOTP secret, re-enter Admin PIN and try again:" admin_pin + libremkey_hotp_initialize $admin_pin $secret $counter_value \ + || die "Setting HOTP secret failed" +fi + +secret="" + +# Make sure our counter is incremented ahead of the next check +increment_tpm_counter $counter > /dev/null \ +|| die "Unable to increment tpm counter" +increment_tpm_counter $counter > /dev/null \ +|| die "Unable to increment tpm counter" + +mount -o remount,rw /boot +sha256sum /tmp/counter-$counter > $HOTP_COUNTER \ +|| die "Unable to create hotp counter file" +mount -o remount,ro /boot + +echo "Librem Key initialized successfully. Press Enter to continue." +read + +exit 0 diff --git a/initrd/bin/unseal-hotp b/initrd/bin/unseal-hotp new file mode 100755 index 000000000..5d7d225d0 --- /dev/null +++ b/initrd/bin/unseal-hotp @@ -0,0 +1,63 @@ +#!/bin/sh +# Retrieve the sealed file and counter from the NVRAM, unseal it and compute the hotp + +. /etc/functions + +HOTP_SEALED="/tmp/secret/hotp.sealed" +HOTP_SECRET="/tmp/secret/hotp.key" +HOTP_COUNTER="/boot/kexec_hotp_counter" + +mount_boot() +{ + # Mount local disk if it is not already mounted + if ! grep -q /boot /proc/mounts ; then + mount -o ro /boot \ + || recovery "Unable to mount /boot" + 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" + +rm -f "$HOTP_SEALED" + +# get current value of HOTP counter in TPM, create if absent +mount_boot + +check_tpm_counter $HOTP_COUNTER hotp \ +|| die "Unable to find/create TPM counter" +counter="$TPM_COUNTER" + +counter_value=$(read_tpm_counter $counter | cut -f2 -d ' ' | awk 'gsub("^000e","")') + +if [ "$counter_value" == "" ]; then + die "Unable to read HOTP TPM counter" +fi + +counter_value=$(printf "%d" 0x${counter_value}) + +if ! hotp $counter_value < "$HOTP_SECRET"; then + rm -f "$HOTP_SECRET" + die 'Unable to compute HOTP hash?' +fi + +rm -f "$HOTP_SECRET" + +increment_tpm_counter $counter > /dev/null \ +|| die "Unable to increment tpm counter" + +mount -o remount,rw /boot +sha256sum /tmp/counter-$counter > $HOTP_COUNTER \ +|| die "Unable to create hotp counter file" +mount -o remount,ro /boot + +exit 0 diff --git a/initrd/etc/functions b/initrd/etc/functions index 17a81c277..4488476a4 100755 --- a/initrd/etc/functions +++ b/initrd/etc/functions @@ -138,18 +138,13 @@ confirm_gpg_card() check_tpm_counter() { + LABEL=${2:-3135106223} # if the /boot.hashes file already exists, read the TPM counter ID # from it. if [ -r "$1" ]; then TPM_COUNTER=`grep counter- "$1" | cut -d- -f2` else - # Initialize label to default if not set - if [ "$2" != "" ]; then - LABEL=$2 - else - LABEL=3135106223 - fi - warn "$BOOT_HASHES does not exist; creating new TPM counter" + warn "$1 does not exist; creating new TPM counter" read -s -p "TPM Owner password: " tpm_password echo tpm counter_create \ diff --git a/modules/libremkey-hotp-verification b/modules/libremkey-hotp-verification new file mode 100644 index 000000000..c9b9d45ab --- /dev/null +++ b/modules/libremkey-hotp-verification @@ -0,0 +1,19 @@ +modules-$(CONFIG_LIBREMKEY) += libremkey-hotp-verification + +libremkey-hotp-verification_depends := libusb $(musl_dep) + +libremkey-hotp-verification_version := git +libremkey-hotp-verification_dir := libremkey-hotp-verification +libremkey-hotp-verification_repo := --recursive https://github.com/Nitrokey/nitrokey-hotp-verification + +libremkey-hotp-verification_target := \ + $(MAKE_JOBS) \ + $(CROSS_TOOLS) \ + +libremkey-hotp-verification_output := \ + libremkey_hotp_verification \ + libremkey_hotp_initialize + +libremkey-hotp-verification_configure := \ + INSTALL="$(INSTALL)" \ + cmake -DCMAKE_TOOLCHAIN_FILE=./Toolchain-heads.cmake -DCMAKE_AR="$(CROSS)ar" . diff --git a/patches/libremkey-hotp-verification.patch b/patches/libremkey-hotp-verification.patch new file mode 100644 index 000000000..13ad08610 --- /dev/null +++ b/patches/libremkey-hotp-verification.patch @@ -0,0 +1,21 @@ +--- nitrokey-hotp-verification-a/Toolchain-heads.cmake 2018-05-22 09:55:46.907209235 -0700 ++++ nitrokey-hotp-verification-b/Toolchain-heads.cmake 2018-05-22 09:55:26.659371966 -0700 +@@ -0,0 +1,18 @@ ++SET(CMAKE_SYSTEM_NAME Linux) ++SET(CMAKE_SYSTEM_VERSION 1) ++ ++# Specify the cross compiler ++SET(CMAKE_C_COMPILER $ENV{INSTALL}/bin/musl-gcc) ++SET(CMAKE_CXX_COMPILER $ENV{INSTALL}/bin/musl-gcc) ++ ++# Where is the target environment ++SET(CMAKE_FIND_ROOT_PATH $ENV{INSTALL}) ++ ++# Search for programs only in the build host directories ++SET(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) ++ ++# Search for libraries and headers only in the target directories ++SET(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) ++SET(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) ++ ++INCLUDE_DIRECTORIES(hidapi) From 2cacb1572971b50923c9844e19e48bcc9e139a15 Mon Sep 17 00:00:00 2001 From: Kyle Rankin Date: Tue, 19 Jun 2018 13:03:01 -0700 Subject: [PATCH 02/10] Add back TPM config counter section to gui-init The section in gui-init that modifies the Heads TPM counter when signing config was accidentally removed. This change adds that section back. --- initrd/bin/gui-init | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/initrd/bin/gui-init b/initrd/bin/gui-init index 27654cdc5..aaa47da86 100755 --- a/initrd/bin/gui-init +++ b/initrd/bin/gui-init @@ -75,7 +75,12 @@ update_checksums() # We don't need them after the user decides to sign rm -f /boot/kexec_package_trigger* - kexec-sign-config -p /boot \ + # sign and auto-roll config counter + extparam= + if [ "$CONFIG_TPM" = "y" ]; then + extparam=-u + fi + kexec-sign-config -p /boot $extparam \ || die "Failed to sign default config" # switch back to ro mode From c42084406d47409ccce5ac6ddd717b576b693eb3 Mon Sep 17 00:00:00 2001 From: Kyle Rankin Date: Tue, 19 Jun 2018 16:18:10 -0700 Subject: [PATCH 03/10] Use HOTP TPM counter instead of Heads when signing, if present TPM v1.2 has a limitation in that only a single monotonic counter can be incremented between reboots [1]. So in the event we are using HOTP monotonic counters, we need to reference those for the Heads rollback counter when we update file signatures in /boot, otherwise the increment stage at kexec-sign-config will fail since at each boot, the HOTP monotonic counter has already been incremented. [1] https://projects.csail.mit.edu/tc/tpmj/UsersGuide.html#inccounter --- initrd/bin/gui-init | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/initrd/bin/gui-init b/initrd/bin/gui-init index aaa47da86..1f4e3dcf4 100755 --- a/initrd/bin/gui-init +++ b/initrd/bin/gui-init @@ -78,7 +78,14 @@ update_checksums() # sign and auto-roll config counter extparam= if [ "$CONFIG_TPM" = "y" ]; then - extparam=-u + if [ -x /bin/libremkey_hotp_verification ]; then + check_tpm_counter /boot/kexec_hotp_counter hotp \ + || die "Unable to find/create TPM counter" + counter="$TPM_COUNTER" + extparam="-c $counter" + else + extparam=-u + fi fi kexec-sign-config -p /boot $extparam \ || die "Failed to sign default config" @@ -241,9 +248,6 @@ while true; do || die "Unable to find/create tpm counter" counter="$TPM_COUNTER" - increment_tpm_counter $counter \ - || die "Unable to increment tpm counter" - sha256sum /tmp/counter-$counter > /boot/kexec_rollback.txt \ || die "Unable to create rollback file" mount -o ro,remount /boot From 7dde5c2aca74c19195a71338324743bf3bf4d952 Mon Sep 17 00:00:00 2001 From: Kyle Rankin Date: Tue, 19 Jun 2018 16:28:37 -0700 Subject: [PATCH 04/10] Revert "Use HOTP TPM counter instead of Heads when signing, if present" This reverts commit c42084406d47409ccce5ac6ddd717b576b693eb3. --- initrd/bin/gui-init | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/initrd/bin/gui-init b/initrd/bin/gui-init index 1f4e3dcf4..aaa47da86 100755 --- a/initrd/bin/gui-init +++ b/initrd/bin/gui-init @@ -78,14 +78,7 @@ update_checksums() # sign and auto-roll config counter extparam= if [ "$CONFIG_TPM" = "y" ]; then - if [ -x /bin/libremkey_hotp_verification ]; then - check_tpm_counter /boot/kexec_hotp_counter hotp \ - || die "Unable to find/create TPM counter" - counter="$TPM_COUNTER" - extparam="-c $counter" - else - extparam=-u - fi + extparam=-u fi kexec-sign-config -p /boot $extparam \ || die "Failed to sign default config" @@ -248,6 +241,9 @@ while true; do || die "Unable to find/create tpm counter" counter="$TPM_COUNTER" + increment_tpm_counter $counter \ + || die "Unable to increment tpm counter" + sha256sum /tmp/counter-$counter > /boot/kexec_rollback.txt \ || die "Unable to create rollback file" mount -o ro,remount /boot From fe34aba719073ef3710b25627b0acbbb5236815d Mon Sep 17 00:00:00 2001 From: Kyle Rankin Date: Wed, 20 Jun 2018 09:20:39 -0700 Subject: [PATCH 05/10] Store HOTP counter directly in /boot instead of TPM The HOTP counter isn't a secret but is just used to prevent replay attacks (the time-based counter in TOTP isn't a secret either) so it doesn't need to be protected in the TPM and storing it as a TPM monotonic counter was causing conflicts with the Heads configuration counter as TPM 1.2 can only increment one counter per reboot. This change moves the HOTP counter into the file in /boot that was previously keeping track of the TPM counter id. --- initrd/bin/gui-init | 2 +- initrd/bin/seal-libremkey | 36 ++++++++++++++++++++++-------------- initrd/bin/unseal-hotp | 28 +++++++++++++++++++--------- 3 files changed, 42 insertions(+), 24 deletions(-) diff --git a/initrd/bin/gui-init b/initrd/bin/gui-init index aaa47da86..64122b72a 100755 --- a/initrd/bin/gui-init +++ b/initrd/bin/gui-init @@ -234,7 +234,7 @@ while true; do # now that the TPM is reset, remove invalid TPM counter files mount_boot mount -o rw,remount /boot - rm -f /boot/kexec_rollback.txt /boot/kexec_hotp_counter + rm -f /boot/kexec_rollback.txt # create Heads TPM counter before any others check_tpm_counter /boot/kexec_rollback.txt \ diff --git a/initrd/bin/seal-libremkey b/initrd/bin/seal-libremkey index 2b7da4005..ddee9e979 100755 --- a/initrd/bin/seal-libremkey +++ b/initrd/bin/seal-libremkey @@ -32,20 +32,23 @@ rm -f "$HOTP_SEALED" secret="`cat $HOTP_SECRET`" rm -f "$HOTP_SECRET" +# 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 # get current value of HOTP counter in TPM, create if absent mount_boot -check_tpm_counter $HOTP_COUNTER hotp \ -|| die "Unable to find/create TPM counter" -counter="$TPM_COUNTER" +#check_tpm_counter $HOTP_COUNTER hotp \ +#|| die "Unable to find/create TPM counter" +#counter="$TPM_COUNTER" +# +#counter_value=$(read_tpm_counter $counter | cut -f2 -d ' ' | awk 'gsub("^000e","")') +#if [ "$counter_value" == "" ]; then +# die "Unable to read HOTP counter" +#fi -counter_value=$(read_tpm_counter $counter | cut -f2 -d ' ' | awk 'gsub("^000e","")') +#counter_value=$(printf "%d" 0x${counter_value}) -if [ "$counter_value" == "" ]; then - die "Unable to read HOTP TPM counter" -fi - -counter_value=$(printf "%d" 0x${counter_value}) +counter_value=1 enable_usb if ! libremkey_hotp_verification info ; then @@ -68,14 +71,19 @@ fi secret="" # Make sure our counter is incremented ahead of the next check -increment_tpm_counter $counter > /dev/null \ -|| die "Unable to increment tpm counter" -increment_tpm_counter $counter > /dev/null \ -|| die "Unable to increment tpm counter" +#increment_tpm_counter $counter > /dev/null \ +#|| die "Unable to increment tpm counter" +#increment_tpm_counter $counter > /dev/null \ +#|| die "Unable to increment tpm counter" mount -o remount,rw /boot -sha256sum /tmp/counter-$counter > $HOTP_COUNTER \ + +counter_value=`expr $counter_value + 1` +echo $counter_value > $HOTP_COUNTER \ || die "Unable to create hotp counter file" + +#sha256sum /tmp/counter-$counter > $HOTP_COUNTER \ +#|| die "Unable to create hotp counter file" mount -o remount,ro /boot echo "Librem Key initialized successfully. Press Enter to continue." diff --git a/initrd/bin/unseal-hotp b/initrd/bin/unseal-hotp index 5d7d225d0..8d4ef192f 100755 --- a/initrd/bin/unseal-hotp +++ b/initrd/bin/unseal-hotp @@ -30,20 +30,25 @@ tpm unsealfile \ rm -f "$HOTP_SEALED" +# 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 # get current value of HOTP counter in TPM, create if absent mount_boot -check_tpm_counter $HOTP_COUNTER hotp \ -|| die "Unable to find/create TPM counter" -counter="$TPM_COUNTER" +#check_tpm_counter $HOTP_COUNTER hotp \ +#|| die "Unable to find/create TPM counter" +#counter="$TPM_COUNTER" +# +#counter_value=$(read_tpm_counter $counter | cut -f2 -d ' ' | awk 'gsub("^000e","")') +# -counter_value=$(read_tpm_counter $counter | cut -f2 -d ' ' | awk 'gsub("^000e","")') +counter_value=$(cat $HOTP_COUNTER) if [ "$counter_value" == "" ]; then - die "Unable to read HOTP TPM counter" + die "Unable to read HOTP counter" fi -counter_value=$(printf "%d" 0x${counter_value}) +#counter_value=$(printf "%d" 0x${counter_value}) if ! hotp $counter_value < "$HOTP_SECRET"; then rm -f "$HOTP_SECRET" @@ -52,12 +57,17 @@ fi rm -f "$HOTP_SECRET" -increment_tpm_counter $counter > /dev/null \ -|| die "Unable to increment tpm counter" +#increment_tpm_counter $counter > /dev/null \ +#|| die "Unable to increment tpm counter" mount -o remount,rw /boot -sha256sum /tmp/counter-$counter > $HOTP_COUNTER \ + +counter_value=`expr $counter_value + 1` +echo $counter_value > $HOTP_COUNTER \ || die "Unable to create hotp counter file" + +#sha256sum /tmp/counter-$counter > $HOTP_COUNTER \ +#|| die "Unable to create hotp counter file" mount -o remount,ro /boot exit 0 From ec3248dbc948ded7ea115fd2d24599d9ad852043 Mon Sep 17 00:00:00 2001 From: Kyle Rankin Date: Wed, 20 Jun 2018 16:20:15 -0700 Subject: [PATCH 06/10] Shorten timeout for Librem Key Currently the Librem Key tests will time out after 40 seconds, which adds to the boot time significantly if the user wants to boot without inserting it. This patch changes that timeout to one second. --- patches/libremkey-hotp-verification.patch | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/patches/libremkey-hotp-verification.patch b/patches/libremkey-hotp-verification.patch index 13ad08610..5376da03b 100644 --- a/patches/libremkey-hotp-verification.patch +++ b/patches/libremkey-hotp-verification.patch @@ -19,3 +19,14 @@ +SET(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) + +INCLUDE_DIRECTORIES(hidapi) +--- libremkey-hotp-verification/device.c 2018-06-20 16:13:36.417804210 -0700 ++++ libremkey-hotp-verification-b/device.c 2018-06-20 16:14:34.532367723 -0700 +@@ -34,7 +34,7 @@ + const unsigned short m_vid = 0x20a0; + const unsigned short m_pid = 0x4108; + +-static const int CONNECTION_ATTEMPTS_COUNT = 80; ++static const int CONNECTION_ATTEMPTS_COUNT = 2; + + static const int CONNECTION_ATTEMPT_DELAY_MICRO_SECONDS = 1000*1000/2; + From be665ac4f9f9c4054b9cbbdf55d124e5aee4f002 Mon Sep 17 00:00:00 2001 From: Kyle Rankin Date: Thu, 21 Jun 2018 16:04:46 -0700 Subject: [PATCH 07/10] Show red background when HOTP code is invalid Granted the user should really be using the Librem Key/phone to check for tampering (since an attacker could control the Heads background color) but this provides another visual queue for the user with the GUI menu to catch less sophisticated tampering. --- initrd/bin/gui-init | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/initrd/bin/gui-init b/initrd/bin/gui-init index 64122b72a..7424b13fd 100755 --- a/initrd/bin/gui-init +++ b/initrd/bin/gui-init @@ -107,6 +107,7 @@ update_totp() last_half=X while true; do MAIN_MENU_OPTIONS="" + MAIN_MENU_BG_COLOR="" unset totp_confirm # update the TOTP code every thirty seconds date=`date "+%Y-%m-%d %H:%M:%S"` @@ -142,6 +143,7 @@ while true; do ;; 4 ) HOTP="invalid code" + MAIN_MENU_BG_COLOR=$CONFIG_ERROR_BG_COLOR ;; * ) HOTP="error checking code" @@ -151,7 +153,7 @@ while true; do HOTP='N/A' fi - whiptail --clear --title "$CONFIG_BOOT_GUI_MENU_NAME" \ + whiptail $MAIN_MENU_BG_COLOR --clear --title "$CONFIG_BOOT_GUI_MENU_NAME" \ --menu "$date\nTOTP: $TOTP | HOTP: $HOTP" 20 90 10 \ 'y' ' Default boot' \ 'r' ' TOTP/HOTP does not match, refresh code' \ From acb2b34873e641dfc5d94c6de6ee62422ae8809a Mon Sep 17 00:00:00 2001 From: Kyle Rankin Date: Thu, 21 Jun 2018 16:30:35 -0700 Subject: [PATCH 08/10] Show warning bg color in main menu when HOTP key not found --- initrd/bin/gui-init | 1 + 1 file changed, 1 insertion(+) diff --git a/initrd/bin/gui-init b/initrd/bin/gui-init index 7424b13fd..9e75b42a3 100755 --- a/initrd/bin/gui-init +++ b/initrd/bin/gui-init @@ -147,6 +147,7 @@ while true; do ;; * ) HOTP="error checking code" + MAIN_MENU_BG_COLOR=$CONFIG_WARNING_BG_COLOR ;; esac else From fd99d160e83550518b754fd1eb7f9b4925c08ae0 Mon Sep 17 00:00:00 2001 From: Kyle Rankin Date: Tue, 3 Jul 2018 12:40:52 -0700 Subject: [PATCH 09/10] Improve status messages for Librem Key HOTP output --- initrd/bin/gui-init | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/initrd/bin/gui-init b/initrd/bin/gui-init index 9e75b42a3..957ebba41 100755 --- a/initrd/bin/gui-init +++ b/initrd/bin/gui-init @@ -139,14 +139,14 @@ while true; do libremkey_hotp_verification check $HOTP case "$?" in 0 ) - HOTP="success" + HOTP="Success" ;; 4 ) - HOTP="invalid code" + HOTP="Invalid code" MAIN_MENU_BG_COLOR=$CONFIG_ERROR_BG_COLOR ;; * ) - HOTP="error checking code" + HOTP="Error checking code, Insert Librem Key and retry" MAIN_MENU_BG_COLOR=$CONFIG_WARNING_BG_COLOR ;; esac From 79a09e7424c67fc3168fef95a0352292a9bcf8eb Mon Sep 17 00:00:00 2001 From: Kyle Rankin Date: Wed, 7 Nov 2018 13:27:52 -0800 Subject: [PATCH 10/10] Ignore PCR5 when sealing key when Librem Key is enabled When the Librem Key is enabled, the kernel loads USB modules at boot, this causes PCR5 to change and breaks unsealing the LUKS key (if set). This change retains the protection of the PCR5 check unless Librem Key is enabled. --- initrd/bin/kexec-seal-key | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/initrd/bin/kexec-seal-key b/initrd/bin/kexec-seal-key index 5f6ab6b39..da5a187e2 100755 --- a/initrd/bin/kexec-seal-key +++ b/initrd/bin/kexec-seal-key @@ -84,6 +84,14 @@ cat "$KEY_DEVICES" | cut -d\ -f1 | xargs /bin/qubes-measure-luks \ || die "Unable to measure the LUKS headers" luks_pcr=`tpm calcfuturepcr -ix 16 -if /tmp/luksDump.txt` +# Librem Key loads USB modules which changes PCR5. +# In the event Librem Key is enabled, skip verification of PCR5 +if [ -x /bin/libremkey_hotp_verification ]; then + pcr_5="X" +else + pcr_5="0000000000000000000000000000000000000000" +fi + # Note that PCR 4 needs to be set with the "normal-boot" # path value, which we do not have right now since we are # in a recovery shell. @@ -104,7 +112,7 @@ tpm sealfile2 \ -ix 2 X \ -ix 3 X \ -ix 4 0000000000000000000000000000000000000000 \ - -ix 5 0000000000000000000000000000000000000000 \ + -ix 5 $pcr_5 \ -ix 6 $luks_pcr \ -ix 7 X \ || die "Unable to seal secret"