#!/bin/bash
*
#   Copyright (C) 2022 SUSE LLC
#
#   This program is free software; you can redistribute it and/or modify
#   it under the terms of the GNU General Public License as published by
#   the Free Software Foundation; either version 2 of the License, or
#   (at your option) any later version.
#
#   This program is distributed in the hope that it will be useful,
#   but WITHOUT ANY WARRANTY; without even the implied warranty of
#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#   GNU General Public License for more details.
#
#   You should have received a copy of the GNU General Public License
#   along with this program; if not, write to the Free Software
#   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
#
#   Written by Olaf Kirch <okir@suse.com>

# Tunables for full disk encryption

# List of PCRs to seal the LUKS key to
FDE_SEAL_PCR_LIST=0,2,4,7,9
FDE_SEAL_PCR_BANK=sha256

# It appears that 128 is the maximum size of what TPM2_Load is willing to handle
FDE_KEY_SIZE_BYTES=128

# These need to match exactly what grub2 uses to create the SRK
TPM2_SRK_ATTRS="userwithauth|restricted|decrypt|fixedtpm|fixedparent|noda|sensitivedataorigin"

# Enable/disable tracing output
FDE_TRACING=true

##################################################################
# Values and locations used by KIWI
##################################################################
KIWI_ROOT_KEYFILE=/root/.root.keyfile
KIWI_ROOT_PASSPHRASE="1234"

##################################################################
# Helper functions for temp file/dir creation
##################################################################
function fde_make_tempdir {

    declare -g FDE_TEMP_DIR

    if [ -z "$FDE_TEMP_DIR" ]; then
	FDE_TEMP_DIR=$(mktemp -d -p /dev/shm fde.XXXXXX)
    fi
}

function fde_clean_tempdir {

    declare -g FDE_TEMP_DIR

    if [ -n "$FDE_TEMP_DIR" ]; then
	rm -rf $FDE_TEMP_DIR
	unset FDE_TEMP_DIR
    fi
}

function fde_make_tempfile {

    fde_make_tempdir
    echo "$FDE_TEMP_DIR/$1"
}

##################################################################
# Display an error box
##################################################################
function fde_error {

    d --title ERROR --msgbox "$*" 8 60
}

function fde_infobox {

    d --infobox "$*" 5 40

}

function fde_trace {

    echo "$*" >&2
}

##################################################################
# Locate the encrypted root device
##################################################################
function __get_root_dev {

    df / | grep /dev/ | cut -f1 -d' '
}

function luks_get_underlying_device {

    local luks_name=$1

    local luks_dev=""
    for id in $(ls /sys/block); do
	test -f "/sys/block/$id/dm/name" || continue
	dm_name=$(cat "/sys/block/$id/dm/name")
	test "$dm_name" = "$luks_name" || continue

	slaves=$(ls /sys/block/$id/slaves)
	case "$slaves" in
	*" "*)
	    echo "Ambiguous number of slave devices for LUKS dm device">&2
	    return 1;;
	esac
	echo "/dev/$slaves"
	return 0
    done

    echo "Unable to find underlying LUKS block device" >&2
}

function __partlabel_to_dev {

    wanted="$1"
    lsblk -nPo NAME,PARTLABEL|while read _line; do
	eval declare -- $_line
	if [ "${PARTLABEL}" = "${wanted}" -a -n "${NAME}" ]; then
	    if ! [[ "${NAME}" =~ /dev/.* ]]; then
		NAME="/dev/${NAME}"
	    fi
	    echo "${NAME}"
	    break
	fi
    done
}

function partlabel_to_dev {

    for iter in 1 2 3; do
	dev=$(__partlabel_to_dev "$@")
	test -z "${dev}" || break
    done
    echo "${dev}"
}

function request_password {

    declare -g result_password

    prompt="$1"
    defpass=$2

    result_password=""

    if [ -n "$defpass" ]; then
	prompt+=" Press Cancel to retain default ($defpass)"
	nocancel=
    else
	nocancel="--no-cancel"
    fi

    attempt=0

    while [ $attempt -lt 3 ]; do
	let attempt+=1

	fde_infobox "About to request password (#$attempt)"
	if ! d $nocancel --insecure --passwordbox "${prompt}" 8 50; then
	    # cancelling the dialog means "I don't want to set this password"
	    echo "${defpass}"
	    return 0
	fi

	password1="$result"

	if d --no-cancel --insecure --passwordbox "Please retype password" 8 50; then
	    password2="$result"
	    if [ "${password1}" = "${password2}" ];  then
		result_password="${password1}"
		return 0
	    fi
	fi
    done
    return 1
}

