#!/bin/bash # This will generate a disk encryption key and seal / ecncrypt # with the current PCRs and then store it in the TPM NVRAM. # It will then need to be bundled into initrd that is booted. set -e -o pipefail . /etc/functions TPM_INDEX=3 TPM_SIZE=312 KEY_FILE="/tmp/secret/secret.key" TPM_SEALED="/tmp/secret/secret.sealed" RECOVERY_KEY="/tmp/secret/recovery.key" . /etc/functions . /tmp/config TRACE_FUNC paramsdir=$1 if [ -z "$paramsdir" ]; then die "Usage $0 /boot" fi KEY_DEVICES="$paramsdir/kexec_key_devices.txt" KEY_LVM="$paramsdir/kexec_key_lvm.txt" if [ ! -r "$KEY_DEVICES" ]; then die "No devices defined for disk encryption" else DEBUG "Devices defined for disk encryption: $(cat "$KEY_DEVICES" | cut -d\ -f1 | tr '\n' ' ')" fi if [ -r "$KEY_LVM" ]; then # Activate the LVM volume group VOLUME_GROUP=$(cat $KEY_LVM) if [ -z "$VOLUME_GROUP" ]; then die "No LVM volume group defined for activation" fi lvm vgchange -a y $VOLUME_GROUP || die "$VOLUME_GROUP: unable to activate volume group" else DEBUG "No LVM volume group defined for activation" fi DEBUG "$(pcrs)" # LUKS Key slot 0 is the manual recovery pass phrase # that they user entered when they installed OS, # key slot 1 is the one that we've generated. read -s -p "Enter LUKS Disk Recovery Key/passphrase: " disk_password echo -n "$disk_password" >"$RECOVERY_KEY" echo read -s -p "New LUKS TPM Disk Unlock Key passphrase for booting: " key_password echo read -s -p "Repeat LUKS TPM Disk Unlock Key passphrase for booting: " key_password2 echo if [ "$key_password" != "$key_password2" ]; then die "Key passphrases do not match" fi # Generate key file echo "++++++ Generating new randomized 128 bytes key file that will be sealed/unsealed by LUKS TPM Disk Unlock Key passphrase" dd \ if=/dev/urandom \ of="$KEY_FILE" \ bs=1 \ count=128 \ 2>/dev/null || die "Unable to generate 128 random bytes" # Count the number of slots used on each device for dev in $(cat "$KEY_DEVICES" | cut -d\ -f1); do DEBUG "Checking number of slots used on $dev LUKS header" #check if the device is a LUKS device with luks[1,2] # Get the number of key slots used on the LUKS header. # LUKS1 Format is : # Slot 0: ENABLED # Slot 1: ENABLED # Slot 2: DISABLED # Slot 3: DISABLED #... # Slot 7: DISABLED # Luks2 only reports on enabled slots. # luks2 Format is : # 0: luks2 # 1: luks2 # Meaning that the number of slots used is the number of lines returned by a grep on the LUKS2 above format. # We need to count the number of ENABLED slots for both LUKS1 and LUKS2 # create regex pattern for both LUKS1 and LUKS2 regex="Slot [0-9]*: ENABLED" regex+="\|" regex+="[0-9]*: luks2" slots_used=$(cryptsetup luksDump "$dev" | grep -c "$regex" || die "Unable to get number of slots used on $dev") DEBUG "Number of slots used on $dev LUKS header: $slots_used" # If slot1 is the only one used, warn and die with proper messages if [ "$slots_used" -eq 1 ]; then # Check if slot 1 is the only one existing if [ "$(cryptsetup luksDump "$dev" | grep -c "Slot 1: ENABLED")" -eq 1 ] || [ "$(cryptsetup luksDump "$dev" | grep -c "1: luks2")" -eq 1 ]; then warn "Slot 1 is the only one existing on $dev LUKS header. Heads cannot use it to store TPM sealed LUKS Disk Unlock Key" warn "Slot 1 should not be the only slot existing on $dev LUKS header. Slot 0 should be used to store LUKS Disk Recovery Key/passphrase" die "You can safely fix this before continuing through Heads recovery shell: cryptsetup luksAddKey $dev" fi else DEBUG "Slot 1 is not the only existing slot on $dev LUKS header." DEBUG "$dev LUKS header's slot 1 will store LUKS Disk Unlock Key that TPM will seal/unseal with LUKS TPM Disk Unlock Key passphrase" fi done # Remove all the old keys from slot 1 for dev in $(cat "$KEY_DEVICES" | cut -d\ -f1); do echo "++++++ $dev: Removing old LUKS TPM Disk Unlock Key in LUKS slot 1" cryptsetup luksKillSlot \ --key-file "$RECOVERY_KEY" \ $dev 1 || warn "$dev: removal of LUKS TPM Disk Unlock Key in LUKS slot 1 failed: might not exist. Continuing" echo "++++++ $dev: Adding LUKS TPM Disk Unlock Key to LUKS slot 1" cryptsetup luksAddKey \ --key-file "$RECOVERY_KEY" \ --key-slot 1 \ $dev "$KEY_FILE" || die "$dev: Unable to add LUKS TPM Disk Unlock Key to LUKS slot 1" done # Now that we have setup the new keys, measure the PCRs # We don't care what ends up in PCR 6; we just want # to get the /tmp/luksDump.txt file. We use PCR16 # since it should still be zero cat "$KEY_DEVICES" | cut -d\ -f1 | xargs /bin/qubes-measure-luks || die "Unable to measure the LUKS headers" pcrf="/tmp/secret/pcrf.bin" tpmr pcrread 0 "$pcrf" tpmr pcrread -a 1 "$pcrf" tpmr pcrread -a 2 "$pcrf" tpmr pcrread -a 3 "$pcrf" # Note that PCR 4 needs to be set with the "normal-boot" path value, read it from event log. tpmr calcfuturepcr 4 >>"$pcrf" if [ "$CONFIG_USER_USB_KEYBOARD" = "y" -o -r /lib/modules/libata.ko -o -x /bin/hotp_verification ]; then DEBUG "Sealing LUKS TPM Disk Unlock Key with PCR5 involvement (additional kernel modules are loaded per board config)..." # Here, we take pcr 5 into consideration if modules are expected to be measured+loaded tpmr pcrread -a 5 "$pcrf" else DEBUG "Sealing LUKS TPM Disk Unlock Key with PCR5=0 (NO additional kernel modules are loaded per board config)..." #no kernel modules are expected to be measured+loaded tpmr calcfuturepcr 5 >>"$pcrf" fi # Precompute the value for pcr 6 DEBUG "Precomputing TPM future value for PCR6 sealing/unsealing of LUKS TPM Disk Unlock Key..." tpmr calcfuturepcr 6 "/tmp/luksDump.txt" >>"$pcrf" # We take into consideration user files in cbfs tpmr pcrread -a 7 "$pcrf" DO_WITH_DEBUG --mask-position 7 \ tpmr seal "$KEY_FILE" "$TPM_INDEX" 0,1,2,3,4,5,6,7 "$pcrf" \ "$TPM_SIZE" "$key_password" || die "Unable to write LUKS TPM Disk Unlock Key to NVRAM" # should be okay if this fails shred -n 10 -z -u "$pcrf" 2>/dev/null || warn "Failed to delete pcrf file - continuing" shred -n 10 -z -u "$KEY_FILE" 2>/dev/null || warn "Failed to delete key file - continuing" mount -o rw,remount $paramsdir || warn "Failed to remount $paramsdir in RW - continuing" cp -f /tmp/luksDump.txt "$paramsdir/kexec_lukshdr_hash.txt" || warn "Failed to copy LUKS header hashes to /boot - continuing" mount -o ro,remount $paramsdir || warn "Failed to remount $paramsdir in RO - continuing"