#!/bin/bash # Binary Micro-Witness Verification Script # Sprint: SPRINT_0128_001_BinaryIndex_binary_micro_witness # Usage: ./verify.sh [witness-path] [-a|--all] [-v|--verbose] set -e SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" WITNESS_DIR="$SCRIPT_DIR/witnesses" PASS_COUNT=0 FAIL_COUNT=0 VERBOSE=false # Colors RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' CYAN='\033[0;36m' NC='\033[0m' # No Color print_status() { local status="$1" local message="$2" case "$status" in PASS) echo -e "${GREEN}[OK]${NC} $message" ;; FAIL) echo -e "${RED}[FAIL]${NC} $message" ;; SKIP) echo -e "${YELLOW}[SKIP]${NC} $message" ;; INFO) echo -e "${CYAN}[INFO]${NC} $message" ;; *) echo "[-] $message" ;; esac } verify_witness() { local witness_path="$1" echo "" echo -e "${CYAN}=== Verifying: $witness_path ===${NC}" if [ ! -f "$witness_path" ]; then print_status "FAIL" "Witness file not found: $witness_path" ((FAIL_COUNT++)) return fi # Check if jq is available if ! command -v jq &> /dev/null; then echo -e "${YELLOW}Warning: jq not installed. Basic verification only.${NC}" echo "Install jq for full verification: apt install jq / brew install jq" # Basic JSON validity check if python3 -c "import json; json.load(open('$witness_path'))" 2>/dev/null || \ python -c "import json; json.load(open('$witness_path'))" 2>/dev/null; then print_status "PASS" "Valid JSON" else print_status "FAIL" "Invalid JSON" ((FAIL_COUNT++)) return fi ((PASS_COUNT++)) return fi # Parse with jq - handle both standalone predicate and in-toto statement local predicate if jq -e '.predicate' "$witness_path" > /dev/null 2>&1; then predicate=$(jq '.predicate' "$witness_path") else predicate=$(jq '.' "$witness_path") fi # Extract fields local schema_version=$(echo "$predicate" | jq -r '.schemaVersion // empty') local binary_digest=$(echo "$predicate" | jq -r '.binary.digest // empty') local binary_filename=$(echo "$predicate" | jq -r '.binary.filename // empty') local cve_id=$(echo "$predicate" | jq -r '.cve.id // empty') local verdict=$(echo "$predicate" | jq -r '.verdict // empty') local confidence=$(echo "$predicate" | jq -r '.confidence // empty') local computed_at=$(echo "$predicate" | jq -r '.computedAt // empty') local evidence_count=$(echo "$predicate" | jq -r '.evidence | length // 0') local binary_index_version=$(echo "$predicate" | jq -r '.tooling.binaryIndexVersion // empty') local lifter=$(echo "$predicate" | jq -r '.tooling.lifter // empty') # Verify required fields local missing_fields="" [ -z "$schema_version" ] && missing_fields="$missing_fields schemaVersion" [ -z "$binary_digest" ] && missing_fields="$missing_fields binary.digest" [ -z "$cve_id" ] && missing_fields="$missing_fields cve.id" [ -z "$verdict" ] && missing_fields="$missing_fields verdict" [ -z "$confidence" ] && missing_fields="$missing_fields confidence" [ -z "$computed_at" ] && missing_fields="$missing_fields computedAt" if [ -n "$missing_fields" ]; then print_status "FAIL" "Missing required fields:$missing_fields" ((FAIL_COUNT++)) return fi print_status "PASS" "All required fields present" # Display witness details echo "" echo "Witness Details:" echo " Binary Digest: $binary_digest" echo " Binary File: $binary_filename" echo " CVE: $cve_id" echo " Verdict: $verdict" echo " Confidence: $(echo "$confidence * 100" | bc)%" echo " Computed At: $computed_at" # Verify schema version if [ "$schema_version" = "1.0.0" ]; then print_status "PASS" "Schema version: $schema_version" else print_status "SKIP" "Unknown schema version: $schema_version" fi # Verify binary digest format if [[ "$binary_digest" =~ ^sha256:[a-fA-F0-9]{64}$ ]]; then print_status "PASS" "Binary digest format valid" else print_status "FAIL" "Invalid binary digest format" ((FAIL_COUNT++)) return fi # Verify CVE ID format if [[ "$cve_id" =~ ^CVE-[0-9]{4}-[0-9]{4,}$ ]]; then print_status "PASS" "CVE ID format valid" else print_status "SKIP" "Non-standard vulnerability ID: $cve_id" fi # Verify verdict case "$verdict" in patched|vulnerable|inconclusive|partial) print_status "PASS" "Verdict: $verdict" ;; *) print_status "FAIL" "Invalid verdict: $verdict" ((FAIL_COUNT++)) return ;; esac # Verify confidence range if (( $(echo "$confidence >= 0 && $confidence <= 1" | bc -l) )); then print_status "PASS" "Confidence in valid range" else print_status "FAIL" "Confidence out of range: $confidence" ((FAIL_COUNT++)) return fi # Check evidence if [ "$evidence_count" -gt 0 ]; then print_status "PASS" "Evidence present: $evidence_count function(s)" if [ "$VERBOSE" = true ]; then echo "" echo "Function Evidence:" echo "$predicate" | jq -r '.evidence[] | " - \(.function): \(.state) (score: \(.score * 100 | floor)%, method: \(.method))"' fi else print_status "SKIP" "No function-level evidence provided" fi # Check tooling print_status "INFO" "Tooling: BinaryIndex $binary_index_version, Lifter: $lifter" # TODO: Signature verification print_status "SKIP" "Signature verification (not yet implemented)" # TODO: Rekor proof verification print_status "SKIP" "Rekor inclusion proof (not yet implemented)" ((PASS_COUNT++)) echo "" echo -e "Result: ${GREEN}VERIFIED${NC}" } # Parse arguments WITNESS_PATH="" VERIFY_ALL=false while [[ $# -gt 0 ]]; do case $1 in -a|--all) VERIFY_ALL=true shift ;; -v|--verbose) VERBOSE=true shift ;; -h|--help) echo "Usage: $0 [witness-path] [-a|--all] [-v|--verbose]" echo "" echo "Examples:" echo " $0 witnesses/openssl-cve-2024-0567.json" echo " $0 --all --verbose" exit 0 ;; *) WITNESS_PATH="$1" shift ;; esac done # Main echo -e "${CYAN}Binary Micro-Witness Verification${NC}" echo -e "${CYAN}==================================${NC}" echo "Sprint: SPRINT_0128_001_BinaryIndex_binary_micro_witness" if [ -n "$WITNESS_PATH" ]; then verify_witness "$WITNESS_PATH" elif [ "$VERIFY_ALL" = true ] || [ -d "$WITNESS_DIR" ]; then for witness in "$WITNESS_DIR"/*.json; do [ -f "$witness" ] && verify_witness "$witness" done else echo "Usage: $0 [witness-path] [-a|--all] [-v|--verbose]" echo "" echo "Examples:" echo " $0 witnesses/openssl-cve-2024-0567.json" echo " $0 --all --verbose" exit 1 fi echo "" echo -e "${CYAN}==================================${NC}" if [ $FAIL_COUNT -eq 0 ]; then echo -e "Summary: ${GREEN}$PASS_COUNT passed${NC}, $FAIL_COUNT failed" else echo -e "Summary: $PASS_COUNT passed, ${RED}$FAIL_COUNT failed${NC}" fi exit $FAIL_COUNT