feat(mining): opt-in ed25519 signature verification for the xmrig updater (#1)
Closes the supply-chain gap the review flagged: today the archive and its SHA-256 share one trust root (the release body), so a compromised/edited release can ship an arbitrary binary that still "verifies". This adds authenticity via a detached ed25519 signature checked against a public key PINNED IN THE BINARY (not fetched), using libsodium's crypto_sign_verify_detached. Opt-in / soft rollout: - kXmrigSignaturePublicKeyBase64 in xmrig_updater.h is EMPTY by default -> signatures are not checked and behavior is unchanged (TLS + SHA-256 only). Paste the base64 public key to enable. - Once a key is pinned, an install verifies a "<archive>.sig" asset (base64/raw 64-byte ed25519 signature over the archive bytes) when present; kXmrigRequireSignature=true additionally refuses installs that publish no signature. - The check runs after the SHA-256 check, over the same already-read archive bytes; refuses on a missing key-but-required, unreachable .sig, or invalid signature. - verifyXmrigSignature + selectXmrigSignatureAsset are pure (libsodium only) and unit-tested: valid base64 + raw-64-byte signatures verify; tampered data, wrong key, and malformed/empty inputs all fail closed. Cross-tool interop verified (Python stdlib base64 == sodium base64). - scripts/sign-xmrig-release.sh: keygen / sign / pubkey helper (PyNaCl = same libsodium ed25519) to produce the .sig assets and the public key to pin. No behavior change until a key is pinned. Both variants build; suite passes; live worker re-verified (signatures off by default). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
72
scripts/sign-xmrig-release.sh
Executable file
72
scripts/sign-xmrig-release.sh
Executable file
@@ -0,0 +1,72 @@
|
||||
#!/usr/bin/env bash
|
||||
# Sign DRG-XMRig release archives for the wallet's in-app updater (opt-in ed25519 signatures).
|
||||
#
|
||||
# The wallet verifies a detached ed25519 signature over the EXACT archive bytes against a public
|
||||
# key pinned in src/util/xmrig_updater.h (kXmrigSignaturePublicKeyBase64). For each archive
|
||||
# <name>.zip this produces <name>.zip.sig containing the base64 ed25519 signature — upload that
|
||||
# .sig next to the .zip as a release asset.
|
||||
#
|
||||
# Keys are 32-byte ed25519: the secret key signs (keep OFFLINE), the public key goes in the wallet.
|
||||
#
|
||||
# Usage:
|
||||
# scripts/sign-xmrig-release.sh keygen [out-prefix] # -> <prefix>.ed25519.{key,pub.b64}
|
||||
# scripts/sign-xmrig-release.sh sign <secret.key> <file>... # -> <file>.sig per file
|
||||
# scripts/sign-xmrig-release.sh pubkey <secret.key> # print the base64 public key to pin
|
||||
#
|
||||
# Requires python3 with PyNaCl (pip install pynacl). PyNaCl uses the same libsodium primitives the
|
||||
# wallet verifies with, so signatures are guaranteed compatible.
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
die() { echo "error: $*" >&2; exit 1; }
|
||||
command -v python3 >/dev/null || die "python3 not found"
|
||||
python3 -c 'import nacl.signing' 2>/dev/null || die "PyNaCl missing — run: pip install pynacl"
|
||||
|
||||
cmd="${1:-}"; shift || true
|
||||
|
||||
case "$cmd" in
|
||||
keygen)
|
||||
prefix="${1:-drg-xmrig}"
|
||||
python3 - "$prefix" <<'PY'
|
||||
import sys, base64, nacl.signing
|
||||
prefix = sys.argv[1]
|
||||
sk = nacl.signing.SigningKey.generate()
|
||||
open(prefix + ".ed25519.key", "wb").write(bytes(sk))
|
||||
import os; os.chmod(prefix + ".ed25519.key", 0o600)
|
||||
pub_b64 = base64.standard_b64encode(bytes(sk.verify_key)).decode()
|
||||
open(prefix + ".ed25519.pub.b64", "w").write(pub_b64 + "\n")
|
||||
print("secret key : %s.ed25519.key (KEEP OFFLINE, mode 600)" % prefix)
|
||||
print("public key : %s.ed25519.pub.b64" % prefix)
|
||||
print()
|
||||
print("Pin this in src/util/xmrig_updater.h (kXmrigSignaturePublicKeyBase64):")
|
||||
print(" %s" % pub_b64)
|
||||
PY
|
||||
;;
|
||||
sign)
|
||||
[ $# -ge 2 ] || die "usage: sign <secret.key> <file>..."
|
||||
keyfile="$1"; shift
|
||||
for f in "$@"; do
|
||||
[ -f "$f" ] || die "no such file: $f"
|
||||
python3 - "$keyfile" "$f" <<'PY'
|
||||
import sys, base64, nacl.signing
|
||||
keyfile, f = sys.argv[1], sys.argv[2]
|
||||
sk = nacl.signing.SigningKey(open(keyfile, "rb").read())
|
||||
sig = sk.sign(open(f, "rb").read()).signature # 64-byte detached ed25519 signature
|
||||
open(f + ".sig", "w").write(base64.standard_b64encode(sig).decode() + "\n")
|
||||
print("signed: %s -> %s.sig" % (f, f))
|
||||
PY
|
||||
done
|
||||
echo "Upload each .sig as a release asset next to its archive."
|
||||
;;
|
||||
pubkey)
|
||||
[ $# -ge 1 ] || die "usage: pubkey <secret.key>"
|
||||
python3 - "$1" <<'PY'
|
||||
import sys, base64, nacl.signing
|
||||
sk = nacl.signing.SigningKey(open(sys.argv[1], "rb").read())
|
||||
print(base64.standard_b64encode(bytes(sk.verify_key)).decode())
|
||||
PY
|
||||
;;
|
||||
*)
|
||||
die "usage: $0 {keygen [prefix] | sign <secret.key> <file>... | pubkey <secret.key>}"
|
||||
;;
|
||||
esac
|
||||
Reference in New Issue
Block a user