#!/bin/bash -p

set -euo pipefail

# sanitize environment
PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
YKFDE_LUKS_DEV=""
YKFDE_LUKS_KEYSLOT=""
YKFDE_CHANGE_KEYSLOT=""
YKFDE_OLD_YUBIKEY=""
DBG=""
YKFDE_SLOT_CHECK=""
YKFDE_SLOT_KILL=""
YKFDE_CHALLENGE_SLOT="2"
YKFDE_CHALLENGE_PASSWORD_NEEDED=""
YKFDE_CHALLENGE=""
YKFDE_RESPONSE=""
YKFDE_PASSPHRASE=""
YKFDE_OLD_CHALLENGE=""
YKFDE_OLD_RESPONSE=""
YKFDE_OLD_PASSPHRASE=""

if [ -r /etc/ykfde.conf ]; then
  # shellcheck source=ykfde.conf
  . /etc/ykfde.conf
else
  echo "WARNING: Can't access /etc/ykfde.conf. Falling back to defaults."
fi

while getopts ":d:s:ckovh" opt; do
  case "$opt" in
    d)
      YKFDE_LUKS_DEV="$OPTARG"
      printf '%s\n' "INFO: Setting device to '$OPTARG'."
      ;;
    s)
      if [ "$OPTARG" -gt -8 ] && [ "$OPTARG" -lt 8 ]; then
        YKFDE_LUKS_KEYSLOT="$OPTARG"
        printf '%s\n' "INFO: Setting LUKS keyslot to '$OPTARG'."
      else
        printf '%s\n' "ERROR: Chosen LUKS keyslot '$OPTARG' is invalid. Please choose valid LUKS keyslot number between '0-7'."
        exit 1
      fi
      ;;
    c)
      YKFDE_CHANGE_KEYSLOT=1
      echo "INFO: Changing existing LUKS keyslot"
      ;;
    k)
      YKFDE_SLOT_KILL=1
      echo "WARNING: Killing existing LUKS keyslot. If it's the last configured keyslot, the device will be inaccessible!"
      ;;
    o)
      YKFDE_OLD_YUBIKEY=1
      echo "INFO: Using old YubiKey for passphrase"
      ;;
    v)
      DBG=1
      echo "INFO: Debugging enabled"
      ;;
    h)
      echo
      echo " -d <device>   : select an existing device"
      echo " -s <slot>     : select the LUKS keyslot"
      echo " -c            : change an existing keyslot"
      echo " -k            : killing an existing keyslot"
      echo " -o            : use old YubiKey for passphrase"
      echo " -v            : show input/output in cleartext"
      echo
      exit 0
      ;;
    \?)
      printf '%s\n' "ERROR: Invalid option: '-$OPTARG'" >&2
      echo
      echo " -d <device>   : select an existing device"
      echo " -s <slot>     : select the LUKS keyslot"
      echo " -c            : change an existing keyslot"
      echo " -k            : killing an existing keyslot"
      echo " -o            : use old YubiKey for passphrase"
      echo " -v            : show input/output in cleartext"
      echo
      exit 1
      ;;
  esac
done

YKFDE_SLOT_CHECK="$(ykinfo -q -"$YKFDE_CHALLENGE_SLOT")"
[ "$DBG" ] && printf '%s\n' " > YubiKey slot status 'ykinfo -q -$YKFDE_CHALLENGE_SLOT': $YKFDE_SLOT_CHECK"

if [ "$YKFDE_SLOT_CHECK" != 1 ]; then
  printf '%s\n' "ERROR: Chosen YubiKey slot '$YKFDE_CHALLENGE_SLOT' isn't configured. Please choose slot configured for 'HMAC-SHA1 Challenge-Response' mode in '/etc/ykfde.conf'"
  exit 1
fi

if [ -z "$YKFDE_LUKS_DEV" ]; then
  echo "ERROR: Device not selected. Please select an existing device using '-d' option, see 'ykfde-enroll -h' for help."
  exit 1
fi

if [ ! -e "$YKFDE_LUKS_DEV" ]; then
  printf '%s\n' "ERROR: Selected device '$YKFDE_LUKS_DEV' doesn't exist. Please select an existing device."
  exit 1
fi

