git / moonbite-network / scripts/release/verify-release.sh · raw

#!/usr/bin/env bash# Verify a downloaded MoonBite release artifact.##   scripts/release/verify-release.sh <artifact> [SHA256SUMS.txt] [--pub deploy/moonbite-release.pub] [--owner moonbitecoin]## Checks, in order:#   1. sha256   - the artifact's hash appears in SHA256SUMS.txt under its own name   (REQUIRED)#   2. minisign - SHA256SUMS.txt.minisig verifies against the pinned public key      (when minisign is installed and the key is not the placeholder)#   3. sigstore - `gh attestation verify <artifact> --owner <owner>` passes          (when gh is installed)## Exit 0 only when every check that could run passed. A check that could not run# (tool missing, placeholder key, no .minisig) is reported as SKIP and does not# fail the result, but the summary says what was and was not proven.# Works in bash on Linux, macOS and Git Bash. No emoji; plain PASS/FAIL/SKIP.set -euo pipefailusage() { sed -n '2,15p' "$0" | sed 's/^# \{0,1\}//'; exit 2; }ARTIFACT=""; SUMS=""; PUB=""; OWNER="moonbitecoin"while [ $# -gt 0 ]; do  case "$1" in    --pub)   PUB="$2"; shift 2 ;;    --owner) OWNER="$2"; shift 2 ;;    -h|--help) usage ;;    -*) echo "unknown option: $1"; usage ;;    *) if [ -z "$ARTIFACT" ]; then ARTIFACT="$1"; elif [ -z "$SUMS" ]; then SUMS="$1"; else usage; fi; shift ;;  esacdone[ -n "$ARTIFACT" ] || usage[ -f "$ARTIFACT" ] || { echo "FAIL  artifact not found: $ARTIFACT"; exit 1; }DIR="$(cd "$(dirname "$ARTIFACT")" && pwd)"NAME="$(basename "$ARTIFACT")"[ -n "$SUMS" ] || SUMS="$DIR/SHA256SUMS.txt"if [ -z "$PUB" ]; then  HERE="$(cd "$(dirname "$0")" && pwd)"  for c in "$DIR/moonbite-release.pub" "$HERE/../../deploy/moonbite-release.pub"; do    [ -f "$c" ] && { PUB="$c"; break; }  donefipass=0; fail=0; skip=0ok()   { echo "PASS  $*"; pass=$((pass+1)); }bad()  { echo "FAIL  $*"; fail=$((fail+1)); }skp()  { echo "SKIP  $*"; skip=$((skip+1)); }sha256_of() {  if command -v sha256sum >/dev/null 2>&1; then sha256sum "$1" | cut -d' ' -f1  elif command -v shasum >/dev/null 2>&1; then shasum -a 256 "$1" | cut -d' ' -f1  else openssl dgst -sha256 "$1" | awk '{print $NF}'; fi}echo "artifact : $ARTIFACT"echo "manifest : $SUMS"echo "pubkey   : ${PUB:-<none found>}"echo# 1. sha256 ------------------------------------------------------------------if [ ! -f "$SUMS" ]; then  bad "sha256: manifest not found ($SUMS). Download SHA256SUMS.txt from the same release."else  ACTUAL="$(sha256_of "$ARTIFACT" | tr 'A-F' 'a-f')"  # "<hex>  name" or "<hex> *name"; match on basename  EXPECTED="$(grep -Ei "^[0-9a-f]{64}[[:space:]]+\*?(.*/)?${NAME//./\\.}[[:space:]]*$" "$SUMS" | head -1 | awk '{print tolower($1)}' || true)"  if [ -z "$EXPECTED" ]; then    bad "sha256: '$NAME' is not listed in $SUMS"  elif [ "$EXPECTED" = "$ACTUAL" ]; then    ok "sha256: $ACTUAL matches manifest"  else    bad "sha256: MISMATCH  manifest=$EXPECTED  actual=$ACTUAL  -- do not run this file"  fifi# 2. minisign ------------------------------------------------------------------KEYLINE=""if [ -n "$PUB" ] && [ -f "$PUB" ]; then  KEYLINE="$(grep -v '^[[:space:]]*#' "$PUB" | grep -v '^[[:space:]]*$' | grep -v '^untrusted comment' | head -1 || true)"fiif ! command -v minisign >/dev/null 2>&1; then  skp "minisign: tool not installed (https://jedisct1.github.io/minisign/)"elif [ -z "$KEYLINE" ] || [ "${KEYLINE#REPLACE_ME}" != "$KEYLINE" ]; then  skp "minisign: release public key is still a placeholder (key ceremony pending, deploy/RELEASE-SIGNING.md)"elif [ ! -f "$SUMS.minisig" ]; then  skp "minisign: no $(basename "$SUMS").minisig next to the manifest"elif minisign -Vm "$SUMS" -P "$KEYLINE" >/dev/null 2>&1; then  ok "minisign: $(basename "$SUMS").minisig verifies against pinned key"else  bad "minisign: signature on $(basename "$SUMS") does NOT verify -- manifest may be forged"fi# 3. Sigstore attestation ------------------------------------------------------if ! command -v gh >/dev/null 2>&1; then  skp "sigstore: GitHub CLI (gh) not installed; cannot check build provenance"else  if OUT="$(gh attestation verify "$ARTIFACT" --owner "$OWNER" 2>&1)"; then    ok "sigstore: attestation verified for owner '$OWNER' (built by GitHub Actions from the public repo)"    echo "$OUT" | grep -Ei 'repo|workflow|ref' | sed 's/^/        /' || true  else    case "$OUT" in      *"not logged"*|*"auth login"*|*"authentication"*|*"Bad credentials"*|*"HTTP 401"*)        skp "sigstore: gh is not authenticated or its token is invalid (run 'gh auth login'); attestation not checked" ;;      *)        bad "sigstore: no valid attestation for this file under owner '$OWNER'"        echo "$OUT" | tail -5 | sed 's/^/        /' ;;    esac  fifi# Summary ----------------------------------------------------------------------echoecho "result: $pass passed, $fail failed, $skip skipped"if [ "$fail" -gt 0 ]; then  echo "VERDICT: FAIL - do not use this artifact."  exit 1fiif [ "$pass" -eq 0 ]; then  echo "VERDICT: NOTHING PROVEN - no check could run."  exit 1fiif [ "$skip" -gt 0 ]; then  echo "VERDICT: PASS (with $skip check(s) skipped - integrity shown; see SKIP lines for what was not proven)"else  echo "VERDICT: PASS - integrity, authenticity and build provenance all verified."fiexit 0