fix(circuit): strict bit-decomposition for path indices (#46)#47
Closed
adamkrellenstein wants to merge 1 commit into
Closed
fix(circuit): strict bit-decomposition for path indices (#46)#47adamkrellenstein wants to merge 1 commit into
adamkrellenstein wants to merge 1 commit into
Conversation
…ces (#46) The Merkle path-selection indices were derived with the non-strict `to_bits_le`, which only enforces `Σ bitᵢ·2ⁱ ≡ x (mod p)` — not canonicity. Since the Pallas scalar modulus is just above 2^254 while NUM_BITS = 255 (and p ≡ 1 mod 2^32), nearly every value admits a second valid 255-bit decomposition `x + p` whose low bits encode `index + 1`. A malicious prover could witness that decomposition to open leaf `idx+1` instead of the challenged `idx` (file tree), or steer the aggregation path to `index+1` while the public scalar stays `index`. Switch the two index-derivation sites in the production circuit (synth.rs: `challenge_with_idx` and `ledger_index_public`) to `to_bits_le_strict`, which enforces the canonical in-field representation. Completeness is preserved (honest values are canonical); the idx+1 opening is closed. Verified: build + api_functionality, circuit_wiring, depth_zero_cheat_regression, and the security_{malicious_prover,challenge_distribution,ledger,negative_cases} suites all pass. Note: the constant-size circuit variant (circuit/formal_components.rs, being rewritten in #44) has the same pattern at its challenge/ledger sites and needs the identical fix folded into that work.
Merging this PR will not alter performance
Comparing Footnotes
|
Contributor
Author
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.
Closes #46.
The Merkle path-selection indices were derived with the non-strict
to_bits_le, which only enforcesΣ bitᵢ·2ⁱ ≡ x (mod p)— not canonicity. Because the Pallas scalar modulus sits just above 2²⁵⁴ whileNUM_BITS = 255(andp ≡ 1 mod 2³²), nearly every value admits a second valid 255-bit decompositionx + pwhose low bits encodeindex + 1. A malicious prover could witness that to open leafidx+1instead of the challengedidx(file tree), or steer the aggregation path toindex+1while the public scalar staysindex.Fix: the two index-derivation sites in the production circuit (
synth.rs—challenge_with_idxandledger_index_public) now useto_bits_le_strict, which enforces the canonical in-field representation. Completeness is preserved (honest values are canonical); the idx+1 opening is closed.The non-index
depth_publicdecomposition is intentionally left asto_bits_le— it feeds active-flag/depth gating, not a path index, so it is not exploitable (per the audit).Verified: build +
api_functionality,circuit_wiring,depth_zero_cheat_regression, andsecurity_{malicious_prover,challenge_distribution,ledger,negative_cases}all pass.The constant-size circuit variant (
formal_components.rs) has the same pattern; the matching fix is pushed to the #44 branch.🤖 Generated with Claude Code
Note
High Risk
Directly patches a malicious-prover soundness issue in proof-of-retrievability path verification; incorrect constraints would break cryptographic guarantees.
Overview
Closes a soundness gap in the production Nova PoR circuit (
synth.rs) where Merkle path-selection bits were derived with non-strict little-endian decomposition.challenge_with_idx(file-tree path) andledger_index_public(aggregation path) now useto_bits_le_strict, so the low bits used as indices are the canonical in-field representation. Non-strict decomposition only enforces congruence mod p; with a 255-bit width and modulus just above 2²⁵⁴, a prover could witness an alternate decomposition whose low bits encode index+1, opening the wrong file leaf or steering the aggregation path while public scalars still match the challenged index.depth_publicremains onto_bits_lebecause it drives depth gating, not path indices (not treated as exploitable here). Comments reference issue #46.Reviewed by Cursor Bugbot for commit 41d7dea. Bugbot is set up for automated code reviews on this repo. Configure here.