#!/usr/bin/env bash
# Permavault evidence package verifier (capture 4f868e92-f12f-4fb7-bb5a-cb096b82eac8)
# Usage: bash verify.sh [--offline]
# Requires: sha256sum or shasum. OpenSSL 3 with Ed25519 for the signature check (the
# script looks for a suitable openssl itself, including Homebrew's). Optional: curl
# (Arweave check), ots (Bitcoin timestamp).
# This script is a convenience. Every check has a manual equivalent in VERIFICATION_GUIDE.md.
set -u
cd "$(dirname "$0")"

GATEWAY='https://ar-io.dev'
PRIMARY_FILE='permavault.xyz.wacz'
PRIMARY_TX='7iFluxfrjGu-BN6mdnfxUqQCClB6z9rYNIjDMYokF3I'
EXPECTED_KEY_ID='98ae373420471711'
PUBLIC_KEY_URL='https://app.permavault.xyz/.well-known/permavault-signing-key.json'
PUBKEY='permavault-public-key.pem'

ONLINE=1
[ "${1:-}" = "--offline" ] && ONLINE=0

PASS=0; FAIL=0; WARN=0
ok()   { PASS=$((PASS+1)); printf '  [PASS] %s\n' "$1"; }
bad()  { FAIL=$((FAIL+1)); printf '  [FAIL] %s\n' "$1"; }
warn() { WARN=$((WARN+1)); printf '  [WARN] %s\n' "$1"; }

hash_file() {
  if command -v sha256sum >/dev/null 2>&1; then sha256sum "$1" | awk '{print $1}'
  elif command -v shasum >/dev/null 2>&1; then shasum -a 256 "$1" | awk '{print $1}'
  else echo ""; fi
}

# A missing hash tool must be a clear tooling error, never a false "file altered" report.
if ! command -v sha256sum >/dev/null 2>&1 && ! command -v shasum >/dev/null 2>&1; then
  echo "ERROR: neither sha256sum nor shasum is installed. Install one and re-run." >&2
  exit 2
fi

echo "Permavault evidence package verification"
echo "Capture: 4f868e92-f12f-4fb7-bb5a-cb096b82eac8"
echo

# OpenSSL capability probe (PV-144). The signature check needs OpenSSL 3 with Ed25519.
# macOS ships LibreSSL as "openssl", which can do neither; running the check anyway
# would FALSELY report the package as tampered. Try the PATH first, then the usual
# Homebrew locations. Set PV_OPENSSL=/path/to/openssl to force a specific binary.
OPENSSL_BIN=""
openssl_capable() {
  command -v "$1" >/dev/null 2>&1 || return 1
  "$1" version >/dev/null 2>&1 || return 1
  "$1" list -public-key-algorithms 2>/dev/null | grep -qi ed25519
}
if [ -n "${PV_OPENSSL:-}" ]; then
  if openssl_capable "$PV_OPENSSL"; then OPENSSL_BIN="$PV_OPENSSL"; fi
else
  for candidate in openssl /opt/homebrew/opt/openssl@3/bin/openssl /usr/local/opt/openssl@3/bin/openssl; do
    if openssl_capable "$candidate"; then OPENSSL_BIN="$candidate"; break; fi
  done
fi

echo "Step 1: Ed25519 signature over manifest.json"
if [ ! -f manifest.json ] || [ ! -f manifest.sig ] || [ ! -f "$PUBKEY" ]; then
  bad "manifest.json, manifest.sig, or $PUBKEY is missing from this folder."
elif [ -z "$OPENSSL_BIN" ]; then
  warn "the openssl on this computer cannot run the signature check (OpenSSL 3 with Ed25519 support is needed; macOS ships LibreSSL, which does not have it)."
  echo "  To complete this check: install OpenSSL 3 with 'brew install openssl', then run this script again."
  echo "  This warning does NOT mean anything is wrong with the package. This one check simply could not run here."
elif "$OPENSSL_BIN" pkeyutl -verify -pubin -inkey "$PUBKEY" -rawin -in manifest.json -sigfile manifest.sig >/dev/null 2>&1; then
  ok "manifest.json signature is valid."
else
  bad "manifest signature DID NOT verify. The manifest or key has been altered."
fi

if [ -n "$OPENSSL_BIN" ] && [ -f "$PUBKEY" ]; then
  # Read the key as its own step and require real output BEFORE fingerprinting: piping a
  # failed command's empty output would fabricate a mismatch (the empty-stream hash).
  PUBDER=$(mktemp)
  if "$OPENSSL_BIN" pkey -pubin -in "$PUBKEY" -outform DER -out "$PUBDER" >/dev/null 2>&1 && [ -s "$PUBDER" ]; then
    ACTUAL_KEY_ID=$(hash_file "$PUBDER" | cut -c1-16)
    if [ -n "$ACTUAL_KEY_ID" ] && [ "$ACTUAL_KEY_ID" = "$EXPECTED_KEY_ID" ]; then
      ok "public key fingerprint matches key id $EXPECTED_KEY_ID."
    elif [ -n "$ACTUAL_KEY_ID" ]; then
      bad "public key fingerprint is $ACTUAL_KEY_ID but this capture was signed with $EXPECTED_KEY_ID."
    fi
  else
    warn "the public key file could not be read by $OPENSSL_BIN, so its fingerprint was not checked."
    echo "  This is a tooling problem on this computer, not a sign that the package was altered."
  fi
  rm -f "$PUBDER"
  echo "  Cross-check this key independently at: $PUBLIC_KEY_URL"
fi
echo

