Query-Bounded Adversary Infrastructure #
This file provides infrastructure for modeling adversaries with bounded query access to oracles. This is fundamental to cryptographic security proofs where security bounds depend on the number of oracle queries.
Main definitions #
boundedOracleCall- Single oracle call with query countingqueryCountPre- Precondition: query count is below boundqueryCountPost- Postcondition: query count incremented by 1
Main results #
pHoare_bounded_call- pHL rule for a single bounded oracle callprEvent_bind_eq- Bad mass of a bind as the expectation of the continuation's bad massprEventComp_bind_step_le- Per-step subadditivity: one step adds at mostεprBad_induction- Adaptive union bound: Pr[bad after q calls] ≤ q * εadvantage_le_query_bound_derived- Advantage bound≤ q * εfrom per-step freshness
Design #
Rather than implementing query-bounded oracles as stateful wrappers (which would require specific location assignments), we provide:
Abstract query counting: A precondition/postcondition pair that tracks a query count value through pHL judgments.
Inductive probability bounds: Theorems that bound the probability of bad events by
q * εwhen each query contributes at mostε.Oracle call patterns: Composable patterns for reasoning about sequences of oracle calls.
This approach integrates with the existing pHL rules from Rules.lean
and the FEL machinery from Bridge.lean / FailureEvent.lean.
References #
- EasyCrypt:
phoarewith oracle call counting - Bellare & Rogaway: birthday bound via query counting
- Shoup: "Sequences of Games" (query counting methodology)
Query Counting via pHL Predicates #
Instead of tracking query counts in specific heap locations, we parametrize over an abstract "counter" function on the heap. This allows the user to choose their own counter representation.
A query counter is a function from heaps to natural numbers. The user defines this based on their specific heap layout.
Equations
Instances For
Precondition: the query counter is at value n
Equations
- CatCrypt.Unary.counterAt count n h = (count h = n)
Instances For
Precondition: the query counter is at most q
Equations
- CatCrypt.Unary.counterBounded count q h = (count h ≤ q)
Instances For
Postcondition: the counter was incremented by exactly 1
Equations
- CatCrypt.Unary.counterIncremented count x✝ h' = ∀ (h₀ : CatCrypt.Core.Heap), count h₀ + 1 = count h'
Instances For
Abstract Bounded Oracle Call #
A bounded oracle call is modeled as: check counter, if under budget then call oracle and increment counter, else return default/fail.
A single oracle call that increments a counter.
This represents the pattern:
let n ← get counter_loc
if n < q then
set counter_loc (n + 1)
oracle k
else
fail -- or return default
We model this abstractly: the computation oracleCall k is wrapped
with counter tracking.
Equations
- One or more equations did not get rendered due to their size.
Instances For
A single oracle call that always proceeds (no budget check). Used when the budget is known to be sufficient from context.
Equations
Instances For
pHL Rules for Query Counting #
pHoare rule for bounded oracle call: if the call computation satisfies a postcondition Q, then the full bounded oracle call also satisfies Q (since it either calls the oracle or returns default).
The user proves pHoare P oracleCall Q and
∀ h, P h → Q default h (the default case satisfies Q too),
and gets pHoare P (boundedOracleCall ...) Q.
Key theorem: after q calls to countedOracleCall, the counter
has been incremented q times.
This is stated abstractly: if we start with counter = 0 and make exactly q calls, the counter ends at q.
Inductive Probability Bounds #
The key result: if each oracle call has at most ε probability of triggering a bad event, and we make at most q calls, then the overall bad probability is at most q * ε.
Rather than proving this inductively over the number of calls (which requires tracking execution traces), we provide it as a composition lemma that the user applies.
Birthday bound pattern: q queries to a domain of size N gives collision probability at most q * (q-1) / (2 * N).
This is a definitional helper that states the standard birthday bound formula. The actual proof obligation is pushed to the user, who must show the per-query collision probability and compose with the query count.
Composition Patterns #
Reusable patterns for composing query-bounded computations.
Sequential composition preserves query bounds.
If c₁ uses at most q₁ queries and c₂ uses at most q₂ queries, then c₁ >> c₂ uses at most q₁ + q₂ queries.
If the counter starts at 0 and the computation makes at most q calls, the count is bounded by q. This is a simple wrapper for readability.
Adaptive q-Query Union Bound #
A q-fold loop of oracle calls, where each call adds at most ε to the
bad-event probability, has total bad probability at most q * ε.
The loop is repeatStep step q, an unrolled q-fold sequential composition of
a single step step : SPComp Unit. The bound is proved by induction on q
from a per-step increment hypothesis; that increment is in turn derived from a
single-step freshness bound via prEventComp_bind_step_le.
The total mass of a sub-distribution is at most one.
A q-fold sequential iteration of a single step computation.
repeatStep step 0 is the trivial computation; repeatStep step (q+1)
runs repeatStep step q and then one more step.
Equations
- CatCrypt.Unary.repeatStep step 0 = CatCrypt.Core.SPComp.pure ()
- CatCrypt.Unary.repeatStep step q.succ = (CatCrypt.Unary.repeatStep step q).bind fun (x : Unit) => step
Instances For
The bad-event probability of a point mass at (a, h) is the indicator of
bad h: 1 if bad h, else 0.
The bad-event probability of a pure computation from h₀ is the indicator
of bad h₀: 1 if bad h₀, else 0.
Pointwise value of a sub-distribution bind at a some outcome: the
probability sums, over reachable intermediate outcomes, the intermediate
probability times the continuation probability.
Bad-mass of a sub-distribution bind: the event probability of d.bind g
is the d-expectation of the event probability of the continuation g
started from each reachable intermediate outcome.
Decomposition of the bad-event mass of a loop step: the bad mass of
m.bind (fun _ => step) is the m-expectation of the bad mass of step
started from each reachable intermediate heap.
Per-step subadditivity of bad mass: if from every non-bad reachable heap the
single step introduces bad with probability at most ε, then prepending any
prefix m adds at most ε to the bad probability.
Adaptive q-query union bound.
A q-fold loop of step, where each single step from a non-bad heap
introduces the bad event with probability at most ε, has total bad
probability at most q * ε.
Proved by induction on q: the base case is 0 (the initial heap is
non-bad); the step case combines the inductive hypothesis with the per-step
increment prEventComp_bind_step_le.
Adaptive q-query union bound over a loop of counted oracle calls.
If step from any non-bad heap introduces the bad event with probability at
most ε, and the initial heap is non-bad, then after q iterations the bad
probability is at most q * ε. Derived from the per-step freshness
hypothesis by prBad_induction, not assumed.
A q-query loop presented as a Bool game: run repeatStep step q, then
return the fixed answer b₀. The tail SPComp.pure b₀ leaves the heap
unchanged, so it does not affect the bad-event probability.
Equations
- CatCrypt.Unary.loopGame step q b₀ = (CatCrypt.Unary.repeatStep step q).bind fun (x : Unit) => CatCrypt.Core.SPComp.pure b₀
Instances For
The bad-event probability of loopGame equals that of the underlying loop:
the deterministic Bool-returning tail preserves the final heap.
Adaptive q-query union bound as an advantage bound.
For the q-query game loopGame step q b₀, if each step from a non-bad heap
introduces the bad event with probability at most ε, and the game agrees
with G₁ on all non-bad outcomes (and is lossless), then the advantage is at
most q * ε. The bad bound is supplied by prBad_induction, not assumed.
Integration with Failure Event Lemma #
Combine query bounds with the FEL to obtain concrete security bounds.
Combined FEL + query bound: advantage ≤ q * ε.
Given:
- Games agree on non-bad outcomes
- G₀ is lossless
- Bad probability under G₀ is at most q * ε Then advantage is at most q * ε.
Combined FEL + birthday bound: advantage ≤ q²/(2N).
Specialization of advantage_le_query_bound for the common
birthday-bound case (e.g., PRF/PRP switching).