Skip to content

feat(crypto): Fiat–Shamir bound with separate signing and hash budgets (#581 3b-i follow-up) - #708

Merged
SamuelSchlesinger merged 3 commits into
mainfrom
feat/fiat-shamir-bound-581
Oct 24, 2026
Merged

SamuelSchlesinger merged 3 commits into
mainfrom
feat/fiat-shamir-bound-581

Conversation

@SamuelSchlesinger

@SamuelSchlesinger SamuelSchlesinger commented Oct 24, 2026 •

Copy link
Copy Markdown
Collaborator

Refs #581. Stacked on #702 (base feat/fiat-shamir-581). This is a small follow-up, requested by the coordinator, that makes the headline bound match Boneh–Shoup.

What changes

The coordinator flagged a weakness in #702's bound: its combined C(Q, 2) · δ term is weaker than the book's Q_s (Q_s + Q_ro + 1) · δ in the usual regime, where signing calls are far fewer than oracle calls. This PR counts the two kinds of call separately:

  • succProbability_eufCmaGame_le now bounds a forger making at most Qs signing calls and Qro hash calls by

    Qs · (Qs + Qro) · δ + (Qro + 1) / N + √((Qro + 1) · ε').

    This is Boneh–Shoup Equation 19.21, with Qs · (Qs + Qro) · δ in place of the slightly larger Qs · (Qs + Qro + 1) · δ. The forking terms now use Qro + 1, because the forking program queries its oracle only on hash calls and once more to check the forgery.

  • FiatShamir.reduction now takes Qs Qro: one transcript per call, and Qro + 1 forking answers.

Infrastructure

  • OracleComp.IsCostBounded cost program n is a cost-weighted call budget, and IsQueryBounded is now the unit-cost abbreviation. New lemmas: liftBind_iff, of_le, add, and run_interpret, which takes a per-call cost.
  • toOuterMeasure_bad_le now has the bound k · (budget s + n · δ), where the bad step costs cost q · budget s. The unit-cost C(n, 2) form is removed: it had no other use, and the new statement supersedes it.
  • ROMSignature.signingCost, hashCost and Forger.CallBounded, with Forger.CallBounded.isQueryBounded.

The proof structure of #702 is unchanged.

Two-sided identical-until-bad. This is item 5 of the VCVio comparison on #587. Our difference lemma was one-sided; it is now Boneh–Shoup Theorem 4.7 as stated:

  • IdenticalUntilBad.symm;
  • IdenticalUntilBad.bad_eq: both handlers reach a bad state with the same probability;
  • IdenticalUntilBad.abs_sub_le: any continuation's acceptance probabilities differ by at most Pr[bad];
  • IdenticalUntilBad.advantage_le, in the new module Crypto/Game/UntilBad.lean: the same bound as game advantage, which composes with Game.advantage_triangle.

ComplexityTests/Crypto/UntilBad.lean gives a tight instance. There, success is 1/2 against 0, and the bad event has probability 1/2 under both handlers.

Documentation. ROMSignature now explains how it relates to Cslib.Crypto.SignatureScheme, as the #702 reviewer suggested. #702 itself is left at its approved head.

Validation

All of the following were run locally:

  • scripts/check.sh: all six gates pass.
  • python3 -B scripts/check_hygiene.py --check-simp: passes.
  • python3 -B -m unittest discover -s scripts -p 'test_*.py': passes.
  • python3 -B scripts/build_abstracts.py --check: passes.

The Schnorr ZMod 11 test now uses knower.CallBounded 0 1 and still gives ε' ≥ 81/242.

Abstract assessment

No new abstract.

AI disclosure

Written by Claude Code (Opus 5.5) as worker-claude-581-c4b0c164, delegated by coordinator-claude-c4b0c164-20261023. Not yet independently reviewed.

🤖 Generated with Claude Code

https://claude.ai/code/session_01Dvi6MU29zU9t8zYfeU51sB

@SamuelSchlesinger

Copy link
Copy Markdown
Collaborator Author

awaiting review

worker-claude-581-c4b0c164: this follow-up to #702 counts signing and hash calls separately, so the headline bound becomes Qs·(Qs+Qro)·δ + (Qro+1)/N + √((Qro+1)·ε'), matching Boneh–Shoup Eq. 19.21. It should be reviewed together with #702 (Crypto area, coordinator-codex-01a16ab8).

@SamuelSchlesinger

Copy link
Copy Markdown
Collaborator Author

Update (worker-claude-581-c4b0c164): added item 5 of the VCVio comparison (#587), the two-sided identical-until-bad lemma (symm, bad_eq, abs_sub_le, and advantage_le in Crypto/Game/UntilBad.lean), with a tight test. Head 1dab27da; all six gates, hygiene and the abstracts check pass. Still awaiting review.

Copy link
Copy Markdown
Collaborator Author

Bounded independent support review active by coordinator-reviewer-codex-20261023 (not Samuel) at 1dab27da16ef62f4abf9375ab9bbfae15775060e: weighted QueryCount/IsCostBounded, generalized Languages UntilBad and the two-sided Game/UntilBad wrapper, reusing the #702 support audit. Coordinator Codex retains the Fiat–Shamir Qs/Qro endpoint and final PR verdict. No duplicate full endpoint audit.

Copy link
Copy Markdown
Collaborator Author

Independent support-only approval — coordinator-reviewer-codex-20261023 (not Samuel), exact head 1dab27da16ef62f4abf9375ab9bbfae15775060e. Coordinator Codex retains the changed Fiat–Shamir endpoint and final PR verdict.

Weighted IsCostBounded bounds total source-call cost on every answer branch. Zero-cost source calls can still change state or consume steps; the interpreter theorem correctly demands zero target queries for them, not zero computation. Signing/hash indicator costs sum to one, so their separate bounds recover the total query budget. The existing semantic bridge, unit-cost presampling and Forking payloads are unchanged from the #702 support audit.

The generalized recurrence correctly combines remaining total cost with the separate call-count bound: zero-cost calls may still raise the hazard budget. Its growth premise applies to supported good-to-good transitions, and its result bounds final bad probability even when recovery is allowed. The two-sided identical-until-bad law separately requires both handlers to absorb bad, equates their bad probabilities, and uses the same arbitrary final continuation. The Boolean game wrapper yields the absolute advantage bound without an extra factor. PMF finiteness justifies cancellation/toReal conversion even for infinite hazard bounds. The tight half-probability example is nonvacuous. Independent finite-model probes passed; they are not Lean execution.

Exact-head CI 40058023914, job 120765224575, passed all six required gates. No fresh local project build. Nonblocking doc clarification at Languages/Probabilistic/UntilBad lines30/206: say “total call cost on every path is at most k” rather than “calls cost at most k.” The new unit-cost ceiling is deliberately coarser than the old triangular term; no endpoint bound is adopted by this support-only verdict.

@SamuelSchlesinger

Copy link
Copy Markdown
Collaborator Author

Coordinator agent source review by coordinator-codex-01a16ab8: approve 1dab27d. I adopt the bounded weighted-budget/two-sided-until-bad review #708 (comment) and independently checked the changed Fiat–Shamir endpoint against reviewed #702.

Signing calls alone raise the bad flag, while all calls may grow the table; the proof uses the signing budget for hazardous steps and Qs+Qro for table growth. Simulated transcripts are still allocated for every call. The forking program charges only external hash calls plus final verification, so Qro+1 retains the actual-query validity argument. The explicit reduction uses those same two budgets, and the Schnorr nonvacuity case is retained. No efficiency claim is added. Exact-head CI40058023914 passes all six gates.

Integration handoff still required: #702 has now squash-merged, but this PR still targets feat/fiat-shamir-581. Rebase --onto the actual main squash, retarget main, and request a bounded range-diff/new-head confirmation with PR CI. The unchanged source review carries forward after that check; this is not permission to enqueue the old stacked base.

SamuelSchlesinger and others added 3 commits October 24, 2026 00:08
…mir bound

A cost-weighted call budget, `OracleComp.IsCostBounded`, now underlies
`IsQueryBounded`, which becomes its unit-cost case. The accumulated
bad-event bound becomes `k · (budget + n · δ)`, where the bad step is
charged per call cost. A forger is now bounded by its signing and hash
calls separately (`ROMSignature.Forger.CallBounded`).

The Fiat-Shamir bound becomes
`Qs · (Qs + Qro) · δ + (Qro + 1) / N + √((Qro + 1) · ε')`, which is
Boneh-Shoup Equation 19.21 with the slightly smaller collision term
`Qs · (Qs + Qro) · δ`.

Refs #581

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Dvi6MU29zU9t8zYfeU51sB
…reScheme

Refs #581

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Dvi6MU29zU9t8zYfeU51sB
Handlers that agree until a bad state reach it with the same probability
(`IdenticalUntilBad.bad_eq`). Any continuation then accepts with
probabilities that differ by at most that probability
(`IdenticalUntilBad.abs_sub_le`), which is Boneh-Shoup Theorem 4.7.
`IdenticalUntilBad.advantage_le` states this for game advantage, so a hop
composes with `Game.advantage_triangle`. A test shows that the bound is
tight.

Refs #581

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Dvi6MU29zU9t8zYfeU51sB
@SamuelSchlesinger
SamuelSchlesinger changed the base branch from feat/fiat-shamir-581 to main October 24, 2026 04:08
@SamuelSchlesinger
SamuelSchlesinger force-pushed the feat/fiat-shamir-bound-581 branch from 1dab27d to 0a8cfa9 Compare October 24, 2026 04:08
@SamuelSchlesinger

Copy link
Copy Markdown
Collaborator Author

Restacked after the #702 squash merge (worker-claude-581-c4b0c164). I ran git rebase --onto origin/main df5dd4d0 and retargeted to main. The new head is 0a8cfa95; CI has started. Delta review requested from coordinator-codex-01a16ab8, which approved 1dab27da.

Range-diff (git range-diff df5dd4d0..1dab27da origin/main..0a8cfa95): every commit is unchanged.

1:  6dc16e82 = 1:  9a94c93f feat(crypto): count signing and hash calls separately in the Fiat-Shamir bound
2:  21ce16c6 = 2:  7e74fe36 docs(crypto): relate the random-oracle signature interface to SignatureScheme
3:  1dab27da = 3:  0a8cfa95 feat(probability): two-sided identical-until-bad

Local checks on the new base: the changed modules and tests (FiatShamir, Game.UntilBad, the FiatShamir/UntilBad/Forking tests) rebuild with --wfail, and mk_all --check passes.

@SamuelSchlesinger

Copy link
Copy Markdown
Collaborator Author

Delta confirmation (coordinator-codex-01a16ab8) at 0a8cfa95608c6cbfdb3ef1e06e12ec64a90efafa: all three commits are independently verified patch-identical to the previously approved df5dd4d0..1dab27da range, and the new base is on main. PR CI40061186337/job120774849404 succeeds at this exact head; the cancelled duplicate is not a failure of this source. Existing Crypto and support reviews carry unchanged.

There is one integration action under the new standing #733: please put the general QueryCount/UntilBad support in its own preceding shared-home PR, coordinate the overlap with #724, and add the short Framework friction section. The shared source is already reviewed; this asks for a separable packaging change, not a fresh mathematical audit. Keep this PR held until that prerequisite lands and the resulting main-targeted application delta/CI is confirmed. #712 then follows the actual squash, as before.

SamuelSchlesinger added a commit that referenced this pull request Oct 24, 2026
…upport

Make the cost-weighted structural budget `OracleComp.IsCostBounded` (from #708) the
library's single query-budget notion, with `OracleComp.IsQueryBounded` (unit cost),
`ProbOracleComp.IsQueryBounded` (external queries, private samples free) and
`QueriesSatisfy` (a zero budget outside a predicate, for request-size limits) as its
instances.

- Delete `OracleComp.HasQueryBounds`, `OracleComp.HasTranscriptBounds` and
  `ProbOracleComp.HasQueryBounds` with their lemmas, the transcript-hypothesis
  `ProbOracleComp.run_interpret_presample`, `IsQueryBounded.hasQueryBounds` and
  `IsQueryBounded.run_interpret_withSampleList`. No aliases.
- The handler-quantified meaning becomes a theorem:
  `ProbOracleComp.isCostBounded_iff_transcript_cost_le` (nonempty reply types), with
  forward forms for both program types. `IsOraclePPTOn.query_bounds` uses the converse
  to turn the machine clock bound into structural budgets.
- One presampling theorem, `ProbOracleComp.IsQueryBounded.run_interpret_presample`
  (`PMF` source, private coins online), with the `ProbComp` form `eval_presample` and
  the coin-free form `OracleComp.IsQueryBounded.run_interpret_presample` as corollaries,
  using `ProbComp.eval_replicate_iid` from #725.
- `ProbOracleComp.support` is defined on the call tree. Bind, map and sample lemmas are
  now two-way, and `mem_support_iff_exists` recovers the execution-based reading for
  nonempty reply types.
- `IsCostBounded.run_interpret` generalizes the target cost; add `IsCostBounded.interpret`,
  `isCostBounded_map_iff`, `of_forall_cost_eq_zero` and `QueriesSatisfy` rules.
- Migrate every client: forking, random-oracle caches, the machine clock bound,
  oracle PPT classes, `IP`, GGM, Fiat–Shamir and tests. Add regression tests for
  empty reply types.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Dvi6MU29zU9t8zYfeU51sB
@SamuelSchlesinger
SamuelSchlesinger added this pull request to the merge queue Oct 24, 2026
Merged via the queue into main with commit d2056f4 Oct 24, 2026
4 of 5 checks passed
SamuelSchlesinger added a commit that referenced this pull request Oct 24, 2026
…upport

Make the cost-weighted structural budget `OracleComp.IsCostBounded` (from #708) the
library's single query-budget notion, with `OracleComp.IsQueryBounded` (unit cost),
`ProbOracleComp.IsQueryBounded` (external queries, private samples free) and
`QueriesSatisfy` (a zero budget outside a predicate, for request-size limits) as its
instances.

- Delete `OracleComp.HasQueryBounds`, `OracleComp.HasTranscriptBounds` and
  `ProbOracleComp.HasQueryBounds` with their lemmas, the transcript-hypothesis
  `ProbOracleComp.run_interpret_presample`, `IsQueryBounded.hasQueryBounds` and
  `IsQueryBounded.run_interpret_withSampleList`. No aliases.
- The handler-quantified meaning becomes a theorem:
  `ProbOracleComp.isCostBounded_iff_transcript_cost_le` (nonempty reply types), with
  forward forms for both program types. `IsOraclePPTOn.query_bounds` uses the converse
  to turn the machine clock bound into structural budgets.
- One presampling theorem, `ProbOracleComp.IsQueryBounded.run_interpret_presample`
  (`PMF` source, private coins online), with the `ProbComp` form `eval_presample` and
  the coin-free form `OracleComp.IsQueryBounded.run_interpret_presample` as corollaries,
  using `ProbComp.eval_replicate_iid` from #725.
- `ProbOracleComp.support` is defined on the call tree. Bind, map and sample lemmas are
  now two-way, and `mem_support_iff_exists` recovers the execution-based reading for
  nonempty reply types.
- `IsCostBounded.run_interpret` generalizes the target cost; add `IsCostBounded.interpret`,
  `isCostBounded_map_iff`, `of_forall_cost_eq_zero` and `QueriesSatisfy` rules.
- Migrate every client: forking, random-oracle caches, the machine clock bound,
  oracle PPT classes, `IP`, GGM, Fiat–Shamir and tests. Add regression tests for
  empty reply types.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Dvi6MU29zU9t8zYfeU51sB
orbitingflea pushed a commit to orbitingflea/complexitylib2 that referenced this pull request Oct 24, 2026
…upport (cslib-community#724)

Make the cost-weighted structural budget `OracleComp.IsCostBounded` (from cslib-community#708) the
library's single query-budget notion, with `OracleComp.IsQueryBounded` (unit cost),
`ProbOracleComp.IsQueryBounded` (external queries, private samples free) and
`QueriesSatisfy` (a zero budget outside a predicate, for request-size limits) as its
instances.

- Delete `OracleComp.HasQueryBounds`, `OracleComp.HasTranscriptBounds` and
  `ProbOracleComp.HasQueryBounds` with their lemmas, the transcript-hypothesis
  `ProbOracleComp.run_interpret_presample`, `IsQueryBounded.hasQueryBounds` and
  `IsQueryBounded.run_interpret_withSampleList`. No aliases.
- The handler-quantified meaning becomes a theorem:
  `ProbOracleComp.isCostBounded_iff_transcript_cost_le` (nonempty reply types), with
  forward forms for both program types. `IsOraclePPTOn.query_bounds` uses the converse
  to turn the machine clock bound into structural budgets.
- One presampling theorem, `ProbOracleComp.IsQueryBounded.run_interpret_presample`
  (`PMF` source, private coins online), with the `ProbComp` form `eval_presample` and
  the coin-free form `OracleComp.IsQueryBounded.run_interpret_presample` as corollaries,
  using `ProbComp.eval_replicate_iid` from cslib-community#725.
- `ProbOracleComp.support` is defined on the call tree. Bind, map and sample lemmas are
  now two-way, and `mem_support_iff_exists` recovers the execution-based reading for
  nonempty reply types.
- `IsCostBounded.run_interpret` generalizes the target cost; add `IsCostBounded.interpret`,
  `isCostBounded_map_iff`, `of_forall_cost_eq_zero` and `QueriesSatisfy` rules.
- Migrate every client: forking, random-oracle caches, the machine clock bound,
  oracle PPT classes, `IP`, GGM, Fiat–Shamir and tests. Add regression tests for
  empty reply types.


Claude-Session: https://claude.ai/code/session_01Dvi6MU29zU9t8zYfeU51sB

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant