From eeecefc96690c4a759fa0fa66664df8c911c52fca523b081ca3a6ee6b8a312aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20H=2E=20Zimnol?= Date: Wed, 10 Jun 2026 23:13:36 +0200 Subject: [PATCH] V9.14.022.2026.06.10: Add boot attestation negative tests --- tests/test_boot_attestation.sh | 228 +++++++++++++++++++++++++++++++++ 1 file changed, 228 insertions(+) create mode 100755 tests/test_boot_attestation.sh diff --git a/tests/test_boot_attestation.sh b/tests/test_boot_attestation.sh new file mode 100755 index 0000000..51b7293 --- /dev/null +++ b/tests/test_boot_attestation.sh @@ -0,0 +1,228 @@ +#!/bin/bash +# SPDX-Version: 3.0 +# SPDX-FileCopyrightText: 2026; WEIDNER, Marc S.; +# SPDX-FileType: SOURCE +# SPDX-License-Identifier: LicenseRef-CNCL-1.1 OR LicenseRef-CCLA-1.1 +# SPDX-PackageName: CISS.debian.live.builder + +set -Ceuo pipefail + +declare ROOT_DIR="" +declare SHA512SUM="" +declare SHASUM="" +declare TMP_BASE="" +declare TMP_ROOT="" + +ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +SHA512SUM="$(command -v sha512sum)" +SHASUM="$(command -v shasum || true)" +TMP_BASE="${TMPDIR:-/tmp}" +TMP_BASE="${TMP_BASE%/}" +TMP_ROOT="$(mktemp -d "${TMP_BASE}/ciss-boot-attestation.XXXXXXXX")" + +cleanup() { + case "${TMP_ROOT}" in + "${TMP_BASE}/ciss-boot-attestation."*) + rm -rf -- "${TMP_ROOT}" + ;; + *) + printf 'Refusing to clean unexpected test path: %s\n' "${TMP_ROOT}" >&2 + return 1 + ;; + esac +} +trap cleanup EXIT + +fail() { + printf 'FAIL: %s\n' "${1}" >&2 + exit 1 +} + +prepare_checksum_hook() { + declare source_hook="${1}" + declare target_hook="${2}" + declare fault_mode="${3}" + declare test_bin="${4}" + declare test_tty="${5}" + declare test_runtime="" + + test_runtime="$(dirname "${test_tty}")" + sed \ + -e "s|/usr/bin/|${test_bin}/|g" \ + -e "s|/run/ciss-|${test_runtime}/ciss-|g" \ + -e "s|_TTY=\"/dev/tty8\"|_TTY=\"${test_tty}\"|" \ + "${source_hook}" > "${target_hook}" + + if [[ "${fault_mode}" == "unknown-state" ]]; then + sed \ + -e 's/_RETURN_PGP="na"/_RETURN_PGP="unknown"/' \ + -e 's/_RETURN_SHA="${?}"/_RETURN_SHA="unknown"/g' \ + "${target_hook}" > "${target_hook}.new" + mv "${target_hook}.new" "${target_hook}" + fi +} + +install_test_sha512sum() { + declare test_bin="${1}" + + if printf '' | "${SHA512SUM}" -c >/dev/null 2>&1; then + ln -s "${SHA512SUM}" "${test_bin}/sha512sum" + elif [[ -n "${SHASUM}" ]]; then + printf '#!/bin/sh\nexec "%s" -a 512 "$@"\n' "${SHASUM}" > "${test_bin}/sha512sum" + chmod 0755 "${test_bin}/sha512sum" + else + fail "No SHA-512 tool with checksum verification support is available." + fi +} + +run_checksum_case() { + declare source_hook="${1}" + declare case_name="${2}" + declare expected_status="${3}" + declare expected_message="${4}" + declare case_dir="" + declare hook_copy="" + declare output_file="" + declare test_bin="" + declare test_tty="" + declare source_id="" + declare fault_mode="" + declare panic_returns="false" + declare status=0 + + source_id="$(printf '%s' "${source_hook#"${ROOT_DIR}"/}" | tr '/' '_')" + case_dir="${TMP_ROOT}/${source_id}-${case_name}" + hook_copy="${case_dir}/0030-ciss-verify-checksums" + output_file="${case_dir}/output.log" + test_bin="${case_dir}/bin" + test_tty="${case_dir}/tty.log" + + mkdir -p "${case_dir}" "${test_bin}" + : >| "${test_tty}" + + case "${case_name}" in + valid) + install_test_sha512sum "${test_bin}" + printf 'trusted rootfs payload\n' > "${case_dir}/payload" + (cd "${case_dir}" && "${test_bin}/sha512sum" payload > sha512sum.txt) + ;; + missing-manifest) + install_test_sha512sum "${test_bin}" + ;; + unsupported-manifest) + install_test_sha512sum "${test_bin}" + printf 'unsupported\n' > "${case_dir}/md5sum.txt" + ;; + failed-checksum) + install_test_sha512sum "${test_bin}" + printf 'trusted rootfs payload\n' > "${case_dir}/payload" + (cd "${case_dir}" && "${test_bin}/sha512sum" payload > sha512sum.txt) + printf 'tampered rootfs payload\n' >| "${case_dir}/payload" + ;; + missing-tool) + install_test_sha512sum "${test_bin}" + printf 'trusted rootfs payload\n' > "${case_dir}/payload" + (cd "${case_dir}" && "${test_bin}/sha512sum" payload > sha512sum.txt) + rm -f "${test_bin}/sha512sum" + ;; + unknown-state) + fault_mode="unknown-state" + panic_returns="true" + install_test_sha512sum "${test_bin}" + printf 'trusted rootfs payload\n' > "${case_dir}/payload" + (cd "${case_dir}" && "${test_bin}/sha512sum" payload > sha512sum.txt) + ;; + *) + fail "Unknown checksum test case: ${case_name}" + ;; + esac + + prepare_checksum_hook "${source_hook}" "${hook_copy}" "${fault_mode}" "${test_bin}" "${test_tty}" + + set +e + # shellcheck disable=SC2034,SC2329 + ( + set +C + CDLB_SCRIPT_FULL="${hook_copy}" + LIVE_BOOT_CMDLINE="verify-checksums=sha512" + LIVE_VERIFY_CHECKSUMS="" + + log_begin_msg() { :; } + log_end_msg() { :; } + log_success_msg() { :; } + panic() { + printf 'PANIC: %s\n' "${1}" >&2 + if [[ "${panic_returns}" == "true" ]]; then + return 0 + fi + exit 97 + } + sleep() { :; } + + # shellcheck source=/dev/null + . "${hook_copy}" + Verify_checksums "${case_dir}" + ) > "${output_file}" 2>&1 + status="${?}" + set -e + + [[ "${status}" -eq "${expected_status}" ]] || { + cat "${output_file}" >&2 + fail "${source_hook} ${case_name}: expected status ${expected_status}, got ${status}" + } + + grep -Fq "${expected_message}" "${output_file}" || { + cat "${output_file}" >&2 + fail "${source_hook} ${case_name}: missing expected message '${expected_message}'" + } +} + +test_rootfs_payload_tamper() { + declare case_dir="${TMP_ROOT}/rootfs-payload-tamper" + declare test_bin="${case_dir}/bin" + declare payload="${case_dir}/crypt_liveiso" + declare manifest="${case_dir}/ciss_rootfs.crypt.decrypted.sha512sum.txt" + declare build_hook="${ROOT_DIR}/config/hooks/live/zzzz_ciss_crypt_squash.hook.binary" + declare boot_hook="${ROOT_DIR}/config/includes.chroot/usr/lib/live/boot/0042_ciss_post_decrypt_attest" + + mkdir -p "${case_dir}" "${test_bin}" + install_test_sha512sum "${test_bin}" + + printf 'trusted selected rootfs mapper payload\n' > "${payload}" + "${test_bin}/sha512sum" "${payload}" > "${manifest}" + "${test_bin}/sha512sum" -c --strict --quiet "${manifest}" + + printf 'tampered selected rootfs mapper payload\n' >> "${payload}" + if "${test_bin}/sha512sum" -c --strict --quiet "${manifest}" >/dev/null 2>&1; then + fail "Modified selected rootfs payload unexpectedly passed checksum verification." + fi + + # shellcheck disable=SC2016 + grep -Fq 'sha512sum "${MAPPER_DEV}" >| "${ROOTFS_ATTESTATION}"' "${build_hook}" || \ + fail "Build hook does not generate the attestation from the decrypted mapper." + # shellcheck disable=SC2016 + grep -Fq '/usr/bin/sha512sum -c --strict --quiet "${HASH_FILE}"' "${boot_hook}" || \ + fail "Boot hook does not verify the selected rootfs payload with sha512sum -c." + # shellcheck disable=SC2016 + grep -Fq '[ "${_ATTESTED_PAYLOAD}" != "${CDLB_MAPPER_DEV}" ]' "${boot_hook}" || \ + fail "Boot hook does not require the manifest to target the selected rootfs payload." +} + +declare -a CHECKSUM_HOOKS=( + "${ROOT_DIR}/config/includes.chroot/usr/lib/live/boot/0030-ciss-verify-checksums" + "${ROOT_DIR}/scripts/usr/lib/live/boot/0030-ciss-verify-checksums" +) +declare checksum_hook="" + +for checksum_hook in "${CHECKSUM_HOOKS[@]}"; do + run_checksum_case "${checksum_hook}" "valid" 0 "Verification of [sha checksum] file successful" + run_checksum_case "${checksum_hook}" "missing-manifest" 97 "No supported checksum manifest found." + run_checksum_case "${checksum_hook}" "unsupported-manifest" 97 "No supported checksum manifest found." + run_checksum_case "${checksum_hook}" "failed-checksum" 97 "No supported checksum manifest was verified successfully." + run_checksum_case "${checksum_hook}" "missing-tool" 97 "No supported checksum verification tool was available." + run_checksum_case "${checksum_hook}" "unknown-state" 1 "Checksum verification ended in an unsupported state." +done + +test_rootfs_payload_tamper + +printf 'PASS: checksum verification fails closed and modified rootfs payloads fail attestation.\n'