Skip to content

feat(windows-miner): Ed25519 signing (canonical-JSON v3)#6432

Merged
Scottcjn merged 2 commits into
mainfrom
feat/windows-miner-ed25519-signing
May 28, 2026
Merged

feat(windows-miner): Ed25519 signing (canonical-JSON v3)#6432
Scottcjn merged 2 commits into
mainfrom
feat/windows-miner-ed25519-signing

Conversation

@Scottcjn
Copy link
Copy Markdown
Owner

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

File Change
miners/windows/rustchain_windows_miner.py +43 lines: try-import miner_crypto, init keypair in __init__, sign canonical JSON before /attest/submit POST
miners/windows/miner_crypto.py NEW — ported from /home/scott/tmp_rustchain/. Ed25519 keypair gen + sign + verify helpers. Depends only on PyNaCl + stdlib.
miners/windows/requirements-miner.txt + PyNaCl>=1.5.0
miners/windows/rustchain_miner_setup.bat Updated MINER_SHA256; added miner_crypto.py download with fail-soft warning if not fetched

Backward compatibility

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:

  • v3 canonical-JSON (this PR's output) — preferred
  • legacy 4-field MAC / sha512_legacy — accepted with [ENROLL/SIG] UNSIGNED enrollment accepted ... upgrade miner to signed flow warning

So upgrading is opt-in via PyNaCl install; existing un-PyNaCl deploys keep working.

Verification

  • py_compile clean on both rustchain_windows_miner.py and miner_crypto.py
  • Signing pattern matches the battle-tested Linux miner (/home/scott/tmp_rustchain/rustchain_linux_miner.py) line-for-line
  • Server-side acceptance verified in PR fix(attest): accept v3 canonical-JSON signature alongside legacy MAC #6426 (canonical-JSON path with signature + public_key + signature_type stripped before re-canonicalization)
  • Live test on a Windows miner once v3.1.0-miner release is cut and someone installs it

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

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>
@github-actions github-actions Bot added BCOS-L1 Beacon Certified Open Source tier BCOS-L1 (required for non-doc PRs) miner Miner client related size/L PR: 201-500 lines labels May 27, 2026
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 27, 2026

✅ BCOS v2 Scan Results

Metric Value
Trust Score 60/100
Certificate ID BCOS-449b9b6c
Tier L1 (met)

BCOS Badge

What does this mean?

The BCOS (Beacon Certified Open Source) engine scans for:

  • SPDX license header compliance
  • Known CVE vulnerabilities (OSV database)
  • Static analysis findings (Semgrep)
  • SBOM completeness
  • Dependency freshness
  • Test infrastructure evidence
  • Review attestation tier

Full report | What is BCOS?


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>
Copy link
Copy Markdown
Contributor

@crystal-tensor crystal-tensor left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ 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

  1. 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
  2. miners/windows/rustchain_windows_miner.py (+70):

    • ✅ Imports miner_crypto with graceful ImportError fallback
    • ✅ Generates/loads keypair on miner init
    • ✅ Signs attestation payload BEFORE adding signature fields (correct canonical ordering)
    • ✅ Adds signature, public_key, signature_type fields to attestation
    • ✅ Legacy sha512 fallback when PyNaCl unavailable
    • ✅ Signed enrollment with epoch query
  3. miners/windows/requirements-miner.txt (+1): Adds PyNaCl dependency

  4. 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

@Scottcjn Scottcjn merged commit 8f160d8 into main May 28, 2026
11 of 12 checks passed
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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

BCOS-L1 Beacon Certified Open Source tier BCOS-L1 (required for non-doc PRs) miner Miner client related size/L PR: 201-500 lines

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants