Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

decrypt multiple devices on boot #22

Draft
wants to merge 11 commits into
base: master
Choose a base branch
from
3 changes: 2 additions & 1 deletion 1-generatesecurebootkeys.sh
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#!/usr/bin/env bash
set -e
source mortar.env
MORTAR_FILE="/etc/mortar/mortar.env"
source "$MORTAR_FILE"
echo "Generating secureboot keys..."
openssl req -new -x509 -newkey rsa:2048 -subj "/CN=PK$SECUREBOOT_MODIFIER/" -keyout "$SECUREBOOT_PK_KEY" -out "$SECUREBOOT_PK_CRT" -days 7300 -nodes -sha256
openssl req -new -x509 -newkey rsa:2048 -subj "/CN=KEK$SECUREBOOT_MODIFIER/" -keyout "$SECUREBOOT_KEK_KEY" -out "$SECUREBOOT_KEK_CRT" -days 7300 -nodes -sha256
Expand Down
33 changes: 26 additions & 7 deletions 3-tpm1.2-prepluksandinstallhooks.sh
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,21 @@ fi

if command -v luksmeta >/dev/null; then
echo "Wiping any existing metadata in the luks keyslot."
luksmeta wipe -d "$CRYPTDEV" -s "$SLOT"
for CRYPTNAME in $CRYPTNAMES; do
CRYPTDEV="$(cryptnametodevice $CRYPTNAME)"
if [ -z "$CRYPTDEV" ]; then echo "ERROR: cannot find CRYPTDEV for CRYPTNAME"; exit 1; fi
luksmeta wipe -d "$CRYPTDEV" -s "$SLOT"
done

fi

echo "Wiping any old luks key in the keyslot..."
cryptsetup luksKillSlot --key-file tmpramfs/user.key "$CRYPTDEV" "$SLOT"
for CRYPTNAME in $CRYPTNAMES; do
CRYPTDEV="$(cryptnametodevice $CRYPTNAME)"
if [ -z "$CRYPTDEV" ]; then echo "ERROR: cannot find CRYPTDEV for CRYPTNAME"; exit 1; fi
cryptsetup luksKillSlot --key-file tmpramfs/user.key "$CRYPTDEV" "$SLOT"
done

read -p "If this is the first time running, do you want to attempt taking ownership of the tpm? (y/N): " takeowner
case "$takeowner" in
[yY]*) tpm_takeownership -z ;;
Expand All @@ -42,7 +52,11 @@ esac
echo "Generating key..."
dd bs=1 count=512 if=/dev/urandom of=tmpramfs/mortar.key
chmod 700 tmpramfs/mortar.key
cryptsetup luksAddKey "$CRYPTDEV" --key-slot "$SLOT" tmpramfs/mortar.key --key-file tmpramfs/user.key
for CRYPTNAME in $CRYPTNAMES; do
CRYPTDEV="$(cryptnametodevice $CRYPTNAME)"
if [ -z "$CRYPTDEV" ]; then echo "ERROR: cannot find CRYPTDEV for CRYPTNAME"; exit 1; fi
cryptsetup luksAddKey "$CRYPTDEV" --key-slot "$SLOT" tmpramfs/mortar.key --key-file tmpramfs/user.key
done

echo "Sealing key to TPM..."
if [ -z "$TPMINDEX" ]; then echo "TPMINDEX not set."; exit 1; fi
Expand All @@ -65,10 +79,15 @@ umount -l tmpramfs
rm -rf tmpramfs

