upleb.uk

Public git repos — served from a NIP-34 GRASP relay at git.upleb.uk

summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormtg0001 <106211915+mtg0001@users.noreply.github.com>2026-08-04 10:18:01 -0300
committerGitHub <noreply@github.com>2026-08-04 10:18:01 -0300
commitf6c5b813075cae4228d241a3567e70b210504749 (patch)
tree8d408438f3579a6a170be4d04cc68d33ab292782
parent530f91910a4eceb6705d1212fa0e3ebe8418c6a3 (diff)
Add release verification tooling (#108)
Co-authored-by: Gilcimar Duarte <ti@gpm.infra>
-rw-r--r--.gitignore1
-rw-r--r--Makefile14
-rw-r--r--README.md19
-rw-r--r--docs/release-verification.md62
-rwxr-xr-xscripts/make-release-checksums.sh78
-rwxr-xr-xscripts/verify-release.sh115
6 files changed, 287 insertions, 2 deletions
diff --git a/.gitignore b/.gitignore
index 1ec9099..111f7ba 100644
--- a/.gitignore
+++ b/.gitignore
@@ -13,6 +13,7 @@ Bitflash-*-windows.zip
13Bitflash-*.AppImage 13Bitflash-*.AppImage
14bitflash-node-*-x86_64 14bitflash-node-*-x86_64
15SHA256SUMS 15SHA256SUMS
16SHA256SUMS.asc
16 17
17# Runtime data / logs 18# Runtime data / logs
18debug.log 19debug.log
diff --git a/Makefile b/Makefile
index a9630ed..ea720f1 100644
--- a/Makefile
+++ b/Makefile
@@ -3,6 +3,8 @@
3# make linux build Bitflash-*-x86_64.AppImage 3# make linux build Bitflash-*-x86_64.AppImage
4# make windows build Bitflash-*-windows.zip (from MSYS2 UCRT64) 4# make windows build Bitflash-*-windows.zip (from MSYS2 UCRT64)
5# make tests build and run standalone unit tests 5# make tests build and run standalone unit tests
6# make checksums create SHA256SUMS for release assets
7# make verify-release TAG=v1.2.13
6# make clean remove build artifacts 8# make clean remove build artifacts
7 9
8ROOT := $(shell pwd) 10ROOT := $(shell pwd)
@@ -160,5 +162,15 @@ clean:
160tests: 162tests:
161 $(MAKE) -C src -f Makefile tests 163 $(MAKE) -C src -f Makefile tests
162 164
165checksums:
166 ./scripts/make-release-checksums.sh
167
168sign-checksums:
169 ./scripts/make-release-checksums.sh --sign $(if $(KEY),--local-user $(KEY),)
170
171verify-release:
172 ./scripts/verify-release.sh $(if $(TAG),$(TAG),latest)
173
163.PHONY: linux windows clean appimage \ 174.PHONY: linux windows clean appimage \
164 tests deps-linux deps-windows deps-apt deps-secp256k1 deps-randomx 175 tests checksums sign-checksums verify-release \
176 deps-linux deps-windows deps-apt deps-secp256k1 deps-randomx
diff --git a/README.md b/README.md
index 6601384..6446b3d 100644
--- a/README.md
+++ b/README.md
@@ -49,13 +49,30 @@ configuration — it connects automatically and starts syncing.
49 49
50**Windows:** extract the `-windows.zip` and run `Bitflash.exe`. 50**Windows:** extract the `-windows.zip` and run `Bitflash.exe`.
51 51
52Every release ships a `SHA256SUMS` covering both assets. Verifying takes a second 52Every release ships a `SHA256SUMS` covering the assets. Verifying takes a second
53and is worth doing: 53and is worth doing:
54 54
55```bash 55```bash
56sha256sum -c SHA256SUMS 56sha256sum -c SHA256SUMS
57``` 57```
58 58
59For newer signed releases, verify the checksum file itself first:
60
61```bash
62gpg --verify SHA256SUMS.asc SHA256SUMS
63sha256sum -c SHA256SUMS
64```
65
66The helper below downloads the release assets, verifies `SHA256SUMS.asc` when it
67is present, then checks the hashes:
68
69```bash
70scripts/verify-release.sh latest
71```
72
73See [release verification](docs/release-verification.md) for the full release
74audit flow and the maintainer signing step.
75
59**Keep your node current.** Consensus rules have changed since the first 76**Keep your node current.** Consensus rules have changed since the first
60releases — 1.2.1 fixed a bug that let anyone spend anyone's coins, and 1.2.2 77releases — 1.2.1 fixed a bug that let anyone spend anyone's coins, and 1.2.2
61added a per-block signature-operation cap. A node on an older build will accept 78added a per-block signature-operation cap. A node on an older build will accept
diff --git a/docs/release-verification.md b/docs/release-verification.md
new file mode 100644
index 0000000..ecacd5f
--- /dev/null
+++ b/docs/release-verification.md
@@ -0,0 +1,62 @@
1# Release Verification
2
3Bitflash releases publish binaries and a `SHA256SUMS` file. The checksum file
4proves that the file you downloaded matches the file the release page names.
5A detached signature, `SHA256SUMS.asc`, proves that the checksum file itself was
6signed by a trusted release key.
7
8That gives users two separate checks:
9
101. `gpg --verify SHA256SUMS.asc SHA256SUMS` checks who signed the checksums.
112. `sha256sum -c SHA256SUMS` checks the binaries against those checksums.
12
13## User Check
14
15From a shell with `curl` or `wget` and `sha256sum`:
16
17```bash
18scripts/verify-release.sh latest
19```
20
21For a specific release:
22
23```bash
24scripts/verify-release.sh v1.2.13
25```
26
27For release audits, require the signature:
28
29```bash
30scripts/verify-release.sh v1.2.13 --require-signature
31```
32
33Older releases may not have `SHA256SUMS.asc`. In that case the script warns and
34still checks file integrity. New release audits should use `--require-signature`.
35
36## Maintainer Flow
37
38After building release assets in the repository root:
39
40```bash
41scripts/make-release-checksums.sh --sign --local-user RELEASE_KEY_ID
42```
43
44Upload all built assets plus:
45
46```text
47SHA256SUMS
48SHA256SUMS.asc
49```
50
51Keep the private signing key offline or on a dedicated release machine. Publish
52the public key fingerprint in the release notes and keep using the same key for
53future releases unless there is a clearly announced rotation.
54
55## Why This Matters
56
57`SHA256SUMS` alone protects against a broken download, but not against someone
58replacing both a binary and the checksum file. Signing `SHA256SUMS` means an
59attacker must also have the release signing key to make the replacement verify.
60
61This is not reproducible builds yet. It is the smaller, immediate step that
62makes every release asset auditable by users before they run it.
diff --git a/scripts/make-release-checksums.sh b/scripts/make-release-checksums.sh
new file mode 100755
index 0000000..5b29e2d
--- /dev/null
+++ b/scripts/make-release-checksums.sh
@@ -0,0 +1,78 @@
1#!/usr/bin/env bash
2set -euo pipefail
3
4sign=0
5key=""
6
7usage() {
8 cat <<'EOF'
9Usage: scripts/make-release-checksums.sh [--sign] [--local-user KEYID]
10
11Creates SHA256SUMS for release assets in the current directory:
12 Bitflash-*-windows.zip
13 Bitflash-*-x86_64.AppImage
14 bitflash-node-*-x86_64
15
16With --sign, also creates SHA256SUMS.asc as a detached ASCII-armored GPG
17signature. Upload both files with the release assets.
18EOF
19}
20
21while [ "$#" -gt 0 ]; do
22 case "$1" in
23 --sign) sign=1 ;;
24 --local-user)
25 [ "$#" -ge 2 ] || { echo "--local-user needs a key id" >&2; exit 2; }
26 key="$2"
27 shift
28 ;;
29 -h|--help) usage; exit 0 ;;
30 *) echo "unknown option: $1" >&2; usage >&2; exit 2 ;;
31 esac
32 shift
33done
34
35if command -v sha256sum >/dev/null 2>&1; then
36 sha_cmd=(sha256sum)
37elif command -v shasum >/dev/null 2>&1; then
38 sha_cmd=(shasum -a 256)
39else
40 echo "missing required command: sha256sum or shasum" >&2
41 exit 1
42fi
43
44assets=()
45for pattern in \
46 "Bitflash-*-windows.zip" \
47 "Bitflash-*-x86_64.AppImage" \
48 "bitflash-node-*-x86_64"
49do
50 for file in $pattern; do
51 [ -e "$file" ] || continue
52 assets+=("$file")
53 done
54done
55
56if [ "${#assets[@]}" -eq 0 ]; then
57 echo "no release assets found in $(pwd)" >&2
58 exit 1
59fi
60
61printf '%s\n' "${assets[@]}" | LC_ALL=C sort | while IFS= read -r file; do
62 "${sha_cmd[@]}" "$file"
63done > SHA256SUMS
64
65echo "Wrote SHA256SUMS"
66
67if [ "$sign" -eq 1 ]; then
68 if ! command -v gpg >/dev/null 2>&1; then
69 echo "missing required command: gpg" >&2
70 exit 1
71 fi
72 gpg_args=(--armor --detach-sign --output SHA256SUMS.asc)
73 if [ -n "$key" ]; then
74 gpg_args+=(--local-user "$key")
75 fi
76 gpg "${gpg_args[@]}" SHA256SUMS
77 echo "Wrote SHA256SUMS.asc"
78fi
diff --git a/scripts/verify-release.sh b/scripts/verify-release.sh
new file mode 100755
index 0000000..23e71d1
--- /dev/null
+++ b/scripts/verify-release.sh
@@ -0,0 +1,115 @@
1#!/usr/bin/env bash
2set -euo pipefail
3
4repo="Bitflash-sh/bitflash"
5tag="${1:-latest}"
6require_signature=0
7
8usage() {
9 cat <<'EOF'
10Usage: scripts/verify-release.sh [tag|latest] [--require-signature]
11
12Downloads the release checksum file and every asset named in it, then verifies:
13 1. SHA256SUMS.asc, if present, against SHA256SUMS with gpg.
14 2. SHA256SUMS against the downloaded assets.
15
16Use --require-signature for release audits. Without it, older releases that
17ship only SHA256SUMS are checked for integrity and reported as unsigned.
18EOF
19}
20
21for arg in "${@:2}"; do
22 case "$arg" in
23 --require-signature) require_signature=1 ;;
24 -h|--help) usage; exit 0 ;;
25 *) echo "unknown option: $arg" >&2; usage >&2; exit 2 ;;
26 esac
27done
28
29if [ "$tag" = "-h" ] || [ "$tag" = "--help" ]; then
30 usage
31 exit 0
32fi
33
34need() {
35 if ! command -v "$1" >/dev/null 2>&1; then
36 echo "missing required command: $1" >&2
37 exit 1
38 fi
39}
40
41need sed
42need awk
43
44if command -v sha256sum >/dev/null 2>&1; then
45 sha_check=(sha256sum -c SHA256SUMS)
46elif command -v shasum >/dev/null 2>&1; then
47 sha_check=(shasum -a 256 -c SHA256SUMS)
48else
49 echo "missing required command: sha256sum or shasum" >&2
50 exit 1
51fi
52
53download() {
54 local url="$1"
55 local out="$2"
56 if command -v curl >/dev/null 2>&1; then
57 curl -fsSL "$url" -o "$out"
58 elif command -v wget >/dev/null 2>&1; then
59 wget -q "$url" -O "$out"
60 else
61 echo "missing required command: curl or wget" >&2
62 exit 1
63 fi
64}
65
66if [ "$tag" = "latest" ]; then
67 tag="$(download "https://api.github.com/repos/$repo/releases/latest" - |
68 sed -n 's/.*"tag_name"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p' |
69 head -1)"
70 if [ -z "$tag" ]; then
71 echo "could not resolve latest release tag" >&2
72 exit 1
73 fi
74fi
75
76workdir="$(mktemp -d "${TMPDIR:-/tmp}/bitflash-release-verify.XXXXXX")"
77cleanup() { rm -rf "$workdir"; }
78trap cleanup EXIT
79
80base_url="https://github.com/$repo/releases/download/$tag"
81
82echo "Verifying Bitflash $tag"
83echo "Working directory: $workdir"
84cd "$workdir"
85
86download "$base_url/SHA256SUMS" SHA256SUMS
87
88if download "$base_url/SHA256SUMS.asc" SHA256SUMS.asc 2>/dev/null; then
89 if ! command -v gpg >/dev/null 2>&1; then
90 echo "SHA256SUMS.asc exists, but gpg is not installed" >&2
91 exit 1
92 fi
93 gpg --verify SHA256SUMS.asc SHA256SUMS
94 echo "Signature OK"
95else
96 if [ "$require_signature" -eq 1 ]; then
97 echo "release does not publish SHA256SUMS.asc" >&2
98 exit 1
99 fi
100 echo "WARNING: no SHA256SUMS.asc found; checking hashes only"
101fi
102
103awk '{print $2}' SHA256SUMS | while IFS= read -r file; do
104 file="${file#\*}"
105 file="${file#./}"
106 [ -z "$file" ] && continue
107 case "$file" in
108 */*|..*) echo "refusing unexpected checksum path: $file" >&2; exit 1 ;;
109 esac
110 echo "Downloading $file"
111 download "$base_url/$file" "$file"
112done
113
114"${sha_check[@]}"
115echo "Release assets OK"