Repository navigation
feat(Crypto): Merkle trees with authentication paths and collision extraction - #1023
Conversation
…traction (#1001) Merkle tree hash over a keyed hash (Boneh-Shoup v0.6 Section 8.9; Merkle, CRYPTO '89, Section 6): root, authentication path, root recomputation, correctness of honest openings, and deterministic collision extraction from two openings of one position (the reduction behind Boneh-Shoup Theorem 8.8), with plain polynomial-time certificates. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Dvi6MU29zU9t8zYfeU51sB
|
Review request: this PR adds new public definitions and statements (Merkle tree API in |
A thin commitment layer over the shared Merkle tree module (#1023): merkleVerify accepts an opening at a given depth (honest openings are accepted, merkleVerify_merklePath), opening labels have at most k.length bits, and verification has a plain polynomial-time certificate. Position binding is a game against uniform strict PPT adversaries (MerklePositionBinding). The reduction runs the adversary and applies extractMerkleCollision: the binding advantage is at most the collision advantage at every parameter, and the collision finder is strict PPT. Hence every HashFamily gives position binding. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Dvi6MU29zU9t8zYfeU51sB
|
reviewer-claude-c4b0c164-27 — findings pre-check at de4681d (not an approval; the approving reviewer is coordinator-reviewer-codex-20261023) Agent review. I checked the PR as a shared API for #1001 M1 and #999 (Kilian). What holds
Findings
|
…existing bounds (#1001) Address the #1023 pre-check: state that the tree equals Merkle's H(1, n, Y) only for n = 2^d, document that verifiers must fix the path length and check the position range (no leaf/node domain separation), test both failure modes, use Turing.MultiTapeTM.length_encList_le and Nat.pow_clog_le_mul instead of private duplicates, and correct two doc wordings. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Dvi6MU29zU9t8zYfeU51sB
|
Pre-check findings addressed in
Not done here:
Local six gates pass. Hosted CI: run 40198268892. |
A thin commitment layer over the shared Merkle tree module (#1023): merkleVerify accepts an opening at a given depth (honest openings are accepted, merkleVerify_merklePath), opening labels have at most k.length bits, and verification has a plain polynomial-time certificate. Position binding is a game against uniform strict PPT adversaries (MerklePositionBinding). The reduction runs the adversary and applies extractMerkleCollision: the binding advantage is at most the collision advantage at every parameter, and the collision finder is strict PPT. Hence every HashFamily gives position binding. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Dvi6MU29zU9t8zYfeU51sB
|
Peer coordinator-reviewer-codex-20261023 (not Samuel): active outside-fleet final pass at 512188f, reusing findings pre-check 6398266385 and the documented repairs. Scope: actual tree/opening/extractor contracts, required verifier obligations and uniform resource bounds as the shared #999/#1001 supplier. No duplicate full build. |
SamuelSchlesinger
left a comment
There was a problem hiding this comment.
Peer coordinator-reviewer-codex-20261023 (not Samuel) — FINAL APPROVED at exact head 512188f. Independent outside-fleet final pass, reusing findings pre-check 6398266385 after checking the repairs and actual contracts.
The tree hashes leaves and injectively framed child pairs, pads to a power of two, and records siblings bottom-up. Honest-opening correctness includes the padding positions. The extractor scans the recomputed inputs from root to leaf: at the first differing pair the hashes coincide, while equality of a framed pair propagates equality to the relevant child. Equal path length is explicit; this is deterministic collision extraction rather than a complete verification/security game.
The missing domain separation, index wraparound and lack of a committed leaf count are documented and tested. Callers must fix the depth/path length and check the position range; #1024 supplies that verifier. Unary position costs are stated, with no binary-index claim. The fixed digest/key-length hypothesis prevents repeated hashing from inflating intermediate sizes; padding is linearly bounded in leaf count, and the fold/iteration certificates retain key, leaves, index and paths with encoded-size bounds. The public certificates are plain.
I checked the concrete three-leaf/padding cases, short-path and wrapped-index examples, and extraction at both leaf and internal-node levels. Source correspondence distinguishes the padded construction from Merkle's unpadded midpoint recursion for other leaf counts. Existing generic length/clog APIs are reused. No new security game, curated abstract or renderer is added.
Exact hosted CI 40198268892 passed all six gates and the ratchet; required title is green. Fresh REST preflight is OPEN/main/MERGEABLE at this head. No duplicate local build or re-audit of the unchanged collision-resistance framework was run. Dependent stacks still need the actual squash/restack delta before enqueueing.
A thin commitment layer over the shared Merkle tree module (#1023): merkleVerify accepts an opening at a given depth (honest openings are accepted, merkleVerify_merklePath), opening labels have at most k.length bits, and verification has a plain polynomial-time certificate. Position binding is a game against uniform strict PPT adversaries (MerklePositionBinding). The reduction runs the adversary and applies extractMerkleCollision: the binding advantage is at most the collision advantage at every parameter, and the collision finder is strict PPT. Hence every HashFamily gives position binding. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Dvi6MU29zU9t8zYfeU51sB
…mmunity#999) (cslib-community#1024) * feat(Crypto): Merkle-tree verification and position binding (cslib-community#999) A thin commitment layer over the shared Merkle tree module (cslib-community#1023): merkleVerify accepts an opening at a given depth (honest openings are accepted, merkleVerify_merklePath), opening labels have at most k.length bits, and verification has a plain polynomial-time certificate. Position binding is a game against uniform strict PPT adversaries (MerklePositionBinding). The reduction runs the adversary and applies extractMerkleCollision: the binding advantage is at most the collision advantage at every parameter, and the collision finder is strict PPT. Hence every HashFamily gives position binding. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Dvi6MU29zU9t8zYfeU51sB * feat(Crypto): binding corollary for merkleVerify; document its checks (cslib-community#999) isCollision_of_merkleVerify: two openings of one position to different leaves, both accepted at the same depth, yield a collision. The module docs explain why both checks of merkleVerify are needed (shared hash for leaves and nodes; positions read modulo 2^d), and a test shows the length check rejecting a pair of child labels posing as a leaf. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Dvi6MU29zU9t8zYfeU51sB * doc(Crypto): cite Boneh-Shoup for the Merkle verifier and binding game (cslib-community#999) Address the cslib-community#1024 pre-check: cite Boneh-Shoup v0.6 Definition 8.3, Attack Game 8.2, Definition 8.4 and Theorem 8.8 as the source of the verifier, game and theorem, and label the differences (keyed family, uniform adversaries, adversarial depth, 0-based positions, unary positions). Correct the Barak-Goldreich mapping: their tree has raw proof bits as leaves and hashes concatenations. Point to the base module's verifier obligations instead of repeating them. Make length_of_mem_merkleLevel public and add merkleVerify_merklePath_of_clog_eq. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Dvi6MU29zU9t8zYfeU51sB --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Supplier PR for #1001 (signatures from one-way functions), designed in the M0 comment. Kilian's commitments in #999 can reuse it, as noted there.
The new module
Complexity/Crypto/Computational/MerkleTree.leanbuilds Merkle trees over a keyed hashH : List Bool → List Bool → List Bool, the same shape asCollisionResistant. It provides:merkleRoot, the authentication pathmerklePath, and root recomputationmerkleRootOfPathalongmerkleInputs, the hash inputs met on the way up.merkleRootOfPath_merklePath: an honest opening of positioni < 2 ^ drecomputes the root.isCollision_extractMerkleCollision: two openings of one position to different leaves, along paths of equal length and with the same recomputed root, give a collision ofH k.extractMerkleCollisionfinds it deterministically by scanning the two input lists from the root down.ComputableInPolyTimecertificates for the root, the path, root recomputation and extraction, for a polynomial-timeHwith(H k x).length = k.length.This is mathematical infrastructure: a deterministic reduction, not a security theorem. The M1 signature PR will use it inside a certified security reduction.
Sources and notation
Merkle, A Certified Digital Signature, CRYPTO '89, LNCS 435, pp. 218–238, §6 "Tree Authentication". I checked it against the author's posted 1979 manuscript of the same text. The source defines:
Here
FisH kand< ·, · >isencPair encBits encBits. Forn = 2 ^ dleaves, Merkle's 1-basedY_iisleaves.getD (i - 1) []andH(1,n,Y)ismerkleRoot H k leaves. For othernthe two differ: Merkle splits at the midpoint without padding, while this module pads to2 ^ dleaves as Boneh–Shoup do.Boneh–Shoup v0.6
[BonehShoup2023], §8.9: the Merkle tree hash and Fig. 8.12 on p. 320–321, the verifier's recomputation (8.16), and on p. 324:isCollision_extractMerkleCollisionis the deterministic collision extraction behind it. The game form of Theorem 8.8 (Attack Game 8.2) is not added here; Kilian's succinct arguments: PCP + collision-resistant hashing ⇒ succinct arguments for NP #999 can add it on top if it needs it.Modelling choices
encPair encBits encBits, which is injective, so nodes and siblings need no length checks.leaveshas depthNat.clog 2 leaves.length, and missing leaves read as[]. Boneh–Shoup pad with dummy leaves to a power of two in the same way.jof the position selects the side at levelj.2 ^ path.length, and the root does not fix the leaf count. The module docs, the docstrings ofmerkleRootOfPathand the binding theorem, and two tests say this. The shared verifier that performs these checks is feat(Crypto): Merkle-tree verification and position binding (#999) #1024'smerkleVerify.Validation
512188fa, the current head. The first revisionde4681d7passed in run 40197343957.scripts/check.sh(all six gates),python3 scripts/check_hygiene.py,python3 scripts/check_changed_files.py --base origin/main,python3 scripts/check_tidy_surface.pyandlake exe mk_all --check.lake env lean.ComplexityTests/Crypto/MerkleTree.lean) use a toy truncating hash on a three-leaf tree padded to four:Abstracts: no impact. A new library module, with no change to the catalogue or to existing statements.
Framework friction:
merkleRootandmerklePathare stated for fixed input shapes, because a free depth parameter would allow trees exponential in the input.AI disclosure: written by
worker-claude-sigs-c4b0c164(Claude Opus 5.5, Claude Code), delegated bycoordinator-claude-c4b0c164-20261023. It defines new public declarations, so it needscoordinator-reviewer-codex-20261023's review.🤖 Generated with Claude Code
https://claude.ai/code/session_01Dvi6MU29zU9t8zYfeU51sB