echo "Adding new sha256 of the luks header to the mortar env file."
if [ -f "$HEADERFILE" ]; then rm "$HEADERFILE"; fi
cryptsetup luksHeaderBackup "$CRYPTDEV" --header-backup-file "$HEADERFILE"
HEADERSHA256=`sha256sum "$HEADERFILE" | cut -f1 -d' '`
sed -i -e "/^HEADERSHA256=.*/{s//HEADERSHA256=$HEADERSHA256/;:a" -e '$!N;$!b' -e '}' "$MORTAR_FILE"
HEADERSHA256=""
for CRYPTNAME in $CRYPTNAMES; do
CRYPTDEV="$(cryptnametodevice $CRYPTNAME)"
if [ -z "$CRYPTDEV" ]; then echo "ERROR: cannot find CRYPTDEV for CRYPTNAME"; exit 1; fi
if [ -f "$HEADERFILE" ]; then rm "$HEADERFILE"; fi
cryptsetup luksHeaderBackup "$CRYPTDEV" --header-backup-file "$HEADERFILE"
HEADERSHA256="$(sha256sum "$HEADERFILE" | cut -f1 -d' ') $HEADERSHA256"
done
sed -i -e "/^HEADERSHA256=.*/{s//HEADERSHA256=\"$HEADERSHA256\"/;:a" -e '$!N;$!b' -e '}' "$MORTAR_FILE"
if [ -f "$HEADERFILE" ]; then rm "$HEADERFILE"; fi

# Figure out our distribuition.
Expand Down
28 changes: 21 additions & 7 deletions mortar.env
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,11 @@ PRIVATE_DIR="$WORKING_DIR"'private/'
# Note to reader: Most of these values can be left as-is. Some have good defaults or are auto-populated as part of the other scripts.
# You should, however specifically review or statically set (the CRYPTNAME auto logic is particularly weak, but getting better):
# EFI_ROOT
# CRYPTDEV
# CRYPTNAME
# CRYPTNAMES
# SLOT
# TPMINDEX
# BINDPCR


# Permission checks.
if [ "$UID" -ne "0" ]; then echo "Must be run as root."; exit 1; fi
mkdir -p "$PRIVATE_DIR"
Expand All @@ -19,6 +17,15 @@ chmod go-rwx -R "$PRIVATE_DIR"
chmod go-rwx -R "$WORKING_DIR"
cd "$WORKING_DIR"

function cryptnametodevice {
DEVICE=$(grep "^[^#;]" /etc/crypttab | grep "$1" | awk '{ print $2 }')
if [ -z $DEVICE ]; then
echo "ERROR: cannot find CRYPTDEV for CRYPTNAME $1"
exit 1
fi
echo $DEVICE
}

TPM_MODE= #1.2 or 2 - not yet used.
# Stage 1.
EFI_ROOT= #Change this for your distro. This is the root of the efi partition and likely contains a directory called "EFI" `mount | grep -i efi` may be helpful. Must end with a / e.g. /efi/ /boot/efi/ etc. Next line tries its best if this isn't set.
Expand Down Expand Up @@ -49,14 +56,21 @@ SECUREBOOT_KEK_AUTH=$PRIVATE_DIR'KEK.auth'
SECUREBOOT_KEK_KEY=$PRIVATE_DIR'KEK.key'

