Skip to content
PQ Stealth Addresses

ROMUpToBad

VCVio's tvDist_simulateQ_randomOracle_withProgramming_le_probEvent_bad bounds the distance between a lazily sampled random oracle and a programmed one (the oracle forced to answer a chosen point with a chosen value) by the probability that the programmed point is queried. It is stated for computations whose only oracle is the hash. Our ROM games live over unifSpec + hashSpec — the adversary also samples — and are simulated by unifFwdImpl hashSpec + hashSpec.randomOracle (dksapRomImpl in DKSAPOracle, romImpl in BlindingROM).

Proved, for that shape (everything here is stated over an arbitrary hashSpec):

  • tvDist_programmedROImpl_trackingROImpl_le: the engine bound (tvDist_simulateQ_run_le_probEvent_output_bad) for unifFwd + withProgramming policy vs unifFwd + withCachingTrackingPolicy policy, with the programming flag as the bad event;
  • relTriple_run'_programmedROImpl_roImpl: the programmed run from (cache₀, false) is, on the output marginal (evalDist_eq_of_relTriple_eqRel), the plain random oracle run from the cache cache₀ overridden by the policy;
  • relTriple_run'_trackingROImpl_roImpl: the tracking run is, on the output marginal, the plain random oracle run from cache₀;
  • tvDist_run'_roImpl_cacheOr_le_probEvent_bad: the three combined — the distance between the random oracle run from cache₀ ⊕ policy and from cache₀ is at most the probability that the policy fires. This is the lemma DKSAPOracle and BlindingROM consume.

unifFwdFlagImpl

Uniform forwarding lifted to the flagged cache state QueryCache × Bool.

noncomputable def unifFwdFlagImpl (hashSpec : OracleSpec ι) :
    QueryImpl unifSpec (StateT (hashSpec.QueryCache × Bool) ProbComp) :=
  (HasQuery.toQueryImpl (spec := unifSpec) (m := ProbComp)).liftTarget
    (StateT (hashSpec.QueryCache × Bool) ProbComp)

ROMUpToBad.lean:43-47 ↗

roImpl

noncomputable def roImpl (hashSpec : OracleSpec ι)
    [∀ t : hashSpec.Domain, SampleableType (hashSpec.Range t)] :
    QueryImpl (unifSpec + hashSpec) (StateT hashSpec.QueryCache ProbComp) :=
  unifFwdImpl hashSpec + hashSpec.randomOracle

ROMUpToBad.lean:50-54 ↗

programmedROImpl

noncomputable def programmedROImpl (policy : hashSpec.ProgrammingPolicy) :
    QueryImpl (unifSpec + hashSpec) (StateT (hashSpec.QueryCache × Bool) ProbComp) :=
  unifFwdFlagImpl hashSpec + QueryImpl.withProgramming uniformSampleImpl policy

ROMUpToBad.lean:57-60 ↗

trackingROImpl

noncomputable def trackingROImpl (policy : hashSpec.ProgrammingPolicy) :
    QueryImpl (unifSpec + hashSpec) (StateT (hashSpec.QueryCache × Bool) ProbComp) :=
  unifFwdFlagImpl hashSpec + QueryImpl.withCachingTrackingPolicy uniformSampleImpl policy

ROMUpToBad.lean:63-66 ↗

The implementations

cacheOr

A cache overridden by a policy: the cache where it is defined, the policy otherwise.

def cacheOr (cache : hashSpec.QueryCache) (policy : hashSpec.ProgrammingPolicy) :
    hashSpec.QueryCache :=
  fun t => (cache t).or (policy t)
 
omit [DecidableEq ι] in

ROMUpToBad.lean:76-80 ↗

cacheOr_apply

@[simp] theorem cacheOr_apply (cache : hashSpec.QueryCache) (t : hashSpec.Domain) :
    cacheOr cache policy t = (cache t).or (policy t) := rfl

ROMUpToBad.lean:82-84 ↗

cacheOr_cacheQuery

theorem cacheOr_cacheQuery (cache : hashSpec.QueryCache) (t : hashSpec.Domain)
    (u : hashSpec.Range t) :
    cacheOr (cache.cacheQuery t u) policy = (cacheOr cache policy).cacheQuery t u := by
proof
  funext t'
  by_cases h : t' = t
  · subst h
    simp only [cacheOr_apply, QueryCache.cacheQuery_self, Option.some_or]
  · simp only [cacheOr_apply, QueryCache.cacheQuery_of_ne _ _ h]

ROMUpToBad.lean:86-94 ↗

cacheOr_cacheQuery_of_policy

