#!/bin/bash
# SPDX-Version: 3.0
# SPDX-CreationInfo: 2025-11-06; 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

guard_sourcing || return "${ERR_GUARD_SRCE}"

#######################################
# Integrates CISS dropbear and SOPS Age Key and CISS and PhysNet primordial SSH identity files.
# Globals:
#   BASH_SOURCE
#   VAR_AGE
#   VAR_AGE_KEY
#   VAR_HANDLER_BUILD_DIR
#   VAR_SSHFP
#   VAR_TMP_SECRET
#   VAR_WORKDIR
# Arguments:
#   None
# Returns:
#   0: on success
#######################################
init_primordial() {
  printf "\e[95m++++ ++++ ++++ ++++ ++++ ++++ ++ 🧪 %s starting ... \e[0m\n" "${BASH_SOURCE[0]}"

  ### Prepare CISS dropbear integration ----------------------------------------------------------------------------------------
  declare var_dropbear_version="2025.88"

  install -d -m 0755 "${VAR_HANDLER_BUILD_DIR}/config/includes.chroot/etc/initramfs-tools/files"
  install -d -m 0755 "${VAR_HANDLER_BUILD_DIR}/config/includes.chroot/root/build"
  install -d -m 0755 "${VAR_HANDLER_BUILD_DIR}/config/includes.chroot/root/dropbear"

  install    -m 0444 "${VAR_WORKDIR}/upgrades/dropbear/dropbear-${var_dropbear_version}.tar.bz2" \
    "${VAR_HANDLER_BUILD_DIR}/config/includes.chroot/root/dropbear/dropbear-${var_dropbear_version}.tar.bz2"
  install    -m 0444 "${VAR_WORKDIR}/upgrades/dropbear/localoptions.h" \
    "${VAR_HANDLER_BUILD_DIR}/config/includes.chroot/root/dropbear/localoptions.h"
  install    -m 0444 "${VAR_WORKDIR}/config/includes.chroot/usr/share/initramfs-tools/scripts/init-premount/dropbear" \
    "${VAR_HANDLER_BUILD_DIR}/config/includes.chroot/root/dropbear.file"

  ### Check for SOPS AGE key integration ---------------------------------------------------------------------------------------
  if [[ "${VAR_AGE,,}" == "true" ]]; then

    install -d -m 0700                                 "${VAR_HANDLER_BUILD_DIR}/config/includes.chroot/root/.config/sops/age"
    install -m 0400 "${VAR_TMP_SECRET}/${VAR_AGE_KEY}" "${VAR_HANDLER_BUILD_DIR}/config/includes.chroot/root/.config/sops/age/keys.txt"
    shred -fzu -n 5 -- "${VAR_TMP_SECRET}/${VAR_AGE_KEY}" 2>/dev/null || rm -f "${VAR_TMP_SECRET}/${VAR_AGE_KEY}"

  fi

  ### Check for SSH CISS and PhysNet primordial-workflow(tm) integration -------------------------------------------------------
  if [[ "${VAR_SSHFP,,}" == "true" ]]; then

    install -d -m 0700                      "${VAR_HANDLER_BUILD_DIR}/config/includes.chroot/root/.ssh"
    install -m 0600 "${VAR_TMP_SECRET}/id"* "${VAR_HANDLER_BUILD_DIR}/config/includes.chroot/root/.ssh/"
    normalize_ssh_keys_in_dir               "${VAR_HANDLER_BUILD_DIR}/config/includes.chroot/root/.ssh"
    shred -fzu -n 5 -- "${VAR_TMP_SECRET}/id"* 2>/dev/null || rm -f "${VAR_TMP_SECRET}/id"*

    install -d -m 0700                             "${VAR_HANDLER_BUILD_DIR}/config/includes.chroot/root/ssh"
    install -m 0600 "${VAR_TMP_SECRET}/ssh_host_"* "${VAR_HANDLER_BUILD_DIR}/config/includes.chroot/root/ssh/"
    normalize_ssh_keys_in_dir                      "${VAR_HANDLER_BUILD_DIR}/config/includes.chroot/root/ssh/"
    shred -fzu -n 5 -- "${VAR_TMP_SECRET}/ssh_host_"* 2>/dev/null || rm -f "${VAR_TMP_SECRET}/ssh_host_"*

  fi

  printf "\e[92m++++ ++++ ++++ ++++ ++++ ++++ ++ ✅ %s successfully applied. \e[0m\n" "${BASH_SOURCE[0]}"

  return 0
}
### Prevents accidental 'unset -f'.
# shellcheck disable=SC2034
readonly -f init_primordial

