PRP/PRF switching lemma — oracles and the shared bad flag (Phase 1) #
Infrastructure for the information-theoretic switching lemma
|Pr[A^RF] − Pr[A^RP]| ≤ q(q−1)/2N, set up for the up-to-bad (Fundamental
Lemma) argument in the relational calculus.
The two oracles are identical until bad: both draw the same underlying
uniform value y on a cache miss and set the shared flag prp_bad to true
iff y already occurs in the oracle's image (a collision). On no collision
both behave identically (cache y); on a collision RF keeps y (with
replacement) while RP resamples a fresh value from the unused outputs
(without replacement). Because the flag is set from the same draw against the
same image, it agrees on both sides — exactly what relE.up_to_bad needs.
prp_bad : Variable Bool— the shared "a collision has occurred" flag.colliding_outputs h inp— outputs already used by inputs other thaninp(the values a fresh draw atinpwould collide with). Equal toinducing_set inp σwhenh = random_oracle_state.get σ.lazy_query_rf/lazy_query_rp— the RF and RP oracles.lazy_query_rf_inFootprint/lazy_query_rp_inFootprint— both touch onlyrandom_oracle_stateandprp_bad.
This is Phase 1 of the plan; the per-query coupling (Phase 2) and the Fundamental-Lemma assembly (Phases 3–4) build on these definitions.
Shared "a collision has occurred" flag for the switching coupling. Set to
true the first time a freshly drawn output already appears in the image;
both lazy_query_rf and lazy_query_rp maintain it identically.
Symmetric instances.
The symmetric forms of the OracleLoop RO-disjointness axioms, needed to frame the RO read/write inside the oracles against the scratch lenses.
Outputs already assigned to some input other than inp — i.e. the values
a fresh draw at inp would collide with, and (when inp is uncached) the
set RP must avoid to stay injective. Definitionally equal to
inducing_set inp σ when h = random_oracle_state.get σ.
Equations
- colliding_outputs h inp = Finset.image (fun (x' : input) => (h x').getD default) ({x' ∈ Finset.univ.erase inp | (h x').isSome = true})
Instances For
RF oracle (instrumented). Acts exactly like lazy_query on the RO
state, additionally setting prp_bad := true when the freshly drawn value
already occurs in the image (the draw creates a collision). Written with
explicit >>= to avoid the do-notation join-point macro on the if.
Equations
- One or more equations did not get rendered due to their size.
Instances For
RP oracle (instrumented, resample-on-collision). Draws the same y
and sets prp_bad on a collision exactly as lazy_query_rf. On a collision
it resamples uniformly from the unused outputs (univ \ colliding_outputs),
keeping the oracle injective; if the outputs are exhausted it falls through
(the flag is already set, so the returned value is immaterial).
Equations
- One or more equations did not get rendered due to their size.
Instances For
Closed-form weakest preconditions (Phase 2a) #
The per-query coupling marginals are computed against these closed forms.
The sub-distribution lazy_query_rp resamples from on a collision: uniform
over the unused outputs, or a point mass on y if all outputs are used.
Equations
- rp_resample_sub h inp y = if hne : (Finset.univ \ colliding_outputs h inp).Nonempty then GaudisCrypt.SubProbability.uniformOfFinset (Finset.univ \ colliding_outputs h inp) hne else pure y
Instances For
lazy_query_rf on a cache hit returns the cached value, no state change.
lazy_query_rp on a cache hit returns the cached value, no state change.
lazy_query_rf on a cache miss: uniform over the drawn value y, setting
the flag and caching y on a collision (with replacement).
lazy_query_rp on a cache miss: uniform over the drawn value y; on a
collision it sets the flag and resamples a fresh value (without
replacement) via rp_resample_sub, otherwise caches y.
lazy_query_rf touches only random_oracle_state and prp_bad: it lives in
the complement of any lens disjoint from both.
lazy_query_rp touches only random_oracle_state and prp_bad.
The per-query coupling (Phase 2b) #
Per-query coupling (the heart of the switching argument). From equal
states with the flag clear, lazy_query_rf and lazy_query_rp are coupled
so that the flag always agrees, and on no-collision runs the full
output-and-state result agrees. The collision branch is coupled by the
independent product (both flags are then true, so the post is satisfied
regardless).
Unary facts: mass one and flag preservation (Phase 3a) #
These feed the flag-set mode of the loop body, where the two games have
already diverged and are coupled only through rel.of_unary: each side
independently keeps prp_bad = true (with full mass).
Setting random_oracle_state doesn't change the flag.
lazy_query_rf is lossless.
lazy_query_rp is lossless.
Two-mode relational lifting (Phase 3b) #
A program that doesn't touch prp_bad almost surely keeps it true.
From losslessness and a.s.-flag-preservation, the full-mass flag form.
Two-mode relational lifting. For two programs over state related by a
precondition that (a) forces flag agreement and (b) when the flag is clear
forces synchronization (h_sync_*), the judgment lifts to the conditional
invariant. The flag-clear mode is given directly; the flag-set mode uses
rel.of_unary (each side independently keeps prp_bad set).
The loop body preserves the conditional invariant (Phase 3b) #
The switching loop body relates RF to RP at the conditional invariant.
oracle_step A with the RF oracle relates to the same with the RP oracle:
the flag always agrees, and as long as it is clear the states stay equal.
Requires A to leave prp_bad untouched and to be lossless.
Loop lifting and the Fundamental Lemma (Phase 3c) #
The whole q-round game relates RF to RP at the conditional invariant.
Switching inequality (Fundamental Lemma). For any state-functional G,
the RF game's expectation is at most the RP game's plus the probability that
RF triggered a collision (prp_bad). Starting from a common state.
Bounding the bad event (Phase 4) #
Caching a fresh input grows the RO size by exactly one (independent of any disjoint scratch state in the base).
One RF query bumps the bad-flag indicator by at most RO_size σ / N: the
flag flips only when the fresh draw lands in the (≤ RO_size)-element
collision set.
Generic oracle_step potential bump, parameterized by the oracle (the
lazy_query-specific oracle_step_wp_indicator_bump adapted).
One loop body bumps RO_size by at most one.
One loop body bumps the bad-flag indicator by at most RO_size σ / N.
The switching lemma (Phase 4) #
Bad-event bound. Starting from a clean state (flag clear, empty oracle),
the probability that q RF rounds set prp_bad is at most q(q−1)/2N. The
bad flag is itself a collision potential, so this is loop_n_birthday_bound
applied directly.
PRP/PRF switching lemma. For a distinguisher A (touching neither the
oracle table nor the internal flag, and lossless) outputting a [0,1]-valued
functional G, the RF and RP games differ by at most q(q−1)/2N:
Pr[A^RF : G] ≤ Pr[A^RP : G] + q(q−1)/2N.
Combines the Fundamental Lemma (switch_up_to_bad) with the birthday bound
on the bad event (loop_rf_bad_bound).