#!/usr/bin/env bash # anchor.sh — Hash, PGP-sign, and OpenTimestamps-anchor a single artifact. # # Usage: ./anchor.sh path/to/artifact.eml # Effect: Produces .sha256, .asc, .ots in the same directory. # # Requirements: # gpg (with the canonical signing key already imported and the fingerprint # configured below or in env var GOYD_PGP_FPR) # sha256sum (coreutils) # ots (pip install opentimestamps-client) # # Resilience notes: # - We sign the hash FILE, not the artifact directly. This is so a verifier # can cheaply check "this artifact is the one I anchored" without re-reading # a multi-MB file. # - We OTS-anchor the hash FILE for the same reason: small file, fast Bitcoin # attestation, no need to push the artifact body anywhere. set -euo pipefail if [[ $# -ne 1 ]]; then echo "usage: $0 " >&2 exit 64 fi ART="$1" if [[ ! -f "$ART" ]]; then echo "not a file: $ART" >&2 exit 66 fi # CHOOSE ONE canonical signing key. The user currently has two fingerprints in # circulation; reconcile before using this script in production. FPR="${GOYD_PGP_FPR:-4A041F506D894F5EE391743864878B56A2EB2D11}" DIR="$(dirname "$ART")" BASE="$(basename "$ART")" HASH_FILE="${ART}.sha256" SIG_FILE="${ART}.asc" OTS_FILE="${HASH_FILE}.ots" # 1. Hash ( cd "$DIR" && sha256sum "$BASE" ) > "$HASH_FILE" echo "wrote $HASH_FILE" # 2. PGP-sign the artifact (detached, armored) gpg --local-user "$FPR" --armor --detach-sign --output "$SIG_FILE" "$ART" echo "wrote $SIG_FILE" # 3. PGP-sign the hash file too (so a verifier can cheaply confirm the binding) gpg --local-user "$FPR" --armor --detach-sign --output "${HASH_FILE}.asc" "$HASH_FILE" echo "wrote ${HASH_FILE}.asc" # 4. OpenTimestamps-anchor the hash file ots stamp "$HASH_FILE" echo "wrote $OTS_FILE (pending Bitcoin confirmation, run 'ots upgrade' later)" echo echo "Verification commands a third party will run:" echo " sha256sum -c $HASH_FILE" echo " gpg --verify $SIG_FILE $ART" echo " gpg --verify ${HASH_FILE}.asc $HASH_FILE" echo " ots verify $OTS_FILE"