feat(mining): enforce xmrig signatures + fix multi-platform checksum/asset bugs

Now that the release publishes a valid .sig per archive (verified against the pinned key for
linux/win/macOS), enable enforcement and fix two bugs that the newer multi-platform release
(v6.25.3, which added a macOS build) exposed:

- kXmrigRequireSignature = true: refuse any install whose release doesn't publish a valid
  ed25519 signature over the archive. Verified live end-to-end against the signed v6.25.3
  (archive SHA-256 + signature -> install).
- Drop the redundant inner-binary SHA-256 check. It keyed on the inner filename, but both the
  linux and macOS archives contain a binary literally named "xmrig", so the two "xmrig (…)"
  checksum lines collided in the map and the linux install compared against the macOS hash ->
  spurious "could not verify" failure. The whole archive is already verified (SHA-256 +
  signature), so every extracted member is authentic by transitivity — the per-member check
  added nothing but ambiguity.
- Fix the macOS platform token: the asset is named "...-macos-x86_64.zip", not "...-macos-x64",
  so selectXmrigAsset never matched it. currentXmrigPlatformToken() now returns "macos-x86_64"
  on Intel macs (arm64 has no build -> Unavailable). Added a matcher test for the macOS naming.

Both variants build; suite stable (0 failures / multiple runs); live require-mode install verified.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-07 09:29:37 -05:00
parent 85b53baeaf
commit b9881278af
4 changed files with 16 additions and 18 deletions

View File

@@ -321,8 +321,11 @@ void XmrigUpdater::runInstall(std::string targetDir)
}
}
// 3. Extract the wanted binaries (flatten the versioned subdir), verifying the miner binary's
// SHA-256 in memory before writing the executable to disk.
// 3. Extract the wanted binaries (flatten the versioned subdir). No per-member hash check is
// needed: the whole archive was already verified above (SHA-256, and the ed25519 signature
// when a key is pinned), so every member is authentic by transitivity. (A per-member check
// keyed on the inner filename is also ambiguous — e.g. both the linux and macOS archives
// contain a binary literally named "xmrig".)
setProgress(State::Extracting, "Installing miner…");
const std::vector<std::string> wanted = xmrigExtractBasenames(token);
const std::string minerName = wanted.front(); // "xmrig" / "xmrig.exe"
@@ -350,17 +353,6 @@ void XmrigUpdater::runInstall(std::string targetDir)
void* mem = mz_zip_reader_extract_to_heap(&zip, i, &outSize, 0);
if (!mem) { failed = true; break; }
// For the miner binary, verify the inner-binary SHA-256 before writing the executable.
if (base == minerName) {
const std::string actual = sha256Hex(mem, outSize);
const auto it = checksums.find(minerName);
if (it == checksums.end() || actual != it->second) {
mz_free(mem);
failed = true;
break;
}
}
// Atomic write: temp file in target dir, then rename over the destination.
const std::string finalPath = (fs::path(targetDir) / base).string();
const std::string tmpPath = finalPath + ".tmp";

View File

@@ -54,7 +54,7 @@ struct XmrigRelease {
// kXmrigRequireSignature=true to additionally refuse installs that publish no signature.
inline constexpr const char* kXmrigSignaturePublicKeyBase64 =
"j/9M+0E8NgcudP1q+23ar5uzwAFzQled8TtkFMaou6Q=";
inline constexpr bool kXmrigRequireSignature = false; // soft rollout: verify .sig if present
inline constexpr bool kXmrigRequireSignature = true; // enforced: refuse installs without a valid .sig
// ── Pure helpers (no I/O; unit-tested) ───────────────────────────────────────

View File

@@ -77,9 +77,9 @@ std::string currentXmrigPlatformToken()
return "win-x64";
#elif defined(__APPLE__)
#if defined(__aarch64__) || defined(__arm64__)
return "macos-arm64";
return "macos-arm64"; // no arm64 build published yet -> resolves to Unavailable
#else
return "macos-x64";
return "macos-x86_64"; // matches the release asset naming (note: not "macos-x64")
#endif
#elif defined(__linux__)
#if defined(__aarch64__)

View File

@@ -3998,10 +3998,16 @@ void testXmrigAssetSelection()
EXPECT_TRUE(linux != win);
EXPECT_TRUE(rel.assets[linux].name.find("linux-x64.zip") != std::string::npos);
EXPECT_TRUE(rel.assets[win].name.find("win-x64.zip") != std::string::npos);
// No macOS build is published -> graceful "not found".
EXPECT_EQ(selectXmrigAsset(rel, "macos-x64"), -1);
// No macOS build in this fixture -> graceful "not found".
EXPECT_EQ(selectXmrigAsset(rel, "macos-x86_64"), -1);
EXPECT_EQ(selectXmrigAsset(rel, "macos-arm64"), -1);
EXPECT_EQ(selectXmrigAsset(rel, ""), -1);
// The matcher handles the macOS asset naming ("macos-x86_64", not "macos-x64").
XmrigRelease mac; mac.ok = true; mac.tag = "v6.25.3";
mac.assets.push_back({"drg-xmrig-6.25.3-macos-x86_64.zip", "https://x/m", 100});
EXPECT_TRUE(selectXmrigAsset(mac, "macos-x86_64") >= 0);
EXPECT_EQ(selectXmrigAsset(mac, "macos-x64"), -1); // the wrong token must NOT match
}
void testXmrigChecksumParsing()