LABEL_PREFIX=aem
SYSFS_TPM_DIR=/sys/class/tpm/tpm0
AEM_DIR=/var/lib/anti-evil-maid
TPM_DIR=/var/lib/tpm
TPMS_DIR=${TPM_DIR}s
CACHE_DIR=/run/anti-evil-maid
SRK_PASSWORD_CACHE=$CACHE_DIR/srk-password
# shellcheck disable=SC2034
SUFFIX_CACHE=$CACHE_DIR/suffix
TPM_OWNER_PASSWORD_FILE=$AEM_DIR/tpm-owner-pw
TPM_FRESHNESS_PASSWORD_FILE=$AEM_DIR/tpm-freshness-pw
TPM_FRESHNESS_INDEX="0x454d"
TPM_FRESHNESS_SLOTS=8


# work with or without plymouth

if command plymouth --ping 2>/dev/null; then
    alias plymouth_active=true
    alias message=plymouth_message
else
    alias plymouth=:
    alias plymouth_active=false
    alias message=log
fi


getparams() {
    _CMDLINE=${_CMDLINE-$(cat /proc/cmdline)}

    for _param in $_CMDLINE; do
        for _key; do
            case "$_param" in "$_key"=*)
                printf '%s\n' "${_param#*=}"
                break
            esac
        done
    done
}

getluksuuids() {
    getparams rd.luks.uuid rd_LUKS_UUID | sed s/^luks-//
}

log() {
    echo "${0##*/}: $1" >&2
}

hex() {
    xxd -ps | tr -dc 0-9a-f
}

unhex() {
    tr -dc 0-9a-f | xxd -ps -r
}

waitfor() {
    case $# in
        2) _file=$2; _what=connected ;;
        3) _file=$3; _what=removed ;;
        *) return 1 ;;
    esac

    if [ "$@" ]; then
        return
    fi

    message "Waiting for $_file to be $_what..."
    plymouth pause-progress
    until [ "$@" ]; do
        sleep 0.1
    done
    plymouth unpause-progress
    message "$_file $_what"
}

waitforenter() {
    msg='Press <ENTER> to continue...'
    if plymouth_active; then
        message "$msg"
        plymouth watch-keystroke --keys=$'\n'
    else
        systemd-ask-password --timeout=0 --echo=no "$msg" >/dev/null
    fi
}

suffixtoslotfile() {
    echo "$AEM_DIR/$LABEL_PREFIX$1/tpm-freshness-slot"
}

suffixtoslot() {
    # returns the slot number assigned to the AEM media given its label suffix
    # as the first argument
    _slotfile=$(suffixtoslotfile "$1")
    cat "$_slotfile" 2>/dev/null
}

assignslottosuffix() {
    # assigns an unused freshness slot number (if available) to an AEM
    # media identified by its label suffix (passed as the first argument)
    _slotfile=$(suffixtoslotfile "$1")
    rm -f "$_slotfile"

    _slotfilesglob=$(suffixtoslotfile '*')
    _lastslot=$((TPM_FRESHNESS_SLOTS - 1))
    _freeslot=$(
        {
            cat "$_slotfilesglob" 2>/dev/null || true
            seq 0 $_lastslot
        } | sort -n | uniq -u | head -n 1
    )

    if [ -z "$_freeslot" ]; then
        message "No more freshness token slots available!"
        return 1
    fi

    mkdir -p "${_slotfile%/*}"
    echo "$_freeslot" >> "$_slotfile"
}

synctpms() {
    _label=${1:?}
    _mnt=${2:?}

    message "Syncing to $_mnt"

    _mnt_tpms_dir=$_mnt/aem/${TPMS_DIR##*/}
    rm -rf "$_mnt_tpms_dir"

    _ids=$(ls "$TPMS_DIR")
    for _id in $_ids; do
        mkdir -p "$_mnt_tpms_dir/$_id"
        # this file is used only with TPM1
        if [ -f "$TPMS_DIR/$_id/system.data" ]; then
            cp "$TPMS_DIR/$_id/system.data" "$_mnt_tpms_dir/$_id"
        fi

        if [ -d "$TPMS_DIR/$_id/$_label" ]; then
            cp -r  "$TPMS_DIR/$_id/$_label" "$_mnt_tpms_dir/$_id"
        fi
    done
}

devtomnt() {
    lsblk -dnr -o MOUNTPOINT "$1" 2>/dev/null |
    sed 's/%/\\x25/g' |
    xargs -0 printf
}

topdev() {
    lsblk -snrp -o KNAME "$1" | tail -n 1
}

external() {
    _aem_whole=$(topdev "$1")
    for _luks_uuid in $(getluksuuids); do
        _luks_whole=$(topdev "/dev/disk/by-uuid/$_luks_uuid")
        if [ "$_aem_whole" = "$_luks_whole" ]; then
            return 1
        fi
    done
    return 0
}

removable() {
    _rm="$(lsblk -dnr -o RM "$1") ${2-$(lsblk -dnr -o LABEL "$1")}"
    case "$_rm" in
        *.rm=[01]) _rm=${_rm##*=} ;;
                *) _rm=${_rm%% *} ;;
    esac

    [ "$_rm" = 1 ]
}

validatetpm() {
    # makes sure TPM is there and can be used, determines TPM version
    if [ ! -d "$SYSFS_TPM_DIR" ]; then
        message "$SYSFS_TPM_DIR isn't present"
        return 1
    fi

    _tpm_version=$(cat "$SYSFS_TPM_DIR/tpm_version_major")
    if [ -z "$_tpm_version" ]; then
        message "Failed to determine the version of the TPM"
        return 1
    fi

    if [ "$_tpm_version" -eq 1 ]; then
        # shellcheck source=../sbin/anti-evil-maid-lib-tpm1
        source /sbin/anti-evil-maid-lib-tpm1
        return 0
    fi

    if [ "$_tpm_version" -eq 2 ]; then
        # shellcheck source=../sbin/anti-evil-maid-lib-tpm2
        source /sbin/anti-evil-maid-lib-tpm2
        return 0
    fi

    message "Unexpected TPM version: $_tpm_version"
    return 1
}