##################################################################
# Write a passphrase to a file s.th. it can be used as
# --key-file argument.
# luksChangeKey requires two of these files, so we pass a string
# as first argument (eg newpass, oldpass)
##################################################################
function luks_write_password {

    filename=$(fde_make_tempfile "$1")
    shift

    echo -n "$*" >$filename
    echo $filename
}

function luks_drop_pass {

    local luks_dev=$1
    old_pass="$2"

    fde_infobox "Dropping old recovery password"
    old_keyfile=$(luks_write_password oldpass "${old_pass}")
    if ! cryptsetup luksRemoveKey "${luks_dev}" ${old_keyfile}; then
	fde_trace "Warning: luksRemoveKey indicates failure"
	return 1
    fi

    rm -f ${old_keyfile}
    return 0
}

function luks_drop_key {

    local luks_dev=$1
    local luks_keyfile="$2"

    fde_infobox "Dropping old LUKS key"
    if ! cryptsetup luksRemoveKey "${luks_dev}" ${luks_keyfile}; then
	echo "Warning: luksRemoveKey indicates failure" >&2
	return 1
    fi

    return 0
}

function luks_change_password {

    local luks_dev=$1
    local luks_old_password="$2"

    request_password "Please enter new LUKS recovery password."
    if [ -z "$result_password" ]; then
        echo "Unable to obtain new recovery password" >&2
	return 1
    fi

    fde_infobox "Updating LUKS recovery password"

    old_keyfile=$(luks_write_password oldpass "${luks_old_password}")
    new_keyfile=$(luks_write_password newpass "${result_password}")
    if ! cryptsetup --key-file "${old_keyfile}" luksChangeKey --pbkdf pbkdf2 "${luks_dev}" ${new_keyfile}; then
	# FIXME: dialog
	echo "Warning: luksAddKey indicates failure" >&2
	return 1
    fi

    rm -f ${new_keyfile} ${old_keyfile}
}

function luks_add_password {

    local luks_dev=$1
    local luks_keyfile="$2"

    request_password "Please enter new LUKS recovery password."
    if [ -z "$result_password" ]; then
        echo "Unable to obtain new recovery password" >&2
	return 1
    fi

    fde_infobox "Updating LUKS recovery password"

    new_keyfile=$(luks_write_password newpass "${result_password}")
    if ! cryptsetup --key-file "${luks_keyfile}" luksAddKey --pbkdf pbkdf2 "${luks_dev}" ${new_keyfile}; then
	echo "Warning: luksAddKey indicates failure" >&2
	return 1
    fi

    rm -f ${new_keyfile}
}

function luks_add_random_key {

    local luks_dev="$1"
    local luks_keyfile="$2"
    local new_keyfile="$3"

    dd if=/dev/random bs=1 count=$FDE_KEY_SIZE_BYTES of=$new_keyfile
    cryptsetup --key-file "${luks_keyfile}" luksAddKey --pbkdf pbkdf2 $luks_dev $new_keyfile
}

function luks_set_random_key {

    local luks_dev="$1"
    local luks_keyfile="$2"

    new_keyfile=/dev/shm/new.keyfile
    dd if=/dev/random bs=1 count=$FDE_KEY_SIZE_BYTES of=$new_keyfile

    # Note: we try to reduce the cost of PBKDF to (almost) nothing.
    # There's no need in slowing down this operation for a
    # key that was random to begin with.
    cryptsetup --key-file "${luks_keyfile}" luksChangeKey \
		--pbkdf pbkdf2 --pbkdf-force-iterations 1000 \
		$luks_dev $new_keyfile

    cp $new_keyfile "${luks_keyfile}"
    rm -f $new_keyfile

}

function luks_reencrypt {

    local luks_dev="$1"
    local luks_keyfile="$2"

    # Online reencryption works with LUKS2 only. If we ever want to do FDE with luks1,
    # we need to perform reencryption during installation, after dd'ing the image to
    # disk and prior to mounting it.
    {
	cryptsetup reencrypt --key-file "$luks_keyfile" --progress-frequency 1 $luks_dev 2>&1|
	    sed -u 's/.* \([0-9]*\)[0-9.]*%.*/\1/'
	    echo 100
    } | d --gauge "Re-encrypting root file system on $luks_dev" 7 40
}

