feat(windows-miner): Ed25519 signing (canonical-JSON v3)#6432
Merged
Conversation
Ports the signing pattern from the Linux dev miner into the canonical Windows miner. Without this, every Windows attestation goes through the unsigned-flow path, which is vulnerable to wallet hijack via MITM (server has no cryptographic binding between wallet field and sender). With this, Windows miners match Linux miners on the signed-flow security that PR #6426 enabled server-side. Three small additions: 1. Top-of-file: try-import miner_crypto with CRYPTO_AVAILABLE flag. 2. RustChainMiner.__init__: generate/load Ed25519 keypair via get_or_create_keypair() so identity persists across reinstalls. 3. Right before /attest/submit POST: sign canonical JSON of the attestation dict, attach signature + public_key + signature_type fields. Server reproduces the same canonical bytes by stripping those three fields and verifying with the Ed25519 verify path. Backward-compatible: if PyNaCl isn't installed or miner_crypto.py is missing, CRYPTO_AVAILABLE=False and the miner falls back to the legacy sha512 pseudo-signature path. Server accepts both (PR #6426 canonical-JSON OR legacy 4-field MAC). Supporting changes: - requirements-miner.txt: added PyNaCl>=1.5.0 - rustchain_miner_setup.bat: updated MINER_SHA256 + adds miner_crypto.py download with a clear "warning: falling back to unsigned mode" notice if the file isn't fetched. This unblocks v3.1.0-miner release with the v3 fingerprint format AND signed attestation flow. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Contributor
✅ BCOS v2 Scan Results
What does this mean?The BCOS (Beacon Certified Open Source) engine scans for:
BCOS v2 Engine - Free & Open Source (MIT) - Elyan Labs |
PR #6432 added signing for /attest/submit but the separate /epoch/enroll endpoint was still going unsigned, producing "[ENROLL/SIG] UNSIGNED enrollment accepted ... upgrade miner to signed flow" warnings. This commit signs the enrollment too. Server expects a 3-field MAC "(miner_pubkey|miner_id|epoch)" verified against the SAME Ed25519 key used during attestation (server cross-checks via signing_pubkey column). - enroll() fetches current epoch from /epoch before signing - Builds the 3-field MAC and signs with miner_crypto.sign_payload - Attaches signature + public_key to enrollment payload - Graceful fallback: if PyNaCl missing OR /epoch fetch fails, sends payload unsigned (server accepts with warning, no regression) Updated MINER_SHA256 in rustchain_miner_setup.bat. Closes the last wallet-hijack surface for v3.1.x Windows miners. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
crystal-tensor
approved these changes
May 28, 2026
Contributor
crystal-tensor
left a comment
There was a problem hiding this comment.
✅ Code Review: APPROVED
Summary
Adds Ed25519 signing to Windows miner, replacing the sha512 pseudo-signature. Signs attestation payloads with real Ed25519 keys via PyNaCl, with graceful fallback to legacy sha512 for installs without PyNaCl.
Changes Reviewed
-
miners/windows/miner_crypto.py(new, +174):- ✅ Ed25519 key generation via PyNaCl (
SigningKey.generate()) - ✅ Keystore: XOR-obscured private key with machine entropy (not full encryption, but better than plaintext)
- ✅
save_keystore(): XOR with machine-id entropy, chmod 0600 - ✅
load_keystore(): Reverse XOR to recover private key - ✅
get_or_create_keypair(): Load existing or generate new, validates key integrity - ✅
sign_payload(): Ed25519 sign → hex signature - ✅
verify_signature(): Ed25519 verify - ✅
canonical_json(): Sorted keys, no whitespace (deterministic for signing) - ✅ Graceful fallback when PyNaCl unavailable
- ✅ Demo mode with generate/sign/verify/tamper test
- ✅ Ed25519 key generation via PyNaCl (
-
miners/windows/rustchain_windows_miner.py(+70):- ✅ Imports
miner_cryptowith graceful ImportError fallback - ✅ Generates/loads keypair on miner init
- ✅ Signs attestation payload BEFORE adding signature fields (correct canonical ordering)
- ✅ Adds
signature,public_key,signature_typefields to attestation - ✅ Legacy sha512 fallback when PyNaCl unavailable
- ✅ Signed enrollment with epoch query
- ✅ Imports
-
miners/windows/requirements-miner.txt(+1): Adds PyNaCl dependency -
miners/windows/rustchain_miner_setup.bat(+19/-1): Adds PyNaCl install step
Security Assessment
- ✅ Critical improvement: Replaces sha512 pseudo-signature with real Ed25519
- ✅ Wallet-hijack protection: MITM can't forge signatures without private key
- ✅ At-rest key protection: XOR with machine entropy (not encryption, but better than plaintext)
- ✅ File permissions: 0600 on keystore file
- ✅ Canonical JSON: Deterministic signing (sorted keys, no whitespace)
- ✅ Graceful fallback: Legacy mode for miners without PyNaCl
Minor Notes (non-blocking)
- XOR obscuration is NOT encryption — determined attacker with disk access can recover the key. Consider proper encryption (e.g., Fernet) in a future PR.
- Machine entropy fallback ("fallback-no-machine-id") is weak — but acceptable for mining use case.
Result: APPROVED ✅
Reviewed by QClaw AI Agent
Bounty claim: 3-25 RTC per CONTRIBUTING.md
This was referenced May 28, 2026
Scottcjn
added a commit
that referenced
this pull request
May 30, 2026
…6523) The canonical Linux miner at miners/linux/rustchain_linux_miner.py had no signing infrastructure at all. Every attestation and enrollment went through the unsigned-flow path, which is vulnerable to wallet hijack via MITM (server has no cryptographic binding between wallet field and sender). PR #6432 closed that gap for Windows; this PR brings Linux to parity. Three additions matching the Windows pattern: 1. Top-of-file: try-import miner_crypto with CRYPTO_AVAILABLE flag. Falls back to unsigned (server accepts with WARNING) when PyNaCl OR miner_crypto.py is missing — no behavior regression. 2. LocalMiner.__init__: generate/load Ed25519 keypair via get_or_create_keypair() so identity persists across reinstalls. 3. In attest(): sign canonical JSON of attestation BEFORE the /attest/submit POST. Attaches signature + public_key + signature_type. Server (PR #6426) strips those three fields and re-canonicalizes for verification. 4. In enroll(): fetch current epoch from /epoch, then sign the 3-field MAC "miner_pubkey|miner_id|epoch" — the server's expected format from rustchain_v2_integrated_v2.2.1_rip200.py line ~4155. Server cross-checks the pubkey matches its miner_attest_recent.signing_pubkey record. Plus: miners/linux/miner_crypto.py copied in (same module already shipped in miners/windows/ via PR #6432). Depends only on PyNaCl + stdlib. Live-verified on dev miner deployment: T40 (t40-thinkpad-banias), POWER8 (power8-s824-sophia, replaced a literal "0"*128 mock-sig path), Victus (victus-x86-scott), C4130 (modern-sophia-Pow-9862e3be) all now Ed25519- signed end-to-end. Server log no longer emits [ENROLL/SIG] UNSIGNED warnings for any of those wallets. Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
The canonical Windows miner currently goes through the unsigned-flow path on every attestation. Server has no cryptographic binding between the wallet field and the sender, so a successful MITM (TLS strip / compromised cert pin) can substitute a different wallet mid-flight. PR #6426 enabled server-side signed-flow on the v3 canonical-JSON scheme; this PR makes the Windows miner produce that.
Changes
miners/windows/rustchain_windows_miner.py__init__, sign canonical JSON before/attest/submitPOSTminers/windows/miner_crypto.py/home/scott/tmp_rustchain/. Ed25519 keypair gen + sign + verify helpers. Depends only on PyNaCl + stdlib.miners/windows/requirements-miner.txtPyNaCl>=1.5.0miners/windows/rustchain_miner_setup.batMINER_SHA256; addedminer_crypto.pydownload with fail-soft warning if not fetchedBackward compatibility
If PyNaCl isn't installed OR
miner_crypto.pyis missing,CRYPTO_AVAILABLE=Falseand the miner falls back to the legacysha512pseudo-signature path. Server accepts both:[ENROLL/SIG] UNSIGNED enrollment accepted ... upgrade miner to signed flowwarningSo upgrading is opt-in via PyNaCl install; existing un-PyNaCl deploys keep working.
Verification
py_compileclean on bothrustchain_windows_miner.pyandminer_crypto.py/home/scott/tmp_rustchain/rustchain_linux_miner.py) line-for-linesignature+public_key+signature_typestripped before re-canonicalization)Why now
User caught the issue mid-release: unsigned flow IS spoofable via MITM, and the previously-published v3.0.0-miner bundle didn't address it. v3.0.0 release was yanked; this PR is the basis for v3.1.0 which will properly sign every attestation.
🤖 Generated with Claude Code