#!/bin/bash # SPDX-Version: 3.0 # SPDX-CreationInfo: 2025-05-05; WEIDNER, Marc S.; # SPDX-ExternalRef: GIT https://git.coresecret.dev/msw/CISS.debian.installer.git # SPDX-FileContributor: WEIDNER, Marc S.; Centurion Intelligence Consulting Agency # SPDX-FileCopyrightText: 2024–2025; WEIDNER, Marc S.; # SPDX-FileType: SOURCE # SPDX-License-Identifier: EUPL-1.2 OR LicenseRef-CCLA-1.0 # SPDX-LicenseComment: This file is part of the CISS.hardened.installer framework. # SPDX-PackageName: CISS.debian.installer # SPDX-Security-Contact: security@coresecret.eu # coresecret.sh to be executed after dropbear SSH login set -C -e -u -o pipefail IFS=$(printf ' \n\t') # shellcheck disable=SC2155 declare -gr CURRENTDATE=$(date +"%F %T") declare -gir MAX_RETRIES=2 ####################################### # Generates informative shell prompt. # Globals: # PS1 # Arguments: # None ####################################### prompt_string() { declare -gx PS1="\ \[\033[1;91m\]\d\[\033[0m\]|\[\033[1;91m\]\u\[\033[0m\]@\ \[\033[1;95m\]\h\[\033[0m\]:\ \[\033[1;96m\]\w\[\033[0m\]/>>\ \$(if [[ \$? -eq 0 ]]; then \ # Show exit status in green if zero echo -e \"\[\033[1;92m\]\$?\[\033[0m\]\"; \ else \ # Show exit status in red otherwise echo -e \"\[\033[1;91m\]\$?\[\033[0m\]\"; \ fi)\ |~\$ " } ####################################### # Trap function to be called on 'ERR'. # Arguments: # $1: ${?} # $2: ${BASH_SOURCE[0]} # $3: ${LINENO} # $4: ${FUNCNAME[0]:-main} # $5: ${BASH_COMMAND} ####################################### trap_on_err() { declare -r errcode="$1" declare -r errscrt="$2" declare -r errline="$3" declare -r errfunc="$4" declare -r errcmmd="$5" trap - ERR stty echo if [[ ${errcode} -eq 0 ]]; then print_scr_scc prompt_string exit 0 else print_scr_err "${errcode}" "${errscrt}" "${errline}" "${errfunc}" "${errcmmd}" sleep 15 sync set +C echo 1 > /proc/sys/kernel/sysrq echo o > /proc/sysrq-trigger fi } ####################################### # Security Trap on 'INT' and 'TERM' to provide a deterministic way to not circumvent the Nuke Routine # Globals: # DEVICES_LUKS # DEVICES_NUKE # Arguments: # None ####################################### trap_on_term() { trap - INT stty echo printf "\n" printf "\e[0;91m✘ System caught a 'SIGINT'. System Power Off in 3 seconds. \e[0m\n" >&2 sync sleep 3 set +C echo 1 > /proc/sys/kernel/sysrq echo o > /proc/sysrq-trigger } ####################################### # Print Error Message for 'non-0' Trap on 'EXIT' on Terminal. # Arguments: # $1: ${?} # $2: ${BASH_SOURCE[0]} # $3: ${LINENO} # $4: ${FUNCNAME[0]:-main} # $5: ${BASH_COMMAND} ####################################### print_scr_err() { declare -r scr_err_errcode="$1" declare -r scr_err_errscrt="$2" declare -r scr_err_errline="$3" declare -r scr_err_errfunc="$4" declare -r scr_err_errcmmd="$5" printf "\n" printf "\e[0;91m✘ System caught an 'ERROR'. System Power Off in 15 seconds. \e[0m\n" >&2 printf "\n" printf "\e[0;91m✘ Error : %s \e[0m\n" "${scr_err_errcode}" >&2 printf "\e[0;91m✘ Line : %s \e[0m\n" "${scr_err_errline}" >&2 printf "\e[0;91m✘ Script : %s \e[0m\n" "${scr_err_errscrt}" >&2 printf "\e[0;91m✘ Function : %s \e[0m\n" "${scr_err_errfunc}" >&2 printf "\e[0;91m✘ Command : %s \e[0m\n" "${scr_err_errcmmd}" >&2 printf "\n" } ####################################### # Print Error Message for '0' Trap on 'ERR' on Terminal. # Arguments: # none ####################################### print_scr_scc() { printf "\e[0;92m✅ Script exited successfully. Proceeding with booting. \e[0m\n" printf "\n" } ####################################### # Gather information of all LUKS Devices available on the system # Arguments: # None ####################################### gather_luks_devices() { declare prev=() curr=() dev="" tries=0 while ((tries < 10)); do mapfile -t curr < <(blkid -t TYPE=crypto_LUKS -o device) if [[ ${curr[*]} == "${prev[*]}" ]]; then break fi prev=("${curr[@]}") tries=$((tries + 1)) sleep 1 done ### Print one device per line for mapfile compatibility for dev in "${curr[@]}"; do printf '%s\n' "${dev}" done } ####################################### # Gather information of all NUKE Devices available on the system # Globals: # DEVICES_LUKS # Arguments: # None ####################################### gather_nuke_devices() { ### 'DEVICES_LUKS' must already be a bash array of device paths declare dev="" declare result=() for dev in "${DEVICES_LUKS[@]}"; do ### Check slot 31 for 'luks2' if cryptsetup luksDump "${dev}" 2> /dev/null | grep -qE '^[[:space:]]*31: luks2'; then result+=("${dev}") fi done ### Print one device per line for mapfile compatibility for dev in "${result[@]}"; do printf '%s\n' "${dev}" done } ####################################### # Read passphrase interactively. # Arguments: # None ####################################### passphrase_ask() { declare -g PASSPHRASE="" printf "\n" stty -echo printf "\e[0;95m🔐 Enter passphrase for decryption: \e[0m\n" read -r PASSPHRASE stty echo printf "\n" } ####################################### # Test the entered passphrase against the dedicated Nuke Keyslot #31. # Arguments: # $1: DEVICE # $2: PASSWD ####################################### passphrase_test() { declare -r DEVICE="$1" declare -r PASPHR="$2" printf '%s' "${PASPHR}" | cryptsetup open --batch-mode --test-passphrase --key-slot 31 "${DEVICE}" > /dev/null 2>&1 } ####################################### # Check the integrity and authenticity of this script itself. # Arguments: # $0: Script Name ####################################### verify_coresecret() { ### Directory of this script # shellcheck disable=SC2155 declare dir="$(dirname "$(readlink -f "${0}")")" # shellcheck disable=SC2155 declare script="$(basename "${0}")" declare algo for algo in sha512 sha384; do # shellcheck disable=SC2155 declare hashfile="${dir}/${script}.${algo}" # shellcheck disable=SC2155 declare sigfile="${hashfile}.sig" # shellcheck disable=SC2155 declare cmd="${algo}sum" printf "\e[0;95m🔏 Verifying signature of: [%s] \e[0m\n" "${hashfile}" gpgv --keyring /etc/keys/pubring.gpg "${sigfile}" "${hashfile}" || { printf "\e[0;91m✘ Signature verification failed for: [%s] \e[0m\n" "${hashfile}" >&2 printf "\e[0;91m✘ System Power Off in 3 seconds. \e[0m\n" >&2 sync sleep 3 set +C echo 1 > /proc/sys/kernel/sysrq echo o > /proc/sysrq-trigger } printf "\e[0;92m🔏 Verifying signature of: [%s] successful. \e[0m\n" "${hashfile}" printf "\e[0;95m🔢 Recomputing Hash: [%s] \e[0m\n" "${algo}" # shellcheck disable=SC2155 declare computed=$($cmd "${dir}/${script}" | awk '{print $1}') # shellcheck disable=SC2155 declare expected=$(cat "${hashfile}") if [[ ${computed} != "${expected}" ]]; then printf "\e[0;91m✘ Hash mismatch for: [%s] \e[0m\n" "${algo}" >&2 printf "\e[0;91m✘ System Power Off in 3 seconds. \e[0m\n" >&2 sync sleep 3 set +C echo 1 > /proc/sys/kernel/sysrq echo o > /proc/sysrq-trigger fi printf "\e[0;92m🔢 Recomputing Hash: [%s] successful. \e[0m\n" "${algo}" done printf "\e[0;92m🔏 All signatures and hashes verified successfully. Proceeding. \e[0m\n" } ### Main Programm trap 'trap_on_err "$?" "${BASH_SOURCE[0]}" "${LINENO}" "${FUNCNAME[0]:-main}" "${BASH_COMMAND}"' ERR trap 'trap_on_term' INT TERM printf "\e[0;91mCoresecret Connection established.\e[0m\n" printf "\e[0;91mStarting Time: %s\e[0m\n" "${CURRENTDATE}" printf "\n" verify_coresecret ### Read newline-separated output into an array mapfile -t DEVICES_LUKS < <(gather_luks_devices) mapfile -t DEVICES_NUKE < <(gather_nuke_devices) ### Debug output: list each element with its index #for idx in "${!DEVICES_LUKS[@]}"; do # printf 'Luks[%d]: %s\n' "${idx}" "${DEVICES_LUKS[${idx}]}" #done ### Debug output: list each element with its index #for idx in "${!DEVICES_NUKE[@]}"; do # printf 'Nuke[%d]: %s\n' "${idx}" "${DEVICES_NUKE[${idx}]}" #done ### # If there are no LUKS devices at all, drop to bash [[ -n ${DEVICES_LUKS[*]} ]] || { printf "\e[0;92m✘ No LUKS Devices found. Dropping to bash ... \e[0m\n" prompt_string exec /bin/bash -i } ### If there are LUKS devices but no Nuke devices, try unlocking flow if [[ -n ${DEVICES_LUKS[*]} ]] && [[ -z ${DEVICES_NUKE[*]} ]]; then ### Attempt interactive unlock with cryptroot-unlock if cryptroot-unlock; then exit 0 else printf "\n" printf "\e[0;91m✘ Unsuccessful command 'cryptroot-unlock'. \e[0m\n" printf "\e[0;92m✘ No LUKS operations performed. Dropping to bash ... \e[0m\n" printf "\e[0;92m✘ To unlock 'root' partition, and maybe others like 'swap', run 'cryptroot-unlock'. \e[0m\n" prompt_string exec /bin/bash -i fi elif [[ -n ${DEVICES_LUKS[*]} ]] && [[ -n ${DEVICES_NUKE[*]} ]]; then declare -i attempt=1 declare NUKED=false declare TEST_DEV="${DEVICES_NUKE[0]}" while ((attempt <= MAX_RETRIES)); do printf "\e[0;95m🔐Attempt %s/%s: \e[0m\n" "${attempt}" "${MAX_RETRIES}" passphrase_ask declare -g PASSWD="${PASSPHRASE}" if passphrase_test "${TEST_DEV}" "${PASSWD}"; then for dev in "${DEVICES_NUKE[@]}"; do cryptsetup erase --batch-mode "${dev}" > /dev/null 2>&1 printf "%s:\e[0;95m✘ LUKS Device Header malfunction. \e[0m\n" "${dev}" done declare -r NUKED=true unset PASSWD break else declare code="$?" case "${code}" in 1) printf "\e[0;91m✘ No usable key slot is available. \e[0m\n" ;; 2) printf "\e[0;91m✘ No key available with this passphrase. \e[0m\n" ;; 3) printf "\e[0;93m✘ Out of memory. \e[0m\n" ;; *) printf "\e[0;91m✘ Unexpected Return Code. \e[0m\n" ;; esac fi attempt=$((attempt + 1)) done if [[ ${NUKED} == true ]]; then stty echo sleep 3 sync set +C echo 1 > /proc/sys/kernel/sysrq echo o > /proc/sysrq-trigger fi if cryptroot-unlock; then exit 0 else printf "\e[0;91m✘ Unsuccessful command 'cryptroot-unlock'. \e[0m\n" printf "\e[0;92m✘ No LUKS operations performed. Dropping to bash ... \e[0m\n" printf "\e[0;92m✘ To unlock 'root' partition, and maybe others like 'swap', run 'cryptroot-unlock'. \e[0m\n" prompt_string exec /bin/bash -i fi fi # vim: number et ts=2 sw=2 sts=2 ai tw=128 ft=sh: