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:
@@ -264,7 +264,10 @@ void XmrigUpdater::runInstall(std::string targetDir)
|
||||
}
|
||||
if (cancel_requested_) { fs::remove(zipPath, ec); setProgress(State::Failed, "Cancelled."); return; }
|
||||
|
||||
// 2. Verify the archive SHA-256 against the published checksum (refuse on missing/mismatch).
|
||||
// 2. Verify the downloaded archive. Read it once, then (a) compare its SHA-256 to the published
|
||||
// checksum and (b) — when a signing key is pinned — verify a detached ed25519 signature over
|
||||
// the archive bytes against that key, so a checksum rewritten in a tampered release body is
|
||||
// not sufficient to install.
|
||||
setProgress(State::Verifying, "Verifying download…");
|
||||
{
|
||||
std::ifstream f(zipPath, std::ios::binary);
|
||||
@@ -279,6 +282,8 @@ void XmrigUpdater::runInstall(std::string targetDir)
|
||||
setProgress(State::Failed, "Could not read the downloaded archive.");
|
||||
return;
|
||||
}
|
||||
|
||||
// 2a. SHA-256 (integrity / transit corruption).
|
||||
const std::string actual = sha256Hex(bytes.data(), bytes.size());
|
||||
const auto it = checksums.find(asset.name);
|
||||
if (it == checksums.end()) {
|
||||
@@ -291,6 +296,29 @@ void XmrigUpdater::runInstall(std::string targetDir)
|
||||
setProgress(State::Failed, "Archive checksum mismatch — refusing to install (possible tampering).");
|
||||
return;
|
||||
}
|
||||
|
||||
// 2b. Detached signature (authenticity) — only when a public key is pinned (opt-in).
|
||||
const std::string pubKey = kXmrigSignaturePublicKeyBase64;
|
||||
if (!pubKey.empty()) {
|
||||
const int sigIdx = selectXmrigSignatureAsset(rel, asset.name);
|
||||
if (sigIdx < 0) {
|
||||
if (kXmrigRequireSignature) {
|
||||
fs::remove(zipPath, ec);
|
||||
setProgress(State::Failed, "No signature published for this release — refusing to install.");
|
||||
return;
|
||||
}
|
||||
DEBUG_LOGF("[xmrig-updater] no signature asset for %s; proceeding on checksum only\n",
|
||||
asset.name.c_str());
|
||||
} else {
|
||||
setProgress(State::Verifying, "Verifying signature…");
|
||||
const std::string sigContent = httpGet(rel.assets[sigIdx].downloadUrl);
|
||||
if (sigContent.empty() || !verifyXmrigSignature(bytes, sigContent, pubKey)) {
|
||||
fs::remove(zipPath, ec);
|
||||
setProgress(State::Failed, "Signature verification failed — refusing to install.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 3. Extract the wanted binaries (flatten the versioned subdir), verifying the miner binary's
|
||||
|
||||
Reference in New Issue
Block a user