function luks_decrypt {

    luks_dev="$1"
    luks_keyfile="$2"

    # Online reencryption works with LUKS2 only. If we ever want to do FDE with luks1,
    # we need to perform reencryption during installation, after dd'ing the image to
    # disk and prior to mounting it.
    {
	cryptsetup reencrypt --decrypt --key-file "$luks_keyfile" --progress-frequency 1 $luks_dev 2>&1|
	    sed -u 's/.* \([0-9]*\)[0-9.]*%.*/\1/'
	    echo 100
    } | d --gauge "Decrypting LUKS device $luks_dev" 7 40
}

##################################################################
# Predict the set of PCR values that the TPM chip will hold at
# the time we need to unseal the key.
#
# This function writes the binary PCR values to stdout. This is
# the same format that tpm2_pcrread produces, and which tpm2_load
# expects as parameter to the --pcr option.
#
# If tracing is on, print the computed values to stderr as well
# (in human readable format) so that they wind up in the log file.
##################################################################
function tpm_build_pcrset {

    grub_cfg="$1"

    echo "Building PCR set for $FDE_SEAL_PCR_LIST" >&2
    for index in ${FDE_SEAL_PCR_LIST//,/ }; do
	case $index in
	9)
	    # we need to predict the value of PCR 9 exactly after reading
	    # grub.cfg from the EFI partition of the installed system.
	    # Note: grub must not hash the sealed key file to PCR9 when
	    # loading it else we're in trouble :-)
	    pcr-oracle --algorithm "$FDE_SEAL_PCR_BANK" --format binary \
			--from-zero 9 \
			file "${grub_cfg}"

	    if $FDE_TRACING; then
		    pcr-oracle --algorithm "$FDE_SEAL_PCR_BANK" \
			--from-zero 9 \
			file "${grub_cfg}" >&2
	    fi;;
	*)
	    pcr-oracle --algorithm "$FDE_SEAL_PCR_BANK" --format binary \
			--from-snapshot $index

	    if $FDE_TRACING; then
		    pcr-oracle --algorithm "$FDE_SEAL_PCR_BANK" \
			--from-snapshot $index >&2
	    fi;;
	esac
    done
}

function tpm_policy_options {

    pcr_values_file=$1

    pcr_list="$FDE_SEAL_PCR_BANK:$FDE_SEAL_PCR_LIST"
    if [ -n "$pcr_values_file" -a -s "$pcr_values_file" ]; then
	echo "--pcr-list $pcr_list --pcr $pcr_values_file"
    else
	echo "--pcr-list $pcr_list"
    fi
}

function tpm_seal_key {

    pcr_values_file=$1
    secret=$2
    pubkey=$3
    privkey=$4

    context=$(fde_make_tempfile primary.ctx)
    session=$(fde_make_tempfile session.ctx)
    policy=$(fde_make_tempfile policy.tpm)

    policy_options=$(tpm_policy_options "$pcr_values_file")

    tpm2_createprimary --quiet -c $context -a "$TPM2_SRK_ATTRS"
    tpm2_startauthsession --session $session
    tpm2_policypcr --session $session $policy_options --policy $policy
    tpm2_create --quiet -C $context -u $pubkey -r $privkey -L $policy -i $secret
    retval=$?
    tpm2_flushcontext $session

    return $retval
}

function tpm_unseal_key {

    pcr_values_file=$1
    pubkey=$2
    privkey=$3
    unsealed_key_file=$4

    fde_make_tempdir
    dir="$FDE_TEMP_DIR"

    context=$dir/primary.ctx
    session=$dir/session.ctx
    policy=$dir/policy.tpm
    key_context=$dir/key.ctx

    policy_options=$(tpm_policy_options "$pcr_values_file")

    tpm2_createprimary --quiet -c $context -a "$TPM2_SRK_ATTRS"
    tpm2_startauthsession --policy-session -S $session
    tpm2_policypcr --session $session $policy_options --policy $policy
    tpm2_load -C $context -u $pubkey -r $privkey -c $key_context
    tpm2_unseal -c $context -p session:$session -c $key_context -o $unsealed_key_file
    tpm2_flushcontext $session

    fde_clean_tempdir
}


