#!/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 "Under kexec-seal-key"

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"
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"
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 disk recovery key: " disk_password
echo -n "$disk_password" > "$RECOVERY_KEY"
echo

read -s -p "New disk unlock password for booting: " key_password
echo
read -s -p "Repeat unlock code: " key_password2
echo

if [ "$key_password" != "$key_password2" ]; then
	die "Key passwords do not match"
fi

# Generate key file
dd \
	if=/dev/urandom \
	of="$KEY_FILE" \
	bs=1 \
	count=128 \
	2>/dev/null \
|| die "Unable to generate 128 random bytes"

# Remove all the old keys from slot 1
for dev in `cat "$KEY_DEVICES" | cut -d\  -f1`; do
	echo "++++++ $dev: Removing old key slot"
	cryptsetup luksKillSlot \
		--key-file "$RECOVERY_KEY" \
		$dev 1 \
	|| warn "$dev: ignoring problem"

	echo "++++++ $dev: Adding key"
	cryptsetup luksAddKey \
		--key-file "$RECOVERY_KEY" \
		--key-slot 1 \
		$dev "$KEY_FILE" \
	|| die "$dev: Unable to add key"
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, which is 0.
dd if=/dev/zero bs="$(tpmr pcrsize)" count=1 status=none >> "$pcrf"
if [ "$CONFIG_USB_KEYBOARD" = "y" -o -r /lib/modules/libata.ko -o -x /bin/hotp_verification ]; then
	DEBUG "Sealing 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 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
	dd if=/dev/zero bs="$(tpmr pcrsize)" count=1 status=none >> "$pcrf"
fi
# Precompute the value for pcr 6
DEBUG "Precomputing TPM future value for PCR6 sealing/unsealing of TPM disk unlock key..."
tpmr calcfuturepcr -a "/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"

# should be okay if this fails
shred -n 10 -z -u "$pcrf".* 2> /dev/null || true
shred -n 10 -z -u "$KEY_FILE" 2> /dev/null \
|| warn "Failed to delete key file - continuing"

cp /tmp/luksDump.txt "$paramsdir/kexec_lukshdr_hash.txt" \
|| warn "Failed to have hashes of LUKS header - continuing"