theorem cacheOr_cacheQuery_of_policy (cache : hashSpec.QueryCache)
    (t : hashSpec.Domain) (u : hashSpec.Range t) (hc : cache t = none) (hp : policy t = some u) :
    cacheOr (cache.cacheQuery t u) policy = cacheOr cache policy := by
proof
  funext t'
  by_cases h : t' = t
  · subst h
    simp only [cacheOr_apply, QueryCache.cacheQuery_self, Option.some_or, hc, hp,
      Option.none_or]
  · simp only [cacheOr_apply, QueryCache.cacheQuery_of_ne _ _ h]

ROMUpToBad.lean:97-106 ↗

A cache overridden by a policy

unifFwdFlagImpl_run

theorem unifFwdFlagImpl_run (n : ℕ) (s : hashSpec.QueryCache × Bool) :
    (unifFwdFlagImpl hashSpec n).run s = (fun x => (x, s)) <$> ($[0..n]) :=
  rfl

ROMUpToBad.lean:113-116 ↗

relTriple_unifFwdFlagImpl_unifFwdImpl

The uniform forwarders are coupled step by step under any state relation.

theorem relTriple_unifFwdFlagImpl_unifFwdImpl
    (R : hashSpec.QueryCache × Bool → hashSpec.QueryCache → Prop) (n : ℕ)
    (s₁ : hashSpec.QueryCache × Bool) (s₂ : hashSpec.QueryCache) (hs : R s₁ s₂) :
    RelTriple ((unifFwdFlagImpl hashSpec n).run s₁) ((unifFwdImpl hashSpec n).run s₂)
      (fun p₁ p₂ => p₁.1 = p₂.1 ∧ R p₁.2 p₂.2) := by
proof
  rw [unifFwdFlagImpl_run, unifFwdImpl_run]
  refine relTriple_map (relTriple_post_mono (relTriple_refl _) ?_)
  intro x y hxy
  exact ⟨hxy, hs⟩

ROMUpToBad.lean:123-132 ↗

Running the uniform forwarders

probOutput_withProgramming_eq_withCachingTrackingPolicy_of_not_bad_output

On a non-bad input, the programmed and tracking hash handlers agree on every non-bad output. (VCVio proves this for its own bridge but keeps it private.)