# Stage 3.
CRYPTDEV= #LUKS Partition e.g. /dev/nvme0n1p3 next line will give it the old college try if this is left blank.
if [ -z "$CRYPTDEV" ]; then temp=$(grep "^[^#;]" /etc/crypttab | head -n1 | awk '{ print $2 }'); if [ -e "$temp" ]; then CRYPTDEV="$temp"; else if [[ "$temp" == "UUID="* ]]; then temp=$(echo "$temp" | cut -f2 -d'='); CRYPTDEV="/dev/disk/by-uuid/$temp"; unset temp; fi; fi; fi
CRYPTNAME= #Unlocked name (find in crypttab) e.g. luks-disk next line will give it the old college try if this is left blank.
if [ -z "$CRYPTNAME" ]; then CRYPTNAME=$(grep -v "^#" /etc/crypttab | head -n1 | awk '{ print $1 }'); fi
#LUKS Partition as named in the crypttab, space separated for multiple disks
#if your crypttab shows for example:
# disk1 UUID=32280417-d1e9-4e36-8c8b-ee2b87f31c31 none initramfs,luks,discard
# disk2 /dev/sda2 none initramfs,luks,discard
#and you want to decode both disk1 and disk2 inside the initrd via tpm key,
#then the line would be:
# CRYPTNAMES="disk1 disk2"
#you do need to name only disks which should get decrypted by mortar
CRYPTNAMES=""
if [ -z "$CRYPTNAMES" ]; then echo "CRYPTNAMES must be specified"; exit 1; fi
LUKSVER= #1 or 2 - best effort will be given if left blank.
SLOT="1" # LUKS keyslot number for use with automatic unlocks.
# Only used for LUKS1: UUID OF THE KEYSLOT, NOT THE DISK. Find with `luksmeta show -d /dev/nvmluks0p3` and check the slot uuid. Logic exists in the luks setup script if this is left blank. (hence why this comment is on a different line).
SLOTUUID=
#TODO: finding TOKENIDs must get adapted for multiple CRYPTNAMES
TOKENID= # Only used for LUKS2 - Token ID in luks header for clevis. Usually 0. Logic below attempts to find out if left blank.
if [ -z "$TOKENID" ]; then TOKENID=$(cryptsetup luksDump "$CRYPTDEV" | grep clevis | sed -e 's/ \(.*\): clevis/\1/'); fi #clevis scripts used sed -rn 's|^\s+([0-9]+): clevis|\1|p' after the cryptsetup pipe. Maybe that is better? *shrug*
# Only used for TPM 1.2 - NVRAM index number for storing LUKS key in TPM.
Expand Down
78 changes: 47 additions & 31 deletions res/debian/tpm1.2/initramfs-tools/scripts/local-top/mortar
Original file line number Diff line number Diff line change
Expand Up @@ -14,50 +14,66 @@ mkdir -p /run/cryptsetup

echo "Initializing... Please wait a few seconds for TPM validation of boot sequence."
echo "Measuring boot system integrity..."
CRYPTDEV= #LUKS Partition /dev/sda3
CRYPTNAME= #Unlocked name (find in crypttab)
CRYPTPAIRS= #name(s) of the unlocked device(s) (first row in crypttab)
SLOT= #Keyslot number in use by mortar.
TPMINDEX=
HEADERSHA256=
HEADERFILE=

# Test if the disk is already unlocked.
if [ -e /dev/mapper/"$CRYPTNAME" ]; then exit; fi
if ! [ -e "$CRYPTDEV" ]; then echo "Cannot find the crypto block device at $CRYPTDEV"; exit 1; fi
sleep 2
if [ -f "$HEADERFILE" ]; then rm "$HEADERFILE"; fi
if cryptsetup luksHeaderBackup "$CRYPTDEV" --header-backup-file "$HEADERFILE"; then
for CRYPTPAIR in $CRYPTPAIRS; do
CRYPTNAME=${CRYPTPAIR%:*}
CRYPTDEV=${CRYPTPAIR#*:}
if [ -f "$HEADERFILE" ]; then rm "$HEADERFILE"; fi
if cryptsetup luksHeaderBackup "$CRYPTDEV" --header-backup-file "$HEADERFILE"; then
HEADERSHA256CUR=`sha256sum "$HEADERFILE" | cut -f1 -d' '`
if [ "$HEADERSHA256" == "$HEADERSHA256CUR" ]; then
echo "HEADER VALIDATION SUCCEEDED."
if test "${HEADERSHA256#*$HEADERSHA256CUR}" != "$HEADERSHA256"; then
echo "HEADER VALIDATION SUCCEEDED FOR $CRYPTNAME."
else
echo "HEADER VALIDATION FAILED."
echo "WAITING 10 SECONDS BEFORE CONTINUING."
echo "KILL SYSTEM NOW IF YOU DO NOT TRUST THIS HEADER."
sleep 7
echo "3..."
sleep 1
echo "2..."
sleep 1
echo "1..."
sleep 1
echo "HEADER VALIDATION FAILED FOR $CRYPTNAME."
echo "WAITING 10 SECONDS BEFORE CONTINUING."
echo "KILL SYSTEM NOW IF YOU DO NOT TRUST THIS HEADER."
sleep 7
echo "3..."
sleep 1
echo "2..."
sleep 1
echo "1..."
sleep 1
fi
sleep 2
fi
fi
if [ -f "$HEADERFILE" ]; then rm "$HEADERFILE"; fi
done

# Get Mortar key.
tpm_nvread -i "$TPMINDEX" -f /tmp/mortar.key >/dev/null
if tpm_nvread -i "$TPMINDEX" -f /tmp/mortar.key >/dev/null; then
echo -e "TPM VALIDATION SUCCEEDED.\n\n"
else
echo -e "TPM VALIDATION FAILED.\n\n"
sleep 3
fi

# Disable future key fetches.
tcsd && sleep 1
tpm_nvread -i "$TPMINDEX" -s 0 >> /dev/null

# Decrypt disk.
if cryptsetup luksOpen $CRYPTDEV $CRYPTNAME --key-file /tmp/mortar.key; then
echo -e "TPM VALIDATION SUCCEEDED.\n\nI found $CRYPTNAME."
rm /tmp/mortar.key
if [ -f /tmp/mortar.key ]; then echo "FAILED TO REMOVE KEYFILE!"; fi
sleep 2
else
echo "TPM VALIDATION FAILED."
# Decrypt disk(s).
for CRYPTPAIR in $CRYPTPAIRS; do
CRYPTNAME=${CRYPTPAIR%:*}
CRYPTDEV=${CRYPTPAIR#*:}
# Test if the disk is already unlocked.
if [ -e /dev/mapper/"$CRYPTNAME" ]; then
echo "skipping $CTYPTNAME as it already exists"
sleep 3
fi
else
if cryptsetup luksOpen $CRYPTDEV $CRYPTNAME --key-file /tmp/mortar.key; then
echo "opened $CRYPTNAME"
else
echo "could't open $CRYPTNAME"
sleep 3
fi
fi
done
sleep 2
rm /tmp/mortar.key
if [ -f /tmp/mortar.key ]; then echo "FAILED TO REMOVE KEYFILE!"; fi
6 changes: 3 additions & 3 deletions res/debian/tpm1.2/install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@ cp -r kernel /etc/
cp -r initramfs-tools /etc/
INITRAMFSSCRIPTFILE='/etc/initramfs-tools/scripts/local-top/mortar'
source /etc/mortar/mortar.env
sed -i -e "/^CRYPTDEV=.*/{s##CRYPTDEV=\"$CRYPTDEV\"#;:a" -e '$!N;$!b' -e '}' "$INITRAMFSSCRIPTFILE"
sed -i -e "/^CRYPTNAME=.*/{s//CRYPTNAME=$CRYPTNAME/;:a" -e '$!N;$!b' -e '}' "$INITRAMFSSCRIPTFILE"
for CRYPTNAME in $CRYPTNAMES; do CRYPTPAIRS="$CRYPTNAME:$(cryptnametodevice $CRYPTNAME) $CRYPTPAIRS"; done
sed -i -e "/^CRYPTPAIRS=.*/{s//CRYPTPAIRS=\"$CRYPTPAIRS\"/;:a" -e '$!N;$!b' -e '}' "$INITRAMFSSCRIPTFILE"
sed -i -e "/^SLOT=.*/{s//SLOT=$SLOT/;:a" -e '$!N;$!b' -e '}' "$INITRAMFSSCRIPTFILE"
sed -i -e "/^TPMINDEX=.*/{s//TPMINDEX=$TPMINDEX/;:a" -e '$!N;$!b' -e '}' "$INITRAMFSSCRIPTFILE"
sed -i -e "/^HEADERSHA256=.*/{s//HEADERSHA256=$HEADERSHA256/;:a" -e '$!N;$!b' -e '}' "$INITRAMFSSCRIPTFILE"
sed -i -e "/^HEADERSHA256=.*/{s//HEADERSHA256=\"$HEADERSHA256\"/;:a" -e '$!N;$!b' -e '}' "$INITRAMFSSCRIPTFILE"
sed -i -e "/^HEADERFILE=.*/{s##HEADERFILE=\"$HEADERFILE\"#;:a" -e '$!N;$!b' -e '}' "$INITRAMFSSCRIPTFILE"

update-initramfs -u
Expand Down