Files
git.stella-ops.org/demos/binary-micro-witness/verify.sh

239 lines
7.3 KiB
Bash

#!/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