BlindingEntropy
BlindingROM reduced the blinding term to the two bad-query probabilities. Here each is bounded
by qH · β: qH bounds the adversary's address-oracle queries and β the point mass of the
address point pack rho (power2Round (u + t)) over the uniform mask u
(BlindPointMassBound). The generic ROM pieces are in ROMUpToBad (§ query budget).
blindPointSample
The address point of a uniform mask: pack rho (power2Round (u + t)) for u uniform.
def blindPointSample (P : Prims R Rho WireBytes T1 Tag Addr K k l)
[SampleableType R] (rho : Rho) (t : Fin k → R) : ProbComp WireBytes :=
do let u ← $ᵗ (Fin k → R)
pure (P.pack rho (P.power2Round (u + t)))BlindPointMassBound
def BlindPointMassBound (P : Prims R Rho WireBytes T1 Tag Addr K k l)
[SampleableType R] (beta : ℝ≥0∞) : Prop :=
∀ (rho : Rho) (t : Fin k → R) (x : WireBytes),
Pr[= x | blindPointSample P rho t] ≤ betaprobOutput_blindBadQuery_le
axioms: propext, Classical.choice, Quot.sound · sorry-free ✓
theorem probOutput_blindBadQuery_le
(P : Prims R Rho WireBytes T1 Tag Addr K k l)
[SampleableType R] [DecidableEq WireBytes]
(tg : Tag) (rho : Bool → Rho) (t : Bool → Fin k → R)
(adv : BlindAdvRO Tag Addr WireBytes) (qH : ℕ)
(hq : ∀ x, IsQueryBoundP (adv x) (fun i => i.isRight = true) qH)
(beta : ℝ≥0∞) (hB : BlindPointMassBound P beta) (b : Bool) :
Pr[= true | blindBadQuery P tg rho t adv b] ≤ (qH : ℝ≥0∞) * beta := byproof
-- the point-mass hypothesis in event form over the mask alone
have hpt : ∀ (a : Addr) (x : WireBytes),
Pr[fun u : Fin k → R => blindPoint P rho t b (u, a) = x | $ᵗ (Fin k → R)] ≤ beta := by
intro a x
have := hB (rho b) (t b) x
rw [show blindPointSample P (rho b) (t b) =
(fun u => blindPoint P rho t b (u, a)) <$> ($ᵗ (Fin k → R)) from by
simp only [blindPointSample, blindPoint, map_eq_bind_pure_comp, Function.comp_def],
probOutput_map] at this
exact this
-- 1. the flag of the programmed run, as an event over the prefix and the run
unfold blindBadQuery badQueryGame
rw [← map_bind, probOutput_map]
-- 2. programmed flag ≤ plain-run "point cached", pointwise in the prefix
have hstep : Pr[fun z => z.2.2 = true |
blindPrefix (R := R) (k := k) (Addr := Addr) >>= fun x =>
(simulateQ (programmedROImpl (pointPolicy (blindPoint P rho t b x) x.2))
(adv (tg, x.2))).run (∅, false)] ≤
Pr[fun w : ((Fin k → R) × Addr) × (Bool × (WireBytes →ₒ Addr).QueryCache) =>
QueryCache.isCached w.2.2 (blindPoint P rho t b w.1) = true |
blindPrefix (R := R) (k := k) (Addr := Addr) >>= fun x =>
(fun z => (x, z)) <$> (simulateQ (roImpl (WireBytes →ₒ Addr)) (adv (tg, x.2))).run ∅] := by
rw [probEvent_bind_eq_tsum, probEvent_bind_eq_tsum]
refine ENNReal.tsum_le_tsum fun x => mul_le_mul' le_rfl ?_
rw [probEvent_flag_programmed_eq_tracking, probEvent_map]
exact probEvent_flag_trackingROImpl_pointPolicy_le (adv (tg, x.2)) _ _ ∅
(QueryCache.empty_apply _)
refine hstep.trans ?_
-- 3. sample the mask last: it is independent of the adversary's run
simp only [blindPrefix, bind_assoc, pure_bind]
rw [probEvent_bind_bind_swap]
refine probEvent_bind_le_const _ _ _ _ fun a _ => ?_
simp only [map_eq_bind_pure_comp, Function.comp_def]
rw [probEvent_bind_bind_swap]
refine probEvent_bind_le_const _ _ _ _ fun z hz => ?_
obtain ⟨S, hS, hkeys⟩ := roImpl_run_keys_subset (adv (tg, a)) qH (hq _) ∅ z hz
rw [show (($ᵗ (Fin k → R)) >>= fun u => pure (((u, a), z))) =
(fun u => ((u, a), z)) <$> ($ᵗ (Fin k → R)) from by
simp only [map_eq_bind_pure_comp, Function.comp_def], probEvent_map]
calc Pr[fun u : Fin k → R => QueryCache.isCached z.2 (blindPoint P rho t b (u, a)) = true |
$ᵗ (Fin k → R)]
≤ S.card * beta :=
probEvent_isSome_apply_le ($ᵗ (Fin k → R)) (fun u => blindPoint P rho t b (u, a)) beta
(hpt a) z.2 S (fun t' h => by
rcases hkeys t' h with h' | h'
· simp at h'
· exact h')
_ ≤ qH * beta := mul_le_mul' (by exact_mod_cast hS) le_rflblindBadProb_le_queryBound
axioms: propext, Classical.choice, Quot.sound · sorry-free ✓
theorem blindBadProb_le_queryBound
(P : Prims R Rho WireBytes T1 Tag Addr K k l)
[SampleableType R] [DecidableEq WireBytes]
(tg : Tag) (rho : Bool → Rho) (t : Bool → Fin k → R)
(adv : BlindAdvRO Tag Addr WireBytes) (qH : ℕ)
(hq : ∀ x, IsQueryBoundP (adv x) (fun i => i.isRight = true) qH)
(beta : ℝ≥0∞) (hB : BlindPointMassBound P beta) (hβ : beta ≠ ⊤) (b : Bool) :
blindBadProb P tg rho t adv b ≤ ((qH : ℝ≥0∞) * beta).toReal :=
ENNReal.toReal_mono (ENNReal.mul_ne_top (ENNReal.natCast_ne_top qH) hβ)
(probOutput_blindBadQuery_le P tg rho t adv qH hq beta hB b)blindingAdvantageRO_le_queryBound
axioms: propext, Classical.choice, Quot.sound · sorry-free ✓
theorem blindingAdvantageRO_le_queryBound
(P : Prims R Rho WireBytes T1 Tag Addr K k l)
[SampleableType R] [DecidableEq WireBytes]
(tg : Tag) (rho : Bool → Rho) (t : Bool → Fin k → R)
(adv : BlindAdvRO Tag Addr WireBytes) (qH : ℕ)
(hq : ∀ x, IsQueryBoundP (adv x) (fun i => i.isRight = true) qH)
(beta : ℝ≥0∞) (hB : BlindPointMassBound P beta) (hβ : beta ≠ ⊤) :
blindingAdvantageRO P tg rho t adv ≤ 2 * ((qH : ℝ≥0∞) * beta).toReal := byproof
calc blindingAdvantageRO P tg rho t adv
≤ blindBadProb P tg rho t adv true + blindBadProb P tg rho t adv false :=
blindingAdvantageRO_le_blindBadProb P tg rho t adv
_ ≤ ((qH : ℝ≥0∞) * beta).toReal + ((qH : ℝ≥0∞) * beta).toReal :=
add_le_add (blindBadProb_le_queryBound P tg rho t adv qH hq beta hB hβ true)
(blindBadProb_le_queryBound P tg rho t adv qH hq beta hB hβ false)
_ = 2 * ((qH : ℝ≥0∞) * beta).toReal := by ringBlindingEntropy.lean:107-122 ↗
mlDsaAddressPointBeta
The ML-DSA point-mass value: each of the k·256 coefficients of the Power2Round high
part has at most 2^d preimages among the q residues.
noncomputable def mlDsaAddressPointBeta (k : ℕ) : ℝ≥0∞ :=
((2 ^ droppedBits : ℝ≥0∞) / modulus) ^ (k * ringDegree)BlindingEntropy.lean:132-134 ↗
power2RoundCoeff_decomp
power2RoundCoeff decomposes its input: 2^d · r₁ + r₀ = r in Coeff.
axioms: propext, Quot.sound · sorry-free ✓
theorem power2RoundCoeff_decomp (r : Coeff) :
(power2Scale : Coeff) * ((power2RoundCoeff r).1 : Coeff) + ((power2RoundCoeff r).2 : Coeff) = r := byproof
have hdam : (power2Scale : Coeff) * ((r.val / power2Scale : ℕ) : Coeff)
+ ((r.val % power2Scale : ℕ) : Coeff) = r := by
rw [← Nat.cast_mul, ← Nat.cast_add]
nth_rewrite 3 [← ZMod.natCast_zmod_val r]
congr 1
exact Nat.div_add_mod r.val power2Scale
simp only [power2RoundCoeff]
split_ifs with h
· push_cast
exact hdam
· simp only [Nat.cast_add, Nat.cast_one, Int.cast_sub, Int.cast_natCast]
linear_combination hdamBlindingEntropy.lean:138-152 ↗
power2RoundCoeff_low_range
axioms: propext, Classical.choice, Quot.sound · sorry-free ✓
theorem power2RoundCoeff_low_range (r : Coeff) :
-4095 ≤ (power2RoundCoeff r).2 ∧ (power2RoundCoeff r).2 ≤ 4096 := byproof
have hS : power2Scale = 8192 := by decide
simp only [power2RoundCoeff, hS]
have := Nat.mod_lt r.val (show 0 < 8192 by norm_num)
split_ifs with h <;> simp only <;> omegaBlindingEntropy.lean:154-160 ↗
card_power2Round_fiber_le
axioms: propext, Classical.choice, Quot.sound · sorry-free ✓
theorem card_power2Round_fiber_le (c : Coeff) :
(Finset.univ.filter fun r : Coeff => ((power2RoundCoeff r).1 : Coeff) = c).card
≤ 2 ^ droppedBits := byproof
have hd : 2 ^ droppedBits = 8192 := by decide
rw [hd]
refine (Finset.card_le_card_of_injOn (fun r => ((power2RoundCoeff r).2 + 4095).toNat)
(t := Finset.range 8192) ?_ ?_).trans (by rw [Finset.card_range])
· intro r _
simp only [Finset.coe_range, Set.mem_Iio]
have := power2RoundCoeff_low_range r
omega
· intro r hr r' hr' heq
simp only [Finset.mem_coe, Finset.mem_filter, Finset.mem_univ, true_and] at hr hr'
have hlow : (power2RoundCoeff r).2 = (power2RoundCoeff r').2 := by
have := power2RoundCoeff_low_range r
have := power2RoundCoeff_low_range r'
simp only at heq
omega
rw [← power2RoundCoeff_decomp r, ← power2RoundCoeff_decomp r', hr, hr', hlow]BlindingEntropy.lean:162-181 ↗
power2RoundHigh_eq_iff
theorem power2RoundHigh_eq_iff (r h : Rq) :
power2RoundHigh r = h ↔ ∀ i, ((power2RoundCoeff (r.get i)).1 : Coeff) = h.get i := byproof
constructor
· rintro rfl i
simp only [power2RoundHigh, Vector.get_eq_getElem, Vector.getElem_ofFn]
· intro hi
refine Poly.ext_get_eq fun i => ?_
rw [← hi i]
simp only [power2RoundHigh, Vector.get_eq_getElem, Vector.getElem_ofFn]BlindingEntropy.lean:183-192 ↗
vector_ofFn_eq_iff
theorem vector_ofFn_eq_iff {α : Type} {n : ℕ} (f : Fin n → α) (h : Vector α n) :
Vector.ofFn f = h ↔ ∀ j, f j = h.get j := byproof
constructor
· rintro rfl j
simp only [Vector.get_eq_getElem, Vector.getElem_ofFn]
· intro hj
refine Vector.ext fun j hj' => ?_
rw [Vector.getElem_ofFn]
exact (hj ⟨j, hj'⟩).trans (Vector.get_eq_getElem _ _)BlindingEntropy.lean:194-203 ↗
instFintypeRq
instance instFintypeRq : Fintype Rq := byproof
change Fintype (Vector Coeff ringDegree)
infer_instanceBlindingEntropy.lean:205-208 ↗
coords
def coords (k : ℕ) : (Fin k → Rq) ≃ (Fin k → Fin ringDegree → Coeff) where
toFun v j i := (v j).get i
invFun w j := Vector.ofFn (w j)
left_inv v := funext fun j => Poly.ext_get_eq fun i => by
simp only [Vector.get_eq_getElem, Vector.getElem_ofFn]
right_inv w := funext fun j => funext fun i => by
simp only [Vector.get_eq_getElem, Vector.getElem_ofFn]
omit [SampleableType Addr] inBlindingEntropy.lean:210-218 ↗
blindPointMassBound_mldsa
axioms: propext, Classical.choice, Quot.sound · sorry-free ✓
theorem blindPointMassBound_mldsa [SampleableType Rq]
(P : Prims Rq Rho WireBytes (Vector Rq k) Tag Addr K k l)
(hP2 : ∀ v : Fin k → Rq,
P.power2Round v = Vector.ofFn fun j => MLDSA.Concrete.power2RoundHigh (v j))
(hpack : ∀ rho, Function.Injective (P.pack rho)) :
BlindPointMassBound P (mlDsaAddressPointBeta k) := byproof
intro rho t x
rw [show blindPointSample P rho t =
(fun u => P.pack rho (P.power2Round (u + t))) <$> ($ᵗ (Fin k → Rq)) from by
simp only [blindPointSample, map_eq_bind_pure_comp, Function.comp_def]
rfl, probOutput_map]
rcases Classical.em (∃ h, P.pack rho h = x) with hx | hx
· obtain ⟨h, rfl⟩ := hx
-- the fiber, in coordinates
have hcard : (Finset.univ.filter fun u : Fin k → Rq => P.power2Round (u + t) = h).card
≤ (2 ^ droppedBits) ^ (k * ringDegree) := by
rw [Finset.card_equiv (Equiv.addRight t)
(t := Finset.univ.filter fun v : Fin k → Rq => P.power2Round v = h) (fun u => by
simp only [Finset.mem_filter, Finset.mem_univ, true_and, Equiv.coe_addRight]
rfl)]
rw [Finset.card_equiv (coords k)
(t := Fintype.piFinset fun j => Fintype.piFinset fun i =>
Finset.univ.filter fun r : Coeff =>
((power2RoundCoeff r).1 : Coeff) = (h.get j).get i) (fun v => ?_)]
· rw [Fintype.card_piFinset]
simp only [Fintype.card_piFinset]
calc ∏ j : Fin k, ∏ i : Fin ringDegree,
(Finset.univ.filter fun r : Coeff =>
((power2RoundCoeff r).1 : Coeff) = (h.get j).get i).card
≤ ∏ _j : Fin k, ∏ _i : Fin ringDegree, 2 ^ droppedBits :=
Finset.prod_le_prod' fun j _ => Finset.prod_le_prod' fun i _ =>
card_power2Round_fiber_le _
_ = (2 ^ droppedBits) ^ (k * ringDegree) := by
simp only [Finset.prod_const, Finset.card_univ, Fintype.card_fin]
rw [← pow_mul, mul_comm]
· simp only [Finset.mem_filter, Finset.mem_univ, true_and, Fintype.mem_piFinset, hP2,
vector_ofFn_eq_iff, power2RoundHigh_eq_iff]
rfl
have htot : Fintype.card (Fin k → Rq) = modulus ^ (k * ringDegree) := by
rw [Fintype.card_congr (coords k), Fintype.card_pi]
simp only [Fintype.card_pi, Finset.prod_const, Finset.card_univ, Fintype.card_fin, ZMod.card]
rw [← pow_mul, mul_comm]
calc Pr[fun u => P.pack rho (P.power2Round (u + t)) = P.pack rho h | $ᵗ (Fin k → Rq)]
≤ Pr[fun u => P.power2Round (u + t) = h | $ᵗ (Fin k → Rq)] :=
probEvent_mono'' fun u hu => hpack rho hu
_ = ((Finset.univ.filter fun u : Fin k → Rq => P.power2Round (u + t) = h).card : ℝ≥0∞)
/ Fintype.card (Fin k → Rq) := probEvent_uniformSample _ _
_ ≤ (((2 ^ droppedBits) ^ (k * ringDegree) : ℕ) : ℝ≥0∞)
/ ((modulus ^ (k * ringDegree) : ℕ) : ℝ≥0∞) := by
rw [htot]
exact ENNReal.div_le_div_right (by exact_mod_cast hcard) _
_ = mlDsaAddressPointBeta k := by
rw [mlDsaAddressPointBeta, Nat.cast_pow, Nat.cast_pow, Nat.cast_pow, Nat.cast_ofNat,
div_eq_mul_inv, ENNReal.inv_pow, ← mul_pow, ← div_eq_mul_inv]
· rw [probEvent_eq_zero fun u _ hu => hx ⟨_, hu⟩]
exact zero_le
omit [SampleableType Addr] inBlindingEntropy.lean:222-279 ↗
blindPointMassBound_mldsa65
axioms: propext, Classical.choice, Quot.sound · sorry-free ✓
theorem blindPointMassBound_mldsa65 [SampleableType Rq]
(P : Prims Rq (MLDSA.Bytes 32) WireBytes (Vector Rq mldsa65.k) Tag Addr K mldsa65.k mldsa65.l)
(hP2 : ∀ v : Fin mldsa65.k → Rq,
P.power2Round v = Vector.ofFn fun j => MLDSA.Concrete.power2RoundHigh (v j))
(hpack : ∀ rho, Function.Injective (P.pack rho)) :
BlindPointMassBound P (mlDsaAddressPointBeta mldsa65.k) :=
blindPointMassBound_mldsa P hP2 hpackBlindingEntropy.lean:281-288 ↗