theorem probOutput_withProgramming_eq_withCachingTrackingPolicy_of_not_bad_output
    (t : hashSpec.Domain) (cache : hashSpec.QueryCache)
    (u : hashSpec.Range t) (cache' : hashSpec.QueryCache) :
    Pr[= (u, (cache', false)) |
        (QueryImpl.withProgramming uniformSampleImpl policy t).run (cache, false)] =
      Pr[= (u, (cache', false)) |
        (QueryImpl.withCachingTrackingPolicy uniformSampleImpl policy t).run (cache, false)] := by
proof
  classical
  cases hcache : cache t with
  | some v =>
    simp only [QueryImpl.withProgramming_apply, StateT.run_mk, hcache, probOutput_pure,
      Prod.mk.injEq, and_true, QueryImpl.withCachingTrackingPolicy_apply, Bool.if_true_left,
      Bool.decide_eq_true, Functor.map_map]
  | none =>
    cases hpol : policy t with
    | none =>
      simp only [QueryImpl.withProgramming_apply, hpol, Functor.map_map, StateT.run_mk, hcache,
        QueryImpl.withCachingTrackingPolicy_apply, Option.isSome_none, Bool.false_eq_true,
        ↓reduceIte]
    | some v =>
      have hne : ∀ (w : hashSpec.Range t) (c : hashSpec.QueryCache),
          ((u, (cache', false)) : hashSpec.Range t × hashSpec.QueryCache × Bool)
            ≠ (w, (c, true)) := by
        intro w c hcontr
        injection hcontr with _ h2
        injection h2 with _ h3
        cases h3
      have hL : (QueryImpl.withProgramming uniformSampleImpl policy t).run (cache, false) =
          (pure (v, (cache.cacheQuery t v, true)) :
            ProbComp (hashSpec.Range t × hashSpec.QueryCache × Bool)) := by
        simp only [QueryImpl.withProgramming_apply, hpol, map_pure, StateT.run_mk, hcache]
      have hR : (QueryImpl.withCachingTrackingPolicy uniformSampleImpl policy t).run
            (cache, false) =
          (uniformSampleImpl t >>= fun u' => pure (u', (cache.cacheQuery t u', true)) :
            ProbComp (hashSpec.Range t × hashSpec.QueryCache × Bool)) := by
        simp only [QueryImpl.withCachingTrackingPolicy_apply, hpol, Option.isSome_some, ↓reduceIte,
          Functor.map_map, StateT.run_mk, hcache, bind_pure_comp]
      rw [hL, hR, probOutput_pure, if_neg (hne v _), probOutput_bind_eq_tsum]
      symm
      refine ENNReal.tsum_eq_zero.mpr (fun u' => ?_)
      rw [probOutput_pure, if_neg (hne u' _), mul_zero]

ROMUpToBad.lean:144-185 ↗

tvDist_programmedROImpl_trackingROImpl_le

axioms: propext, Classical.choice, Quot.sound · sorry-free ✓

theorem tvDist_programmedROImpl_trackingROImpl_le {α : Type}
    (oa : OracleComp (unifSpec + hashSpec) α) (cache₀ : hashSpec.QueryCache) :
    tvDist ((simulateQ (programmedROImpl policy) oa).run (cache₀, false))
        ((simulateQ (trackingROImpl policy) oa).run (cache₀, false))
      ≤ Pr[fun z : α × hashSpec.QueryCache × Bool => z.2.2 = true |
          (simulateQ (programmedROImpl policy) oa).run (cache₀, false)].toReal := by
proof
  refine OracleComp.ProgramLogic.Relational.tvDist_simulateQ_run_le_probEvent_output_bad
    (programmedROImpl policy) (trackingROImpl policy) oa cache₀ ?_ ?_ ?_
  · rintro (n | t) s u s'
    · rfl
    · exact probOutput_withProgramming_eq_withCachingTrackingPolicy_of_not_bad_output
        policy t s u s'
  · rintro (n | t) ⟨c, b⟩ hp z hz
    · cases (show b = true from hp)
      simp only [add_apply_inl, programmedROImpl, QueryImpl.add_apply_inl, unifFwdFlagImpl_run,
        support_map, ProbComp.support_uniformFin, Set.image_univ] at hz
      obtain ⟨x, rfl⟩ := hz
      rfl
    · cases (show b = true from hp)
      exact QueryImpl.withProgramming_bad_monotone (so := uniformSampleImpl) (policy := policy)
        t c z hz
  · rintro (n | t) ⟨c, b⟩ hp z hz
    · cases (show b = true from hp)
      simp only [add_apply_inl, trackingROImpl, QueryImpl.add_apply_inl, unifFwdFlagImpl_run,
        support_map, ProbComp.support_uniformFin, Set.image_univ] at hz
      obtain ⟨x, rfl⟩ := hz
      rfl
    · cases (show b = true from hp)
      exact QueryImpl.withCachingTrackingPolicy_bad_monotone (so := uniformSampleImpl)
        (policy := policy) t c z hz

ROMUpToBad.lean:189-219 ↗

The engine: programmed vs tracking, identical until the flag fires

relTriple_run'_programmedROImpl_roImpl

Programmed = cached. Running the programmed oracle from (cache₀, false) has the same output distribution as running the plain random oracle from cache₀ overridden by the policy.

theorem relTriple_run'_programmedROImpl_roImpl {α : Type}
    (oa : OracleComp (unifSpec + hashSpec) α) (cache₀ : hashSpec.QueryCache) :
    RelTriple ((simulateQ (programmedROImpl policy) oa).run' (cache₀, false))
      ((simulateQ (roImpl hashSpec) oa).run' (cacheOr cache₀ policy)) (EqRel α) := by
proof
  refine relTriple_simulateQ_run' (programmedROImpl policy) (roImpl hashSpec)
    (fun p c => c = cacheOr p.1 policy) oa ?_ (cache₀, false) _ rfl
  rintro (n | t) ⟨c, bad⟩ c' hc'
  · exact relTriple_unifFwdFlagImpl_unifFwdImpl (R := fun p c => c = cacheOr p.1 policy) n
      (c, bad) c' hc'
  · subst hc'
    simp only [programmedROImpl, roImpl, QueryImpl.add_apply_inr, OracleSpec.randomOracle]
    cases hcache : c t with
    | some v =>
      simp only [add_apply_inr, QueryImpl.withProgramming_apply, StateT.run_mk, hcache,
        QueryImpl.withCaching_apply, StateT.run_bind, StateT.run_get, pure_bind, cacheOr_apply,
        Option.some_or, StateT.run_pure, relTriple_iff_relWP, MAlgRelOrdered.relWP_pure, and_self]
    | none =>
      cases hpol : policy t with
      | some p =>
        simp only [add_apply_inr, QueryImpl.withProgramming_apply, hpol, map_pure, StateT.run_mk,
          hcache, QueryImpl.withCaching_apply, StateT.run_bind, StateT.run_get, pure_bind,
          cacheOr_apply, Option.or_some, Option.getD_none, StateT.run_pure, relTriple_iff_relWP,
          MAlgRelOrdered.relWP_pure, true_and]
        exact (cacheOr_cacheQuery_of_policy policy c t p hcache hpol).symm
      | none =>
        simp only [add_apply_inr, QueryImpl.withProgramming_apply, hpol, Functor.map_map,
          StateT.run_mk, hcache, QueryImpl.withCaching_apply, StateT.run_bind, StateT.run_get,
          pure_bind, cacheOr_apply, Option.or_self, StateT.run_monadLift, monadLift_self,
          bind_pure_comp, StateT.run_modifyGet]
        refine relTriple_map (relTriple_post_mono (relTriple_refl _) ?_)
        intro x y hxy
        cases hxy
        exact ⟨rfl, (cacheOr_cacheQuery policy c t x).symm⟩

ROMUpToBad.lean:233-266 ↗

relTriple_run'_trackingROImpl_roImpl

theorem relTriple_run'_trackingROImpl_roImpl {α : Type}
    (oa : OracleComp (unifSpec + hashSpec) α) (cache₀ : hashSpec.QueryCache) :
    RelTriple ((simulateQ (trackingROImpl policy) oa).run' (cache₀, false))
      ((simulateQ (roImpl hashSpec) oa).run' cache₀) (EqRel α) := by
proof
  refine relTriple_simulateQ_run' (trackingROImpl policy) (roImpl hashSpec)
    (fun p c => c = p.1) oa ?_ (cache₀, false) _ rfl
  rintro (n | t) ⟨c, bad⟩ c' hc'
  · exact relTriple_unifFwdFlagImpl_unifFwdImpl (R := fun p c => c = p.1) n (c, bad) c' hc'
  · subst hc'
    simp only [trackingROImpl, roImpl, QueryImpl.add_apply_inr, OracleSpec.randomOracle]
    cases hcache : c' t with
    | some v =>
      simp only [add_apply_inr, QueryImpl.withCachingTrackingPolicy_apply, Bool.if_true_left,
        Bool.decide_eq_true, Functor.map_map, StateT.run_mk, hcache, QueryImpl.withCaching_apply,
        StateT.run_bind, StateT.run_get, pure_bind, StateT.run_pure, relTriple_iff_relWP,
        MAlgRelOrdered.relWP_pure, and_self]
    | none =>
      simp only [add_apply_inr, QueryImpl.withCachingTrackingPolicy_apply, Bool.if_true_left,
        Bool.decide_eq_true, Functor.map_map, StateT.run_mk, hcache, QueryImpl.withCaching_apply,
        StateT.run_bind, StateT.run_get, pure_bind, StateT.run_monadLift, monadLift_self,
        bind_pure_comp, StateT.run_modifyGet]
      refine relTriple_map (relTriple_post_mono (relTriple_refl _) ?_)
      intro x y hxy
      cases hxy
      exact ⟨rfl, rfl⟩

ROMUpToBad.lean:269-294 ↗

tvDist_run'_roImpl_cacheOr_le_probEvent_bad

axioms: propext, Classical.choice, Quot.sound · sorry-free ✓

theorem tvDist_run'_roImpl_cacheOr_le_probEvent_bad {α : Type}
    (oa : OracleComp (unifSpec + hashSpec) α) (cache₀ : hashSpec.QueryCache) :
    tvDist ((simulateQ (roImpl hashSpec) oa).run' (cacheOr cache₀ policy))
        ((simulateQ (roImpl hashSpec) oa).run' cache₀)
      ≤ Pr[fun z : α × hashSpec.QueryCache × Bool => z.2.2 = true |
          (simulateQ (programmedROImpl policy) oa).run (cache₀, false)].toReal := by
proof
  have hB := evalDist_eq_of_relTriple_eqRel (relTriple_run'_programmedROImpl_roImpl policy oa cache₀)
  have hC := evalDist_eq_of_relTriple_eqRel (relTriple_run'_trackingROImpl_roImpl policy oa cache₀)
  calc tvDist ((simulateQ (roImpl hashSpec) oa).run' (cacheOr cache₀ policy))
          ((simulateQ (roImpl hashSpec) oa).run' cache₀)
      = tvDist ((simulateQ (programmedROImpl policy) oa).run' (cache₀, false))
          ((simulateQ (trackingROImpl policy) oa).run' (cache₀, false)) := by
        rw [tvDist, tvDist, hB, hC]
    _ ≤ tvDist ((simulateQ (programmedROImpl policy) oa).run (cache₀, false))
          ((simulateQ (trackingROImpl policy) oa).run (cache₀, false)) := by
        rw [StateT.run', StateT.run']
        exact tvDist_map_le Prod.fst _ _
    _ ≤ _ := tvDist_programmedROImpl_trackingROImpl_le policy oa cache₀

ROMUpToBad.lean:298-316 ↗

probEvent_flag_programmed_eq_tracking

axioms: propext, Classical.choice, Quot.sound · sorry-free ✓

theorem probEvent_flag_programmed_eq_tracking {α : Type}
    (oa : OracleComp (unifSpec + hashSpec) α) (cache₀ : hashSpec.QueryCache) :
    Pr[fun z => z.2.2 = true |
        (simulateQ (programmedROImpl policy) oa).run (cache₀, false)] =
      Pr[fun z => z.2.2 = true |
        (simulateQ (trackingROImpl policy) oa).run (cache₀, false)] := by
proof
  refine OracleComp.ProgramLogic.Relational.probEvent_output_bad_eq'
    (programmedROImpl policy) (trackingROImpl policy) ?_ ?_ ?_ oa cache₀
  · rintro (n | t) s u s'
    · rfl
    · exact probOutput_withProgramming_eq_withCachingTrackingPolicy_of_not_bad_output
        policy t s u s'
  · rintro (n | t) ⟨c, b⟩ hp z hz
    · cases (show b = true from hp)
      simp only [add_apply_inl, programmedROImpl, QueryImpl.add_apply_inl, unifFwdFlagImpl_run,
        support_map, ProbComp.support_uniformFin, Set.image_univ] at hz
      obtain ⟨x, rfl⟩ := hz
      rfl
    · cases (show b = true from hp)
      exact QueryImpl.withProgramming_bad_monotone (so := uniformSampleImpl) (policy := policy)
        t c z hz
  · rintro (n | t) ⟨c, b⟩ hp z hz
    · cases (show b = true from hp)
      simp only [add_apply_inl, trackingROImpl, QueryImpl.add_apply_inl, unifFwdFlagImpl_run,
        support_map, ProbComp.support_uniformFin, Set.image_univ] at hz
      obtain ⟨x, rfl⟩ := hz
      rfl
    · cases (show b = true from hp)
      exact QueryImpl.withCachingTrackingPolicy_bad_monotone (so := uniformSampleImpl)
        (policy := policy) t c z hz

ROMUpToBad.lean:321-351 ↗

Projections: programmed and tracking runs against the plain random oracle

pointPolicy

The policy programming one point t ↦ u.

def pointPolicy (t : hashSpec.Domain) (u : hashSpec.Range t) : hashSpec.ProgrammingPolicy :=
  fun t' => if h : t' = t then some (h ▸ u) else none
 
omit [∀ t : hashSpec.Domain, SampleableType (hashSpec.Range t)] in

ROMUpToBad.lean:361-364 ↗

cacheOr_empty_pointPolicy

theorem cacheOr_empty_pointPolicy (t : hashSpec.Domain) (u : hashSpec.Range t) :
    cacheOr ∅ (pointPolicy t u) = (∅ : hashSpec.QueryCache).cacheQuery t u := by
proof
  funext t'
  by_cases h : t' = t
  · subst h
    simp only [cacheOr_apply, QueryCache.empty_apply, Option.none_or, pointPolicy, dite_true,
      QueryCache.cacheQuery_self]
  · simp only [cacheOr_apply, QueryCache.empty_apply, Option.none_or, pointPolicy, h, dite_false,
      QueryCache.cacheQuery_of_ne _ _ h]

ROMUpToBad.lean:366-375 ↗

badQueryGame

noncomputable def badQueryGame {γ : Type} (pre : ProbComp γ)
    (oa : γ → OracleComp (unifSpec + hashSpec) Bool)
    (pt : γ → hashSpec.Domain) (val : (x : γ) → hashSpec.Range (pt x)) : ProbComp Bool :=
  pre >>= fun x =>
    (fun z => z.2.2) <$>
      (simulateQ (programmedROImpl (pointPolicy (pt x) (val x))) (oa x)).run (∅, false)

ROMUpToBad.lean:377-383 ↗

boolDistAdvantage_run'_cacheQuery_run'_empty_le

axioms: propext, Classical.choice, Quot.sound · sorry-free ✓

theorem boolDistAdvantage_run'_cacheQuery_run'_empty_le {γ : Type} (pre : ProbComp γ)
    (oa : γ → OracleComp (unifSpec + hashSpec) Bool)
    (pt : γ → hashSpec.Domain) (val : (x : γ) → hashSpec.Range (pt x)) :
    ProbComp.boolDistAdvantage
      (pre >>= fun x => (simulateQ (roImpl hashSpec) (oa x)).run'
        ((∅ : hashSpec.QueryCache).cacheQuery (pt x) (val x)))
      (pre >>= fun x => (simulateQ (roImpl hashSpec) (oa x)).run' ∅)
      ≤ (Pr[= true | badQueryGame pre oa pt val]).toReal := by
proof
  classical
  set f : γ → ProbComp Bool := fun x =>
    (simulateQ (roImpl hashSpec) (oa x)).run' ((∅ : hashSpec.QueryCache).cacheQuery (pt x) (val x))
  set g : γ → ProbComp Bool := fun x => (simulateQ (roImpl hashSpec) (oa x)).run' ∅
  set bad : γ → ℝ≥0∞ := fun x =>
    Pr[fun z : Bool × hashSpec.QueryCache × Bool => z.2.2 = true |
      (simulateQ (programmedROImpl (pointPolicy (pt x) (val x))) (oa x)).run (∅, false)]
  have hterm : ∀ x, tvDist (f x) (g x) ≤ (bad x).toReal := by
    intro x
    have := tvDist_run'_roImpl_cacheOr_le_probEvent_bad (pointPolicy (pt x) (val x)) (oa x) ∅
    rwa [cacheOr_empty_pointPolicy] at this
  have hbadGame : Pr[= true | badQueryGame pre oa pt val] = ∑' x, Pr[= x | pre] * bad x := by
    rw [badQueryGame, probOutput_bind_eq_tsum]
    refine tsum_congr fun x => ?_
    rw [probOutput_map]
  have hne_top : ∀ x, Pr[= x | pre] * bad x ≠ ⊤ := fun x =>
    ENNReal.mul_ne_top (ne_top_of_le_ne_top one_ne_top probOutput_le_one)
      (ne_top_of_le_ne_top one_ne_top probEvent_le_one)
  have hsum_ne_top : (∑' x, Pr[= x | pre] * bad x) ≠ ⊤ := by
    rw [← hbadGame]
    exact ne_top_of_le_ne_top one_ne_top probOutput_le_one
  have hsummable : Summable fun x => (Pr[= x | pre] * bad x).toReal :=
    ENNReal.summable_toReal hsum_ne_top
  calc ProbComp.boolDistAdvantage (pre >>= f) (pre >>= g)
      ≤ tvDist (pre >>= f) (pre >>= g) := abs_probOutput_toReal_sub_le_tvDist _ _
    _ ≤ ∑' x, Pr[= x | pre].toReal * tvDist (f x) (g x) := tvDist_bind_left_le pre f g
    _ ≤ ∑' x, (Pr[= x | pre] * bad x).toReal := by
        refine Summable.tsum_le_tsum (fun x => ?_) ?_ hsummable
        · rw [ENNReal.toReal_mul]
          exact mul_le_mul_of_nonneg_left (hterm x) ENNReal.toReal_nonneg
        · exact hsummable.of_nonneg_of_le
            (fun x => mul_nonneg ENNReal.toReal_nonneg (tvDist_nonneg _ _))
            (fun x => by
              rw [ENNReal.toReal_mul]
              exact mul_le_mul_of_nonneg_left (hterm x) ENNReal.toReal_nonneg)
    _ = (Pr[= true | badQueryGame pre oa pt val]).toReal := by
        rw [hbadGame, ENNReal.tsum_toReal_eq hne_top]

ROMUpToBad.lean:387-432 ↗

Programming a single point, and the averaged bound

probEvent_bind_le_const

A bound that holds on every support point of the continuation bounds the bind.

axioms: propext, Classical.choice, Quot.sound · sorry-free ✓

theorem probEvent_bind_le_const {α β : Type} (mx : ProbComp α) (my : α → ProbComp β)
    (q : β → Prop) (c : ℝ≥0∞) (h : ∀ x ∈ support mx, Pr[q | my x] ≤ c) :
    Pr[q | mx >>= my] ≤ c := by
proof
  rw [probEvent_bind_eq_tsum]
  calc ∑' x, Pr[= x | mx] * Pr[q | my x]
      ≤ ∑' x, Pr[= x | mx] * c := by
        refine ENNReal.tsum_le_tsum fun x => ?_
        by_cases hx : x ∈ support mx
        · exact mul_le_mul' le_rfl (h x hx)
        · simp [probOutput_eq_zero_of_not_mem_support hx]
    _ = (∑' x, Pr[= x | mx]) * c := ENNReal.tsum_mul_right
    _ ≤ 1 * c := mul_le_mul' tsum_probOutput_le_one le_rfl
    _ = c := one_mul c

ROMUpToBad.lean:453-466 ↗

probEvent_flag_trackingROImpl_pointPolicy_le

axioms: propext, Classical.choice, Quot.sound · sorry-free ✓

theorem probEvent_flag_trackingROImpl_pointPolicy_le {α : Type}
    (oa : OracleComp (unifSpec + hashSpec) α) (pt : hashSpec.Domain) (v : hashSpec.Range pt)
    (cache₀ : hashSpec.QueryCache) (h₀ : cache₀ pt = none) :
    Pr[fun z => z.2.2 = true |
        (simulateQ (trackingROImpl (pointPolicy pt v)) oa).run (cache₀, false)] ≤
      Pr[fun z => QueryCache.isCached z.2 pt = true | (simulateQ (roImpl hashSpec) oa).run cache₀] := by
proof
  have hrel := relTriple_simulateQ_run (trackingROImpl (pointPolicy pt v)) (roImpl hashSpec)
    (fun p c => c = p.1 ∧ p.2 = (p.1 pt).isSome) oa ?_ (cache₀, false) cache₀ ⟨rfl, by simp [h₀]⟩
  · refine probEvent_le_of_relTriple hrel ?_
    rintro a b ⟨-, hc, hflag⟩ hbad
    show (b.2 pt).isSome = true
    rw [hc, ← hflag]
    exact hbad
  · rintro (n | t) ⟨c, bad⟩ c' ⟨hc', hbad⟩
    · exact relTriple_unifFwdFlagImpl_unifFwdImpl
        (R := fun p c => c = p.1 ∧ p.2 = (p.1 pt).isSome) n (c, bad) c' ⟨hc', hbad⟩
    · subst hc'
      simp only [trackingROImpl, roImpl, QueryImpl.add_apply_inr, OracleSpec.randomOracle]
      cases hcache : c' t with
      | some w =>
        simp only [add_apply_inr, QueryImpl.withCachingTrackingPolicy_apply, Functor.map_map,
          StateT.run_mk, hcache, QueryImpl.withCaching_apply, StateT.run_bind, StateT.run_get,
          pure_bind, StateT.run_pure, relTriple_iff_relWP, MAlgRelOrdered.relWP_pure]
        refine ⟨trivial, trivial, ?_⟩
        simpa using hbad
      | none =>
        simp only [add_apply_inr, QueryImpl.withCachingTrackingPolicy_apply, Functor.map_map,
          StateT.run_mk, hcache, QueryImpl.withCaching_apply, StateT.run_bind, StateT.run_get,
          pure_bind, StateT.run_monadLift, monadLift_self, bind_pure_comp, StateT.run_modifyGet]
        refine relTriple_map (relTriple_post_mono (relTriple_refl _) ?_)
        intro x y hxy
        cases hxy
        dsimp only at hbad ⊢
        refine ⟨rfl, rfl, ?_⟩
        by_cases ht : t = pt
        · subst ht
          simp [pointPolicy]
        · simp only [pointPolicy, ht, dite_false, Option.isSome_none, Bool.false_eq_true,
            if_false, QueryCache.cacheQuery_of_ne _ _ (Ne.symm ht)]
          exact hbad

ROMUpToBad.lean:470-510 ↗

roImpl_run_keys_subset

axioms: propext, Classical.choice, Quot.sound · sorry-free ✓

theorem roImpl_run_keys_subset {α : Type}
    (oa : OracleComp (unifSpec + hashSpec) α) (n : ℕ)
    (hq : IsQueryBoundP oa (fun i => i.isRight = true) n) (cache₀ : hashSpec.QueryCache) :
    ∀ z ∈ support ((simulateQ (roImpl hashSpec) oa).run cache₀),
      ∃ S : Finset hashSpec.Domain, S.card ≤ n ∧
        ∀ t, QueryCache.isCached z.2 t = true → QueryCache.isCached cache₀ t = true ∨ t ∈ S := by
proof
  induction oa using OracleComp.inductionOn generalizing n cache₀ with
  | pure x =>
    intro z hz
    simp only [simulateQ_pure, StateT.run_pure, support_pure, Set.mem_singleton_iff] at hz
    subst hz
    exact ⟨∅, by simp, fun t h => Or.inl h⟩
  | query_bind t mx ih =>
    intro z hz
    rw [isQueryBoundP_query_bind_iff] at hq
    obtain ⟨hvalid, hrest⟩ := hq
    simp only [simulateQ_bind, simulateQ_query, OracleQuery.input_query, OracleQuery.cont_query,
      id_map, StateT.run_bind, support_bind, Set.mem_iUnion] at hz
    obtain ⟨⟨u, c'⟩, hstep, hz⟩ := hz
    rcases t with n' | t
    · have hrun : (roImpl hashSpec (Sum.inl n')).run cache₀ =
          ((fun x => (x, cache₀)) <$> ($[0..n']) :
            ProbComp ((unifSpec + hashSpec).Range (Sum.inl n') × hashSpec.QueryCache)) := rfl
      have hb := hrest u
      simp only [Sum.isRight_inl, Bool.false_eq_true, if_false] at hb
      rw [hrun] at hstep
      simp only [support_map, Set.mem_image, Prod.mk.injEq] at hstep
      obtain ⟨x, -, rfl, rfl⟩ := hstep
      exact ih _ n hb _ z hz
    · have hb := hrest u
      simp only [Sum.isRight_inr, if_true] at hb
      simp only [Sum.isRight_inr, not_true_eq_false, false_or] at hvalid
      cases hcache : cache₀ t with
      | some w =>
        have hrun : (roImpl hashSpec (Sum.inr t)).run cache₀ =
            (pure (w, cache₀) :
              ProbComp ((unifSpec + hashSpec).Range (Sum.inr t) × hashSpec.QueryCache)) := by
          show (hashSpec.randomOracle t).run cache₀ = _
          simp [StateT.run_bind, StateT.run_get, hcache]
        rw [hrun] at hstep
        simp only [support_pure, Set.mem_singleton_iff, Prod.mk.injEq] at hstep
        obtain ⟨rfl, rfl⟩ := hstep
        obtain ⟨S, hS, hkeys⟩ := ih _ (n - 1) hb _ z hz
        exact ⟨S, hS.trans (Nat.sub_le n 1), hkeys⟩
      | none =>
        have hrun : (roImpl hashSpec (Sum.inr t)).run cache₀ =
            (($ᵗ hashSpec.Range t) >>= fun x => pure (x, cache₀.cacheQuery t x) :
              ProbComp ((unifSpec + hashSpec).Range (Sum.inr t) × hashSpec.QueryCache)) := by
          show (hashSpec.randomOracle t).run cache₀ = _
          simp [StateT.run_bind, StateT.run_get, hcache, StateT.run_monadLift, monadLift_self,
            StateT.run_modifyGet, uniformSampleImpl]
        rw [hrun] at hstep
        simp only [support_bind, support_pure, Set.mem_iUnion, Set.mem_singleton_iff,
          Prod.mk.injEq] at hstep
        obtain ⟨x, -, rfl, rfl⟩ := hstep
        obtain ⟨S, hS, hkeys⟩ := ih _ (n - 1) hb _ z hz
        refine ⟨insert t S, ?_, ?_⟩
        · calc (insert t S).card ≤ S.card + 1 := Finset.card_insert_le _ _
            _ ≤ n := by omega
        · intro t' h
          rcases hkeys t' h with h' | h'
          · by_cases htt : t' = t
            · subst htt
              exact Or.inr (Finset.mem_insert_self _ _)
            · rw [QueryCache.isCached_cacheQuery_of_ne _ _ htt] at h'
              exact Or.inl h'
          · exact Or.inr (Finset.mem_insert_of_mem h')
 
omit [DecidableEq ι] [∀ t : hashSpec.Domain, SampleableType (hashSpec.Range t)] in

ROMUpToBad.lean:514-582 ↗

probEvent_isSome_apply_le

axioms: propext, Classical.choice, Quot.sound · sorry-free ✓

theorem probEvent_isSome_apply_le {γ : Type} (samp : ProbComp γ) (f : γ → hashSpec.Domain)
    (beta : ℝ≥0∞) (hB : ∀ x, Pr[fun y => f y = x | samp] ≤ beta)
    (c : hashSpec.QueryCache) (S : Finset hashSpec.Domain)
    (hS : ∀ t, QueryCache.isCached c t = true → t ∈ S) :
    Pr[fun y => QueryCache.isCached c (f y) = true | samp] ≤ S.card * beta := by
proof
  calc Pr[fun y => QueryCache.isCached c (f y) = true | samp]
      ≤ Pr[fun y => ∃ t ∈ S, f y = t | samp] :=
        probEvent_mono'' fun y hy => ⟨f y, hS _ hy, rfl⟩
    _ ≤ ∑ t ∈ S, Pr[fun y => f y = t | samp] :=
        probEvent_exists_finset_le_sum S samp (fun t y => f y = t)
    _ ≤ ∑ _t ∈ S, beta := Finset.sum_le_sum fun t _ => hB t
    _ = S.card * beta := by rw [Finset.sum_const, nsmul_eq_mul]

ROMUpToBad.lean:585-597 ↗