echo "Step 2: file hashes against manifest.json"
# manifest.json is emitted canonically (one key per line), so the name/sha256
# pairs inside the files array can be extracted line by line.
MANIFEST_TABLE=$(awk '
  /"files": \[/ { infiles=1; next }
  infiles && /^  \]/ { infiles=0 }
  infiles && /"name":/ { line=$0; sub(/.*"name": "/, "", line); sub(/",?$/, "", line); name=line }
  infiles && /"sha256":/ { line=$0; sub(/.*"sha256": "/, "", line); sub(/",?$/, "", line); printf "%s\t%s\n", name, line }
' manifest.json 2>/dev/null)

if [ -z "$MANIFEST_TABLE" ]; then
  bad "could not read the file list from manifest.json."
else
  while IFS=$(printf '\t') read -r FNAME FHASH; do
    [ -z "$FNAME" ] && continue
    if [ ! -f "$FNAME" ]; then
      bad "$FNAME is listed in the manifest but missing from this folder."
    elif [ "$(hash_file "$FNAME")" = "$FHASH" ]; then
      ok "$FNAME matches its sealed SHA-256."
    else
      bad "$FNAME DOES NOT match its sealed SHA-256. The file has been altered."
    fi
  done <<EOF_TABLE
$MANIFEST_TABLE
EOF_TABLE
fi
echo

echo "Step 3: permanent copy on Arweave (online check)"
if [ "$ONLINE" -eq 0 ]; then
  echo "  Skipped (--offline)."
elif [ -z "$PRIMARY_TX" ]; then
  warn "no Arweave transaction id is recorded for the primary artifact yet."
elif ! command -v curl >/dev/null 2>&1; then
  warn "curl not found. Fetch $GATEWAY/$PRIMARY_TX manually and compare hashes."
else
  TMP_DL=$(mktemp)
  if curl -sfL "$GATEWAY/$PRIMARY_TX" -o "$TMP_DL"; then
    ARWEAVE_HASH=$(hash_file "$TMP_DL")
    LOCAL_HASH=$(hash_file "$PRIMARY_FILE" 2>/dev/null || echo "")
    if [ -n "$LOCAL_HASH" ] && [ "$ARWEAVE_HASH" = "$LOCAL_HASH" ]; then
      ok "Arweave serves the exact same bytes ($GATEWAY/$PRIMARY_TX)."
    else
      bad "the Arweave copy does not match the local primary artifact."
    fi
  else
    warn "could not download $GATEWAY/$PRIMARY_TX (network issue, or the bundle is still seeding)."
  fi
  rm -f "$TMP_DL"
fi
echo

echo "Step 4: Bitcoin timestamp (OpenTimestamps)"
if [ -f manifest.ots ]; then
  if command -v ots >/dev/null 2>&1; then
    if ots verify -f manifest.json manifest.ots; then
      ok "OpenTimestamps proof verified."
    else
      warn "ots could not verify the proof (it may still be pending Bitcoin confirmation)."
    fi
  else
    warn "manifest.ots is present but the ots client is not installed (pip install opentimestamps-client)."
  fi
else
  echo "  No manifest.ots in this package. Nothing to check."
fi
echo

echo "Step 5: RFC 3161 timestamp token"
MANIFEST_SHA='156c51a5b58a5afc8fc84cb870351f0df49b06440ad2835e169b79ebd5a227b7'
# The probed OpenSSL 3 is preferred; any openssl works for ts -verify (LibreSSL has ts).
TS_OPENSSL="$OPENSSL_BIN"
if [ -z "$TS_OPENSSL" ] && command -v openssl >/dev/null 2>&1; then TS_OPENSSL="openssl"; fi
if [ -f manifest.tsr ]; then
  if [ -z "$TS_OPENSSL" ]; then
    warn "manifest.tsr is present but no openssl is available to check it."
  else
    TSA_CHAIN=""
    if [ -f eidas_validation.json ]; then
      TSA_CHAIN=$(mktemp)
      # Extract certificateChainPem without requiring jq: pull the JSON string field and
      # unescape \n. Falls back to jq when available (more robust).
      if command -v jq >/dev/null 2>&1; then
        jq -r '.certificateChainPem // empty' eidas_validation.json > "$TSA_CHAIN" 2>/dev/null
      else
        sed -n 's/.*"certificateChainPem": "\(.*\)",\{0,1\}$/\1/p' eidas_validation.json | head -1 | sed 's/\\n/\n/g' > "$TSA_CHAIN"
      fi
    fi
    if [ -n "$TSA_CHAIN" ] && [ -s "$TSA_CHAIN" ]; then
      # TSAs differ in returning a bare token vs a full response — try both forms.
      if "$TS_OPENSSL" ts -verify -digest "$MANIFEST_SHA" -in manifest.tsr -token_in -CAfile "$TSA_CHAIN" >/dev/null 2>&1 \
         || "$TS_OPENSSL" ts -verify -digest "$MANIFEST_SHA" -in manifest.tsr -CAfile "$TSA_CHAIN" >/dev/null 2>&1; then
        ok "RFC 3161 timestamp token verified against the manifest hash."
      else
        bad "the RFC 3161 token DID NOT verify over the manifest hash."
      fi
    else
      warn "eidas_validation.json (the token's certificate chain) is missing; cannot verify manifest.tsr offline."
    fi
    [ -n "$TSA_CHAIN" ] && rm -f "$TSA_CHAIN"
  fi
else
  echo "  No manifest.tsr in this package. Nothing to check."
fi
echo

echo "Result: $PASS passed, $FAIL failed, $WARN warnings."
if [ "$FAIL" -gt 0 ]; then
  echo "VERIFICATION FAILED. Do not rely on this package until the failures are explained."
  exit 1
fi
if [ "$WARN" -gt 0 ]; then
  echo "Verification passed, with the warnings noted above."
  exit 0
fi
echo "Verification passed."
exit 0
