Fingerprint validation reads evidence content; RIP-309 rotation unified to one algorithm - #8065
Open
Scottcjn wants to merge 2 commits into
Open
Fingerprint validation reads evidence content; RIP-309 rotation unified to one algorithm#8065Scottcjn wants to merge 2 commits into
Scottcjn wants to merge 2 commits into
Conversation
…ation
Fix A: validate_fingerprint_data only checked that evidence EXISTED, never
what it said. A payload of {"passed": true, "data": {"vm_indicators":
["qemu", "hypervisor"]}} was accepted because vm_indicators was present and
the verdict was not literally False. Non-empty vm_indicators (or
is_likely_vm) now rejects regardless of the claimed verdict. The == False
verdict comparisons for anti_emulation, clock_drift and rom_fingerprint are
now "is not True" so an omitted verdict no longer skips both branches.
Bool-format C miner checks are untouched. Same pattern closed for the other
checks: a check claiming pass while its data records fail_reason(s) is
rejected, and the Phase 4 hard-failure scan no longer hides behind the
client-supplied all_passed flag.
Fix B: two divergent 4-of-6 rotation algorithms disagreed for the same
epoch. Enroll used sha256("rip-309:" + hash) with sorted per-name hashing
over a tuple containing simd_bias; settlement and rewards used
sha256(hash_bytes + b"measurement_nonce") with random.Random(seed).sample
over simd_identity names. Weight was granted against one subset and vetoed
against another. All selectors now delegate to the tested
rip_309_measurement_rotation.get_reward_active_fingerprint_checks, the
check-name tuples use the canonical simd_identity, and the stored checks
maps mirror simd_bias/simd_identity at evaluation. Settlement behavior is
bit-for-bit unchanged; the enroll-side subset changes to match it.
Not deployed anywhere. Tests: 29 new tests pass, full tests/ suite 3693
passed with only pre-existing failures, node/tests 1439 passed with the
identical pre-existing failure list as main.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Signed-off-by: Scott <scottbphone12@gmail.com>
Contributor
|
| Metric | Value |
|---|---|
| Trust Score | 49/100 |
| Certificate ID | BCOS-2aaf5a94 |
| Tier | L2 (not met) |
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
BCOS v2 Engine - Free & Open Source (MIT) - Elyan Labs
This was referenced Jul 27, 2026
Open
A Proxmox VM is attesting on production right now with fingerprint_passed=1, earning 0.8x, by submitting six bare true values and no evidence at all. The bool branch only rejected an explicit False, so true fell straight through. The earlier commit on this branch hardened the dict path and said in a comment that it deliberately left the bool path alone for C miners. That was the wrong call: it left the live exploit open and wrote the reasoning down as if it were intentional. Two independent reviews landed on the same point, that anti_emulation is not another measurement channel like cache timing. It is the VM gate, and the design says a VM earns nothing, so capable hardware must affirmatively pass it rather than merely not fail it. Capability is now decided by the server-derived device class rather than by which keys the client omitted. No data cannot distinguish cannot-measure from did-not-bother, and only the class can. The bare boolean stays valid for the 6502, i386, DOS and console-bridge classes, which genuinely cannot serialise a nested object. The old comment claimed a bare boolean would be flagged for reduced weight. No reduced weight was applied anywhere. Signed-off-by: Scott <scottbphone12@gmail.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.
Two contained security fixes in the node's fingerprint validation and RIP-309 rotation. Nothing here is deployed. No production server was touched. This is code plus tests only, and the rollout risk for Fix B is spelled out below so whoever deploys knows what changes on the live chain.
BCOS Checklist (Required For Non-Doc PRs)
# SPDX-License-Identifier: MITWhat Changed
Fix A: fingerprint validation now reads the evidence, not just its presence
validate_fingerprint_data()innode/rustchain_v2_integrated_v2.2.1_rip200.py. The 2026-02-02 hardening required evidence to EXIST but never looked at what it said. This payload was accepted:{"passed": true, "data": {"vm_indicators": ["qemu", "hypervisor"]}}has_evidencewas satisfied becausevm_indicatorswas present, andpassedwas not literallyFalse, so the server held the string "qemu" and never looked at it.Verified line numbers in the pre-fix file:
has_evidenceat 3523-3529 and the== Falseverdict check at 3534== Falseat 3562, rom_fingerprint== Falseat 3613elif isinstance(anti_emu_check, bool)at 3537 andelif isinstance(clock_check, bool)at 3572all_passed == FalsePhase 4 gate at 3619Changes:
vm_indicatorsnow rejects withvm_self_reported:<indicators>regardless of the claimed verdict.is_likely_vm: truelikewise. Honest-miner impact is none, and I verified that instead of assuming it:node/fingerprint_checks.pylines 629-638 always emitvm_indicatorsand set it to an empty list on real hardware (valid = len(vm_indicators) == 0).== Falsecomparisons becameis not True(anti_emulation, clock_drift, rom_fingerprint). Omitting thepassedkey used to skip both the pass branch and the fail branch. Distinct reasons:anti_emulation_no_verdict,clock_drift_no_verdict,rom_check_no_verdict. The bool-format branches at 3537/3572 are untouched and covered by tests, so the C-payload miners (Apple II, i386) keep working. Note: this is a strict identity check, so a client that sends"passed": 1instead of JSONtruewould now be rejected; JSON booleans decode to Python bools, so spec-compliant clients are unaffected.fail_reason(cache_timing, simd_identity, thermal_drift, instruction_jitter, rom, device_age_oracle, pico bridgefail_reasons). A check claiming pass while its own data records a failure is now rejected:check_self_reported_failure:<name>:<reason>. Honest clients only setfail_reasonwhen the check failed.detection_detailswith a forgedemulator_detected: falseis now rejected. The client only records details when it matched a known emulator ROM.all_passed == False, so a payload claimingall_passed: true(or omitting it) while embedding individually failed hard checks (thermal_drift, instruction_jitter, simd_identity) was returned as "valid". The per-check scan now runs regardless of the client's overall claim. cache_timing stays soft, clock_drift stays soft for vintage archs, exactly as before.all_passed: falsewith no failed check shown is rejected as internally inconsistent (all_passed_false_unattributed).Fix B: one RIP-309 rotation algorithm instead of three
Verified the divergence at these pre-fix locations:
node/rustchain_v2_integrated_v2.2.1_rip200.py):derive_measurement_nonceat 2188-2191 computedsha256("rip-309:" + hash);select_active_fingerprint_checksat 2194-2211 ranked by sorted per-name hash over the tuple at 2176-2183, which containedsimd_bias.finalize_epoch, same file): inline at 3989-3996,sha256(hash_bytes + b"measurement_nonce")intorandom.Random(seed).sample(...)over names containingsimd_identity.node/rip_200_round_robin_1cpu1vote.pyline 635): already delegated torip_309_measurement_rotation.get_reward_active_fingerprint_checks(same algorithm as settlement).rip_200_round_robin_1cpu1vote.pylines 41-65 carried its own duplicate of the legacy sorted-hash selector.So for the same epoch, enroll granted weight against one 4-of-6 subset and settlement vetoed against a different one. Measured agreement between the two algorithms over 10,000 random hashes: 673/10,000 = 6.7%, right at the expected 1/15 for independent 4-of-6 draws. They disagreed 93.3% of the time.
I verified the claim that
get_reward_active_fingerprint_checksis the tested one before choosing it:node/tests/test_rip309_fingerprint_rotation.pygolden-tests it against the inline settlement algorithm (line 127) and its empty-hash fallback (line 133). It is also what the live reward path already uses. So it wins, and everything else now delegates to it:select_active_fingerprint_checksandderive_measurement_noncedelegate torip_309_measurement_rotation. The T3.6 fail-closed behavior is preserved and slightly extended: empty, all-zeros, and now also non-hex previous hashes activate all six checks.finalize_epoch: the inline copy is replaced by a call to the same helper. Bit-for-bit identical selection (test proves it), so settlement behavior does not change.rip_200_round_robin_1cpu1vote.py: the duplicate legacy selector now delegates too.simd_identity(what the miners actually emit, perfingerprint_checks.pyline 866, and whatrip_309_measurement_rotation.ALL_FP_CHECKSuses). Thesimd_biasalias from the RIP-309 issue text keeps working: the integrated node's_fingerprint_checks_mapalready mirrored both names, and the storedfingerprint_checks_jsonmaps consulted infinalize_epoch(pre-fix line 4022) andcalculate_epoch_rewards_time_agednow mirrorsimd_bias <-> simd_identitybefore evaluation. The alias map inrip_309_measurement_rotation.pylines 42-45 was confirmed present.node/tests/test_rip309_integrated_simd_alias.pyhard-coded the oldsimd_biasname in the epoch-0 rotation; updated to the canonical name and extended to cover the alias in both directions.Rollout risk for Fix B (read before deploying)
test_enroll_selector_matches_old_finalize_epoch_inline_algorithm.epoch_enrolluses INSERT OR IGNORE, first enrollment wins. The new subset applies to enrollments after deploy.simd_biasAND whose SIMD check failed AND with simd in the active set. Deployed miners emitsimd_identity(fingerprint_checks.pyline 866), so expected real-world impact is zero; a spoofing client renaming its failed check tosimd_biasto dodge the veto now gets caught.Testing / Evidence
New tests (29):
That includes the headline pair: the QEMU-self-reporting payload with a forged
passed: trueis rejected (vm_self_reported:['qemu', 'hypervisor']), and a clean payload shaped like realfingerprint_checks.pyoutput on honest hardware still returnsvalid.Full suites, compared against a clean checkout of main to separate my impact from pre-existing failures:
python3 -m py_compilepasses on both changed node files and all three test files.Not deployed. The four production nodes are still running the old code until someone reviews this and rolls it out with the enroll-subset change above in mind.