diff --git a/evidence/gate1_zero_disk_recovery_receipt.json b/evidence/gate1_zero_disk_recovery_receipt.json index f1d24b13..4f5f628f 100644 --- a/evidence/gate1_zero_disk_recovery_receipt.json +++ b/evidence/gate1_zero_disk_recovery_receipt.json @@ -3,7 +3,7 @@ "name": "standalone_ram_recovery_media", "status": "PASS_EMULATOR_VERIFIED", "physical_hardware_status": "PENDING_ATTENDED_EXERCISE", - "captured_utc": "2026-09-24T04:18:09Z", + "captured_utc": "2026-09-24T04:29:54Z", "host": "spark-b87b", "arch": "aarch64", "kernel_version": "7.0.0-1019-nvidia", @@ -24,7 +24,8 @@ "internal_nvme_unmounted": true, "init_entrypoint": "rdinit=/init", "atlas_recov_protected": true, - "qemu_zero_disk_verified": true + "qemu_zero_disk_verified": true, + "secrets_policy": "NO PLAINTEXT SECRETS IN REPOSITORY OR BUILD ARTIFACTS" }, "utilities_packaged": [ "/bin/busybox", @@ -34,9 +35,15 @@ "/bin/blkid", "/bin/mount", "/bin/umount", - "/sbin/cryptsetup", - "/usr/bin/age", - "/usr/bin/tpm2_pcrread", - "/usr/bin/efibootmgr" + "/bin/mkdir", + "/bin/cat", + "/bin/grep", + "/bin/sed", + "/bin/sha256sum", + "/bin/cryptsetup", + "/bin/age", + "/bin/tpm2_pcrread", + "/bin/efibootmgr", + "/bin/gocryptfs" ] } diff --git a/scripts/build_recovery_media.sh b/scripts/build_recovery_media.sh index 7299e70a..4439191e 100755 --- a/scripts/build_recovery_media.sh +++ b/scripts/build_recovery_media.sh @@ -2,8 +2,8 @@ # build_recovery_media.sh: Build standalone UEFI recovery system on target USB drive # Certified for Secure Boot execution using signed Shim + GRUB / standalone RAM recovery payload. # Invariant: 100% self-contained RAM disk under rdinit=/init. Internal NVMe must not be mounted. -# Guard: Explicitly rejects /dev/sda and any partition labeled ATLAS_RECOV to protect offline secrets. -# Zero Disk Secrets and Unslop compliant. +# Guard: Multi-factor validation rejects ATLAS_RECOV (label, UUID, PARTUUID) and host internal NVMe. +# Invariant: NO PLAINTEXT SECRETS IN REPOSITORY OR BUILD ARTIFACTS. Unslop compliant. set -euo pipefail @@ -20,11 +20,41 @@ if [[ ! -b "${TARGET_DEV}" ]]; then exit 1 fi -# Safety guard: Prevent accidental overwrite of ATLAS_RECOV or host disks +# Multi-factor identity inspection TARGET_LABEL=$(lsblk -no LABEL "${TARGET_DEV}" 2>/dev/null || blkid -s LABEL -o value "${TARGET_DEV}" 2>/dev/null || true) -if [[ "${TARGET_LABEL}" == "ATLAS_RECOV" || "${TARGET_DEV}" == "/dev/sda"* ]]; then - echo "FATAL: ${TARGET_DEV} (label: ) matches or resides on ATLAS_RECOV." >&2 - echo "ATLAS_RECOV contains encrypted offline keys and must NEVER be overwritten." >&2 +TARGET_UUID=$(lsblk -no UUID "${TARGET_DEV}" 2>/dev/null || blkid -s UUID -o value "${TARGET_DEV}" 2>/dev/null || true) +TARGET_PARTUUID=$(lsblk -no PARTUUID "${TARGET_DEV}" 2>/dev/null || blkid -s PARTUUID -o value "${TARGET_DEV}" 2>/dev/null || true) +TARGET_MOUNTS=$(lsblk -no MOUNTPOINTS "${TARGET_DEV}" 2>/dev/null || true) + +# 1. Reject ATLAS_RECOV by label, filesystem UUID, and partition UUID +KNOWN_ATLAS_RECOV_UUID="669D-4D0E" +KNOWN_ATLAS_RECOV_PARTUUID="335d7260-01" + +if [[ "${TARGET_LABEL}" == "ATLAS_RECOV" || "${TARGET_UUID}" == "${KNOWN_ATLAS_RECOV_UUID}" || "${TARGET_PARTUUID}" == "${KNOWN_ATLAS_RECOV_PARTUUID}" ]]; then + echo "FATAL: Target matches ATLAS_RECOV identity (Label: , UUID: , PARTUUID: )." >&2 + echo "ATLAS_RECOV contains encrypted offline recovery keys and must NEVER be overwritten." >&2 + exit 1 +fi + +# 2. Reject internal NVMe storage and system partitions +KNOWN_HOST_ROOT_UUID="d27bfd26-ff30-400e-9eca-9cdf73de9406" +KNOWN_HOST_EFI_UUID="9DA2-3597" + +if [[ "${TARGET_DEV}" == *"nvme"* || "${TARGET_UUID}" == "${KNOWN_HOST_ROOT_UUID}" || "${TARGET_UUID}" == "${KNOWN_HOST_EFI_UUID}" ]]; then + echo "FATAL: Target ${TARGET_DEV} is an internal host system disk or EFI system partition." >&2 + exit 1 +fi + +# 3. Reject active host mounts +if [[ -n "${TARGET_MOUNTS}" ]]; then + echo "FATAL: Target ${TARGET_DEV} has active mountpoints: ${TARGET_MOUNTS}. Unmount before provisioning." >&2 + exit 1 +fi + +# 4. If target is a disk containing partitions, ensure none of its child partitions match ATLAS_RECOV +CHILD_IDS=$(lsblk -no LABEL,UUID,PARTUUID "${TARGET_DEV}" 2>/dev/null || true) +if echo "${CHILD_IDS}" | grep -qE "ATLAS_RECOV|${KNOWN_ATLAS_RECOV_UUID}|${KNOWN_ATLAS_RECOV_PARTUUID}"; then + echo "FATAL: Device ${TARGET_DEV} contains partition matching ATLAS_RECOV identity." >&2 exit 1 fi diff --git a/scripts/capture_gate1_receipt.sh b/scripts/capture_gate1_receipt.sh index cf054fa4..1ae1312d 100755 --- a/scripts/capture_gate1_receipt.sh +++ b/scripts/capture_gate1_receipt.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash # capture_gate1_receipt.sh: Capture Gate 1 standalone recovery evidence receipt -# Zero Disk Secrets and Unslop compliant. +# Invariant: NO PLAINTEXT SECRETS IN REPOSITORY OR BUILD ARTIFACTS. Unslop compliant. set -euo pipefail @@ -48,7 +48,8 @@ cat << JSON_EOF > "${RECEIPT_FILE}" "internal_nvme_unmounted": true, "init_entrypoint": "rdinit=/init", "atlas_recov_protected": true, - "qemu_zero_disk_verified": true + "qemu_zero_disk_verified": true, + "secrets_policy": "NO PLAINTEXT SECRETS IN REPOSITORY OR BUILD ARTIFACTS" }, "utilities_packaged": [ "/bin/busybox", @@ -58,10 +59,16 @@ cat << JSON_EOF > "${RECEIPT_FILE}" "/bin/blkid", "/bin/mount", "/bin/umount", - "/sbin/cryptsetup", - "/usr/bin/age", - "/usr/bin/tpm2_pcrread", - "/usr/bin/efibootmgr" + "/bin/mkdir", + "/bin/cat", + "/bin/grep", + "/bin/sed", + "/bin/sha256sum", + "/bin/cryptsetup", + "/bin/age", + "/bin/tpm2_pcrread", + "/bin/efibootmgr", + "/bin/gocryptfs" ] } JSON_EOF diff --git a/scripts/qemu_security_suite.sh b/scripts/qemu_security_suite.sh index 13faa254..25abe9b7 100755 --- a/scripts/qemu_security_suite.sh +++ b/scripts/qemu_security_suite.sh @@ -26,10 +26,6 @@ AIENOS_COMMIT="${commit}" AIENOS_RESTART_SECS=1 cargo build --quiet --release \ -p aienos-boot --target aarch64-unknown-uefi --features handoff --bin aienos-handoff work="$(mktemp -d)" -tpm_dir="${work}/tpm" -mkdir -p "${tpm_dir}" "${work}/esp/EFI/BOOT" "${work}/esp/EFI/AIENOS" -touch "${work}/esp/EFI/AIENOS/BOOTREPORT.TXT" -cp target/aarch64-unknown-uefi/release/aienos-handoff.efi "${work}/esp/EFI/BOOT/BOOTAA64.EFI" cleanup() { if [[ -n "${SWTPM_PID:-}" ]] && kill -0 "${SWTPM_PID}" 2>/dev/null; then @@ -40,6 +36,11 @@ cleanup() { trap cleanup EXIT echo "=== Subtest 1: Software TPM 2.0 Integration & Live Boot ===" +tpm_dir="${work}/tpm" +mkdir -p "${tpm_dir}" "${work}/subtest1/esp/EFI/BOOT" "${work}/subtest1/esp/EFI/AIENOS" +touch "${work}/subtest1/esp/EFI/AIENOS/BOOTREPORT.TXT" +cp target/aarch64-unknown-uefi/release/aienos-handoff.efi "${work}/subtest1/esp/EFI/BOOT/BOOTAA64.EFI" + swtpm socket --tpm2 \ --tpmstate dir="${tpm_dir}" \ --ctrl type=unixio,path="${tpm_dir}/swtpm-sock" \ @@ -47,8 +48,8 @@ swtpm socket --tpm2 \ SWTPM_PID=$! sleep 1 -cp "${vars_fd}" "${work}/vars.fd" -log="${work}/serial_tpm.log" +cp "${vars_fd}" "${work}/subtest1/vars.fd" +log="${work}/subtest1/serial_tpm.log" set +e timeout ${AIENOS_QEMU_TIMEOUT:-120} qemu-system-aarch64 \ @@ -57,44 +58,48 @@ timeout ${AIENOS_QEMU_TIMEOUT:-120} qemu-system-aarch64 \ -tpmdev emulator,id=tpm0,chardev=chrtpm \ -device tpm-tis-device,tpmdev=tpm0 \ -drive if=pflash,format=raw,readonly=on,file="${code_fd}" \ - -drive if=pflash,format=raw,file="${work}/vars.fd" \ - -drive if=none,id=esp,format=raw,file=fat:rw:"${work}/esp" \ + -drive if=pflash,format=raw,file="${work}/subtest1/vars.fd" \ + -drive if=none,id=esp,format=raw,file=fat:rw:"${work}/subtest1/esp" \ -device virtio-blk-pci,drive=esp \ -device ramfb -display none -nic none \ -serial file:"${log}" -no-reboot tpm_status=$? set -e -tr -d '\r' <"${log}" >"${work}/serial_tpm.txt" -if grep -q "kernel: alive" "${work}/serial_tpm.txt" && grep -q "report_kind: final" "${work}/serial_tpm.txt"; then +tr -d "\r" <"${log}" >"${work}/subtest1/serial_tpm.txt" +if grep -q "kernel: alive" "${work}/subtest1/serial_tpm.txt" && grep -q "report_kind: final" "${work}/subtest1/serial_tpm.txt"; then echo "PASS swTPM live boot reached kernel alive and final report" else echo "FAIL swTPM live boot did not complete cleanly" - cat "${work}/serial_tpm.txt" + cat "${work}/subtest1/serial_tpm.txt" exit 1 fi echo "=== Subtest 2: Soak Testing (${ITERATIONS} Sequential Boots) ===" for i in $(seq 1 "${ITERATIONS}"); do - cp "${vars_fd}" "${work}/vars.fd" - soak_log="${work}/serial_soak_${i}.log" + soak_work="${work}/soak_${i}" + mkdir -p "${soak_work}/esp/EFI/BOOT" "${soak_work}/esp/EFI/AIENOS" + touch "${soak_work}/esp/EFI/AIENOS/BOOTREPORT.TXT" + cp target/aarch64-unknown-uefi/release/aienos-handoff.efi "${soak_work}/esp/EFI/BOOT/BOOTAA64.EFI" + cp "${vars_fd}" "${soak_work}/vars.fd" + soak_log="${soak_work}/serial_soak.log" set +e timeout ${AIENOS_QEMU_TIMEOUT:-120} qemu-system-aarch64 \ -M virt,virtualization=on -cpu max -smp 4 -m 2048 \ -drive if=pflash,format=raw,readonly=on,file="${code_fd}" \ - -drive if=pflash,format=raw,file="${work}/vars.fd" \ - -drive if=none,id=esp,format=raw,file=fat:rw:"${work}/esp" \ + -drive if=pflash,format=raw,file="${soak_work}/vars.fd" \ + -drive if=none,id=esp,format=raw,file=fat:rw:"${soak_work}/esp" \ -device virtio-blk-pci,drive=esp \ -device ramfb -display none -nic none \ -serial file:"${soak_log}" -no-reboot soak_status=$? set -e - tr -d '\r' <"${soak_log}" >"${work}/serial_soak_${i}.txt" - if grep -q "kernel: alive" "${work}/serial_soak_${i}.txt" && grep -q "report_kind: final" "${work}/serial_soak_${i}.txt"; then + tr -d "\r" <"${soak_log}" >"${soak_work}/serial_soak.txt" + if grep -q "kernel: alive" "${soak_work}/serial_soak.txt" && grep -q "report_kind: final" "${soak_work}/serial_soak.txt"; then echo "PASS Soak run ${i}/${ITERATIONS}: clean boot & reset" else echo "FAIL Soak run ${i}/${ITERATIONS} failed" - cat "${work}/serial_soak_${i}.txt" + cat "${soak_work}/serial_soak.txt" exit 1 fi done @@ -119,7 +124,7 @@ corrupt_status=$? set -e if [[ -f "${corrupt_log}" ]]; then - tr -d '\r' <"${corrupt_log}" >"${corrupt_work}/serial_corrupt.txt" + tr -d "\r" <"${corrupt_log}" >"${corrupt_work}/serial_corrupt.txt" if grep -q "kernel: alive" "${corrupt_work}/serial_corrupt.txt"; then echo "FAIL Corrupted binary unexpectedly reached kernel alive" exit 1