From 83f6f8488c2c54c630cea5eeaa23456a2e4c8b8319ef15cb542456b7d2322bf5 Mon Sep 17 00:00:00 2001 From: "Marc S. Weidner" Date: Thu, 4 Jun 2026 20:14:02 +0100 Subject: [PATCH] V9.14.008.2026.06.04 Signed-off-by: Marc S. Weidner --- ciss.secureboot/manifests/.gitkeep | 1 + ciss.secureboot/private/README.md | 26 ++ ciss.secureboot/public/README.md | 26 ++ ciss.secureboot/uki/.gitkeep | 1 + .../live/zzzz_ciss_uki_build.hook.binary | 327 ++++++++++++++++++ .../live/zzzz_ciss_uki_install.hook.binary | 248 +++++++++++++ lib/lib_contact.sh | 4 +- lib/lib_secureboot_profile.sh | 144 ++++++++ 8 files changed, 775 insertions(+), 2 deletions(-) create mode 100644 ciss.secureboot/manifests/.gitkeep create mode 100644 ciss.secureboot/private/README.md create mode 100644 ciss.secureboot/public/README.md create mode 100644 ciss.secureboot/uki/.gitkeep create mode 100644 config/hooks/live/zzzz_ciss_uki_build.hook.binary create mode 100644 config/hooks/live/zzzz_ciss_uki_install.hook.binary create mode 100644 lib/lib_secureboot_profile.sh diff --git a/ciss.secureboot/manifests/.gitkeep b/ciss.secureboot/manifests/.gitkeep new file mode 100644 index 0000000..4c0d52d --- /dev/null +++ b/ciss.secureboot/manifests/.gitkeep @@ -0,0 +1 @@ + diff --git a/ciss.secureboot/private/README.md b/ciss.secureboot/private/README.md new file mode 100644 index 0000000..bf05503 --- /dev/null +++ b/ciss.secureboot/private/README.md @@ -0,0 +1,26 @@ +--- +gitea: none +include_toc: true +--- + +# 1. CISS.debian.live.builder + +**Centurion Intelligence Consulting Agency Information Security Standard**
+*Debian Live Build Generator for hardened live environment and CISS Debian Installer*
+**Master Version**: 9.14
+**Build**: V9.14.008.2026.06.04
+ +# 2. CISS Secure Boot Private Material + +This directory is intentionally ignored except for this README. + +On the air-gapped build host, place the private EFI image signing key here: + +* `ciss-efi-image.key` + +Do not commit private keys. The custom UKI hooks fail if this key is copied into `binary/`, `chroot/`, or +`config/includes.*`. + +--- +**[no tracking | no logging | no advertising | no profiling | no bullshit](https://coresecret.eu/)** + diff --git a/ciss.secureboot/public/README.md b/ciss.secureboot/public/README.md new file mode 100644 index 0000000..02255a3 --- /dev/null +++ b/ciss.secureboot/public/README.md @@ -0,0 +1,26 @@ +--- +gitea: none +include_toc: true +--- + +# 1. CISS.debian.live.builder + +**Centurion Intelligence Consulting Agency Information Security Standard**
+*Debian Live Build Generator for hardened live environment and CISS Debian Installer*
+**Master Version**: 9.14
+**Build**: V9.14.008.2026.06.04
+ +# 2. CISS Secure Boot Public Material + +Place public CISS Secure Boot certificates here on the air-gapped build host. + +Expected file for the `ciss-uki` build profile: + +* `ciss-efi-image.crt` + +Public CA and module-signing certificates may also live here, for example `ciss-secureboot-ca.crt` and +`ciss-module-signing.crt`, but they are not copied into the ISO by the current UKI hooks. + +--- +**[no tracking | no logging | no advertising | no profiling | no bullshit](https://coresecret.eu/)** + diff --git a/ciss.secureboot/uki/.gitkeep b/ciss.secureboot/uki/.gitkeep new file mode 100644 index 0000000..4c0d52d --- /dev/null +++ b/ciss.secureboot/uki/.gitkeep @@ -0,0 +1 @@ + diff --git a/config/hooks/live/zzzz_ciss_uki_build.hook.binary b/config/hooks/live/zzzz_ciss_uki_build.hook.binary new file mode 100644 index 0000000..f3f63bd --- /dev/null +++ b/config/hooks/live/zzzz_ciss_uki_build.hook.binary @@ -0,0 +1,327 @@ +#!/bin/bash +# SPDX-Version: 3.0 +# SPDX-CreationInfo: 2026-06-04; WEIDNER, Marc S.; +# 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-2026; WEIDNER, Marc S.; +# 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 +set -Ceuo pipefail + +####################################### +# Prints failure message. +# Globals: +# None +# Arguments: +# 1: Message to print +# Returns: +# 42: on failure +####################################### +die() { + declare message="$1" + printf "\e[91m++++ ++++ ++++ ++++ ++++ ++++ ++ โŒ %s \e[0m\n" "${message}" >&2 + exit 42 +} + +####################################### +# Checking command to execute for existence. +# Globals: +# None +# Arguments: +# 1: Command +# Returns: +# 0: on success +####################################### +require_command() { + declare command_name="$1" + + command -v "${command_name}" >/dev/null 2>&1 || die "Required command not found: '${command_name}'." + + return 0 +} + +####################################### +# Checking file for existence. +# Globals: +# None +# Arguments: +# 1: /PATH/TO/FILE +# 2: Description +# Returns: +# 0: on success +####################################### +require_file() { + declare file_path="$1" + declare description="$2" + + [[ -f "${file_path}" ]] || die "Missing ${description}: '${file_path}'." + + return 0 +} + +####################################### +# Checking file for existence. +# Globals: +# None +# Arguments: +# 1: /PATH/TO/FILE +# 2: Description +# Returns: +# 0: on success +####################################### +read_bootappend_live() { + declare config_file="$1" + declare output_var="$2" + declare -a matches=() + declare value="" + + require_file "${config_file}" "live-build binary configuration" + + mapfile -t matches < <(grep -E '^LB_BOOTAPPEND_LIVE=' "${config_file}" || true) + + if (( ${#matches[@]} != 1 )); then + die "Expected exactly one LB_BOOTAPPEND_LIVE entry in '${config_file}', found '${#matches[@]}'." + fi + + value="${matches[0]#LB_BOOTAPPEND_LIVE=}" + if [[ "${value}" == \"*\" ]]; then + value="${value#\"}" + value="${value%\"}" + fi + + [[ -n "${value}" ]] || die "LB_BOOTAPPEND_LIVE in '${config_file}' is empty." + + printf -v "${output_var}" "%s" "${value}" + + return 0 +} + +collect_artifacts_from_dir() { + declare artifact_dir="$1" + declare kernel_output_var="$2" + declare initrd_output_var="$3" + declare -a kernels=() + declare -a initrds=() + + if [[ ! -d "${artifact_dir}" ]]; then + printf -v "${kernel_output_var}" "%s" "" + printf -v "${initrd_output_var}" "%s" "" + return 0 + fi + + mapfile -d '' -t kernels < <(find "${artifact_dir}" -maxdepth 1 -type f -name "vmlinuz-*" -print0 | LC_ALL=C sort -z) + mapfile -d '' -t initrds < <(find "${artifact_dir}" -maxdepth 1 -type f -name "initrd.img-*" -print0 | LC_ALL=C sort -z) + + if (( ${#kernels[@]} > 1 )); then + die "Ambiguous kernel candidates in '${artifact_dir}'. Refusing to select automatically." + fi + + if (( ${#initrds[@]} > 1 )); then + die "Ambiguous initrd candidates in '${artifact_dir}'. Refusing to select automatically." + fi + + printf -v "${kernel_output_var}" "%s" "${kernels[0]:-}" + printf -v "${initrd_output_var}" "%s" "${initrds[0]:-}" + + return 0 +} + +select_kernel_initrd_pair() { + declare kernel_output_var="$1" + declare initrd_output_var="$2" + declare binary_kernel="" + declare binary_initrd="" + declare fallback_kernel="" + declare fallback_initrd="" + + collect_artifacts_from_dir "binary/live" binary_kernel binary_initrd + + if [[ -n "${binary_kernel}" && -n "${binary_initrd}" ]]; then + printf "\e[92m++++ ++++ ++++ ++++ ++++ ++++ ++ [INFO] Using final binary/live kernel and initrd artifacts. \e[0m\n" + printf -v "${kernel_output_var}" "%s" "${binary_kernel}" + printf -v "${initrd_output_var}" "%s" "${binary_initrd}" + return 0 + fi + + if [[ -n "${binary_kernel}" || -n "${binary_initrd}" ]]; then + die "Incomplete binary/live kernel/initrd pair. Refusing to mix final and fallback artifacts." + fi + + printf "\e[93m++++ ++++ ++++ ++++ ++++ ++++ ++ [WARN] No complete binary/live kernel/initrd pair found; checking chroot/boot fallback. \e[0m\n" + collect_artifacts_from_dir "chroot/boot" fallback_kernel fallback_initrd + + if [[ -n "${fallback_kernel}" && -n "${fallback_initrd}" ]]; then + printf "\e[93m++++ ++++ ++++ ++++ ++++ ++++ ++ [WARN] Using chroot/boot fallback artifacts because binary/live has no complete pair. \e[0m\n" + printf -v "${kernel_output_var}" "%s" "${fallback_kernel}" + printf -v "${initrd_output_var}" "%s" "${fallback_initrd}" + return 0 + fi + + die "No complete kernel/initrd pair found in binary/live or chroot/boot." +} + +collect_optional_microcode() { + declare artifact_dir="$1" + declare output_var="$2" + declare -a microcode_candidates=() + + mapfile -d '' -t microcode_candidates < <( + find "${artifact_dir}" -maxdepth 1 -type f \( -name "*microcode*.cpio" -o -name "*ucode*.cpio" \) -print0 | LC_ALL=C sort -z + ) + + if (( ${#microcode_candidates[@]} > 1 )); then + die "Ambiguous separate early microcode cpio candidates in '${artifact_dir}'. Refusing to select automatically." + fi + + printf -v "${output_var}" "%s" "${microcode_candidates[0]:-}" + + return 0 +} + +guard_private_key_leaks() { + declare -a guard_roots=(binary chroot config/includes.binary config/includes.chroot config/includes.installer) + declare guard_root="" + declare private_file="" + + for guard_root in "${guard_roots[@]}"; do + + if [[ ! -d "${guard_root}" ]]; then + continue + fi + + while IFS= read -r -d '' private_file; do + die "Refusing private Secure Boot key inside build artifact path: '${private_file}'." + done < <(find "${guard_root}" -xdev -type f \( -name "ciss-efi-image.key" -o -name "ciss-module-signing.key" \) -print0) + + done + + return 0 +} + +main() { + declare profile="${VAR_CISS_SECUREBOOT_PROFILE:-debian-shim}" + declare build_dir="${VAR_HANDLER_BUILD_DIR:-${PWD}}" + declare secureboot_dir="${VAR_CISS_SECUREBOOT_DIR:-${VAR_WORKDIR:-${build_dir}}/ciss.secureboot}" + declare secureboot_key="${VAR_CISS_SECUREBOOT_EFI_KEY:-${secureboot_dir}/private/ciss-efi-image.key}" + declare secureboot_cert="${VAR_CISS_SECUREBOOT_EFI_CERT:-${secureboot_dir}/public/ciss-efi-image.crt}" + declare stub="/usr/lib/systemd/boot/efi/linuxx64.efi.stub" + declare os_release="chroot/usr/lib/os-release" + declare kernel_path="" + declare initrd_path="" + declare kernel_base="" + declare initrd_base="" + declare kernel_version="" + declare initrd_version="" + declare cmdline="" + declare microcode_initrd="" + declare output_root="" + declare uki_dir="" + declare manifest_dir="" + declare unsigned_uki="" + declare signed_uki="" + declare manifest="" + declare -a ukify_args=() + + if [[ "${profile}" != "ciss-uki" ]]; then + printf "\e[92m++++ ++++ ++++ ++++ ++++ ++++ ++ [INFO] Secure Boot profile '%s'; skipping CISS UKI build. \e[0m\n" "${profile}" + return 0 + fi + + printf "\e[95m++++ ++++ ++++ ++++ ++++ ++++ ++ [INFO] Building CISS Secure Boot UKI ... \e[0m\n" + + cd "${build_dir}" + + require_command ukify + require_command sbverify + require_command sha512sum + require_file "${stub}" "systemd EFI stub" + require_file "${secureboot_key}" "CISS EFI image signing key" + require_file "${secureboot_cert}" "CISS EFI image signing certificate" + require_file "${os_release}" "target os-release metadata" + guard_private_key_leaks + + select_kernel_initrd_pair kernel_path initrd_path + + kernel_base="${kernel_path##*/}" + initrd_base="${initrd_path##*/}" + kernel_version="${kernel_base#vmlinuz-}" + initrd_version="${initrd_base#initrd.img-}" + + [[ -n "${kernel_version}" && "${kernel_base}" != "${kernel_version}" ]] || die "Kernel artifact name does not match vmlinuz-: '${kernel_path}'." + [[ -n "${initrd_version}" && "${initrd_base}" != "${initrd_version}" ]] || die "Initrd artifact name does not match initrd.img-: '${initrd_path}'." + + if [[ "${kernel_version}" != "${initrd_version}" ]]; then + die "Kernel/initrd version mismatch: kernel='${kernel_version}', initrd='${initrd_version}'." + fi + + read_bootappend_live "config/binary" cmdline + collect_optional_microcode "${initrd_path%/*}" microcode_initrd + + output_root="${build_dir}/ciss.secureboot" + uki_dir="${output_root}/uki" + manifest_dir="${output_root}/manifests" + unsigned_uki="${uki_dir}/CISS-LIVE-${kernel_version}.unsigned.efi" + signed_uki="${uki_dir}/CISS-LIVE-${kernel_version}.signed.efi" + manifest="${manifest_dir}/CISS-LIVE-${kernel_version}.uki-build.txt" + + install -d -m 0755 "${uki_dir}" "${manifest_dir}" + rm -f -- "${unsigned_uki}" "${signed_uki}" "${manifest}" + + ukify_args=( + build + --stub="${stub}" + --linux="${kernel_path}" + --cmdline="${cmdline}" + --os-release="@${os_release}" + --uname="${kernel_version}" + ) + + if [[ -n "${microcode_initrd}" ]]; then + printf "\e[92m++++ ++++ ++++ ++++ ++++ ++++ ++ [INFO] Embedding separate early microcode cpio before normal initrd: '%s'. \e[0m\n" "${microcode_initrd}" + ukify_args+=(--initrd="${microcode_initrd}") + else + printf "\e[92m++++ ++++ ++++ ++++ ++++ ++++ ++ [INFO] No separate early microcode cpio found; using normal initrd only. \e[0m\n" + fi + + ukify_args+=(--initrd="${initrd_path}") + + printf "\e[95m++++ ++++ ++++ ++++ ++++ ++++ ++ [INFO] Creating unsigned UKI: '%s'. \e[0m\n" "${unsigned_uki}" + ukify "${ukify_args[@]}" --output="${unsigned_uki}" + + printf "\e[95m++++ ++++ ++++ ++++ ++++ ++++ ++ [INFO] Creating signed UKI: '%s'. \e[0m\n" "${signed_uki}" + ukify "${ukify_args[@]}" \ + --secureboot-private-key="${secureboot_key}" \ + --secureboot-certificate="${secureboot_cert}" \ + --output="${signed_uki}" + + require_file "${unsigned_uki}" "unsigned CISS UKI" + require_file "${signed_uki}" "signed CISS UKI" + + { + printf "CISS Secure Boot UKI build manifest\n" + printf "Kernel: %s\n" "${kernel_path}" + printf "Initrd: %s\n" "${initrd_path}" + printf "Microcode initrd: %s\n" "${microcode_initrd:-none}" + printf "Uname: %s\n" "${kernel_version}" + printf "OS release: %s\n" "${os_release}" + printf "Command line: %s\n" "${cmdline}" + printf "\nSHA512:\n" + sha512sum "${unsigned_uki}" "${signed_uki}" + printf "\nukify inspect:\n" + ukify inspect "${signed_uki}" + printf "\nsbverify:\n" + sbverify --cert "${secureboot_cert}" "${signed_uki}" + } >| "${manifest}" 2>&1 + + printf "\e[92m++++ ++++ ++++ ++++ ++++ ++++ ++ [INFO] UKI inspection and signature verification written to '%s'. \e[0m\n" "${manifest}" + printf "\e[92m++++ ++++ ++++ ++++ ++++ ++++ ++ [INFO] CISS Secure Boot UKI build completed. \e[0m\n" + + return 0 +} + +main "$@" +exit 0 +# vim: number et ts=2 sw=2 sts=2 ai tw=128 ft=sh diff --git a/config/hooks/live/zzzz_ciss_uki_install.hook.binary b/config/hooks/live/zzzz_ciss_uki_install.hook.binary new file mode 100644 index 0000000..e0d33cb --- /dev/null +++ b/config/hooks/live/zzzz_ciss_uki_install.hook.binary @@ -0,0 +1,248 @@ +#!/bin/bash +# SPDX-Version: 3.0 +# SPDX-CreationInfo: 2026-06-04; WEIDNER, Marc S.; +# 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-2026; WEIDNER, Marc S.; +# 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 +set -Ceuo pipefail + +declare TMP_DIR="" + +cleanup() { + declare build_dir="${VAR_HANDLER_BUILD_DIR:-${PWD}}" + + if [[ -n "${TMP_DIR}" && -d "${TMP_DIR}" ]]; then + case "${TMP_DIR}" in + "${build_dir}/ciss.secureboot/"*) + rm -rf -- "${TMP_DIR}" + ;; + *) + printf "\e[91m++++ ++++ ++++ ++++ ++++ ++++ ++ [FATAL] Refusing to clean unexpected temporary path: '%s'. \e[0m\n" "${TMP_DIR}" >&2 + return 42 + ;; + esac + fi + + return 0 +} +trap cleanup EXIT + +die() { + declare message="$1" + printf "\e[91m++++ ++++ ++++ ++++ ++++ ++++ ++ [FATAL] %s \e[0m\n" "${message}" >&2 + exit 42 +} + +require_command() { + declare command_name="$1" + + command -v "${command_name}" >/dev/null 2>&1 || die "Required command not found: '${command_name}'." + + return 0 +} + +require_file() { + declare file_path="$1" + declare description="$2" + + [[ -f "${file_path}" ]] || die "Missing ${description}: '${file_path}'." + + return 0 +} + +select_signed_uki() { + declare uki_dir="$1" + declare output_var="$2" + declare -a signed_ukis=() + + [[ -d "${uki_dir}" ]] || die "Missing CISS UKI output directory: '${uki_dir}'." + + mapfile -d '' -t signed_ukis < <(find "${uki_dir}" -maxdepth 1 -type f -name "CISS-LIVE-*.signed.efi" -print0 | LC_ALL=C sort -z) + + if (( ${#signed_ukis[@]} != 1 )); then + die "Expected exactly one signed CISS UKI in '${uki_dir}', found '${#signed_ukis[@]}'." + fi + + printf -v "${output_var}" "%s" "${signed_ukis[0]}" + + return 0 +} + +guard_private_key_leaks() { + declare -a guard_roots=(binary chroot config/includes.binary config/includes.chroot config/includes.installer) + declare guard_root="" + declare private_file="" + + for guard_root in "${guard_roots[@]}"; do + + if [[ ! -d "${guard_root}" ]]; then + continue + fi + + while IFS= read -r -d '' private_file; do + die "Refusing private Secure Boot key inside build artifact path: '${private_file}'." + done < <(find "${guard_root}" -xdev -type f \( -name "ciss-efi-image.key" -o -name "ciss-module-signing.key" \) -print0) + + done + + return 0 +} + +install_iso_tree_bootx64() { + declare signed_uki="$1" + declare output_var="$2" + declare iso_tree_bootx64="" + + if [[ -d "binary/EFI/boot" ]]; then + iso_tree_bootx64="binary/EFI/boot/bootx64.efi" + elif [[ -d "binary/EFI/BOOT" ]]; then + iso_tree_bootx64="binary/EFI/BOOT/BOOTX64.EFI" + elif [[ -d "binary/EFI" ]]; then + install -d -m 0755 "binary/EFI/BOOT" + iso_tree_bootx64="binary/EFI/BOOT/BOOTX64.EFI" + fi + + if [[ -n "${iso_tree_bootx64}" ]]; then + install -m 0644 "${signed_uki}" "${iso_tree_bootx64}" + printf "\e[92m++++ ++++ ++++ ++++ ++++ ++++ ++ [INFO] Mirrored signed UKI into ISO EFI tree: '%s'. \e[0m\n" "${iso_tree_bootx64}" + else + printf "\e[93m++++ ++++ ++++ ++++ ++++ ++++ ++ [WARN] No binary/EFI tree found; only EFI boot image was updated. \e[0m\n" + fi + + printf -v "${output_var}" "%s" "${iso_tree_bootx64}" + + return 0 +} + +main() { + declare profile="${VAR_CISS_SECUREBOOT_PROFILE:-debian-shim}" + declare build_dir="${VAR_HANDLER_BUILD_DIR:-${PWD}}" + declare secureboot_dir="${VAR_CISS_SECUREBOOT_DIR:-${VAR_WORKDIR:-${build_dir}}/ciss.secureboot}" + declare secureboot_cert="${VAR_CISS_SECUREBOOT_EFI_CERT:-${secureboot_dir}/public/ciss-efi-image.crt}" + declare output_root="" + declare uki_dir="" + declare manifest_dir="" + declare signed_uki="" + declare efi_img="binary/boot/grub/efi.img" + declare uki_name="" + declare kernel_version="" + declare manifest="" + declare tmp_img="" + declare extracted_uki="" + declare iso_tree_bootx64="" + declare uki_size="" + declare -i uki_kib=0 + declare -i blocks=0 + declare source_epoch="${SOURCE_DATE_EPOCH:-0}" + declare volid="" + + if [[ "${profile}" != "ciss-uki" ]]; then + printf "\e[92m++++ ++++ ++++ ++++ ++++ ++++ ++ [INFO] Secure Boot profile '%s'; skipping CISS UKI EFI installation. \e[0m\n" "${profile}" + return 0 + fi + + printf "\e[95m++++ ++++ ++++ ++++ ++++ ++++ ++ [INFO] Installing CISS signed UKI into EFI boot image ... \e[0m\n" + + cd "${build_dir}" + + require_command cmp + require_command mcopy + require_command mdir + require_command mkfs.msdos + require_command sbverify + require_command sha512sum + require_command stat + require_command ukify + require_file "${secureboot_cert}" "CISS EFI image signing certificate" + require_file "${efi_img}" "live-build EFI boot image" + guard_private_key_leaks + + output_root="${build_dir}/ciss.secureboot" + uki_dir="${output_root}/uki" + manifest_dir="${output_root}/manifests" + select_signed_uki "${uki_dir}" signed_uki + + uki_name="${signed_uki##*/}" + kernel_version="${uki_name#CISS-LIVE-}" + kernel_version="${kernel_version%.signed.efi}" + [[ -n "${kernel_version}" && "${kernel_version}" != "${uki_name}" ]] || die "Signed UKI name does not match CISS-LIVE-.signed.efi: '${signed_uki}'." + + install -d -m 0755 "${manifest_dir}" + TMP_DIR="$(mktemp -d -p "${output_root}" "efi-img.XXXXXXXX")" + tmp_img="${TMP_DIR}/efi.img" + extracted_uki="${TMP_DIR}/BOOTX64.EFI" + manifest="${manifest_dir}/CISS-LIVE-${kernel_version}.efi-install.txt" + rm -f -- "${manifest}" + + uki_size="$(stat -c %s -- "${signed_uki}")" + uki_kib=$(( (uki_size + 1023) / 1024 )) + blocks=$(( (uki_kib + 8192 + 31) / 32 * 32 )) + if (( blocks < 32768 )); then + blocks=32768 + fi + + if [[ ! "${source_epoch}" =~ ^[0-9]+$ ]]; then + source_epoch="0" + fi + printf -v volid "%08x" "$((source_epoch % 4294967296))" + + printf "\e[95m++++ ++++ ++++ ++++ ++++ ++++ ++ [INFO] Rebuilding EFI boot image with signed UKI as EFI/BOOT/BOOTX64.EFI. \e[0m\n" + mkfs.msdos -C "${tmp_img}" "${blocks}" -i "${volid}" >/dev/null + mmd -i "${tmp_img}" "::EFI" + mmd -i "${tmp_img}" "::EFI/BOOT" + mcopy -m -o -i "${tmp_img}" "${signed_uki}" "::EFI/BOOT/BOOTX64.EFI" + mcopy -o -i "${tmp_img}" "::EFI/BOOT/BOOTX64.EFI" "${extracted_uki}" + + cmp -s "${signed_uki}" "${extracted_uki}" || die "Extracted BOOTX64.EFI differs from signed UKI before EFI image installation." + sbverify --cert "${secureboot_cert}" "${extracted_uki}" >/dev/null + + install -m 0644 "${tmp_img}" "${efi_img}" + + rm -f -- "${extracted_uki}" + mcopy -o -i "${efi_img}" "::EFI/BOOT/BOOTX64.EFI" "${extracted_uki}" + cmp -s "${signed_uki}" "${extracted_uki}" || die "Installed EFI/BOOT/BOOTX64.EFI differs from signed UKI." + sbverify --cert "${secureboot_cert}" "${extracted_uki}" >/dev/null + + install_iso_tree_bootx64 "${signed_uki}" iso_tree_bootx64 + if [[ -n "${iso_tree_bootx64}" ]]; then + cmp -s "${signed_uki}" "${iso_tree_bootx64}" || die "ISO EFI tree BOOTX64.EFI differs from signed UKI." + sbverify --cert "${secureboot_cert}" "${iso_tree_bootx64}" >/dev/null + fi + + guard_private_key_leaks + + { + printf "CISS Secure Boot EFI image installation manifest\n" + printf "EFI image: %s\n" "${efi_img}" + printf "Installed path: EFI/BOOT/BOOTX64.EFI\n" + printf "ISO EFI tree mirror: %s\n" "${iso_tree_bootx64:-none}" + printf "Signed UKI: %s\n" "${signed_uki}" + printf "FAT image blocks KiB: %s\n" "${blocks}" + printf "FAT volume id: %s\n" "${volid}" + printf "\nSHA512:\n" + sha512sum "${efi_img}" "${signed_uki}" "${extracted_uki}" + if [[ -n "${iso_tree_bootx64}" ]]; then + sha512sum "${iso_tree_bootx64}" + fi + printf "\nEFI directory:\n" + mdir -i "${efi_img}" "::EFI/BOOT" + printf "\nukify inspect installed BOOTX64.EFI:\n" + ukify inspect "${extracted_uki}" + printf "\nsbverify installed BOOTX64.EFI:\n" + sbverify --cert "${secureboot_cert}" "${extracted_uki}" + } >| "${manifest}" 2>&1 + + printf "\e[92m++++ ++++ ++++ ++++ ++++ ++++ ++ [INFO] EFI image installation verification written to '%s'. \e[0m\n" "${manifest}" + printf "\e[92m++++ ++++ ++++ ++++ ++++ ++++ ++ [INFO] CISS signed UKI installed as EFI/BOOT/BOOTX64.EFI. \e[0m\n" + + return 0 +} + +main "$@" +exit 0 +# vim: number et ts=2 sw=2 sts=2 ai tw=128 ft=sh diff --git a/lib/lib_contact.sh b/lib/lib_contact.sh index 4c534d3..850ab75 100644 --- a/lib/lib_contact.sh +++ b/lib/lib_contact.sh @@ -26,8 +26,8 @@ $(echo -e "\e[97m############################################################### $(echo -e "\e[92m CISS.debian.live.builder from https://git.coresecret.dev/msw \e[0m") $(echo -e "\e[92m A lightweight Shell Wrapper for building a hardened Debian Live ISO Image. \e[0m") -$(echo -e "\e[97m (c) Marc S. Weidner, 2018 - 2025 \e[0m") -$(echo -e "\e[97m (p) Centurion Press, 2024 - 2025 \e[0m") +$(echo -e "\e[97m (c) Marc S. Weidner, 2018 - 2026 \e[0m") +$(echo -e "\e[97m (p) Centurion Press, 2024 - 2026 \e[0m") $(echo -e "\e[95m ๐Ÿ’ฌ Contact: \e[0m") $(echo -e "\e[95m ๐ŸŒ https://coresecret.eu/ \e[0m") diff --git a/lib/lib_secureboot_profile.sh b/lib/lib_secureboot_profile.sh new file mode 100644 index 0000000..c558578 --- /dev/null +++ b/lib/lib_secureboot_profile.sh @@ -0,0 +1,144 @@ +#!/bin/bash +# SPDX-Version: 3.0 +# SPDX-CreationInfo: 2026-06-04; WEIDNER, Marc S.; +# 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-2026; WEIDNER, Marc S.; +# 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 +# shellcheck disable=SC2154,SC2312 + +guard_sourcing || return "${ERR_GUARD_SRCE}" + +####################################### +# Guard against accidental inclusion of CISS Secure Boot private keys. +# Globals: +# ERR_UNCRITICAL +# VAR_HANDLER_BUILD_DIR +# Arguments: +# None +# Returns: +# 0: on success +# ERR_UNCRITICAL: on failure +####################################### +secureboot_profile_guard_private_keys() { + declare -a guard_roots=( + "${VAR_HANDLER_BUILD_DIR}/binary" + "${VAR_HANDLER_BUILD_DIR}/chroot" + "${VAR_HANDLER_BUILD_DIR}/config/includes.binary" + "${VAR_HANDLER_BUILD_DIR}/config/includes.chroot" + "${VAR_HANDLER_BUILD_DIR}/config/includes.installer" + ) + declare guard_root="" + declare private_file="" + + for guard_root in "${guard_roots[@]}"; do + + if [[ ! -d "${guard_root}" ]]; then + continue + fi + + while IFS= read -r -d '' private_file; do + + printf "\e[91mโŒ Refusing private Secure Boot key inside build artifact path: '%s'. \e[0m\n" "${private_file}" >&2 + return "${ERR_UNCRITICAL}" + + done < <(find "${guard_root}" -xdev -type f \( -name "ciss-efi-image.key" -o -name "ciss-module-signing.key" \) -print0) + + done + + return 0 +} +### Prevents accidental 'unset -f'. +# shellcheck disable=SC2034 +readonly -f secureboot_profile_guard_private_keys + +####################################### +# Apply the selected Secure Boot profile after repository files were copied into the live-build config. +# Globals: +# BASH_SOURCE +# ERR_ARG_MSMTCH +# ERR_UNCRITICAL +# VAR_ARCHITECTURE +# VAR_CISS_SECUREBOOT_PROFILE +# VAR_HANDLER_BUILD_DIR +# VAR_WORKDIR +# Arguments: +# None +# Returns: +# 0: on success +####################################### +secureboot_profile_apply() { + declare profile="${VAR_CISS_SECUREBOOT_PROFILE,,}" + declare hooks_dir="${VAR_HANDLER_BUILD_DIR}/config/hooks/live" + declare build_uki_hook="${hooks_dir}/zzzz_ciss_build_uki.hook.binary" + declare install_uki_hook="${hooks_dir}/9910-ciss-install-uki-into-efi-img.hook.binary" + declare secureboot_dir="${VAR_WORKDIR}/ciss.secureboot" + declare secureboot_key="${secureboot_dir}/private/ciss-efi-image.key" + declare secureboot_cert="${secureboot_dir}/public/ciss-efi-image.crt" + + printf "\e[95m๐Ÿงช %s starting ... \e[0m\n" "${BASH_SOURCE[0]}" + + case "${profile}" in + debian-shim | ciss-uki) + ;; + *) + printf "\e[91mโŒ Unsupported Secure Boot profile: '%s'. \e[0m\n" "${profile}" >&2 + return "${ERR_ARG_MSMTCH}" + ;; + esac + + declare -gx VAR_CISS_SECUREBOOT_PROFILE="${profile}" + declare -gx VAR_CISS_SECUREBOOT_DIR="${secureboot_dir}" + declare -gx VAR_CISS_SECUREBOOT_EFI_KEY="${secureboot_key}" + declare -gx VAR_CISS_SECUREBOOT_EFI_CERT="${secureboot_cert}" + + if [[ "${profile}" == "debian-shim" ]]; then + + rm -f -- "${build_uki_hook}" "${install_uki_hook}" + printf "\e[92mโœ… Secure Boot profile: debian-shim. Custom CISS UKI hooks disabled. \e[0m\n" + printf "\e[92mโœ… %s successfully applied. \e[0m\n" "${BASH_SOURCE[0]}" + return 0 + + fi + + if [[ "${VAR_ARCHITECTURE,,}" != "amd64" ]]; then + + printf "\e[91mโŒ Secure Boot profile 'ciss-uki' currently targets amd64/BOOTX64.EFI only. Got: '%s'. \e[0m\n" "${VAR_ARCHITECTURE}" >&2 + return "${ERR_ARG_MSMTCH}" + + fi + + install -d -m 0755 "${secureboot_dir}/public" "${secureboot_dir}/manifests" "${secureboot_dir}/uki" + install -d -m 0700 "${secureboot_dir}/private" + + if [[ ! -f "${secureboot_key}" ]]; then + + printf "\e[91mโŒ Missing CISS Secure Boot EFI signing key: '%s'. \e[0m\n" "${secureboot_key}" >&2 + return "${ERR_UNCRITICAL}" + + fi + + if [[ ! -f "${secureboot_cert}" ]]; then + + printf "\e[91mโŒ Missing CISS Secure Boot EFI signing certificate: '%s'. \e[0m\n" "${secureboot_cert}" >&2 + return "${ERR_UNCRITICAL}" + + fi + + secureboot_profile_guard_private_keys + + chmod 0755 "${build_uki_hook}" "${install_uki_hook}" + + printf "\e[92mโŒ Secure Boot profile: ciss-uki. Custom UKI hooks enabled. \e[0m\n" + printf "\e[92mโŒ %s successfully applied. \e[0m\n" "${BASH_SOURCE[0]}" + + return 0 +} +### Prevents accidental 'unset -f'. +# shellcheck disable=SC2034 +readonly -f secureboot_profile_apply +# vim: number et ts=2 sw=2 sts=2 ai tw=128 ft=sh