SPRTwoHop
Proved: the two decision-MLWE problems the SPR argument needs, both as VCVio
LearningWithErrors.Problems over the seed rho; the two reduction adversaries
built from an unlinkability distinguisher; that each hop's game gap IS the
LearningWithErrors.advantage of its reduction adversary; and the resulting
four-term bound on KEM.sprAdv for ML-KEM-768.
Also proved: the real anonymity branch may be reordered to encapsulate first
and draw the other key afterwards (evalDist_anonSetup_bind_anonBranch, by
VCVio's relational tactics), so that reorder costs nothing and is NOT folded
into the idealization term.
Assumed: the two non-lattice terms of that bound stay unbounded here -- the
primitive-idealization term (G and PRF_eta as independent samplers; the
seeded shape keeps SampleNTT concrete) and the simulator term
(compress-and-encode regularity, plus the honest key distribution). Neither is
an MLWE statement. See docs/spr-two-hop.md.
instNeZeroMlkemModulus
The FIPS 203 modulus is nonzero -- needed to see ZMod modulus as
FinEnum, which modulus being a def hides from instance search.
instance instNeZeroMlkemModulus : NeZero MLKEM.modulus := ⟨by decide⟩instSampleableTypeRq
instance instSampleableTypeRq : SampleableType MLKEM.Rq :=
inferInstanceAs (SampleableType (Vector MLKEM.Coeff MLKEM.ringDegree))instSampleableTypeTq
instance instSampleableTypeTq : SampleableType MLKEM.Tq :=
SampleableType.ofEquiv (α := MLKEM.Rq)
⟨fun f => ⟨f⟩, fun t => t.coeffs, fun _ => rfl, fun _ => rfl⟩Uniform sampling on the ML-KEM ring
LearningWithErrors.advantage_eq_boolDistAdvantage
The decision-LWE advantage is the distance between the two named games, so
it composes with the game-hopping triangle inequalities. (Scoped under
PqStealth; upstream ask, docs/vcvio-upstream.md item 4.)
axioms: propext, Classical.choice, Quot.sound · sorry-free ✓
theorem LearningWithErrors.advantage_eq_boolDistAdvantage {Sample Secret Output : Type}
[Add Output] (problem : LearningWithErrors.Problem Sample Secret Output)
(adv : LearningWithErrors.Adversary problem) :
LearningWithErrors.advantage problem adv =
(LearningWithErrors.game0 problem adv).boolDistAdvantage
(LearningWithErrors.game1 problem adv) := byproof
have key := ProbComp.boolBiasAdvantage_bind_uniformBool_eq_boolDistAdvantage
(pure () : ProbComp Unit) (fun _ => LearningWithErrors.game0 problem adv)
(fun _ => LearningWithErrors.game1 problem adv)
simp only [pure_bind] at key
rw [LearningWithErrors.advantage, ← key]
refine congrArg ProbComp.boolBiasAdvantage ?_
simp only [LearningWithErrors.experiment]
refine bind_congr fun b => ?_
cases b <;>
simp only [if_true, LearningWithErrors.game0, LearningWithErrors.game1, bind_assoc]VCVio's LWE advantage as a game distance
pkOf
The K-PKE public key carrying rho and the encoding of t̂.
def pkOf (rho : Seed32) (tHat : TqVec params.k) : EncapsulationKey params encoding :=
{ tHatEncoded := encoding.byteEncode12Vec tHat, rho := rho }ctOf
def ctOf (u : RqVec params.k) (v : Rq) : Ciphertext params encoding :=
{ uEncoded := encoding.byteEncodeDUVec (encoding.compressDU u)
vEncoded := encoding.byteEncodeDV (encoding.compressDV v) }sampleEta1Hat
def sampleEta1Hat (offset : ℕ) : ProbComp (TqVec params.k) := do
let sigma ← $ᵗ Seed32
pure (ring.nttVec (prims.sampleVecEta1 sigma offset))sampleNoise
def sampleNoise : ProbComp (RqVec params.k × Rq) := do
let c₁ ← $ᵗ Coins
let c₂ ← $ᵗ Coins
pure (prims.sampleVecEta2 c₁ params.k, prims.prfEta2 c₂ (2 * params.k))idealKeygen
def idealKeygen : ProbComp (EncapsulationKey params encoding) := do
let rho ← $ᵗ Seed32
let s ← sampleEta1Hat ring encoding prims 0
let e ← sampleEta1Hat ring encoding prims params.k
pure (pkOf encoding rho (ring.matVecMul (prims.publicMatrix rho) s + e))idealEncrypt
def idealEncrypt (ek : EncapsulationKey params encoding) :
ProbComp (Ciphertext params encoding) := do
let y ← sampleEta1Hat ring encoding prims 0
let ne ← sampleNoise encoding prims
let m ← $ᵗ Message
pure (ctOf encoding
(ring.invNTTVec (ring.matTransposeVecMul (prims.publicMatrix ek.rho) y) + ne.1)
(ring.invNTT (ring.dot (encoding.byteDecode12Vec ek.tHatEncoded) y) + ne.2
+ encoding.decompress1 (encoding.byteDecode1 m)))uniformKeygen
def uniformKeygen : ProbComp (EncapsulationKey params encoding) := do
let rho ← $ᵗ Seed32
let t ← $ᵗ (TqVec params.k)
pure (pkOf encoding rho t)randCt
def randCt : ProbComp (Ciphertext params encoding) := do
let u ← $ᵗ (RqVec params.k)
let v ← $ᵗ Rq
let m ← $ᵗ Message
pure (ctOf encoding u (v + encoding.decompress1 (encoding.byteDecode1 m)))Semantic constructors and the idealized samplers
idealBranch
The anonymity branch in idealized form, parametric in how the challenge key and the challenge ciphertext are produced.
def idealBranch (adv : StealthScheme.UnlinkAdv (EncapsulationKey params encoding)
(Ciphertext params encoding))
(kb : ProbComp (EncapsulationKey params encoding))
(ct : EncapsulationKey params encoding → ProbComp (Ciphertext params encoding))
(b : Bool) : ProbComp Bool := do
let pkb ← kb
let c ← ct pkb
let pkOther ← idealKeygen ring encoding prims
adv (if b then pkOther else pkb) (if b then pkb else pkOther) cThe game chain
keyHopProblem
Key hop. Given (rho, t̂), decide whether t̂ = ·ŝ + ê for
PRF_η₁-distributed (ŝ, ê) or t̂ is uniform.
def keyHopProblem :
LearningWithErrors.Problem Seed32 (TqVec params.k) (TqVec params.k) where
sampleChallenge := $ᵗ Seed32
sampleSecret := sampleEta1Hat ring encoding prims 0
sampleError := sampleEta1Hat ring encoding prims params.k
noiseless := fun s rho => ring.matVecMul (prims.publicMatrix rho) s
sampleUniform := $ᵗ (TqVec params.k)ctHopProblem
def ctHopProblem :
LearningWithErrors.Problem (Seed32 × TqVec params.k) (TqVec params.k)
(RqVec params.k × Rq) where
sampleChallenge := do
let rho ← $ᵗ Seed32
let t ← $ᵗ (TqVec params.k)
pure (rho, t)
sampleSecret := sampleEta1Hat ring encoding prims 0
sampleError := sampleNoise encoding prims
noiseless := fun y ch =>
(ring.invNTTVec (ring.matTransposeVecMul (prims.publicMatrix ch.1) y),
ring.invNTT (ring.dot ch.2 y))
sampleUniform := do
let u ← $ᵗ (RqVec params.k)
let v ← $ᵗ Rq
pure (u, v)The two decision-MLWE problems
keyHopAdv
Key-hop reduction. Plant the MLWE challenge as recipient b's
encapsulation key, generate the other recipient honestly, encrypt honestly to
the planted key.
def keyHopAdv (b : Bool) : LearningWithErrors.Adversary (keyHopProblem ring encoding prims) :=
fun chal => do
let c ← idealEncrypt ring encoding prims (pkOf encoding chal.1 chal.2)
let pkOther ← idealKeygen ring encoding prims
adv (if b then pkOther else pkOf encoding chal.1 chal.2)
(if b then pkOf encoding chal.1 chal.2 else pkOther) cctHopAdv
def ctHopAdv (b : Bool) : LearningWithErrors.Adversary (ctHopProblem ring encoding prims) :=
fun chal => do
let m ← $ᵗ Message
let pkOther ← idealKeygen ring encoding prims
adv (if b then pkOther else pkOf encoding chal.1.1 chal.1.2)
(if b then pkOf encoding chal.1.1 chal.1.2 else pkOther)
(ctOf encoding chal.2.1 (chal.2.2 + encoding.decompress1 (encoding.byteDecode1 m)))The two reduction adversaries
idealEncrypt_pkOf
Encrypting to a planted key sees t̂ itself: the byteEncode12Vec /
byteDecode12Vec roundtrip is an Encoding field, not a Laws hypothesis.
axioms: propext, Classical.choice, Quot.sound · sorry-free ✓
theorem idealEncrypt_pkOf (rho : Seed32) (t : TqVec params.k) :
idealEncrypt ring encoding prims (pkOf encoding rho t) = (do
let y ← sampleEta1Hat ring encoding prims 0
let ne ← sampleNoise encoding prims
let m ← $ᵗ Message
pure (ctOf encoding
(ring.invNTTVec (ring.matTransposeVecMul (prims.publicMatrix rho) y) + ne.1)
(ring.invNTT (ring.dot t y) + ne.2
+ encoding.decompress1 (encoding.byteDecode1 m)))) := byproof
simp only [idealEncrypt, pkOf, encoding.byteDecode12Vec_byteEncode12Vec]game0_keyHopProblem
axioms: propext, Classical.choice, Quot.sound · sorry-free ✓
theorem game0_keyHopProblem (b : Bool) :
LearningWithErrors.game0 (keyHopProblem ring encoding prims)
(keyHopAdv ring encoding prims adv b) =
idealBranch ring encoding prims adv (idealKeygen ring encoding prims)
(idealEncrypt ring encoding prims) b := byproof
simp only [LearningWithErrors.game0, LearningWithErrors.distr, keyHopProblem, keyHopAdv,
idealBranch, idealKeygen, bind_assoc, pure_bind]game1_keyHopProblem
axioms: propext, Classical.choice, Quot.sound · sorry-free ✓
theorem game1_keyHopProblem (b : Bool) :
LearningWithErrors.game1 (keyHopProblem ring encoding prims)
(keyHopAdv ring encoding prims adv b) =
idealBranch ring encoding prims adv (uniformKeygen encoding)
(idealEncrypt ring encoding prims) b := byproof
simp only [LearningWithErrors.game1, LearningWithErrors.uniformDistr, keyHopProblem, keyHopAdv,
idealBranch, uniformKeygen, bind_assoc, pure_bind]game0_ctHopProblem
axioms: propext, Classical.choice, Quot.sound · sorry-free ✓
theorem game0_ctHopProblem (b : Bool) :
LearningWithErrors.game0 (ctHopProblem ring encoding prims)
(ctHopAdv ring encoding prims adv b) =
idealBranch ring encoding prims adv (uniformKeygen encoding)
(idealEncrypt ring encoding prims) b := byproof
simp only [LearningWithErrors.game0, LearningWithErrors.distr, ctHopProblem, ctHopAdv,
idealBranch, uniformKeygen, idealEncrypt_pkOf, bind_assoc, pure_bind, Prod.fst_add,
Prod.snd_add]game1_ctHopProblem
axioms: propext, Classical.choice, Quot.sound · sorry-free ✓
theorem game1_ctHopProblem (b : Bool) :
LearningWithErrors.game1 (ctHopProblem ring encoding prims)
(ctHopAdv ring encoding prims adv b) =
idealBranch ring encoding prims adv (uniformKeygen encoding)
(fun _ => randCt encoding) b := byproof
simp only [LearningWithErrors.game1, LearningWithErrors.uniformDistr, ctHopProblem, ctHopAdv,
idealBranch, uniformKeygen, randCt, bind_assoc, pure_bind]The hop identities
KEM.anonBranchReordered
The real anonymity branch in the idealized games' order: challenge key, its encapsulation, then the other key.
def KEM.anonBranchReordered (b : Bool) : ProbComp Bool := do
let pkb ← Prod.fst <$> kem.keygen
let c ← Prod.fst <$> kem.encaps pkb
let pkOther ← Prod.fst <$> kem.keygen
adv (if b then pkOther else pkb) (if b then pkb else pkOther) cKEM.evalDist_anonSetup_bind_anonBranch
axioms: propext, Classical.choice, Quot.sound · sorry-free ✓
theorem KEM.evalDist_anonSetup_bind_anonBranch (b : Bool) :
𝒟[kem.anonSetup >>= kem.anonBranch adv b] = 𝒟[kem.anonBranchReordered adv b] := byproof
cases b
· simp only [KEM.anonSetup, KEM.anonBranch, KEM.anonBranchReordered, bind_assoc, pure_bind,
map_eq_bind_pure_comp, Function.comp_def, Bool.false_eq_true, if_false]
by_equiv
rvcstep
intro a b hab
cases hab
rvcstep swap left
rvcgen
· simp only [KEM.anonSetup, KEM.anonBranch, KEM.anonBranchReordered, bind_assoc, pure_bind,
map_eq_bind_pure_comp, Function.comp_def, if_true]
by_equiv
rvcstep swap left
rvcstep
intro a b hab
cases hab
rvcstep swap left
rvcgenKEM.boolDistAdvantage_anonBranch_anonBranchReordered
theorem KEM.boolDistAdvantage_anonBranch_anonBranchReordered (b : Bool) :
(kem.anonSetup >>= kem.anonBranch adv b).boolDistAdvantage
(kem.anonBranchReordered adv b) = 0 := byproof
rw [ProbComp.boolDistAdvantage, probOutput_congr rfl (kem.evalDist_anonSetup_bind_anonBranch adv b),
sub_self, abs_zero]The real branch, reordered
primitiveIdealization
The gap between the (reordered) real ML-KEM anonymity branch and its
idealized form: G as a fresh-seed sampler (key seeds, and the encryption coins
G(m ‖ H(ek))) and PRF_η as an independent sampler. SampleNTT stays
concrete -- the MLWE problems below are seeded on rho. Not a lattice term --
it is the ROM/PRF step.
noncomputable def primitiveIdealization (b : Bool) : ℝ :=
(KEM.anonBranchReordered (MLKEM.asKEMScheme ring encoding prims) adv b).boolDistAdvantage
(idealBranch ring encoding prims adv (idealKeygen ring encoding prims)
(idealEncrypt ring encoding prims) b)simulatorGap
noncomputable def simulatorGap (b : Bool) : ℝ :=
(idealBranch ring encoding prims adv (uniformKeygen encoding)
(fun _ => randCt encoding) b).boolDistAdvantage
(KEM.anonSetup (MLKEM.asKEMScheme ring encoding prims) >>= KEM.simBranch sim adv)encodingRegularity
noncomputable def encodingRegularity (b : Bool) : ℝ :=
(idealBranch ring encoding prims adv (uniformKeygen encoding)
(fun _ => randCt encoding) b).boolDistAdvantage
(idealBranch ring encoding prims adv (uniformKeygen encoding) (fun _ => sim) b)keyRestoration
noncomputable def keyRestoration (b : Bool) : ℝ :=
(idealBranch ring encoding prims adv (uniformKeygen encoding) (fun _ => sim) b).boolDistAdvantage
(KEM.anonSetup (MLKEM.asKEMScheme ring encoding prims) >>= KEM.simBranch sim adv)simulatorGap_le
axioms: propext, Classical.choice, Quot.sound · sorry-free ✓
theorem simulatorGap_le (b : Bool) :
simulatorGap ring encoding prims adv sim b ≤
encodingRegularity ring encoding prims adv sim b
+ keyRestoration ring encoding prims adv sim b :=
ProbComp.boolDistAdvantage_triangle _ _ _The four named terms
sprAdv_le_two_hop_decomposition
SPR of ML-KEM through the two MLWE hops. The per-branch SPR advantage is bounded by the primitive-idealization term, the two decision-MLWE advantages of the named reduction adversaries, and the simulator gap.
axioms: propext, Classical.choice, Quot.sound · sorry-free ✓
theorem sprAdv_le_two_hop_decomposition (b : Bool) :
KEM.sprAdv (MLKEM.asKEMScheme ring encoding prims) sim adv b ≤
primitiveIdealization ring encoding prims adv b
+ LearningWithErrors.advantage (keyHopProblem ring encoding prims)
(keyHopAdv ring encoding prims adv b)
+ LearningWithErrors.advantage (ctHopProblem ring encoding prims)
(ctHopAdv ring encoding prims adv b)
+ simulatorGap ring encoding prims adv sim b := byproof
have hkey : LearningWithErrors.advantage (keyHopProblem ring encoding prims)
(keyHopAdv ring encoding prims adv b) =
(idealBranch ring encoding prims adv (idealKeygen ring encoding prims)
(idealEncrypt ring encoding prims) b).boolDistAdvantage
(idealBranch ring encoding prims adv (uniformKeygen encoding)
(idealEncrypt ring encoding prims) b) := by
rw [LearningWithErrors.advantage_eq_boolDistAdvantage, game0_keyHopProblem,
game1_keyHopProblem]
have hct : LearningWithErrors.advantage (ctHopProblem ring encoding prims)
(ctHopAdv ring encoding prims adv b) =
(idealBranch ring encoding prims adv (uniformKeygen encoding)
(idealEncrypt ring encoding prims) b).boolDistAdvantage
(idealBranch ring encoding prims adv (uniformKeygen encoding)
(fun _ => randCt encoding) b) := by
rw [LearningWithErrors.advantage_eq_boolDistAdvantage, game0_ctHopProblem,
game1_ctHopProblem]
rw [KEM.sprAdv, hkey, hct, primitiveIdealization, simulatorGap]
set kem : KEM (EncapsulationKey params encoding) (DecapsulationKey params encoding)
(Ciphertext params encoding) SharedSecret := MLKEM.asKEMScheme ring encoding prims
set G₀ : ProbComp Bool := KEM.anonSetup kem >>= KEM.anonBranch kem adv b
set G₀' : ProbComp Bool := KEM.anonBranchReordered kem adv b
set G₁ : ProbComp Bool := idealBranch ring encoding prims adv (idealKeygen ring encoding prims)
(idealEncrypt ring encoding prims) b
set G₂ : ProbComp Bool := idealBranch ring encoding prims adv (uniformKeygen encoding)
(idealEncrypt ring encoding prims) b
set G₃ : ProbComp Bool := idealBranch ring encoding prims adv (uniformKeygen encoding)
(fun _ => randCt encoding) b
set G₄ : ProbComp Bool := KEM.anonSetup kem >>= KEM.simBranch sim adv
calc ProbComp.boolDistAdvantage G₀ G₄
≤ ProbComp.boolDistAdvantage G₀ G₀' + ProbComp.boolDistAdvantage G₀' G₄ :=
ProbComp.boolDistAdvantage_triangle _ _ _
_ = ProbComp.boolDistAdvantage G₀' G₄ := by
rw [KEM.boolDistAdvantage_anonBranch_anonBranchReordered kem adv b, zero_add]
_ ≤ ProbComp.boolDistAdvantage G₀' G₁ + ProbComp.boolDistAdvantage G₁ G₄ :=
ProbComp.boolDistAdvantage_triangle _ _ _
_ ≤ ProbComp.boolDistAdvantage G₀' G₁ +
(ProbComp.boolDistAdvantage G₁ G₂ + ProbComp.boolDistAdvantage G₂ G₄) := by
gcongr
exact ProbComp.boolDistAdvantage_triangle _ _ _
_ ≤ ProbComp.boolDistAdvantage G₀' G₁ +
(ProbComp.boolDistAdvantage G₁ G₂ +
(ProbComp.boolDistAdvantage G₂ G₃ + ProbComp.boolDistAdvantage G₃ G₄)) := by
gcongr
exact ProbComp.boolDistAdvantage_triangle _ _ _
_ = _ := by ringThe bound
mlkem768KeyHopProblem
The key hop at ML-KEM-768: t̂ = ·ŝ + ê against uniform, over the matrix
SampleNTT expands from rho.
def mlkem768KeyHopProblem :
LearningWithErrors.Problem Seed32 (TqVec mlkem768.k) (TqVec mlkem768.k) :=
keyHopProblem concreteNTTRingOps mlkem768Encoding mlkem768Primitivesmlkem768CtHopProblem
def mlkem768CtHopProblem :
LearningWithErrors.Problem (Seed32 × TqVec mlkem768.k) (TqVec mlkem768.k)
(RqVec mlkem768.k × Rq) :=
ctHopProblem concreteNTTRingOps mlkem768Encoding mlkem768Primitivesmlkem768_sprAdv_le_two_hop_decomposition
axioms: propext, Classical.choice, Quot.sound · sorry-free ✓
theorem mlkem768_sprAdv_le_two_hop_decomposition (b : Bool) :
mlkem768KEM.sprAdv mlkem768UniformCiphertext adv768 b ≤
primitiveIdealization concreteNTTRingOps mlkem768Encoding mlkem768Primitives adv768 b
+ LearningWithErrors.advantage mlkem768KeyHopProblem
(keyHopAdv concreteNTTRingOps mlkem768Encoding mlkem768Primitives adv768 b)
+ LearningWithErrors.advantage mlkem768CtHopProblem
(ctHopAdv concreteNTTRingOps mlkem768Encoding mlkem768Primitives adv768 b)
+ simulatorGap concreteNTTRingOps mlkem768Encoding mlkem768Primitives adv768
mlkem768UniformCiphertext b :=
sprAdv_le_two_hop_decomposition concreteNTTRingOps mlkem768Encoding mlkem768Primitives adv768
mlkem768UniformCiphertext bAt ML-KEM-768
primitiveIdealizationBound
ROM/PRF assumption: the primitive-idealization term is bounded by
epsilonPrim.
def primitiveIdealizationBound (epsilonPrim : ℝ) : Prop :=
∀ b, primitiveIdealization ring encoding prims adv b ≤ epsilonPrimencodingRegularityBound
def encodingRegularityBound (epsilonEnc : ℝ) : Prop :=
∀ b, encodingRegularity ring encoding prims adv sim b ≤ epsilonEnckeyRestorationAdv
def keyRestorationAdv (b : Bool) :
LearningWithErrors.Adversary (keyHopProblem ring encoding prims) :=
fun chal => do
let simCt ← sim
let pkOther ← idealKeygen ring encoding prims
adv (if b then pkOther else pkOf encoding chal.1 chal.2)
(if b then pkOf encoding chal.1 chal.2 else pkOther) simCt
omit [DecidableEq encoding.EncodedTHat] [DecidableEq encoding.EncodedU]
[DecidableEq encoding.EncodedV] ingame0_keyRestorationAdv
axioms: propext, Classical.choice, Quot.sound · sorry-free ✓
theorem game0_keyRestorationAdv (b : Bool) :
LearningWithErrors.game0 (keyHopProblem ring encoding prims)
(keyRestorationAdv ring encoding prims adv sim b) =
idealBranch ring encoding prims adv (idealKeygen ring encoding prims)
(fun _ => sim) b := byproof
simp only [LearningWithErrors.game0, LearningWithErrors.distr, keyHopProblem,
keyRestorationAdv, idealBranch, idealKeygen, bind_assoc, pure_bind]
omit [DecidableEq encoding.EncodedTHat] [DecidableEq encoding.EncodedU]
[DecidableEq encoding.EncodedV] ingame1_keyRestorationAdv
axioms: propext, Classical.choice, Quot.sound · sorry-free ✓
theorem game1_keyRestorationAdv (b : Bool) :
LearningWithErrors.game1 (keyHopProblem ring encoding prims)
(keyRestorationAdv ring encoding prims adv sim b) =
idealBranch ring encoding prims adv (uniformKeygen encoding)
(fun _ => sim) b := byproof
simp only [LearningWithErrors.game1, LearningWithErrors.uniformDistr, keyHopProblem,
keyRestorationAdv, idealBranch, uniformKeygen, bind_assoc, pure_bind]keyIdealization
noncomputable def keyIdealization (b : Bool) : ℝ :=
(idealBranch ring encoding prims adv (idealKeygen ring encoding prims)
(fun _ => sim) b).boolDistAdvantage
(KEM.anonSetup (MLKEM.asKEMScheme ring encoding prims) >>= KEM.simBranch sim adv)keyRestoration_le_mlwe_add_keyIdealization
axioms: propext, Classical.choice, Quot.sound · sorry-free ✓
theorem keyRestoration_le_mlwe_add_keyIdealization (b : Bool) :
keyRestoration ring encoding prims adv sim b ≤
LearningWithErrors.advantage (keyHopProblem ring encoding prims)
(keyRestorationAdv ring encoding prims adv sim b)
+ keyIdealization ring encoding prims adv sim b := byproof
rw [keyRestoration, keyIdealization, LearningWithErrors.advantage_eq_boolDistAdvantage,
game0_keyRestorationAdv, game1_keyRestorationAdv]
set Gu : ProbComp Bool :=
idealBranch ring encoding prims adv (uniformKeygen encoding) (fun _ => sim) b
set Gi : ProbComp Bool :=
idealBranch ring encoding prims adv (idealKeygen ring encoding prims) (fun _ => sim) b
set Gr : ProbComp Bool :=
KEM.anonSetup (MLKEM.asKEMScheme ring encoding prims) >>= KEM.simBranch sim adv
calc ProbComp.boolDistAdvantage Gu Gr
≤ ProbComp.boolDistAdvantage Gu Gi + ProbComp.boolDistAdvantage Gi Gr :=
ProbComp.boolDistAdvantage_triangle _ _ _
_ = ProbComp.boolDistAdvantage Gi Gu + ProbComp.boolDistAdvantage Gi Gr := by
rw [ProbComp.boolDistAdvantage_comm Gu Gi]keyRestorationMLWE
def keyRestorationMLWE (epsilonRestore : ℝ) : Prop :=
∀ b, keyRestoration ring encoding prims adv sim b ≤ epsilonRestoresprAdv_le_mlwe
theorem sprAdv_le_mlwe (b : Bool)
(epsilonPrim epsilonEnc epsilonRestore : ℝ)
(hPrim : ∀ b, primitiveIdealization ring encoding prims adv b ≤ epsilonPrim)
(hEnc : ∀ b, encodingRegularity ring encoding prims adv sim b ≤ epsilonEnc)
(hRestore : ∀ b, keyRestoration ring encoding prims adv sim b ≤ epsilonRestore) :
KEM.sprAdv (MLKEM.asKEMScheme ring encoding prims) sim adv b ≤
epsilonPrim
+ LearningWithErrors.advantage (keyHopProblem ring encoding prims)
(keyHopAdv ring encoding prims adv b)
+ LearningWithErrors.advantage (ctHopProblem ring encoding prims)
(ctHopAdv ring encoding prims adv b)
+ epsilonEnc
+ epsilonRestore := byproof
calc
KEM.sprAdv (MLKEM.asKEMScheme ring encoding prims) sim adv b
≤ primitiveIdealization ring encoding prims adv b
+ LearningWithErrors.advantage (keyHopProblem ring encoding prims)
(keyHopAdv ring encoding prims adv b)
+ LearningWithErrors.advantage (ctHopProblem ring encoding prims)
(ctHopAdv ring encoding prims adv b)
+ simulatorGap ring encoding prims adv sim b :=
sprAdv_le_two_hop_decomposition ring encoding prims adv sim b
_ ≤ epsilonPrim + _ + _ + (encodingRegularity ring encoding prims adv sim b
+ keyRestoration ring encoding prims adv sim b) := by
gcongr
· exact hPrim b
· exact simulatorGap_le ring encoding prims adv sim b
_ ≤ epsilonPrim + _ + _ + (epsilonEnc + epsilonRestore) := by
gcongr
· exact hEnc b
· exact hRestore b
_ = _ := by ringmlkem768_sprAdv_le_mlwe
ML-KEM-768 SPR ≤ named assumptions + three MLWE advantages, against the
uniform-ciphertext-bytes simulator mlkem768UniformCiphertext (the same
simulator as mlkem768_sprAdv_le_two_hop_decomposition).
theorem mlkem768_sprAdv_le_mlwe (b : Bool)
(epsilonPrim epsilonEnc epsilonRestore : ℝ)
(hPrim : ∀ b, primitiveIdealization concreteNTTRingOps mlkem768Encoding mlkem768Primitives adv768 b ≤ epsilonPrim)
(hEnc : ∀ b, encodingRegularity concreteNTTRingOps mlkem768Encoding mlkem768Primitives adv768
mlkem768UniformCiphertext b ≤ epsilonEnc)
(hRestore : ∀ b, keyRestoration concreteNTTRingOps mlkem768Encoding mlkem768Primitives adv768
mlkem768UniformCiphertext b ≤ epsilonRestore) :
mlkem768KEM.sprAdv mlkem768UniformCiphertext adv768 b ≤
epsilonPrim
+ LearningWithErrors.advantage mlkem768KeyHopProblem
(keyHopAdv concreteNTTRingOps mlkem768Encoding mlkem768Primitives adv768 b)
+ LearningWithErrors.advantage mlkem768CtHopProblem
(ctHopAdv concreteNTTRingOps mlkem768Encoding mlkem768Primitives adv768 b)
+ epsilonEnc
+ epsilonRestore :=
sprAdv_le_mlwe concreteNTTRingOps mlkem768Encoding mlkem768Primitives adv768
mlkem768UniformCiphertext b epsilonPrim epsilonEnc epsilonRestore hPrim hEnc hRestore