function seal_key_verify {

    opt_verify=false
    if [ "$1" == "--verify" ]; then
	opt_verify=true
	shift
    fi

    pcr_values_file=$1
    secret=$2
    sealed_key_file=$3

    pubkey=$(fde_make_tempfile sealed-key.pub)
    privkey=$(fde_make_tempfile sealed-key.priv)

    if ! tpm_seal_key "$pcr_values_file" $secret $pubkey $privkey; then
	return 1
    fi

    # The sealed key that grub expects is just the concatenation of
    # TPM2B_PUBLIC and a TPM2B containing the private key portion
    cat $pubkey $privkey > $sealed_key_file

    if $opt_verify; then
	unsealed_file=$(fde_make_tempfile unsealed.key)
	tpm_unseal_key "$pcr_values_file" $pubkey $privkey $unsealed_file

	if ! cmp --quiet $secret $unsealed_file; then
	    echo "Problem: TPM seal/unseal did not work!" >&2
	    retval=1
	else
	    echo "Splendid, we were able to unseal the TPM protected key" >&2
	    retval=0
	fi
	rm -f "$unsealed_file"
    fi

    rm -f $pubkey $privkey
    return $retval
}


function tpm_test {

    key_size=$1

    pcr_values_file=$(fde_make_tempfile pcr-values)
    tpm2_pcrread -o $pcr_values_file $FDE_SEAL_PCR_BANK:$FDE_SEAL_PCR_LIST >&2

    secret=$(fde_make_tempfile secret)
    dd if=/dev/zero of=$secret bs=$key_size count=1 >&2

    sealed_secret=$(fde_make_tempfile sealed.key)
    seal_key_verify --verify $pcr_values_file $secret $sealed_secret >&2
    retval=$?

    fde_clean_tempdir
    return $retval
}

function tpm_seal_secret {

    secret="$1"
    efi_grub_dir="$2"
    sealed_secret=$3

    # Construct a set of PCR values against which to seal the key.
    # Most of these come straight from the PCR snapshot taken by tpm_record_pcrs
    # in grub.cfg. The only exception is PCR9, which covers the files that grub
    # reads during early boot (ie grub.cfg). And that grub.cfg file is
    # going to be different from the one that got used when booting the installer.
    pcr_values_file=$(fde_make_tempfile pcr-set)
    tpm_build_pcrset "${efi_grub_dir}/grub.cfg" >"${pcr_values_file}"

    # Since we're now sealing the key against a predicted value of PCR9 (rather
    # than the actual value), we can no longer verify that the key can be
    # unsealed.
    if ! seal_key_verify $pcr_values_file $secret $sealed_secret >&2; then
	rm -f $sealed_secret
	# FIXME: this should be an error dialog.
	# Let's hope the user has set a recovery password
	echo "Failed to seal LUKS encryption key" >&2
	return 1
    fi

    rm -f "${pcr_values_file}"
}

function mount_system_fs {

    declare -g MOUNTED_DIRS
    declare -g SYSTEM_MP

    where="$1"; shift
    destdir="${SYSTEM_MP}/$where"
    if ! mount "$@" "$destdir"; then
	echo "Failed to mount system $where" >&2
	return 1
    fi

    MOUNTED_DIRS="$destdir $MOUNTED_DIRS"
    return 0
}

function mount_system_begin {

    declare -g MOUNTED_DIRS
    declare -g SYSTEM_MP

    mountpoint="$1"

    SYSTEM_MP="$mountpoint"
    mkdir "$SYSTEM_MP"

    MOUNTED_DIRS=""
}

function unmount_system {

    declare -g MOUNTED_DIRS
    declare -g SYSTEM_MP

    for dir in $MOUNTED_DIRS; do
	umount $dir
    done

    rmdir $SYSTEM_MP

    unset MOUNTED_DIRS SYSTEM_MP
}

# Change the cryptomount command in early boot grub.cfg
# This turns something like this:
#	cryptomount -u XX-YY-ZZ -p defpass
# into something like this:
#       tpm2_key_protector_init bla bla bla
#       if ! cryptomount -u XX-YY-ZZ -k tpm2; then
#		cryptomount -u XX-YY-ZZ
#	fi
#	tpm2_key_protector_clear
function fde_frob_cryptomount_command {

    orig_cmd="$*"

    crypto_cmd=""
    orig_protector=""

    while [ $# -gt 0 ]; do
	case $1 in
	-u|--uuid)
		crypto_cmd+=" -u $2"
		shift 2;;
	-p|--password)
		shift 2;;
	-k|--protector)
		orig_protector="$1 $2"
		shift 2;;
	*)	crypto_cmd+=" $1"
		shift;;
	esac
    done

    cat <<-EOF

tpm2_key_protector_init -b $FDE_SEAL_PCR_BANK -p $FDE_SEAL_PCR_LIST -k \$prefix/$sealed_key_file
if ! $crypto_cmd -k tpm2; then
    $crypto_cmd $orig_protector
fi
tpm2_key_protector_clear

EOF
}

function fde_update_grub {

    grub_dir="$1"
    sealed_key_file="$2"

    grub_cfg_file="${grub_dir}/grub.cfg"

    cat $grub_cfg_file | while read line; do
	case "$line" in
	*cryptomount*)
	    fde_frob_cryptomount_command $line;;
	*)
	    echo "$line";;
	esac
    done >$grub_cfg_file.new

    mv $grub_cfg_file.new $grub_cfg_file
}

function fde_protect_tpm {

    local luks_dev=$1
    local luks_keyfile=$2

    luks_set_random_key "${luks_dev}" "${luks_keyfile}"

    efi_grub_dir=/boot/efi/EFI/BOOT

    # First update grub.cfg...
    fde_update_grub "$efi_grub_dir" sealed.key

    # ... then seal the key against a PCR9 value that covers grub.cfg
    tpm_seal_secret "${luks_keyfile}" "${efi_grub_dir}" "$efi_grub_dir/sealed.key"

    return $?
}

function fde_setup_encrypted {

    root_dev=$1
    luks_keyfile="$2"
    luks_recovery_pass="$3"

    with_pass=false
    with_tpm=false
    with_ccid=false

    for method in $FDE_PROTECTION; do
    	case $method in
	pass) with_pass=true;;
	tpm)  with_tpm=true;;
	ccid) with_ccid=true;;
	esac
    done

    if ! [[ "$root_dev" =~ /dev/mapper/.* ]]; then
	fde_error "root device $root_dev does not look like a dm device"
        return 1
    fi

    luks_name=$(expr "$root_dev" : ".*/\(.*\)")
    luks_dev=$(luks_get_underlying_device "$luks_name")
    if [ -z "$luks_dev" ]; then
        fde_error "Unable to determine underlying LUKS device for $root_dev"
	return 1
    fi

    if $with_tpm && ! tpm_test $FDE_KEY_SIZE_BYTES; then
	fde_error "TPM key sealing and unsealing does not seem to work"
	return 1
    fi

    # Change the built-in recovery password to the one provided by the user
    if $with_pass && ! luks_change_password "${luks_dev}" "${luks_recovery_pass}"; then
	fde_error "Failed to change recovery password."
	with_pass=false
    fi

    # "cryptsetup reencrypt" requires to specify the key slot number (--key-slot) if
    # there are multiple key slots and only the specified key slot will survive after
    # reencryption. There are two key slots already in the partition: one for the
    # password and the other one for the random key. To avoid the additional key slot
    # parameter, drop the old random key if the user chooses a recovery password.
    # Otherwise, drop the password key slot.
    if $with_pass; then
        # Drop the random key slot and use the password for reencryption
        if ! luks_drop_key "${luks_dev}" "${luks_keyfile}"; then
            fde_error "Failed to remove initial random key"
            return 1
        fi

        # Reencrypt with the new password
        pass_keyfile=$(luks_write_password pass "${result_password}")
        luks_reencrypt "${luks_dev}" "${pass_keyfile}"

        # Generate a random key again
        luks_add_random_key "${luks_dev}" "${pass_keyfile}" "${luks_keyfile}"

        # Remove the password file
        rm -f ${pass_keyfile}
    else
        # Drop the password key slot and use the random key for reencryption
        if ! luks_drop_pass "${luks_dev}" "${luks_recovery_pass}"; then
            fde_error "Failed to remove initial password"
            return 1
        fi

        # Reencrypt with the old random key
        luks_reencrypt "${luks_dev}" "${luks_keyfile}"
    fi

    if $with_tpm && ! fde_protect_tpm "${luks_dev}" "${luks_keyfile}"; then
	fde_error "Failed to protect encrypted volume with TPM"
	with_tpm=false
    fi

    if $with_pass || $with_tpm || $with_ccid; then
    	: all is well
    else
	return 1
    fi

    # Normally, kiwi wants to keep the "key" that unlocks LUKS in .root.keyfile.
    # However, since we're protecting that key with the TPM now, there's no
    # need to keep a secret bit of data around as clear-text, copying it into
    # initrd and potentially leaking it.
    rm -f "${luks_keyfile}"
}

function fde_setup_unencrypted {

    root_dev=$1
    luks_keyfile="$2"
    luks_recovery_pass="$3"

    if ! [[ "$root_dev" =~ /dev/mapper/.* ]]; then
	fde_error "root device $root_dev does not look like a dm device"
        return 1
    fi

    luks_name=$(expr "$root_dev" : ".*/\(.*\)")
    luks_dev=$(luks_get_underlying_device "$luks_name")
    if [ -z "$luks_dev" ]; then
        fde_error "Unable to determine underlying LUKS device for $root_dev"
	return 1
    fi

    luks_decrypt "${luks_dev}" "${luks_keyfile}"

    rm -f "${luks_keyfile}"
    rm -f /etc/crypttab

    fde_infobox "Re-creating initial ramdisk"
    if ! mkinitrd >&2; then
    	fde_error "Failed to rebuild initrd"
	return 1
    fi

    return 0
}


function get_efivar_binary_byte {

    name="$1"
    path="/sys/firmware/efi/vars/$name/data"
    if [ ! -f "$path" ]; then
	echo "n/a"
    else
	cat "$path" | tr '\0\1' '01'
    fi
}

function fde_platform_is_tpm_capable {

    if ! tpm2_selftest -V -f; then
	fde_trace "This system does not have a TPM2 chip. Full disk encryption not available"
	return 1
    fi

    if ! [ -d /sys/firmware/efi ]; then
	fde_trace "This system does not seem to use UEFI. Full disk encryption not available"
	return 1
    fi

    secure_boot=$(get_efivar_binary_byte SecureBoot-8be4df61-93ca-11d2-aa0d-00e098032b8c)
    setup_mode=$(get_efivar_binary_byte SetupMode-8be4df61-93ca-11d2-aa0d-00e098032b8c)
    if [ "$secure_boot" != 1 -a "$setup_mode" != 0 ]; then
	fde_trace "Secure Boot not enabled (secure_boot=$secure_boot; setup_mode=$setup_mode)"
	fde_trace "This system does not seem to use Secure Boot. Full disk encryption not available"
	return 1
    fi

    return 0
}

function __fde_valid_protections {

    for tag in $*; do
        case $tag in
        pass|tpm) : ;;
        *)
	    fde_error "FDE key protection scheme $tag not yet implemented"
	    return 1;;
        esac
    done

    return 0
}

##################################################################
# Check what protection method the system supports and let the
# user choose.
# returns false if the user declined device encryption
# The ccid thing is there just as a teaser for now :-)
##################################################################
function fde_choose_protection {

    declare -g FDE_PROTECTION
    declare -a options

    FDE_PROTECTION=""

    message="ALP can be installed with an encrypted root and boot partition. Please choose the desired protection method(s) or press Cancel to install without encryption"
    options+=(pass 'Pass phrase' on)

    if fde_platform_is_tpm_capable; then
    	options+=(tpm 'Stored inside the TPM chip' on)
    fi

    options+=(ccid 'Stored inside a CCID capable token' off)

    while true; do
        d --title "Full Disk Encryption" --checklist \
			"$message" 12 74 4 \
			"${options[@]}"

	if [ $? -ne 0 ]; then
	    FDE_PROTECTION=""
	    return 1
	fi

	FDE_PROTECTION="$result"
	fde_trace "user selected protections: <$FDE_PROTECTION>"

	if [ -z "$FDE_PROTECTION" ]; then
	    return 1
	fi

	if __fde_valid_protections $FDE_PROTECTION; then
	    break
	fi
    done

    return 0
}

function fde_firstboot {

    if fde_choose_protection; then
	if fde_setup_encrypted "$@"; then
	    return 0
	fi

	fde_error "Failed to set up for full disk encryption. Trying to recover"
    fi

    fde_setup_unencrypted "$@"
}

function fde_systemd_firstboot {

    fde_infobox "Full Disk Encryption with TPM2 support"

    set -x

    if [ ! -f "$KIWI_ROOT_KEYFILE" ]; then
    	fde_error "Missing $KIWI_ROOT_KEYFILE. FDE setup will most likely fail."
    fi

    # FIXME: rather than hard-coding the recovery password here,
    # have kiwi write it to /.root.something and read it from there
    fde_firstboot $(__get_root_dev) "$KIWI_ROOT_KEYFILE" "$KIWI_ROOT_PASSPHRASE" 2>/var/log/fde-firstboot.log

    set +x

    fde_clean_tempdir
}

function fde_post {

    :
}

function fde_cleanup {

    :
}
