Lazy Sampling Infrastructure #
This file provides infrastructure for lazy/eager sampling equivalences, which are fundamental to PRF security proofs.
Key Definitions #
lazyEvalAbstract- Lazy evaluation of a random function at a given key- Equivalence theorems between lazy and eager sampling patterns
Motivation #
In PRF security proofs, the ideal game uses a "random function" that:
- On first query with key k: samples a fresh random value v, stores (k, v), returns v
- On repeated query with key k: returns the previously stored value
This is called "lazy sampling" because random values are sampled on demand. The alternative "eager sampling" samples all values upfront.
Both produce the same distribution, which is the key insight for PRF security.
Design #
Rather than working with concrete heap locations (which require Fintype coercions
and TypeName instances for map types), we parametrize over abstract lookup and
store operations. This provides a clean interface that can be instantiated
with any backing store implementation.
The abstract interface is:
lookup : K -> SPComp (Option V)- check if a key has a stored valuestore : K -> V -> SPComp Unit- store a key-value pair
References #
- SSProve PRF.v (Rocq): lazy sampling for PRF security games
- [Bellare & Rogaway, Introduction to Modern Cryptography]
Lazy Evaluation Pattern #
Lazy evaluation of a random function via an abstract table interface.
Given lookup and store operations, this evaluates the random function
at key k:
- If
khas been seen before (lookup returnssome v), return the stored value - If
kis new (lookup returnsnone), sample a fresh value, store it, and return it
This pattern captures the "ideal world" random function oracle in PRF security games.
Equations
- One or more equations did not get rendered due to their size.
Instances For
Eager Evaluation Pattern #
Eager evaluation: always sample fresh, ignoring the table.
This is a reference pattern used to state equivalence theorems. It simply samples a fresh value from V without any table interaction.
Instances For
Core Properties #
Unfolding lemma for lazyEvalAbstract.
When lookup returns none, lazy evaluation reduces to:
sample a fresh value, store it, and return it.
When lookup always returns none, lazy evaluation is equivalent to
sampling then storing.
This is the key property for the first query to a fresh key:
if the table has no entry for k, then lazyEvalAbstract behaves
as sample V >>= fun v => store k v >>= fun _ => pure v.
Proof: We unfold the definition and use the hypothesis that
lookup k is equivalent to pure none to simplify.
When lookup returns some v0, lazy evaluation returns v0 without sampling.
This is the key idempotency property: repeated queries with the same key return the previously stored value.
Relational Properties #
Reflexivity: a lazy evaluation oracle is equivalent to itself.
This follows directly from rHoare_refl.
When the lookup always returns none on both sides, lazy evaluation
produces the same distribution as fresh sampling (followed by store).
This is the core theorem connecting lazy and eager sampling for a single fresh query. Both sides:
- Call lookup (which returns none)
- Sample a fresh value
- Store it
- Return it
Since both sides execute the same code, they are trivially equivalent.
The more interesting case (proved below) is when we compare lazy evaluation to plain sampling without the store step.
For a single fresh query, lazy evaluation and plain sampling produce the same output distribution (though they may differ in heap effects due to the store operation).
Specifically, if lookup returns none, the VALUE returned by
lazyEvalAbstract is uniformly distributed over V, same as
SPComp.sample V.
The postcondition captures: the values are equal and both heaps have been updated consistently.
Random Function Oracle #
A random function oracle: evaluates lazily from a table.
This captures the "ideal world" of a PRF security game where the oracle answers with random values, but is consistent (same input always gives same output).
Equivalent to lazyEvalAbstract but named for clarity in
security game definitions.
Equations
- CatCrypt.LazySampling.randomFunctionOracle lookup store = CatCrypt.LazySampling.lazyEvalAbstract lookup store
Instances For
The random function oracle is definitionally equal to lazy evaluation.
For a single query on a fresh key, the random function oracle reduces to sampling followed by storing.
This is the bridge between:
- PRF ideal game (lazy random function)
- Simple uniform sampling (for each query)
The proof uses the fact that when the table has no entry for k, lazyEvalAbstract samples uniformly.
For a single query on a cached key, the random function oracle returns the cached value immediately.
Heap-Based Instantiation Helpers #
These helpers provide the connection between the abstract lookup/store
interface and concrete heap locations. Users instantiate these for their
specific table representation.
The typical pattern for a PRF proof is:
- Define a location
table_locwhose type isK -> V(total function) - Define a location
seen_locwhose type isK -> Bool(tracking queries) - Implement
lookupas: read seen, if seen[k] then read table[k] else none - Implement
storeas: write table[k] := v, write seen[k] := true - Apply the lazy sampling theorems
Construct a lookup function from heap get operations.
Given a "seen" predicate (which keys have been queried) and a
table read function, produces the lookup interface expected by
lazyEvalAbstract.
Note: This is a definitional helper, not a theorem. The actual implementation depends on the heap representation chosen by the user.
Equations
Instances For
Construct a store function from heap set operations.
Given write functions for the table and the seen map, produces
the store interface expected by lazyEvalAbstract.
Equations
- CatCrypt.LazySampling.mkStore writeTable writeSeen k v = do writeTable k v writeSeen k
Instances For
Lazy/Eager Equivalence for Dead Stores #
When the store operation is dead (its effects are not observed), lazy evaluation reduces to plain sampling. This simplification is useful when proving security for single-query games.
If the store has no observable effect (is equivalent to pure ()), then lazy evaluation on a fresh key is equivalent to plain sampling.
This captures the common case in single-query PRF proofs where the table update is never read again.
Corollary: with dead store and fresh key, lazy evaluation equals plain sampling.
Since (SPComp.sample V).bind pure = SPComp.sample V by the monad
right identity law, this gives a clean equivalence.
Pairwise Independence #
For multi-query PRF games, distinct keys produce independent random values. This section provides the relational statement.
Two lazy evaluations at distinct keys with dead stores and fresh lookups produce independent uniform values.
Given:
- Both keys are fresh (lookup returns none for both)
- The store operations are dead (not observed afterwards)
Then the joint distribution of (lazyEval k1, lazyEval k2) is the product of two uniform distributions.
When stores are dead, each lazy evaluation reduces to plain sampling
via lazyEval_fresh_eq_sample, making both sides identical.
This is fundamental for multi-query PRF security in single-query reductions where the table updates are not read again.
Unary Properties #
Unary property: lazy evaluation on a fresh key produces a value in the support of the uniform distribution.
Since lazy evaluation samples from SPComp.sample V when the key
is fresh, the output is uniformly distributed over V.
Unary property: lazy evaluation on a cached key returns exactly the cached value.
Lazy/Eager Sampling Tactics #
ssprove_eager rewrites lazy evaluation to eager sampling.
This tactic tries to apply lazy/eager sampling equivalences:
lazyEval_fresh_eq_sample— when lookup is fresh and store is dead, lazy evaluation equals plain samplinglazyEval_fresh— unfold lazy evaluation when lookup returns nonelazyEval_cached— when lookup returns a cached value
Side conditions (freshness and dead store) are discharged by rfl and simp.
Example:
-- Goal contains: lazyEvalAbstract lookup store k
-- After ssprove_eager (with fresh key and dead store):
-- Goal contains: SPComp.sample V
Equations
- CatCrypt.Tactics.tacticSsprove_eager = Lean.ParserDescr.node `CatCrypt.Tactics.tacticSsprove_eager 1024 (Lean.ParserDescr.nonReservedSymbol "ssprove_eager" false)
Instances For
ssprove_lazy_unfold unfolds the lazy evaluation abstraction and simplifies.
This is useful when you need to reason about the internal structure of
lazyEvalAbstract rather than using the high-level equivalence theorems.
Equations
- CatCrypt.Tactics.tacticSsprove_lazy_unfold = Lean.ParserDescr.node `CatCrypt.Tactics.tacticSsprove_lazy_unfold 1024 (Lean.ParserDescr.nonReservedSymbol "ssprove_lazy_unfold" false)