mirror of
https://github.com/saymrwulf/verifying-crypto-with-lean.git
synced 2026-09-04 20:03:41 +00:00
112 lines
5 KiB
Bash
112 lines
5 KiB
Bash
|
|
#!/usr/bin/env bash
|
||
|
|
# check-book.sh — THE button for verifying-crypto-with-lean.
|
||
|
|
#
|
||
|
|
# This script is the only source of the words "ALL GREEN" for this
|
||
|
|
# repository. It rebuilds the book from the committed sources and then
|
||
|
|
# verifies that every countable claim printed in the book matches reality
|
||
|
|
# measured at run time: chapter counts, the week plan, exercise/solution
|
||
|
|
# pairing, the recomputed SLH-DSA arithmetic, the transparency log's leaf
|
||
|
|
# and certificate counts, the first dual-signed head, the extracted
|
||
|
|
# parameter card, and the 71-digit Q — digit for digit.
|
||
|
|
#
|
||
|
|
# Phases
|
||
|
|
# 0 source hygiene (check-book.py)
|
||
|
|
# 1 build (tectonic via build.sh; fails on TeX errors)
|
||
|
|
# 1b built-PDF claims (pages, unresolved refs, page-count claims)
|
||
|
|
# 2 internal congruence (counts and arithmetic inside the book)
|
||
|
|
# 3 cross-repo congruence (log + fips205 + P25519 siblings)
|
||
|
|
#
|
||
|
|
# Environment
|
||
|
|
# BOOK_LOCAL_ONLY=1 skip phase 3; verdict is downgraded, never ALL GREEN
|
||
|
|
# ESTATE_ROOT parent dir of the sibling repos (default: ../)
|
||
|
|
# LTL_DIR / FIPS205_DIR / P25519_FILE override individual siblings
|
||
|
|
#
|
||
|
|
# Modes
|
||
|
|
# ./check-book.sh full run
|
||
|
|
# ./check-book.sh --selftest adversarial self-test: mutates copies of the
|
||
|
|
# sources and asserts the button turns RED
|
||
|
|
set -euo pipefail
|
||
|
|
cd "$(dirname "$0")"
|
||
|
|
HERE="$(pwd)"
|
||
|
|
|
||
|
|
selftest() {
|
||
|
|
echo "=== SELFTEST: the button must go red for the right reasons ==="
|
||
|
|
command -v python3 >/dev/null || { echo "python3 required"; exit 1; }
|
||
|
|
# the mutated copy keeps the REAL sibling repos: only the book is mutated,
|
||
|
|
# so a red verdict proves the mutation was caught, not that a repo was lost
|
||
|
|
local ESTATE; ESTATE="${ESTATE_ROOT:-$(dirname "$HERE")}"
|
||
|
|
local tmp out pass=0 fail=0
|
||
|
|
run_copy() {
|
||
|
|
tmp="$(mktemp -d)"; mkdir -p "$tmp/chapters"
|
||
|
|
cp main.tex README.md "$tmp/"; cp chapters/*.tex "$tmp/chapters/"
|
||
|
|
}
|
||
|
|
run_mutated() { # $1 description, $2 mutation cmd, $3 expected FAIL substring
|
||
|
|
run_copy
|
||
|
|
( cd "$tmp" && eval "$2" )
|
||
|
|
out="$(SKIP_BUILD=1 ESTATE_ROOT="$ESTATE" python3 "$HERE/check-book.py" "$tmp" 2>&1)" \
|
||
|
|
&& { echo " FAIL mutation NOT caught: $1"; fail=$((fail+1)); rm -rf "$tmp"; return; }
|
||
|
|
if echo "$out" | grep -q "FAIL.*$3"; then
|
||
|
|
echo " ok caught for the right reason: $1"; pass=$((pass+1))
|
||
|
|
else
|
||
|
|
echo " FAIL red, but not on the expected check ('$3'): $1"; fail=$((fail+1))
|
||
|
|
echo "$out" | grep " FAIL" | head -3
|
||
|
|
fi
|
||
|
|
rm -rf "$tmp"
|
||
|
|
}
|
||
|
|
# control: the unmutated copy must pass (proves the harness can go green)
|
||
|
|
run_copy
|
||
|
|
if SKIP_BUILD=1 ESTATE_ROOT="$ESTATE" python3 "$HERE/check-book.py" "$tmp" >/dev/null 2>&1; then
|
||
|
|
echo " ok control: unmutated copy passes"; pass=$((pass+1))
|
||
|
|
else
|
||
|
|
echo " FAIL control: unmutated copy should pass but is red"; fail=$((fail+1))
|
||
|
|
SKIP_BUILD=1 ESTATE_ROOT="$ESTATE" python3 "$HERE/check-book.py" "$tmp" | grep FAIL || true
|
||
|
|
fi
|
||
|
|
rm -rf "$tmp"
|
||
|
|
|
||
|
|
run_mutated "chapter-count claim drifts (fourteen -> thirteen)" \
|
||
|
|
"sed -i 's/fourteen chapters/thirteen chapters/' README.md" \
|
||
|
|
"README chapter count"
|
||
|
|
run_mutated "a solution deleted (ch13 solhead 13.6 dropped)" \
|
||
|
|
"sed -i 's/\\\\solhead{13.6}/% gone/' chapters/ch13-second-summit.tex" \
|
||
|
|
"ch13: exercises == solutions"
|
||
|
|
run_mutated "one digit of the 71-digit Q changed in ch07" \
|
||
|
|
"sed -i 's/740582127325613583022312264370627886761/740582127325613583022312264370627886762/' chapters/ch07-primality-certificates.tex" \
|
||
|
|
"printed Q == repository"
|
||
|
|
run_mutated "leaf-count claim drifts (nineteen leaves -> twenty)" \
|
||
|
|
"sed -i \"s/log's nineteen leaves/log's twenty leaves/\" chapters/ch14-attestation-protocol.tex" \
|
||
|
|
"ch14 'nineteen leaves'"
|
||
|
|
run_mutated "worst-case arithmetic drifts (3,824 -> 3,689)" \
|
||
|
|
"sed -i 's/3{,}824/3{,}689/' chapters/ch13-second-summit.tex" \
|
||
|
|
"ch13 prints worst total"
|
||
|
|
run_mutated "a chapter stops ending on its checkpoint" \
|
||
|
|
"printf '\n\\\\begin{aha}\nstray box after the checkpoint\n\\\\end{aha}\n' >> chapters/ch05-numbers-and-automation.tex" \
|
||
|
|
"ch05.*last environment is checkpoint"
|
||
|
|
run_mutated "week plan and heading diverge (heading says fifteen)" \
|
||
|
|
"sed -i 's/A fourteen-week plan/A fifteen-week plan/' main.tex" \
|
||
|
|
"week-plan heading"
|
||
|
|
|
||
|
|
echo
|
||
|
|
if [ "$fail" -gt 0 ]; then
|
||
|
|
echo "SELFTEST RED: $fail defect(s) in the button itself"; exit 1
|
||
|
|
fi
|
||
|
|
echo "SELFTEST GREEN: $pass/$pass (control + 7 mutations, each caught on its own check)"
|
||
|
|
exit 0
|
||
|
|
}
|
||
|
|
|
||
|
|
[ "${1:-}" = "--selftest" ] && selftest
|
||
|
|
|
||
|
|
echo "=== Phase 1: build ==="
|
||
|
|
./build.sh
|
||
|
|
command -v pdfinfo >/dev/null && command -v pdftotext >/dev/null || {
|
||
|
|
echo "FAIL: poppler-utils (pdfinfo/pdftotext) required"; exit 1; }
|
||
|
|
|
||
|
|
python3 "$HERE/check-book.py" "$HERE"
|
||
|
|
|
||
|
|
echo
|
||
|
|
if [ "${BOOK_LOCAL_ONLY:-0}" = "1" ]; then
|
||
|
|
echo "VERDICT: build green + local claims green; cross-repo NOT verified."
|
||
|
|
else
|
||
|
|
echo "ALL GREEN — the book builds and every countable claim matches"
|
||
|
|
echo "measured reality (sources, PDF, transparency log, extracted code)."
|
||
|
|
fi
|