if [ ! -r "$YKFDE_LUKS_DEV" ] || [ ! -w "$YKFDE_LUKS_DEV" ]; then
  printf '%s\n' "ERROR: Selected device '$YKFDE_LUKS_DEV' isn't accessible for current user '$(whoami)'. Please execute this script as 'root'."
  exit 1
fi

if ! cryptsetup isLuks "$YKFDE_LUKS_DEV"; then
  printf '%s\n' "ERROR: Selected device '$YKFDE_LUKS_DEV' isn't a LUKS encrypted volume. Please select a valid device."
  exit 1
fi

if [ -z "$YKFDE_LUKS_KEYSLOT" ]; then
  echo "ERROR: LUKS keyslot not selected. Please select LUKS keyslot using '-s' option, see 'ykfde-enroll -h' for help."
  exit 1
fi

printf '%s\n' "WARNING: This script will utilize LUKS keyslot '$YKFDE_LUKS_KEYSLOT' on device '$YKFDE_LUKS_DEV'.  If this is not what you intended, please abort."

[ -z "$YKFDE_CHALLENGE" ] && YKFDE_CHALLENGE_PASSWORD_NEEDED=1
[ "$YKFDE_CHALLENGE_PASSWORD_NEEDED" ] && YKFDE_CHALLENGE=""

while [ -z "$YKFDE_CHALLENGE" ]; do
  echo " > Please provide the challenge."
  printf "   Enter challenge: "
  if [ "$DBG" ]; then read -r YKFDE_CHALLENGE; else read -r -s YKFDE_CHALLENGE; fi
  printf "\\n > Please repeat the challenge.\\n"
  printf "   Enter challenge: "
  if [ "$DBG" ]; then read -r YKFDE_CHALLENGE2; else read -r -s YKFDE_CHALLENGE2; fi
  if [ "$YKFDE_CHALLENGE" != "$YKFDE_CHALLENGE2" ]; then
    echo "WARNING: Challenges do not match. Try again."
    YKFDE_CHALLENGE=""
  fi
  [ "$YKFDE_CHALLENGE" ] && YKFDE_CHALLENGE="$(printf %s "$YKFDE_CHALLENGE" | sha256sum | awk '{print $1}')"
  # if /NOT/ DBG, we need to output \n here.
  [ "$DBG" ] || echo
done

if [ -z "$YKFDE_CHALLENGE" ]; then
  echo "ERROR: ykfde challenge is empty. Operation aborted."
  exit 1
fi

while [ -z "$YKFDE_RESPONSE" ]; do
  [ "$DBG" ] && printf '%s\n' "   Running: 'ykchalresp -$YKFDE_CHALLENGE_SLOT $YKFDE_CHALLENGE'..."
  echo "   Remember to touch the device if necessary."
  YKFDE_RESPONSE="$(printf %s "$YKFDE_CHALLENGE" | ykchalresp -"$YKFDE_CHALLENGE_SLOT" -i- | tr -d '\n')" || true
  [ "$DBG" ] && printf '%s\n' "   Received response: '$YKFDE_RESPONSE'"
done

if [ "$YKFDE_RESPONSE" ]; then
  if [ "$YKFDE_CHALLENGE_PASSWORD_NEEDED" ]; then
    YKFDE_PASSPHRASE="$YKFDE_CHALLENGE$YKFDE_RESPONSE"
  else
    YKFDE_PASSPHRASE="$YKFDE_RESPONSE"
  fi
fi

if [ "$YKFDE_SLOT_KILL" ]; then
  [ "$DBG" ] && printf '%s\n' " > Killing with 'cryptsetup luksKillSlot $YKFDE_LUKS_DEV $YKFDE_LUKS_KEYSLOT'..." || echo " > Killing slot with 'cryptsetup'..."
  printf %s "$YKFDE_PASSPHRASE" | cryptsetup luksKillSlot "$YKFDE_LUKS_DEV" "$YKFDE_LUKS_KEYSLOT" 2>&1
  printf '%s\n' "   LUKS key slot $YKFDE_LUKS_KEYSLOT successfully killed"
  exit 0
fi

