#!/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: LicenseRef-CNCL-1.1 OR LicenseRef-CCLA-1.1
# 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

# Module summary:
# - Runs after the encrypted live root filesystem has been decrypted.
# - Requires the pinned public key, attestation hash file, and detached signature to exist as readable, non-empty regular files
#   inside the decrypted rootfs.
# - Verifies the attestation signature with gpgv against the pinned key material.
# - Confirms that the signature fingerprint matches the build-time expected rootfs fingerprint and panics on missing, malformed,
#   or mismatched evidence.

_SAVED_SET_OPTS="$(set +o)"

set -eu

printf "\e[95m[INFO] Starting             : [/usr/lib/live/boot/0042_ciss_post_decrypt_attest] \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@"

### Name of the top-level dm-crypt mapping (e.g., cryptsetup --label): zzzz_ciss_crypt_squash.hook.binary ----------------------
export CDLB_MAPPER_NAME="${CDLB_MAPPER_NAME:-crypt_liveiso}"

### Attestation file locations inside decrypted rootfs. ------------------------------------------------------------------------
CDLB_ATTEST_FPR_SHA="${CDLB_ATTEST_FPR_SHA:-/root/root/.ciss/attestation/${CDLB_EXP_FPR}.gpg.sha512sum.txt}"
CDLB_ATTEST_FPR_SIG="${CDLB_ATTEST_FPR_SIG:-/root/root/.ciss/attestation/${CDLB_EXP_FPR}.gpg.sha512sum.txt.sig}"
CDLB_KEY_DIR="${CDLB_KEY_DIR:-/etc/ciss/keys}"

### 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' "$*"; }

#######################################
# Validate a boot-time attestation input file.
# Globals:
#   None
# Arguments:
#   1: Human-readable artifact label
#   2: Absolute artifact path
# Returns:
#   0: on success
#######################################
require_attestation_file() {
  artifact_label="${1}"
  artifact_path="${2}"

  if [ ! -e "${artifact_path}" ]; then

    if [ -L "${artifact_path}" ]; then

      log_er "0042()              : ${artifact_label} is a broken symlink, not a regular file: [${artifact_path}]"
      panic  "0042()              : ${artifact_label} is a broken symlink, not a regular file: [${artifact_path}]"

    fi

    log_er "0042()              : ${artifact_label} missing: [${artifact_path}]"
    panic  "0042()              : ${artifact_label} missing: [${artifact_path}]"

  fi

  if [ -L "${artifact_path}" ] || [ ! -f "${artifact_path}" ]; then

    log_er "0042()              : ${artifact_label} is not a regular file: [${artifact_path}]"
    panic  "0042()              : ${artifact_label} is not a regular file: [${artifact_path}]"

  fi

  if [ ! -s "${artifact_path}" ]; then

    log_er "0042()              : ${artifact_label} is empty: [${artifact_path}]"
    panic  "0042()              : ${artifact_label} is empty: [${artifact_path}]"

  fi

  if [ ! -r "${artifact_path}" ]; then

    log_er "0042()              : ${artifact_label} is not readable: [${artifact_path}]"
    panic  "0042()              : ${artifact_label} is not readable: [${artifact_path}]"

  fi

  return 0
}

HASH_FILE="${CDLB_ATTEST_FPR_SHA}"
SIGN_FILE="${CDLB_ATTEST_FPR_SIG}"
KEYFILE="${CDLB_KEY_DIR}/${CDLB_EXP_FPR}.gpg"

require_attestation_file "Public key"            "${KEYFILE}"
require_attestation_file "Attestation data"      "${HASH_FILE}"
require_attestation_file "Attestation signature" "${SIGN_FILE}"

log_in "0042()               : Verifying rootfs attestation with 'gpgv' and inside LUKS encrypted rootfs pinned GPG FPR."

if ! _STATUS="$(/usr/bin/gpgv --keyring "${KEYFILE}" --status-fd 1 "${SIGN_FILE}" "${HASH_FILE}" 2>&1)"; then

  log_er "0042()              : gpgv verification failed for signature: [${SIGN_FILE}]"

  if [ -n "${_STATUS}" ]; then

    printf '%s\n' "${_STATUS}" >&2

  fi

  sleep 8
  panic  "0042()              : gpgv verification failed for signature: [${SIGN_FILE}]"

fi

_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 "0042()               : Signature FPR valid: got: [${_CDLB_SIG_FILE_FPR}] expected: [${CDLB_EXP_FPR}]"

else

  log_er "0042()              : 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

eval "${_SAVED_SET_OPTS}"

printf "\e[92m[INFO] Successfully applied : [/usr/lib/live/boot/0042_ciss_post_decrypt_attest] \n\e[0m"

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