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:
@@ -43,6 +43,18 @@ struct XmrigRelease {
|
||||
std::string error;
|
||||
};
|
||||
|
||||
// ── Release signature (opt-in supply-chain hardening) ────────────────────────
|
||||
//
|
||||
// Pinned ed25519 public key (base64, 32 bytes) used to verify a detached signature over the
|
||||
// downloaded archive. EMPTY by default = signature verification DISABLED (TLS + the release's
|
||||
// published SHA-256 are the only gates, unchanged behavior). To enable: paste the base64 public
|
||||
// key here and have the release publish a "<archive-name>.sig" asset containing the base64 (or
|
||||
// raw 64-byte) ed25519 signature of the archive bytes (see scripts/sign-xmrig-release.sh). Once a
|
||||
// key is set, an install verifies the signature when the .sig asset is present; set
|
||||
// kXmrigRequireSignature=true to additionally refuse installs that publish no signature.
|
||||
inline constexpr const char* kXmrigSignaturePublicKeyBase64 = "";
|
||||
inline constexpr bool kXmrigRequireSignature = false;
|
||||
|
||||
// ── Pure helpers (no I/O; unit-tested) ───────────────────────────────────────
|
||||
|
||||
// Parse the Gitea GET /releases/latest JSON into an XmrigRelease (ok=false + error on failure).
|
||||
@@ -68,6 +80,17 @@ std::string sha256Hex(const void* data, std::size_t len);
|
||||
// {"xmrig.exe", "WinRing0x64.sys"} on Windows. First entry is always the miner binary.
|
||||
std::vector<std::string> xmrigExtractBasenames(const std::string& platformToken);
|
||||
|
||||
// Index of the detached-signature asset for a given archive (name "<archive>.sig" or
|
||||
// "<archive>.minisig"), or -1 if none is published.
|
||||
int selectXmrigSignatureAsset(const XmrigRelease& release, const std::string& archiveName);
|
||||
|
||||
// Verify a detached ed25519 signature over `data`. `signatureFileContent` is the .sig asset body
|
||||
// (base64 of, or raw, a 64-byte ed25519 signature; whitespace tolerated). `pubKeyBase64` is the
|
||||
// base64 32-byte public key. Returns true only on a cryptographically valid signature.
|
||||
bool verifyXmrigSignature(const std::string& data,
|
||||
const std::string& signatureFileContent,
|
||||
const std::string& pubKeyBase64);
|
||||
|
||||
// ── Background worker ────────────────────────────────────────────────────────
|
||||
|
||||
class XmrigUpdater {
|
||||
|
||||
Reference in New Issue
Block a user