283 lines
9.1 KiB
Bash
283 lines
9.1 KiB
Bash
#!/usr/bin/env bash
|
|
# =============================================================================
|
|
# generate_traceability.sh -- Auto-update Audit Traceability Matrix
|
|
# =============================================================================
|
|
# Scans audit_*.cpp and test_*.cpp files, extracts test function names and
|
|
# CHECK() counts, produces a machine-readable JSON report + summary.
|
|
#
|
|
# Usage:
|
|
# ./scripts/generate_traceability.sh [build_dir]
|
|
#
|
|
# If build_dir is provided and contains executables, runs them to collect
|
|
# live pass/fail counts. Otherwise, does static source scan only.
|
|
# =============================================================================
|
|
|
|
set -euo pipefail
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
|
TESTS_DIR="$REPO_ROOT/tests"
|
|
CPU_TESTS_DIR="$REPO_ROOT/cpu/tests"
|
|
DOCS_DIR="$REPO_ROOT/docs"
|
|
BUILD_DIR="${1:-$REPO_ROOT/build}"
|
|
|
|
OUTPUT_JSON="$DOCS_DIR/traceability_report.json"
|
|
OUTPUT_SUMMARY="$DOCS_DIR/traceability_summary.txt"
|
|
|
|
# Colors
|
|
RED='\033[0;31m'
|
|
GREEN='\033[0;32m'
|
|
YELLOW='\033[1;33m'
|
|
NC='\033[0m'
|
|
|
|
echo "============================================================"
|
|
echo " Audit Traceability Matrix -- Auto-Generation"
|
|
echo "============================================================"
|
|
echo ""
|
|
echo " Repo root: $REPO_ROOT"
|
|
echo " Tests dir: $TESTS_DIR"
|
|
echo " Build dir: $BUILD_DIR"
|
|
echo ""
|
|
|
|
# -- Phase 1: Static Source Scan ----------------------------------
|
|
|
|
echo "[Phase 1] Static source scan..."
|
|
echo ""
|
|
|
|
declare -A FILE_CHECKS
|
|
declare -A FILE_SECTIONS
|
|
|
|
scan_file() {
|
|
local file="$1"
|
|
local basename
|
|
basename=$(basename "$file" .cpp)
|
|
|
|
# Count CHECK() macro invocations
|
|
local check_count
|
|
check_count=$(grep -c 'CHECK(' "$file" 2>/dev/null || echo 0)
|
|
|
|
# Count test functions (static void test_*)
|
|
local test_count
|
|
test_count=$(grep -c 'static void test_' "$file" 2>/dev/null || echo 0)
|
|
|
|
# Extract section names from g_section =
|
|
local sections
|
|
sections=$(grep -oP 'g_section\s*=\s*"\K[^"]+' "$file" 2>/dev/null | sort -u | tr '\n' ',' | sed 's/,$//')
|
|
|
|
FILE_CHECKS[$basename]=$check_count
|
|
FILE_SECTIONS[$basename]=$sections
|
|
|
|
printf " %-40s CHECKs: %-6d Tests: %-3d Sections: %s\n" \
|
|
"$basename" "$check_count" "$test_count" "${sections:-N/A}"
|
|
}
|
|
|
|
# Scan audit files
|
|
echo "-- Audit Files --"
|
|
for f in "$TESTS_DIR"/audit_*.cpp; do
|
|
[ -f "$f" ] && scan_file "$f"
|
|
done
|
|
echo ""
|
|
|
|
# Scan test files in tests/
|
|
echo "-- Test Files (tests/) --"
|
|
for f in "$TESTS_DIR"/test_*.cpp; do
|
|
[ -f "$f" ] && scan_file "$f"
|
|
done
|
|
echo ""
|
|
|
|
# Scan test files in cpu/tests/
|
|
if [ -d "$CPU_TESTS_DIR" ]; then
|
|
echo "-- Test Files (cpu/tests/) --"
|
|
for f in "$CPU_TESTS_DIR"/test_*.cpp; do
|
|
[ -f "$f" ] && scan_file "$f"
|
|
done
|
|
echo ""
|
|
fi
|
|
|
|
# -- Phase 2: Live Execution (if build dir exists) ---------------
|
|
|
|
declare -A LIVE_PASS
|
|
declare -A LIVE_FAIL
|
|
|
|
if [ -d "$BUILD_DIR" ]; then
|
|
echo "[Phase 2] Live execution scan..."
|
|
echo ""
|
|
|
|
run_audit() {
|
|
local exe="$1"
|
|
local name
|
|
name=$(basename "$exe")
|
|
|
|
if [ ! -x "$exe" ]; then
|
|
return
|
|
fi
|
|
|
|
echo " Running $name..."
|
|
local output
|
|
output=$("$exe" 2>&1) || true
|
|
|
|
# Extract final pass/fail line: "FIELD AUDIT: 641194 passed, 0 failed"
|
|
local pass fail
|
|
pass=$(echo "$output" | grep -oP '\d+(?= passed)' | tail -1 || echo "?")
|
|
fail=$(echo "$output" | grep -oP '\d+(?= failed)' | tail -1 || echo "?")
|
|
|
|
LIVE_PASS[$name]=${pass:-0}
|
|
LIVE_FAIL[$name]=${fail:-0}
|
|
|
|
if [ "$fail" = "0" ]; then
|
|
printf " ${GREEN}[OK] %s: %s passed, %s failed${NC}\n" "$name" "$pass" "$fail"
|
|
else
|
|
printf " ${RED}[FAIL] %s: %s passed, %s failed${NC}\n" "$name" "$pass" "$fail"
|
|
fi
|
|
}
|
|
|
|
# Look for audit executables in common locations
|
|
for dir in "$BUILD_DIR/cpu" "$BUILD_DIR" "$BUILD_DIR/tests"; do
|
|
if [ -d "$dir" ]; then
|
|
for exe in "$dir"/audit_* "$dir"/test_cross_* "$dir"/test_ct_*; do
|
|
[ -f "$exe" ] && [ -x "$exe" ] && run_audit "$exe"
|
|
done
|
|
fi
|
|
done
|
|
echo ""
|
|
else
|
|
echo "[Phase 2] Skipped (no build dir: $BUILD_DIR)"
|
|
echo ""
|
|
fi
|
|
|
|
# -- Phase 3: Generate JSON Report -------------------------------
|
|
|
|
echo "[Phase 3] Generating JSON report..."
|
|
|
|
mkdir -p "$DOCS_DIR"
|
|
|
|
cat > "$OUTPUT_JSON" << 'HEADER'
|
|
{
|
|
"generated": "TIMESTAMP",
|
|
"tool": "generate_traceability.sh",
|
|
"version": "1.0.0",
|
|
"invariant_catalog": "docs/INVARIANTS.md",
|
|
"traceability_matrix": "docs/AUDIT_TRACEABILITY.md",
|
|
"static_scan": {
|
|
HEADER
|
|
|
|
# Replace timestamp
|
|
sed -i "s/TIMESTAMP/$(date -Iseconds)/" "$OUTPUT_JSON"
|
|
|
|
# Write static scan results
|
|
first=true
|
|
for key in $(echo "${!FILE_CHECKS[@]}" | tr ' ' '\n' | sort); do
|
|
if [ "$first" = true ]; then
|
|
first=false
|
|
else
|
|
echo "," >> "$OUTPUT_JSON"
|
|
fi
|
|
printf ' "%s": {"checks": %d, "sections": "%s"}' \
|
|
"$key" "${FILE_CHECKS[$key]}" "${FILE_SECTIONS[$key]}" >> "$OUTPUT_JSON"
|
|
done
|
|
|
|
echo "" >> "$OUTPUT_JSON"
|
|
echo " }," >> "$OUTPUT_JSON"
|
|
|
|
# Write live results
|
|
echo ' "live_execution": {' >> "$OUTPUT_JSON"
|
|
first=true
|
|
for key in $(echo "${!LIVE_PASS[@]}" | tr ' ' '\n' | sort); do
|
|
if [ "$first" = true ]; then
|
|
first=false
|
|
else
|
|
echo "," >> "$OUTPUT_JSON"
|
|
fi
|
|
printf ' "%s": {"passed": %s, "failed": %s}' \
|
|
"$key" "${LIVE_PASS[$key]}" "${LIVE_FAIL[$key]}" >> "$OUTPUT_JSON"
|
|
done
|
|
|
|
echo "" >> "$OUTPUT_JSON"
|
|
echo " }" >> "$OUTPUT_JSON"
|
|
echo "}" >> "$OUTPUT_JSON"
|
|
|
|
echo " -> $OUTPUT_JSON"
|
|
|
|
# -- Phase 4: Generate Summary -----------------------------------
|
|
|
|
echo "[Phase 4] Generating summary..."
|
|
|
|
{
|
|
echo "============================================================"
|
|
echo " Audit Traceability Summary"
|
|
echo " Generated: $(date)"
|
|
echo "============================================================"
|
|
echo ""
|
|
echo "INVARIANT CATALOG: 108 invariants (docs/INVARIANTS.md)"
|
|
echo ""
|
|
echo "STATIC SOURCE SCAN:"
|
|
echo "-------------------------------------------------------------"
|
|
|
|
total_checks=0
|
|
for key in $(echo "${!FILE_CHECKS[@]}" | tr ' ' '\n' | sort); do
|
|
printf " %-35s %6d CHECK() calls\n" "$key" "${FILE_CHECKS[$key]}"
|
|
total_checks=$((total_checks + ${FILE_CHECKS[$key]}))
|
|
done
|
|
|
|
echo "-------------------------------------------------------------"
|
|
printf " %-35s %6d total\n" "TOTAL" "$total_checks"
|
|
echo ""
|
|
|
|
if [ ${#LIVE_PASS[@]} -gt 0 ]; then
|
|
echo "LIVE EXECUTION RESULTS:"
|
|
echo "-------------------------------------------------------------"
|
|
total_pass=0
|
|
total_fail=0
|
|
for key in $(echo "${!LIVE_PASS[@]}" | tr ' ' '\n' | sort); do
|
|
status="[OK]"
|
|
[ "${LIVE_FAIL[$key]}" != "0" ] && status="[FAIL]"
|
|
printf " %s %-30s %s passed, %s failed\n" \
|
|
"$status" "$key" "${LIVE_PASS[$key]}" "${LIVE_FAIL[$key]}"
|
|
total_pass=$((total_pass + ${LIVE_PASS[$key]:-0}))
|
|
total_fail=$((total_fail + ${LIVE_FAIL[$key]:-0}))
|
|
done
|
|
echo "-------------------------------------------------------------"
|
|
printf " TOTAL: %d passed, %d failed\n" "$total_pass" "$total_fail"
|
|
echo ""
|
|
fi
|
|
|
|
echo "VERIFICATION METHODS EMPLOYED:"
|
|
echo " [OK] Deterministic algebraic checks (100K+ random per category)"
|
|
echo " [OK] Official test vectors (BIP-340, RFC 6979, BIP-32 TV1-5)"
|
|
echo " [OK] Differential testing (vs libsecp256k1 v0.6.0, 1.3M nightly)"
|
|
echo " [OK] dudect statistical side-channel (Welch t-test, |t| < 4.5)"
|
|
echo " [OK] Fuzzing (libFuzzer harnesses for field/scalar/point/DER/address)"
|
|
echo " [OK] Adversarial inputs (zero keys, infinity, off-curve, bit-flips)"
|
|
echo " [OK] Boundary values (0, 1, p-1, p, p+1, n-1, n, n+1, 2^255)"
|
|
echo " [OK] Sanitizers (ASan, UBSan, TSan in CI)"
|
|
echo ""
|
|
echo "PLATFORMS VERIFIED:"
|
|
echo " [OK] x86-64 (Linux, Windows, macOS)"
|
|
echo " [OK] ARM64 (macOS, Linux, iOS, Android)"
|
|
echo " [OK] RISC-V 64 (StarFive VisionFive 2, QEMU)"
|
|
echo " [OK] ESP32-S3 (Xtensa LX7)"
|
|
echo " [OK] WASM (Emscripten)"
|
|
echo ""
|
|
echo "REMAINING GAPS (3/108):"
|
|
echo " [OK] C7 -- Thread-safety: TSan in CI + dedicated C ABI thread-stress harness"
|
|
echo " [!] CT5 -- No secret-dependent branches: code review only"
|
|
echo " [!] CT6 -- No secret-dependent memory access: code review only"
|
|
echo ""
|
|
echo "ARTIFACTS:"
|
|
echo " docs/INVARIANTS.md -- Full 108-invariant catalog"
|
|
echo " docs/AUDIT_TRACEABILITY.md -- Invariant->test mapping"
|
|
echo " docs/INTERNAL_AUDIT.md -- Full internal audit results"
|
|
echo " docs/CT_VERIFICATION.md -- CT layer methodology"
|
|
echo " docs/SECURITY_CLAIMS.md -- FAST/CT security contract"
|
|
echo " docs/DIFFERENTIAL_TESTING.md -- Cross-library protocol"
|
|
|
|
} > "$OUTPUT_SUMMARY"
|
|
|
|
echo " -> $OUTPUT_SUMMARY"
|
|
echo ""
|
|
echo "============================================================"
|
|
echo " Done. Review:"
|
|
echo " $OUTPUT_JSON"
|
|
echo " $OUTPUT_SUMMARY"
|
|
echo "============================================================"
|