#!/bin/sh
# bashsupport disable=BP5007
# shellcheck disable=SC2249
# shellcheck shell=sh

# SPDX-Version: 3.0
# SPDX-CreationInfo: 2025-11-12; WEIDNER, Marc S.; <msw@coresecret.dev>
# SPDX-ExternalRef: GIT https://git.coresecret.dev/msw/CISS.debian.live.builder.git
# SPDX-FileContributor: WEIDNER, Marc S.; Centurion Intelligence Consulting Agency
# SPDX-FileCopyrightText: 2024-2025; WEIDNER, Marc S.; <msw@coresecret.dev>
# SPDX-FileType: SOURCE
# SPDX-License-Identifier: GPL-3.0-or-later
# SPDX-LicenseComment: This file is part of the CISS.debian.installer.secure framework.
# SPDX-PackageName: CISS.debian.live.builder
# SPDX-Security-Contact: security@coresecret.eu

### Modified Version of the original file:
### https://salsa.debian.org/live-team/live-boot 'components/0030-ciss-verify-checksums'
### If the offered checksum is successfully verified, proceed with booting. Otherwise, panic.

#######################################
# Modified checksum-integrity and authenticity-verification-script depending on pinned GPG FPR for boot process verification.
# Globals:
#   LIVE_BOOT_CMDLINE
#   _TTY
# Arguments:
#   1: _MOUNTPOINT
# Returns:
#   0 : Successful verification
#######################################
Verify_checksums() {
  printf "\e[95m[INFO] Starting             : [/usr/lib/live/boot/0030-ciss-verify-checksums] \n\e[0m"

  ### Declare variables --------------------------------------------------------------------------------------------------------

  ### Will be replaced at build time:
  export CDLB_EXP_FPR="@EXP_FPR@"
  export CDLB_EXP_CA_FPR="@EXP_CA_FPR@"

  ### Declare functions --------------------------------------------------------------------------------------------------------

  #######################################
  # Helper for colored text output on stdout.
  # Globals:
  #   None
  # Arguments:
  #   *: String to print
  #######################################
  log_in() { printf '\e[95m[INFO] %s \n\e[0m' "$*"; }


  #######################################
  # Helper for colored text output on stdout.
  # Globals:
  #   None
  # Arguments:
  #   *: String to print
  #######################################
  log_ok() { printf '\e[92m[INFO] %s \n\e[0m' "$*"; }

  #######################################
  # Helper for colored text output on stdout.
  # Globals:
  #   None
  # Arguments:
  #   *: String to print
  #######################################
  log_er() { printf '\e[91m[FATAL] %s \n\e[0m' "$*"; }

  _MOUNTPOINT="${1}"

  _PARAMETER=""

  _TTY="/dev/tty8"

  LIVE_VERIFY_CHECKSUMS_DIGESTS="${LIVE_VERIFY_CHECKSUMS_DIGESTS:-sha512 sha384 sha256}"

  LIVE_VERIFY_CHECKSUMS_SIGNATURES="false"

  _KEYFILE=""

  _MP=""

  ### Parse commandline arguments ----------------------------------------------------------------------------------------------
  for _PARAMETER in ${LIVE_BOOT_CMDLINE}; do

    case "${_PARAMETER}" in

      live-boot.verify-checksums=* | verify-checksums=*)

        LIVE_VERIFY_CHECKSUMS="true"
        LIVE_VERIFY_CHECKSUMS_DIGESTS="${_PARAMETER#*verify-checksums=}"
        ;;

      live-boot.verify-checksums | verify-checksums)

        LIVE_VERIFY_CHECKSUMS="true"
        ;;

      live-boot.verify-checksums-signatures | verify-checksums-signatures)

        LIVE_VERIFY_CHECKSUMS_SIGNATURES="true"
        ;;

    esac

  done

  ### Check if the function should be skipped ----------------------------------------------------------------------------------
  case "${LIVE_VERIFY_CHECKSUMS}" in

    true)
      :
      ;;

    *)
      return 0
      ;;

  esac

  ### Check GPG pubkey file correct path ---------------------------------------------------------------------------------------
  for _MP in /lib/live/mount/medium /run/live/medium /cdrom /; do

    if [ -e "${_MP}/${CDLB_EXP_FPR}.gpg" ]; then

      _KEYFILE="${_MP}/${CDLB_EXP_FPR}.gpg"

      if [ -e "${_MP}/${CDLB_EXP_CA_FPR}.gpg" ]; then

        _CA_KEYFILE="${_MP}/${CDLB_EXP_CA_FPR}.gpg"

      fi

      break

    fi

  done

  # shellcheck disable=SC2164
  cd "${_MOUNTPOINT}"


  ### CDLB verification of script integrity itself -----------------------------------------------------------------------------
  if [ "${LIVE_VERIFY_CHECKSUMS_SIGNATURES}" = "true" ]; then

    log_begin_msg "Verifying integrity of: [0030-ciss-verify-checksums]"
    printf "\n"

    _CAND=""
    CDLB_SCRIPT_SELF=""  CDLB_CMD="" CDLB_COMPUTED="" CDLB_EXPECTED="" CDLB_HASHFILE="" CDLB_SIG_FILE=""

    CDLB_CMD="/usr/bin/sha512sum"
    CDLB_SHA="sha512"

    for _CAND in /scripts/live-bottom/0030-ciss-verify-checksums /usr/lib/live/boot/0030-ciss-verify-checksums ; do

      [ -e "${_CAND}" ] && { CDLB_SCRIPT_SELF="${_CAND}"; break; }

    done

    CDLB_SCRIPT_FILE="${CDLB_SCRIPT_SELF##*/}"
    CDLB_SCRIPT_PATH="${CDLB_SCRIPT_SELF%/*}"
    CDLB_SCRIPT_FULL="${CDLB_SCRIPT_PATH%/}/${CDLB_SCRIPT_FILE}"
    CDLB_HASHFILE="/etc/ciss/hashes/${CDLB_SCRIPT_FILE}.${CDLB_SHA}sum.txt"
    CDLB_SIG_FILE="/etc/ciss/signatures/${CDLB_SCRIPT_FILE}.${CDLB_SHA}sum.txt.sig"

    _STATUS="$(/usr/bin/gpgv --keyring "${_KEYFILE}" --status-fd 1 "${CDLB_SIG_FILE}" "${CDLB_HASHFILE}")"

    _CDLB_SIG_FILE_FPR="$(printf '%s\n' "${_STATUS}" | awk '/^\[GNUPG:\] VALIDSIG /{print $3; exit}')"

    ### Compare against pinned and expected fingerprint.
    if [ "${_CDLB_SIG_FILE_FPR}" = "${CDLB_EXP_FPR}" ]; then

      log_ok "Signature FPR valid: got: [${_CDLB_SIG_FILE_FPR}] expected: [${CDLB_EXP_FPR}]"

    else

      log_er "Signature FPR mismatch: got: [${_CDLB_SIG_FILE_FPR}] expected: [${CDLB_EXP_FPR}]"
      sleep 8
      panic "[FATAL] Signature FPR mismatch: got: [${_CDLB_SIG_FILE_FPR}] expected: [${CDLB_EXP_FPR}]."

    fi

    ### Script self-integrity and authenticity checks --------------------------------------------------------------------------
    ### Assumption: initramfs itself is not altered.
    log_in "Verifying signature of: [${CDLB_SIG_FILE}] ..."

    if ! /usr/bin/gpgv --keyring "${_KEYFILE}" --status-fd 1 "${CDLB_SIG_FILE}" "${CDLB_HASHFILE}"; then

      log_er "Verifying signature of: [${CDLB_SIG_FILE}] failed."
      sleep 8
      panic  "[FATAL] Verifying signature of: [${CDLB_SIG_FILE}] failed."

    else

      log_ok "Verifying signature of: [${CDLB_SIG_FILE}] successful."

    fi

    log_in "Recomputing hash for: [${CDLB_SHA}] ..."

    CDLB_COMPUTED=$("${CDLB_CMD}" "${CDLB_SCRIPT_FULL}" | { read -r first _ || exit 1; printf '%s\n' "${first}"; })
    IFS=' ' read -r CDLB_EXPECTED _ < "${CDLB_HASHFILE}"

    if [ "${CDLB_COMPUTED}" != "${CDLB_EXPECTED}" ]; then

      log_er "Recomputing hash for: [${CDLB_SHA}] failed."
      sleep 8
      panic  "[FATAL] Recomputing hash for: [${CDLB_SHA}] failed."

    fi

    log_ok "Recomputing hash for: [${CDLB_SHA}] successful."
    log_ok "Verification of authenticity and integrity of [${CDLB_SCRIPT_FULL}] successfully completed."
    log_end_msg
    printf "\n"

  fi

  ### Checksum and checksum signature verification -----------------------------------------------------------------------------
  log_begin_msg "Verifying checksums"
  printf "\n"
  log_in "Verifying checksums ..."

  # shellcheck disable=SC2001
  for _DIGEST in $(echo "${LIVE_VERIFY_CHECKSUMS_DIGESTS}" | sed -e 's|,| |g'); do

    # shellcheck disable=SC2060
    _CHECKSUMS="$(echo "${_DIGEST}" | tr [a-z] [A-Z])SUMS ${_DIGEST}sum.txt"

    for _CHECKSUM in ${_CHECKSUMS}; do

      if [ -e "${_CHECKSUM}" ]; then

        log_in "Found: [${_CHECKSUM}] ..."

        if [ -e "/usr/bin/${_DIGEST}sum" ]; then

          log_in "Found: [/usr/bin/${_DIGEST}sum] ..."

          if [ "${LIVE_VERIFY_CHECKSUMS_SIGNATURES}" = "true" ]; then

            log_in "Checking signature of: [${_CHECKSUM}] ..."

            _CHECKSUM_SIGNATURE="${_CHECKSUM}.sig"

            if /usr/bin/gpgv --keyring "${_KEYFILE}" --status-fd 1 "${_CHECKSUM_SIGNATURE}" "${_CHECKSUM}"; then

              _RETURN_PGP="${?}"
              log_in "Checking signature of: [${_CHECKSUM}] successful."

            else

              _RETURN_PGP="${?}"
              log_er "Checking signature of: [${_CHECKSUM}] failed."

            fi

          else

            _RETURN_PGP="na"

          fi

          # shellcheck disable=SC2312
          if grep -v '^#' "${_CHECKSUM}" | /usr/bin/"${_DIGEST}"sum -c > "${_TTY}"; then

            _RETURN_SHA="${?}"
            log_ok "Found: [/usr/bin/${_DIGEST}sum] successful verified: [${_CHECKSUM}]"

          else

            _RETURN_SHA="${?}"
            log_er "Found: [/usr/bin/${_DIGEST}sum] unsuccessful verified: [${_CHECKSUM}]"

          fi

          # Stop after the first verification.
          break 2

        else

          _RETURN_SHA="255"
          log_er "NOT Found [/usr/bin/${_DIGEST}sum]."

        fi

      fi

    done

  done

  log_end_msg
  printf "\n"

  case "${_RETURN_PGP},${_RETURN_SHA}" in

    "0,0")
      log_ok "Verification of [GPG signature] and [sha checksum] file successful; continuing booting."
      log_success_msg "Verification of [GPG signature] and [sha checksum] file successful; continuing booting."
      return 0
      ;;

    "na,0")
      log_ok "Verification of [sha checksum] file successful; continuing booting."
      log_success_msg "Verification of [sha checksum] file successful; continuing booting."
      return 0
      ;;

    "0,"*)
      log_er "Verification of [GPG signature] file successful, while verification of [sha checksum] file failed."
      sleep 8
      panic  "Verification of [GPG signature] file successful, while verification of [sha checksum] file failed."
      ;;

    *",0")
      log_er "Verification of [GPG signature] file failed, while verification of [sha checksum] file successful."
      sleep 8
      panic  "Verification of [GPG signature] file failed, while verification of [sha checksum] file successful."
      ;;

    "na,"*)
      log_er "Verification of [sha checksum] file failed."
      sleep 8
      panic  "Verification of [sha checksum] file failed."
      ;;

  esac
}
# vim: number et ts=2 sw=2 sts=2 ai tw=128 ft=sh