#######################################
# Normalize SSH key files: strip CRLF.
# Globals:
#   None
# Arguments:
#   1: ssh_host_key or id file
# Returns:
#   0: on success
#   ERR_SANITIZING: on failure
#######################################
normalize_ssh_key_file() {
  declare    var_key_file="" var_tmp_file=""
  declare -i var_is_pub=0
  var_key_file="$1"

  [[ -f "${var_key_file}" ]] || return 0

  # shellcheck disable=SC2249
  case "${var_key_file}" in
    *.pub)
      var_is_pub=1
      ;;
  esac

  ### If there is any CR (carriage return), strip it.
  if grep -q $'\r' "${var_key_file}"; then

    ### Use a temporary file to avoid in-place corruption-
    var_tmp_file="${var_key_file}.noCR.$$"

    ### Remove only '\r', keep everything else as-is.
    if ! tr -d '\r' < "${var_key_file}" >| "${var_tmp_file}"; then

      printf "\e[91m++++ ++++ ++++ ++++ ++++ ++++ ++ ❌ Failed to normalize CRLF: [%s] \e[0m\n" "${var_key_file}"
      rm -f "${var_tmp_file}"
      return "${ERR_SANITIZING}"

    fi

    mv "${var_tmp_file}" "${var_key_file}"

    if [[ "${var_is_pub}" -eq 1 ]]; then

      chmod 0644 "${var_key_file}"

    else

      chmod 0600 "${var_key_file}"

    fi

    ### Validate with ssh-keygen if available.
    if command -v ssh-keygen >/dev/null 2>&1; then

      ### Always: fingerprint check (works for private and public keys).
      if ! ssh-keygen -lf "${var_key_file}" >/dev/null; then

        printf "\e[91m++++ ++++ ++++ ++++ ++++ ++++ ++ ❌ Failed check ssh-keygen -lf: [%s] \e[0m\n" "${var_key_file}"
        return "${ERR_SANITIZING}"

      fi

      ### Only for private keys: derive the public key to ensure libcrypto can parse the private key.
      if [[ "${var_is_pub}" -eq 0 ]]; then

        if ! ssh-keygen -yf "${var_key_file}" >/dev/null; then

          printf "\e[91m++++ ++++ ++++ ++++ ++++ ++++ ++ ❌ Failed check ssh-keygen -yf: [%s] \e[0m\n" "${var_key_file}"
          return "${ERR_SANITIZING}"

        fi

      fi

    fi

    sha256sum "${var_key_file}" >| "${var_key_file}.sha256sum.txt"
    chmod 0440 "${var_key_file}.sha256sum.txt"

  fi

  return 0
}
### Prevents accidental 'unset -f'.
# shellcheck disable=SC2034
readonly -f normalize_ssh_key_file

#######################################
# Normalize SSH key files in dir.
# Globals:
#   None
# Arguments:
#   1: directory
# Returns:
#   0: on success
#   ERR_SANITIZING: on failure
#######################################
normalize_ssh_keys_in_dir() {
  declare var_key_dir="" var_key_file="" _old_nullglob="" _old_dotglob="" _old_failglob=""
  var_key_dir="$1"

  ### Enable nullglob/dotglob, disable failglob for safe globbing.
  _old_nullglob="$(shopt -p nullglob || true)"
  _old_dotglob="$( shopt -p dotglob  || true)"
  _old_failglob="$(shopt -p failglob || true)"

  shopt -s nullglob dotglob
  shopt -u failglob

  if [[ ! -d "${var_key_dir}" ]]; then
    eval "${_old_nullglob}" 2>/dev/null || true
    eval "${_old_dotglob}"  2>/dev/null || true
    eval "${_old_failglob}" 2>/dev/null || true
    return 0
  fi

  ### Cover both root identity keys and host keys.
  for var_key_file in "${var_key_dir}"/id_* "${var_key_dir}"/ssh_host_*; do

    [[ -e "${var_key_file}" ]] || continue

    if ! normalize_ssh_key_file "${var_key_file}"; then
      eval "${_old_nullglob}" 2>/dev/null || true
      eval "${_old_dotglob}"  2>/dev/null || true
      eval "${_old_failglob}" 2>/dev/null || true
      return "${ERR_SANITIZING}"
    fi

  done

  eval "${_old_nullglob}" 2>/dev/null || true
  eval "${_old_dotglob}"  2>/dev/null || true
  eval "${_old_failglob}" 2>/dev/null || true

  return 0
}
### Prevents accidental 'unset -f'.
# shellcheck disable=SC2034
readonly -f normalize_ssh_keys_in_dir
# vim: number et ts=2 sw=2 sts=2 ai tw=128 ft=sh