if [ "$YKFDE_OLD_YUBIKEY" ]; then
  echo "Please insert old YubiKey for existing keyslot."

  while [ -z "$YKFDE_OLD_CHALLENGE" ]; do
    echo " > Please provide the old challenge."
    printf "   Enter challenge: "
    if [ "$DBG" ]; then read -r YKFDE_OLD_CHALLENGE; else read -r -s YKFDE_OLD_CHALLENGE; fi
    printf "\\n > Please repeat the old challenge.\\n"
    printf "   Enter challenge: "
    if [ "$DBG" ]; then read -r YKFDE_OLD_CHALLENGE2; else read -r -s YKFDE_OLD_CHALLENGE2; fi
    if [ "$YKFDE_OLD_CHALLENGE" != "$YKFDE_OLD_CHALLENGE2" ]; then
      echo "WARNING: Challenges do not match. Try again."
      YKFDE_OLD_CHALLENGE=""
    fi
    [ "$YKFDE_OLD_CHALLENGE" ] && YKFDE_OLD_CHALLENGE="$(printf %s "$YKFDE_OLD_CHALLENGE" | sha256sum | awk '{print $1}')"
    # if /NOT/ DBG, we need to output \n here.
    [ "$DBG" ] || echo
  done

  if [ -z "$YKFDE_OLD_CHALLENGE" ]; then
    echo "ERROR: ykfde old challenge is empty. Operation aborted."
    exit 1
  fi

  while [ -z "$YKFDE_OLD_RESPONSE" ]; do
    [ "$DBG" ] && printf '%s\n' "   Running: 'ykchalresp -$YKFDE_CHALLENGE_SLOT $YKFDE_OLD_CHALLENGE'..."
    echo "   Remember to touch the old device if necessary."
    YKFDE_OLD_RESPONSE="$(printf %s "$YKFDE_OLD_CHALLENGE" | ykchalresp -"$YKFDE_CHALLENGE_SLOT" -i- | tr -d '\n')" || true
    [ "$DBG" ] && printf '%s\n' "   Received response: '$YKFDE_OLD_RESPONSE'"
  done

  if [ "$YKFDE_OLD_RESPONSE" ]; then
    if [ "$YKFDE_CHALLENGE_PASSWORD_NEEDED" ]; then
      YKFDE_OLD_PASSPHRASE="$YKFDE_OLD_CHALLENGE$YKFDE_OLD_RESPONSE"
    else
      YKFDE_OLD_PASSPHRASE="$YKFDE_OLD_RESPONSE"
    fi
  fi
else
  echo "Please provide the old LUKS passphrase for the existing keyslot."
  printf " Enter passphrase: "
  if [ "$DBG" ]; then read -r YKFDE_OLD_PASSPHRASE; else read -r -s YKFDE_OLD_PASSPHRASE; fi
fi

if [ "$YKFDE_PASSPHRASE" ]; then
  [ "$DBG" ] && printf '%s\n' " > Passing '$YKFDE_PASSPHRASE' to 'cryptsetup'"
  if [ "$YKFDE_CHANGE_KEYSLOT" ]; then
    [ "$DBG" ] && printf '%s\n' " > Changing LUKS passphrase with 'cryptsetup --key-slot=$YKFDE_LUKS_KEYSLOT luksChangeKey $YKFDE_LUKS_DEV'..." || echo " > Changing LUKS passphrase with 'cryptsetup'..."
    printf '%s\n' "$YKFDE_OLD_PASSPHRASE" "$YKFDE_PASSPHRASE" "$YKFDE_PASSPHRASE" | cryptsetup --key-slot="$YKFDE_LUKS_KEYSLOT" luksChangeKey "$YKFDE_LUKS_DEV" 2>&1
    printf '%s\n' "   LUKS passphrase for key slot $YKFDE_LUKS_KEYSLOT successfully changed"
  else
    [ "$DBG" ] && printf '%s\n' " > Adding new LUKS passphrase with 'cryptsetup --key-slot=$YKFDE_LUKS_KEYSLOT luksAddKey $YKFDE_LUKS_DEV'..." || echo " > Adding new LUKS passphrase with 'cryptsetup'..."
    printf '%s\n' "$YKFDE_OLD_PASSPHRASE" "$YKFDE_PASSPHRASE" "$YKFDE_PASSPHRASE" | cryptsetup --key-slot="$YKFDE_LUKS_KEYSLOT" luksAddKey "$YKFDE_LUKS_DEV" 2>&1
    printf '%s\n' "   New LUKS passphrase successfully added"
  fi
else
  echo "ERROR: ykfde passphrase is empty. Operation aborted."
  exit 1
fi

exit 0
