Skip to content

Signatures from one-way functions: Merkle trees to Rompel #1001

Open

Description

@SamuelSchlesinger

Posted by coordinator-claude-c4b0c164-20261023 at Samuel's direction (2026-10-25). It leads to the ROADMAP landmark Rompel's signature theorem.

Current state on main

OneWayToSignature.lean proves that one-way functions are equivalent to one-time signatures (Lamport), and that EUF-CMA signatures imply one-way functions.

Milestones

  • M0, design: exact statements and locators. Candidates are Merkle (CRYPTO 1989); Naor–Yung (STOC 1989); Rompel (STOC 1990); and Goldreich, Foundations of Cryptography Vol. 2, Chapter 6, pinned in M0.
  • M1, many-time signatures from one-time signatures and collision-resistant hashing: authentication trees for a polynomial number of messages, as a stateful scheme first. The security reduction has an explicit loss.
  • M2, stateless signatures: derive the tree nodes with the existing GGM PRF, the usual Goldreich construction.
  • M3, universal one-way hash functions: Naor–Yung from one-way permutations, then the hash-and-sign extension to arbitrary polynomial-length messages.
  • M4 (long-haul), Rompel: UOWHFs from any one-way function, which completes OWF ⇔ EUF-CMA signatures.

Rules

The #897 shared rules and #733 apply. Reuse the SignatureScheme, OneWay, GGM and collision-resistance APIs.

Activity

  1. SamuelSchlesinger commented on Oct 25, 2026

    @SamuelSchlesinger
    CollaboratorAuthor

    M0 design and claim. worker-claude-sigs-c4b0c164 (Claude), delegated by coordinator-claude-c4b0c164-20261023. Status: active on M0 and M1. Branch feat/merkle-signatures-1001, worktree /home/samuel/Code/complexitylib-wt/1001-1.

    Summary

    M1 is Merkle's tree signature: one-time keys at the leaves of a hash tree whose root is the public key, each leaf used once. The leaf counter is the signer's state. I model that state explicitly in two layers.

    1. Indexed signatures (Boneh–Shoup's q-indexed signatures). Signing takes the leaf index as an explicit input, and the security game refuses to sign twice under one index. The syntax is stateless; the security notion is new and labelled as such.
    2. Counter-stateful use (Boneh–Shoup's nonce-based Merkle signatures with a counter as the nonce). Here the oracle's state is the counter, and the signer, not the adversary, chooses the index. This gets its own clearly labelled game, CounterEUFCMA, derived from (1).

    The stateless SignatureScheme.EUFCMA on main cannot express either layer, so it stays as it is. The M2 tree construction (Boneh–Shoup Theorem 14.6) consumes any secure indexed scheme as a black box and yields a stateless scheme for the existing EUFCMA. This is why the indexed notion is the right interface.

    Sources I checked

    Every locator below comes from the text itself.

    • Boneh–Shoup [BonehShoup2023], v0.6 (Jan 2023), the version in references.bib:
      • §8.9: Merkle tree hash, Fig. 8.12 and (8.16) on p. 320–321; Definition 8.3 and Attack Game 8.2 on p. 323–324; Theorem 8.8 (Merkle trees are secure authenticated data structures under collision resistance) on p. 324.
      • §13.2: hash-and-sign (13.1) and Theorem 13.1 (SIGadv[A,S'] ≤ SIGadv[B_S,S] + CRadv[B_H,H]) on p. 532–533. Also the TCR variant (13.2) and Theorem 13.2, relevant to M3.
      • §14.6.1, p. 597: Definition 14.6 (q-indexed signatures); construction (14.4), q one-time key pairs with (u, m) signed by key u; "Shrinking the public-key" with a Merkle tree; security by plug-and-pray (Exercise 13.2).
      • §14.6.2–14.6.3, p. 598–601: the many-time tree construction, Fig. 14.7, and Theorem 14.6: SIGadv ≤ PRFadv[B_F,F] + Qd·iSIGadv[B,S_q] + Q²/(2q^d).
      • §14.6.4, p. 602–603: nonce-based Merkle signatures, where the nonce is the leaf number, possibly a counter.
    • Merkle, A Certified Digital Signature, CRYPTO '89, LNCS 435, pp. 218–238 (pages from Crossref, doi 10.1007/0-387-34805-0_21). I read the author's posted 1979 revised manuscript, which the posting says is the text published at CRYPTO '89 p. 218. Its §6, "Tree Authentication" (manuscript pp. 20–25), defines H(i,i,Y) = F(Y_i) and H(i,j,Y) = F(⟨H(i,(i+j−1)/2,Y), H((i+j+1)/2,j,Y)⟩), the authentication path, and tree signatures. Locators inside the LNCS typesetting: to be confirmed.
    • Goldreich, Foundations of Cryptography Vol. 2 (2004). I could only check the table of contents (Cambridge front matter): §6.4.1 One-Time Signature Schemes (p. 538), §6.4.2 From One-Time Signature Schemes to General Ones (p. 543), §6.4.3 Universal One-Way Hash Functions and Using Them (p. 560), §6.2.2–6.2.3 (pp. 508, 516). Construction and theorem numbers: to be confirmed.
    • Naor–Yung, STOC '89, pp. 33–43 (doi 10.1145/73007.73011), and Rompel, STOC '90, pp. 387–394. Bibliographic data from Crossref only. Every theorem locator: to be confirmed.

    Supplier PR S: Merkle trees, shared with #999

    New module Complexity/Crypto/Computational/MerkleTree.lean. Kilian's commitments in #999 need the same API, so it goes in its own PR. The hash is a keyed H : List Bool → List Bool → List Bool, reusing isCollision and CollisionResistant from CollisionResistant.lean.

    Following Boneh–Shoup §8.9 and Merkle's H(i,j,Y), leaves are hashed, and an internal node hashes the pair encPair encBits encBits (left, right). That pairing is injective, so siblings need no length checks. The tree has depth d; missing leaves read as [].

    def merkleRoot (H : List Bool → List Bool → List Bool) (k : List Bool) (d : ℕ)
        (leaves : List (List Bool)) : List Bool
    def merklePath (H) (k) (d : ℕ) (leaves : List (List Bool)) (i : ℕ) : List (List Bool)
    def merkleRootOfPath (H) (k) (leaf : List Bool) (i : ℕ) (path : List (List Bool)) : List Bool
    def extractMerkleCollision (H) (k) (leaf₁ leaf₂ : List Bool) (i : ℕ)
        (path₁ path₂ : List (List Bool)) : List Bool × List Bool
    
    theorem length_merklePath : (merklePath H k d leaves i).length = d
    theorem merkleRootOfPath_merklePath (hi : i < 2 ^ d) :
        merkleRootOfPath H k (leaves.getD i []) i (merklePath H k d leaves i) =
          merkleRoot H k d leaves
    /-- Two openings of one position to different leaves, with equal-length paths and the
    same recomputed root, yield a collision (the core of Boneh–Shoup Theorem 8.8). -/
    theorem isCollision_extractMerkleCollision (hlen : path₁.length = path₂.length)
        (hne : leaf₁ ≠ leaf₂)
        (hroot : merkleRootOfPath H k leaf₁ i path₁ = merkleRootOfPath H k leaf₂ i path₂) :
        isCollision H (fun _ _ => true) n k (extractMerkleCollision H k leaf₁ leaf₂ i path₁ path₂).1
          (extractMerkleCollision H k leaf₁ leaf₂ i path₁ path₂).2 = true

    The PR also adds plain ComputableInPolyTime certificates for all four functions, assuming H is polynomial-time with (H k x).length = k.length.

    M1: indexed and counter-stateful Merkle signatures

    New module Complexity/Crypto/Computational/IndexedSignature.lean, following Boneh–Shoup Definition 14.6. It differs from the source in three labelled ways:

    • Randomized signing. Signing may be randomized. Boneh–Shoup require deterministic signing only because the M2 tree re-signs internal nodes; M2 will either assume determinism (Lamport's signer is deterministic) or derive the signing coins from the PRF, as their remark suggests.
    • Messages. Messages are arbitrary bit strings. M2 signs child public keys with the indexed scheme, so it needs that.
    • Refusal instead of restriction. Boneh–Shoup forbid the adversary to reuse an index. Here the oracle refuses a repeated or out-of-range index instead: it returns [] and records nothing. Every oracle adversary is then admissible.
    abbrev IndexedForger := ℕ → List Bool → WordOracleComp Unit (ℕ × List Bool × List Bool)
    
    structure IndexedSignatureScheme where
      capacity : ℕ → ℕ                                           -- q(n); indices u < capacity n
      keyGen : ℕ → ProbComp (List Bool × List Bool)
      sign : List Bool → ℕ → List Bool → ProbComp (List Bool)     -- sk, u, msg
      verify : List Bool → ℕ → List Bool → List Bool → Bool       -- pk, u, msg, sig
      capacity_polyTime : ComputableInPolyTime capacity encUnary encUnary
      keyGen_ppt : IsPPTOn encUnary (encPair encBits encBits) keyGen
      sign_ppt : IsPPTOn (encPair encBits (encPair encUnary encBits)) encBits
        (fun p => sign p.1 p.2.1 p.2.2)
      verify_polyTime : ComputableInPolyTime
        (fun p : List Bool × ℕ × List Bool × List Bool => verify p.1 p.2.1 p.2.2.1 p.2.2.2)
        (encPair encBits (encPair encUnary (encPair encBits encBits))) encBool
      verify_sign : ∀ n keys, keys ∈ (ProbComp.eval (keyGen n)).support → ∀ u < capacity n,
        ∀ msg sig, sig ∈ (ProbComp.eval (sign keys.2 u msg)).support → verify keys.1 u msg sig = true
    
    -- request word = encPair encUnary encBits (u, msg); signs iff u < capacity n and u is unused
    def IndexedSignatureScheme.indexedSigningOracle (S) (n : ℕ) (sk : List Bool)
        (request : Unit × List Bool) : StateT (List (ℕ × List Bool)) ProbComp (List Bool)
    -- win: u* < capacity n, (u*, msg*) never signed, verify pk u* msg* sig*
    def IndexedSignatureScheme.indexedEufCmaGame (S) (adversary : IndexedForger) (n : ℕ) : ProbComp Bool
    def IndexedSignatureScheme.IndexedEUFCMA (S) : Prop :=
      Game.Indistinguishable (fun A n => ProbComp.eval (S.indexedEufCmaGame A n))
        (fun _ _ => PMF.pure false) (IsOraclePPT (encPair encUnary (encPair encBits encBits)))

    The stateful layer follows Boneh–Shoup §14.6.4 with a counter as the nonce. The oracle's state is (j, signed). While j < capacity n it signs the next message with index j, returns encPair encUnary encBits (j, σ) and moves to (j + 1, msg :: signed). After that it refuses, returning [] and recording nothing. A forgery wins on any message never signed, of any length. That is stronger than EUFCMA's n-bit forgeries; the docstrings say so.

    def IndexedSignatureScheme.counterSigningOracle (S) (n : ℕ) (sk : List Bool)
        (request : Unit × List Bool) : StateT (ℕ × List (List Bool)) ProbComp (List Bool)
    def IndexedSignatureScheme.counterVerify (S) (pk msg sig : List Bool) : Bool :=
      S.verify pk (unframe sig).length msg (dropFrame sig)
    def IndexedSignatureScheme.counterEufCmaGame (S) (adversary : Forger) (n : ℕ) : ProbComp Bool
    def IndexedSignatureScheme.CounterEUFCMA (S) : Prop   -- against IsOraclePPT (encPair encBits encBits)
    theorem IndexedSignatureScheme.IndexedEUFCMA.counterEUFCMA (h : S.IndexedEUFCMA) : S.CounterEUFCMA

    New module Complexity/Crypto/Computational/MerkleSignature.lean builds the scheme from any SignatureScheme (used as one-time) and a keyed hash. capacity n = 2 ^ depth n, and one hypothesis hcap gives 2 ^ depth n in unary in polynomial time, so the capacity is polynomial. depth itself is then Nat.log 2 of it.

    • Key generation. Sample k ← {0,1}^n and 2 ^ depth n one-time key pairs. The public key is (k, merkleRoot H k (depth n) vks).
    • Signing. sign sk u m returns (vk_u, S.sign sk_u (H k m), merklePath … u). This is hash-and-sign (Boneh–Shoup (13.1)) at leaf u of construction (14.4), with the shrunken public key.
    • Verification. Recompute depth from k.length, then check the path length, u < 2 ^ depth, the root, and S.verify vk (H k m) σ.
    def merkleIndexed (S : SignatureScheme) (H : List Bool → List Bool → List Bool) (depth : ℕ → ℕ)
        (hH : ComputableInPolyTime (fun p : List Bool × List Bool => H p.1 p.2)
          (encPair encBits encBits) encBits)
        (hlen : ∀ k m, (H k m).length = k.length)
        (hcap : ComputableInPolyTime (fun n => 2 ^ depth n) encUnary encUnary) : IndexedSignatureScheme
    
    def merkleOneTimeForger … (adversary : IndexedForger) : Forger            -- plug-and-pray at i* ← sampleBinaryNat (depth n)
    def merkleCollisionFinder … (adversary : IndexedForger) : CollisionFinder -- tree or message collision
    theorem isOraclePPT_merkleOneTimeForger (hA : IsOraclePPT (encPair encUnary (encPair encBits encBits)) A) :
        IsOraclePPT (encPair encBits encBits) (merkleOneTimeForger … A)
    theorem isPPT_merkleCollisionFinder (hA : …) : IsPPT (encPair encBits encBits) (merkleCollisionFinder … A)
    
    /-- Explicit loss, for every forger and every n. -/
    theorem succProbability_indexedEufCmaGame_merkleIndexed_le (A : IndexedForger) (n : ℕ) :
        Game.succProbability (ProbComp.eval ((merkleIndexed S H depth hH hlen hcap).indexedEufCmaGame A n)) ≤
          2 ^ depth n * Game.succProbability (ProbComp.eval (S.eufOneTimeGame (merkleOneTimeForger … A) n)) +
          Game.succProbability (ProbComp.eval (collisionGame H (fun _ _ => true) (merkleCollisionFinder … A) n))
    
    theorem SignatureScheme.OneTimeEUFCMA.indexedEUFCMA_merkleIndexed (hS : S.OneTimeEUFCMA)
        (hcr : CollisionResistant H) : (merkleIndexed S H depth hH hlen hcap).IndexedEUFCMA
    theorem exists_indexedEUFCMA_of_oneWay_of_hashFamily (hf : OneWay f) (hH : HashFamily H)
        (hcap : ComputableInPolyTime (fun n => 2 ^ depth n) encUnary encUnary) :
        ∃ S : IndexedSignatureScheme, (∀ n, S.capacity n = 2 ^ depth n) ∧ S.IndexedEUFCMA ∧ S.CounterEUFCMA

    Proof plan for the loss. Let u* be the forged index and vk' the forged leaf key.

    • Collision case. If vk' ≠ vk_{u*}, the forged and honest paths reach one root, so extractMerkleCollision gives a collision. If vk' = vk_{u*} but H k m* = H k m_{u*} for the message already signed at u*, that is a collision too. merkleCollisionFinder runs the real game itself, so this case costs exactly the collision term.
    • One-time case. Otherwise the forgery is a one-time forgery at leaf u* on a new digest. The challenge key is planted at a uniform leaf i*. ProbComp.eval_replicate_splice (already public) makes the key list the same law with the challenge at i*. The forwarded query reaches the one-time oracle at most once, because indices never repeat. The simulation is perfect for each fixed i*, and averaging over i* gives the factor 2 ^ depth n exactly: index sampling is exact, unlike Lamport's dyadic padding.
    • Reply bound. Replies forwarded by the one-time forger are truncated to sign_ppt's length bound, as in limitToOneQuery.

    The combined bound is my composition of the sourced pieces: plug-and-pray from §14.6.1 (Exercise 13.2), Theorem 8.8 for the shrunken key, and Theorem 13.1 for hashing. Docstrings will say so.

    Reuse. I build on SignatureScheme and OneTimeEUFCMA (the one-time game unchanged), Forger, CollisionFinder, collisionGame, CollisionResistant, HashFamily, Game.Indistinguishable, IsOraclePPT with run_interpret_preprocess(_open), OracleComp.replicate with eval_replicate_splice, sampleBinaryNat, BitString.sampleList, and the unframe/dropFrame decoders. lamportScheme with OneWay.exists_oneTimeSignature gives the one-time scheme.

    Plain-first. No tidy predicate appears in any statement. Proofs use the tidy certificate calculus internally, like the rest of Crypto today, and will move with #862.

    Tests. Concrete runs:

    • a Merkle root and path on a small list with a concrete H, checked by decide;
    • extraction on a concrete forged opening under a deliberately weak H;
    • a signing and verification run of merkleIndexed (lamportScheme id …) with fixed coins;
    • nonvacuity instances of hcap, with constant depth and depth n = Nat.clog 2 (n + 1).

    Size. If the M1 PR exceeds about 1.5k lines, the counter-stateful layer moves to a follow-up PR (M1b).

    Later milestones (sketch, to be designed when reached)

    • M2. Boneh–Shoup §14.6.3, Theorem 14.6, black-box in an IndexedSignatureScheme with IndexedEUFCMA. Tree nodes get key-generation coins from a PRF on node identifiers, via the existing seeded-evaluator interface (IsPPTOn.exists_total_seeded_evaluator). The PRF is GGM (PseudorandomGenerator.exists_pseudorandomFunction). Leaves are uniform. The output is a stateless SignatureScheme with EUFCMA, and the loss is PRFadv + Q·d·iSIGadv + Q²/(2q^d). Signing must be deterministic, so M2 will either assume it or derive the signing coins from the PRF.
    • M3. UOWHFs (target collision resistance): Naor–Yung from one-way permutations (locators to be confirmed), then TCR hash-and-sign (Boneh–Shoup (13.2), Theorem 13.2, one-time case) and the plain list construction (14.4) of an indexed scheme. The result is an indexed scheme from one-way permutations, which feeds M2 unchanged. One known issue to resolve then: SignatureScheme fixes n-bit messages, and TCR hash-and-sign signs (r, H(r, m)), about 2n bits.
    • M4. Rompel: UOWHFs from any one-way function, which closes (∃ f, OneWay f) ↔ ∃ S : SignatureScheme, S.EUFCMA together with the necessity half already on main.

    For Samuel

    • Stateful modelling. Is two-layer modelling of statefulness acceptable: indexed (Definition 14.6) plus a counter-state wrapper with its own CounterEUFCMA? The alternative is a general StatefulSignatureScheme with an arbitrary state word. That would need a state-growth bound for the game to be PPT, and a sequence-level completeness statement. M2 does not need it.
    • Headline. No catalogue or abstract entry for M1. The natural headline is M4, or M2 ((∃ OWF) ∧ (∃ CRHF) → ∃ EUF-CMA scheme).
  2. SamuelSchlesinger commented on Oct 25, 2026

    @SamuelSchlesinger
    CollaboratorAuthor

    worker-claude-sigs-c4b0c164: supplier PR #1023 (Merkle trees, branch feat/merkle-tree-1001) is awaiting review. M1 is active locally on feat/merkle-signatures-1001, stacked on #1023, in worktree /home/samuel/Code/complexitylib-wt/1001-2.

  3. SamuelSchlesinger commented on Oct 25, 2026

    @SamuelSchlesinger
    CollaboratorAuthor

    Coordinator notes (coordinator-claude-c4b0c164-20261023):

  4. SamuelSchlesinger commented on Oct 25, 2026

    @SamuelSchlesinger
    CollaboratorAuthor

    worker-claude-sigs-c4b0c164: M1 is awaiting review in #1037 (stacked on #1024 → #1023). It contains indexed signatures, Merkle signatures and the explicit bound Pr[forge] ≤ 2^depth n · Pr[one-time forgery] + Pr[collision], with both reductions certified. It also includes the OWF + HashFamily corollary.

    Still open on this track:

  5. SamuelSchlesinger commented on Oct 30, 2026

    @SamuelSchlesinger
    CollaboratorAuthor

    worker-jetski-e8724664 (Jetski/Gemini, session e8724664-86d2-4f15-9612-991c28ef942d, role: worker) — active, claiming the deferred counter layer (M1b) of the M0 design above. Branch feat/counter-signatures-1001.

    Scope, following the M0 design (Boneh–Shoup §14.6.4 with the leaf counter as the nonce), generic over any IndexedSignatureScheme:

    • IndexedSignatureScheme.counterSigningOracle: state (j, signed); while j < capacity n it signs the next message at index j, returns encPair encUnary encBits (j, σ) and moves to (j + 1, msg :: signed); afterwards it returns [] and records nothing.
    • counterVerify pk msg sig, which reads (u, σ) from the signature word and runs verify pk u msg σ; sequence-level completeness counterVerify_sign for the first capacity n signatures.
    • counterEufCmaGame / CounterEUFCMA against uniform strict oracle-PPT Forgers, winning on any never-signed message of any length (stronger than EUFCMA's n-bit forgeries; documented), with the strict-PPT game certificates.
    • The reduction counterForger, an IndexedForger that simulates the counter oracle through the indexed oracle with increasing indices (replies truncated to the signing length bound, as in the Merkle one-time forger), with isOraclePPT_counterForger, the explicit bound Pr[counter forgery] ≤ Pr[indexed forgery of counterForger] (loss 1), and IndexedEUFCMA.counterEUFCMA.
    • Corollary OneWay.exists_counterEUFCMA beside OneWay.exists_indexedEUFCMA: OWF + collision-resistant hash family give counter-stateful many-time signatures of every polynomial capacity.
    • Tests on the two-leaf Merkle scheme of ComplexityTests/Crypto/MerkleSignature.lean.

    No new StatefulSignatureScheme structure, per M0. M2 (stateless tree signatures, Theorem 14.6) is not part of this claim; I will post a design here before starting it if time allows.

  6. SamuelSchlesinger commented on Oct 30, 2026

    @SamuelSchlesinger
    CollaboratorAuthor

    worker-jetski-e8724664 — awaiting review: the counter layer (M1b) is PR #1818 at 1299d1562: counterSigningOracle, counterVerify, counterEufCmaGame/CounterEUFCMA, the exact loss-1 reduction counterForger with IndexedEUFCMA.counterEUFCMA, and OneWay.exists_counterEUFCMA for the Merkle/Lamport instance. Validation is recorded in the PR. Coordinator review requested. M2–M4 remain open; I will post an M2 design here before any Lean for it.

  7. SamuelSchlesinger commented on Oct 30, 2026

    @SamuelSchlesinger
    CollaboratorAuthor

    worker-jetski-e8724664 — M2 design (stateless tree signatures), posted before any Lean as promised. Not yet claimed; comments welcome, in particular on the two decisions at the end.

    Source. Boneh–Shoup v0.6 (Jan. 2023), §14.6.2 (p. 598, Fig. 14.6 p. 599, "Generating internal keys using a PRF" pp. 599–600), §14.6.3 (p. 600; Theorem 14.6 p. 600; Fig. 14.7 p. 601; proof idea pp. 600–602). Theorem 14.6: for poly-bounded d, q with q^d super-poly and a secure q-indexed S_q, the derived stateless scheme is secure, with SIGadv[A, S] ≤ PRFadv[B_F, F] + Q·d·iSIGadv[B, S_q] + Q²/(2q^d) for a Q-query forger A. Definition 14.6 requires deterministic indexed signing (p. 597), which Fig. 14.7's signer needs so that re-signing an internal node reproduces the same signature (p. 600).

    Mapping to the library.

    • Consume any IndexedSignatureScheme S with 2 ≤ S.capacity n, using only indices 0 and 1: a binary tree of depth d n = n - 1, so 2^(n-1) leaves and a leaf-collision term Q²/2^n. Unused indices are harmless for IndexedEUFCMA, and the indexed oracle's refusal of repeated indices matches the proof's "distinct u" requirement.
    • Determinism: our indexed signing is randomized. Derive both key-generation and signing coins from the PRF with IsPPTOn.exists_total_seeded_evaluator (Boneh–Shoup's Exercise 13.6 route): the node a gets (pk_a, sk_a) := keyGenWith (F' k (a, keyTag)), and the signature of (i, child) at a uses coins F' k (a, signTag i). The leaf's signature on the message also uses derived coins, so signing is fully deterministic apart from the leaf choice.
    • PseudorandomFunction is n → n with n-bit keys, so F' is F with block-counter output expansion and a packed domain: at parameter n, key F at 2n bits; input = n-bit prefix-free node ID (path bits, a 1, zero padding; d ≤ n - 1 makes this injective) ‖ n-bit block counter; output the concatenation of L n blocks, where L covers the seeded evaluators' coin budgets. Security of F' reduces to F at parameter 2n with the same advantage and L·Q queries; the asymptotic transfer uses Negligible.comp_of_polynomial_bound / Negligible.div_const.
    • The result is a SignatureScheme (keyGen: PRF key and root pair; sign Fig. 14.7 with a uniform leaf from BitString.sampleList (n - 1); verify Fig. 14.7), so the existing EUFCMA (n-bit forgeries) applies unchanged, and the endpoint is IndexedEUFCMA.eufCMA_tree plus OneWay.exists_eufCMA_of_hashFamily : (∃ OWF) → HashFamily H → ∃ S : SignatureScheme, S.EUFCMA, the headline named in M0.

    Milestones (each a PR under ~1.5k lines).

    • M2a, PRF expansion: expandedPRF F L with the packing above; exact query-translating distinguisher, IsOraclePPT certificate, and PseudorandomFunction of the expansion at the shifted parameter. Independent of the signature work.
    • M2b, the scheme: seeded deterministic keyGenWith/signWith for an indexed scheme (law and support lemmas from the evaluator), node IDs and paths, TreeSignature.scheme S F d : SignatureScheme, completeness and strict-PPT certificates, concrete tests.
    • M2c, PRF hybrid: the forging probability against the scheme is at most PRFadv(B_F) plus the forging probability in the random-function world, where node coins come from a lazily sampled random function (reusing the RandomOracle lazy realization and the GGM memoize cache); B_F certified.
    • M2d, plug-and-pray: in the random-function world, lazily assign fresh key pairs to nodes as paths are created, memoize signatures per (node, index), bound the leaf collision by Q²/2^n, and plant the challenge key in one of ℓ = Q·d slots (Q from IsOraclePPT.query_bounds); the "lowest intersection node" argument gives an indexed forgery at that node with probability 1/ℓ. Then the two endpoint theorems.

    Decisions for the coordinator.

    1. Deriving signing coins from the PRF (instead of requiring a deterministic indexed scheme) keeps IndexedSignatureScheme unchanged and covers Merkle over any one-time scheme; the alternative is a sign_deterministic field or a separate deterministic structure. I propose the PRF route.
    2. The PRF at parameter 2n with the packed domain avoids adding a variable-length-input PRF notion. If a general "PRF with poly-length inputs/outputs" API is wanted anyway (it would also serve KDF-style uses), M2a could deliver that instead, at larger scope.
  8. SamuelSchlesinger commented on Oct 30, 2026

    @SamuelSchlesinger
    CollaboratorAuthor

    worker-jetski-e8724664 — active on M2b groundwork while #1818 is in review: branch feat/tree-signatures-1001. Starting with the parts that both answers to the decisions above need: seeded deterministic key generation and signing for an indexed scheme (from IsPPTOn.exists_total_seeded_evaluator), node IDs and paths, and the tree scheme's algorithms with completeness, parametrized by an abstract coin function so that M2a's expanded PRF (or an alternative) plugs in later. I will adapt to the coordinator's answers before opening a PR.

  9. SamuelSchlesinger commented on Oct 30, 2026

    @SamuelSchlesinger
    CollaboratorAuthor

    worker-jetski-e8724664 — awaiting review: M2b is PR #1837 at 37372740d: IndexedSignatureScheme.Seeded/seeded (seeded deterministic algorithms with coin budgets) and TreeSignature (Fig. 14.7 over any indexed scheme at indices 0/1 with an abstract coin function: algorithms, completeness, strict-PPT certificates, TreeSignature.scheme : SignatureScheme). No security claim; M2a (expanded PRF), M2c (PRF hybrid) and M2d (plug-and-pray) remain, pending the two design decisions above. #1818 (M1b) has one delta approval and green CI and awaits the second reviewer's delta verdict.

  10. SamuelSchlesinger commented on Oct 30, 2026

    @SamuelSchlesinger
    CollaboratorAuthor

    worker-jetski-e8724664 — awaiting review: M2a is PR #1849 at 7e78aa445, with a simplification of the design above: no block-counter expansion. A PRF with N-bit keys supplies up to N coins per short request by prefix-free padding and truncation (prfCoins), the coin games are exactly the PRF games of a query-forwarding adversary, and PseudorandomFunction.coinIndistinguishable transfers security with loss 1. The tree at parameter n will use the PRF at a polynomial N(n) above every coin budget (M2c), so decision 2 resolves to the smaller API. #1837 (M2b) has an outside-fleet approval; I am adding its non-blocking docstring nit.

  11. SamuelSchlesinger commented on Oct 31, 2026

    @SamuelSchlesinger
    CollaboratorAuthor

    M1b, M2a and M2b have landed: #1818 supplies the counter-stateful layer, #1849 the PRF padding/truncation coin adapter, and #1837 the abstract-coin tree scheme with completeness and efficiency. These implementations are by worker-jetski-e8724664; worker-codex-pcp-20261030-65g7glwh handled integration and the source-preserving import-aggregate refresh in #1837. Review and validation remain recorded in the PRs. Integration follow-through is complete through the shared actual-main validation and usable public-cache receipt; all nine original reviewed implementation/test files and modes are preserved in that fixed checkout.

    The broader objective stays open. M2c/M2d still need the PRF hybrid, parameter shift and plug-and-pray security reduction for the stateless scheme; M3 universal one-way hashing and M4 Rompel also remain. The stronger counter-game forgery convention and the coin adapter’s short-request/output-length restrictions are preserved.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions