diff --git a/Complexity/Crypto/Computational/CramerShoup.lean b/Complexity/Crypto/Computational/CramerShoup.lean index 4dd264ac30..a3a76c2fde 100644 --- a/Complexity/Crypto/Computational/CramerShoup.lean +++ b/Complexity/Crypto/Computational/CramerShoup.lean @@ -24,12 +24,13 @@ import Complexity.Tactic.PPT /-! # The Cramer–Shoup public-key encryption scheme and 1CCA security under DDH -This module completes the single-challenge adaptive chosen-ciphertext (**1CCA / IND-CCA2**, -Definition 12.2, printed p. 468) security proof for the **Multiplicative Cramer–Shoup public-key -encryption scheme** ($\mathcal{E}_{\mathrm{MCS}}$, [Boneh and Shoup, Exercise 12.27, printed -pp. 521–522][BonehShoup2023], specialized to associated data $\mathcal{D} = \mathrm{Unit}$) defined -in `Complexity.Crypto.Computational.CramerShoup.Scheme` over a `PrimeOrderGroupFamily`, in the -standard model under: +This module completes the single-challenge adaptive chosen-ciphertext (**1CCA / IND-CCA2** and +**1CCA with associated data**, Definition 12.2, printed p. 468, and Section 12.7, Definition 12.7, +printed pp. 501–502) security proof for the **Multiplicative Cramer–Shoup public-key encryption +scheme** ($\mathcal{E}_{\mathrm{MCS}}$, [Boneh and Shoup, Exercise 12.27, printed +pp. 521–522][BonehShoup2023], over an arbitrary associated-data type `AD : Type` as well as the +associated-data-free specialization) defined in `Complexity.Crypto.Computational.CramerShoup.Scheme` +over a `PrimeOrderGroupFamily`, in the standard model under: 1. the Decisional Diffie–Hellman (`DDH`) assumption, 2. collision resistance (`CollisionResistantFor`) of the $\mathbb{Z}_q$-valued hash family $H'$ (`cramerShoupHashMod`), and @@ -40,15 +41,18 @@ following the Games 0–6 proof template of [Boneh and Shoup, Section 12.5.3, Th Remark 12.3, printed pp. 488–493][BonehShoup2023]. We first prove the concrete and asymptotic 1CCA bounds for the **ideal exact-uniform PMF** games -(`cramerShoupMsgCCA2Game`, `cramerShoupGroupCCA2Game`, `cramerShoupGroupINDCCA2`, -`cramerShoupCCA2Game`, `cramerShoupINDCCA2`), in which key and challenge exponents are drawn from -`PMF.uniformOfFintype (ZMod (F.order d))` and the `42 / 2 ^ n` dyadic loss arises solely from the -`sampleModNat` exponent draws inside the strict-PPT DDH and CRHF reductions. We then bridge these -ideal games to the library's **native strict-PPT PKE 1CCA notion** (`Encryption.cca2GameOn` and -`Encryption.INDCCA2On` in `Complexity/Crypto/Computational/Encryption/CCA.lean`) via -`advantage_cca2GameOn_cramerShoupGroupCCA2Game_le` (which accounts for the additional `8 / 2 ^ n` -per-bit `sampleModNat` approximation loss of `cramerShoupKeyGenProb` and `cramerShoupEncryptProb`), -yielding `DDH.indcca2On_cramerShoup`. +(`cramerShoupMsgCCA2Game`, `cramerShoupADGroupCCA2Game`, `cramerShoupADGroupINDCCA2`, +`cramerShoupGroupCCA2Game`, `cramerShoupGroupINDCCA2`, `cramerShoupADCCA2Game`, +`cramerShoupADINDCCA2`, `cramerShoupCCA2Game`, `cramerShoupINDCCA2`), in which key and challenge +exponents are drawn from `PMF.uniformOfFintype (ZMod (F.order d))` and the `42 / 2 ^ n` dyadic loss +arises solely from the `sampleModNat` exponent draws inside the strict-PPT DDH and CRHF reductions. +We then bridge these ideal games to the library's **native strict-PPT PKE 1CCA notions** +(`Encryption.cca2ADGameOn`, `Encryption.INDCCA2ADOn`, `Encryption.cca2GameOn`, and +`Encryption.INDCCA2On`) via `advantage_cca2ADGameOn_cramerShoupADGroupCCA2Game_le` and +`advantage_cca2GameOn_cramerShoupGroupCCA2Game_le` (which account for the additional `8 / 2 ^ n` +per-bit `sampleModNat` approximation loss of `cramerShoupKeyGenProb`, `cramerShoupADEncryptProb`, +and `cramerShoupEncryptProb`), yielding `DDH.indcca2ADOn_cramerShoup` and +`DDH.indcca2On_cramerShoup`. ## Source Statements ([BonehShoup2023], Section 12.5.3 & Exercise 12.27) @@ -113,38 +117,38 @@ Verbatim from [BonehShoup2023], Exercise 12.27 (printed pp. 521–522): ## Scope and Quantitative Adaptation -* **Associated data specialized to $\mathcal{D} = \mathrm{Unit}$** and **single-challenge 1CCA - (`Definition 12.2`)**. +* **Associated data $\mathcal{D}$** ([BonehShoup2023], Section 12.7, Definition 12.7, printed + pp. 501–502, and Exercise 12.27, printed pp. 521–522) and **single-challenge 1CCA + (`Definition 12.2` / `Definition 12.7`)**. * **Adaptation of Game 4 from $\mathcal{E}_{\mathrm{CS}}$ to $\mathcal{E}_{\mathrm{MCS}}$**: in Theorem 12.9 ($\mathcal{E}_{\mathrm{CS}}$), $H'$ hashes the pair $(v, w)$ and $\mathit{Coll}_{u,v}(\hat{v}, \hat{w})$ tests $(\hat{v}, \hat{w}) \ne (v, w) \land H'(\hat{v}, \hat{w}) = H'(v, w)$. In Multiplicative Cramer–Shoup $\mathcal{E}_{\mathrm{MCS}}$ - (Exercise 12.27, printed pp. 521–522, with $\mathcal{D} = \mathrm{Unit}$), $H'$ hashes the triple - $(v, w, e) \in \mathbb{G}^3$ (`encBits3`), so the Game 4 collision predicate is originally adapted - to $(\hat{v}, \hat{w}, \hat{e}) \ne (v, w, e) \land H'(\hat{v}, \hat{w}, \hat{e}) = H'(v, w, e)$ - (while Phase 2 decryption already rejects the full challenge ciphertext - $(\hat{v}, \hat{w}, \hat{e}, \hat{z}') = (v, w, e, z')$). -* **Ideal exact-uniform PMF games vs. native `Encryption.INDCCA2On`**: - `cramerShoupMsgCCA2Game`, `cramerShoupGroupCCA2Game`, `cramerShoupGroupINDCCA2`, - `cramerShoupCCA2Game`, and `cramerShoupINDCCA2` are **ideal exact-uniform PMF** 1CCA experiments - and security definitions, in which the challenger samples exponents from - `PMF.uniformOfFintype (ZMod (F.order d))`. In `advantage_cramerShoupGroupCCA2Game_le` (an original - quantitative adaptation of `(12.19)` to $\mathcal{E}_{\mathrm{MCS}}$ in the uniform strict-PPT - model), the two fixed-bit DDH distinguishers (`cramerShoupToDDHDistinguisher`) and two fixed-bit - collision finders (`cramerShoupToCollisionFinder`) contribute `42 / 2 ^ n` from their internal + with associated data (Exercise 12.27, printed pp. 521–522), $H'$ hashes the 4-tuple + $(v, w, e, d) \in \mathbb{G}^3 \times \mathcal{D}$ (`encBits4`), so the Game 4 collision + predicate tests $(\hat{v}, \hat{w}, \hat{e}, \hat{d}) \ne (v, w, e, d) \land + H'(\hat{v}, \hat{w}, \hat{e}, \hat{d}) = H'(v, w, e, d)$ (while Phase 2 decryption rejects the + full challenge pair $((\hat{v}, \hat{w}, \hat{e}, \hat{z}'), \hat{d}) = ((v, w, e, z'), d)$). +* **Ideal exact-uniform PMF games vs. native `Encryption.INDCCA2ADOn` / `Encryption.INDCCA2On`**: + `cramerShoupMsgCCA2Game`, `cramerShoupADGroupCCA2Game`, `cramerShoupADGroupINDCCA2`, + `cramerShoupGroupCCA2Game`, `cramerShoupGroupINDCCA2`, `cramerShoupADCCA2Game`, + `cramerShoupADINDCCA2`, `cramerShoupCCA2Game`, and `cramerShoupINDCCA2` are **ideal exact-uniform + PMF** 1CCA experiments and security definitions, in which the challenger samples exponents from + `PMF.uniformOfFintype (ZMod (F.order d))`. In `advantage_cramerShoupADGroupCCA2Game_le` and + `advantage_cramerShoupGroupCCA2Game_le` (original quantitative adaptations of `(12.19)` to + $\mathcal{E}_{\mathrm{MCS}}$ in the uniform strict-PPT model), the two fixed-bit DDH + distinguishers and two fixed-bit collision finders contribute `42 / 2 ^ n` from their internal `sampleModNat` exponent draws, and `(2 * Q₁ n + 2 * Q₂ n + 2) / m` combines the non-DH triple exclusion (`1 / m`) and the adaptive Lemma 12.8 bound (`(Q₁ n + Q₂ n) / m`) across `bit = false` - and `bit = true`. The **native strict-PPT** security theorem `DDH.indcca2On_cramerShoup` - instantiates `Encryption.INDCCA2On` (`Complexity/Crypto/Computational/Encryption/CCA.lean`), - bridging from `cramerShoupKeyGenProb` and `cramerShoupEncryptProb` to the ideal game via - `advantage_cca2GameOn_cramerShoupGroupCCA2Game_le` with an additional `8 / 2 ^ n` statistical loss - per challenge bit (`16 / 2 ^ n` across both bits). -* **Group-order growth premise**: the asymptotic theorems (`DDH.cramerShoupMsgINDCCA2`, - `DDH.cramerShoupGroupINDCCA2`, `DDH.cramerShoupINDCCA2`, and `DDH.indcca2On_cramerShoup`) assume a - positive lower bound `m : ℕ → ℕ` on the group order `F.order` with - `Negligible (fun n => (1 / m n : ℝ≥0))` so that the information-theoretic projective-hash and - non-DH-triple terms `(2 * Q₁ n + 2 * Q₂ n + 2) / m n` are negligible for polynomial query bounds - `Q₁, Q₂`. + and `bit = true`. The **native strict-PPT** security theorems `DDH.indcca2ADOn_cramerShoup` and + `DDH.indcca2On_cramerShoup` instantiate `Encryption.INDCCA2ADOn` and `Encryption.INDCCA2On`, + bridging from `cramerShoupKeyGenProb`, `cramerShoupADEncryptProb`, and `cramerShoupEncryptProb` + to the ideal games with an additional `8 / 2 ^ n` statistical loss per challenge bit (`16 / 2 ^ n` + across both bits). +* **Group-order growth premise**: the asymptotic theorems assume a positive lower bound `m : ℕ → ℕ` + on the group order `F.order` with `Negligible (fun n => (1 / m n : ℝ≥0))` so that the + information-theoretic projective-hash and non-DH-triple terms `(2 * Q₁ n + 2 * Q₂ n + 2) / m n` + are negligible for polynomial query bounds `Q₁, Q₂`. -/ @[expose] public section @@ -158,15 +162,17 @@ variable (F : PrimeOrderGroupFamily) /-- The **ideal exact-uniform PMF** single-challenge 1CCA experiment for Multiplicative Cramer–Shoup -parameterized by a message-to-group-word encoding `encodeMsg : F.Desc × Msg → List Bool` -([BonehShoup2023], Section 12.1, Attack Game 12.1 and Definition 12.2, printed pp. 467–468, and -Exercise 12.27, printed pp. 521–522, specialized to associated data $\mathcal{D} = \mathrm{Unit}$; -Game 0 of Section 12.5.3, printed pp. 490–491). Key and challenge exponents are sampled from the -exact uniform distribution `PMF.uniformOfFintype (ZMod (F.order d))`. -/ +parameterized by `hasAD : Bool`, a message-to-group-word encoding `encodeMsg : F.Desc × Msg → +List Bool`, and an associated-data encoding `encodeAD : F.Desc × AD → List Bool` +([BonehShoup2023], Section 12.1, Attack Game 12.1 and Definition 12.2, printed pp. 467–468; Section +12.7, Definition 12.7, printed pp. 501–502; and Exercise 12.27, printed pp. 521–522; Game 0 of +Section 12.5.3, printed pp. 490–491). Key and challenge exponents are sampled from the exact uniform +distribution `PMF.uniformOfFintype (ZMod (F.order d))`. -/ noncomputable def cramerShoupMsgCCA2Game - {Msg : Type} (encodeMsg : F.Desc × Msg → List Bool) (setup : ℕ → PMF F.Desc) + (hasAD : Bool) {Msg AD : Type} (encodeMsg : F.Desc × Msg → List Bool) + (encodeAD : F.Desc × AD → List Bool) (setup : ℕ → PMF F.Desc) (H : F.Desc × List Bool → List Bool) (isElem : F.Desc × List Bool → Bool) - (adversary : CCA2AdversaryOf Msg) (bit : Bool) (n : ℕ) : Game := do + (adversary : ADCCA2AdversaryOf Msg AD) (bit : Bool) (n : ℕ) : Game := do let d ← setup n let α ← PMF.uniformOfFintype (ZMod (F.order d)) let σ ← PMF.uniformOfFintype (ZMod (F.order d)) @@ -180,33 +186,56 @@ noncomputable def cramerShoupMsgCCA2Game let pk := F.encodeCramerShoupPublicKeyValues d pkVal let choiceOut ← ProbComp.eval ((OracleComp.interpret (ProbOracleComp.handler (fun m => StateT.lift (ProbComp.unifFin m)) - (F.cramerShoupPhase1Oracle H isElem d sk)) + (F.cramerShoupPhase1Oracle hasAD H isElem d sk)) (adversary.choose n pk)).run ()) let choice := choiceOut.1 - let m₀ := encodeMsg (d, choice.1.1) - let m₁ := encodeMsg (d, choice.1.2) + let m₀ := encodeMsg (d, choice.1.1.1) + let m₁ := encodeMsg (d, choice.1.1.2) + let ad := encodeAD (d, choice.1.2) if isElem (d, m₀) && isElem (d, m₁) then let m := if bit then m₁ else m₀ let β ← PMF.uniformOfFintype (ZMod (F.order d)) let ct := F.encodeCramerShoupEncrypt H d (F.encode d pkVal.1, F.encode d pkVal.2.1, F.encode d pkVal.2.2.1, F.encode d pkVal.2.2.2) - m β.val + m ad β.val let guessOut ← ProbComp.eval ((OracleComp.interpret (ProbOracleComp.handler (fun m => StateT.lift (ProbComp.unifFin m)) - (F.cramerShoupPhase2Oracle H isElem d sk ct)) + (F.cramerShoupPhase2Oracle hasAD H isElem d sk ct ad)) (adversary.guess n choice.2 ct)).run ()) return guessOut.1 else return false +/-- The **ideal exact-uniform PMF** single-challenge 1CCA experiment with associated data for +Multiplicative Cramer–Shoup over encoded group-element messages (`Msg = List Bool`, `encodeMsg = +Prod.snd`) and associated-data type `AD` encoded via `encAD : AD ↪ List Bool`. -/ +noncomputable abbrev cramerShoupADGroupCCA2Game + {AD : Type} (encAD : AD ↪ List Bool) + (setup : ℕ → PMF F.Desc) (H : F.Desc × List Bool → List Bool) + (isElem : F.Desc × List Bool → Bool) (adversary : WordADCCA2Adversary AD) + (bit : Bool) (n : ℕ) : Game := + F.cramerShoupMsgCCA2Game true Prod.snd (fun p => encAD p.2) setup H isElem adversary bit n + +/-- Ideal exact-uniform PMF single-challenge 1CCA security with associated data of Multiplicative +Cramer–Shoup over encoded group-element messages (`WordADCCA2Adversary AD`). -/ +def cramerShoupADGroupINDCCA2 + {AD : Type} (encAD : AD ↪ List Bool) + (setup : ℕ → PMF F.Desc) (H : F.Desc × List Bool → List Bool) + (isElem : F.Desc × List Bool → Bool) : Prop := + Game.Indistinguishable + (fun adversary n => F.cramerShoupADGroupCCA2Game encAD setup H isElem adversary false n) + (fun adversary n => F.cramerShoupADGroupCCA2Game encAD setup H isElem adversary true n) + (WordADCCA2Adversary.IsPPT encAD) + /-- The **ideal exact-uniform PMF** single-challenge 1CCA experiment for Multiplicative Cramer–Shoup -over encoded group-element messages (`Msg = List Bool`, `encodeMsg = Prod.snd`), enforcing the -group-membership challenge admissibility check `isElem (d, m₀) && isElem (d, m₁)`. -/ +over encoded group-element messages (`Msg = List Bool`, `encodeMsg = Prod.snd`, associated-data-free +specialization `ad = []`), enforcing the group-membership challenge admissibility check +`isElem (d, m₀) && isElem (d, m₁)`. -/ noncomputable abbrev cramerShoupGroupCCA2Game (setup : ℕ → PMF F.Desc) (H : F.Desc × List Bool → List Bool) (isElem : F.Desc × List Bool → Bool) (adversary : WordCCA2Adversary) (bit : Bool) (n : ℕ) : Game := - F.cramerShoupMsgCCA2Game Prod.snd setup H isElem adversary bit n + F.cramerShoupMsgCCA2Game false Prod.snd (fun _ => []) setup H isElem adversary.toADUnit bit n /-- Ideal exact-uniform PMF single-challenge 1CCA security of Multiplicative Cramer–Shoup over encoded group-element messages (`WordCCA2Adversary`). -/ @@ -218,12 +247,36 @@ def cramerShoupGroupINDCCA2 (fun adversary n => F.cramerShoupGroupCCA2Game setup H isElem adversary true n) WordCCA2Adversary.IsPPT +/-- The **ideal exact-uniform PMF** single-challenge 1CCA experiment with associated data for +Multiplicative Cramer–Shoup over single-bit messages (`Msg = Bool`, embedded into `F.Carrier d` via +`F.encodeBit`) and associated-data type `AD` encoded via `encAD : AD ↪ List Bool`. -/ +noncomputable abbrev cramerShoupADCCA2Game + {AD : Type} (encAD : AD ↪ List Bool) + (setup : ℕ → PMF F.Desc) (H : F.Desc × List Bool → List Bool) + (isElem : F.Desc × List Bool → Bool) (adversary : ADCCA2Adversary AD) + (bit : Bool) (n : ℕ) : Game := + F.cramerShoupMsgCCA2Game true (fun p => F.encodeBit p.1 p.2) (fun p => encAD p.2) + setup H isElem adversary bit n + +/-- Ideal exact-uniform PMF single-challenge 1CCA security with associated data of Multiplicative +Cramer–Shoup over single-bit messages (`ADCCA2Adversary AD`). -/ +def cramerShoupADINDCCA2 + {AD : Type} (encAD : AD ↪ List Bool) + (setup : ℕ → PMF F.Desc) (H : F.Desc × List Bool → List Bool) + (isElem : F.Desc × List Bool → Bool) : Prop := + Game.Indistinguishable + (fun adversary n => F.cramerShoupADCCA2Game encAD setup H isElem adversary false n) + (fun adversary n => F.cramerShoupADCCA2Game encAD setup H isElem adversary true n) + (ADCCA2Adversary.IsPPT encAD) + /-- The **ideal exact-uniform PMF** single-challenge 1CCA experiment for Multiplicative Cramer–Shoup -over single-bit messages (`Msg = Bool`, embedded into `F.Carrier d` via `F.encodeBit`). -/ +over single-bit messages (`Msg = Bool`, embedded into `F.Carrier d` via `F.encodeBit`, +associated-data-free specialization `ad = []`). -/ noncomputable abbrev cramerShoupCCA2Game (setup : ℕ → PMF F.Desc) (H : F.Desc × List Bool → List Bool) (isElem : F.Desc × List Bool → Bool) (adversary : CCA2Adversary) (bit : Bool) (n : ℕ) : Game := - F.cramerShoupMsgCCA2Game (fun p => F.encodeBit p.1 p.2) setup H isElem adversary bit n + F.cramerShoupMsgCCA2Game false (fun p => F.encodeBit p.1 p.2) (fun _ => []) + setup H isElem adversary.toADUnit bit n /-- Ideal exact-uniform PMF single-challenge 1CCA security of Multiplicative Cramer–Shoup over single-bit messages (`CCA2Adversary`). -/ @@ -236,13 +289,14 @@ def cramerShoupINDCCA2 CCA2Adversary.IsPPT /-- **Game 1** ([BonehShoup2023], Section 12.5.3, printed p. 490, adapted to -$\mathcal{E}_{\mathrm{MCS}}$): draw `(α, β)` and `sk` uniformly from `ZMod q`, and compute the -challenge ciphertext from `(v, w) = (β • g, (α * β) • g)` using the secret key via +$\mathcal{E}_{\mathrm{MCS}}$ with associated data): draw `(α, β)` and `sk` uniformly from `ZMod q`, +and compute the challenge ciphertext from `(v, w) = (β • g, (α * β) • g)` using the secret key via `encodeCramerShoupChallenge`. -/ noncomputable def cramerShoupGame1 - {Msg : Type} (encodeMsg : F.Desc × Msg → List Bool) (setup : ℕ → PMF F.Desc) + (hasAD : Bool) {Msg AD : Type} (encodeMsg : F.Desc × Msg → List Bool) + (encodeAD : F.Desc × AD → List Bool) (setup : ℕ → PMF F.Desc) (H : F.Desc × List Bool → List Bool) (isElem : F.Desc × List Bool → Bool) - (adversary : CCA2AdversaryOf Msg) (bit : Bool) (n : ℕ) : Game := do + (adversary : ADCCA2AdversaryOf Msg AD) (bit : Bool) (n : ℕ) : Game := do let d ← setup n let α ← PMF.uniformOfFintype (ZMod (F.order d)) let β ← PMF.uniformOfFintype (ZMod (F.order d)) @@ -259,17 +313,18 @@ noncomputable def cramerShoupGame1 let pk := F.encodeCramerShoupPublicKey d u sk let choiceOut ← ProbComp.eval ((OracleComp.interpret (ProbOracleComp.handler (fun m => StateT.lift (ProbComp.unifFin m)) - (F.cramerShoupPhase1Oracle H isElem d sk)) + (F.cramerShoupPhase1Oracle hasAD H isElem d sk)) (adversary.choose n pk)).run ()) let choice := choiceOut.1 - let m₀ := encodeMsg (d, choice.1.1) - let m₁ := encodeMsg (d, choice.1.2) + let m₀ := encodeMsg (d, choice.1.1.1) + let m₁ := encodeMsg (d, choice.1.1.2) + let ad := encodeAD (d, choice.1.2) if isElem (d, m₀) && isElem (d, m₁) then let m := if bit then m₁ else m₀ - let ct := F.encodeCramerShoupChallenge H d sk v w m + let ct := F.encodeCramerShoupChallenge H d sk v w m ad let guessOut ← ProbComp.eval ((OracleComp.interpret (ProbOracleComp.handler (fun m => StateT.lift (ProbComp.unifFin m)) - (F.cramerShoupPhase2Oracle H isElem d sk ct)) + (F.cramerShoupPhase2Oracle hasAD H isElem d sk ct ad)) (adversary.guess n choice.2 ct)).run ()) return guessOut.1 else @@ -279,11 +334,12 @@ noncomputable def cramerShoupGame1 computing the challenge ciphertext from `(v, w) = (β • g, (α * β) • g)` using the secret key produces the exact same distribution as public-key encryption. -/ theorem cramerShoupMsgCCA2Game_eq_game1 - {Msg : Type} (encodeMsg : F.Desc × Msg → List Bool) (setup : ℕ → PMF F.Desc) + (hasAD : Bool) {Msg AD : Type} (encodeMsg : F.Desc × Msg → List Bool) + (encodeAD : F.Desc × AD → List Bool) (setup : ℕ → PMF F.Desc) (H : F.Desc × List Bool → List Bool) (isElem : F.Desc × List Bool → Bool) - (adversary : CCA2AdversaryOf Msg) (bit : Bool) (n : ℕ) : - F.cramerShoupMsgCCA2Game encodeMsg setup H isElem adversary bit n = - F.cramerShoupGame1 encodeMsg setup H isElem adversary bit n := by + (adversary : ADCCA2AdversaryOf Msg AD) (bit : Bool) (n : ℕ) : + F.cramerShoupMsgCCA2Game hasAD encodeMsg encodeAD setup H isElem adversary bit n = + F.cramerShoupGame1 hasAD encodeMsg encodeAD setup H isElem adversary bit n := by simp only [cramerShoupMsgCCA2Game, cramerShoupGame1, PMF.monad_bind_eq_bind, PMF.monad_pure_eq_pure, encodeCramerShoupPublicKey_encode, skNatOfZMod, ZMod.natCast_zmod_val, encodeCramerShoupEncrypt_publicKey_eq_challenge] @@ -302,19 +358,20 @@ theorem cramerShoupMsgCCA2Game_eq_game1 refine congrArg _ (funext fun τ₂ => ?_) conv_rhs => rw [PMF.bind_comm (PMF.uniformOfFintype (ZMod (F.order d)))] refine congrArg _ (funext fun choiceOut => ?_) - by_cases h : (isElem (d, encodeMsg (d, choiceOut.1.1.1)) && - isElem (d, encodeMsg (d, choiceOut.1.1.2))) = true + by_cases h : (isElem (d, encodeMsg (d, choiceOut.1.1.1.1)) && + isElem (d, encodeMsg (d, choiceOut.1.1.1.2))) = true · simp only [h, ↓reduceIte] · simp only [h, Bool.false_eq_true, ↓reduceIte, PMF.bind_const] /-- **Game 2** ([BonehShoup2023], Section 12.5.3, printed p. 490, adapted to -$\mathcal{E}_{\mathrm{MCS}}$): replace the Diffie–Hellman challenge pair +$\mathcal{E}_{\mathrm{MCS}}$ with associated data): replace the Diffie–Hellman challenge pair `(v, w) = (β • g, (α * β) • g)` by an independent random pair `(v, w) = (β • g, γ • g)` with uniform `γ : ZMod (F.order d)`. -/ noncomputable def cramerShoupGame2 - {Msg : Type} (encodeMsg : F.Desc × Msg → List Bool) (setup : ℕ → PMF F.Desc) + (hasAD : Bool) {Msg AD : Type} (encodeMsg : F.Desc × Msg → List Bool) + (encodeAD : F.Desc × AD → List Bool) (setup : ℕ → PMF F.Desc) (H : F.Desc × List Bool → List Bool) (isElem : F.Desc × List Bool → Bool) - (adversary : CCA2AdversaryOf Msg) (bit : Bool) (n : ℕ) : Game := do + (adversary : ADCCA2AdversaryOf Msg AD) (bit : Bool) (n : ℕ) : Game := do let d ← setup n let α ← PMF.uniformOfFintype (ZMod (F.order d)) let β ← PMF.uniformOfFintype (ZMod (F.order d)) @@ -332,31 +389,34 @@ noncomputable def cramerShoupGame2 let pk := F.encodeCramerShoupPublicKey d u sk let choiceOut ← ProbComp.eval ((OracleComp.interpret (ProbOracleComp.handler (fun m => StateT.lift (ProbComp.unifFin m)) - (F.cramerShoupPhase1Oracle H isElem d sk)) + (F.cramerShoupPhase1Oracle hasAD H isElem d sk)) (adversary.choose n pk)).run ()) let choice := choiceOut.1 - let m₀ := encodeMsg (d, choice.1.1) - let m₁ := encodeMsg (d, choice.1.2) + let m₀ := encodeMsg (d, choice.1.1.1) + let m₁ := encodeMsg (d, choice.1.1.2) + let ad := encodeAD (d, choice.1.2) if isElem (d, m₀) && isElem (d, m₁) then let m := if bit then m₁ else m₀ - let ct := F.encodeCramerShoupChallenge H d sk v w m + let ct := F.encodeCramerShoupChallenge H d sk v w m ad let guessOut ← ProbComp.eval ((OracleComp.interpret (ProbOracleComp.handler (fun m => StateT.lift (ProbComp.unifFin m)) - (F.cramerShoupPhase2Oracle H isElem d sk ct)) + (F.cramerShoupPhase2Oracle hasAD H isElem d sk ct ad)) (adversary.guess n choice.2 ct)).run ()) return guessOut.1 else return false private theorem advantage_ddhRealGame_cramerShoupGame1_le - {Msg : Type} (encodeMsg : F.Desc × Msg → List Bool) (setup : ℕ → PMF F.Desc) + (hasAD : Bool) {Msg AD : Type} (encodeMsg : F.Desc × Msg → List Bool) + (encodeAD : F.Desc × AD → List Bool) (setup : ℕ → PMF F.Desc) (H : F.Desc × List Bool → List Bool) (isElem : F.Desc × List Bool → Bool) (hmem : ∀ d x, isElem (d, x) = true ↔ x ∈ Set.range (F.encode d)) - (adversary : CCA2AdversaryOf Msg) (bit : Bool) (n : ℕ) : + (adversary : ADCCA2AdversaryOf Msg AD) (bit : Bool) (n : ℕ) : Game.advantage (F.ddhRealGame setup - (F.cramerShoupMsgToDDHDistinguisher encodeMsg H isElem adversary bit) n) - (F.cramerShoupGame1 encodeMsg setup H isElem adversary bit n) ≤ 6 / 2 ^ n := by + (F.cramerShoupMsgToDDHDistinguisher hasAD encodeMsg encodeAD H isElem adversary bit) n) + (F.cramerShoupGame1 hasAD encodeMsg encodeAD setup H isElem adversary bit n) ≤ + 6 / 2 ^ n := by unfold ddhRealGame cramerShoupGame1 cramerShoupMsgToDDHDistinguisher simp only [ProbComp.eval_bind, PMF.monad_bind_eq_bind, PMF.monad_pure_eq_pure] refine advantage_bind_le_nnreal (setup n) _ _ fun d => ?_ @@ -364,24 +424,28 @@ private theorem advantage_ddhRealGame_cramerShoupGame1_le refine advantage_bind_le_nnreal (PMF.uniformOfFintype (ZMod (F.order d))) _ _ fun β => ?_ refine F.advantage_bind_sampleCramerShoupSK_le n d _ _ fun s t s₁ t₁ s₂ t₂ => ?_ rw [F.encodeCramerShoupPublicKey_eq_skNatOfZMod d α ((s, t), ((s₁, t₁), (s₂, t₂))), - F.cramerShoupPhase1Oracle_eq_skNatOfZMod H isElem hmem d ((s, t), ((s₁, t₁), (s₂, t₂)))] + F.cramerShoupPhase1Oracle_eq_skNatOfZMod hasAD H isElem hmem d + ((s, t), ((s₁, t₁), (s₂, t₂)))] refine congrArg _ (funext fun choiceOut => ?_) - by_cases h : (isElem (d, encodeMsg (d, choiceOut.1.1.1)) && - isElem (d, encodeMsg (d, choiceOut.1.1.2))) = true + by_cases h : (isElem (d, encodeMsg (d, choiceOut.1.1.1.1)) && + isElem (d, encodeMsg (d, choiceOut.1.1.1.2))) = true · simp only [h, ↓reduceIte, ProbComp.eval_bind, ProbComp.eval_pure] rw [F.encodeCramerShoupChallenge_eq_skNatOfZMod H d ((s, t), ((s₁, t₁), (s₂, t₂))), - F.cramerShoupPhase2Oracle_eq_skNatOfZMod H isElem hmem d ((s, t), ((s₁, t₁), (s₂, t₂)))] + F.cramerShoupPhase2Oracle_eq_skNatOfZMod hasAD H isElem hmem d + ((s, t), ((s₁, t₁), (s₂, t₂)))] · simp only [h, Bool.false_eq_true, ↓reduceIte, ProbComp.eval_pure] private theorem advantage_ddhIdealGame_cramerShoupGame2_le - {Msg : Type} (encodeMsg : F.Desc × Msg → List Bool) (setup : ℕ → PMF F.Desc) + (hasAD : Bool) {Msg AD : Type} (encodeMsg : F.Desc × Msg → List Bool) + (encodeAD : F.Desc × AD → List Bool) (setup : ℕ → PMF F.Desc) (H : F.Desc × List Bool → List Bool) (isElem : F.Desc × List Bool → Bool) (hmem : ∀ d x, isElem (d, x) = true ↔ x ∈ Set.range (F.encode d)) - (adversary : CCA2AdversaryOf Msg) (bit : Bool) (n : ℕ) : + (adversary : ADCCA2AdversaryOf Msg AD) (bit : Bool) (n : ℕ) : Game.advantage (F.ddhIdealGame setup - (F.cramerShoupMsgToDDHDistinguisher encodeMsg H isElem adversary bit) n) - (F.cramerShoupGame2 encodeMsg setup H isElem adversary bit n) ≤ 6 / 2 ^ n := by + (F.cramerShoupMsgToDDHDistinguisher hasAD encodeMsg encodeAD H isElem adversary bit) n) + (F.cramerShoupGame2 hasAD encodeMsg encodeAD setup H isElem adversary bit n) ≤ + 6 / 2 ^ n := by unfold ddhIdealGame cramerShoupGame2 cramerShoupMsgToDDHDistinguisher simp only [ProbComp.eval_bind, PMF.monad_bind_eq_bind, PMF.monad_pure_eq_pure] refine advantage_bind_le_nnreal (setup n) _ _ fun d => ?_ @@ -390,13 +454,15 @@ private theorem advantage_ddhIdealGame_cramerShoupGame2_le refine advantage_bind_le_nnreal (PMF.uniformOfFintype (ZMod (F.order d))) _ _ fun γ => ?_ refine F.advantage_bind_sampleCramerShoupSK_le n d _ _ fun s t s₁ t₁ s₂ t₂ => ?_ rw [F.encodeCramerShoupPublicKey_eq_skNatOfZMod d α ((s, t), ((s₁, t₁), (s₂, t₂))), - F.cramerShoupPhase1Oracle_eq_skNatOfZMod H isElem hmem d ((s, t), ((s₁, t₁), (s₂, t₂)))] + F.cramerShoupPhase1Oracle_eq_skNatOfZMod hasAD H isElem hmem d + ((s, t), ((s₁, t₁), (s₂, t₂)))] refine congrArg _ (funext fun choiceOut => ?_) - by_cases h : (isElem (d, encodeMsg (d, choiceOut.1.1.1)) && - isElem (d, encodeMsg (d, choiceOut.1.1.2))) = true + by_cases h : (isElem (d, encodeMsg (d, choiceOut.1.1.1.1)) && + isElem (d, encodeMsg (d, choiceOut.1.1.1.2))) = true · simp only [h, ↓reduceIte, ProbComp.eval_bind, ProbComp.eval_pure] rw [F.encodeCramerShoupChallenge_eq_skNatOfZMod H d ((s, t), ((s₁, t₁), (s₂, t₂))), - F.cramerShoupPhase2Oracle_eq_skNatOfZMod H isElem hmem d ((s, t), ((s₁, t₁), (s₂, t₂)))] + F.cramerShoupPhase2Oracle_eq_skNatOfZMod hasAD H isElem hmem d + ((s, t), ((s₁, t₁), (s₂, t₂)))] · simp only [h, Bool.false_eq_true, ↓reduceIte, ProbComp.eval_pure] private theorem advantage_four_le (g₁ g₂ g₃ g₄ : Game) {n : ℕ} @@ -413,29 +479,33 @@ p. 492): the distinguishing advantage between `cramerShoupGame1` and `cramerShou most the DDH advantage of `cramerShoupMsgToDDHDistinguisher` plus `12 / 2 ^ n` (six approximate `sampleModNat` secret-key exponent draws in `ddhRealGame` and six in `ddhIdealGame`). -/ theorem advantage_cramerShoupGame1_game2_le - {Msg : Type} (encodeMsg : F.Desc × Msg → List Bool) (setup : ℕ → PMF F.Desc) + (hasAD : Bool) {Msg AD : Type} (encodeMsg : F.Desc × Msg → List Bool) + (encodeAD : F.Desc × AD → List Bool) (setup : ℕ → PMF F.Desc) (H : F.Desc × List Bool → List Bool) (isElem : F.Desc × List Bool → Bool) (hmem : ∀ d x, isElem (d, x) = true ↔ x ∈ Set.range (F.encode d)) - (adversary : CCA2AdversaryOf Msg) (bit : Bool) (n : ℕ) : + (adversary : ADCCA2AdversaryOf Msg AD) (bit : Bool) (n : ℕ) : Game.advantage - (F.cramerShoupGame1 encodeMsg setup H isElem adversary bit n) - (F.cramerShoupGame2 encodeMsg setup H isElem adversary bit n) ≤ + (F.cramerShoupGame1 hasAD encodeMsg encodeAD setup H isElem adversary bit n) + (F.cramerShoupGame2 hasAD encodeMsg encodeAD setup H isElem adversary bit n) ≤ Game.advantage (F.ddhRealGame setup - (F.cramerShoupMsgToDDHDistinguisher encodeMsg H isElem adversary bit) n) + (F.cramerShoupMsgToDDHDistinguisher hasAD encodeMsg encodeAD H isElem adversary bit) n) (F.ddhIdealGame setup - (F.cramerShoupMsgToDDHDistinguisher encodeMsg H isElem adversary bit) n) + + (F.cramerShoupMsgToDDHDistinguisher hasAD encodeMsg encodeAD H isElem adversary bit) n) + 12 / 2 ^ n := advantage_four_le _ _ _ _ - (F.advantage_ddhRealGame_cramerShoupGame1_le encodeMsg setup H isElem hmem adversary bit n) - (F.advantage_ddhIdealGame_cramerShoupGame2_le encodeMsg setup H isElem hmem adversary bit n) + (F.advantage_ddhRealGame_cramerShoupGame1_le hasAD encodeMsg encodeAD setup H isElem hmem + adversary bit n) + (F.advantage_ddhIdealGame_cramerShoupGame2_le hasAD encodeMsg encodeAD setup H isElem hmem + adversary bit n) /-- Exact-exponent collision game matching the collision event between `cramerShoupGame2` and `cramerShoupGame3`. -/ private noncomputable def cramerShoupExactCollisionGame - {Msg : Type} (encodeMsg : F.Desc × Msg → List Bool) (setup : ℕ → PMF F.Desc) + (hasAD : Bool) {Msg AD : Type} (encodeMsg : F.Desc × Msg → List Bool) + (encodeAD : F.Desc × AD → List Bool) (setup : ℕ → PMF F.Desc) (H : F.Desc × List Bool → List Bool) (isElem : F.Desc × List Bool → Bool) - (adversary : CCA2AdversaryOf Msg) (bit : Bool) (n : ℕ) : Game := do + (adversary : ADCCA2AdversaryOf Msg AD) (bit : Bool) (n : ℕ) : Game := do let d ← setup n let α ← PMF.uniformOfFintype (ZMod (F.order d)) let β ← PMF.uniformOfFintype (ZMod (F.order d)) @@ -453,31 +523,39 @@ private noncomputable def cramerShoupExactCollisionGame let pk := F.encodeCramerShoupPublicKey d u sk let choiceOut ← ProbComp.eval ((OracleComp.interpret (ProbOracleComp.handler (fun m => StateT.lift (ProbComp.unifFin m)) - (F.cramerShoupPhase1Oracle H isElem d sk)) + (F.cramerShoupPhase1Oracle hasAD H isElem d sk)) (adversary.choose n pk)).run ()) let choice := choiceOut.1 - let m₀ := encodeMsg (d, choice.1.1) - let m₁ := encodeMsg (d, choice.1.2) + let m₀ := encodeMsg (d, choice.1.1.1) + let m₁ := encodeMsg (d, choice.1.1.2) + let ad := encodeAD (d, choice.1.2) if isElem (d, m₀) && isElem (d, m₁) then let m := if bit then m₁ else m₀ - let ct := F.encodeCramerShoupChallenge H d sk v w m - runFinderOracle (F.cramerShoupCollisionPhase2Reply H isElem d sk ct) - (fun req => F.isCramerShoupCollision H d req ct) extractCramerShoupTriple + let ct := F.encodeCramerShoupChallenge H d sk v w m ad + runFinderOracle (F.cramerShoupCollisionPhase2Reply hasAD H isElem d sk ct ad) + (fun req => + let q := splitCramerShoupQuery hasAD req + F.isCramerShoupCollision H d q.1 q.2 ct ad) + (fun req => + let q := splitCramerShoupQuery hasAD req + extractCramerShoupTuple q.1 q.2) (adversary.guess n choice.2 ct) none (fun cand => isCollisionFor (F.cramerShoupHashMod H) (fun _ _ _ => true) n d - cand (extractCramerShoupTriple ct)) + cand (extractCramerShoupTuple ct ad)) else return false private theorem advantage_cramerShoupGame2_game3_le_exactCollision - {Msg : Type} (encodeMsg : F.Desc × Msg → List Bool) (setup : ℕ → PMF F.Desc) + (hasAD : Bool) {Msg AD : Type} (encodeMsg : F.Desc × Msg → List Bool) + (encodeAD : F.Desc × AD → List Bool) (setup : ℕ → PMF F.Desc) (H : F.Desc × List Bool → List Bool) (isElem : F.Desc × List Bool → Bool) - (adversary : CCA2AdversaryOf Msg) (bit : Bool) (n : ℕ) : + (adversary : ADCCA2AdversaryOf Msg AD) (bit : Bool) (n : ℕ) : Game.advantage - (F.cramerShoupGame2 encodeMsg setup H isElem adversary bit n) - (F.cramerShoupGame3 encodeMsg setup H isElem adversary bit n) ≤ + (F.cramerShoupGame2 hasAD encodeMsg encodeAD setup H isElem adversary bit n) + (F.cramerShoupGame3 hasAD encodeMsg encodeAD setup H isElem adversary bit n) ≤ Game.succProbability - (F.cramerShoupExactCollisionGame encodeMsg setup H isElem adversary bit n) := by + (F.cramerShoupExactCollisionGame hasAD encodeMsg encodeAD setup H isElem adversary + bit n) := by unfold cramerShoupGame2 cramerShoupGame3 cramerShoupExactCollisionGame simp only [PMF.monad_bind_eq_bind, PMF.monad_pure_eq_pure] refine advantage_bind_le_succProbability_bind (setup n) _ _ _ fun d => ?_ @@ -491,52 +569,60 @@ private theorem advantage_cramerShoupGame2_game3_le_exactCollision refine bind_apply_le_add_bind_apply _ _ _ _ true fun σ₂ => ?_ refine bind_apply_le_add_bind_apply _ _ _ _ true fun τ₂ => ?_ refine bind_apply_le_add_bind_apply _ _ _ _ true fun choiceOut => ?_ - by_cases h : (isElem (d, encodeMsg (d, choiceOut.1.1.1)) && - isElem (d, encodeMsg (d, choiceOut.1.1.2))) = true + by_cases h : (isElem (d, encodeMsg (d, choiceOut.1.1.1.1)) && + isElem (d, encodeMsg (d, choiceOut.1.1.1.2))) = true · simp only [h, ↓reduceIte] let sk := F.skNatOfZMod d σ τ σ₁ τ₁ σ₂ τ₂ let v := F.encode d (β • F.generator d) let w := F.encode d (γ • F.generator d) - let m := if bit then encodeMsg (d, choiceOut.1.1.2) else encodeMsg (d, choiceOut.1.1.1) - let ct := F.encodeCramerShoupChallenge H d sk v w m + let m := if bit then encodeMsg (d, choiceOut.1.1.1.2) else encodeMsg (d, choiceOut.1.1.1.1) + let ad := encodeAD (d, choiceOut.1.1.2) + let ct := F.encodeCramerShoupChallenge H d sk v w m ad have hrun : (ProbComp.eval ((OracleComp.interpret (ProbOracleComp.handler (fun m => StateT.lift (ProbComp.unifFin m)) - (F.cramerShoupPhase2Oracle H isElem d sk ct)) + (F.cramerShoupPhase2Oracle hasAD H isElem d sk ct ad)) (adversary.guess n choiceOut.1.2 ct)).run ())).bind (fun guessOut => PMF.pure guessOut.1) = - runStatelessOracle (F.cramerShoupPhase2Reply H isElem d sk ct) + runStatelessOracle (F.cramerShoupPhase2Reply hasAD H isElem d sk ct ad) (adversary.guess n choiceOut.1.2 ct) PMF.pure := rfl rw [hrun] refine runStatelessOracle_le_runFinderOracle - (F.cramerShoupPhase2Reply H isElem d sk ct) - (F.cramerShoupCollisionPhase2Reply H isElem d sk ct) - (fun req => F.isCramerShoupCollision H d req ct) extractCramerShoupTriple + (F.cramerShoupPhase2Reply hasAD H isElem d sk ct ad) + (F.cramerShoupCollisionPhase2Reply hasAD H isElem d sk ct ad) + (fun req => + let q := splitCramerShoupQuery hasAD req + F.isCramerShoupCollision H d q.1 q.2 ct ad) + (fun req => + let q := splitCramerShoupQuery hasAD req + extractCramerShoupTuple q.1 q.2) (fun cand => isCollisionFor (F.cramerShoupHashMod H) (fun _ _ _ => true) n d - cand (extractCramerShoupTriple ct)) - (fun req hbad => by simp [cramerShoupPhase2Reply, cramerShoupCollisionPhase2Reply, hbad]) + cand (extractCramerShoupTuple ct ad)) (fun req hbad => by - rw [← F.isCramerShoupCollision_eq_isCollisionFor H n d req ct]; exact hbad) + simp [cramerShoupPhase2Reply, cramerShoupCollisionPhase2Reply, hbad]) + (fun req hbad => by + rw [← F.isCramerShoupCollision_eq_isCollisionFor H n d _ _ ct ad]; exact hbad) (adversary.guess n choiceOut.1.2 ct) PMF.pure true · simp only [h, Bool.false_eq_true, ↓reduceIte] exact ⟨le_self_add, le_self_add⟩ private theorem succProbability_cramerShoupExactCollisionGame_le - {Msg : Type} (encodeMsg : F.Desc × Msg → List Bool) (setup : ℕ → PMF F.Desc) + (hasAD : Bool) {Msg AD : Type} (encodeMsg : F.Desc × Msg → List Bool) + (encodeAD : F.Desc × AD → List Bool) (setup : ℕ → PMF F.Desc) (H : F.Desc × List Bool → List Bool) (isElem : F.Desc × List Bool → Bool) (hmem : ∀ d x, isElem (d, x) = true ↔ x ∈ Set.range (F.encode d)) - (adversary : CCA2AdversaryOf Msg) (bit : Bool) (n : ℕ) : + (adversary : ADCCA2AdversaryOf Msg AD) (bit : Bool) (n : ℕ) : Game.succProbability - (F.cramerShoupExactCollisionGame encodeMsg setup H isElem adversary bit n) ≤ + (F.cramerShoupExactCollisionGame hasAD encodeMsg encodeAD setup H isElem adversary bit n) ≤ Game.succProbability (collisionGameFor setup (F.cramerShoupHashMod H) (fun _ _ _ => true) - (F.cramerShoupMsgToCollisionFinder encodeMsg H isElem adversary bit) n) + + (F.cramerShoupMsgToCollisionFinder hasAD encodeMsg encodeAD H isElem adversary bit) n) + 9 / 2 ^ n := by - let C := F.cramerShoupMsgToCollisionFinder encodeMsg H isElem adversary bit + let C := F.cramerShoupMsgToCollisionFinder hasAD encodeMsg encodeAD H isElem adversary bit have hadv : Game.advantage (collisionGameFor setup (F.cramerShoupHashMod H) (fun _ _ _ => true) C n) - (F.cramerShoupExactCollisionGame encodeMsg setup H isElem adversary bit n) ≤ + (F.cramerShoupExactCollisionGame hasAD encodeMsg encodeAD setup H isElem adversary bit n) ≤ 9 / 2 ^ n := by unfold collisionGameFor cramerShoupExactCollisionGame C cramerShoupMsgToCollisionFinder simp only [ProbComp.eval_bind, PMF.monad_bind_eq_bind, PMF.monad_pure_eq_pure, PMF.bind_bind, @@ -550,17 +636,24 @@ private theorem succProbability_cramerShoupExactCollisionGame_le let pk := F.encodeCramerShoupPublicKey d u sk (ProbComp.eval ((OracleComp.interpret (ProbOracleComp.handler (fun m => StateT.lift (ProbComp.unifFin m)) - (F.cramerShoupPhase1Oracle H isElem d sk)) + (F.cramerShoupPhase1Oracle hasAD H isElem d sk)) (adversary.choose n pk)).run ())).bind fun choiceOut => let choice := choiceOut.1 - if isElem (d, encodeMsg (d, choice.1.1)) && isElem (d, encodeMsg (d, choice.1.2)) then - let m := if bit then encodeMsg (d, choice.1.2) else encodeMsg (d, choice.1.1) - let ct := F.encodeCramerShoupChallenge H d sk v w m - runFinderOracle (F.cramerShoupCollisionPhase2Reply H isElem d sk ct) - (fun req => F.isCramerShoupCollision H d req ct) extractCramerShoupTriple + if isElem (d, encodeMsg (d, choice.1.1.1)) && + isElem (d, encodeMsg (d, choice.1.1.2)) then + let m := if bit then encodeMsg (d, choice.1.1.2) else encodeMsg (d, choice.1.1.1) + let ad := encodeAD (d, choice.1.2) + let ct := F.encodeCramerShoupChallenge H d sk v w m ad + runFinderOracle (F.cramerShoupCollisionPhase2Reply hasAD H isElem d sk ct ad) + (fun req => + let q := splitCramerShoupQuery hasAD req + F.isCramerShoupCollision H d q.1 q.2 ct ad) + (fun req => + let q := splitCramerShoupQuery hasAD req + extractCramerShoupTuple q.1 q.2) (adversary.guess n choice.2 ct) none (fun cand => isCollisionFor (F.cramerShoupHashMod H) (fun _ _ _ => true) n d - cand (extractCramerShoupTriple ct)) + cand (extractCramerShoupTuple ct ad)) else PMF.pure false let kTriple (α β γ : ZMod (F.order d)) : Game := (ProbComp.eval (F.sampleCramerShoupSK n d)).bind fun sk => @@ -570,21 +663,24 @@ private theorem succProbability_cramerShoupExactCollisionGame_le let pk := F.encodeCramerShoupPublicKey d u sk (ProbComp.eval ((OracleComp.interpret (ProbOracleComp.handler (fun m => StateT.lift (ProbComp.unifFin m)) - (F.cramerShoupPhase1Oracle H isElem d sk)) + (F.cramerShoupPhase1Oracle hasAD H isElem d sk)) (adversary.choose n pk)).run ())).bind fun choiceOut => let choice := choiceOut.1 (ProbComp.eval - (if isElem (d, encodeMsg (d, choice.1.1)) && isElem (d, encodeMsg (d, choice.1.2)) then - let m := if bit then encodeMsg (d, choice.1.2) else encodeMsg (d, choice.1.1) - let ct := F.encodeCramerShoupChallenge H d sk v w m + (if isElem (d, encodeMsg (d, choice.1.1.1)) && + isElem (d, encodeMsg (d, choice.1.1.2)) then + let m := if bit then encodeMsg (d, choice.1.1.2) else encodeMsg (d, choice.1.1.1) + let ad := encodeAD (d, choice.1.2) + let ct := F.encodeCramerShoupChallenge H d sk v w m ad ((OracleComp.interpret (ProbOracleComp.handler (fun m => StateT.lift (ProbComp.unifFin m)) - (F.cramerShoupCollisionPhase2Oracle H isElem d sk ct)) + (F.cramerShoupCollisionPhase2Oracle hasAD H isElem d sk ct ad)) (adversary.guess n choice.2 ct)).run none) >>= fun out => - let target := extractCramerShoupTriple ct + let target := extractCramerShoupTuple ct ad pure (out.2.getD target, target) else pure ([], []))).bind fun pair => - PMF.pure (isCollisionFor (F.cramerShoupHashMod H) (fun _ _ _ => true) n d pair.1 pair.2) + PMF.pure + (isCollisionFor (F.cramerShoupHashMod H) (fun _ _ _ => true) n d pair.1 pair.2) have hstep1 := F.advantage_bind_sampleCramerShoupTripleExponents_le n d kTriple have hstep2 : Game.advantage @@ -609,14 +705,15 @@ private theorem succProbability_cramerShoupExactCollisionGame_le fun s t s₁ t₁ s₂ t₂ => ?_ dsimp only [kZMod] rw [F.encodeCramerShoupPublicKey_eq_skNatOfZMod d α ((s, t), ((s₁, t₁), (s₂, t₂))), - F.cramerShoupPhase1Oracle_eq_skNatOfZMod H isElem hmem d ((s, t), ((s₁, t₁), (s₂, t₂)))] + F.cramerShoupPhase1Oracle_eq_skNatOfZMod hasAD H isElem hmem d + ((s, t), ((s₁, t₁), (s₂, t₂)))] refine congrArg _ (funext fun choiceOut => ?_) - by_cases h : (isElem (d, encodeMsg (d, choiceOut.1.1.1)) && - isElem (d, encodeMsg (d, choiceOut.1.1.2))) = true + by_cases h : (isElem (d, encodeMsg (d, choiceOut.1.1.1.1)) && + isElem (d, encodeMsg (d, choiceOut.1.1.1.2))) = true · simp only [h, ↓reduceIte, ProbComp.eval_bind, ProbComp.eval_pure, PMF.bind_bind, PMF.pure_bind] rw [F.encodeCramerShoupChallenge_eq_skNatOfZMod H d ((s, t), ((s₁, t₁), (s₂, t₂))), - F.cramerShoupCollisionPhase2Oracle_eq_skNatOfZMod H isElem hmem d + F.cramerShoupCollisionPhase2Oracle_eq_skNatOfZMod hasAD H isElem hmem d ((s, t), ((s₁, t₁), (s₂, t₂)))] unfold runFinderOracle cramerShoupCollisionPhase2Oracle refine congrArg _ (funext fun out => ?_) @@ -627,35 +724,37 @@ private theorem succProbability_cramerShoupExactCollisionGame_le rfl exact ((Game.advantage_triangle _ _ _).trans (add_le_add hstep1 hstep2)).trans_eq (by ring) have htri := Game.advantage_triangle - (F.cramerShoupExactCollisionGame encodeMsg setup H isElem adversary bit n) + (F.cramerShoupExactCollisionGame hasAD encodeMsg encodeAD setup H isElem adversary bit n) (collisionGameFor setup (F.cramerShoupHashMod H) (fun _ _ _ => true) C n) (PMF.pure false) rw [Game.advantage_pure_false, Game.advantage_pure_false, - Game.advantage_comm (F.cramerShoupExactCollisionGame encodeMsg setup H isElem adversary bit n), + Game.advantage_comm + (F.cramerShoupExactCollisionGame hasAD encodeMsg encodeAD setup H isElem adversary bit n), add_comm] at htri exact htri.trans (add_le_add le_rfl hadv) /-- **Game 2 $\to$ Game 3 bound** ([BonehShoup2023], Section 12.5.3, equations (12.24)–(12.25), -printed p. 492, adapted to $\mathcal{E}_{\mathrm{MCS}}$): the distinguishing advantage between -`cramerShoupGame2` and `cramerShoupGame3` is bounded by the collision-finding success probability of -`cramerShoupMsgToCollisionFinder` plus `9 / 2 ^ n` (from the nine approximate `sampleModNat` -exponent draws in `cramerShoupMsgToCollisionFinder`). -/ +printed p. 492, adapted to $\mathcal{E}_{\mathrm{MCS}}$ with associated data): the distinguishing +advantage between `cramerShoupGame2` and `cramerShoupGame3` is bounded by the collision-finding +success probability of `cramerShoupMsgToCollisionFinder` plus `9 / 2 ^ n` (from the nine approximate +`sampleModNat` exponent draws in `cramerShoupMsgToCollisionFinder`). -/ theorem advantage_cramerShoupGame2_game3_le - {Msg : Type} (encodeMsg : F.Desc × Msg → List Bool) (setup : ℕ → PMF F.Desc) + (hasAD : Bool) {Msg AD : Type} (encodeMsg : F.Desc × Msg → List Bool) + (encodeAD : F.Desc × AD → List Bool) (setup : ℕ → PMF F.Desc) (H : F.Desc × List Bool → List Bool) (isElem : F.Desc × List Bool → Bool) (hmem : ∀ d x, isElem (d, x) = true ↔ x ∈ Set.range (F.encode d)) - (adversary : CCA2AdversaryOf Msg) (bit : Bool) (n : ℕ) : + (adversary : ADCCA2AdversaryOf Msg AD) (bit : Bool) (n : ℕ) : Game.advantage - (F.cramerShoupGame2 encodeMsg setup H isElem adversary bit n) - (F.cramerShoupGame3 encodeMsg setup H isElem adversary bit n) ≤ + (F.cramerShoupGame2 hasAD encodeMsg encodeAD setup H isElem adversary bit n) + (F.cramerShoupGame3 hasAD encodeMsg encodeAD setup H isElem adversary bit n) ≤ Game.succProbability (collisionGameFor setup (F.cramerShoupHashMod H) (fun _ _ _ => true) - (F.cramerShoupMsgToCollisionFinder encodeMsg H isElem adversary bit) n) + + (F.cramerShoupMsgToCollisionFinder hasAD encodeMsg encodeAD H isElem adversary bit) n) + 9 / 2 ^ n := (F.advantage_cramerShoupGame2_game3_le_exactCollision - encodeMsg setup H isElem adversary bit n).trans + hasAD encodeMsg encodeAD setup H isElem adversary bit n).trans (F.succProbability_cramerShoupExactCollisionGame_le - encodeMsg setup H isElem hmem adversary bit n) + hasAD encodeMsg encodeAD setup H isElem hmem adversary bit n) /-! ### Concrete and asymptotic 1CCA security bounds ([BonehShoup2023], Theorem 12.9 & Ex. 12.27) -/ @@ -664,39 +763,42 @@ theorem advantage_cramerShoupGame2_game3_le at most the DDH advantage of `cramerShoupMsgToDDHDistinguisher` plus the collision-finding success probability of `cramerShoupMsgToCollisionFinder` plus `(Q₁ + Q₂ + 1) / m + 21 / 2 ^ n`. -/ theorem advantage_cramerShoupMsgCCA2Game_game5_le - {Msg : Type} (encodeMsg : F.Desc × Msg → List Bool) (setup : ℕ → PMF F.Desc) + (hasAD : Bool) {Msg AD : Type} (encodeMsg : F.Desc × Msg → List Bool) + (encodeAD : F.Desc × AD → List Bool) (setup : ℕ → PMF F.Desc) (H : F.Desc × List Bool → List Bool) (isElem : F.Desc × List Bool → Bool) (hmem : ∀ d x, isElem (d, x) = true ↔ x ∈ Set.range (F.encode d)) - (adversary : CCA2AdversaryOf Msg) (bit : Bool) (n Q₁ Q₂ m : ℕ) + (adversary : ADCCA2AdversaryOf Msg AD) (bit : Bool) (n Q₁ Q₂ m : ℕ) (hm0 : 0 < m) (hm : ∀ d ∈ (setup n).support, m ≤ F.order d) (hQ₁ : F.HonestPhase1QueryBound setup adversary n Q₁) - (hQ₂ : F.HonestPhase2QueryBound setup H isElem adversary n Q₂) : + (hQ₂ : F.HonestPhase2QueryBound hasAD encodeAD setup H isElem adversary n Q₂) : Game.advantage - (F.cramerShoupMsgCCA2Game encodeMsg setup H isElem adversary bit n) - (F.cramerShoupGame5 encodeMsg setup H isElem adversary bit n) ≤ + (F.cramerShoupMsgCCA2Game hasAD encodeMsg encodeAD setup H isElem adversary bit n) + (F.cramerShoupGame5 hasAD encodeMsg encodeAD setup H isElem adversary bit n) ≤ Game.advantage (F.ddhRealGame setup - (F.cramerShoupMsgToDDHDistinguisher encodeMsg H isElem adversary bit) n) + (F.cramerShoupMsgToDDHDistinguisher hasAD encodeMsg encodeAD H isElem adversary bit) n) (F.ddhIdealGame setup - (F.cramerShoupMsgToDDHDistinguisher encodeMsg H isElem adversary bit) n) + + (F.cramerShoupMsgToDDHDistinguisher hasAD encodeMsg encodeAD H isElem adversary bit) n) + Game.succProbability (collisionGameFor setup (F.cramerShoupHashMod H) (fun _ _ _ => true) - (F.cramerShoupMsgToCollisionFinder encodeMsg H isElem adversary bit) n) + + (F.cramerShoupMsgToCollisionFinder hasAD encodeMsg encodeAD H isElem adversary bit) n) + ((Q₁ + Q₂ + 1 : ℕ) : ℝ≥0) / (m : ℝ≥0) + 21 / 2 ^ n := by rw [F.cramerShoupMsgCCA2Game_eq_game1] - have h12 := F.advantage_cramerShoupGame1_game2_le encodeMsg setup H isElem hmem adversary bit n - have h23 := F.advantage_cramerShoupGame2_game3_le encodeMsg setup H isElem hmem adversary bit n - have h34 := F.advantage_cramerShoupGame3_game4_le encodeMsg setup H isElem hmem adversary bit - n Q₁ Q₂ m hm0 hm hQ₁ hQ₂ - have h45 := F.advantage_cramerShoupGame4_game5_le encodeMsg setup H isElem hmem adversary bit - n m hm0 hm + have h12 := F.advantage_cramerShoupGame1_game2_le hasAD encodeMsg encodeAD setup H isElem hmem + adversary bit n + have h23 := F.advantage_cramerShoupGame2_game3_le hasAD encodeMsg encodeAD setup H isElem hmem + adversary bit n + have h34 := F.advantage_cramerShoupGame3_game4_le hasAD encodeMsg encodeAD setup H isElem hmem + adversary bit n Q₁ Q₂ m hm0 hm hQ₁ hQ₂ + have h45 := F.advantage_cramerShoupGame4_game5_le hasAD encodeMsg encodeAD setup H isElem hmem + adversary bit n m hm0 hm refine ((Game.advantage_triangle _ - (F.cramerShoupGame2 encodeMsg setup H isElem adversary bit n) _).trans + (F.cramerShoupGame2 hasAD encodeMsg encodeAD setup H isElem adversary bit n) _).trans (add_le_add h12 ((Game.advantage_triangle _ - (F.cramerShoupGame3 encodeMsg setup H isElem adversary bit n) _).trans + (F.cramerShoupGame3 hasAD encodeMsg encodeAD setup H isElem adversary bit n) _).trans (add_le_add h23 ((Game.advantage_triangle _ - (F.cramerShoupGame4 encodeMsg setup H isElem adversary bit n) _).trans + (F.cramerShoupGame4 hasAD encodeMsg encodeAD setup H isElem adversary bit n) _).trans (add_le_add h34 h45)))))).trans_eq ?_ push_cast ring @@ -705,44 +807,49 @@ theorem advantage_cramerShoupMsgCCA2Game_game5_le $\to$ Game 5 bounds for `bit = false` and `bit = true` via `cramerShoupGame5_false_eq_true` (where `42 / 2 ^ n` is the reduction loss from the `sampleModNat` exponent draws inside the reductions). -/ theorem advantage_cramerShoupMsgCCA2Game_le_of_honest_bounds - {Msg : Type} (encodeMsg : F.Desc × Msg → List Bool) (setup : ℕ → PMF F.Desc) + (hasAD : Bool) {Msg AD : Type} (encodeMsg : F.Desc × Msg → List Bool) + (encodeAD : F.Desc × AD → List Bool) (setup : ℕ → PMF F.Desc) (H : F.Desc × List Bool → List Bool) (isElem : F.Desc × List Bool → Bool) (hmem : ∀ d x, isElem (d, x) = true ↔ x ∈ Set.range (F.encode d)) - (adversary : CCA2AdversaryOf Msg) (n Q₁ Q₂ m : ℕ) + (adversary : ADCCA2AdversaryOf Msg AD) (n Q₁ Q₂ m : ℕ) (hm0 : 0 < m) (hm : ∀ d ∈ (setup n).support, m ≤ F.order d) (hQ₁ : F.HonestPhase1QueryBound setup adversary n Q₁) - (hQ₂ : F.HonestPhase2QueryBound setup H isElem adversary n Q₂) : + (hQ₂ : F.HonestPhase2QueryBound hasAD encodeAD setup H isElem adversary n Q₂) : Game.advantage - (F.cramerShoupMsgCCA2Game encodeMsg setup H isElem adversary false n) - (F.cramerShoupMsgCCA2Game encodeMsg setup H isElem adversary true n) ≤ + (F.cramerShoupMsgCCA2Game hasAD encodeMsg encodeAD setup H isElem adversary false n) + (F.cramerShoupMsgCCA2Game hasAD encodeMsg encodeAD setup H isElem adversary true n) ≤ Game.advantage (F.ddhRealGame setup - (F.cramerShoupMsgToDDHDistinguisher encodeMsg H isElem adversary false) n) + (F.cramerShoupMsgToDDHDistinguisher hasAD encodeMsg encodeAD H isElem adversary false) n) (F.ddhIdealGame setup - (F.cramerShoupMsgToDDHDistinguisher encodeMsg H isElem adversary false) n) + + (F.cramerShoupMsgToDDHDistinguisher hasAD encodeMsg encodeAD H isElem adversary false) + n) + Game.advantage (F.ddhRealGame setup - (F.cramerShoupMsgToDDHDistinguisher encodeMsg H isElem adversary true) n) + (F.cramerShoupMsgToDDHDistinguisher hasAD encodeMsg encodeAD H isElem adversary true) n) (F.ddhIdealGame setup - (F.cramerShoupMsgToDDHDistinguisher encodeMsg H isElem adversary true) n) + + (F.cramerShoupMsgToDDHDistinguisher hasAD encodeMsg encodeAD H isElem adversary true) + n) + Game.succProbability (collisionGameFor setup (F.cramerShoupHashMod H) (fun _ _ _ => true) - (F.cramerShoupMsgToCollisionFinder encodeMsg H isElem adversary false) n) + + (F.cramerShoupMsgToCollisionFinder hasAD encodeMsg encodeAD H isElem adversary false) + n) + Game.succProbability (collisionGameFor setup (F.cramerShoupHashMod H) (fun _ _ _ => true) - (F.cramerShoupMsgToCollisionFinder encodeMsg H isElem adversary true) n) + + (F.cramerShoupMsgToCollisionFinder hasAD encodeMsg encodeAD H isElem adversary true) n) + ((2 * Q₁ + 2 * Q₂ + 2 : ℕ) : ℝ≥0) / (m : ℝ≥0) + 42 / 2 ^ n := by - have hfalse := F.advantage_cramerShoupMsgCCA2Game_game5_le encodeMsg setup H isElem hmem - adversary false n Q₁ Q₂ m hm0 hm hQ₁ hQ₂ - have htrue := F.advantage_cramerShoupMsgCCA2Game_game5_le encodeMsg setup H isElem hmem - adversary true n Q₁ Q₂ m hm0 hm hQ₁ hQ₂ + have hfalse := F.advantage_cramerShoupMsgCCA2Game_game5_le hasAD encodeMsg encodeAD setup H + isElem hmem adversary false n Q₁ Q₂ m hm0 hm hQ₁ hQ₂ + have htrue := F.advantage_cramerShoupMsgCCA2Game_game5_le hasAD encodeMsg encodeAD setup H + isElem hmem adversary true n Q₁ Q₂ m hm0 hm hQ₁ hQ₂ have htri := Game.advantage_triangle - (F.cramerShoupMsgCCA2Game encodeMsg setup H isElem adversary false n) - (F.cramerShoupGame5 encodeMsg setup H isElem adversary false n) - (F.cramerShoupMsgCCA2Game encodeMsg setup H isElem adversary true n) + (F.cramerShoupMsgCCA2Game hasAD encodeMsg encodeAD setup H isElem adversary false n) + (F.cramerShoupGame5 hasAD encodeMsg encodeAD setup H isElem adversary false n) + (F.cramerShoupMsgCCA2Game hasAD encodeMsg encodeAD setup H isElem adversary true n) rw [F.cramerShoupGame5_false_eq_true, - Game.advantage_comm (F.cramerShoupGame5 encodeMsg setup H isElem adversary true n)] at htri + Game.advantage_comm + (F.cramerShoupGame5 hasAD encodeMsg encodeAD setup H isElem adversary true n)] at htri refine (htri.trans (add_le_add hfalse htrue)).trans_eq ?_ push_cast ring @@ -753,40 +860,84 @@ DDH advantages of `cramerShoupMsgToDDHDistinguisher` (`false` and `true`), the c success probabilities of `cramerShoupMsgToCollisionFinder` (`false` and `true`), plus `(2 * Q₁ n + 2 * Q₂ n + 2) / m + 42 / 2 ^ n`. -/ theorem advantage_cramerShoupMsgCCA2Game_le - {Msg : Type} (encodeMsg : F.Desc × Msg → List Bool) + (hasAD : Bool) {Msg AD : Type} (encodeMsg : F.Desc × Msg → List Bool) + (encodeAD : F.Desc × AD → List Bool) (setup : ℕ → PMF F.Desc) (H : F.Desc × List Bool → List Bool) (isElem : F.Desc × List Bool → Bool) (hmem : ∀ d x, isElem (d, x) = true ↔ x ∈ Set.range (F.encode d)) - (adversary : CCA2AdversaryOf Msg) (Q₁ Q₂ : ℕ → ℕ) + (adversary : ADCCA2AdversaryOf Msg AD) (Q₁ Q₂ : ℕ → ℕ) (hQ : adversary.IsQueryBounded Q₁ Q₂) (n m : ℕ) (hm0 : 0 < m) (hm : ∀ d ∈ (setup n).support, m ≤ F.order d) : Game.advantage - (F.cramerShoupMsgCCA2Game encodeMsg setup H isElem adversary false n) - (F.cramerShoupMsgCCA2Game encodeMsg setup H isElem adversary true n) ≤ + (F.cramerShoupMsgCCA2Game hasAD encodeMsg encodeAD setup H isElem adversary false n) + (F.cramerShoupMsgCCA2Game hasAD encodeMsg encodeAD setup H isElem adversary true n) ≤ Game.advantage (F.ddhRealGame setup - (F.cramerShoupMsgToDDHDistinguisher encodeMsg H isElem adversary false) n) + (F.cramerShoupMsgToDDHDistinguisher hasAD encodeMsg encodeAD H isElem adversary false) n) (F.ddhIdealGame setup - (F.cramerShoupMsgToDDHDistinguisher encodeMsg H isElem adversary false) n) + + (F.cramerShoupMsgToDDHDistinguisher hasAD encodeMsg encodeAD H isElem adversary false) + n) + Game.advantage (F.ddhRealGame setup - (F.cramerShoupMsgToDDHDistinguisher encodeMsg H isElem adversary true) n) + (F.cramerShoupMsgToDDHDistinguisher hasAD encodeMsg encodeAD H isElem adversary true) n) (F.ddhIdealGame setup - (F.cramerShoupMsgToDDHDistinguisher encodeMsg H isElem adversary true) n) + + (F.cramerShoupMsgToDDHDistinguisher hasAD encodeMsg encodeAD H isElem adversary true) + n) + Game.succProbability (collisionGameFor setup (F.cramerShoupHashMod H) (fun _ _ _ => true) - (F.cramerShoupMsgToCollisionFinder encodeMsg H isElem adversary false) n) + + (F.cramerShoupMsgToCollisionFinder hasAD encodeMsg encodeAD H isElem adversary false) + n) + Game.succProbability (collisionGameFor setup (F.cramerShoupHashMod H) (fun _ _ _ => true) - (F.cramerShoupMsgToCollisionFinder encodeMsg H isElem adversary true) n) + + (F.cramerShoupMsgToCollisionFinder hasAD encodeMsg encodeAD H isElem adversary true) n) + ((2 * Q₁ n + 2 * Q₂ n + 2 : ℕ) : ℝ≥0) / (m : ℝ≥0) + 42 / 2 ^ n := - F.advantage_cramerShoupMsgCCA2Game_le_of_honest_bounds encodeMsg setup H isElem hmem adversary - n (Q₁ n) (Q₂ n) m hm0 hm + F.advantage_cramerShoupMsgCCA2Game_le_of_honest_bounds hasAD encodeMsg encodeAD setup H isElem + hmem adversary n (Q₁ n) (Q₂ n) m hm0 hm (fun _ _ _ _ _ _ => hQ.1 n _) (fun _ _ _ _ _ _ _ _ _ _ _ _ _ => hQ.2 n _ _) +/-- **Theorem 12.9 & Exercise 12.27 concrete ideal-game bound (group-element messages with +associated data `AD`)** ([BonehShoup2023], Section 12.5.3, Theorem 12.9 and Remark 12.3, printed +pp. 488–493, Section 12.7, printed pp. 501–502, and Exercise 12.27, printed pp. 521–522): +concrete bound (12.19) for the ideal exact-uniform PMF 1CCA game `cramerShoupADGroupCCA2Game` for +Multiplicative Cramer–Shoup $\mathcal{E}_{\mathrm{MCS}}$ with associated data in the two-run +single-bit reduction style (with `42 / 2 ^ n` from the approximate `sampleModNat` exponent draws in +the reductions). -/ +theorem advantage_cramerShoupADGroupCCA2Game_le + {AD : Type} (encAD : AD ↪ List Bool) + (setup : ℕ → PMF F.Desc) + (H : F.Desc × List Bool → List Bool) + (isElem : F.Desc × List Bool → Bool) + (hmem : ∀ d x, isElem (d, x) = true ↔ x ∈ Set.range (F.encode d)) + (adversary : WordADCCA2Adversary AD) (Q₁ Q₂ : ℕ → ℕ) + (hQ : adversary.IsQueryBounded Q₁ Q₂) (n m : ℕ) + (hm0 : 0 < m) (hm : ∀ d ∈ (setup n).support, m ≤ F.order d) : + Game.advantage + (F.cramerShoupADGroupCCA2Game encAD setup H isElem adversary false n) + (F.cramerShoupADGroupCCA2Game encAD setup H isElem adversary true n) ≤ + Game.advantage + (F.ddhRealGame setup + (F.cramerShoupADToDDHDistinguisher encAD H isElem adversary false) n) + (F.ddhIdealGame setup + (F.cramerShoupADToDDHDistinguisher encAD H isElem adversary false) n) + + Game.advantage + (F.ddhRealGame setup + (F.cramerShoupADToDDHDistinguisher encAD H isElem adversary true) n) + (F.ddhIdealGame setup + (F.cramerShoupADToDDHDistinguisher encAD H isElem adversary true) n) + + Game.succProbability + (collisionGameFor setup (F.cramerShoupHashMod H) (fun _ _ _ => true) + (F.cramerShoupADToCollisionFinder encAD H isElem adversary false) n) + + Game.succProbability + (collisionGameFor setup (F.cramerShoupHashMod H) (fun _ _ _ => true) + (F.cramerShoupADToCollisionFinder encAD H isElem adversary true) n) + + ((2 * Q₁ n + 2 * Q₂ n + 2 : ℕ) : ℝ≥0) / (m : ℝ≥0) + + 42 / 2 ^ n := + F.advantage_cramerShoupMsgCCA2Game_le true Prod.snd (fun p => encAD p.2) setup H isElem hmem + adversary Q₁ Q₂ hQ n m hm0 hm + /-- **Theorem 12.9 & Exercise 12.27 concrete ideal-game bound (group-element messages)** ([BonehShoup2023], Section 12.5.3, Theorem 12.9 and Remark 12.3, printed pp. 488–493, and Exercise 12.27, printed pp. 521–522): original adaptation of the concrete bound (12.19) to the @@ -822,7 +973,49 @@ theorem advantage_cramerShoupGroupCCA2Game_le (F.cramerShoupToCollisionFinder H isElem adversary true) n) + ((2 * Q₁ n + 2 * Q₂ n + 2 : ℕ) : ℝ≥0) / (m : ℝ≥0) + 42 / 2 ^ n := - F.advantage_cramerShoupMsgCCA2Game_le Prod.snd setup H isElem hmem adversary Q₁ Q₂ hQ n m hm0 hm + F.advantage_cramerShoupMsgCCA2Game_le false Prod.snd (fun _ => []) setup H isElem hmem + adversary.toADUnit Q₁ Q₂ hQ.toADUnit n m hm0 hm + +/-- **Concrete ideal-game 1CCA security bound for single-bit messages with associated data +(`cramerShoupADCCA2Game`)**. -/ +theorem advantage_cramerShoupADCCA2Game_le + {AD : Type} (encAD : AD ↪ List Bool) + (setup : ℕ → PMF F.Desc) + (H : F.Desc × List Bool → List Bool) + (isElem : F.Desc × List Bool → Bool) + (hmem : ∀ d x, isElem (d, x) = true ↔ x ∈ Set.range (F.encode d)) + (adversary : ADCCA2Adversary AD) (Q₁ Q₂ : ℕ → ℕ) + (hQ : adversary.IsQueryBounded Q₁ Q₂) (n m : ℕ) + (hm0 : 0 < m) (hm : ∀ d ∈ (setup n).support, m ≤ F.order d) : + Game.advantage + (F.cramerShoupADCCA2Game encAD setup H isElem adversary false n) + (F.cramerShoupADCCA2Game encAD setup H isElem adversary true n) ≤ + Game.advantage + (F.ddhRealGame setup + (F.cramerShoupMsgToDDHDistinguisher true (fun p => F.encodeBit p.1 p.2) + (fun p => encAD p.2) H isElem adversary false) n) + (F.ddhIdealGame setup + (F.cramerShoupMsgToDDHDistinguisher true (fun p => F.encodeBit p.1 p.2) + (fun p => encAD p.2) H isElem adversary false) n) + + Game.advantage + (F.ddhRealGame setup + (F.cramerShoupMsgToDDHDistinguisher true (fun p => F.encodeBit p.1 p.2) + (fun p => encAD p.2) H isElem adversary true) n) + (F.ddhIdealGame setup + (F.cramerShoupMsgToDDHDistinguisher true (fun p => F.encodeBit p.1 p.2) + (fun p => encAD p.2) H isElem adversary true) n) + + Game.succProbability + (collisionGameFor setup (F.cramerShoupHashMod H) (fun _ _ _ => true) + (F.cramerShoupMsgToCollisionFinder true (fun p => F.encodeBit p.1 p.2) + (fun p => encAD p.2) H isElem adversary false) n) + + Game.succProbability + (collisionGameFor setup (F.cramerShoupHashMod H) (fun _ _ _ => true) + (F.cramerShoupMsgToCollisionFinder true (fun p => F.encodeBit p.1 p.2) + (fun p => encAD p.2) H isElem adversary true) n) + + ((2 * Q₁ n + 2 * Q₂ n + 2 : ℕ) : ℝ≥0) / (m : ℝ≥0) + + 42 / 2 ^ n := + F.advantage_cramerShoupMsgCCA2Game_le true (fun p => F.encodeBit p.1 p.2) (fun p => encAD p.2) + setup H isElem hmem adversary Q₁ Q₂ hQ n m hm0 hm /-- **Concrete ideal-game 1CCA security bound for single-bit messages (`cramerShoupCCA2Game`)**. -/ theorem advantage_cramerShoupCCA2Game_le @@ -838,30 +1031,30 @@ theorem advantage_cramerShoupCCA2Game_le (F.cramerShoupCCA2Game setup H isElem adversary true n) ≤ Game.advantage (F.ddhRealGame setup - (F.cramerShoupMsgToDDHDistinguisher (fun p => F.encodeBit p.1 p.2) - H isElem adversary false) n) + (F.cramerShoupMsgToDDHDistinguisher false (fun p => F.encodeBit p.1 p.2) (fun _ => []) + H isElem adversary.toADUnit false) n) (F.ddhIdealGame setup - (F.cramerShoupMsgToDDHDistinguisher (fun p => F.encodeBit p.1 p.2) - H isElem adversary false) n) + + (F.cramerShoupMsgToDDHDistinguisher false (fun p => F.encodeBit p.1 p.2) (fun _ => []) + H isElem adversary.toADUnit false) n) + Game.advantage (F.ddhRealGame setup - (F.cramerShoupMsgToDDHDistinguisher (fun p => F.encodeBit p.1 p.2) - H isElem adversary true) n) + (F.cramerShoupMsgToDDHDistinguisher false (fun p => F.encodeBit p.1 p.2) (fun _ => []) + H isElem adversary.toADUnit true) n) (F.ddhIdealGame setup - (F.cramerShoupMsgToDDHDistinguisher (fun p => F.encodeBit p.1 p.2) - H isElem adversary true) n) + + (F.cramerShoupMsgToDDHDistinguisher false (fun p => F.encodeBit p.1 p.2) (fun _ => []) + H isElem adversary.toADUnit true) n) + Game.succProbability (collisionGameFor setup (F.cramerShoupHashMod H) (fun _ _ _ => true) - (F.cramerShoupMsgToCollisionFinder (fun p => F.encodeBit p.1 p.2) - H isElem adversary false) n) + + (F.cramerShoupMsgToCollisionFinder false (fun p => F.encodeBit p.1 p.2) (fun _ => []) + H isElem adversary.toADUnit false) n) + Game.succProbability (collisionGameFor setup (F.cramerShoupHashMod H) (fun _ _ _ => true) - (F.cramerShoupMsgToCollisionFinder (fun p => F.encodeBit p.1 p.2) - H isElem adversary true) n) + + (F.cramerShoupMsgToCollisionFinder false (fun p => F.encodeBit p.1 p.2) (fun _ => []) + H isElem adversary.toADUnit true) n) + ((2 * Q₁ n + 2 * Q₂ n + 2 : ℕ) : ℝ≥0) / (m : ℝ≥0) + 42 / 2 ^ n := - F.advantage_cramerShoupMsgCCA2Game_le (fun p => F.encodeBit p.1 p.2) setup H isElem hmem - adversary Q₁ Q₂ hQ n m hm0 hm + F.advantage_cramerShoupMsgCCA2Game_le false (fun p => F.encodeBit p.1 p.2) (fun _ => []) setup H + isElem hmem adversary.toADUnit Q₁ Q₂ hQ.toADUnit n m hm0 hm /-! ### Polynomial length bounds and asymptotic 1CCA security -/ @@ -889,18 +1082,21 @@ private theorem length_encodeCramerShoupPublicKey_le {d : F.Desc} {B : ℕ} simp only [length_encPair] lia -/-- **Asymptotic ideal-game 1CCA security of Multiplicative Cramer–Shoup for a general message -encoding** ([BonehShoup2023], Theorem 12.9 & Exercise 12.27, printed pp. 488–493, 521–522): if DDH -holds for `setup`, `cramerShoupHashMod H` is collision-resistant for `setup`, every supported group -at parameter `n` has order at least `m n > 0` with `1 / m n` negligible, and supported group -descriptions have polynomially bounded bit-length, then the ideal exact-uniform PMF game -`cramerShoupMsgCCA2Game` is indistinguishable between `bit = false` and `bit = true` against all -strict oracle PPT adversaries. -/ +/-- **Asymptotic ideal-game 1CCA security of Multiplicative Cramer–Shoup for general message and +associated-data encodings** ([BonehShoup2023], Theorem 12.9, Section 12.7 & Exercise 12.27, +printed pp. 488–493, 501–502, 521–522): if DDH holds for `setup`, `cramerShoupHashMod H` is +collision-resistant for `setup`, every supported group at parameter `n` has order at least +`m n > 0` with `1 / m n` negligible, and supported group descriptions have polynomially bounded +bit-length, then the ideal exact-uniform PMF game `cramerShoupMsgCCA2Game` is indistinguishable +between `bit = false` and `bit = true` against all strict oracle PPT adversaries. -/ theorem DDH.cramerShoupMsgINDCCA2 {F : PrimeOrderGroupFamily} - {Msg : Type} {encMsg : Msg ↪ List Bool} + (hasAD : Bool) + {Msg AD : Type} {encMsg : Msg ↪ List Bool} {encAD : AD ↪ List Bool} {encodeMsg : F.Desc × Msg → List Bool} + {encodeAD : F.Desc × AD → List Bool} (hencMsg : ComputableInPolyTime encodeMsg (encPair F.encDesc encMsg) encBits) + (hencAD : ComputableInPolyTime encodeAD (encPair F.encDesc encAD) encBits) {setup : ℕ → PMF F.Desc} {H : F.Desc × List Bool → List Bool} {isElem : F.Desc × List Bool → Bool} @@ -915,9 +1111,11 @@ theorem DDH.cramerShoupMsgINDCCA2 (hdesc : ∃ c k : ℕ, ∀ n, ∀ d ∈ (setup n).support, (F.encDesc d).length ≤ c * (n + 1) ^ k) : Game.Indistinguishable - (fun adversary n => F.cramerShoupMsgCCA2Game encodeMsg setup H isElem adversary false n) - (fun adversary n => F.cramerShoupMsgCCA2Game encodeMsg setup H isElem adversary true n) - (CCA2AdversaryOf.IsPPT encMsg) := by + (fun adversary n => + F.cramerShoupMsgCCA2Game hasAD encodeMsg encodeAD setup H isElem adversary false n) + (fun adversary n => + F.cramerShoupMsgCCA2Game hasAD encodeMsg encodeAD setup H isElem adversary true n) + (ADCCA2AdversaryOf.IsPPT encMsg encAD) := by intro adversary hA obtain ⟨cd, kd, hdesc_le⟩ := hdesc obtain ⟨ce, ke, henc_le⟩ := F.exists_length_encode_le @@ -932,27 +1130,32 @@ theorem DDH.cramerShoupMsgINDCCA2 have hddh0 : Negligible fun n => Game.advantage (F.ddhRealGame setup - (F.cramerShoupMsgToDDHDistinguisher encodeMsg H isElem adversary false) n) + (F.cramerShoupMsgToDDHDistinguisher hasAD encodeMsg encodeAD H isElem adversary false) n) (F.ddhIdealGame setup - (F.cramerShoupMsgToDDHDistinguisher encodeMsg H isElem adversary false) n) := - hDDH _ (F.isPPTOn_cramerShoupMsgToDDHDistinguisher hencMsg hH hisElem hA false) + (F.cramerShoupMsgToDDHDistinguisher hasAD encodeMsg encodeAD H isElem adversary false) + n) := + hDDH _ (F.isPPTOn_cramerShoupMsgToDDHDistinguisher hasAD hencMsg hencAD hH hisElem hA false) have hddh1 : Negligible fun n => Game.advantage (F.ddhRealGame setup - (F.cramerShoupMsgToDDHDistinguisher encodeMsg H isElem adversary true) n) + (F.cramerShoupMsgToDDHDistinguisher hasAD encodeMsg encodeAD H isElem adversary true) n) (F.ddhIdealGame setup - (F.cramerShoupMsgToDDHDistinguisher encodeMsg H isElem adversary true) n) := - hDDH _ (F.isPPTOn_cramerShoupMsgToDDHDistinguisher hencMsg hH hisElem hA true) + (F.cramerShoupMsgToDDHDistinguisher hasAD encodeMsg encodeAD H isElem adversary true) + n) := + hDDH _ (F.isPPTOn_cramerShoupMsgToDDHDistinguisher hasAD hencMsg hencAD hH hisElem hA true) have hcr0 : Negligible fun n => Game.succProbability (collisionGameFor setup (F.cramerShoupHashMod H) (fun _ _ _ => true) - (F.cramerShoupMsgToCollisionFinder encodeMsg H isElem adversary false) n) := - hCR.negligible _ (F.isPPTOn_cramerShoupMsgToCollisionFinder hencMsg hH hisElem hA false) + (F.cramerShoupMsgToCollisionFinder hasAD encodeMsg encodeAD H isElem adversary false) + n) := + hCR.negligible _ + (F.isPPTOn_cramerShoupMsgToCollisionFinder hasAD hencMsg hencAD hH hisElem hA false) have hcr1 : Negligible fun n => Game.succProbability (collisionGameFor setup (F.cramerShoupHashMod H) (fun _ _ _ => true) - (F.cramerShoupMsgToCollisionFinder encodeMsg H isElem adversary true) n) := - hCR.negligible _ (F.isPPTOn_cramerShoupMsgToCollisionFinder hencMsg hH hisElem hA true) + (F.cramerShoupMsgToCollisionFinder hasAD encodeMsg encodeAD H isElem adversary true) n) := + hCR.negligible _ + (F.isPPTOn_cramerShoupMsgToCollisionFinder hasAD hencMsg hencAD hH hisElem hA true) have hpoly_Q : PolynomiallyBounded (fun n => 2 * Q₁ n + 2 * Q₂ n + 2) := by dsimp only [Q₁, Q₂, Lst, Lct, Lpk, B, Ld] fun_prop @@ -968,8 +1171,8 @@ theorem DDH.cramerShoupMsgINDCCA2 negligible_const_div_two_pow 42 refine negligible_of_le (((((hddh0.add hddh1).add hcr0).add hcr1).add hq_neg).add h42_neg) (fun n => ?_) - refine F.advantage_cramerShoupMsgCCA2Game_le_of_honest_bounds encodeMsg setup H isElem hmem - adversary n (Q₁ n) (Q₂ n) (m n) (hm0 n) (hm n) ?_ ?_ + refine F.advantage_cramerShoupMsgCCA2Game_le_of_honest_bounds hasAD encodeMsg encodeAD setup H + isElem hmem adversary n (Q₁ n) (Q₂ n) (m n) (hm0 n) (hm n) ?_ ?_ · intro d hd α δ δ₁ δ₂ have hd_le : (F.encDesc d).length ≤ Ld n := hdesc_le n d hd have hB_le : ∀ x : F.Carrier d, (F.encode d x).length ≤ B n := @@ -996,12 +1199,40 @@ theorem DDH.cramerShoupMsgINDCCA2 F.encode d (γ • F.generator d), F.encode d (projHash F d (δ - α * τ) τ (β • F.generator d) (γ • F.generator d) + mElem), F.encode d ((β * (δ₁ + F.cramerShoupHashMod H d - (encBits3 (F.encode d (β • F.generator d), F.encode d (γ • F.generator d), + (encBits4 (F.encode d (β • F.generator d), + F.encode d (γ • F.generator d), F.encode d (projHash F d (δ - α * τ) τ (β • F.generator d) (γ • F.generator d) + - mElem))) * δ₂) + (γ - α * β) * ξ) • F.generator d))).length ≤ Lct n := + mElem), + encodeAD (d, choiceOut.1.1.2))) * δ₂) + (γ - α * β) * ξ) • + F.generator d))).length ≤ Lct n := F.length_encBits4_encode_le hB_le _ _ _ _ exact (hQ₂_raw n choiceOut.1.2 _).mono (by dsimp only [Q₂]; gcongr) +/-- **Theorem 12.9 & Exercise 12.27 (asymptotic ideal-game 1CCA security for group-element +messages with associated data `AD`)**: Multiplicative Cramer–Shoup satisfies the ideal +exact-uniform PMF 1CCA notion `cramerShoupADGroupINDCCA2` under DDH, collision resistance of +`cramerShoupHashMod H`, a positive group-order lower bound `m n > 0` with `1 / m n` negligible, +and polynomially bounded group description bit-length. -/ +theorem DDH.cramerShoupADGroupINDCCA2 + {F : PrimeOrderGroupFamily} + {AD : Type} (encAD : AD ↪ List Bool) + {setup : ℕ → PMF F.Desc} + {H : F.Desc × List Bool → List Bool} + {isElem : F.Desc × List Bool → Bool} + (hH : ComputableInPolyTime H (encPair F.encDesc encBits) encBits) + (hisElem : ComputableInPolyTime isElem (encPair F.encDesc encBits) encBool) + (hmem : ∀ d x, isElem (d, x) = true ↔ x ∈ Set.range (F.encode d)) + (hDDH : F.DDH setup) + (hCR : CollisionResistantFor F.encDesc setup (F.cramerShoupHashMod H)) + {m : ℕ → ℕ} (hm0 : ∀ n, 0 < m n) + (hm : ∀ n, ∀ d ∈ (setup n).support, m n ≤ F.order d) + (hinv : Negligible fun n => (1 / m n : ℝ≥0)) + (hdesc : ∃ c k : ℕ, ∀ n, ∀ d ∈ (setup n).support, + (F.encDesc d).length ≤ c * (n + 1) ^ k) : + F.cramerShoupADGroupINDCCA2 encAD setup H isElem := + hDDH.cramerShoupMsgINDCCA2 true (by fun_prop) (by fun_prop) hH hisElem hmem hCR hm0 hm hinv + hdesc + /-- **Theorem 12.9 & Exercise 12.27 (asymptotic ideal-game 1CCA security for group-element messages)**: Multiplicative Cramer–Shoup satisfies the ideal exact-uniform PMF 1CCA notion `cramerShoupGroupINDCCA2` under DDH, collision resistance of `cramerShoupHashMod H`, a positive @@ -1022,8 +1253,35 @@ theorem DDH.cramerShoupGroupINDCCA2 (hinv : Negligible fun n => (1 / m n : ℝ≥0)) (hdesc : ∃ c k : ℕ, ∀ n, ∀ d ∈ (setup n).support, (F.encDesc d).length ≤ c * (n + 1) ^ k) : - F.cramerShoupGroupINDCCA2 setup H isElem := - hDDH.cramerShoupMsgINDCCA2 (by fun_prop) hH hisElem hmem hCR hm0 hm hinv hdesc + F.cramerShoupGroupINDCCA2 setup H isElem := by + intro adversary hA + exact hDDH.cramerShoupMsgINDCCA2 false (by fun_prop) (by fun_prop) hH hisElem hmem hCR hm0 hm + hinv hdesc adversary.toADUnit hA.toADUnit + +/-- **Asymptotic ideal-game 1CCA security for single-bit messages with associated data +(`cramerShoupADINDCCA2`)**: Multiplicative Cramer–Shoup with bit embedding `encodeBit` satisfies +the ideal exact-uniform PMF 1CCA notion `cramerShoupADINDCCA2` under DDH, collision resistance of +`cramerShoupHashMod H`, a positive group-order lower bound `m n > 0` with `1 / m n` negligible, and +polynomially bounded group description bit-length. -/ +theorem DDH.cramerShoupADINDCCA2 + {F : PrimeOrderGroupFamily} + {AD : Type} (encAD : AD ↪ List Bool) + {setup : ℕ → PMF F.Desc} + {H : F.Desc × List Bool → List Bool} + {isElem : F.Desc × List Bool → Bool} + (hH : ComputableInPolyTime H (encPair F.encDesc encBits) encBits) + (hisElem : ComputableInPolyTime isElem (encPair F.encDesc encBits) encBool) + (hmem : ∀ d x, isElem (d, x) = true ↔ x ∈ Set.range (F.encode d)) + (hDDH : F.DDH setup) + (hCR : CollisionResistantFor F.encDesc setup (F.cramerShoupHashMod H)) + {m : ℕ → ℕ} (hm0 : ∀ n, 0 < m n) + (hm : ∀ n, ∀ d ∈ (setup n).support, m n ≤ F.order d) + (hinv : Negligible fun n => (1 / m n : ℝ≥0)) + (hdesc : ∃ c k : ℕ, ∀ n, ∀ d ∈ (setup n).support, + (F.encDesc d).length ≤ c * (n + 1) ^ k) : + F.cramerShoupADINDCCA2 encAD setup H isElem := + hDDH.cramerShoupMsgINDCCA2 true F.computableInPolyTime_encodeBit (by fun_prop) hH hisElem hmem + hCR hm0 hm hinv hdesc /-- **Asymptotic ideal-game 1CCA security for single-bit messages (`cramerShoupINDCCA2`)**: Multiplicative Cramer–Shoup with bit embedding `encodeBit` satisfies the ideal exact-uniform PMF @@ -1045,11 +1303,14 @@ theorem DDH.cramerShoupINDCCA2 (hinv : Negligible fun n => (1 / m n : ℝ≥0)) (hdesc : ∃ c k : ℕ, ∀ n, ∀ d ∈ (setup n).support, (F.encDesc d).length ≤ c * (n + 1) ^ k) : - F.cramerShoupINDCCA2 setup H isElem := - hDDH.cramerShoupMsgINDCCA2 F.computableInPolyTime_encodeBit hH hisElem hmem hCR hm0 hm hinv hdesc + F.cramerShoupINDCCA2 setup H isElem := by + intro adversary hA + exact hDDH.cramerShoupMsgINDCCA2 false F.computableInPolyTime_encodeBit (by fun_prop) hH hisElem + hmem hCR hm0 hm hinv hdesc adversary.toADUnit hA.toADUnit -/-! ### `Encryption.cca2GameOn` and `Encryption.INDCCA2On` instantiation -/ +/-! ### `Encryption.cca2ADGameOn`, `Encryption.INDCCA2ADOn`, `Encryption.cca2GameOn`, and +`Encryption.INDCCA2On` instantiations -/ private theorem advantage_four_le_eight (g₁ g₂ g₃ g₄ : Game) {n : ℕ} (h₁ : Game.advantage g₁ g₂ ≤ 1 / 2 ^ n) @@ -1065,46 +1326,279 @@ private theorem advantage_four_le_eight (g₁ g₂ g₃ g₄ : Game) {n : ℕ} add_le_add h₁ (add_le_add h₂ h₃) _ = 8 / 2 ^ n := by ring +private theorem eval_run_interpret_map_bind + {α β γ State : Type} + (oracle : Unit × List Bool → StateT State ProbComp (List Bool)) + (f : α → β) (oa : WordOracleComp Unit α) (s : State) + (k : β × State → PMF γ) : + (ProbComp.eval ((OracleComp.interpret + (ProbOracleComp.handler (fun m => StateT.lift (ProbComp.unifFin m)) oracle) + (f <$> oa)).run s)).bind k = + (ProbComp.eval ((OracleComp.interpret + (ProbOracleComp.handler (fun m => StateT.lift (ProbComp.unifFin m)) oracle) + oa).run s)).bind (fun p => k (f p.1, p.2)) := by + rw [OracleComp.interpret_map, StateT.run_map] + simp only [← bind_pure_comp, ProbComp.eval_bind, ProbComp.eval_pure, + PMF.bind_bind, PMF.pure_bind] + private noncomputable def cramerShoupCCA2InnerB + (hasAD : Bool) {AD : Type} (encodeAD : F.Desc × AD → List Bool) (H : F.Desc × List Bool → List Bool) (isElem : F.Desc × List Bool → Bool) - (d : F.Desc) (adversary : WordCCA2Adversary) (bit : Bool) (n : ℕ) + (d : F.Desc) (adversary : WordADCCA2Adversary AD) (bit : Bool) (n : ℕ) (α σ τ σ₁ τ₁ σ₂ τ₂ : ZMod (F.order d)) - (choiceOut : ((List Bool × List Bool) × List Bool) × Unit) + (choiceOut : (((List Bool × List Bool) × AD) × List Bool) × Unit) (β : ZMod (F.order d)) : Game := let sk := F.skNatOfZMod d σ τ σ₁ τ₁ σ₂ τ₂ let pkVal := F.cramerShoupPublicKey d α σ τ σ₁ τ₁ σ₂ τ₂ - let m := if bit then choiceOut.1.1.2 else choiceOut.1.1.1 + let m := if bit then choiceOut.1.1.1.2 else choiceOut.1.1.1.1 + let adBits := encodeAD (d, choiceOut.1.1.2) let ct := F.encodeCramerShoupEncrypt H d (F.encode d pkVal.1, F.encode d pkVal.2.1, - F.encode d pkVal.2.2.1, F.encode d pkVal.2.2.2) m β.val + F.encode d pkVal.2.2.1, F.encode d pkVal.2.2.2) m adBits β.val (ProbComp.eval ((OracleComp.interpret (ProbOracleComp.handler (fun m => StateT.lift (ProbComp.unifFin m)) - (F.cramerShoupPhase2Oracle H isElem d sk ct)) + (F.cramerShoupPhase2Oracle hasAD H isElem d sk ct adBits)) (adversary.guess n choiceOut.1.2 ct)).run ())).bind (fun guessOut => PMF.pure guessOut.1) private noncomputable def cramerShoupCCA2InnerSK + (hasAD : Bool) {AD : Type} (encodeAD : F.Desc × AD → List Bool) (H : F.Desc × List Bool → List Bool) (isElem : F.Desc × List Bool → Bool) - (d : F.Desc) (adversary : WordCCA2Adversary) (bit : Bool) (n : ℕ) + (d : F.Desc) (adversary : WordADCCA2Adversary AD) (bit : Bool) (n : ℕ) (α σ τ σ₁ τ₁ σ₂ τ₂ : ZMod (F.order d)) (useExactB : Bool) : Game := let sk := F.skNatOfZMod d σ τ σ₁ τ₁ σ₂ τ₂ let pk := F.encodeCramerShoupPublicKey d (F.encode d (α • F.generator d)) sk (ProbComp.eval ((OracleComp.interpret (ProbOracleComp.handler (fun m => StateT.lift (ProbComp.unifFin m)) - (F.cramerShoupPhase1Oracle H isElem d sk)) + (F.cramerShoupPhase1Oracle hasAD H isElem d sk)) (adversary.choose n pk)).run ())).bind fun choiceOut => - if isElem (d, choiceOut.1.1.1) && isElem (d, choiceOut.1.1.2) then + if isElem (d, choiceOut.1.1.1.1) && isElem (d, choiceOut.1.1.1.2) then if useExactB then (PMF.uniformOfFintype (ZMod (F.order d))).bind fun β => - F.cramerShoupCCA2InnerB H isElem d adversary bit n α σ τ σ₁ τ₁ σ₂ τ₂ choiceOut β + F.cramerShoupCCA2InnerB hasAD encodeAD H isElem d adversary bit n + α σ τ σ₁ τ₁ σ₂ τ₂ choiceOut β else (ProbComp.eval (F.sampleExp n d)).bind fun b => - F.cramerShoupCCA2InnerB H isElem d adversary bit n α σ τ σ₁ τ₁ σ₂ τ₂ choiceOut - (b : ZMod (F.order d)) + F.cramerShoupCCA2InnerB hasAD encodeAD H isElem d adversary bit n + α σ τ σ₁ τ₁ σ₂ τ₂ choiceOut (b : ZMod (F.order d)) else PMF.pure false +private theorem advantage_cramerShoupCCA2InnerSK_le + (hasAD : Bool) {AD : Type} (encodeAD : F.Desc × AD → List Bool) + (H : F.Desc × List Bool → List Bool) + (isElem : F.Desc × List Bool → Bool) + (d : F.Desc) (adversary : WordADCCA2Adversary AD) (bit : Bool) (n : ℕ) : + Game.advantage + ((ProbComp.eval (F.sampleExp n d)).bind fun a => + (ProbComp.eval (F.sampleCramerShoupSK n d)).bind fun sk => + F.cramerShoupCCA2InnerSK hasAD encodeAD H isElem d adversary bit n + (a : ZMod (F.order d)) + (sk.1.1 : ZMod (F.order d)) (sk.1.2 : ZMod (F.order d)) + (sk.2.1.1 : ZMod (F.order d)) (sk.2.1.2 : ZMod (F.order d)) + (sk.2.2.1 : ZMod (F.order d)) (sk.2.2.2 : ZMod (F.order d)) false) + ((PMF.uniformOfFintype (ZMod (F.order d))).bind fun α => + (PMF.uniformOfFintype (ZMod (F.order d))).bind fun σ => + (PMF.uniformOfFintype (ZMod (F.order d))).bind fun τ => + (PMF.uniformOfFintype (ZMod (F.order d))).bind fun σ₁ => + (PMF.uniformOfFintype (ZMod (F.order d))).bind fun τ₁ => + (PMF.uniformOfFintype (ZMod (F.order d))).bind fun σ₂ => + (PMF.uniformOfFintype (ZMod (F.order d))).bind fun τ₂ => + F.cramerShoupCCA2InnerSK hasAD encodeAD H isElem d adversary bit n + α σ τ σ₁ τ₁ σ₂ τ₂ true) ≤ + 8 / 2 ^ n := by + refine advantage_four_le_eight _ + ((PMF.uniformOfFintype (ZMod (F.order d))).bind fun α => + (ProbComp.eval (F.sampleCramerShoupSK n d)).bind fun sk => + F.cramerShoupCCA2InnerSK hasAD encodeAD H isElem d adversary bit n α + (sk.1.1 : ZMod (F.order d)) (sk.1.2 : ZMod (F.order d)) + (sk.2.1.1 : ZMod (F.order d)) (sk.2.1.2 : ZMod (F.order d)) + (sk.2.2.1 : ZMod (F.order d)) (sk.2.2.2 : ZMod (F.order d)) false) + ((PMF.uniformOfFintype (ZMod (F.order d))).bind fun α => + (PMF.uniformOfFintype (ZMod (F.order d))).bind fun σ => + (PMF.uniformOfFintype (ZMod (F.order d))).bind fun τ => + (PMF.uniformOfFintype (ZMod (F.order d))).bind fun σ₁ => + (PMF.uniformOfFintype (ZMod (F.order d))).bind fun τ₁ => + (PMF.uniformOfFintype (ZMod (F.order d))).bind fun σ₂ => + (PMF.uniformOfFintype (ZMod (F.order d))).bind fun τ₂ => + F.cramerShoupCCA2InnerSK hasAD encodeAD H isElem d adversary bit n + α σ τ σ₁ τ₁ σ₂ τ₂ false) _ + (F.advantage_bind_sampleExp_le n d (fun α => + (ProbComp.eval (F.sampleCramerShoupSK n d)).bind fun sk => + F.cramerShoupCCA2InnerSK hasAD encodeAD H isElem d adversary bit n α + (sk.1.1 : ZMod (F.order d)) (sk.1.2 : ZMod (F.order d)) + (sk.2.1.1 : ZMod (F.order d)) (sk.2.1.2 : ZMod (F.order d)) + (sk.2.2.1 : ZMod (F.order d)) (sk.2.2.2 : ZMod (F.order d)) false)) + (advantage_bind_le_nnreal _ _ _ fun α => + F.advantage_bind_sampleCramerShoupSK_le n d _ (fun σ τ σ₁ τ₁ σ₂ τ₂ => + F.cramerShoupCCA2InnerSK hasAD encodeAD H isElem d adversary bit n + α σ τ σ₁ τ₁ σ₂ τ₂ false) (fun _ _ _ _ _ _ => rfl)) ?_ + refine advantage_bind_le_nnreal _ _ _ fun α => ?_ + refine advantage_bind_le_nnreal _ _ _ fun σ => ?_ + refine advantage_bind_le_nnreal _ _ _ fun τ => ?_ + refine advantage_bind_le_nnreal _ _ _ fun σ₁ => ?_ + refine advantage_bind_le_nnreal _ _ _ fun τ₁ => ?_ + refine advantage_bind_le_nnreal _ _ _ fun σ₂ => ?_ + refine advantage_bind_le_nnreal _ _ _ fun τ₂ => ?_ + dsimp only [cramerShoupCCA2InnerSK] + refine advantage_bind_le_nnreal _ _ _ fun choiceOut => ?_ + by_cases h : (isElem (d, choiceOut.1.1.1.1) && isElem (d, choiceOut.1.1.1.2)) = true + · simp only [h, ↓reduceIte, Bool.false_eq_true] + exact F.advantage_bind_sampleExp_le n d + (F.cramerShoupCCA2InnerB hasAD encodeAD H isElem d adversary bit n + α σ τ σ₁ τ₁ σ₂ τ₂ choiceOut) + · simp only [h, Bool.false_eq_true, ↓reduceIte, Game.advantage_self, zero_le] + +/-- The native `Encryption.cca2ADGameOn` experiment for `(cramerShoupValid, cramerShoupKeyGenProb, +cramerShoupADEncryptProb, cramerShoupADDecryptOpt)` is within statistical distance `8 / 2 ^ n` of +the ideal exact-uniform PMF game `cramerShoupADGroupCCA2Game encAD (fun n => PMF.pure (d n))` +(accounting for the eight `sampleModNat` exponent draws in `cramerShoupKeyGenProb` and +`cramerShoupADEncryptProb`). -/ +theorem advantage_cca2ADGameOn_cramerShoupADGroupCCA2Game_le + {AD : Type} (encAD : AD ↪ List Bool) + (H : F.Desc × List Bool → List Bool) + (isElem : F.Desc × List Bool → Bool) + (hmem : ∀ d x, isElem (d, x) = true ↔ x ∈ Set.range (F.encode d)) + (d : ℕ → F.Desc) (adversary : WordADCCA2Adversary AD) (bit : Bool) (n : ℕ) : + Game.advantage + (ProbComp.eval + (cca2ADGameOn (F.cramerShoupValid isElem d) encAD encBits (F.cramerShoupKeyGenProb d) + (F.cramerShoupADEncryptProb encAD H d) (F.cramerShoupADDecryptOpt H isElem d) + adversary bit n)) + (F.cramerShoupADGroupCCA2Game encAD (fun n => PMF.pure (d n)) H isElem + adversary bit n) ≤ + 8 / 2 ^ n := by + have hgame0 : + ProbComp.eval + (cca2ADGameOn (F.cramerShoupValid isElem d) encAD encBits (F.cramerShoupKeyGenProb d) + (F.cramerShoupADEncryptProb encAD H d) (F.cramerShoupADDecryptOpt H isElem d) + adversary bit n) = + (ProbComp.eval (F.sampleExp n (d n))).bind fun a => + (ProbComp.eval (F.sampleCramerShoupSK n (d n))).bind fun sk => + F.cramerShoupCCA2InnerSK true (fun p => encAD p.2) H isElem (d n) adversary bit n + (a : ZMod (F.order (d n))) + (sk.1.1 : ZMod (F.order (d n))) (sk.1.2 : ZMod (F.order (d n))) + (sk.2.1.1 : ZMod (F.order (d n))) (sk.2.1.2 : ZMod (F.order (d n))) + (sk.2.2.1 : ZMod (F.order (d n))) (sk.2.2.2 : ZMod (F.order (d n))) false := by + simp only [cca2ADGameOn, cramerShoupKeyGenProb, cramerShoupADEncryptProb, cramerShoupValid, + ProbComp.eval_bind, ProbComp.eval_pure, PMF.bind_bind, PMF.pure_bind, F.smul_encode, + F.cca2ADPhase1Oracle_cramerShoupADDecryptOpt] + refine congrArg _ (funext fun a => congrArg _ (funext fun sk => ?_)) + dsimp only [cramerShoupCCA2InnerSK] + rw [F.encodeCramerShoupPublicKey_eq_skNatOfZMod (d n) (a : ZMod (F.order (d n))) sk, + F.cramerShoupPhase1Oracle_eq_skNatOfZMod true H isElem hmem (d n) sk] + refine congrArg _ (funext fun choiceOut => ?_) + by_cases h : (isElem (d n, choiceOut.1.1.1.1) && isElem (d n, choiceOut.1.1.1.2)) = true + · simp only [h, ↓reduceIte, Bool.false_eq_true, ProbComp.eval_bind, ProbComp.eval_pure, + PMF.bind_bind, PMF.pure_bind, F.cca2ADPhase2Oracle_cramerShoupADDecryptOpt, + F.decodeBits4_dropFrame_encodeCramerShoupPublicKey, cramerShoupCCA2InnerB] + refine congrArg _ (funext fun b => ?_) + have hct := + F.encodeCramerShoupEncrypt_skNatOfZMod H (d n) (a : ZMod (F.order (d n))) + (sk.1.1 : ZMod (F.order (d n))) (sk.1.2 : ZMod (F.order (d n))) + (sk.2.1.1 : ZMod (F.order (d n))) (sk.2.1.2 : ZMod (F.order (d n))) + (sk.2.2.1 : ZMod (F.order (d n))) (sk.2.2.2 : ZMod (F.order (d n))) + (if bit then choiceOut.1.1.1.2 else choiceOut.1.1.1.1) (encAD choiceOut.1.1.2) b + rw [hct, F.cramerShoupPhase2Oracle_eq_skNatOfZMod true H isElem hmem (d n) sk] + · simp only [h, Bool.false_eq_true, ↓reduceIte, ProbComp.eval_pure] + have hgame1 : + F.cramerShoupADGroupCCA2Game encAD (fun n => PMF.pure (d n)) H isElem + adversary bit n = + (PMF.uniformOfFintype (ZMod (F.order (d n)))).bind fun α => + (PMF.uniformOfFintype (ZMod (F.order (d n)))).bind fun σ => + (PMF.uniformOfFintype (ZMod (F.order (d n)))).bind fun τ => + (PMF.uniformOfFintype (ZMod (F.order (d n)))).bind fun σ₁ => + (PMF.uniformOfFintype (ZMod (F.order (d n)))).bind fun τ₁ => + (PMF.uniformOfFintype (ZMod (F.order (d n)))).bind fun σ₂ => + (PMF.uniformOfFintype (ZMod (F.order (d n)))).bind fun τ₂ => + F.cramerShoupCCA2InnerSK true (fun p => encAD p.2) H isElem (d n) adversary + bit n α σ τ σ₁ τ₁ σ₂ τ₂ true := by + simp only [cramerShoupADGroupCCA2Game, cramerShoupMsgCCA2Game, PMF.monad_bind_eq_bind, + PMF.monad_pure_eq_pure, PMF.pure_bind] + refine congrArg _ (funext fun α => congrArg _ (funext fun σ => + congrArg _ (funext fun τ => congrArg _ (funext fun σ₁ => + congrArg _ (funext fun τ₁ => congrArg _ (funext fun σ₂ => + congrArg _ (funext fun τ₂ => ?_))))))) + simp only [cramerShoupCCA2InnerSK, cramerShoupCCA2InnerB, ↓reduceIte, + encodeCramerShoupPublicKey_encode, skNatOfZMod, ZMod.natCast_zmod_val] + rw [hgame0, hgame1] + exact F.advantage_cramerShoupCCA2InnerSK_le true (fun p => encAD p.2) H isElem (d n) + adversary bit n + +/-- **Theorem 12.9 & Exercise 12.27 via `Encryption.INDCCA2ADOn`**: for any associated-data +encoding `encAD : AD ↪ List Bool` and polynomial-time group description family `d : ℕ → F.Desc`, +the probabilistic polynomial-time Multiplicative Cramer–Shoup encryption scheme with associated +data `(cramerShoupKeyGenProb d, cramerShoupADEncryptProb encAD H d, cramerShoupADDecryptOpt H +isElem d)` satisfies the native `Encryption.INDCCA2ADOn` notion with respect to the +group-membership admissibility predicate `cramerShoupValid isElem d` under DDH, collision +resistance of `cramerShoupHashMod H`, and a positive group-order lower bound `m n > 0` with +`1 / m n` negligible. -/ +theorem DDH.indcca2ADOn_cramerShoup + {F : PrimeOrderGroupFamily} + {AD : Type} (encAD : AD ↪ List Bool) + {H : F.Desc × List Bool → List Bool} + {isElem : F.Desc × List Bool → Bool} + {d : ℕ → F.Desc} + (hH : ComputableInPolyTime H (encPair F.encDesc encBits) encBits) + (hisElem : ComputableInPolyTime isElem (encPair F.encDesc encBits) encBool) + (hd : ComputableInPolyTime d encUnary F.encDesc) + (hmem : ∀ d x, isElem (d, x) = true ↔ x ∈ Set.range (F.encode d)) + (hDDH : F.DDH (fun n => PMF.pure (d n))) + (hCR : CollisionResistantFor F.encDesc (fun n => PMF.pure (d n)) (F.cramerShoupHashMod H)) + {m : ℕ → ℕ} (hm0 : ∀ n, 0 < m n) + (hm : ∀ n, m n ≤ F.order (d n)) + (hinv : Negligible fun n => (1 / m n : ℝ≥0)) : + INDCCA2ADOn (F.cramerShoupValid isElem d) encBits encAD encBits + (F.cramerShoupKeyGenProb d) (F.cramerShoupADEncryptProb encAD H d) + (F.cramerShoupADDecryptOpt H isElem d) := by + have hdesc : ∃ c k : ℕ, ∀ n, ∀ d' ∈ (PMF.pure (d n)).support, + (F.encDesc d').length ≤ c * (n + 1) ^ k := by + obtain ⟨c, k, hck⟩ := hd.tidy.polyBounded_length.exists_le + refine ⟨c, k, fun n d' hd' => ?_⟩ + rw [PMF.support_pure, Set.mem_singleton_iff] at hd' + subst hd' + simpa only [encUnary_apply, List.length_replicate] using hck n + have hgroup : + F.cramerShoupADGroupINDCCA2 encAD (fun n => PMF.pure (d n)) H isElem := + hDDH.cramerShoupADGroupINDCCA2 encAD hH hisElem hmem hCR hm0 + (fun n d' hd' => by + rw [PMF.support_pure, Set.mem_singleton_iff] at hd' + subst hd' + exact hm n) + hinv hdesc + intro adversary hA + have hmid := hgroup adversary hA + have h8 : Negligible fun n => (8 : ℝ≥0) / 2 ^ n := negligible_const_div_two_pow 8 + refine negligible_of_le ((h8.add hmid).add h8) fun n => ?_ + have h0 := F.advantage_cca2ADGameOn_cramerShoupADGroupCCA2Game_le encAD H isElem hmem d + adversary false n + have h1 := F.advantage_cca2ADGameOn_cramerShoupADGroupCCA2Game_le encAD H isElem hmem d + adversary true n + rw [Game.advantage_comm] at h1 + have htri1 := Game.advantage_triangle + (ProbComp.eval + (cca2ADGameOn (F.cramerShoupValid isElem d) encAD encBits (F.cramerShoupKeyGenProb d) + (F.cramerShoupADEncryptProb encAD H d) (F.cramerShoupADDecryptOpt H isElem d) + adversary false n)) + (F.cramerShoupADGroupCCA2Game encAD (fun n => PMF.pure (d n)) H isElem + adversary false n) + (ProbComp.eval + (cca2ADGameOn (F.cramerShoupValid isElem d) encAD encBits (F.cramerShoupKeyGenProb d) + (F.cramerShoupADEncryptProb encAD H d) (F.cramerShoupADDecryptOpt H isElem d) + adversary true n)) + have htri2 := Game.advantage_triangle + (F.cramerShoupADGroupCCA2Game encAD (fun n => PMF.pure (d n)) H isElem + adversary false n) + (F.cramerShoupADGroupCCA2Game encAD (fun n => PMF.pure (d n)) H isElem + adversary true n) + (ProbComp.eval + (cca2ADGameOn (F.cramerShoupValid isElem d) encAD encBits (F.cramerShoupKeyGenProb d) + (F.cramerShoupADEncryptProb encAD H d) (F.cramerShoupADDecryptOpt H isElem d) + adversary true n)) + exact (htri1.trans (add_le_add le_rfl htri2)).trans + (by rw [add_assoc]; exact add_le_add h0 (add_le_add le_rfl h1)) + /-- The native `Encryption.cca2GameOn` experiment for `(cramerShoupValid, cramerShoupKeyGenProb, cramerShoupEncryptProb, cramerShoupDecryptOpt)` is within statistical distance `8 / 2 ^ n` of the ideal exact-uniform PMF game `cramerShoupGroupCCA2Game (fun n => PMF.pure (d n))` (accounting for @@ -1126,7 +1620,7 @@ theorem advantage_cca2GameOn_cramerShoupGroupCCA2Game_le (F.cramerShoupEncryptProb H d) (F.cramerShoupDecryptOpt H isElem d) adversary bit n) = (ProbComp.eval (F.sampleExp n (d n))).bind fun a => (ProbComp.eval (F.sampleCramerShoupSK n (d n))).bind fun sk => - F.cramerShoupCCA2InnerSK H isElem (d n) adversary bit n + F.cramerShoupCCA2InnerSK false (fun _ => []) H isElem (d n) adversary.toADUnit bit n (a : ZMod (F.order (d n))) (sk.1.1 : ZMod (F.order (d n))) (sk.1.2 : ZMod (F.order (d n))) (sk.2.1.1 : ZMod (F.order (d n))) (sk.2.1.2 : ZMod (F.order (d n))) @@ -1135,9 +1629,10 @@ theorem advantage_cca2GameOn_cramerShoupGroupCCA2Game_le ProbComp.eval_bind, ProbComp.eval_pure, PMF.bind_bind, PMF.pure_bind, F.smul_encode, F.cca2Phase1Oracle_cramerShoupDecryptOpt] refine congrArg _ (funext fun a => congrArg _ (funext fun sk => ?_)) - dsimp only [cramerShoupCCA2InnerSK] - rw [F.encodeCramerShoupPublicKey_eq_skNatOfZMod (d n) (a : ZMod (F.order (d n))) sk, - F.cramerShoupPhase1Oracle_eq_skNatOfZMod H isElem hmem (d n) sk] + dsimp only [cramerShoupCCA2InnerSK, CCA2AdversaryOf.toADUnit] + rw [eval_run_interpret_map_bind, + F.encodeCramerShoupPublicKey_eq_skNatOfZMod (d n) (a : ZMod (F.order (d n))) sk, + F.cramerShoupPhase1Oracle_eq_skNatOfZMod false H isElem hmem (d n) sk] refine congrArg _ (funext fun choiceOut => ?_) by_cases h : (isElem (d n, choiceOut.1.1.1) && isElem (d n, choiceOut.1.1.2)) = true · simp only [h, ↓reduceIte, Bool.false_eq_true, ProbComp.eval_bind, ProbComp.eval_pure, @@ -1149,8 +1644,8 @@ theorem advantage_cca2GameOn_cramerShoupGroupCCA2Game_le (sk.1.1 : ZMod (F.order (d n))) (sk.1.2 : ZMod (F.order (d n))) (sk.2.1.1 : ZMod (F.order (d n))) (sk.2.1.2 : ZMod (F.order (d n))) (sk.2.2.1 : ZMod (F.order (d n))) (sk.2.2.2 : ZMod (F.order (d n))) - (if bit then choiceOut.1.1.2 else choiceOut.1.1.1) b - rw [hct, F.cramerShoupPhase2Oracle_eq_skNatOfZMod H isElem hmem (d n) sk] + (if bit then choiceOut.1.1.2 else choiceOut.1.1.1) [] b + rw [hct, F.cramerShoupPhase2Oracle_eq_skNatOfZMod false H isElem hmem (d n) sk] · simp only [h, Bool.false_eq_true, ↓reduceIte, ProbComp.eval_pure] have hgame1 : F.cramerShoupGroupCCA2Game (fun n => PMF.pure (d n)) H isElem adversary bit n = @@ -1161,8 +1656,8 @@ theorem advantage_cca2GameOn_cramerShoupGroupCCA2Game_le (PMF.uniformOfFintype (ZMod (F.order (d n)))).bind fun τ₁ => (PMF.uniformOfFintype (ZMod (F.order (d n)))).bind fun σ₂ => (PMF.uniformOfFintype (ZMod (F.order (d n)))).bind fun τ₂ => - F.cramerShoupCCA2InnerSK H isElem (d n) adversary bit n - α σ τ σ₁ τ₁ σ₂ τ₂ true := by + F.cramerShoupCCA2InnerSK false (fun _ => []) H isElem (d n) adversary.toADUnit + bit n α σ τ σ₁ τ₁ σ₂ τ₂ true := by simp only [cramerShoupGroupCCA2Game, cramerShoupMsgCCA2Game, PMF.monad_bind_eq_bind, PMF.monad_pure_eq_pure, PMF.pure_bind] refine congrArg _ (funext fun α => congrArg _ (funext fun σ => @@ -1172,46 +1667,8 @@ theorem advantage_cca2GameOn_cramerShoupGroupCCA2Game_le simp only [cramerShoupCCA2InnerSK, cramerShoupCCA2InnerB, ↓reduceIte, encodeCramerShoupPublicKey_encode, skNatOfZMod, ZMod.natCast_zmod_val] rw [hgame0, hgame1] - refine advantage_four_le_eight _ - ((PMF.uniformOfFintype (ZMod (F.order (d n)))).bind fun α => - (ProbComp.eval (F.sampleCramerShoupSK n (d n))).bind fun sk => - F.cramerShoupCCA2InnerSK H isElem (d n) adversary bit n α - (sk.1.1 : ZMod (F.order (d n))) (sk.1.2 : ZMod (F.order (d n))) - (sk.2.1.1 : ZMod (F.order (d n))) (sk.2.1.2 : ZMod (F.order (d n))) - (sk.2.2.1 : ZMod (F.order (d n))) (sk.2.2.2 : ZMod (F.order (d n))) false) - ((PMF.uniformOfFintype (ZMod (F.order (d n)))).bind fun α => - (PMF.uniformOfFintype (ZMod (F.order (d n)))).bind fun σ => - (PMF.uniformOfFintype (ZMod (F.order (d n)))).bind fun τ => - (PMF.uniformOfFintype (ZMod (F.order (d n)))).bind fun σ₁ => - (PMF.uniformOfFintype (ZMod (F.order (d n)))).bind fun τ₁ => - (PMF.uniformOfFintype (ZMod (F.order (d n)))).bind fun σ₂ => - (PMF.uniformOfFintype (ZMod (F.order (d n)))).bind fun τ₂ => - F.cramerShoupCCA2InnerSK H isElem (d n) adversary bit n - α σ τ σ₁ τ₁ σ₂ τ₂ false) _ - (F.advantage_bind_sampleExp_le n (d n) (fun α => - (ProbComp.eval (F.sampleCramerShoupSK n (d n))).bind fun sk => - F.cramerShoupCCA2InnerSK H isElem (d n) adversary bit n α - (sk.1.1 : ZMod (F.order (d n))) (sk.1.2 : ZMod (F.order (d n))) - (sk.2.1.1 : ZMod (F.order (d n))) (sk.2.1.2 : ZMod (F.order (d n))) - (sk.2.2.1 : ZMod (F.order (d n))) (sk.2.2.2 : ZMod (F.order (d n))) false)) - (advantage_bind_le_nnreal _ _ _ fun α => - F.advantage_bind_sampleCramerShoupSK_le n (d n) _ (fun σ τ σ₁ τ₁ σ₂ τ₂ => - F.cramerShoupCCA2InnerSK H isElem (d n) adversary bit n - α σ τ σ₁ τ₁ σ₂ τ₂ false) (fun _ _ _ _ _ _ => rfl)) ?_ - refine advantage_bind_le_nnreal _ _ _ fun α => ?_ - refine advantage_bind_le_nnreal _ _ _ fun σ => ?_ - refine advantage_bind_le_nnreal _ _ _ fun τ => ?_ - refine advantage_bind_le_nnreal _ _ _ fun σ₁ => ?_ - refine advantage_bind_le_nnreal _ _ _ fun τ₁ => ?_ - refine advantage_bind_le_nnreal _ _ _ fun σ₂ => ?_ - refine advantage_bind_le_nnreal _ _ _ fun τ₂ => ?_ - dsimp only [cramerShoupCCA2InnerSK] - refine advantage_bind_le_nnreal _ _ _ fun choiceOut => ?_ - by_cases h : (isElem (d n, choiceOut.1.1.1) && isElem (d n, choiceOut.1.1.2)) = true - · simp only [h, ↓reduceIte, Bool.false_eq_true] - exact F.advantage_bind_sampleExp_le n (d n) - (F.cramerShoupCCA2InnerB H isElem (d n) adversary bit n α σ τ σ₁ τ₁ σ₂ τ₂ choiceOut) - · simp only [h, Bool.false_eq_true, ↓reduceIte, Game.advantage_self, zero_le] + exact F.advantage_cramerShoupCCA2InnerSK_le false (fun _ => []) H isElem (d n) + adversary.toADUnit bit n /-- **Theorem 12.9 & Exercise 12.27 via `Encryption.INDCCA2On`**: for any polynomial-time group description family `d : ℕ → F.Desc`, the probabilistic polynomial-time Cramer–Shoup encryption diff --git a/Complexity/Crypto/Computational/CramerShoup/Reductions.lean b/Complexity/Crypto/Computational/CramerShoup/Reductions.lean index 237c7cb504..9b26853680 100644 --- a/Complexity/Crypto/Computational/CramerShoup/Reductions.lean +++ b/Complexity/Crypto/Computational/CramerShoup/Reductions.lean @@ -23,10 +23,10 @@ import Complexity.Tactic.PPT This module constructs the uniform strict-PPT (`IsPPTOn`) operations and security reductions for the **Multiplicative Cramer–Shoup public-key encryption scheme** ($\mathcal{E}_{\mathrm{MCS}}$, -[Boneh and Shoup, Exercise 12.27, printed pp. 521–522][BonehShoup2023], specialized to associated -data $\mathcal{D} = \mathrm{Unit}$) over a `PrimeOrderGroupFamily`, following the Games 0–4 -reduction structure of [Boneh and Shoup, Section 12.5.3, Theorem 12.9, printed -pp. 488–493][BonehShoup2023]: +[Boneh and Shoup, Exercise 12.27, printed pp. 521–522][BonehShoup2023], with associated data +$\mathcal{D}$ as in Section 12.7, Definition 12.7, printed pp. 501–502) over a +`PrimeOrderGroupFamily`, following the Games 0–4 reduction structure of [Boneh and Shoup, +Section 12.5.3, Theorem 12.9, printed pp. 488–493][BonehShoup2023]: 1. **Approximate exponent sampling via `sampleModNat`**: `sampleExp`, `sampleCramerShoupPair`, `sampleCramerShoupTripleExponents`, and `sampleCramerShoupSK`, together @@ -35,15 +35,18 @@ pp. 488–493][BonehShoup2023]: 2. **Phase 1/2 and collision-tracking decryption oracle handlers**: `cramerShoupPhase1Oracle`, `cramerShoupPhase2Reply`, `cramerShoupPhase2Oracle`, and `cramerShoupCollisionPhase2Oracle`. 3. **Certified strict-PPT DDH distinguishers (`Game 1 → Game 2`, eq. (12.22), printed p. 492)**: - `cramerShoupMsgToDDHDistinguisher` and `cramerShoupToDDHDistinguisher`, with `IsPPTOn` - certificates `isPPTOn_cramerShoupMsgToDDHDistinguisher` and + `cramerShoupMsgToDDHDistinguisher`, `cramerShoupADToDDHDistinguisher`, and + `cramerShoupToDDHDistinguisher`, with `IsPPTOn` certificates + `isPPTOn_cramerShoupMsgToDDHDistinguisher`, `isPPTOn_cramerShoupADToDDHDistinguisher`, and `isPPTOn_cramerShoupToDDHDistinguisher`. 4. **Certified strict-PPT CRHF collision finders (`Game 3 → Game 4`, eqs. (12.24)–(12.25), printed - p. 492)**: `cramerShoupMsgToCollisionFinder` and `cramerShoupToCollisionFinder`, with `IsPPTOn` - certificates `isPPTOn_cramerShoupMsgToCollisionFinder` and + p. 492)**: `cramerShoupMsgToCollisionFinder`, `cramerShoupADToCollisionFinder`, and + `cramerShoupToCollisionFinder`, with `IsPPTOn` certificates + `isPPTOn_cramerShoupMsgToCollisionFinder`, `isPPTOn_cramerShoupADToCollisionFinder`, and `isPPTOn_cramerShoupToCollisionFinder`. -5. **Certified strict-PPT `Encryption.cca2GameOn` operations**: `cramerShoupValid`, - `cramerShoupKeyGenProb`, `cramerShoupEncryptProb`, and `cramerShoupDecryptOpt`, together with +5. **Certified strict-PPT `Encryption.cca2ADGameOn` and `Encryption.cca2GameOn` operations**: + `cramerShoupValid`, `cramerShoupKeyGenProb`, `cramerShoupADEncryptProb`, + `cramerShoupEncryptProb`, `cramerShoupADDecryptOpt`, and `cramerShoupDecryptOpt`, together with their `ComputableInPolyTime` and `IsPPTOn` certificates. -/ @@ -63,9 +66,16 @@ private theorem computableTidilyInPolyTime_decodeBits4 : computableInPolyTime_decodeBits4.tidy @[local fun_prop] -private theorem computableTidilyInPolyTime_extractCramerShoupTriple : - ComputableTidilyInPolyTime extractCramerShoupTriple encBits encBits := - computableInPolyTime_extractCramerShoupTriple.tidy +private theorem computableTidilyInPolyTime_extractCramerShoupTuple : + ComputableTidilyInPolyTime (fun p : List Bool × List Bool => extractCramerShoupTuple p.1 p.2) + (encPair encBits encBits) encBits := + computableInPolyTime_extractCramerShoupTuple.tidy + +@[local fun_prop] +private theorem computableTidilyInPolyTime_splitCramerShoupQuery (hasAD : Bool) : + ComputableTidilyInPolyTime (splitCramerShoupQuery hasAD) + encBits (encPair encBits encBits) := + (computableInPolyTime_splitCramerShoupQuery hasAD).tidy @[local fun_prop] private theorem computableTidilyInPolyTime_encodeCramerShoupPublicKey @@ -83,33 +93,35 @@ private theorem computableTidilyInPolyTime_encodeCramerShoupChallenge {H : F.Desc × List Bool → List Bool} (hH : ComputableTidilyInPolyTime H (encPair F.encDesc encBits) encBits) {γ : Type} {encIn : γ ↪ List Bool} {d : γ → F.Desc} - {sk : γ → (ℕ × ℕ) × ((ℕ × ℕ) × (ℕ × ℕ))} {v w m : γ → List Bool} + {sk : γ → (ℕ × ℕ) × ((ℕ × ℕ) × (ℕ × ℕ))} {v w m ad : γ → List Bool} (hd : ComputableTidilyInPolyTime d encIn F.encDesc) (hsk : ComputableTidilyInPolyTime sk encIn encCramerShoupSK) (hv : ComputableTidilyInPolyTime v encIn encBits) (hw : ComputableTidilyInPolyTime w encIn encBits) - (hm : ComputableTidilyInPolyTime m encIn encBits) : + (hm : ComputableTidilyInPolyTime m encIn encBits) + (had : ComputableTidilyInPolyTime ad encIn encBits) : ComputableTidilyInPolyTime - (fun a => F.encodeCramerShoupChallenge H (d a) (sk a) (v a) (w a) (m a)) + (fun a => F.encodeCramerShoupChallenge H (d a) (sk a) (v a) (w a) (m a) (ad a)) encIn encBits := (F.computableInPolyTime_encodeCramerShoupChallenge (.of_tidy hH)).tidy.comp - (hd.prod (hsk.prod (hv.prod (hw.prod hm)))) + (hd.prod (hsk.prod (hv.prod (hw.prod (hm.prod had))))) @[local fun_prop] private theorem computableTidilyInPolyTime_encodeCramerShoupEncrypt {H : F.Desc × List Bool → List Bool} (hH : ComputableTidilyInPolyTime H (encPair F.encDesc encBits) encBits) {γ : Type} {encIn : γ ↪ List Bool} {d : γ → F.Desc} - {pk : γ → List Bool × List Bool × List Bool × List Bool} {m : γ → List Bool} {b : γ → ℕ} + {pk : γ → List Bool × List Bool × List Bool × List Bool} {m ad : γ → List Bool} {b : γ → ℕ} (hd : ComputableTidilyInPolyTime d encIn F.encDesc) (hpk : ComputableTidilyInPolyTime pk encIn (encPair encBits (encPair encBits (encPair encBits encBits)))) (hm : ComputableTidilyInPolyTime m encIn encBits) + (had : ComputableTidilyInPolyTime ad encIn encBits) (hb : ComputableTidilyInPolyTime b encIn encNat) : ComputableTidilyInPolyTime - (fun a => F.encodeCramerShoupEncrypt H (d a) (pk a) (m a) (b a)) encIn encBits := + (fun a => F.encodeCramerShoupEncrypt H (d a) (pk a) (m a) (ad a) (b a)) encIn encBits := (F.computableInPolyTime_encodeCramerShoupEncrypt (.of_tidy hH)).tidy.comp - (hd.prod (hpk.prod (hm.prod hb))) + (hd.prod (hpk.prod (hm.prod (had.prod hb)))) private theorem computableTidilyInPolyTime_comp {α β γ : Type} {encA : α ↪ List Bool} {encB : β ↪ List Bool} {encC : γ ↪ List Bool} @@ -124,19 +136,20 @@ private theorem computableTidilyInPolyTime_decodeCramerShoup (hH : ComputableTidilyInPolyTime H (encPair F.encDesc encBits) encBits) (hisElem : ComputableTidilyInPolyTime isElem (encPair F.encDesc encBits) encBool) {γ : Type} {encIn : γ ↪ List Bool} {d : γ → F.Desc} - {sk : γ → (ℕ × ℕ) × ((ℕ × ℕ) × (ℕ × ℕ))} {ct : γ → List Bool} + {sk : γ → (ℕ × ℕ) × ((ℕ × ℕ) × (ℕ × ℕ))} {ct ad : γ → List Bool} (hd : ComputableTidilyInPolyTime d encIn F.encDesc) (hsk : ComputableTidilyInPolyTime sk encIn encCramerShoupSK) - (hct : ComputableTidilyInPolyTime ct encIn encBits) : + (hct : ComputableTidilyInPolyTime ct encIn encBits) + (had : ComputableTidilyInPolyTime ad encIn encBits) : ComputableTidilyInPolyTime - (fun a => F.decodeCramerShoup H isElem (d a) (sk a) (ct a)) + (fun a => F.decodeCramerShoup H isElem (d a) (sk a) (ct a) (ad a)) encIn (encOption encBits) := computableTidilyInPolyTime_comp - (g := fun p : F.Desc × ((ℕ × ℕ) × ((ℕ × ℕ) × (ℕ × ℕ))) × List Bool => - F.decodeCramerShoup H isElem p.1 p.2.1 p.2.2) - (f := fun a => (d a, sk a, ct a)) + (g := fun p : F.Desc × ((ℕ × ℕ) × ((ℕ × ℕ) × (ℕ × ℕ))) × List Bool × List Bool => + F.decodeCramerShoup H isElem p.1 p.2.1 p.2.2.1 p.2.2.2) + (f := fun a => (d a, sk a, ct a, ad a)) (F.computableInPolyTime_decodeCramerShoup (.of_tidy hH) (.of_tidy hisElem)).tidy - (hd.prod (hsk.prod hct)) + (hd.prod (hsk.prod (hct.prod had))) /-! ### Approximate exponent sampling via `sampleModNat` -/ @@ -333,23 +346,29 @@ private theorem computableTidilyInPolyTime_add_unary {γ : Type} {encIn : γ ↪ private theorem computableTidilyInPolyTime_isCramerShoupCollision {H : F.Desc × List Bool → List Bool} (hH : ComputableInPolyTime H (encPair F.encDesc encBits) encBits) - {γ : Type} {encIn : γ ↪ List Bool} {d : γ → F.Desc} {req ct : γ → List Bool} + {γ : Type} {encIn : γ ↪ List Bool} {d : γ → F.Desc} {ct_q ad_q ct ad : γ → List Bool} (hd : ComputableTidilyInPolyTime d encIn F.encDesc) - (hreq : ComputableTidilyInPolyTime req encIn encBits) - (hct : ComputableTidilyInPolyTime ct encIn encBits) : + (hct_q : ComputableTidilyInPolyTime ct_q encIn encBits) + (had_q : ComputableTidilyInPolyTime ad_q encIn encBits) + (hct : ComputableTidilyInPolyTime ct encIn encBits) + (had : ComputableTidilyInPolyTime ad encIn encBits) : ComputableTidilyInPolyTime - (fun a => F.isCramerShoupCollision H (d a) (req a) (ct a)) encIn encBool := by + (fun a => F.isCramerShoupCollision H (d a) (ct_q a) (ad_q a) (ct a) (ad a)) + encIn encBool := by have hx := - computableTidilyInPolyTime_comp computableTidilyInPolyTime_extractCramerShoupTriple hreq + computableTidilyInPolyTime_comp computableTidilyInPolyTime_extractCramerShoupTuple + (hct_q.prod had_q) have hy := - computableTidilyInPolyTime_comp computableTidilyInPolyTime_extractCramerShoupTriple hct + computableTidilyInPolyTime_comp computableTidilyInPolyTime_extractCramerShoupTuple + (hct.prod had) have hbeqBits : ComputableTidilyInPolyTime (fun q : List Bool × List Bool => q.1 == q.2) (encPair encBits encBits) encBool := by polytime have hnot : ComputableTidilyInPolyTime (fun b : Bool => !b) encBool encBool := by polytime have hand : ComputableTidilyInPolyTime (fun q : Bool × Bool => q.1 && q.2) (encPair encBool encBool) encBool := by polytime have hne : ComputableTidilyInPolyTime - (fun a => extractCramerShoupTriple (req a) != extractCramerShoupTriple (ct a)) + (fun a => + extractCramerShoupTuple (ct_q a) (ad_q a) != extractCramerShoupTuple (ct a) (ad a)) encIn encBool := computableTidilyInPolyTime_comp hnot (computableTidilyInPolyTime_comp hbeqBits (hx.prod hy)) have hord := computableTidilyInPolyTime_comp F.computableInPolyTime_order.tidy hd @@ -360,13 +379,17 @@ private theorem computableTidilyInPolyTime_isCramerShoupCollision (computableTidilyInPolyTime_comp ComputableTidilyInPolyTime.bitsToNat (computableTidilyInPolyTime_comp hH.tidy (hd.prod hy))) hord have heqMod : ComputableTidilyInPolyTime - (fun a => (ofBits (H (d a, extractCramerShoupTriple (req a))) % F.order (d a)) == - (ofBits (H (d a, extractCramerShoupTriple (ct a))) % F.order (d a))) encIn encBool := by + (fun a => + (ofBits (H (d a, extractCramerShoupTuple (ct_q a) (ad_q a))) % F.order (d a)) == + (ofBits (H (d a, extractCramerShoupTuple (ct a) (ad a))) % F.order (d a))) + encIn encBool := by have hcongr : - (fun a => (ofBits (H (d a, extractCramerShoupTriple (req a))) % F.order (d a)) == - (ofBits (H (d a, extractCramerShoupTriple (ct a))) % F.order (d a))) = - (fun a => encNat (ofBits (H (d a, extractCramerShoupTriple (req a))) % F.order (d a)) == - encNat (ofBits (H (d a, extractCramerShoupTriple (ct a))) % F.order (d a))) := by + (fun a => + (ofBits (H (d a, extractCramerShoupTuple (ct_q a) (ad_q a))) % F.order (d a)) == + (ofBits (H (d a, extractCramerShoupTuple (ct a) (ad a))) % F.order (d a))) = + (fun a => + encNat (ofBits (H (d a, extractCramerShoupTuple (ct_q a) (ad_q a))) % F.order (d a)) == + encNat (ofBits (H (d a, extractCramerShoupTuple (ct a) (ad a))) % F.order (d a))) := by funext a; rw [Bool.eq_iff_iff, beq_iff_eq, beq_iff_eq, encNat.injective.eq_iff] rw [hcongr] exact computableTidilyInPolyTime_comp hbeqBits @@ -374,39 +397,49 @@ private theorem computableTidilyInPolyTime_isCramerShoupCollision (computableTidilyInPolyTime_comp (ComputableTidilyInPolyTime.encode encNat) hmody)) exact computableTidilyInPolyTime_comp hand (hne.prod heqMod) -/-- Phase 1 decryption oracle handler for Cramer–Shoup. -/ -def cramerShoupPhase1Oracle (H : F.Desc × List Bool → List Bool) +/-- Phase 1 decryption oracle handler for Cramer–Shoup (`hasAD = true` for `cca2ADPhase1Oracle`, +`hasAD = false` for `cca2Phase1Oracle`). -/ +def cramerShoupPhase1Oracle (hasAD : Bool) (H : F.Desc × List Bool → List Bool) (isElem : F.Desc × List Bool → Bool) (d : F.Desc) (sk : (ℕ × ℕ) × ((ℕ × ℕ) × (ℕ × ℕ))) (request : Unit × List Bool) : StateT Unit ProbComp (List Bool) := - fun s => pure (F.cramerShoupPhase1Reply H isElem d sk request.2, s) + fun s => pure (F.cramerShoupPhase1Reply hasAD H isElem d sk request.2, s) -/-- Stateless Phase 2 decryption reply for Cramer–Shoup, rejecting the challenge ciphertext `ct` +/-- Stateless Phase 2 decryption reply for Cramer–Shoup, rejecting the challenge `(ct, ad)` with `[]`. -/ -def cramerShoupPhase2Reply (H : F.Desc × List Bool → List Bool) +def cramerShoupPhase2Reply (hasAD : Bool) (H : F.Desc × List Bool → List Bool) (isElem : F.Desc × List Bool → Bool) (d : F.Desc) - (sk : (ℕ × ℕ) × ((ℕ × ℕ) × (ℕ × ℕ))) (ct req : List Bool) : List Bool := - if req == ct then [] else encOption encBits (F.decodeCramerShoup H isElem d sk req) - -/-- Phase 2 decryption oracle handler for Cramer–Shoup, rejecting the challenge ciphertext `ct`. -/ -def cramerShoupPhase2Oracle (H : F.Desc × List Bool → List Bool) + (sk : (ℕ × ℕ) × ((ℕ × ℕ) × (ℕ × ℕ))) (ct ad req : List Bool) : List Bool := + let q := splitCramerShoupQuery hasAD req + if (q.1 == ct) && (q.2 == ad) then + [] + else + encOption encBits (F.decodeCramerShoup H isElem d sk q.1 q.2) + +/-- Phase 2 decryption oracle handler for Cramer–Shoup, rejecting the challenge `(ct, ad)`. -/ +def cramerShoupPhase2Oracle (hasAD : Bool) (H : F.Desc × List Bool → List Bool) (isElem : F.Desc × List Bool → Bool) (d : F.Desc) - (sk : (ℕ × ℕ) × ((ℕ × ℕ) × (ℕ × ℕ))) (ct : List Bool) (request : Unit × List Bool) : + (sk : (ℕ × ℕ) × ((ℕ × ℕ) × (ℕ × ℕ))) (ct ad : List Bool) (request : Unit × List Bool) : StateT Unit ProbComp (List Bool) := - fun s => pure (F.cramerShoupPhase2Reply H isElem d sk ct request.2, s) + fun s => pure (F.cramerShoupPhase2Reply hasAD H isElem d sk ct ad request.2, s) -/-- Phase 2 collision-tracking oracle handler for `cramerShoupToCollisionFinder`: replies with -`cramerShoupCollisionPhase2Reply` and records `some (extractCramerShoupTriple request.2)` whenever -`isCramerShoupCollision H d request.2 ct` holds. -/ -def cramerShoupCollisionPhase2Oracle (H : F.Desc × List Bool → List Bool) +/-- Phase 2 collision-tracking oracle handler for `cramerShoupMsgToCollisionFinder`: replies with +`cramerShoupCollisionPhase2Reply` and records `some (extractCramerShoupTuple q.1 q.2)` whenever +`isCramerShoupCollision H d q.1 q.2 ct ad` holds (where `q = splitCramerShoupQuery hasAD req`). -/ +def cramerShoupCollisionPhase2Oracle (hasAD : Bool) (H : F.Desc × List Bool → List Bool) (isElem : F.Desc × List Bool → Bool) (d : F.Desc) - (sk : (ℕ × ℕ) × ((ℕ × ℕ) × (ℕ × ℕ))) (ct : List Bool) (request : Unit × List Bool) : + (sk : (ℕ × ℕ) × ((ℕ × ℕ) × (ℕ × ℕ))) (ct ad : List Bool) (request : Unit × List Bool) : StateT (Option (List Bool)) ProbComp (List Bool) := - finderPhase2Oracle (F.cramerShoupCollisionPhase2Reply H isElem d sk ct) - (fun req => F.isCramerShoupCollision H d req ct) extractCramerShoupTriple request + finderPhase2Oracle (F.cramerShoupCollisionPhase2Reply hasAD H isElem d sk ct ad) + (fun req => + let q := splitCramerShoupQuery hasAD req + F.isCramerShoupCollision H d q.1 q.2 ct ad) + (fun req => + let q := splitCramerShoupQuery hasAD req + extractCramerShoupTuple q.1 q.2) request private theorem computableTidilyInPolyTime_cramerShoupPhase1Reply - {H : F.Desc × List Bool → List Bool} {isElem : F.Desc × List Bool → Bool} + (hasAD : Bool) {H : F.Desc × List Bool → List Bool} {isElem : F.Desc × List Bool → Bool} (hH : ComputableInPolyTime H (encPair F.encDesc encBits) encBits) (hisElem : ComputableInPolyTime isElem (encPair F.encDesc encBits) encBool) {γ : Type} {encIn : γ ↪ List Bool} {d : γ → F.Desc} @@ -415,59 +448,79 @@ private theorem computableTidilyInPolyTime_cramerShoupPhase1Reply (hsk : ComputableTidilyInPolyTime sk encIn encCramerShoupSK) (hreq : ComputableTidilyInPolyTime req encIn encBits) : ComputableTidilyInPolyTime - (fun a => F.cramerShoupPhase1Reply H isElem (d a) (sk a) (req a)) encIn encBits := - computableTidilyInPolyTime_comp (ComputableTidilyInPolyTime.encode (encOption encBits)) - (F.computableTidilyInPolyTime_decodeCramerShoup hH.tidy hisElem.tidy hd hsk hreq) + (fun a => F.cramerShoupPhase1Reply hasAD H isElem (d a) (sk a) (req a)) encIn encBits := by + have hsplit := computableTidilyInPolyTime_comp + (computableTidilyInPolyTime_splitCramerShoupQuery hasAD) hreq + exact computableTidilyInPolyTime_comp (ComputableTidilyInPolyTime.encode (encOption encBits)) + (F.computableTidilyInPolyTime_decodeCramerShoup hH.tidy hisElem.tidy hd hsk + hsplit.fst' hsplit.snd') private theorem computableTidilyInPolyTime_cramerShoupPhase2Reply - {H : F.Desc × List Bool → List Bool} {isElem : F.Desc × List Bool → Bool} + (hasAD : Bool) {H : F.Desc × List Bool → List Bool} {isElem : F.Desc × List Bool → Bool} (hH : ComputableInPolyTime H (encPair F.encDesc encBits) encBits) (hisElem : ComputableInPolyTime isElem (encPair F.encDesc encBits) encBool) {γ : Type} {encIn : γ ↪ List Bool} {d : γ → F.Desc} - {sk : γ → (ℕ × ℕ) × ((ℕ × ℕ) × (ℕ × ℕ))} {ct req : γ → List Bool} + {sk : γ → (ℕ × ℕ) × ((ℕ × ℕ) × (ℕ × ℕ))} {ct ad req : γ → List Bool} (hd : ComputableTidilyInPolyTime d encIn F.encDesc) (hsk : ComputableTidilyInPolyTime sk encIn encCramerShoupSK) (hct : ComputableTidilyInPolyTime ct encIn encBits) + (had : ComputableTidilyInPolyTime ad encIn encBits) (hreq : ComputableTidilyInPolyTime req encIn encBits) : ComputableTidilyInPolyTime - (fun a => F.cramerShoupPhase2Reply H isElem (d a) (sk a) (ct a) (req a)) encIn encBits := by + (fun a => F.cramerShoupPhase2Reply hasAD H isElem (d a) (sk a) (ct a) (ad a) (req a)) + encIn encBits := by + have hsplit := computableTidilyInPolyTime_comp + (computableTidilyInPolyTime_splitCramerShoupQuery hasAD) hreq have hbeq : ComputableTidilyInPolyTime (fun q : List Bool × List Bool => q.1 == q.2) (encPair encBits encBits) encBool := by polytime - exact ComputableTidilyInPolyTime.cond - (computableTidilyInPolyTime_comp hbeq (hreq.prod hct)) (.const []) - (F.computableTidilyInPolyTime_cramerShoupPhase1Reply hH hisElem hd hsk hreq) + have hand : ComputableTidilyInPolyTime (fun q : Bool × Bool => q.1 && q.2) + (encPair encBool encBool) encBool := by polytime + have heq := computableTidilyInPolyTime_comp hand + ((computableTidilyInPolyTime_comp hbeq (hsplit.fst'.prod hct)).prod + (computableTidilyInPolyTime_comp hbeq (hsplit.snd'.prod had))) + exact ComputableTidilyInPolyTime.cond heq (.const []) + (F.computableTidilyInPolyTime_cramerShoupPhase1Reply hasAD hH hisElem hd hsk hreq) private theorem computableTidilyInPolyTime_cramerShoupCollisionPhase2Reply - {H : F.Desc × List Bool → List Bool} {isElem : F.Desc × List Bool → Bool} + (hasAD : Bool) {H : F.Desc × List Bool → List Bool} {isElem : F.Desc × List Bool → Bool} (hH : ComputableInPolyTime H (encPair F.encDesc encBits) encBits) (hisElem : ComputableInPolyTime isElem (encPair F.encDesc encBits) encBool) {γ : Type} {encIn : γ ↪ List Bool} {d : γ → F.Desc} - {sk : γ → (ℕ × ℕ) × ((ℕ × ℕ) × (ℕ × ℕ))} {ct req : γ → List Bool} + {sk : γ → (ℕ × ℕ) × ((ℕ × ℕ) × (ℕ × ℕ))} {ct ad req : γ → List Bool} (hd : ComputableTidilyInPolyTime d encIn F.encDesc) (hsk : ComputableTidilyInPolyTime sk encIn encCramerShoupSK) (hct : ComputableTidilyInPolyTime ct encIn encBits) + (had : ComputableTidilyInPolyTime ad encIn encBits) (hreq : ComputableTidilyInPolyTime req encIn encBits) : ComputableTidilyInPolyTime - (fun a => F.cramerShoupCollisionPhase2Reply H isElem (d a) (sk a) (ct a) (req a)) + (fun a => F.cramerShoupCollisionPhase2Reply hasAD H isElem (d a) (sk a) (ct a) (ad a) (req a)) encIn encBits := by + have hsplit := computableTidilyInPolyTime_comp + (computableTidilyInPolyTime_splitCramerShoupQuery hasAD) hreq have hbeq : ComputableTidilyInPolyTime (fun q : List Bool × List Bool => q.1 == q.2) (encPair encBits encBits) encBool := by polytime + have hand : ComputableTidilyInPolyTime (fun q : Bool × Bool => q.1 && q.2) + (encPair encBool encBool) encBool := by polytime have hor : ComputableTidilyInPolyTime (fun q : Bool × Bool => q.1 || q.2) (encPair encBool encBool) encBool := by polytime + have heq := computableTidilyInPolyTime_comp hand + ((computableTidilyInPolyTime_comp hbeq (hsplit.fst'.prod hct)).prod + (computableTidilyInPolyTime_comp hbeq (hsplit.snd'.prod had))) exact ComputableTidilyInPolyTime.cond (computableTidilyInPolyTime_comp hor - ((computableTidilyInPolyTime_comp hbeq (hreq.prod hct)).prod - (F.computableTidilyInPolyTime_isCramerShoupCollision hH hd hreq hct))) + (heq.prod + (F.computableTidilyInPolyTime_isCramerShoupCollision hH hd + hsplit.fst' hsplit.snd' hct had))) (.const []) - (F.computableTidilyInPolyTime_cramerShoupPhase1Reply hH hisElem hd hsk hreq) + (F.computableTidilyInPolyTime_cramerShoupPhase1Reply hasAD hH hisElem hd hsk hreq) private theorem isPPTOn_run_cramerShoupPhase1Oracle - {Msg : Type} {encMsg : Msg ↪ List Bool} + (hasAD : Bool) {Msg AD : Type} {encMsg : Msg ↪ List Bool} {encAD : AD ↪ List Bool} {H : F.Desc × List Bool → List Bool} {isElem : F.Desc × List Bool → Bool} - {adversary : CCA2AdversaryOf Msg} + {adversary : ADCCA2AdversaryOf Msg AD} (hH : ComputableInPolyTime H (encPair F.encDesc encBits) encBits) (hisElem : ComputableInPolyTime isElem (encPair F.encDesc encBits) encBool) - (hA : adversary.IsPPT encMsg) + (hA : adversary.IsPPT encMsg encAD) {γ : Type} {encIn : γ ↪ List Bool} {n : γ → ℕ} {d : γ → F.Desc} {sk : γ → (ℕ × ℕ) × ((ℕ × ℕ) × (ℕ × ℕ))} {pk : γ → List Bool} @@ -475,198 +528,229 @@ private theorem isPPTOn_run_cramerShoupPhase1Oracle (hd : ComputableTidilyInPolyTime d encIn F.encDesc) (hsk : ComputableTidilyInPolyTime sk encIn encCramerShoupSK) (hpk : ComputableTidilyInPolyTime pk encIn encBits) : - IsPPTOn encIn (encPair (encPair (encPair encMsg encMsg) encBits) (encFinEnum Unit)) + IsPPTOn encIn + (encPair (encPair (encPair (encPair encMsg encMsg) encAD) encBits) (encFinEnum Unit)) (fun a => (OracleComp.interpret (ProbOracleComp.handler (fun m => StateT.lift (ProbComp.unifFin m)) - (F.cramerShoupPhase1Oracle H isElem (d a) (sk a))) + (F.cramerShoupPhase1Oracle hasAD H isElem (d a) (sk a))) (adversary.choose (n a) (pk a))).run ()) := by have hp1 := ComputableTidilyInPolyTime.fst encIn encBits have hp2 := ComputableTidilyInPolyTime.snd encIn encBits - have hreply := F.computableTidilyInPolyTime_cramerShoupPhase1Reply hH hisElem + have hreply := F.computableTidilyInPolyTime_cramerShoupPhase1Reply hasAD hH hisElem (computableTidilyInPolyTime_comp hd hp1) (computableTidilyInPolyTime_comp hsk hp1) hp2 have hE1 := ComputableTidilyInPolyTime.fst encIn (encPair (encPair (encFinEnum Unit) encBits) (encFinEnum Unit)) have hE2 := ComputableTidilyInPolyTime.snd encIn (encPair (encPair (encFinEnum Unit) encBits) (encFinEnum Unit)) - have hreplyIn := F.computableTidilyInPolyTime_cramerShoupPhase1Reply hH hisElem + have hreplyIn := F.computableTidilyInPolyTime_cramerShoupPhase1Reply hasAD hH hisElem (computableTidilyInPolyTime_comp hd hE1) (computableTidilyInPolyTime_comp hsk hE1) hE2.fst'.snd' exact hA.choose.on.run_interpret_preprocess (prepare := fun a : γ => (n a, pk a)) (initial := fun _ => ()) - (resources := fun a req => (F.cramerShoupPhase1Reply H isElem (d a) (sk a) req).length) + (resources := fun a req => (F.cramerShoupPhase1Reply hasAD H isElem (d a) (sk a) req).length) (hn.prod hpk) (hreplyIn.prod hE2.snd').isPPTOn (.const ()) (fun _ _ => True) (fun _ => trivial) (computableTidilyInPolyTime_comp .length_bits hreply) (fun a request state _ result hresult => by have hresEq : result = - (F.cramerShoupPhase1Reply H isElem (d a) (sk a) request.2, state) := by + (F.cramerShoupPhase1Reply hasAD H isElem (d a) (sk a) request.2, state) := by simpa [cramerShoupPhase1Oracle] using hresult subst hresEq exact ⟨trivial, le_rfl, Nat.le_add_right _ _⟩) private theorem isPPTOn_run_cramerShoupPhase2Oracle - {Msg : Type} {encMsg : Msg ↪ List Bool} + (hasAD : Bool) {Msg AD : Type} {encMsg : Msg ↪ List Bool} {encAD : AD ↪ List Bool} {H : F.Desc × List Bool → List Bool} {isElem : F.Desc × List Bool → Bool} - {adversary : CCA2AdversaryOf Msg} + {adversary : ADCCA2AdversaryOf Msg AD} (hH : ComputableInPolyTime H (encPair F.encDesc encBits) encBits) (hisElem : ComputableInPolyTime isElem (encPair F.encDesc encBits) encBool) - (hA : adversary.IsPPT encMsg) + (hA : adversary.IsPPT encMsg encAD) {γ : Type} {encIn : γ ↪ List Bool} {n : γ → ℕ} {d : γ → F.Desc} - {sk : γ → (ℕ × ℕ) × ((ℕ × ℕ) × (ℕ × ℕ))} {state ct : γ → List Bool} + {sk : γ → (ℕ × ℕ) × ((ℕ × ℕ) × (ℕ × ℕ))} {state ct ad : γ → List Bool} (hn : ComputableTidilyInPolyTime n encIn encUnary) (hd : ComputableTidilyInPolyTime d encIn F.encDesc) (hsk : ComputableTidilyInPolyTime sk encIn encCramerShoupSK) (hstate : ComputableTidilyInPolyTime state encIn encBits) - (hct : ComputableTidilyInPolyTime ct encIn encBits) : + (hct : ComputableTidilyInPolyTime ct encIn encBits) + (had : ComputableTidilyInPolyTime ad encIn encBits) : IsPPTOn encIn (encPair encBool (encFinEnum Unit)) (fun a => (OracleComp.interpret (ProbOracleComp.handler (fun m => StateT.lift (ProbComp.unifFin m)) - (F.cramerShoupPhase2Oracle H isElem (d a) (sk a) (ct a))) + (F.cramerShoupPhase2Oracle hasAD H isElem (d a) (sk a) (ct a) (ad a))) (adversary.guess (n a) (state a) (ct a))).run ()) := by have hp1 := ComputableTidilyInPolyTime.fst encIn encBits have hp2 := ComputableTidilyInPolyTime.snd encIn encBits - have hreply := F.computableTidilyInPolyTime_cramerShoupPhase2Reply hH hisElem + have hreply := F.computableTidilyInPolyTime_cramerShoupPhase2Reply hasAD hH hisElem (computableTidilyInPolyTime_comp hd hp1) (computableTidilyInPolyTime_comp hsk hp1) - (computableTidilyInPolyTime_comp hct hp1) hp2 + (computableTidilyInPolyTime_comp hct hp1) (computableTidilyInPolyTime_comp had hp1) hp2 have hE1 := ComputableTidilyInPolyTime.fst encIn (encPair (encPair (encFinEnum Unit) encBits) (encFinEnum Unit)) have hE2 := ComputableTidilyInPolyTime.snd encIn (encPair (encPair (encFinEnum Unit) encBits) (encFinEnum Unit)) - have hreplyIn := F.computableTidilyInPolyTime_cramerShoupPhase2Reply hH hisElem + have hreplyIn := F.computableTidilyInPolyTime_cramerShoupPhase2Reply hasAD hH hisElem (computableTidilyInPolyTime_comp hd hE1) (computableTidilyInPolyTime_comp hsk hE1) - (computableTidilyInPolyTime_comp hct hE1) hE2.fst'.snd' + (computableTidilyInPolyTime_comp hct hE1) (computableTidilyInPolyTime_comp had hE1) + hE2.fst'.snd' exact hA.guess.run_interpret_preprocess (prepare := fun a : γ => (n a, state a, ct a)) (initial := fun _ => ()) - (resources := fun a req => (F.cramerShoupPhase2Reply H isElem (d a) (sk a) (ct a) req).length) + (resources := fun a req => + (F.cramerShoupPhase2Reply hasAD H isElem (d a) (sk a) (ct a) (ad a) req).length) (hn.prod (hstate.prod hct)) (hreplyIn.prod hE2.snd').isPPTOn (.const ()) (fun _ _ => True) (fun _ => trivial) (computableTidilyInPolyTime_comp .length_bits hreply) (fun a request st _ result hresult => by have hresEq : result = - (F.cramerShoupPhase2Reply H isElem (d a) (sk a) (ct a) request.2, st) := by + (F.cramerShoupPhase2Reply hasAD H isElem (d a) (sk a) (ct a) (ad a) request.2, st) := by simpa [cramerShoupPhase2Oracle] using hresult subst hresEq exact ⟨trivial, le_rfl, Nat.le_add_right _ _⟩) private theorem isPPTOn_run_cramerShoupCollisionPhase2Oracle - {Msg : Type} {encMsg : Msg ↪ List Bool} + (hasAD : Bool) {Msg AD : Type} {encMsg : Msg ↪ List Bool} {encAD : AD ↪ List Bool} {H : F.Desc × List Bool → List Bool} {isElem : F.Desc × List Bool → Bool} - {adversary : CCA2AdversaryOf Msg} + {adversary : ADCCA2AdversaryOf Msg AD} (hH : ComputableInPolyTime H (encPair F.encDesc encBits) encBits) (hisElem : ComputableInPolyTime isElem (encPair F.encDesc encBits) encBool) - (hA : adversary.IsPPT encMsg) + (hA : adversary.IsPPT encMsg encAD) {γ : Type} {encIn : γ ↪ List Bool} {n : γ → ℕ} {d : γ → F.Desc} - {sk : γ → (ℕ × ℕ) × ((ℕ × ℕ) × (ℕ × ℕ))} {state ct : γ → List Bool} + {sk : γ → (ℕ × ℕ) × ((ℕ × ℕ) × (ℕ × ℕ))} {state ct ad : γ → List Bool} (hn : ComputableTidilyInPolyTime n encIn encUnary) (hd : ComputableTidilyInPolyTime d encIn F.encDesc) (hsk : ComputableTidilyInPolyTime sk encIn encCramerShoupSK) (hstate : ComputableTidilyInPolyTime state encIn encBits) - (hct : ComputableTidilyInPolyTime ct encIn encBits) : + (hct : ComputableTidilyInPolyTime ct encIn encBits) + (had : ComputableTidilyInPolyTime ad encIn encBits) : IsPPTOn encIn (encPair encBool (encOption encBits)) (fun a => (OracleComp.interpret (ProbOracleComp.handler (fun m => StateT.lift (ProbComp.unifFin m)) - (F.cramerShoupCollisionPhase2Oracle H isElem (d a) (sk a) (ct a))) + (F.cramerShoupCollisionPhase2Oracle hasAD H isElem (d a) (sk a) (ct a) (ad a))) (adversary.guess (n a) (state a) (ct a))).run none) := by have hp1 := ComputableTidilyInPolyTime.fst encIn encBits have hp2 := ComputableTidilyInPolyTime.snd encIn encBits - have hreply := F.computableTidilyInPolyTime_cramerShoupCollisionPhase2Reply hH hisElem + have hreply := F.computableTidilyInPolyTime_cramerShoupCollisionPhase2Reply hasAD hH hisElem (computableTidilyInPolyTime_comp hd hp1) (computableTidilyInPolyTime_comp hsk hp1) - (computableTidilyInPolyTime_comp hct hp1) hp2 + (computableTidilyInPolyTime_comp hct hp1) (computableTidilyInPolyTime_comp had hp1) hp2 have hE1 := ComputableTidilyInPolyTime.fst encIn (encPair (encPair (encFinEnum Unit) encBits) (encOption encBits)) have hE2 := ComputableTidilyInPolyTime.snd encIn (encPair (encPair (encFinEnum Unit) encBits) (encOption encBits)) - have hreplyIn := F.computableTidilyInPolyTime_cramerShoupCollisionPhase2Reply hH hisElem + have hreplyIn := F.computableTidilyInPolyTime_cramerShoupCollisionPhase2Reply hasAD hH hisElem (computableTidilyInPolyTime_comp hd hE1) (computableTidilyInPolyTime_comp hsk hE1) - (computableTidilyInPolyTime_comp hct hE1) hE2.fst'.snd' + (computableTidilyInPolyTime_comp hct hE1) (computableTidilyInPolyTime_comp had hE1) + hE2.fst'.snd' + have hsplitIn := computableTidilyInPolyTime_comp + (computableTidilyInPolyTime_splitCramerShoupQuery hasAD) hE2.fst'.snd' have hcollIn := F.computableTidilyInPolyTime_isCramerShoupCollision hH - (computableTidilyInPolyTime_comp hd hE1) hE2.fst'.snd' (computableTidilyInPolyTime_comp hct hE1) + (computableTidilyInPolyTime_comp hd hE1) hsplitIn.fst' hsplitIn.snd' + (computableTidilyInPolyTime_comp hct hE1) (computableTidilyInPolyTime_comp had hE1) have hsome : ComputableTidilyInPolyTime (fun x : List Bool => (some x : Option (List Bool))) encBits (encOption encBits) := by polytime have hnextState := ComputableTidilyInPolyTime.cond hcollIn (computableTidilyInPolyTime_comp hsome - (computableTidilyInPolyTime_comp computableTidilyInPolyTime_extractCramerShoupTriple - hE2.fst'.snd')) + (computableTidilyInPolyTime_comp computableTidilyInPolyTime_extractCramerShoupTuple + hsplitIn)) hE2.snd' + have hsplitP2 := computableTidilyInPolyTime_comp + (computableTidilyInPolyTime_splitCramerShoupQuery hasAD) hp2 have hsomeLen := computableTidilyInPolyTime_comp ComputableTidilyInPolyTime.length_bits (computableTidilyInPolyTime_comp (ComputableTidilyInPolyTime.encode (encOption encBits)) (computableTidilyInPolyTime_comp hsome - (computableTidilyInPolyTime_comp computableTidilyInPolyTime_extractCramerShoupTriple hp2))) + (computableTidilyInPolyTime_comp computableTidilyInPolyTime_extractCramerShoupTuple + hsplitP2))) have hres := computableTidilyInPolyTime_add_unary (computableTidilyInPolyTime_comp ComputableTidilyInPolyTime.length_bits hreply) hsomeLen exact hA.guess.run_interpret_preprocess (prepare := fun a : γ => (n a, state a, ct a)) (initial := fun _ => none) (resources := fun a req => - (F.cramerShoupCollisionPhase2Reply H isElem (d a) (sk a) (ct a) req).length + - (encOption encBits (some (extractCramerShoupTriple req))).length) + let q := splitCramerShoupQuery hasAD req + (F.cramerShoupCollisionPhase2Reply hasAD H isElem (d a) (sk a) (ct a) (ad a) req).length + + (encOption encBits (some (extractCramerShoupTuple q.1 q.2))).length) (hn.prod (hstate.prod hct)) (hreplyIn.prod hnextState).isPPTOn (.const (none : Option (List Bool))) (fun _ _ => True) (fun _ => trivial) hres (fun a request st _ result hresult => by + let q := splitCramerShoupQuery hasAD request.2 have hresEq : result = - (F.cramerShoupCollisionPhase2Reply H isElem (d a) (sk a) (ct a) request.2, - if F.isCramerShoupCollision H (d a) request.2 (ct a) then - some (extractCramerShoupTriple request.2) + (F.cramerShoupCollisionPhase2Reply hasAD H isElem (d a) (sk a) (ct a) (ad a) request.2, + if F.isCramerShoupCollision H (d a) q.1 q.2 (ct a) (ad a) then + some (extractCramerShoupTuple q.1 q.2) else st) := by - simpa [cramerShoupCollisionPhase2Oracle, finderPhase2Oracle] using hresult - subst hresEq; dsimp only + simpa [cramerShoupCollisionPhase2Oracle, finderPhase2Oracle, q] using hresult + subst hresEq; dsimp only [q] refine ⟨trivial, Nat.le_add_right _ _, ?_⟩ split_ifs <;> omega) -/-- The DDH distinguisher constructed from a `CCA2AdversaryOf Msg` with message-to-group-word -encoding `encodeMsg : F.Desc × Msg → List Bool` ([BonehShoup2023], proof of Theorem 12.9, -Game 1 $\to$ Game 2, equation (12.22), printed pp. 490, 492, adapted to -$\mathcal{E}_{\mathrm{MCS}}$). -/ +/-- The DDH distinguisher constructed from an `ADCCA2AdversaryOf Msg AD` with message-to-group-word +encoding `encodeMsg : F.Desc × Msg → List Bool` and associated-data encoding +`encodeAD : F.Desc × AD → List Bool` ([BonehShoup2023], proof of Theorem 12.9, Game 1 $\to$ Game 2, +equation (12.22), printed pp. 490, 492, adapted to $\mathcal{E}_{\mathrm{MCS}}$ with associated +data). -/ noncomputable def cramerShoupMsgToDDHDistinguisher - {Msg : Type} (encodeMsg : F.Desc × Msg → List Bool) + (hasAD : Bool) {Msg AD : Type} (encodeMsg : F.Desc × Msg → List Bool) + (encodeAD : F.Desc × AD → List Bool) (H : F.Desc × List Bool → List Bool) (isElem : F.Desc × List Bool → Bool) - (adversary : CCA2AdversaryOf Msg) (bit : Bool) + (adversary : ADCCA2AdversaryOf Msg AD) (bit : Bool) (n : ℕ) (input : F.Desc × List Bool × List Bool × List Bool) : ProbComp Bool := F.sampleCramerShoupSK n input.1 >>= fun sk => let pk := F.encodeCramerShoupPublicKey input.1 input.2.1 sk ((OracleComp.interpret (ProbOracleComp.handler (fun m => StateT.lift (ProbComp.unifFin m)) - (F.cramerShoupPhase1Oracle H isElem input.1 sk)) + (F.cramerShoupPhase1Oracle hasAD H isElem input.1 sk)) (adversary.choose n pk)).run ()) >>= fun choiceOut => let choice := choiceOut.1 - let m₀ := encodeMsg (input.1, choice.1.1) - let m₁ := encodeMsg (input.1, choice.1.2) + let m₀ := encodeMsg (input.1, choice.1.1.1) + let m₁ := encodeMsg (input.1, choice.1.1.2) + let ad := encodeAD (input.1, choice.1.2) if isElem (input.1, m₀) && isElem (input.1, m₁) then let m := if bit then m₁ else m₀ - let ct := F.encodeCramerShoupChallenge H input.1 sk input.2.2.1 input.2.2.2 m + let ct := F.encodeCramerShoupChallenge H input.1 sk input.2.2.1 input.2.2.2 m ad ((OracleComp.interpret (ProbOracleComp.handler (fun m => StateT.lift (ProbComp.unifFin m)) - (F.cramerShoupPhase2Oracle H isElem input.1 sk ct)) + (F.cramerShoupPhase2Oracle hasAD H isElem input.1 sk ct ad)) (adversary.guess n choice.2 ct)).run ()) >>= fun guessOut => pure guessOut.1 else pure false -/-- The DDH distinguisher constructed from a group-message 1CCA adversary (`Msg = List Bool`, -`encodeMsg = Prod.snd`). -/ +/-- The DDH distinguisher constructed from a group-message 1CCA adversary with associated data +(`Msg = List Bool`, `encodeMsg = Prod.snd`, `encodeAD = fun p => encAD p.2`). -/ +noncomputable abbrev cramerShoupADToDDHDistinguisher + {AD : Type} (encAD : AD ↪ List Bool) + (H : F.Desc × List Bool → List Bool) (isElem : F.Desc × List Bool → Bool) + (adversary : ADCCA2AdversaryOf (List Bool) AD) (bit : Bool) + (n : ℕ) (input : F.Desc × List Bool × List Bool × List Bool) : ProbComp Bool := + F.cramerShoupMsgToDDHDistinguisher true Prod.snd (fun p => encAD p.2) H isElem adversary bit n + input + +/-- The DDH distinguisher constructed from an associated-data-free group-message 1CCA adversary +(`Msg = List Bool`, `encodeMsg = Prod.snd`, `ad = []`). -/ noncomputable abbrev cramerShoupToDDHDistinguisher (H : F.Desc × List Bool → List Bool) (isElem : F.Desc × List Bool → Bool) (adversary : CCA2AdversaryOf (List Bool)) (bit : Bool) (n : ℕ) (input : F.Desc × List Bool × List Bool × List Bool) : ProbComp Bool := - F.cramerShoupMsgToDDHDistinguisher Prod.snd H isElem adversary bit n input + F.cramerShoupMsgToDDHDistinguisher false Prod.snd (fun _ => []) H isElem adversary.toADUnit bit n + input -/-- `cramerShoupMsgToDDHDistinguisher` is uniform strict PPT whenever `encodeMsg`, `H`, and -`isElem` are computable in polynomial time and `adversary` is a strict oracle PPT 1CCA adversary. -/ +/-- `cramerShoupMsgToDDHDistinguisher` is uniform strict PPT whenever `encodeMsg`, `encodeAD`, `H`, +and `isElem` are computable in polynomial time and `adversary` is a strict oracle PPT 1CCA +adversary with associated data. -/ theorem isPPTOn_cramerShoupMsgToDDHDistinguisher - {Msg : Type} {encMsg : Msg ↪ List Bool} - {encodeMsg : F.Desc × Msg → List Bool} + (hasAD : Bool) {Msg AD : Type} {encMsg : Msg ↪ List Bool} {encAD : AD ↪ List Bool} + {encodeMsg : F.Desc × Msg → List Bool} {encodeAD : F.Desc × AD → List Bool} {H : F.Desc × List Bool → List Bool} {isElem : F.Desc × List Bool → Bool} - {adversary : CCA2AdversaryOf Msg} + {adversary : ADCCA2AdversaryOf Msg AD} (hencMsg : ComputableInPolyTime encodeMsg (encPair F.encDesc encMsg) encBits) + (hencAD : ComputableInPolyTime encodeAD (encPair F.encDesc encAD) encBits) (hH : ComputableInPolyTime H (encPair F.encDesc encBits) encBits) (hisElem : ComputableInPolyTime isElem (encPair F.encDesc encBits) encBool) - (hA : adversary.IsPPT encMsg) (bit : Bool) : + (hA : adversary.IsPPT encMsg encAD) (bit : Bool) : IsPPTOn (encPair encUnary (encPair F.encDesc (encPair encBits (encPair encBits encBits)))) encBool (fun p => - F.cramerShoupMsgToDDHDistinguisher encodeMsg H isElem adversary bit p.1 p.2) := by + F.cramerShoupMsgToDDHDistinguisher hasAD encodeMsg encodeAD H isElem adversary bit + p.1 p.2) := by let E0 := encPair encUnary (encPair F.encDesc (encPair encBits (encPair encBits encBits))) let E1 := encPair E0 encCramerShoupSK - let EChoice := encPair (encPair (encPair encMsg encMsg) encBits) (encFinEnum Unit) + let EChoice := encPair (encPair (encPair (encPair encMsg encMsg) encAD) encBits) (encFinEnum Unit) have hn0 := ComputableTidilyInPolyTime.fst encUnary (encPair F.encDesc (encPair encBits (encPair encBits encBits))) have hd0 := (ComputableTidilyInPolyTime.snd encUnary @@ -676,34 +760,50 @@ theorem isPPTOn_cramerShoupMsgToDDHDistinguisher have hsk1 := ComputableTidilyInPolyTime.snd E0 encCramerShoupSK have hpk := F.computableTidilyInPolyTime_encodeCramerShoupPublicKey hp1_0.snd'.fst' hp1_0.snd'.snd'.fst' hsk1 - have hphase1 := F.isPPTOn_run_cramerShoupPhase1Oracle hH hisElem hA + have hphase1 := F.isPPTOn_run_cramerShoupPhase1Oracle hasAD hH hisElem hA hp1_0.fst' hp1_0.snd'.fst' hsk1 hpk have hand : ComputableTidilyInPolyTime (fun q : Bool × Bool => q.1 && q.2) (encPair encBool encBool) encBool := by polytime have hp2_1 := ComputableTidilyInPolyTime.fst E1 EChoice have hp2_2 := ComputableTidilyInPolyTime.snd E1 EChoice have hd2 := hp2_1.fst'.snd'.fst' - have hm₀ := computableTidilyInPolyTime_comp hencMsg.tidy (hd2.prod hp2_2.fst'.fst'.fst') - have hm₁ := computableTidilyInPolyTime_comp hencMsg.tidy (hd2.prod hp2_2.fst'.fst'.snd') + have hm₀ := computableTidilyInPolyTime_comp hencMsg.tidy (hd2.prod hp2_2.fst'.fst'.fst'.fst') + have hm₁ := computableTidilyInPolyTime_comp hencMsg.tidy (hd2.prod hp2_2.fst'.fst'.fst'.snd') + have had := computableTidilyInPolyTime_comp hencAD.tidy (hd2.prod hp2_2.fst'.fst'.snd') have hvalid := computableTidilyInPolyTime_comp hand ((computableTidilyInPolyTime_comp hisElem.tidy (hd2.prod hm₀)).prod (computableTidilyInPolyTime_comp hisElem.tidy (hd2.prod hm₁))) have hm : ComputableTidilyInPolyTime - (fun p => if bit then encodeMsg (p.1.1.2.1, p.2.1.1.2) - else encodeMsg (p.1.1.2.1, p.2.1.1.1)) (encPair E1 EChoice) encBits := by + (fun p => if bit then encodeMsg (p.1.1.2.1, p.2.1.1.1.2) + else encodeMsg (p.1.1.2.1, p.2.1.1.1.1)) (encPair E1 EChoice) encBits := by cases bit · exact hm₀ · exact hm₁ have hct := F.computableTidilyInPolyTime_encodeCramerShoupChallenge hH.tidy - hd2 hp2_1.snd' hp2_1.fst'.snd'.snd'.snd'.fst' hp2_1.fst'.snd'.snd'.snd'.snd' hm - have hphase2 := F.isPPTOn_run_cramerShoupPhase2Oracle hH hisElem hA - hp2_1.fst'.fst' hd2 hp2_1.snd' hp2_2.fst'.snd' hct + hd2 hp2_1.snd' hp2_1.fst'.snd'.snd'.snd'.fst' hp2_1.fst'.snd'.snd'.snd'.snd' hm had + have hphase2 := F.isPPTOn_run_cramerShoupPhase2Oracle hasAD hH hisElem hA + hp2_1.fst'.fst' hd2 hp2_1.snd' hp2_2.fst'.snd' hct had unfold cramerShoupMsgToDDHDistinguisher refine hsk.bind_with (hphase1.bind_with ?_) exact IsPPTOn.cond hvalid (hphase2.bind_with (ComputableTidilyInPolyTime.isPPTOn (by fun_prop))) (ComputableTidilyInPolyTime.isPPTOn (.const false)) +/-- `cramerShoupADToDDHDistinguisher` is uniform strict PPT whenever `H` and `isElem` are +computable in polynomial time and `adversary` is a strict oracle PPT 1CCA adversary with associated +data. -/ +theorem isPPTOn_cramerShoupADToDDHDistinguisher + {AD : Type} {encAD : AD ↪ List Bool} + {H : F.Desc × List Bool → List Bool} {isElem : F.Desc × List Bool → Bool} + {adversary : ADCCA2AdversaryOf (List Bool) AD} + (hH : ComputableInPolyTime H (encPair F.encDesc encBits) encBits) + (hisElem : ComputableInPolyTime isElem (encPair F.encDesc encBits) encBool) + (hA : adversary.IsPPT encBits encAD) (bit : Bool) : + IsPPTOn (encPair encUnary (encPair F.encDesc (encPair encBits (encPair encBits encBits)))) + encBool (fun p => F.cramerShoupADToDDHDistinguisher encAD H isElem adversary bit p.1 p.2) := + F.isPPTOn_cramerShoupMsgToDDHDistinguisher true (by fun_prop) + (.of_tidy ((ComputableTidilyInPolyTime.encode encAD).comp (by fun_prop))) hH hisElem hA bit + /-- `cramerShoupToDDHDistinguisher` is uniform strict PPT whenever `H` and `isElem` are computable in polynomial time and `adversary` is a strict oracle PPT 1CCA adversary. -/ theorem isPPTOn_cramerShoupToDDHDistinguisher @@ -714,16 +814,18 @@ theorem isPPTOn_cramerShoupToDDHDistinguisher (hA : adversary.IsPPT encBits) (bit : Bool) : IsPPTOn (encPair encUnary (encPair F.encDesc (encPair encBits (encPair encBits encBits)))) encBool (fun p => F.cramerShoupToDDHDistinguisher H isElem adversary bit p.1 p.2) := - F.isPPTOn_cramerShoupMsgToDDHDistinguisher (by fun_prop) hH hisElem hA bit + F.isPPTOn_cramerShoupMsgToDDHDistinguisher false (by fun_prop) (.of_tidy (.const [])) + hH hisElem hA.toADUnit bit -/-- The setup-parameterized collision finder constructed from a `CCA2AdversaryOf Msg` with -message-to-group-word encoding `encodeMsg : F.Desc × Msg → List Bool` ([BonehShoup2023], proof of -Theorem 12.9, Game 4, equations (12.24)–(12.25), printed p. 492, adapted to -$\mathcal{E}_{\mathrm{MCS}}$). -/ +/-- The setup-parameterized collision finder constructed from an `ADCCA2AdversaryOf Msg AD` with +message-to-group-word encoding `encodeMsg : F.Desc × Msg → List Bool` and associated-data encoding +`encodeAD : F.Desc × AD → List Bool` ([BonehShoup2023], proof of Theorem 12.9, Game 4, equations +(12.24)–(12.25), printed p. 492, adapted to $\mathcal{E}_{\mathrm{MCS}}$ with associated data). -/ noncomputable def cramerShoupMsgToCollisionFinder - {Msg : Type} (encodeMsg : F.Desc × Msg → List Bool) + (hasAD : Bool) {Msg AD : Type} (encodeMsg : F.Desc × Msg → List Bool) + (encodeAD : F.Desc × AD → List Bool) (H : F.Desc × List Bool → List Bool) (isElem : F.Desc × List Bool → Bool) - (adversary : CCA2AdversaryOf Msg) (bit : Bool) : + (adversary : ADCCA2AdversaryOf Msg AD) (bit : Bool) : CollisionFinderFor F.Desc := fun n d => F.sampleCramerShoupTripleExponents n d >>= fun abc => @@ -735,50 +837,63 @@ noncomputable def cramerShoupMsgToCollisionFinder let pk := F.encodeCramerShoupPublicKey d u sk ((OracleComp.interpret (ProbOracleComp.handler (fun m => StateT.lift (ProbComp.unifFin m)) - (F.cramerShoupPhase1Oracle H isElem d sk)) + (F.cramerShoupPhase1Oracle hasAD H isElem d sk)) (adversary.choose n pk)).run ()) >>= fun choiceOut => let choice := choiceOut.1 - let m₀ := encodeMsg (d, choice.1.1) - let m₁ := encodeMsg (d, choice.1.2) + let m₀ := encodeMsg (d, choice.1.1.1) + let m₁ := encodeMsg (d, choice.1.1.2) + let ad := encodeAD (d, choice.1.2) if isElem (d, m₀) && isElem (d, m₁) then let m := if bit then m₁ else m₀ - let ct := F.encodeCramerShoupChallenge H d sk v w m + let ct := F.encodeCramerShoupChallenge H d sk v w m ad ((OracleComp.interpret (ProbOracleComp.handler (fun m => StateT.lift (ProbComp.unifFin m)) - (F.cramerShoupCollisionPhase2Oracle H isElem d sk ct)) + (F.cramerShoupCollisionPhase2Oracle hasAD H isElem d sk ct ad)) (adversary.guess n choice.2 ct)).run none) >>= fun out => - let target := extractCramerShoupTriple ct + let target := extractCramerShoupTuple ct ad pure (out.2.getD target, target) else pure ([], []) -/-- The setup-parameterized collision finder constructed from a group-message 1CCA adversary -(`Msg = List Bool`, `encodeMsg = Prod.snd`). -/ +/-- The setup-parameterized collision finder constructed from a group-message 1CCA adversary with +associated data (`Msg = List Bool`, `encodeMsg = Prod.snd`, `encodeAD = fun p => encAD p.2`). -/ +noncomputable abbrev cramerShoupADToCollisionFinder + {AD : Type} (encAD : AD ↪ List Bool) + (H : F.Desc × List Bool → List Bool) (isElem : F.Desc × List Bool → Bool) + (adversary : ADCCA2AdversaryOf (List Bool) AD) (bit : Bool) : + CollisionFinderFor F.Desc := + F.cramerShoupMsgToCollisionFinder true Prod.snd (fun p => encAD p.2) H isElem adversary bit + +/-- The setup-parameterized collision finder constructed from an associated-data-free group-message +1CCA adversary (`Msg = List Bool`, `encodeMsg = Prod.snd`, `ad = []`). -/ noncomputable abbrev cramerShoupToCollisionFinder (H : F.Desc × List Bool → List Bool) (isElem : F.Desc × List Bool → Bool) (adversary : CCA2AdversaryOf (List Bool)) (bit : Bool) : CollisionFinderFor F.Desc := - F.cramerShoupMsgToCollisionFinder Prod.snd H isElem adversary bit + F.cramerShoupMsgToCollisionFinder false Prod.snd (fun _ => []) H isElem adversary.toADUnit bit -/-- `cramerShoupMsgToCollisionFinder` is uniform strict PPT whenever `encodeMsg`, `H`, and -`isElem` are computable in polynomial time and `adversary` is a strict oracle PPT 1CCA adversary. -/ +/-- `cramerShoupMsgToCollisionFinder` is uniform strict PPT whenever `encodeMsg`, `encodeAD`, `H`, +and `isElem` are computable in polynomial time and `adversary` is a strict oracle PPT 1CCA +adversary with associated data. -/ theorem isPPTOn_cramerShoupMsgToCollisionFinder - {Msg : Type} {encMsg : Msg ↪ List Bool} - {encodeMsg : F.Desc × Msg → List Bool} + (hasAD : Bool) {Msg AD : Type} {encMsg : Msg ↪ List Bool} {encAD : AD ↪ List Bool} + {encodeMsg : F.Desc × Msg → List Bool} {encodeAD : F.Desc × AD → List Bool} {H : F.Desc × List Bool → List Bool} {isElem : F.Desc × List Bool → Bool} - {adversary : CCA2AdversaryOf Msg} + {adversary : ADCCA2AdversaryOf Msg AD} (hencMsg : ComputableInPolyTime encodeMsg (encPair F.encDesc encMsg) encBits) + (hencAD : ComputableInPolyTime encodeAD (encPair F.encDesc encAD) encBits) (hH : ComputableInPolyTime H (encPair F.encDesc encBits) encBits) (hisElem : ComputableInPolyTime isElem (encPair F.encDesc encBits) encBool) - (hA : adversary.IsPPT encMsg) (bit : Bool) : + (hA : adversary.IsPPT encMsg encAD) (bit : Bool) : IsPPTOn (encPair encUnary F.encDesc) (encPair encBits encBits) (fun p => - F.cramerShoupMsgToCollisionFinder encodeMsg H isElem adversary bit p.1 p.2) := by + F.cramerShoupMsgToCollisionFinder hasAD encodeMsg encodeAD H isElem adversary bit + p.1 p.2) := by let E0 := encPair encUnary F.encDesc let EExp := encPair encNat (encPair encNat encNat) let E1 := encPair E0 EExp let E2 := encPair E1 encCramerShoupSK - let EChoice := encPair (encPair (encPair encMsg encMsg) encBits) (encFinEnum Unit) + let EChoice := encPair (encPair (encPair (encPair encMsg encMsg) encAD) encBits) (encFinEnum Unit) let E3 := encPair E2 EChoice have habc := F.isPPTOn_sampleCramerShoupTripleExponents (ComputableTidilyInPolyTime.fst encUnary F.encDesc) @@ -792,15 +907,16 @@ theorem isPPTOn_cramerShoupMsgToCollisionFinder have hgen2 := computableTidilyInPolyTime_comp F.computableInPolyTime_generator.tidy hd2 have hu := computableTidilyInPolyTime_comp hsmul (hd2.prod (hp2_1.snd'.fst'.prod hgen2)) have hpk := F.computableTidilyInPolyTime_encodeCramerShoupPublicKey hd2 hu hsk2 - have hphase1 := F.isPPTOn_run_cramerShoupPhase1Oracle hH hisElem hA + have hphase1 := F.isPPTOn_run_cramerShoupPhase1Oracle hasAD hH hisElem hA hp2_1.fst'.fst' hd2 hsk2 hpk have hand : ComputableTidilyInPolyTime (fun q : Bool × Bool => q.1 && q.2) (encPair encBool encBool) encBool := by polytime have hp3_1 := ComputableTidilyInPolyTime.fst E2 EChoice have hp3_2 := ComputableTidilyInPolyTime.snd E2 EChoice have hd3 := hp3_1.fst'.fst'.snd' - have hm₀ := computableTidilyInPolyTime_comp hencMsg.tidy (hd3.prod hp3_2.fst'.fst'.fst') - have hm₁ := computableTidilyInPolyTime_comp hencMsg.tidy (hd3.prod hp3_2.fst'.fst'.snd') + have hm₀ := computableTidilyInPolyTime_comp hencMsg.tidy (hd3.prod hp3_2.fst'.fst'.fst'.fst') + have hm₁ := computableTidilyInPolyTime_comp hencMsg.tidy (hd3.prod hp3_2.fst'.fst'.fst'.snd') + have had := computableTidilyInPolyTime_comp hencAD.tidy (hd3.prod hp3_2.fst'.fst'.snd') have hvalid := computableTidilyInPolyTime_comp hand ((computableTidilyInPolyTime_comp hisElem.tidy (hd3.prod hm₀)).prod (computableTidilyInPolyTime_comp hisElem.tidy (hd3.prod hm₁))) @@ -810,20 +926,21 @@ theorem isPPTOn_cramerShoupMsgToCollisionFinder have hw := computableTidilyInPolyTime_comp hsmul (hd3.prod (hp3_1.fst'.snd'.snd'.snd'.prod hgen3)) have hm : ComputableTidilyInPolyTime - (fun p => if bit then encodeMsg (p.1.1.1.2, p.2.1.1.2) - else encodeMsg (p.1.1.1.2, p.2.1.1.1)) E3 encBits := by + (fun p => if bit then encodeMsg (p.1.1.1.2, p.2.1.1.1.2) + else encodeMsg (p.1.1.1.2, p.2.1.1.1.1)) E3 encBits := by cases bit · exact hm₀ · exact hm₁ have hct := F.computableTidilyInPolyTime_encodeCramerShoupChallenge hH.tidy - hd3 hp3_1.snd' hv hw hm - have hphase2c := F.isPPTOn_run_cramerShoupCollisionPhase2Oracle hH hisElem hA - hp3_1.fst'.fst'.fst' hd3 hp3_1.snd' hp3_2.fst'.snd' hct + hd3 hp3_1.snd' hv hw hm had + have hphase2c := F.isPPTOn_run_cramerShoupCollisionPhase2Oracle hasAD hH hisElem hA + hp3_1.fst'.fst'.fst' hd3 hp3_1.snd' hp3_2.fst'.snd' hct had have hp4_1 := ComputableTidilyInPolyTime.fst E3 (encPair encBool (encOption encBits)) have hp4_2 := ComputableTidilyInPolyTime.snd E3 (encPair encBool (encOption encBits)) have htarget := - computableTidilyInPolyTime_comp computableTidilyInPolyTime_extractCramerShoupTriple - (computableTidilyInPolyTime_comp hct hp4_1) + computableTidilyInPolyTime_comp computableTidilyInPolyTime_extractCramerShoupTuple + ((computableTidilyInPolyTime_comp hct hp4_1).prod + (computableTidilyInPolyTime_comp had hp4_1)) have houtPair := (ComputableTidilyInPolyTime.optionGetDWithInput hp4_2.snd' htarget).prod htarget unfold cramerShoupMsgToCollisionFinder refine habc.bind_with (hsk.bind_with (hphase1.bind_with ?_)) @@ -831,6 +948,20 @@ theorem isPPTOn_cramerShoupMsgToCollisionFinder (hphase2c.bind_with houtPair.isPPTOn) (ComputableTidilyInPolyTime.isPPTOn (.const (([] : List Bool), []))) +/-- `cramerShoupADToCollisionFinder` is uniform strict PPT whenever `H` and `isElem` are computable +in polynomial time and `adversary` is a strict oracle PPT 1CCA adversary with associated data. -/ +theorem isPPTOn_cramerShoupADToCollisionFinder + {AD : Type} {encAD : AD ↪ List Bool} + {H : F.Desc × List Bool → List Bool} {isElem : F.Desc × List Bool → Bool} + {adversary : ADCCA2AdversaryOf (List Bool) AD} + (hH : ComputableInPolyTime H (encPair F.encDesc encBits) encBits) + (hisElem : ComputableInPolyTime isElem (encPair F.encDesc encBits) encBool) + (hA : adversary.IsPPT encBits encAD) (bit : Bool) : + IsPPTOn (encPair encUnary F.encDesc) (encPair encBits encBits) + (fun p => F.cramerShoupADToCollisionFinder encAD H isElem adversary bit p.1 p.2) := + F.isPPTOn_cramerShoupMsgToCollisionFinder true (by fun_prop) + (.of_tidy ((ComputableTidilyInPolyTime.encode encAD).comp (by fun_prop))) hH hisElem hA bit + /-- `cramerShoupToCollisionFinder` is uniform strict PPT whenever `H` and `isElem` are computable in polynomial time and `adversary` is a strict oracle PPT 1CCA adversary. -/ theorem isPPTOn_cramerShoupToCollisionFinder @@ -841,7 +972,8 @@ theorem isPPTOn_cramerShoupToCollisionFinder (hA : adversary.IsPPT encBits) (bit : Bool) : IsPPTOn (encPair encUnary F.encDesc) (encPair encBits encBits) (fun p => F.cramerShoupToCollisionFinder H isElem adversary bit p.1 p.2) := - F.isPPTOn_cramerShoupMsgToCollisionFinder (by fun_prop) hH hisElem hA bit + F.isPPTOn_cramerShoupMsgToCollisionFinder false (by fun_prop) (.of_tidy (.const [])) + hH hisElem hA.toADUnit bit /-- `encodeCramerShoupPublicKey` depends on `sk` only through its residues modulo `F.order d`. -/ theorem encodeCramerShoupPublicKey_eq_skNatOfZMod (d : F.Desc) @@ -856,13 +988,13 @@ theorem encodeCramerShoupPublicKey_eq_skNatOfZMod (d : F.Desc) /-- `encodeCramerShoupChallenge` depends on `sk` only through its residues modulo `F.order d`. -/ theorem encodeCramerShoupChallenge_eq_skNatOfZMod (H : F.Desc × List Bool → List Bool) (d : F.Desc) - (sk : (ℕ × ℕ) × ((ℕ × ℕ) × (ℕ × ℕ))) (v w : F.Carrier d) (m : List Bool) : - F.encodeCramerShoupChallenge H d sk (F.encode d v) (F.encode d w) m = + (sk : (ℕ × ℕ) × ((ℕ × ℕ) × (ℕ × ℕ))) (v w : F.Carrier d) (m ad : List Bool) : + F.encodeCramerShoupChallenge H d sk (F.encode d v) (F.encode d w) m ad = F.encodeCramerShoupChallenge H d (F.skNatOfZMod d (sk.1.1 : ZMod (F.order d)) (sk.1.2 : ZMod (F.order d)) (sk.2.1.1 : ZMod (F.order d)) (sk.2.1.2 : ZMod (F.order d)) (sk.2.2.1 : ZMod (F.order d)) (sk.2.2.2 : ZMod (F.order d))) - (F.encode d v) (F.encode d w) m := by + (F.encode d v) (F.encode d w) m ad := by simp only [encodeCramerShoupChallenge, skNatOfZMod, encodeProjHash_encode, encodeProjHash2_encode, ZMod.natCast_zmod_val] @@ -870,12 +1002,12 @@ theorem encodeCramerShoupChallenge_eq_skNatOfZMod theorem decodeCramerShoup_eq_skNatOfZMod (H : F.Desc × List Bool → List Bool) (isElem : F.Desc × List Bool → Bool) (hmem : ∀ d x, isElem (d, x) = true ↔ x ∈ Set.range (F.encode d)) - (d : F.Desc) (sk : (ℕ × ℕ) × ((ℕ × ℕ) × (ℕ × ℕ))) (ct : List Bool) : - F.decodeCramerShoup H isElem d sk ct = + (d : F.Desc) (sk : (ℕ × ℕ) × ((ℕ × ℕ) × (ℕ × ℕ))) (ct ad : List Bool) : + F.decodeCramerShoup H isElem d sk ct ad = F.decodeCramerShoup H isElem d (F.skNatOfZMod d (sk.1.1 : ZMod (F.order d)) (sk.1.2 : ZMod (F.order d)) (sk.2.1.1 : ZMod (F.order d)) (sk.2.1.2 : ZMod (F.order d)) - (sk.2.2.1 : ZMod (F.order d)) (sk.2.2.2 : ZMod (F.order d))) ct := by + (sk.2.2.1 : ZMod (F.order d)) (sk.2.2.2 : ZMod (F.order d))) ct ad := by unfold decodeCramerShoup by_cases hvw : isElem (d, (decodeBits4 ct).1) = true ∧ isElem (d, (decodeBits4 ct).2.1) = true @@ -891,46 +1023,46 @@ theorem decodeCramerShoup_eq_skNatOfZMod /-- `cramerShoupPhase1Oracle` depends on `sk` only through its residues modulo `F.order d`. -/ theorem cramerShoupPhase1Oracle_eq_skNatOfZMod - (H : F.Desc × List Bool → List Bool) (isElem : F.Desc × List Bool → Bool) + (hasAD : Bool) (H : F.Desc × List Bool → List Bool) (isElem : F.Desc × List Bool → Bool) (hmem : ∀ d x, isElem (d, x) = true ↔ x ∈ Set.range (F.encode d)) (d : F.Desc) (sk : (ℕ × ℕ) × ((ℕ × ℕ) × (ℕ × ℕ))) : - F.cramerShoupPhase1Oracle H isElem d sk = - F.cramerShoupPhase1Oracle H isElem d + F.cramerShoupPhase1Oracle hasAD H isElem d sk = + F.cramerShoupPhase1Oracle hasAD H isElem d (F.skNatOfZMod d (sk.1.1 : ZMod (F.order d)) (sk.1.2 : ZMod (F.order d)) (sk.2.1.1 : ZMod (F.order d)) (sk.2.1.2 : ZMod (F.order d)) (sk.2.2.1 : ZMod (F.order d)) (sk.2.2.2 : ZMod (F.order d))) := by funext req s simp only [cramerShoupPhase1Oracle, cramerShoupPhase1Reply, - F.decodeCramerShoup_eq_skNatOfZMod H isElem hmem d sk req.2] + F.decodeCramerShoup_eq_skNatOfZMod H isElem hmem d sk] /-- `cramerShoupPhase2Oracle` depends on `sk` only through its residues modulo `F.order d`. -/ theorem cramerShoupPhase2Oracle_eq_skNatOfZMod - (H : F.Desc × List Bool → List Bool) (isElem : F.Desc × List Bool → Bool) + (hasAD : Bool) (H : F.Desc × List Bool → List Bool) (isElem : F.Desc × List Bool → Bool) (hmem : ∀ d x, isElem (d, x) = true ↔ x ∈ Set.range (F.encode d)) - (d : F.Desc) (sk : (ℕ × ℕ) × ((ℕ × ℕ) × (ℕ × ℕ))) (ct : List Bool) : - F.cramerShoupPhase2Oracle H isElem d sk ct = - F.cramerShoupPhase2Oracle H isElem d + (d : F.Desc) (sk : (ℕ × ℕ) × ((ℕ × ℕ) × (ℕ × ℕ))) (ct ad : List Bool) : + F.cramerShoupPhase2Oracle hasAD H isElem d sk ct ad = + F.cramerShoupPhase2Oracle hasAD H isElem d (F.skNatOfZMod d (sk.1.1 : ZMod (F.order d)) (sk.1.2 : ZMod (F.order d)) (sk.2.1.1 : ZMod (F.order d)) (sk.2.1.2 : ZMod (F.order d)) - (sk.2.2.1 : ZMod (F.order d)) (sk.2.2.2 : ZMod (F.order d))) ct := by + (sk.2.2.1 : ZMod (F.order d)) (sk.2.2.2 : ZMod (F.order d))) ct ad := by funext req s simp only [cramerShoupPhase2Oracle, cramerShoupPhase2Reply, - F.decodeCramerShoup_eq_skNatOfZMod H isElem hmem d sk req.2] + F.decodeCramerShoup_eq_skNatOfZMod H isElem hmem d sk] /-- `cramerShoupCollisionPhase2Oracle` depends on `sk` only through its residues modulo `F.order d`. -/ theorem cramerShoupCollisionPhase2Oracle_eq_skNatOfZMod - (H : F.Desc × List Bool → List Bool) (isElem : F.Desc × List Bool → Bool) + (hasAD : Bool) (H : F.Desc × List Bool → List Bool) (isElem : F.Desc × List Bool → Bool) (hmem : ∀ d x, isElem (d, x) = true ↔ x ∈ Set.range (F.encode d)) - (d : F.Desc) (sk : (ℕ × ℕ) × ((ℕ × ℕ) × (ℕ × ℕ))) (ct : List Bool) : - F.cramerShoupCollisionPhase2Oracle H isElem d sk ct = - F.cramerShoupCollisionPhase2Oracle H isElem d + (d : F.Desc) (sk : (ℕ × ℕ) × ((ℕ × ℕ) × (ℕ × ℕ))) (ct ad : List Bool) : + F.cramerShoupCollisionPhase2Oracle hasAD H isElem d sk ct ad = + F.cramerShoupCollisionPhase2Oracle hasAD H isElem d (F.skNatOfZMod d (sk.1.1 : ZMod (F.order d)) (sk.1.2 : ZMod (F.order d)) (sk.2.1.1 : ZMod (F.order d)) (sk.2.1.2 : ZMod (F.order d)) - (sk.2.2.1 : ZMod (F.order d)) (sk.2.2.2 : ZMod (F.order d))) ct := by + (sk.2.2.1 : ZMod (F.order d)) (sk.2.2.2 : ZMod (F.order d))) ct ad := by funext req s simp only [cramerShoupCollisionPhase2Oracle, finderPhase2Oracle, cramerShoupCollisionPhase2Reply, - F.decodeCramerShoup_eq_skNatOfZMod H isElem hmem d sk req.2] + F.decodeCramerShoup_eq_skNatOfZMod H isElem hmem d sk] /-- Word-level public-key encryption under `cramerShoupPublicKey d α σ τ σ₁ τ₁ σ₂ τ₂` coincides with `encodeCramerShoupChallenge` on `(v, w) = (β • g, (α * β) • g)` ([BonehShoup2023], Section 12.5.3, @@ -938,13 +1070,13 @@ Game 0 $\to$ Game 1, equation (12.21), printed p. 490). -/ @[simp] theorem encodeCramerShoupEncrypt_publicKey_eq_challenge (H : F.Desc × List Bool → List Bool) (d : F.Desc) - (α β σ τ σ₁ τ₁ σ₂ τ₂ : ZMod (F.order d)) (m : List Bool) : + (α β σ τ σ₁ τ₁ σ₂ τ₂ : ZMod (F.order d)) (m ad : List Bool) : let pkVal := F.cramerShoupPublicKey d α σ τ σ₁ τ₁ σ₂ τ₂ F.encodeCramerShoupEncrypt H d (F.encode d pkVal.1, F.encode d pkVal.2.1, F.encode d pkVal.2.2.1, F.encode d pkVal.2.2.2) - m β.val = + m ad β.val = F.encodeCramerShoupChallenge H d (F.skNatOfZMod d σ τ σ₁ τ₁ σ₂ τ₂) - (F.encode d (β • F.generator d)) (F.encode d ((α * β) • F.generator d)) m := by + (F.encode d (β • F.generator d)) (F.encode d ((α * β) • F.generator d)) m ad := by have hαβ : (α * β) • F.generator d = β • (α • F.generator d) := by rw [smul_smul, mul_comm] rw [hαβ] simp only [encodeCramerShoupEncrypt, encodeCramerShoupChallenge, cramerShoupPublicKey, @@ -952,7 +1084,7 @@ theorem encodeCramerShoupEncrypt_publicKey_eq_challenge encodeProjHash_encode, encodeProjHash2_encode, projHash_smul_generator, projHash2_smul_generator] -/-! ### `Encryption.cca2GameOn` and `Encryption.INDCCA2On` instantiation -/ +/-! ### `Encryption.cca2ADGameOn` and `Encryption.cca2GameOn` instantiation -/ /-- Unpack a canonically framed 6-tuple secret key `((s, t), ((s₁, t₁), (s₂, t₂)))` of natural numbers. -/ @@ -985,9 +1117,9 @@ private theorem computableTidilyInPolyTime_decodeCramerShoupSK : exact (hpair.comp hp1).prod ((hpair.comp (hp1.comp hp2)).prod (hpair.comp (hp2.comp hp2))) -/-- Challenge-pair group-membership admissibility predicate for `Encryption.cca2GameOn` -([BonehShoup2023], Section 12.5.3 & Exercise 12.27): both challenge words `m₀` and `m₁` must -belong to the group `F.Carrier (d n)`. -/ +/-- Challenge-pair group-membership admissibility predicate for `Encryption.cca2ADGameOn` and +`Encryption.cca2GameOn` ([BonehShoup2023], Section 12.5.3 & Exercise 12.27): both challenge words +`m₀` and `m₁` must belong to the group `F.Carrier (d n)`. -/ def cramerShoupValid (isElem : F.Desc × List Bool → Bool) (d : ℕ → F.Desc) (n : ℕ) (_pk m₀ m₁ : List Bool) : Bool := isElem (d n, m₀) && isElem (d n, m₁) @@ -1001,20 +1133,39 @@ noncomputable def cramerShoupKeyGenProb (d : ℕ → F.Desc) (n : ℕ) : let u := F.smul (d n, a, F.encode (d n) (F.generator (d n))) pure (F.encodeCramerShoupPublicKey (d n) u sk, encCramerShoupSK sk) -/-- Probabilistic polynomial-time Cramer–Shoup encryption for a group family `d : ℕ → F.Desc`, -sampling the encryption exponent via `sampleModNat`. -/ +/-- Probabilistic polynomial-time Cramer–Shoup encryption with associated data for a group family +`d : ℕ → F.Desc`, sampling the encryption exponent via `sampleModNat`. -/ +noncomputable def cramerShoupADEncryptProb + {AD : Type} (encAD : AD ↪ List Bool) + (H : F.Desc × List Bool → List Bool) (d : ℕ → F.Desc) + (n : ℕ) (pk m : List Bool) (ad : AD) : ProbComp (List Bool) := + F.sampleExp n (d n) >>= fun b => + pure (F.encodeCramerShoupEncrypt H (d n) (decodeBits4 (dropFrame pk)) m (encAD ad) b) + +/-- Probabilistic polynomial-time Cramer–Shoup encryption for a group family `d : ℕ → F.Desc` +(associated-data-free specialization `ad = []`), sampling the encryption exponent via +`sampleModNat`. -/ noncomputable def cramerShoupEncryptProb (H : F.Desc × List Bool → List Bool) (d : ℕ → F.Desc) (n : ℕ) (pk m : List Bool) : ProbComp (List Bool) := F.sampleExp n (d n) >>= fun b => - pure (F.encodeCramerShoupEncrypt H (d n) (decodeBits4 (dropFrame pk)) m b) + pure (F.encodeCramerShoupEncrypt H (d n) (decodeBits4 (dropFrame pk)) m [] b) + +/-- Deterministic polynomial-time Cramer–Shoup decryption with associated data for +`Encryption.cca2ADGameOn`. -/ +def cramerShoupADDecryptOpt + (H : F.Desc × List Bool → List Bool) + (isElem : F.Desc × List Bool → Bool) + (d : ℕ → F.Desc) (n : ℕ) (skBits ct ad : List Bool) : Option (List Bool) := + F.decodeCramerShoup H isElem (d n) (decodeCramerShoupSK skBits) ct ad -/-- Deterministic polynomial-time Cramer–Shoup decryption for `Encryption.cca2GameOn`. -/ +/-- Deterministic polynomial-time Cramer–Shoup decryption for `Encryption.cca2GameOn` +(associated-data-free specialization `ad = []`). -/ def cramerShoupDecryptOpt (H : F.Desc × List Bool → List Bool) (isElem : F.Desc × List Bool → Bool) (d : ℕ → F.Desc) (n : ℕ) (skBits ct : List Bool) : Option (List Bool) := - F.decodeCramerShoup H isElem (d n) (decodeCramerShoupSK skBits) ct + F.cramerShoupADDecryptOpt H isElem d n skBits ct [] /-- `cramerShoupValid` is computable in polynomial time whenever `isElem` and `d` are. -/ theorem computableInPolyTime_cramerShoupValid @@ -1066,6 +1217,29 @@ theorem isPPTOn_cramerShoupKeyGenProb {d : ℕ → F.Desc} ((F.isPPTOn_sampleCramerShoupSK (by fun_prop) (hd.tidy.comp (by fun_prop))).bind_with (hpk.prod hskBits).isPPTOn) +/-- `cramerShoupADEncryptProb` is uniformly strict PPT whenever `H` and `d` are computable in +polynomial time. -/ +theorem isPPTOn_cramerShoupADEncryptProb + {AD : Type} (encAD : AD ↪ List Bool) + {H : F.Desc × List Bool → List Bool} {d : ℕ → F.Desc} + (hH : ComputableInPolyTime H (encPair F.encDesc encBits) encBits) + (hd : ComputableInPolyTime d encUnary F.encDesc) : + IsPPTOn (encPair encUnary (encPair encBits (encPair encBits encAD))) encBits + (fun p => F.cramerShoupADEncryptProb encAD H d p.1 p.2.1 p.2.2.1 p.2.2.2) := by + let E0 := encPair encUnary (encPair encBits (encPair encBits encAD)) + let E1 := encPair E0 encNat + have hct : ComputableTidilyInPolyTime + (fun p : (ℕ × List Bool × List Bool × AD) × ℕ => + F.encodeCramerShoupEncrypt H (d p.1.1) (decodeBits4 (dropFrame p.1.2.1)) + p.1.2.2.1 (encAD p.1.2.2.2) p.2) + E1 encBits := + F.computableTidilyInPolyTime_encodeCramerShoupEncrypt hH.tidy (hd.tidy.comp (by fun_prop)) + (computableTidilyInPolyTime_decodeBits4.comp (by fun_prop)) (by fun_prop) + ((ComputableTidilyInPolyTime.encode encAD).comp (by fun_prop)) (by fun_prop) + unfold cramerShoupADEncryptProb + exact (F.isPPTOn_sampleExp (by fun_prop) + (hd.comp (by fun_prop))).bind_with hct.isPPTOn + /-- `cramerShoupEncryptProb` is uniformly strict PPT whenever `H` and `d` are computable in polynomial time. -/ theorem isPPTOn_cramerShoupEncryptProb @@ -1078,14 +1252,32 @@ theorem isPPTOn_cramerShoupEncryptProb let E1 := encPair E0 encNat have hct : ComputableTidilyInPolyTime (fun p : (ℕ × List Bool × List Bool) × ℕ => - F.encodeCramerShoupEncrypt H (d p.1.1) (decodeBits4 (dropFrame p.1.2.1)) p.1.2.2 p.2) + F.encodeCramerShoupEncrypt H (d p.1.1) (decodeBits4 (dropFrame p.1.2.1)) p.1.2.2 [] p.2) E1 encBits := F.computableTidilyInPolyTime_encodeCramerShoupEncrypt hH.tidy (hd.tidy.comp (by fun_prop)) - (computableTidilyInPolyTime_decodeBits4.comp (by fun_prop)) (by fun_prop) (by fun_prop) + (computableTidilyInPolyTime_decodeBits4.comp (by fun_prop)) (by fun_prop) + (.const []) (by fun_prop) unfold cramerShoupEncryptProb exact (F.isPPTOn_sampleExp (by fun_prop) (hd.comp (by fun_prop))).bind_with hct.isPPTOn +/-- `cramerShoupADDecryptOpt` is computable in polynomial time whenever `H`, `isElem`, and `d` +are. -/ +theorem computableInPolyTime_cramerShoupADDecryptOpt + {H : F.Desc × List Bool → List Bool} {isElem : F.Desc × List Bool → Bool} + {d : ℕ → F.Desc} + (hH : ComputableInPolyTime H (encPair F.encDesc encBits) encBits) + (hisElem : ComputableInPolyTime isElem (encPair F.encDesc encBits) encBool) + (hd : ComputableInPolyTime d encUnary F.encDesc) : + ComputableInPolyTime + (fun p : ℕ × List Bool × List Bool × List Bool => + F.cramerShoupADDecryptOpt H isElem d p.1 p.2.1 p.2.2.1 p.2.2.2) + (encPair encUnary (encPair encBits (encPair encBits encBits))) (encOption encBits) := by + exact .of_tidy (F.computableTidilyInPolyTime_decodeCramerShoup hH.tidy hisElem.tidy + (hd.tidy.comp (by fun_prop)) + (computableTidilyInPolyTime_decodeCramerShoupSK.comp (by fun_prop)) + (by fun_prop) (by fun_prop)) + /-- `cramerShoupDecryptOpt` is computable in polynomial time whenever `H`, `isElem`, and `d` are. -/ theorem computableInPolyTime_cramerShoupDecryptOpt @@ -1099,27 +1291,54 @@ theorem computableInPolyTime_cramerShoupDecryptOpt (encPair encUnary (encPair encBits encBits)) (encOption encBits) := by exact .of_tidy (F.computableTidilyInPolyTime_decodeCramerShoup hH.tidy hisElem.tidy (hd.tidy.comp (by fun_prop)) - (computableTidilyInPolyTime_decodeCramerShoupSK.comp (by fun_prop)) (by fun_prop)) + (computableTidilyInPolyTime_decodeCramerShoupSK.comp (by fun_prop)) + (by fun_prop) (.const [])) + +/-- `cca2ADPhase1Oracle` for `cramerShoupADDecryptOpt` agrees with +`cramerShoupPhase1Oracle true`. -/ +theorem cca2ADPhase1Oracle_cramerShoupADDecryptOpt + (H : F.Desc × List Bool → List Bool) (isElem : F.Desc × List Bool → Bool) + (d : ℕ → F.Desc) (n : ℕ) (sk : (ℕ × ℕ) × ((ℕ × ℕ) × (ℕ × ℕ))) : + cca2ADPhase1Oracle encBits (F.cramerShoupADDecryptOpt H isElem d) n (encCramerShoupSK sk) = + F.cramerShoupPhase1Oracle true H isElem (d n) sk := by + funext req s + simp only [cca2ADPhase1Oracle, cramerShoupADDecryptOpt, decodeCramerShoupSK_encCramerShoupSK, + cramerShoupPhase1Oracle, cramerShoupPhase1Reply, splitCramerShoupQuery, ↓reduceIte] + +/-- `cca2ADPhase2Oracle` for `cramerShoupADDecryptOpt` agrees with +`cramerShoupPhase2Oracle true`. -/ +theorem cca2ADPhase2Oracle_cramerShoupADDecryptOpt + (H : F.Desc × List Bool → List Bool) (isElem : F.Desc × List Bool → Bool) + (d : ℕ → F.Desc) (n : ℕ) (sk : (ℕ × ℕ) × ((ℕ × ℕ) × (ℕ × ℕ))) (ct ad : List Bool) : + cca2ADPhase2Oracle encBits (F.cramerShoupADDecryptOpt H isElem d) n (encCramerShoupSK sk) + ct ad = + F.cramerShoupPhase2Oracle true H isElem (d n) sk ct ad := by + funext req s + simp only [cca2ADPhase2Oracle, cramerShoupADDecryptOpt, decodeCramerShoupSK_encCramerShoupSK, + cramerShoupPhase2Oracle, cramerShoupPhase2Reply, splitCramerShoupQuery, ↓reduceIte] + split_ifs <;> rfl -/-- `cca2Phase1Oracle` for `cramerShoupDecryptOpt` agrees with `cramerShoupPhase1Oracle`. -/ +/-- `cca2Phase1Oracle` for `cramerShoupDecryptOpt` agrees with `cramerShoupPhase1Oracle false`. -/ theorem cca2Phase1Oracle_cramerShoupDecryptOpt (H : F.Desc × List Bool → List Bool) (isElem : F.Desc × List Bool → Bool) (d : ℕ → F.Desc) (n : ℕ) (sk : (ℕ × ℕ) × ((ℕ × ℕ) × (ℕ × ℕ))) : cca2Phase1Oracle encBits (F.cramerShoupDecryptOpt H isElem d) n (encCramerShoupSK sk) = - F.cramerShoupPhase1Oracle H isElem (d n) sk := by + F.cramerShoupPhase1Oracle false H isElem (d n) sk := by funext req s - simp only [cca2Phase1Oracle, cramerShoupDecryptOpt, decodeCramerShoupSK_encCramerShoupSK, - cramerShoupPhase1Oracle, cramerShoupPhase1Reply] + simp only [cca2Phase1Oracle, cramerShoupDecryptOpt, cramerShoupADDecryptOpt, + decodeCramerShoupSK_encCramerShoupSK, cramerShoupPhase1Oracle, cramerShoupPhase1Reply, + splitCramerShoupQuery, Bool.false_eq_true, ↓reduceIte] -/-- `cca2Phase2Oracle` for `cramerShoupDecryptOpt` agrees with `cramerShoupPhase2Oracle`. -/ +/-- `cca2Phase2Oracle` for `cramerShoupDecryptOpt` agrees with `cramerShoupPhase2Oracle false`. -/ theorem cca2Phase2Oracle_cramerShoupDecryptOpt (H : F.Desc × List Bool → List Bool) (isElem : F.Desc × List Bool → Bool) (d : ℕ → F.Desc) (n : ℕ) (sk : (ℕ × ℕ) × ((ℕ × ℕ) × (ℕ × ℕ))) (ct : List Bool) : cca2Phase2Oracle encBits (F.cramerShoupDecryptOpt H isElem d) n (encCramerShoupSK sk) ct = - F.cramerShoupPhase2Oracle H isElem (d n) sk ct := by + F.cramerShoupPhase2Oracle false H isElem (d n) sk ct [] := by funext req s - simp only [cca2Phase2Oracle, cramerShoupDecryptOpt, decodeCramerShoupSK_encCramerShoupSK, - cramerShoupPhase2Oracle, cramerShoupPhase2Reply] + simp only [cca2Phase2Oracle, cramerShoupDecryptOpt, cramerShoupADDecryptOpt, + decodeCramerShoupSK_encCramerShoupSK, cramerShoupPhase2Oracle, cramerShoupPhase2Reply, + splitCramerShoupQuery, Bool.false_eq_true, ↓reduceIte, BEq.rfl, Bool.and_true] split_ifs <;> rfl /-- Extracting the 4-tuple of public-key group words from `encodeCramerShoupPublicKey`. -/ @@ -1136,18 +1355,18 @@ theorem decodeBits4_dropFrame_encodeCramerShoupPublicKey `cramerShoupPublicKey`. -/ theorem encodeCramerShoupEncrypt_skNatOfZMod (H : F.Desc × List Bool → List Bool) (d : F.Desc) - (α σ τ σ₁ τ₁ σ₂ τ₂ : ZMod (F.order d)) (m : List Bool) (b : ℕ) : + (α σ τ σ₁ τ₁ σ₂ τ₂ : ZMod (F.order d)) (m ad : List Bool) (b : ℕ) : let u := F.encode d (α • F.generator d) let sk := F.skNatOfZMod d σ τ σ₁ τ₁ σ₂ τ₂ let pkVal := F.cramerShoupPublicKey d α σ τ σ₁ τ₁ σ₂ τ₂ F.encodeCramerShoupEncrypt H d (u, F.encodeProjHashAux d sk.1.1 sk.1.2 u, F.encodeProjHashAux d sk.2.1.1 sk.2.1.2 u, - F.encodeProjHashAux d sk.2.2.1 sk.2.2.2 u) m b = + F.encodeProjHashAux d sk.2.2.1 sk.2.2.2 u) m ad b = F.encodeCramerShoupEncrypt H d (F.encode d pkVal.1, F.encode d pkVal.2.1, F.encode d pkVal.2.2.1, F.encode d pkVal.2.2.2) - m (b : ZMod (F.order d)).val := by + m ad (b : ZMod (F.order d)).val := by simp only [skNatOfZMod, encodeProjHashAux_encode, ZMod.natCast_zmod_val, cramerShoupPublicKey, projHash2Aux, encodeCramerShoupEncrypt, F.smul_encode, F.add_encode] diff --git a/Complexity/Crypto/Computational/CramerShoup/Scheme.lean b/Complexity/Crypto/Computational/CramerShoup/Scheme.lean index 0e6744c8c9..4e60a0c43c 100644 --- a/Complexity/Crypto/Computational/CramerShoup/Scheme.lean +++ b/Complexity/Crypto/Computational/CramerShoup/Scheme.lean @@ -25,17 +25,34 @@ import Complexity.Tactic.PolyTime # Multiplicative Cramer–Shoup Encryption Scheme and Adaptive Projective-Hash Rejection This module formalizes the **Multiplicative Cramer–Shoup public-key encryption scheme** -($\mathcal{E}_{\mathrm{MCS}}$, [Boneh and Shoup, Section 12.5.3 and Exercise 12.27, printed -pp. 488–493, 521–522][BonehShoup2023], specialized to associated data -$\mathcal{D} = \mathrm{Unit}$) over a `PrimeOrderGroupFamily`, proves its algebraic and word-level +($\mathcal{E}_{\mathrm{MCS}}$, [Boneh and Shoup, Section 12.5.3, Section 12.7, and Exercise 12.27, +printed pp. 488–493, 501–502, 521–522][BonehShoup2023], over an arbitrary associated-data type +`AD : Type`) over a `PrimeOrderGroupFamily`, proves its algebraic and word-level decryption correctness and polynomial-time computability certificates, and establishes the **adaptive Lemma 12.8 projective-hash rejection bound** and **Lemma 12.6 one-time group-mask challenge blindness** (Games 3 $\to$ 3.5 $\to$ 4 $\to$ 5 of Theorem 12.9, printed pp. 492–493) -against an adaptive `CCA2AdversaryOf Msg`. Groups are written additively as +against an adaptive `ADCCA2AdversaryOf Msg AD`. Groups are written additively as `ZMod (F.order d)`-modules (matching `DiscreteLog`, `DiffieHellman`, `ElGamal`, and `ProjectiveHash`). -## Source Statements ([BonehShoup2023], Sections 12.5.1–12.5.3 & Exercise 12.27) +## Source Statements ([BonehShoup2023], Sections 12.5.1–12.5.3, 12.7 & Exercise 12.27) + +Verbatim from [BonehShoup2023], Section 12.7, Definition 12.7 (printed pp. 501–502): +> **Definition 12.7 (CCA and 1CCA security with associated data).** The definition of CCA +> security for ordinary public-key encryption schemes carries over naturally to AD public-key +> encryption schemes. Attack Game 12.1 is modified as follows. For encryption queries, in +> addition to a pair of messages $(m_{i0}, m_{i1})$, the adversary also submits associated data +> $d_i$, and the challenger computes $c_i \xleftarrow{R} E(pk, m_{ib}, d_i)$. For decryption +> queries, in addition to a ciphertext $\hat{c}_j$, the adversary submits associated data +> $\hat{d}_j$, and the challenger computes $\hat{m}_j \leftarrow D(sk, \hat{c}_j, \hat{d}_j)$. +> The restriction is that the pair $(\hat{c}_j, \hat{d}_j)$ may not be among the pairs +> $(c_1, d_1), (c_2, d_2), \ldots$ corresponding to previous encryption queries. An adversary +> $\mathcal{A}$'s advantage in this game is denoted +> $\text{CCA}^{\text{ad}}\text{adv}[\mathcal{A}, \mathcal{E}]$, and the scheme is said to be CCA +> secure if this advantage is negligible for all efficient adversaries $\mathcal{A}$. If we +> restrict the adversary to a single encryption query, as in Definition 12.2, the advantage is +> denoted $\text{1CCA}^{\text{ad}}\text{adv}[\mathcal{A}, \mathcal{E}]$, and the scheme is said +> to be 1CCA secure if this advantage is negligible for all efficient adversaries $\mathcal{A}$. Verbatim from [BonehShoup2023], Sections 12.5.1–12.5.2 (Lemma 12.6 in §12.5.1, printed p. 486; Lemma 12.8 in §12.5.2, printed p. 488): @@ -140,6 +157,231 @@ Verbatim from [BonehShoup2023], Exercise 12.27 (printed pp. 521–522): @[expose] public section +namespace Cslib.Crypto.Encryption + +open Probability Turing.MultiTapeTM + +/-- A single-challenge adaptive chosen-ciphertext adversary with associated data +(1CCA with associated data, [BonehShoup2023], Section 12.7, Definition 12.7, printed +pp. 501–502) over message space `Msg` and associated-data space `AD`. +Both stages have access to the single-channel decryption oracle (`Operation = Unit`), where each +decryption query is a framed pair `encPair encBits encBits (ct, ad)`. -/ +structure ADCCA2AdversaryOf (Msg AD : Type) where + /-- Phase 1: given the unary security parameter `n` and encoded public key `pk`, make decryption + queries, choose two challenge messages `(m₀, m₁) : Msg × Msg` together with challenge associated + data `ad : AD`, and save a state word. -/ + choose : ℕ → List Bool → WordOracleComp Unit (((Msg × Msg) × AD) × List Bool) + /-- Phase 2: given `n`, the saved state word, and the challenge ciphertext `ct`, make decryption + queries (where querying `(ct, encAD ad)` is rejected) and output a guess bit. -/ + guess : ℕ → List Bool → List Bool → WordOracleComp Unit Bool + +/-- Uniform strict oracle PPT admissibility of both stages of an `ADCCA2AdversaryOf Msg AD` with +respect to message encoding `encMsg` and associated-data encoding `encAD`. -/ +structure ADCCA2AdversaryOf.IsPPT {Msg AD : Type} (encMsg : Msg ↪ List Bool) + (encAD : AD ↪ List Bool) (adversary : ADCCA2AdversaryOf Msg AD) : Prop where + /-- Phase 1 message and associated-data selection is uniform strict oracle PPT on `(n, pk)`. -/ + choose : IsOraclePPT (Operation := Unit) + (encPair (encPair (encPair encMsg encMsg) encAD) encBits) adversary.choose + /-- Phase 2 guessing is uniform strict oracle PPT on `(n, state, ct)`. -/ + guess : IsOraclePPTOn (Operation := Unit) (encPair encUnary (encPair encBits encBits)) encBool + (fun p => adversary.guess p.1 p.2.1 p.2.2) + +/-- Per-stage decryption-query bound `(Q₁ n, Q₂ n)` for an `ADCCA2AdversaryOf Msg AD`. -/ +def ADCCA2AdversaryOf.IsQueryBounded {Msg AD : Type} (adversary : ADCCA2AdversaryOf Msg AD) + (Q₁ Q₂ : ℕ → ℕ) : Prop := + (∀ n pk, (adversary.choose n pk).IsQueryBounded (Q₁ n)) ∧ + (∀ n state ct, (adversary.guess n state ct).IsQueryBounded (Q₂ n)) + +/-- Every strict oracle PPT `ADCCA2AdversaryOf Msg AD` has polynomial bounds on its Phase 1 +queries, Phase 2 queries, and saved-state length in terms of `(n, pk.length, ct.length)`. -/ +theorem ADCCA2AdversaryOf.IsPPT.query_bounds {Msg AD : Type} {encMsg : Msg ↪ List Bool} + {encAD : AD ↪ List Bool} {adversary : ADCCA2AdversaryOf Msg AD} + (hA : adversary.IsPPT encMsg encAD) : + ∃ c₁ d₁ c₂ d₂ cs ds : ℕ, + (∀ n pk, (adversary.choose n pk).IsQueryBounded (c₁ * (n + pk.length + 2) ^ d₁)) ∧ + (∀ n state ct, + (adversary.guess n state ct).IsQueryBounded + (c₂ * (n + state.length + ct.length + 2) ^ d₂)) ∧ + (∀ n pk (State : Type) + (oracle : Unit × List Bool → StateT State PMF (List Bool)) s s' choice, + (choice, s') ∈ ((OracleComp.interpret + (ProbOracleComp.handler (fun m => StateT.lift (ProbComp.unifOracle m)) oracle) + (adversary.choose n pk)).run s).support → + choice.2.length ≤ cs * (n + pk.length + 2) ^ ds) := by + obtain ⟨c₁, d₁, hchoose⟩ := hA.choose.query_bounds + obtain ⟨c₂, d₂, hguess⟩ := hA.guess.query_bounds + obtain ⟨cs, ds, hlen⟩ := hA.choose.on.length_le + refine ⟨c₁, d₁, c₂ * 4 ^ d₂, d₂, cs * 2 ^ ds, ds, fun n pk => (hchoose n pk).1, + fun n state ct => ?_, fun n pk State oracle s s' choice hmem => ?_⟩ + · have hsize : (encPair encUnary (encPair encBits encBits) (n, state, ct)).length + 1 ≤ + 4 * (n + state.length + ct.length + 2) := by + simp only [length_encPair, encUnary_apply, List.length_replicate, encBits_apply] + lia + have hpoly := Nat.mul_le_mul_left c₂ (Nat.pow_le_pow_left hsize d₂) + rw [Nat.mul_pow, ← Nat.mul_assoc] at hpoly + exact (hguess (n, state, ct)).1.mono hpoly + · have hbound := hlen (n, pk) State oracle s s' choice hmem + have hstate : choice.2.length ≤ + (encPair (encPair (encPair encMsg encMsg) encAD) encBits choice).length := by + simp only [length_encPair, encBits_apply] + lia + have hsize : (encPair encUnary encBits (n, pk)).length + 1 ≤ + 2 * (n + pk.length + 2) := by + simp only [length_encPair, encUnary_apply, List.length_replicate, encBits_apply] + lia + have hpoly := Nat.mul_le_mul_left cs (Nat.pow_le_pow_left hsize ds) + rw [Nat.mul_pow, ← Nat.mul_assoc] at hpoly + exact (hstate.trans hbound).trans hpoly + +/-- Single-bit 1CCA adversary with associated data `AD` (`Msg = Bool`). -/ +abbrev ADCCA2Adversary (AD : Type) : Type := ADCCA2AdversaryOf Bool AD + +/-- Native uniform strict oracle PPT admissibility for a bit 1CCA adversary with associated data. -/ +abbrev ADCCA2Adversary.IsPPT {AD : Type} (encAD : AD ↪ List Bool) + (adversary : ADCCA2Adversary AD) : Prop := + ADCCA2AdversaryOf.IsPPT encBool encAD adversary + +/-- Bit-string 1CCA adversary with associated data `AD` (`Msg = List Bool`). -/ +abbrev WordADCCA2Adversary (AD : Type) : Type := ADCCA2AdversaryOf (List Bool) AD + +/-- Native uniform strict oracle PPT admissibility for a bit-string 1CCA adversary with associated +data. -/ +abbrev WordADCCA2Adversary.IsPPT {AD : Type} (encAD : AD ↪ List Bool) + (adversary : WordADCCA2Adversary AD) : Prop := + ADCCA2AdversaryOf.IsPPT encBits encAD adversary + +/-- Embed an associated-data-free `CCA2AdversaryOf Msg` as an `ADCCA2AdversaryOf Msg Unit` with +trivial associated data `() : Unit`. -/ +def CCA2AdversaryOf.toADUnit {Msg : Type} (adversary : CCA2AdversaryOf Msg) : + ADCCA2AdversaryOf Msg Unit where + choose n pk := (fun p => ((p.1, ()), p.2)) <$> adversary.choose n pk + guess := adversary.guess + +/-- `CCA2AdversaryOf.toADUnit` preserves uniform strict oracle PPT admissibility. -/ +theorem CCA2AdversaryOf.IsPPT.toADUnit {Msg : Type} {encMsg : Msg ↪ List Bool} + {adversary : CCA2AdversaryOf Msg} (hA : adversary.IsPPT encMsg) : + adversary.toADUnit.IsPPT encMsg (encFinEnum Unit) where + choose := (hA.choose.on.map (by fun_prop)).isOraclePPT + guess := hA.guess + +/-- `CCA2AdversaryOf.toADUnit` preserves per-stage decryption-query bounds. -/ +theorem CCA2AdversaryOf.IsQueryBounded.toADUnit {Msg : Type} {adversary : CCA2AdversaryOf Msg} + {Q₁ Q₂ : ℕ → ℕ} (hQ : adversary.IsQueryBounded Q₁ Q₂) : + adversary.toADUnit.IsQueryBounded Q₁ Q₂ := + ⟨fun n pk => by + simp only [CCA2AdversaryOf.toADUnit, ProbOracleComp.IsQueryBounded, + OracleComp.isCostBounded_map_iff] + exact hQ.1 n pk, + hQ.2⟩ + +/-- Phase 1 decryption oracle with associated data ([BonehShoup2023], Section 12.7, +Definition 12.7, printed pp. 501–502): unpack `request.2` into ciphertext `unframe request.2` and +associated data `dropFrame request.2`, and reply with +`encOption encOut (decrypt n sk (unframe request.2) (dropFrame request.2))`. -/ +def cca2ADPhase1Oracle {Out : Type} (encOut : Out ↪ List Bool) + (decrypt : ℕ → List Bool → List Bool → List Bool → Option Out) + (n : ℕ) (sk : List Bool) (request : Unit × List Bool) : + StateT Unit ProbComp (List Bool) := + fun s => pure (encOption encOut (decrypt n sk (unframe request.2) (dropFrame request.2)), s) + +/-- Phase 2 decryption oracle with associated data ([BonehShoup2023], Section 12.7, +Definition 12.7, printed pp. 501–502): unpack `request.2` into `(unframe request.2, +dropFrame request.2)`, reject the challenge pair `(ct, ad)` with `[]` (`encOption encOut none`), +and answer all other queries with +`encOption encOut (decrypt n sk (unframe request.2) (dropFrame request.2))`. -/ +def cca2ADPhase2Oracle {Out : Type} (encOut : Out ↪ List Bool) + (decrypt : ℕ → List Bool → List Bool → List Bool → Option Out) + (n : ℕ) (sk ct ad : List Bool) (request : Unit × List Bool) : + StateT Unit ProbComp (List Bool) := + fun s => + if (unframe request.2 == ct) && (dropFrame request.2 == ad) then + pure ([], s) + else + pure (encOption encOut (decrypt n sk (unframe request.2) (dropFrame request.2)), s) + +/-- Single-challenge 1CCA experiment with associated data ([BonehShoup2023], Section 12.7, +Definition 12.7, printed pp. 501–502) and challenge-pair admissibility predicate +`valid : ℕ → List Bool → Msg → Msg → Bool`: sample `(pk, sk) ← keyGen n`, run Phase 1 (`choose`) +against `cca2ADPhase1Oracle`, check `valid n pk m₀ m₁`, encrypt `(if bit then m₁ else m₀)` with +associated data `ad` to obtain `ct`, and run Phase 2 (`guess`) against `cca2ADPhase2Oracle` +rejecting `(ct, encAD ad)`. -/ +noncomputable def cca2ADGameOn {Msg AD Out : Type} + (valid : ℕ → List Bool → Msg → Msg → Bool) + (encAD : AD ↪ List Bool) (encOut : Out ↪ List Bool) + (keyGen : ℕ → ProbComp (List Bool × List Bool)) + (encrypt : ℕ → List Bool → Msg → AD → ProbComp (List Bool)) + (decrypt : ℕ → List Bool → List Bool → List Bool → Option Out) + (adversary : ADCCA2AdversaryOf Msg AD) (bit : Bool) (n : ℕ) : ProbComp Bool := do + let keys ← keyGen n + let choiceOut ← (OracleComp.interpret + (ProbOracleComp.handler (fun m => StateT.lift (ProbComp.unifFin m)) + (cca2ADPhase1Oracle encOut decrypt n keys.2)) + (adversary.choose n keys.1)).run () + let choice := choiceOut.1 + if valid n keys.1 choice.1.1.1 choice.1.1.2 then + let ct ← encrypt n keys.1 (if bit then choice.1.1.2 else choice.1.1.1) choice.1.2 + let guessOut ← (OracleComp.interpret + (ProbOracleComp.handler (fun m => StateT.lift (ProbComp.unifFin m)) + (cca2ADPhase2Oracle encOut decrypt n keys.2 ct (encAD choice.1.2))) + (adversary.guess n choice.2 ct)).run () + return guessOut.1 + else + return false + +/-- Unconditional single-challenge 1CCA experiment with associated data +(`valid := fun _ _ _ _ => true`). -/ +noncomputable abbrev cca2ADGame {Msg AD Out : Type} + (encAD : AD ↪ List Bool) (encOut : Out ↪ List Bool) + (keyGen : ℕ → ProbComp (List Bool × List Bool)) + (encrypt : ℕ → List Bool → Msg → AD → ProbComp (List Bool)) + (decrypt : ℕ → List Bool → List Bool → List Bool → Option Out) + (adversary : ADCCA2AdversaryOf Msg AD) (bit : Bool) (n : ℕ) : ProbComp Bool := + cca2ADGameOn (fun _ _ _ _ => true) encAD encOut keyGen encrypt decrypt adversary bit n + +/-- Single-challenge 1CCA security with associated data ([BonehShoup2023], Section 12.7, +Definition 12.7, printed pp. 501–502) with respect to challenge-pair admissibility predicate +`valid`, message encoding `encMsg`, associated-data encoding `encAD`, and decryption output +encoding `encOut`. -/ +def INDCCA2ADOn {Msg AD Out : Type} (valid : ℕ → List Bool → Msg → Msg → Bool) + (encMsg : Msg ↪ List Bool) (encAD : AD ↪ List Bool) (encOut : Out ↪ List Bool) + (keyGen : ℕ → ProbComp (List Bool × List Bool)) + (encrypt : ℕ → List Bool → Msg → AD → ProbComp (List Bool)) + (decrypt : ℕ → List Bool → List Bool → List Bool → Option Out) : Prop := + Game.Indistinguishable + (fun adversary n => + ProbComp.eval (cca2ADGameOn valid encAD encOut keyGen encrypt decrypt adversary false n)) + (fun adversary n => + ProbComp.eval (cca2ADGameOn valid encAD encOut keyGen encrypt decrypt adversary true n)) + (ADCCA2AdversaryOf.IsPPT encMsg encAD) + +/-- Single-challenge 1CCA security with associated data over a fixed-length message space `Msg` +(`valid := fun _ _ _ _ => true`). -/ +abbrev INDCCA2ADOf {Msg AD Out : Type} (encMsg : Msg ↪ List Bool) (encAD : AD ↪ List Bool) + (encOut : Out ↪ List Bool) + (keyGen : ℕ → ProbComp (List Bool × List Bool)) + (encrypt : ℕ → List Bool → Msg → AD → ProbComp (List Bool)) + (decrypt : ℕ → List Bool → List Bool → List Bool → Option Out) : Prop := + INDCCA2ADOn (fun _ _ _ _ => true) encMsg encAD encOut keyGen encrypt decrypt + +/-- Single-challenge 1CCA security with associated data for bit encryption (`Msg = Bool`, +`Out = Bool`). -/ +abbrev INDCCA2AD {AD : Type} (encAD : AD ↪ List Bool) + (keyGen : ℕ → ProbComp (List Bool × List Bool)) + (encrypt : ℕ → List Bool → Bool → AD → ProbComp (List Bool)) + (decrypt : ℕ → List Bool → List Bool → List Bool → Option Bool) : Prop := + INDCCA2ADOf encBool encAD encBool keyGen encrypt decrypt + +/-- Single-challenge 1CCA security with associated data for bit-string encryption +(`Msg = List Bool`, `Out = List Bool`) under Attack Game 12.1's equal-length challenge rule +`m₀.length == m₁.length`. -/ +abbrev WordINDCCA2AD {AD : Type} (encAD : AD ↪ List Bool) + (keyGen : ℕ → ProbComp (List Bool × List Bool)) + (encrypt : ℕ → List Bool → List Bool → AD → ProbComp (List Bool)) + (decrypt : ℕ → List Bool → List Bool → List Bool → Option (List Bool)) : Prop := + INDCCA2ADOn (fun _ _ m₀ m₁ => m₀.length == m₁.length) encBits encAD encBits keyGen encrypt decrypt + +end Cslib.Crypto.Encryption + namespace Cslib.Crypto.PrimeOrderGroupFamily open Probability Turing.MultiTapeTM ProjectiveHash Encryption Game @@ -179,15 +421,22 @@ theorem decodeBits4_encBits4 (p : List Bool × List Bool × List Bool × List Bo rcases p with ⟨a, b, c, d⟩ simp [encBits4, decodeBits4, encPair_apply, unframe_frame_append, dropFrame_frame_append] -/-- Extract the framed `(v, w, e)` triple from an encoded 4-tuple ciphertext `ct`. -/ -def extractCramerShoupTriple (ct : List Bool) : List Bool := +/-- Extract the framed `(v, w, e, ad)` 4-tuple from an encoded 4-tuple ciphertext `ct` and +associated-data bit string `ad`. -/ +def extractCramerShoupTuple (ct ad : List Bool) : List Bool := let parts := decodeBits4 ct - encBits3 (parts.1, parts.2.1, parts.2.2.1) + encBits4 (parts.1, parts.2.1, parts.2.2.1, ad) @[simp] -theorem extractCramerShoupTriple_encBits4 (v w e z' : List Bool) : - extractCramerShoupTriple (encBits4 (v, w, e, z')) = encBits3 (v, w, e) := by - simp [extractCramerShoupTriple] +theorem extractCramerShoupTuple_encBits4 (v w e z' ad : List Bool) : + extractCramerShoupTuple (encBits4 (v, w, e, z')) ad = encBits4 (v, w, e, ad) := by + simp [extractCramerShoupTuple] + +/-- Unpack a decryption-oracle query `req` into `(ct, ad)`: when `hasAD = true`, unpack the framed +pair `(unframe req, dropFrame req)`; when `hasAD = false`, treat `req` as `ct` with empty associated +data `[]`. -/ +def splitCramerShoupQuery (hasAD : Bool) (req : List Bool) : List Bool × List Bool := + if hasAD then (unframe req, dropFrame req) else (req, []) @[local fun_prop] private theorem computableTidilyInPolyTime_decodeBits4 : @@ -196,11 +445,27 @@ private theorem computableTidilyInPolyTime_decodeBits4 : unfold decodeBits4; fun_prop @[local fun_prop] -private theorem computableTidilyInPolyTime_extractCramerShoupTriple : - ComputableTidilyInPolyTime extractCramerShoupTriple encBits encBits := by - have h := computableTidilyInPolyTime_decodeBits4 - exact (ComputableTidilyInPolyTime.encode encBits3).comp - (h.fst'.prod (h.snd'.fst'.prod h.snd'.snd'.fst')) +private theorem computableTidilyInPolyTime_extractCramerShoupTuple : + ComputableTidilyInPolyTime + (fun p : List Bool × List Bool => extractCramerShoupTuple p.1 p.2) + (encPair encBits encBits) encBits := by + have h := computableTidilyInPolyTime_decodeBits4.comp + (ComputableTidilyInPolyTime.fst encBits encBits) + have had := ComputableTidilyInPolyTime.snd encBits encBits + exact (ComputableTidilyInPolyTime.encode encBits4).comp + (h.fst'.prod (h.snd'.fst'.prod (h.snd'.snd'.fst'.prod had))) + +@[local fun_prop] +private theorem computableTidilyInPolyTime_splitCramerShoupQuery (hasAD : Bool) : + ComputableTidilyInPolyTime (splitCramerShoupQuery hasAD) encBits + (encPair encBits encBits) := by + cases hasAD + · change ComputableTidilyInPolyTime (fun req : List Bool => (req, ([] : List Bool))) + encBits (encPair encBits encBits) + fun_prop + · change ComputableTidilyInPolyTime (fun req : List Bool => (unframe req, dropFrame req)) + encBits (encPair encBits encBits) + fun_prop /-- `decodeBits4` is computable in polynomial time. -/ theorem computableInPolyTime_decodeBits4 : @@ -208,10 +473,17 @@ theorem computableInPolyTime_decodeBits4 : (encPair encBits (encPair encBits (encPair encBits encBits))) := .of_tidy computableTidilyInPolyTime_decodeBits4 -/-- `extractCramerShoupTriple` is computable in polynomial time. -/ -theorem computableInPolyTime_extractCramerShoupTriple : - ComputableInPolyTime extractCramerShoupTriple encBits encBits := - .of_tidy computableTidilyInPolyTime_extractCramerShoupTriple +/-- `extractCramerShoupTuple` is computable in polynomial time. -/ +theorem computableInPolyTime_extractCramerShoupTuple : + ComputableInPolyTime + (fun p : List Bool × List Bool => extractCramerShoupTuple p.1 p.2) + (encPair encBits encBits) encBits := + .of_tidy computableTidilyInPolyTime_extractCramerShoupTuple + +/-- `splitCramerShoupQuery` is computable in polynomial time. -/ +theorem computableInPolyTime_splitCramerShoupQuery (hasAD : Bool) : + ComputableInPolyTime (splitCramerShoupQuery hasAD) encBits (encPair encBits encBits) := + .of_tidy (computableTidilyInPolyTime_splitCramerShoupQuery hasAD) /-! ### Algebraic Multiplicative Cramer–Shoup ([BonehShoup2023], Section 12.5.3 & Exercise 12.27) -/ @@ -222,10 +494,10 @@ def cramerShoupHashMod (H : F.Desc × List Bool → List Bool) (d : F.Desc) (x : ZMod (F.order d) := (ofBits (H (d, x)) : ZMod (F.order d)) -/-- Encode a triple of group elements `(v, w, e) : F.Carrier d × F.Carrier d × F.Carrier d` as a -framed bit string. -/ -def encodeCramerShoupTriple (d : F.Desc) (v w e : F.Carrier d) : List Bool := - encBits3 (F.encode d v, F.encode d w, F.encode d e) +/-- Encode a triple of group elements `(v, w, e) : F.Carrier d × F.Carrier d × F.Carrier d` together +with an encoded associated-data word `ad : List Bool` as a framed 4-tuple bit string. -/ +def encodeCramerShoupTuple (d : F.Desc) (v w e : F.Carrier d) (ad : List Bool) : List Bool := + encBits4 (F.encode d v, F.encode d w, F.encode d e, ad) /-- Algebraic Cramer–Shoup public key `pk = (u, h, h₁, h₂) ∈ G⁴` generated from secret exponents `(α, σ, τ, σ₁, τ₁, σ₂, τ₂)` ([BonehShoup2023], Section 12.5.3, printed p. 489). -/ @@ -235,26 +507,26 @@ def cramerShoupPublicKey (d : F.Desc) (α σ τ σ₁ τ₁ σ₂ τ₂ : ZMod ( let h₁₂ := projHash2Aux F d σ₁ τ₁ σ₂ τ₂ u (u, h, h₁₂.1, h₁₂.2) -/-- Algebraic Multiplicative Cramer–Shoup encryption $E(pk, m; \beta) = (v, w, e, z')$ -([BonehShoup2023], Exercise 12.27, printed p. 521, with $\mathcal{D} = \mathrm{Unit}$). -/ +/-- Algebraic Multiplicative Cramer–Shoup encryption $E(pk, m, ad; \beta) = (v, w, e, z')$ +([BonehShoup2023], Exercise 12.27, printed p. 521). -/ def cramerShoupEncrypt (H : F.Desc × List Bool → List Bool) (d : F.Desc) (pk : F.Carrier d × F.Carrier d × F.Carrier d × F.Carrier d) - (m : F.Carrier d) (β : ZMod (F.order d)) : + (m : F.Carrier d) (ad : List Bool) (β : ZMod (F.order d)) : F.Carrier d × F.Carrier d × F.Carrier d × F.Carrier d := let v := β • F.generator d; let w := β • pk.1; let e := β • pk.2.1 + m - let ρ := F.cramerShoupHashMod H d (F.encodeCramerShoupTriple d v w e) + let ρ := F.cramerShoupHashMod H d (F.encodeCramerShoupTuple d v w e ad) (v, w, e, β • (pk.2.2.1 + ρ • pk.2.2.2)) /-- Under a genuine public key `cramerShoupPublicKey d α σ τ σ₁ τ₁ σ₂ τ₂`, the components of `cramerShoupEncrypt` coincide with projective hash evaluation (`Game 0 = Game 1`, [BonehShoup2023], Section 12.5.3, equation (12.21), printed p. 490). -/ theorem cramerShoupEncrypt_cramerShoupPublicKey (H : F.Desc × List Bool → List Bool) (d : F.Desc) - (α σ τ σ₁ τ₁ σ₂ τ₂ β : ZMod (F.order d)) (m : F.Carrier d) : - F.cramerShoupEncrypt H d (F.cramerShoupPublicKey d α σ τ σ₁ τ₁ σ₂ τ₂) m β = + (α σ τ σ₁ τ₁ σ₂ τ₂ β : ZMod (F.order d)) (m : F.Carrier d) (ad : List Bool) : + F.cramerShoupEncrypt H d (F.cramerShoupPublicKey d α σ τ σ₁ τ₁ σ₂ τ₂) m ad β = let v := β • F.generator d let w := (α * β) • F.generator d let e := projHash F d σ τ v w + m - let ρ := F.cramerShoupHashMod H d (F.encodeCramerShoupTriple d v w e) + let ρ := F.cramerShoupHashMod H d (F.encodeCramerShoupTuple d v w e ad) let z' := projHash2 F d σ₁ τ₁ σ₂ τ₂ ρ v w (v, w, e, z') := by dsimp only [cramerShoupEncrypt, cramerShoupPublicKey] @@ -268,16 +540,17 @@ theorem cramerShoupEncrypt_cramerShoupPublicKey (H : F.Desc × List Bool → Lis rw [← hw, projHash2_smul_generator] rw [hw, ← he, ← hz'] -/-- Algebraic Multiplicative Cramer–Shoup decryption $D(sk, (v, w, e, z'))$ +/-- Algebraic Multiplicative Cramer–Shoup decryption $D(sk, (v, w, e, z'), ad)$ ([BonehShoup2023], Exercise 12.27, printed p. 522): check -$f'_{\sigma_1,\tau_1,\sigma_2,\tau_2}(v, w, \rho) = z'$ where $\rho = H'(v, w, e)$, and if so +$f'_{\sigma_1,\tau_1,\sigma_2,\tau_2}(v, w, \rho) = z'$ where $\rho = H'(v, w, e, ad)$, and if so output `some (e - f_{σ,τ}(v, w))`. -/ def cramerShoupDecrypt [∀ d, DecidableEq (F.Carrier d)] (H : F.Desc × List Bool → List Bool) (d : F.Desc) (sk : (ZMod (F.order d) × ZMod (F.order d)) × ((ZMod (F.order d) × ZMod (F.order d)) × (ZMod (F.order d) × ZMod (F.order d)))) - (ct : F.Carrier d × F.Carrier d × F.Carrier d × F.Carrier d) : Option (F.Carrier d) := - let ρ := F.cramerShoupHashMod H d (F.encodeCramerShoupTriple d ct.1 ct.2.1 ct.2.2.1) + (ct : F.Carrier d × F.Carrier d × F.Carrier d × F.Carrier d) (ad : List Bool) : + Option (F.Carrier d) := + let ρ := F.cramerShoupHashMod H d (F.encodeCramerShoupTuple d ct.1 ct.2.1 ct.2.2.1 ad) if projHash2 F d sk.2.1.1 sk.2.1.2 sk.2.2.1 sk.2.2.2 ρ ct.1 ct.2.1 = ct.2.2.2 then some (ct.2.2.1 - projHash F d sk.1.1 sk.1.2 ct.1 ct.2.1) else @@ -285,14 +558,15 @@ def cramerShoupDecrypt [∀ d, DecidableEq (F.Carrier d)] /-- **Algebraic Cramer–Shoup decryption correctness** ([BonehShoup2023], Section 12.5.3 & Exercise 12.27, printed pp. 489, 521–522): decrypting an honest encryption under -`cramerShoupPublicKey d α σ τ σ₁ τ₁ σ₂ τ₂` with `((σ, τ), ((σ₁, τ₁), (σ₂, τ₂)))` recovers -`some m`. -/ +`cramerShoupPublicKey d α σ τ σ₁ τ₁ σ₂ τ₂` with `((σ, τ), ((σ₁, τ₁), (σ₂, τ₂)))` and the same +associated data `ad` recovers `some m`. -/ @[simp] theorem cramerShoupDecrypt_cramerShoupEncrypt [∀ d, DecidableEq (F.Carrier d)] (H : F.Desc × List Bool → List Bool) (d : F.Desc) - (α σ τ σ₁ τ₁ σ₂ τ₂ β : ZMod (F.order d)) (m : F.Carrier d) : + (α σ τ σ₁ τ₁ σ₂ τ₂ β : ZMod (F.order d)) (m : F.Carrier d) (ad : List Bool) : F.cramerShoupDecrypt H d ((σ, τ), ((σ₁, τ₁), (σ₂, τ₂))) - (F.cramerShoupEncrypt H d (F.cramerShoupPublicKey d α σ τ σ₁ τ₁ σ₂ τ₂) m β) = some m := by + (F.cramerShoupEncrypt H d (F.cramerShoupPublicKey d α σ τ σ₁ τ₁ σ₂ τ₂) m ad β) ad = + some m := by rw [F.cramerShoupEncrypt_cramerShoupPublicKey]; simp [cramerShoupDecrypt] /-! ### Word-level polynomial-time Cramer–Shoup operations -/ @@ -377,57 +651,59 @@ def encodeCramerShoupCiphertext (d : F.Desc) encBits4 (F.encode d ct.1, F.encode d ct.2.1, F.encode d ct.2.2.1, F.encode d ct.2.2.2) /-- Word-level Cramer–Shoup encryption from the public key `(u, h, h₁, h₂)`, encoded message `m`, -and randomness `b : ℕ`. -/ +encoded associated data `ad`, and randomness `b : ℕ`. -/ def encodeCramerShoupEncrypt (H : F.Desc × List Bool → List Bool) (d : F.Desc) - (pk : List Bool × List Bool × List Bool × List Bool) (m : List Bool) (b : ℕ) : List Bool := + (pk : List Bool × List Bool × List Bool × List Bool) (m ad : List Bool) (b : ℕ) : List Bool := let v := F.smul (d, b, F.encode d (F.generator d)); let w := F.smul (d, b, pk.1) let e := F.add (d, F.smul (d, b, pk.2.1), m) - let r := ofBits (H (d, encBits3 (v, w, e))) + let r := ofBits (H (d, encBits4 (v, w, e, ad))) encBits4 (v, w, e, F.smul (d, b, F.add (d, pk.2.2.1, F.smul (d, r, pk.2.2.2)))) @[simp] theorem encodeCramerShoupEncrypt_encode (H : F.Desc × List Bool → List Bool) (d : F.Desc) - (pk : F.Carrier d × F.Carrier d × F.Carrier d × F.Carrier d) (m : F.Carrier d) (b : ℕ) : + (pk : F.Carrier d × F.Carrier d × F.Carrier d × F.Carrier d) + (m : F.Carrier d) (ad : List Bool) (b : ℕ) : F.encodeCramerShoupEncrypt H d (F.encode d pk.1, F.encode d pk.2.1, F.encode d pk.2.2.1, F.encode d pk.2.2.2) - (F.encode d m) b = + (F.encode d m) ad b = F.encodeCramerShoupCiphertext d - (F.cramerShoupEncrypt H d pk m (b : ZMod (F.order d))) := by + (F.cramerShoupEncrypt H d pk m ad (b : ZMod (F.order d))) := by simp only [encodeCramerShoupEncrypt, encodeCramerShoupCiphertext, cramerShoupEncrypt, - encodeCramerShoupTriple, cramerShoupHashMod, F.smul_encode, F.add_encode] + encodeCramerShoupTuple, cramerShoupHashMod, F.smul_encode, F.add_encode] /-- Word-level Game 1 challenge encryption from `(v, w)` (encoded group elements), secret key -`sk`, and encoded message `m` ([BonehShoup2023], Section 12.5.3, Game 1, printed p. 490). -/ +`sk`, encoded message `m`, and encoded associated data `ad` ([BonehShoup2023], Section 12.5.3, +Game 1, printed p. 490, and Exercise 12.27, printed pp. 521–522). -/ def encodeCramerShoupChallenge (H : F.Desc × List Bool → List Bool) (d : F.Desc) - (sk : (ℕ × ℕ) × ((ℕ × ℕ) × (ℕ × ℕ))) (v w m : List Bool) : List Bool := + (sk : (ℕ × ℕ) × ((ℕ × ℕ) × (ℕ × ℕ))) (v w m ad : List Bool) : List Bool := let e := F.add (d, F.encodeProjHash d sk.1.1 sk.1.2 v w, m) - let r := ofBits (H (d, encBits3 (v, w, e))) + let r := ofBits (H (d, encBits4 (v, w, e, ad))) encBits4 (v, w, e, F.encodeProjHash2 d sk.2.1.1 sk.2.1.2 sk.2.2.1 sk.2.2.2 r v w) @[simp] theorem encodeCramerShoupChallenge_encode (H : F.Desc × List Bool → List Bool) (d : F.Desc) - (sk : (ℕ × ℕ) × ((ℕ × ℕ) × (ℕ × ℕ))) (v w m : F.Carrier d) : - F.encodeCramerShoupChallenge H d sk (F.encode d v) (F.encode d w) (F.encode d m) = + (sk : (ℕ × ℕ) × ((ℕ × ℕ) × (ℕ × ℕ))) (v w m : F.Carrier d) (ad : List Bool) : + F.encodeCramerShoupChallenge H d sk (F.encode d v) (F.encode d w) (F.encode d m) ad = let e := projHash F d sk.1.1 sk.1.2 v w + m - let ρ := F.cramerShoupHashMod H d (F.encodeCramerShoupTriple d v w e) + let ρ := F.cramerShoupHashMod H d (F.encodeCramerShoupTuple d v w e ad) let z' := projHash2 F d sk.2.1.1 sk.2.1.2 sk.2.2.1 sk.2.2.2 ρ v w F.encodeCramerShoupCiphertext d (v, w, e, z') := by simp only [encodeCramerShoupChallenge, encodeProjHash_encode, F.add_encode, - encodeProjHash2_encode, encodeCramerShoupTriple, cramerShoupHashMod, + encodeProjHash2_encode, encodeCramerShoupTuple, cramerShoupHashMod, encodeCramerShoupCiphertext] /-- Word-level Cramer–Shoup decryption ([BonehShoup2023], Section 12.5.3, Remark 12.3, and -Exercise 12.27, printed pp. 489, 493, 522): given `(d, sk, ct)`, unpack `(v, w, e, z') := +Exercise 12.27, printed pp. 489, 493, 522): given `(d, sk, ct, ad)`, unpack `(v, w, e, z') := decodeBits4 ct`, check canonical framing `encBits4 (v, w, e, z') == ct`, check group membership `isElem (d, v) && isElem (d, w) && isElem (d, e)`, and check -`encodeProjHash2 d s₁ t₁ s₂ t₂ r v w == z'`; if all pass, return -`some (encode (e - f_{s,t}(v, w)))`, else `none`. -/ +`encodeProjHash2 d s₁ t₁ s₂ t₂ r v w == z'` where `r = ofBits (H (d, encBits4 (v, w, e, ad)))`; if +all pass, return `some (encode (e - f_{s,t}(v, w)))`, else `none`. -/ def decodeCramerShoup (H : F.Desc × List Bool → List Bool) (isElem : F.Desc × List Bool → Bool) (d : F.Desc) - (sk : (ℕ × ℕ) × ((ℕ × ℕ) × (ℕ × ℕ))) (ct : List Bool) : Option (List Bool) := + (sk : (ℕ × ℕ) × ((ℕ × ℕ) × (ℕ × ℕ))) (ct ad : List Bool) : Option (List Bool) := let parts := decodeBits4 ct let v := parts.1; let w := parts.2.1; let e := parts.2.2.1; let z' := parts.2.2.2 - let r := ofBits (H (d, encBits3 (v, w, e))) + let r := ofBits (H (d, encBits4 (v, w, e, ad))) if (encBits4 parts == ct) && isElem (d, v) && isElem (d, w) && isElem (d, e) && (F.encodeProjHash2 d sk.2.1.1 sk.2.1.2 sk.2.2.1 sk.2.2.2 r v w == z') then some (F.add (d, e, F.encodeGroupNeg d (F.encodeProjHash d sk.1.1 sk.1.2 v w))) @@ -440,12 +716,12 @@ theorem decodeCramerShoup_encode [∀ d, DecidableEq (F.Carrier d)] (H : F.Desc × List Bool → List Bool) (isElem : F.Desc × List Bool → Bool) (hmem : ∀ d x, isElem (d, x) = true ↔ x ∈ Set.range (F.encode d)) (d : F.Desc) (sk : (ℕ × ℕ) × ((ℕ × ℕ) × (ℕ × ℕ))) - (ct : F.Carrier d × F.Carrier d × F.Carrier d × F.Carrier d) : - F.decodeCramerShoup H isElem d sk (F.encodeCramerShoupCiphertext d ct) = + (ct : F.Carrier d × F.Carrier d × F.Carrier d × F.Carrier d) (ad : List Bool) : + F.decodeCramerShoup H isElem d sk (F.encodeCramerShoupCiphertext d ct) ad = (F.cramerShoupDecrypt H d (((sk.1.1 : ZMod (F.order d)), (sk.1.2 : ZMod (F.order d))), (((sk.2.1.1 : ZMod (F.order d)), (sk.2.1.2 : ZMod (F.order d))), - ((sk.2.2.1 : ZMod (F.order d)), (sk.2.2.2 : ZMod (F.order d))))) ct).map + ((sk.2.2.1 : ZMod (F.order d)), (sk.2.2.2 : ZMod (F.order d))))) ct ad).map (F.encode d) := by rcases ct with ⟨v, w, e, z'⟩ have hv : isElem (d, F.encode d v) = true := (hmem d _).mpr ⟨v, rfl⟩ @@ -455,7 +731,7 @@ theorem decodeCramerShoup_encode [∀ d, DecidableEq (F.Carrier d)] rw [Bool.eq_iff_iff, beq_iff_eq, decide_eq_true_eq]; exact (F.encode_injective d).eq_iff simp only [decodeCramerShoup, encodeCramerShoupCiphertext, decodeBits4_encBits4, beq_self_eq_true, hv, hw, he, Bool.true_and, encodeProjHash2_encode, hinj, encodeProjHash_encode, - encodeGroupNeg_encode, F.add_encode, cramerShoupDecrypt, encodeCramerShoupTriple, + encodeGroupNeg_encode, F.add_encode, cramerShoupDecrypt, encodeCramerShoupTuple, cramerShoupHashMod, sub_eq_add_neg, decide_eq_true_eq] split_ifs <;> rfl @@ -466,12 +742,12 @@ theorem decodeCramerShoup_cramerShoupEncrypt (H : F.Desc × List Bool → List Bool) (isElem : F.Desc × List Bool → Bool) (hmem : ∀ d x, isElem (d, x) = true ↔ x ∈ Set.range (F.encode d)) (d : F.Desc) (α β : ZMod (F.order d)) - (sk : (ℕ × ℕ) × ((ℕ × ℕ) × (ℕ × ℕ))) (m : F.Carrier d) : + (sk : (ℕ × ℕ) × ((ℕ × ℕ) × (ℕ × ℕ))) (m : F.Carrier d) (ad : List Bool) : F.decodeCramerShoup H isElem d sk (F.encodeCramerShoupCiphertext d (F.cramerShoupEncrypt H d (F.cramerShoupPublicKey d α sk.1.1 sk.1.2 sk.2.1.1 sk.2.1.2 sk.2.2.1 sk.2.2.2) - m β)) = some (F.encode d m) := by + m ad β)) ad = some (F.encode d m) := by classical rw [F.decodeCramerShoup_encode H isElem hmem, F.cramerShoupDecrypt_cramerShoupEncrypt]; rfl @@ -563,19 +839,20 @@ private theorem computableTidilyInPolyTime_encodeCramerShoupChallenge {H : F.Desc × List Bool → List Bool} (hH : ComputableTidilyInPolyTime H (encPair F.encDesc encBits) encBits) {γ : Type} {encIn : γ ↪ List Bool} {d : γ → F.Desc} - {sk : γ → (ℕ × ℕ) × ((ℕ × ℕ) × (ℕ × ℕ))} {v w m : γ → List Bool} + {sk : γ → (ℕ × ℕ) × ((ℕ × ℕ) × (ℕ × ℕ))} {v w m ad : γ → List Bool} (hd : ComputableTidilyInPolyTime d encIn F.encDesc) (hsk : ComputableTidilyInPolyTime sk encIn encCramerShoupSK) (hv : ComputableTidilyInPolyTime v encIn encBits) (hw : ComputableTidilyInPolyTime w encIn encBits) - (hm : ComputableTidilyInPolyTime m encIn encBits) : + (hm : ComputableTidilyInPolyTime m encIn encBits) + (had : ComputableTidilyInPolyTime ad encIn encBits) : ComputableTidilyInPolyTime - (fun a => F.encodeCramerShoupChallenge H (d a) (sk a) (v a) (w a) (m a)) + (fun a => F.encodeCramerShoupChallenge H (d a) (sk a) (v a) (w a) (m a) (ad a)) encIn encBits := by have he := F.computableInPolyTime_add.tidy.comp (hd.prod ((F.computableTidilyInPolyTime_encodeProjHash hd hsk.fst'.fst' hsk.fst'.snd' hv hw).prod hm)) have hr := ComputableTidilyInPolyTime.bitsToNat.comp (hH.comp (hd.prod - ((ComputableTidilyInPolyTime.encode encBits3).comp (hv.prod (hw.prod he))))) + ((ComputableTidilyInPolyTime.encode encBits4).comp (hv.prod (hw.prod (he.prod had)))))) exact (ComputableTidilyInPolyTime.encode encBits4).comp (hv.prod (hw.prod (he.prod (F.computableTidilyInPolyTime_encodeProjHash2 hd hsk.snd'.fst'.fst' hsk.snd'.fst'.snd' hsk.snd'.snd'.fst' hsk.snd'.snd'.snd' hr hv hw)))) @@ -586,33 +863,36 @@ theorem computableInPolyTime_encodeCramerShoupChallenge {H : F.Desc × List Bool → List Bool} (hH : ComputableInPolyTime H (encPair F.encDesc encBits) encBits) : ComputableInPolyTime - (fun p : F.Desc × ((ℕ × ℕ) × ((ℕ × ℕ) × (ℕ × ℕ))) × List Bool × List Bool × List Bool => - F.encodeCramerShoupChallenge H p.1 p.2.1 p.2.2.1 p.2.2.2.1 p.2.2.2.2) - (encPair F.encDesc (encPair encCramerShoupSK (encPair encBits (encPair encBits encBits)))) + (fun p : F.Desc × ((ℕ × ℕ) × ((ℕ × ℕ) × (ℕ × ℕ))) × + List Bool × List Bool × List Bool × List Bool => + F.encodeCramerShoupChallenge H p.1 p.2.1 p.2.2.1 p.2.2.2.1 p.2.2.2.2.1 p.2.2.2.2.2) + (encPair F.encDesc (encPair encCramerShoupSK + (encPair encBits (encPair encBits (encPair encBits encBits))))) encBits := .of_tidy (F.computableTidilyInPolyTime_encodeCramerShoupChallenge hH.tidy - (by fun_prop) (by fun_prop) (by fun_prop) (by fun_prop) (by fun_prop)) + (by fun_prop) (by fun_prop) (by fun_prop) (by fun_prop) (by fun_prop) (by fun_prop)) @[local fun_prop] private theorem computableTidilyInPolyTime_encodeCramerShoupEncrypt {H : F.Desc × List Bool → List Bool} (hH : ComputableTidilyInPolyTime H (encPair F.encDesc encBits) encBits) {γ : Type} {encIn : γ ↪ List Bool} {d : γ → F.Desc} - {pk : γ → List Bool × List Bool × List Bool × List Bool} {m : γ → List Bool} {b : γ → ℕ} + {pk : γ → List Bool × List Bool × List Bool × List Bool} {m ad : γ → List Bool} {b : γ → ℕ} (hd : ComputableTidilyInPolyTime d encIn F.encDesc) (hpk : ComputableTidilyInPolyTime pk encIn (encPair encBits (encPair encBits (encPair encBits encBits)))) (hm : ComputableTidilyInPolyTime m encIn encBits) + (had : ComputableTidilyInPolyTime ad encIn encBits) (hb : ComputableTidilyInPolyTime b encIn encNat) : ComputableTidilyInPolyTime - (fun a => F.encodeCramerShoupEncrypt H (d a) (pk a) (m a) (b a)) encIn encBits := by + (fun a => F.encodeCramerShoupEncrypt H (d a) (pk a) (m a) (ad a) (b a)) encIn encBits := by have hv := F.computableInPolyTime_smul.tidy.comp (hd.prod (hb.prod (F.computableInPolyTime_generator.tidy.comp hd))) have hw := F.computableInPolyTime_smul.tidy.comp (hd.prod (hb.prod hpk.fst')) have he := F.computableInPolyTime_add.tidy.comp (hd.prod ((F.computableInPolyTime_smul.tidy.comp (hd.prod (hb.prod hpk.snd'.fst'))).prod hm)) have hr := ComputableTidilyInPolyTime.bitsToNat.comp (hH.comp (hd.prod - ((ComputableTidilyInPolyTime.encode encBits3).comp (hv.prod (hw.prod he))))) + ((ComputableTidilyInPolyTime.encode encBits4).comp (hv.prod (hw.prod (he.prod had)))))) have hz' := F.computableInPolyTime_smul.tidy.comp (hd.prod (hb.prod (F.computableInPolyTime_add.tidy.comp (hd.prod (hpk.snd'.snd'.fst'.prod (F.computableInPolyTime_smul.tidy.comp (hd.prod (hr.prod hpk.snd'.snd'.snd')))))))) @@ -623,11 +903,12 @@ theorem computableInPolyTime_encodeCramerShoupEncrypt {H : F.Desc × List Bool → List Bool} (hH : ComputableInPolyTime H (encPair F.encDesc encBits) encBits) : ComputableInPolyTime - (fun p : F.Desc × (List Bool × List Bool × List Bool × List Bool) × List Bool × ℕ => - F.encodeCramerShoupEncrypt H p.1 p.2.1 p.2.2.1 p.2.2.2) - (encPair F.encDesc (encPair encBits4 (encPair encBits encNat))) encBits := + (fun p : F.Desc × (List Bool × List Bool × List Bool × List Bool) × + List Bool × List Bool × ℕ => + F.encodeCramerShoupEncrypt H p.1 p.2.1 p.2.2.1 p.2.2.2.1 p.2.2.2.2) + (encPair F.encDesc (encPair encBits4 (encPair encBits (encPair encBits encNat)))) encBits := .of_tidy (F.computableTidilyInPolyTime_encodeCramerShoupEncrypt hH.tidy - (by fun_prop) (by fun_prop) (by fun_prop) (by fun_prop)) + (by fun_prop) (by fun_prop) (by fun_prop) (by fun_prop) (by fun_prop)) @[local fun_prop] private theorem computableTidilyInPolyTime_decodeCramerShoup @@ -635,18 +916,19 @@ private theorem computableTidilyInPolyTime_decodeCramerShoup (hH : ComputableTidilyInPolyTime H (encPair F.encDesc encBits) encBits) (hisElem : ComputableTidilyInPolyTime isElem (encPair F.encDesc encBits) encBool) {γ : Type} {encIn : γ ↪ List Bool} {d : γ → F.Desc} - {sk : γ → (ℕ × ℕ) × ((ℕ × ℕ) × (ℕ × ℕ))} {ct : γ → List Bool} + {sk : γ → (ℕ × ℕ) × ((ℕ × ℕ) × (ℕ × ℕ))} {ct ad : γ → List Bool} (hd : ComputableTidilyInPolyTime d encIn F.encDesc) (hsk : ComputableTidilyInPolyTime sk encIn encCramerShoupSK) - (hct : ComputableTidilyInPolyTime ct encIn encBits) : + (hct : ComputableTidilyInPolyTime ct encIn encBits) + (had : ComputableTidilyInPolyTime ad encIn encBits) : ComputableTidilyInPolyTime - (fun a => F.decodeCramerShoup H isElem (d a) (sk a) (ct a)) + (fun a => F.decodeCramerShoup H isElem (d a) (sk a) (ct a) (ad a)) encIn (encOption encBits) := by have hparts := computableTidilyInPolyTime_decodeBits4.comp hct have hv := hparts.fst'; have hw := hparts.snd'.fst' have he := hparts.snd'.snd'.fst'; have hz' := hparts.snd'.snd'.snd' have hr := ComputableTidilyInPolyTime.bitsToNat.comp (hH.comp (hd.prod - ((ComputableTidilyInPolyTime.encode encBits3).comp (hv.prod (hw.prod he))))) + ((ComputableTidilyInPolyTime.encode encBits4).comp (hv.prod (hw.prod (he.prod had)))))) have hbeq : ComputableTidilyInPolyTime (fun p : List Bool × List Bool => p.1 == p.2) (encPair encBits encBits) encBool := by polytime have hand : ComputableTidilyInPolyTime @@ -672,11 +954,12 @@ theorem computableInPolyTime_decodeCramerShoup (hH : ComputableInPolyTime H (encPair F.encDesc encBits) encBits) (hisElem : ComputableInPolyTime isElem (encPair F.encDesc encBits) encBool) : ComputableInPolyTime - (fun p : F.Desc × ((ℕ × ℕ) × ((ℕ × ℕ) × (ℕ × ℕ))) × List Bool => - F.decodeCramerShoup H isElem p.1 p.2.1 p.2.2) - (encPair F.encDesc (encPair encCramerShoupSK encBits)) (encOption encBits) := + (fun p : F.Desc × ((ℕ × ℕ) × ((ℕ × ℕ) × (ℕ × ℕ))) × List Bool × List Bool => + F.decodeCramerShoup H isElem p.1 p.2.1 p.2.2.1 p.2.2.2) + (encPair F.encDesc (encPair encCramerShoupSK (encPair encBits encBits))) + (encOption encBits) := .of_tidy (F.computableTidilyInPolyTime_decodeCramerShoup hH.tidy hisElem.tidy - (by fun_prop) (by fun_prop) (by fun_prop)) + (by fun_prop) (by fun_prop) (by fun_prop) (by fun_prop)) /-- Change of variables `σ ↦ δ - α * τ` under two independent uniform draws in `ZMod q`. -/ private theorem uniform_sub_mul_bind {α_out : Type*} (q : ℕ) [NeZero q] (α : ZMod q) @@ -704,44 +987,47 @@ private theorem uniform_sub_bind {α_out : Type*} (q : ℕ) [NeZero q] (c : ZMod /-! ### Collision detection and decryption oracle replies -/ -/-- Test whether a Phase 2 ciphertext query `req` yields a `cramerShoupHashMod H d` collision with -the challenge ciphertext `ct` on their `(v, w, e)` prefixes. -/ +/-- Test whether a Phase 2 ciphertext query `(ct_q, ad_q)` yields a `cramerShoupHashMod H d` +collision with the challenge `(ct, ad)` on their `(v, w, e, ad)` tuples. -/ def isCramerShoupCollision (H : F.Desc × List Bool → List Bool) - (d : F.Desc) (req ct : List Bool) : Bool := - let x := extractCramerShoupTriple req - let y := extractCramerShoupTriple ct + (d : F.Desc) (ct_q ad_q ct ad : List Bool) : Bool := + let x := extractCramerShoupTuple ct_q ad_q + let y := extractCramerShoupTuple ct ad (x != y) && ((ofBits (H (d, x)) % F.order d) == (ofBits (H (d, y)) % F.order d)) /-- `isCramerShoupCollision` coincides with `isCollisionFor (F.cramerShoupHashMod H)` on the -extracted `(v, w, e)` triples. -/ +extracted `(v, w, e, ad)` tuples. -/ theorem isCramerShoupCollision_eq_isCollisionFor (H : F.Desc × List Bool → List Bool) - (n : ℕ) (d : F.Desc) (req ct : List Bool) : - F.isCramerShoupCollision H d req ct = + (n : ℕ) (d : F.Desc) (ct_q ad_q ct ad : List Bool) : + F.isCramerShoupCollision H d ct_q ad_q ct ad = isCollisionFor (F.cramerShoupHashMod H) (fun _ _ _ => true) n d - (extractCramerShoupTriple req) (extractCramerShoupTriple ct) := by + (extractCramerShoupTuple ct_q ad_q) (extractCramerShoupTuple ct ad) := by rw [Bool.eq_iff_iff] simp only [isCramerShoupCollision, isCollisionFor, cramerShoupHashMod, Bool.true_and, Bool.and_eq_true, bne_iff_ne, beq_iff_eq, decide_eq_true_eq, ZMod.natCast_eq_natCast_iff'] -/-- Stateless Phase 1 decryption reply for Cramer–Shoup. -/ +/-- Stateless Phase 1 decryption reply for Cramer–Shoup (`hasAD = true` for `cca2ADPhase1Oracle`, +`hasAD = false` for `cca2Phase1Oracle`). -/ def cramerShoupPhase1Reply - (H : F.Desc × List Bool → List Bool) (isElem : F.Desc × List Bool → Bool) + (hasAD : Bool) (H : F.Desc × List Bool → List Bool) (isElem : F.Desc × List Bool → Bool) (d : F.Desc) (sk : (ℕ × ℕ) × ((ℕ × ℕ) × (ℕ × ℕ))) (req : List Bool) : List Bool := - encOption encBits (F.decodeCramerShoup H isElem d sk req) + let q := splitCramerShoupQuery hasAD req + encOption encBits (F.decodeCramerShoup H isElem d sk q.1 q.2) -/-- Stateless Phase 2 decryption reply that rejects the challenge ciphertext `ct` and any query -`req` colliding with `ct` under `cramerShoupHashMod H d`. -/ +/-- Stateless Phase 2 decryption reply that rejects the challenge `(ct, ad)` and any query +`(ct_q, ad_q)` colliding with `(ct, ad)` under `cramerShoupHashMod H d`. -/ def cramerShoupCollisionPhase2Reply - (H : F.Desc × List Bool → List Bool) (isElem : F.Desc × List Bool → Bool) - (d : F.Desc) (sk : (ℕ × ℕ) × ((ℕ × ℕ) × (ℕ × ℕ))) (ct req : List Bool) : List Bool := - if (req == ct) || F.isCramerShoupCollision H d req ct then + (hasAD : Bool) (H : F.Desc × List Bool → List Bool) (isElem : F.Desc × List Bool → Bool) + (d : F.Desc) (sk : (ℕ × ℕ) × ((ℕ × ℕ) × (ℕ × ℕ))) (ct ad req : List Bool) : List Bool := + let q := splitCramerShoupQuery hasAD req + if ((q.1 == ct) && (q.2 == ad)) || F.isCramerShoupCollision H d q.1 q.2 ct ad then [] else - encOption encBits (F.decodeCramerShoup H isElem d sk req) + encOption encBits (F.decodeCramerShoup H isElem d sk q.1 q.2) /-! ### Canonical secret-key conversion and hybrid Games 3–5 -/ @@ -752,14 +1038,16 @@ def skNatOfZMod (d : F.Desc) (σ τ σ₁ τ₁ σ₂ τ₂ : ZMod (F.order d)) ((σ.val, τ.val), ((σ₁.val, τ₁.val), (σ₂.val, τ₂.val))) /-- **Game 3** (corresponding to Game 4 of [BonehShoup2023], Section 12.5.3, printed p. 492, -adapted to $\mathcal{E}_{\mathrm{MCS}}$): Phase 1 uses `cramerShoupPhase1Reply` and Phase 2 rejects -both `ct` and any ciphertext query `req` whose `(v, w, e)` prefix collides with `ct` under -`cramerShoupHashMod H d` (`cramerShoupCollisionPhase2Reply`). -/ +adapted to $\mathcal{E}_{\mathrm{MCS}}$ with associated data): Phase 1 uses `cramerShoupPhase1Reply` +and Phase 2 rejects both `(ct, ad)` and any ciphertext query `(ct_q, ad_q)` whose `(v, w, e, ad)` +tuple collides with `(ct, ad)` under `cramerShoupHashMod H d` +(`cramerShoupCollisionPhase2Reply`). -/ noncomputable def cramerShoupGame3 - {Msg : Type} (encodeMsg : F.Desc × Msg → List Bool) + (hasAD : Bool) {Msg AD : Type} (encodeMsg : F.Desc × Msg → List Bool) + (encodeAD : F.Desc × AD → List Bool) (setup : ℕ → PMF F.Desc) (H : F.Desc × List Bool → List Bool) (isElem : F.Desc × List Bool → Bool) - (adversary : CCA2AdversaryOf Msg) (bit : Bool) (n : ℕ) : Game := do + (adversary : ADCCA2AdversaryOf Msg AD) (bit : Bool) (n : ℕ) : Game := do let d ← setup n; let U := PMF.uniformOfFintype (ZMod (F.order d)) let α ← U; let β ← U; let γ ← U; let σ ← U; let τ ← U; let σ₁ ← U; let τ₁ ← U; let σ₂ ← U let τ₂ ← U @@ -767,13 +1055,14 @@ noncomputable def cramerShoupGame3 let u := F.encode d (α • F.generator d); let v := F.encode d (β • F.generator d) let w := F.encode d (γ • F.generator d) let pk := F.encodeCramerShoupPublicKey d u sk - runStatelessOracle (F.cramerShoupPhase1Reply H isElem d sk) + runStatelessOracle (F.cramerShoupPhase1Reply hasAD H isElem d sk) (adversary.choose n pk) fun choice => - let m₀ := encodeMsg (d, choice.1.1); let m₁ := encodeMsg (d, choice.1.2) + let m₀ := encodeMsg (d, choice.1.1.1); let m₁ := encodeMsg (d, choice.1.1.2) + let ad := encodeAD (d, choice.1.2) if isElem (d, m₀) && isElem (d, m₁) then let m := if bit then m₁ else m₀ - let ct := F.encodeCramerShoupChallenge H d sk v w m - runStatelessOracle (F.cramerShoupCollisionPhase2Reply H isElem d sk ct) + let ct := F.encodeCramerShoupChallenge H d sk v w m ad + runStatelessOracle (F.cramerShoupCollisionPhase2Reply hasAD H isElem d sk ct ad) (adversary.guess n choice.2 ct) PMF.pure else PMF.pure false @@ -787,41 +1076,43 @@ pair does not satisfy `w' == F.smul (d, α.val, v')`, and otherwise decrypt usin by `projHash_fiber_eq` and `projHash2_fiber_dh`). -/ def decodeCramerShoupDH (H : F.Desc × List Bool → List Bool) (isElem : F.Desc × List Bool → Bool) - (d : F.Desc) (α δ δ₁ δ₂ : ZMod (F.order d)) (ct : List Bool) : Option (List Bool) := + (d : F.Desc) (α δ δ₁ δ₂ : ZMod (F.order d)) (ct ad : List Bool) : Option (List Bool) := let parts := decodeBits4 ct if parts.2.1 == F.smul (d, α.val, parts.1) then - F.decodeCramerShoup H isElem d (F.skNatOfZMod d δ 0 δ₁ 0 δ₂ 0) ct + F.decodeCramerShoup H isElem d (F.skNatOfZMod d δ 0 δ₁ 0 δ₂ 0) ct ad else none /-- Stateless Phase 1 decryption reply for Game 3.5 and Game 4, rejecting non-DH queries. -/ def cramerShoupDHPhase1Reply - (H : F.Desc × List Bool → List Bool) (isElem : F.Desc × List Bool → Bool) + (hasAD : Bool) (H : F.Desc × List Bool → List Bool) (isElem : F.Desc × List Bool → Bool) (d : F.Desc) (α δ δ₁ δ₂ : ZMod (F.order d)) (req : List Bool) : List Bool := - encOption encBits (F.decodeCramerShoupDH H isElem d α δ δ₁ δ₂ req) + let q := splitCramerShoupQuery hasAD req + encOption encBits (F.decodeCramerShoupDH H isElem d α δ δ₁ δ₂ q.1 q.2) -/-- Stateless Phase 2 decryption reply for Game 4, rejecting `ct`, `H'`-collisions with `ct`, and -non-DH queries. -/ +/-- Stateless Phase 2 decryption reply for Game 4, rejecting `(ct, ad)`, `H'`-collisions with +`(ct, ad)`, and non-DH queries. -/ def cramerShoupDHPhase2Reply - (H : F.Desc × List Bool → List Bool) (isElem : F.Desc × List Bool → Bool) - (d : F.Desc) (α δ δ₁ δ₂ : ZMod (F.order d)) (ct req : List Bool) : List Bool := - if (req == ct) || F.isCramerShoupCollision H d req ct then + (hasAD : Bool) (H : F.Desc × List Bool → List Bool) (isElem : F.Desc × List Bool → Bool) + (d : F.Desc) (α δ δ₁ δ₂ : ZMod (F.order d)) (ct ad req : List Bool) : List Bool := + let q := splitCramerShoupQuery hasAD req + if ((q.1 == ct) && (q.2 == ad)) || F.isCramerShoupCollision H d q.1 q.2 ct ad then [] else - encOption encBits (F.decodeCramerShoupDH H isElem d α δ δ₁ δ₂ req) + encOption encBits (F.decodeCramerShoupDH H isElem d α δ δ₁ δ₂ q.1 q.2) /-- Bad-event predicate for Game 3 $\to$ Game 4 ([BonehShoup2023], Section 12.5.3, equations -(12.26)–(12.27), printed pp. 492–493): `req = encBits4 (v', w', e', z')` is a syntactically +(12.26)–(12.27), printed pp. 492–493): `ct_q = encBits4 (v', w', e', z')` is a syntactically well-formed non-DH ciphertext (`w' != F.smul (d, α.val, v')`) whose tag `z'` matches -`encodeProjHash2`. -/ +`encodeProjHash2` on `(v', w', e', ad_q)`. -/ def isCramerShoupBadDH (H : F.Desc × List Bool → List Bool) (isElem : F.Desc × List Bool → Bool) (d : F.Desc) (α : ZMod (F.order d)) - (sk : (ℕ × ℕ) × ((ℕ × ℕ) × (ℕ × ℕ))) (req : List Bool) : Bool := - let parts := decodeBits4 req + (sk : (ℕ × ℕ) × ((ℕ × ℕ) × (ℕ × ℕ))) (ct_q ad_q : List Bool) : Bool := + let parts := decodeBits4 ct_q let v := parts.1; let w := parts.2.1; let e := parts.2.2.1; let z' := parts.2.2.2 - let r := ofBits (H (d, encBits3 (v, w, e))) - (encBits4 parts == req) && isElem (d, v) && isElem (d, w) && isElem (d, e) && + let r := ofBits (H (d, encBits4 (v, w, e, ad_q))) + (encBits4 parts == ct_q) && isElem (d, v) && isElem (d, w) && isElem (d, e) && (w != F.smul (d, α.val, v)) && (F.encodeProjHash2 d sk.2.1.1 sk.2.1.2 sk.2.2.1 sk.2.2.2 r v w == z') @@ -837,13 +1128,13 @@ private theorem encodeCramerShoupPublicKey_fiber_eq (d : F.Desc) private theorem encodeCramerShoupChallenge_fiber_eq (H : F.Desc × List Bool → List Bool) (d : F.Desc) - (α β γ δ τ δ₁ δ₂ ξ τ₂ : ZMod (F.order d)) (m : List Bool) : + (α β γ δ τ δ₁ δ₂ ξ τ₂ : ZMod (F.order d)) (m ad : List Bool) : let v := F.encode d (β • F.generator d); let w := F.encode d (γ • F.generator d) let e := F.add (d, F.encodeProjHash d (δ - α * τ).val τ.val v w, m) - let ρ := F.cramerShoupHashMod H d (encBits3 (v, w, e)) + let ρ := F.cramerShoupHashMod H d (encBits4 (v, w, e, ad)) F.encodeCramerShoupChallenge H d (F.skNatOfZMod d (δ - α * τ) τ (δ₁ - α * (ξ - ρ * τ₂)) (ξ - ρ * τ₂) - (δ₂ - α * τ₂) τ₂) v w m = + (δ₂ - α * τ₂) τ₂) v w m ad = encBits4 (v, w, e, F.encode d ((β * (δ₁ + ρ * δ₂) + (γ - α * β) * ξ) • F.generator d)) := by simp only [encodeCramerShoupChallenge, skNatOfZMod, encodeProjHash2_encode, ZMod.natCast_zmod_val, cramerShoupHashMod, projHash2_fiber_challenge_eq] @@ -851,24 +1142,24 @@ private theorem encodeCramerShoupChallenge_fiber_eq private theorem decodeCramerShoup_fiber_eq_decodeCramerShoupDH_of_not_bad (H : F.Desc × List Bool → List Bool) (isElem : F.Desc × List Bool → Bool) (hmem : ∀ d x, isElem (d, x) = true ↔ x ∈ Set.range (F.encode d)) - (d : F.Desc) (α δ τ δ₁ τ₁ δ₂ τ₂ : ZMod (F.order d)) (req : List Bool) + (d : F.Desc) (α δ τ δ₁ τ₁ δ₂ τ₂ : ZMod (F.order d)) (ct_q ad_q : List Bool) (hbad : F.isCramerShoupBadDH H isElem d α - (F.skNatOfZMod d (δ - α * τ) τ (δ₁ - α * τ₁) τ₁ (δ₂ - α * τ₂) τ₂) req = false) : + (F.skNatOfZMod d (δ - α * τ) τ (δ₁ - α * τ₁) τ₁ (δ₂ - α * τ₂) τ₂) ct_q ad_q = false) : F.decodeCramerShoup H isElem d - (F.skNatOfZMod d (δ - α * τ) τ (δ₁ - α * τ₁) τ₁ (δ₂ - α * τ₂) τ₂) req = - F.decodeCramerShoupDH H isElem d α δ δ₁ δ₂ req := by + (F.skNatOfZMod d (δ - α * τ) τ (δ₁ - α * τ₁) τ₁ (δ₂ - α * τ₂) τ₂) ct_q ad_q = + F.decodeCramerShoupDH H isElem d α δ δ₁ δ₂ ct_q ad_q := by unfold decodeCramerShoupDH - by_cases hdh : ((decodeBits4 req).2.1 == F.smul (d, α.val, (decodeBits4 req).1)) = true + by_cases hdh : ((decodeBits4 ct_q).2.1 == F.smul (d, α.val, (decodeBits4 ct_q).1)) = true · simp only [hdh, ↓reduceIte] unfold decodeCramerShoup - by_cases hv : isElem (d, (decodeBits4 req).1) = true + by_cases hv : isElem (d, (decodeBits4 ct_q).1) = true · obtain ⟨vElem, hvElem⟩ := (hmem d _).mp hv obtain ⟨β', rfl⟩ := F.smul_generator_surjective d vElem - have hwElem : (decodeBits4 req).2.1 = F.encode d ((α * β') • F.generator d) := by + have hwElem : (decodeBits4 ct_q).2.1 = F.encode d ((α * β') • F.generator d) := by rw [beq_iff_eq.mp hdh, ← hvElem, F.smul_encode, ZMod.natCast_zmod_val, smul_smul] have hproj1 : - F.encodeProjHash d (δ - α * τ).val τ.val (decodeBits4 req).1 (decodeBits4 req).2.1 = - F.encodeProjHash d δ.val 0 (decodeBits4 req).1 (decodeBits4 req).2.1 := by + F.encodeProjHash d (δ - α * τ).val τ.val (decodeBits4 ct_q).1 (decodeBits4 ct_q).2.1 = + F.encodeProjHash d δ.val 0 (decodeBits4 ct_q).1 (decodeBits4 ct_q).2.1 := by rw [← hvElem, hwElem, encodeProjHash_encode, encodeProjHash_encode] simp only [ZMod.natCast_zmod_val, Nat.cast_zero] have hfib1 := projHash_fiber_eq F d α β' (α * β') δ τ @@ -878,9 +1169,9 @@ private theorem decodeCramerShoup_fiber_eq_decodeCramerShoupDH_of_not_bad rw [hfib1, hfib0] have hproj2 (r : ℕ) : F.encodeProjHash2 d (δ₁ - α * τ₁).val τ₁.val (δ₂ - α * τ₂).val τ₂.val r - (decodeBits4 req).1 (decodeBits4 req).2.1 = + (decodeBits4 ct_q).1 (decodeBits4 ct_q).2.1 = F.encodeProjHash2 d δ₁.val 0 δ₂.val 0 r - (decodeBits4 req).1 (decodeBits4 req).2.1 := by + (decodeBits4 ct_q).1 (decodeBits4 ct_q).2.1 := by rw [← hvElem, hwElem, encodeProjHash2_encode, encodeProjHash2_encode] simp only [ZMod.natCast_zmod_val, Nat.cast_zero] have hfib2 := projHash2_fiber_dh F d α β' δ₁ δ₂ τ₁ τ₂ (r : ZMod (F.order d)) @@ -891,43 +1182,48 @@ private theorem decodeCramerShoup_fiber_eq_decodeCramerShoupDH_of_not_bad · simp only [Bool.not_eq_true] at hv; simp [hv] · simp only [Bool.not_eq_true] at hdh simp only [hdh, Bool.false_eq_true, ↓reduceIte] - have hne : ((decodeBits4 req).2.1 != F.smul (d, α.val, (decodeBits4 req).1)) = true := by + have hne : ((decodeBits4 ct_q).2.1 != F.smul (d, α.val, (decodeBits4 ct_q).1)) = true := by simpa [bne] using hdh unfold decodeCramerShoup; dsimp only; rw [ite_eq_right_iff] intro h; exfalso; revert h hbad; simp [isCramerShoupBadDH, hne] private theorem cramerShoupPhase1Reply_fiber_eq_of_not_bad - (H : F.Desc × List Bool → List Bool) (isElem : F.Desc × List Bool → Bool) + (hasAD : Bool) (H : F.Desc × List Bool → List Bool) (isElem : F.Desc × List Bool → Bool) (hmem : ∀ d x, isElem (d, x) = true ↔ x ∈ Set.range (F.encode d)) (d : F.Desc) (α δ τ δ₁ τ₁ δ₂ τ₂ : ZMod (F.order d)) (req : List Bool) (hbad : F.isCramerShoupBadDH H isElem d α - (F.skNatOfZMod d (δ - α * τ) τ (δ₁ - α * τ₁) τ₁ (δ₂ - α * τ₂) τ₂) req = false) : - F.cramerShoupPhase1Reply H isElem d + (F.skNatOfZMod d (δ - α * τ) τ (δ₁ - α * τ₁) τ₁ (δ₂ - α * τ₂) τ₂) + (splitCramerShoupQuery hasAD req).1 (splitCramerShoupQuery hasAD req).2 = false) : + F.cramerShoupPhase1Reply hasAD H isElem d (F.skNatOfZMod d (δ - α * τ) τ (δ₁ - α * τ₁) τ₁ (δ₂ - α * τ₂) τ₂) req = - F.cramerShoupDHPhase1Reply H isElem d α δ δ₁ δ₂ req := by + F.cramerShoupDHPhase1Reply hasAD H isElem d α δ δ₁ δ₂ req := by simp only [cramerShoupPhase1Reply, cramerShoupDHPhase1Reply, F.decodeCramerShoup_fiber_eq_decodeCramerShoupDH_of_not_bad - H isElem hmem d α δ τ δ₁ τ₁ δ₂ τ₂ req hbad] + H isElem hmem d α δ τ δ₁ τ₁ δ₂ τ₂ _ _ hbad] private theorem cramerShoupCollisionPhase2Reply_fiber_eq_of_not_bad - (H : F.Desc × List Bool → List Bool) (isElem : F.Desc × List Bool → Bool) + (hasAD : Bool) (H : F.Desc × List Bool → List Bool) (isElem : F.Desc × List Bool → Bool) (hmem : ∀ d x, isElem (d, x) = true ↔ x ∈ Set.range (F.encode d)) - (d : F.Desc) (α δ τ δ₁ τ₁ δ₂ τ₂ : ZMod (F.order d)) (ct req : List Bool) - (hbad : (!((req == ct) || F.isCramerShoupCollision H d req ct) && + (d : F.Desc) (α δ τ δ₁ τ₁ δ₂ τ₂ : ZMod (F.order d)) (ct ad req : List Bool) + (hbad : (let q := splitCramerShoupQuery hasAD req + !(((q.1 == ct) && (q.2 == ad)) || F.isCramerShoupCollision H d q.1 q.2 ct ad) && F.isCramerShoupBadDH H isElem d α - (F.skNatOfZMod d (δ - α * τ) τ (δ₁ - α * τ₁) τ₁ (δ₂ - α * τ₂) τ₂) req) = false) : - F.cramerShoupCollisionPhase2Reply H isElem d - (F.skNatOfZMod d (δ - α * τ) τ (δ₁ - α * τ₁) τ₁ (δ₂ - α * τ₂) τ₂) ct req = - F.cramerShoupDHPhase2Reply H isElem d α δ δ₁ δ₂ ct req := by + (F.skNatOfZMod d (δ - α * τ) τ (δ₁ - α * τ₁) τ₁ (δ₂ - α * τ₂) τ₂) q.1 q.2) = false) : + F.cramerShoupCollisionPhase2Reply hasAD H isElem d + (F.skNatOfZMod d (δ - α * τ) τ (δ₁ - α * τ₁) τ₁ (δ₂ - α * τ₂) τ₂) ct ad req = + F.cramerShoupDHPhase2Reply hasAD H isElem d α δ δ₁ δ₂ ct ad req := by unfold cramerShoupCollisionPhase2Reply cramerShoupDHPhase2Reply - by_cases hrej : ((req == ct) || F.isCramerShoupCollision H d req ct) = true - · simp [hrej] + dsimp only + let q := splitCramerShoupQuery hasAD req + by_cases hrej : + (((q.1 == ct) && (q.2 == ad)) || F.isCramerShoupCollision H d q.1 q.2 ct ad) = true + · simp [q, hrej] · simp only [Bool.not_eq_true] at hrej have hbad' : F.isCramerShoupBadDH H isElem d α - (F.skNatOfZMod d (δ - α * τ) τ (δ₁ - α * τ₁) τ₁ (δ₂ - α * τ₂) τ₂) req = false := by - simpa [hrej] using hbad - simp [hrej, F.decodeCramerShoup_fiber_eq_decodeCramerShoupDH_of_not_bad - H isElem hmem d α δ τ δ₁ τ₁ δ₂ τ₂ req hbad'] + (F.skNatOfZMod d (δ - α * τ) τ (δ₁ - α * τ₁) τ₁ (δ₂ - α * τ₂) τ₂) q.1 q.2 = false := by + simpa [q, hrej] using hbad + simp [q, hrej, F.decodeCramerShoup_fiber_eq_decodeCramerShoupDH_of_not_bad + H isElem hmem d α δ τ δ₁ τ₁ δ₂ τ₂ q.1 q.2 hbad'] private theorem tsum_uniform_smul_generator_eq {d : F.Desc} (e : ZMod (F.order d) ≃ ZMod (F.order d)) (z' : List Bool) : @@ -951,19 +1247,19 @@ private theorem tsum_uniform_smul_generator_eq {d : F.Desc} private theorem exists_nonDH_of_badDH_pre (isElem : F.Desc × List Bool → Bool) (hmem : ∀ d x, isElem (d, x) = true ↔ x ∈ Set.range (F.encode d)) - (d : F.Desc) (α : ZMod (F.order d)) (req : List Bool) - (hpre : ((encBits4 (decodeBits4 req) == req) && isElem (d, (decodeBits4 req).1) && - isElem (d, (decodeBits4 req).2.1) && isElem (d, (decodeBits4 req).2.2.1) && - ((decodeBits4 req).2.1 != F.smul (d, α.val, (decodeBits4 req).1))) = true) : - ∃ β' γ' : ZMod (F.order d), F.encode d (β' • F.generator d) = (decodeBits4 req).1 ∧ - F.encode d (γ' • F.generator d) = (decodeBits4 req).2.1 ∧ γ' ≠ α * β' := by - have hvw : isElem (d, (decodeBits4 req).1) = true ∧ - isElem (d, (decodeBits4 req).2.1) = true ∧ - ((decodeBits4 req).2.1 != F.smul (d, α.val, (decodeBits4 req).1)) = true := by + (d : F.Desc) (α : ZMod (F.order d)) (ct_q : List Bool) + (hpre : ((encBits4 (decodeBits4 ct_q) == ct_q) && isElem (d, (decodeBits4 ct_q).1) && + isElem (d, (decodeBits4 ct_q).2.1) && isElem (d, (decodeBits4 ct_q).2.2.1) && + ((decodeBits4 ct_q).2.1 != F.smul (d, α.val, (decodeBits4 ct_q).1))) = true) : + ∃ β' γ' : ZMod (F.order d), F.encode d (β' • F.generator d) = (decodeBits4 ct_q).1 ∧ + F.encode d (γ' • F.generator d) = (decodeBits4 ct_q).2.1 ∧ γ' ≠ α * β' := by + have hvw : isElem (d, (decodeBits4 ct_q).1) = true ∧ + isElem (d, (decodeBits4 ct_q).2.1) = true ∧ + ((decodeBits4 ct_q).2.1 != F.smul (d, α.val, (decodeBits4 ct_q).1)) = true := by revert hpre - cases encBits4 (decodeBits4 req) == req <;> cases isElem (d, (decodeBits4 req).1) <;> - cases isElem (d, (decodeBits4 req).2.1) <;> cases isElem (d, (decodeBits4 req).2.2.1) <;> - cases (decodeBits4 req).2.1 != F.smul (d, α.val, (decodeBits4 req).1) <;> simp + cases encBits4 (decodeBits4 ct_q) == ct_q <;> cases isElem (d, (decodeBits4 ct_q).1) <;> + cases isElem (d, (decodeBits4 ct_q).2.1) <;> cases isElem (d, (decodeBits4 ct_q).2.2.1) <;> + cases (decodeBits4 ct_q).2.1 != F.smul (d, α.val, (decodeBits4 ct_q).1) <;> simp obtain ⟨vElem, hvElem⟩ := (hmem d _).mp hvw.1; obtain ⟨wElem, hwElem⟩ := (hmem d _).mp hvw.2.1 obtain ⟨β', rfl⟩ := F.smul_generator_surjective d vElem obtain ⟨γ', rfl⟩ := F.smul_generator_surjective d wElem @@ -975,33 +1271,33 @@ private theorem tsum_and_isCramerShoupBadDH_le (hmem : ∀ d x, isElem (d, x) = true ↔ x ∈ Set.range (F.encode d)) (d : F.Desc) (α : ZMod (F.order d)) (sk : ZMod (F.order d) → (ℕ × ℕ) × ((ℕ × ℕ) × (ℕ × ℕ))) - (cond : Bool) (req : List Bool) + (cond : Bool) (ct_q ad_q : List Bool) (hequiv : ∀ β' γ' : ZMod (F.order d), γ' ≠ α * β' → ∃ e : ZMod (F.order d) ≃ ZMod (F.order d), ∀ τ, projHash2 F d ((sk τ).2.1.1 : ZMod (F.order d)) ((sk τ).2.1.2 : ZMod (F.order d)) ((sk τ).2.2.1 : ZMod (F.order d)) ((sk τ).2.2.2 : ZMod (F.order d)) - (ofBits (H (d, encBits3 ((decodeBits4 req).1, (decodeBits4 req).2.1, - (decodeBits4 req).2.2.1))) : ZMod (F.order d)) + (ofBits (H (d, encBits4 ((decodeBits4 ct_q).1, (decodeBits4 ct_q).2.1, + (decodeBits4 ct_q).2.2.1, ad_q))) : ZMod (F.order d)) (β' • F.generator d) (γ' • F.generator d) = e τ • F.generator d) : (∑' τ : ZMod (F.order d), PMF.uniformOfFintype (ZMod (F.order d)) τ * - if (cond && F.isCramerShoupBadDH H isElem d α (sk τ) req) + if (cond && F.isCramerShoupBadDH H isElem d α (sk τ) ct_q ad_q) then (1 : ℝ≥0∞) else 0) ≤ (F.order d : ℝ≥0∞)⁻¹ := by - let parts := decodeBits4 req - let r := ofBits (H (d, encBits3 (parts.1, parts.2.1, parts.2.2.1))) + let parts := decodeBits4 ct_q + let r := ofBits (H (d, encBits4 (parts.1, parts.2.1, parts.2.2.1, ad_q))) by_cases hpre : - ((encBits4 parts == req) && isElem (d, parts.1) && isElem (d, parts.2.1) && + ((encBits4 parts == ct_q) && isElem (d, parts.1) && isElem (d, parts.2.1) && isElem (d, parts.2.2.1) && (parts.2.1 != F.smul (d, α.val, parts.1))) = true · obtain ⟨β', γ', hvElem, hwElem, hγ'⟩ := - F.exists_nonDH_of_badDH_pre isElem hmem d α req hpre + F.exists_nonDH_of_badDH_pre isElem hmem d α ct_q hpre obtain ⟨e_equiv, he_equiv⟩ := hequiv β' γ' hγ' have hle (τ : ZMod (F.order d)) : - (if (cond && F.isCramerShoupBadDH H isElem d α (sk τ) req) + (if (cond && F.isCramerShoupBadDH H isElem d α (sk τ) ct_q ad_q) then (1 : ℝ≥0∞) else 0) ≤ if F.encode d (e_equiv τ • F.generator d) == parts.2.2.2 then 1 else 0 := by have hbad_eq : - F.isCramerShoupBadDH H isElem d α (sk τ) req = + F.isCramerShoupBadDH H isElem d α (sk τ) ct_q ad_q = (F.encode d (e_equiv τ • F.generator d) == parts.2.2.2) := by - change (((encBits4 parts == req) && isElem (d, parts.1) && isElem (d, parts.2.1) && + change (((encBits4 parts == ct_q) && isElem (d, parts.1) && isElem (d, parts.2.1) && isElem (d, parts.2.2.1) && (parts.2.1 != F.smul (d, α.val, parts.1))) && (F.encodeProjHash2 d (sk τ).2.1.1 (sk τ).2.1.2 (sk τ).2.2.1 (sk τ).2.2.2 r parts.1 parts.2.1 == parts.2.2.2)) = _ @@ -1013,9 +1309,9 @@ private theorem tsum_and_isCramerShoupBadDH_le (F.tsum_uniform_smul_generator_eq e_equiv parts.2.2.2) · simp only [Bool.not_eq_true] at hpre have hfalse (τ : ZMod (F.order d)) : - (cond && F.isCramerShoupBadDH H isElem d α (sk τ) req) = false := by + (cond && F.isCramerShoupBadDH H isElem d α (sk τ) ct_q ad_q) = false := by rw [Bool.and_eq_false_iff]; right - change (((encBits4 parts == req) && isElem (d, parts.1) && isElem (d, parts.2.1) && + change (((encBits4 parts == ct_q) && isElem (d, parts.1) && isElem (d, parts.2.1) && isElem (d, parts.2.2.1) && (parts.2.1 != F.smul (d, α.val, parts.1))) && _) = false simp [hpre] simp_rw [hfalse]; simp @@ -1023,17 +1319,17 @@ private theorem tsum_and_isCramerShoupBadDH_le private theorem tsum_isCramerShoupBadDH_phase1_le (H : F.Desc × List Bool → List Bool) (isElem : F.Desc × List Bool → Bool) (hmem : ∀ d x, isElem (d, x) = true ↔ x ∈ Set.range (F.encode d)) - (d : F.Desc) (α δ τ δ₁ δ₂ τ₂ : ZMod (F.order d)) (req : List Bool) : + (d : F.Desc) (α δ τ δ₁ δ₂ τ₂ : ZMod (F.order d)) (ct_q ad_q : List Bool) : (∑' τ₁ : ZMod (F.order d), PMF.uniformOfFintype (ZMod (F.order d)) τ₁ * if F.isCramerShoupBadDH H isElem d α - (F.skNatOfZMod d (δ - α * τ) τ (δ₁ - α * τ₁) τ₁ (δ₂ - α * τ₂) τ₂) req + (F.skNatOfZMod d (δ - α * τ) τ (δ₁ - α * τ₁) τ₁ (δ₂ - α * τ₂) τ₂) ct_q ad_q then (1 : ℝ≥0∞) else 0) ≤ (F.order d : ℝ≥0∞)⁻¹ := by simpa only [Bool.true_and] using F.tsum_and_isCramerShoupBadDH_le H isElem hmem d α - (fun τ₁ => F.skNatOfZMod d (δ - α * τ) τ (δ₁ - α * τ₁) τ₁ (δ₂ - α * τ₂) τ₂) true req + (fun τ₁ => F.skNatOfZMod d (δ - α * τ) τ (δ₁ - α * τ₁) τ₁ (δ₂ - α * τ₂) τ₂) true ct_q ad_q fun β' γ' hγ' => by let ρ' : ZMod (F.order d) := - (ofBits (H (d, encBits3 ((decodeBits4 req).1, (decodeBits4 req).2.1, - (decodeBits4 req).2.2.1))) : ZMod (F.order d)) + (ofBits (H (d, encBits4 ((decodeBits4 ct_q).1, (decodeBits4 ct_q).2.1, + (decodeBits4 ct_q).2.2.1, ad_q))) : ZMod (F.order d)) refine ⟨(Equiv.addRight (ρ' * τ₂)).trans (projHashFiberEquiv F d hγ' (δ₁ + ρ' * δ₂)), fun τ₁ => ?_⟩ simp only [skNatOfZMod, ZMod.natCast_zmod_val, projHash2_fiber_eq, Equiv.trans_apply, @@ -1042,48 +1338,55 @@ private theorem tsum_isCramerShoupBadDH_phase1_le private theorem tsum_isCramerShoupBadDH_phase2_le (H : F.Desc × List Bool → List Bool) (isElem : F.Desc × List Bool → Bool) (hmem : ∀ d x, isElem (d, x) = true ↔ x ∈ Set.range (F.encode d)) - (d : F.Desc) (α β γ δ τ δ₁ δ₂ ξ : ZMod (F.order d)) (e req : List Bool) : + (d : F.Desc) (α β γ δ τ δ₁ δ₂ ξ : ZMod (F.order d)) (e ad ct_q ad_q : List Bool) : let v := F.encode d (β • F.generator d); let w := F.encode d (γ • F.generator d) - let ρ := F.cramerShoupHashMod H d (encBits3 (v, w, e)) + let ρ := F.cramerShoupHashMod H d (encBits4 (v, w, e, ad)) let ct := encBits4 (v, w, e, F.encode d ((β * (δ₁ + ρ * δ₂) + (γ - α * β) * ξ) • F.generator d)) (∑' τ₂ : ZMod (F.order d), PMF.uniformOfFintype (ZMod (F.order d)) τ₂ * - if (!((req == ct) || F.isCramerShoupCollision H d req ct) && + if (!(((ct_q == ct) && (ad_q == ad)) || F.isCramerShoupCollision H d ct_q ad_q ct ad) && F.isCramerShoupBadDH H isElem d α (F.skNatOfZMod d (δ - α * τ) τ (δ₁ - α * (ξ - ρ * τ₂)) (ξ - ρ * τ₂) - (δ₂ - α * τ₂) τ₂) req) + (δ₂ - α * τ₂) τ₂) ct_q ad_q) then (1 : ℝ≥0∞) else 0) ≤ (F.order d : ℝ≥0∞)⁻¹ := by intro v w ρ ct - let parts := decodeBits4 req - let r' := ofBits (H (d, encBits3 (parts.1, parts.2.1, parts.2.2.1))) + let parts := decodeBits4 ct_q + let r' := ofBits (H (d, encBits4 (parts.1, parts.2.1, parts.2.2.1, ad_q))) let ρ' : ZMod (F.order d) := (r' : ZMod (F.order d)) by_cases hρ : ρ' = ρ · have hfalse (τ₂ : ZMod (F.order d)) : - (!((req == ct) || F.isCramerShoupCollision H d req ct) && + (!(((ct_q == ct) && (ad_q == ad)) || F.isCramerShoupCollision H d ct_q ad_q ct ad) && F.isCramerShoupBadDH H isElem d α (F.skNatOfZMod d (δ - α * τ) τ (δ₁ - α * (ξ - ρ * τ₂)) (ξ - ρ * τ₂) - (δ₂ - α * τ₂) τ₂) req) = false := by + (δ₂ - α * τ₂) τ₂) ct_q ad_q) = false := by rw [Bool.and_eq_false_iff] - by_cases hrej : ((req == ct) || F.isCramerShoupCollision H d req ct) = true + by_cases hrej : + (((ct_q == ct) && (ad_q == ad)) || F.isCramerShoupCollision H d ct_q ad_q ct ad) = true · left; simp [hrej] · right - simp only [Bool.or_eq_true, beq_iff_eq, not_or, Bool.not_eq_true] at hrej - rcases hrej with ⟨hne_ct, hnot_coll⟩ - by_cases hframe : (encBits4 parts == req) = true - · have hreq_eq : encBits4 (parts.1, parts.2.1, parts.2.2.1, parts.2.2.2) = req := + simp only [Bool.or_eq_true, not_or, Bool.not_eq_true] at hrej + rcases hrej with ⟨hne_ct_ad, hnot_coll⟩ + by_cases hframe : (encBits4 parts == ct_q) = true + · have hreq_eq : encBits4 (parts.1, parts.2.1, parts.2.2.1, parts.2.2.2) = ct_q := beq_iff_eq.mp hframe - have htriple_eq : encBits3 (parts.1, parts.2.1, parts.2.2.1) = encBits3 (v, w, e) := by - by_contra hne_triple - have hcoll : F.isCramerShoupCollision H d req ct = true := by - rw [F.isCramerShoupCollision_eq_isCollisionFor H 0 d req ct, + have htuple_eq : + encBits4 (parts.1, parts.2.1, parts.2.2.1, ad_q) = encBits4 (v, w, e, ad) := by + by_contra hne_tuple + have hcoll : F.isCramerShoupCollision H d ct_q ad_q ct ad = true := by + rw [F.isCramerShoupCollision_eq_isCollisionFor H 0 d ct_q ad_q ct ad, isCollisionFor_eq_true_iff] refine ⟨rfl, rfl, ?_, ?_⟩ - · simpa [extractCramerShoupTriple, parts, ct, decodeBits4_encBits4] using hne_triple - · simpa [extractCramerShoupTriple, parts, ct, decodeBits4_encBits4, + · simpa [extractCramerShoupTuple, parts, ct, decodeBits4_encBits4] using hne_tuple + · simpa [extractCramerShoupTuple, parts, ct, decodeBits4_encBits4, cramerShoupHashMod, ρ', ρ, r'] using hρ exact Bool.false_ne_true (hnot_coll.symm.trans hcoll) - rcases Prod.mk.inj (encBits3.injective htriple_eq) with ⟨hv_eq, hrest⟩ - rcases Prod.mk.inj hrest with ⟨hw_eq, he_eq⟩ + rcases Prod.mk.inj (encBits4.injective htuple_eq) with ⟨hv_eq, hrest1⟩ + rcases Prod.mk.inj hrest1 with ⟨hw_eq, hrest2⟩ + rcases Prod.mk.inj hrest2 with ⟨he_eq, had_eq⟩ + have hne_ct : ct_q ≠ ct := by + intro hct_eq + have htrue : ((ct_q == ct) && (ad_q == ad)) = true := by simp [hct_eq, had_eq] + exact Bool.false_ne_true (hne_ct_ad.symm.trans htrue) have htag_eq : F.encodeProjHash2 d (δ₁ - α * (ξ - ρ * τ₂)).val (ξ - ρ * τ₂).val (δ₂ - α * τ₂).val τ₂.val r' parts.1 parts.2.1 = @@ -1103,7 +1406,7 @@ private theorem tsum_isCramerShoupBadDH_phase2_le · exact F.tsum_and_isCramerShoupBadDH_le H isElem hmem d α (fun τ₂ => F.skNatOfZMod d (δ - α * τ) τ (δ₁ - α * (ξ - ρ * τ₂)) (ξ - ρ * τ₂) (δ₂ - α * τ₂) τ₂) - (!((req == ct) || F.isCramerShoupCollision H d req ct)) req + (!(((ct_q == ct) && (ad_q == ad)) || F.isCramerShoupCollision H d ct_q ad_q ct ad)) ct_q ad_q fun β' γ' hγ' => ⟨projHash2QueryFiberEquiv F d hγ' hρ δ₁ δ₂ ξ, fun τ₂ => by simp only [skNatOfZMod, ZMod.natCast_zmod_val, projHash2_fiber_query_eq, projHash2QueryFiberEquiv_apply, ρ', r', parts]⟩ @@ -1115,21 +1418,23 @@ theorem div_order_le_div_of_le {d : F.Desc} {Q m : ℕ} (hm0 : 0 < m) (hm : m gcongr /-- Phase 1 query bound on honest public keys at security parameter `n`. -/ -def HonestPhase1QueryBound {Msg : Type} (setup : ℕ → PMF F.Desc) - (adversary : CCA2AdversaryOf Msg) (n Q₁ : ℕ) : Prop := +def HonestPhase1QueryBound {Msg AD : Type} (setup : ℕ → PMF F.Desc) + (adversary : ADCCA2AdversaryOf Msg AD) (n Q₁ : ℕ) : Prop := ∀ d ∈ (setup n).support, ∀ α δ δ₁ δ₂ : ZMod (F.order d), (adversary.choose n (F.encodeCramerShoupPublicKey d (F.encode d (α • F.generator d)) (F.skNatOfZMod d δ 0 δ₁ 0 δ₂ 0))).IsQueryBounded Q₁ /-- Phase 2 query bound on honest Phase 1 states and challenge ciphertexts at parameter `n`. -/ -def HonestPhase2QueryBound {Msg : Type} (setup : ℕ → PMF F.Desc) +def HonestPhase2QueryBound (hasAD : Bool) {Msg AD : Type} + (encodeAD : F.Desc × AD → List Bool) (setup : ℕ → PMF F.Desc) (H : F.Desc × List Bool → List Bool) (isElem : F.Desc × List Bool → Bool) - (adversary : CCA2AdversaryOf Msg) (n Q₂ : ℕ) : Prop := + (adversary : ADCCA2AdversaryOf Msg AD) (n Q₂ : ℕ) : Prop := ∀ d ∈ (setup n).support, ∀ α β γ δ τ δ₁ δ₂ ξ : ZMod (F.order d), ∀ choiceOut ∈ (ProbComp.eval ((OracleComp.interpret (ProbOracleComp.handler (fun m => StateT.lift (ProbComp.unifFin m)) - (fun req (s : Unit) => pure (F.cramerShoupDHPhase1Reply H isElem d α δ δ₁ δ₂ req.2, s))) + (fun req (s : Unit) => + pure (F.cramerShoupDHPhase1Reply hasAD H isElem d α δ δ₁ δ₂ req.2, s))) (adversary.choose n (F.encodeCramerShoupPublicKey d (F.encode d (α • F.generator d)) (F.skNatOfZMod d δ 0 δ₁ 0 δ₂ 0)))).run ())).support, @@ -1137,7 +1442,8 @@ def HonestPhase2QueryBound {Msg : Type} (setup : ℕ → PMF F.Desc) let v := F.encode d (β • F.generator d); let w := F.encode d (γ • F.generator d) let e := F.encode d (projHash F d (δ - α * τ) τ (β • F.generator d) (γ • F.generator d) + mElem) - let ρ := F.cramerShoupHashMod H d (encBits3 (v, w, e)) + let ad := encodeAD (d, choiceOut.1.1.2) + let ρ := F.cramerShoupHashMod H d (encBits4 (v, w, e, ad)) let ct := encBits4 (v, w, e, F.encode d ((β * (δ₁ + ρ * δ₂) + (γ - α * β) * ξ) • F.generator d)) (adversary.guess n choiceOut.1.2 ct).IsQueryBounded Q₂ @@ -1147,10 +1453,11 @@ Section 12.5.3, printed pp. 491–492): under the fiber change of variables `σ = δ - α * τ`, `σ₁ = δ₁ - α * τ₁`, `σ₂ = δ₂ - α * τ₂`, replace Phase 1's decryption oracle `cramerShoupPhase1Reply` with `cramerShoupDHPhase1Reply` (rejecting non-DH queries in Phase 1). -/ noncomputable def cramerShoupGame3Half - {Msg : Type} (encodeMsg : F.Desc × Msg → List Bool) + (hasAD : Bool) {Msg AD : Type} (encodeMsg : F.Desc × Msg → List Bool) + (encodeAD : F.Desc × AD → List Bool) (setup : ℕ → PMF F.Desc) (H : F.Desc × List Bool → List Bool) (isElem : F.Desc × List Bool → Bool) - (adversary : CCA2AdversaryOf Msg) (bit : Bool) (n : ℕ) : Game := do + (adversary : ADCCA2AdversaryOf Msg AD) (bit : Bool) (n : ℕ) : Game := do let d ← setup n; let U := PMF.uniformOfFintype (ZMod (F.order d)) let α ← U; let β ← U; let γ ← U; let δ ← U; let τ ← U; let δ₁ ← U; let τ₁ ← U; let δ₂ ← U let τ₂ ← U @@ -1158,13 +1465,14 @@ noncomputable def cramerShoupGame3Half let u := F.encode d (α • F.generator d); let v := F.encode d (β • F.generator d) let w := F.encode d (γ • F.generator d) let pk := F.encodeCramerShoupPublicKey d u (F.skNatOfZMod d δ 0 δ₁ 0 δ₂ 0) - runStatelessOracle (F.cramerShoupDHPhase1Reply H isElem d α δ δ₁ δ₂) + runStatelessOracle (F.cramerShoupDHPhase1Reply hasAD H isElem d α δ δ₁ δ₂) (adversary.choose n pk) fun choice => - let m₀ := encodeMsg (d, choice.1.1); let m₁ := encodeMsg (d, choice.1.2) + let m₀ := encodeMsg (d, choice.1.1.1); let m₁ := encodeMsg (d, choice.1.1.2) + let ad := encodeAD (d, choice.1.2) if isElem (d, m₀) && isElem (d, m₁) then let m := if bit then m₁ else m₀ - let ct := F.encodeCramerShoupChallenge H d sk v w m - runStatelessOracle (F.cramerShoupCollisionPhase2Reply H isElem d sk ct) + let ct := F.encodeCramerShoupChallenge H d sk v w m ad + runStatelessOracle (F.cramerShoupCollisionPhase2Reply hasAD H isElem d sk ct ad) (adversary.guess n choice.2 ct) PMF.pure else PMF.pure false @@ -1173,16 +1481,17 @@ noncomputable def cramerShoupGame3Half `cramerShoupDHPhase1Reply` changes the game advantage by at most `Q₁ / m` whenever Phase 1 makes at most `Q₁` queries on every honest public key. -/ theorem advantage_cramerShoupGame3_game3Half_le - {Msg : Type} (encodeMsg : F.Desc × Msg → List Bool) + (hasAD : Bool) {Msg AD : Type} (encodeMsg : F.Desc × Msg → List Bool) + (encodeAD : F.Desc × AD → List Bool) (setup : ℕ → PMF F.Desc) (H : F.Desc × List Bool → List Bool) (isElem : F.Desc × List Bool → Bool) (hmem : ∀ d x, isElem (d, x) = true ↔ x ∈ Set.range (F.encode d)) - (adversary : CCA2AdversaryOf Msg) (bit : Bool) (n Q₁ m : ℕ) + (adversary : ADCCA2AdversaryOf Msg AD) (bit : Bool) (n Q₁ m : ℕ) (hm0 : 0 < m) (hm : ∀ d ∈ (setup n).support, m ≤ F.order d) (hQ₁ : F.HonestPhase1QueryBound setup adversary n Q₁) : Game.advantage - (F.cramerShoupGame3 encodeMsg setup H isElem adversary bit n) - (F.cramerShoupGame3Half encodeMsg setup H isElem adversary bit n) ≤ + (F.cramerShoupGame3 hasAD encodeMsg encodeAD setup H isElem adversary bit n) + (F.cramerShoupGame3Half hasAD encodeMsg encodeAD setup H isElem adversary bit n) ≤ (Q₁ : ℝ≥0) / (m : ℝ≥0) := by unfold cramerShoupGame3 cramerShoupGame3Half; simp only [PMF.monad_bind_eq_bind] refine advantage_bind_le_of_support (setup n) _ _ fun d hd => ?_ @@ -1202,25 +1511,29 @@ theorem advantage_cramerShoupGame3_game3Half_le rw [advantage_bind_comm U U] refine advantage_bind_le_nnreal U _ _ fun τ₂ => ?_ exact (advantage_runStatelessOracle_bind_le U - (fun τ₁ => F.cramerShoupPhase1Reply H isElem d + (fun τ₁ => F.cramerShoupPhase1Reply hasAD H isElem d (F.skNatOfZMod d (δ - α * τ) τ (δ₁ - α * τ₁) τ₁ (δ₂ - α * τ₂) τ₂)) - (F.cramerShoupDHPhase1Reply H isElem d α δ δ₁ δ₂) + (F.cramerShoupDHPhase1Reply hasAD H isElem d α δ δ₁ δ₂) (fun τ₁ req => F.isCramerShoupBadDH H isElem d α - (F.skNatOfZMod d (δ - α * τ) τ (δ₁ - α * τ₁) τ₁ (δ₂ - α * τ₂) τ₂) req) + (F.skNatOfZMod d (δ - α * τ) τ (δ₁ - α * τ₁) τ₁ (δ₂ - α * τ₂) τ₂) + (splitCramerShoupQuery hasAD req).1 (splitCramerShoupQuery hasAD req).2) _ (hQ₁ d hd α δ δ₁ δ₂) (fun τ₁ req hbad => - F.cramerShoupPhase1Reply_fiber_eq_of_not_bad H isElem hmem d α δ τ δ₁ τ₁ δ₂ τ₂ req hbad) - (fun req => F.tsum_isCramerShoupBadDH_phase1_le H isElem hmem d α δ τ δ₁ δ₂ τ₂ req)).trans + F.cramerShoupPhase1Reply_fiber_eq_of_not_bad hasAD H isElem hmem d α δ τ δ₁ τ₁ δ₂ τ₂ req hbad) + (fun req => F.tsum_isCramerShoupBadDH_phase1_le H isElem hmem d α δ τ δ₁ δ₂ τ₂ + (splitCramerShoupQuery hasAD req).1 (splitCramerShoupQuery hasAD req).2)).trans (F.div_order_le_div_of_le hm0 (hm d hd)) /-- **Game 4** (corresponding to Game 5 of [BonehShoup2023], Section 12.5.3, printed p. 492, -adapted to $\mathcal{E}_{\mathrm{MCS}}$): both Phase 1 and Phase 2 decryption oracles reject any -non-DH ciphertext query (`w' != F.smul (d, α.val, v')`) via `decodeCramerShoupDH`. -/ +adapted to $\mathcal{E}_{\mathrm{MCS}}$ with associated data): both Phase 1 and Phase 2 decryption +oracles reject any non-DH ciphertext query (`w' != F.smul (d, α.val, v')`) via +`decodeCramerShoupDH`. -/ noncomputable def cramerShoupGame4 - {Msg : Type} (encodeMsg : F.Desc × Msg → List Bool) + (hasAD : Bool) {Msg AD : Type} (encodeMsg : F.Desc × Msg → List Bool) + (encodeAD : F.Desc × AD → List Bool) (setup : ℕ → PMF F.Desc) (H : F.Desc × List Bool → List Bool) (isElem : F.Desc × List Bool → Bool) - (adversary : CCA2AdversaryOf Msg) (bit : Bool) (n : ℕ) : Game := do + (adversary : ADCCA2AdversaryOf Msg AD) (bit : Bool) (n : ℕ) : Game := do let d ← setup n; let U := PMF.uniformOfFintype (ZMod (F.order d)) let α ← U; let β ← U; let γ ← U; let δ ← U; let τ ← U; let δ₁ ← U; let τ₁ ← U; let δ₂ ← U let τ₂ ← U @@ -1228,13 +1541,14 @@ noncomputable def cramerShoupGame4 let u := F.encode d (α • F.generator d); let v := F.encode d (β • F.generator d) let w := F.encode d (γ • F.generator d) let pk := F.encodeCramerShoupPublicKey d u (F.skNatOfZMod d δ 0 δ₁ 0 δ₂ 0) - runStatelessOracle (F.cramerShoupDHPhase1Reply H isElem d α δ δ₁ δ₂) + runStatelessOracle (F.cramerShoupDHPhase1Reply hasAD H isElem d α δ δ₁ δ₂) (adversary.choose n pk) fun choice => - let m₀ := encodeMsg (d, choice.1.1); let m₁ := encodeMsg (d, choice.1.2) + let m₀ := encodeMsg (d, choice.1.1.1); let m₁ := encodeMsg (d, choice.1.1.2) + let ad := encodeAD (d, choice.1.2) if isElem (d, m₀) && isElem (d, m₁) then let m := if bit then m₁ else m₀ - let ct := F.encodeCramerShoupChallenge H d sk v w m - runStatelessOracle (F.cramerShoupDHPhase2Reply H isElem d α δ δ₁ δ₂ ct) + let ct := F.encodeCramerShoupChallenge H d sk v w m ad + runStatelessOracle (F.cramerShoupDHPhase2Reply hasAD H isElem d α δ δ₁ δ₂ ct ad) (adversary.guess n choice.2 ct) PMF.pure else PMF.pure false @@ -1243,16 +1557,17 @@ noncomputable def cramerShoupGame4 `cramerShoupDHPhase2Reply` changes the game advantage by at most `Q₂ / m` whenever Phase 2 makes at most `Q₂` queries on every honest Phase 1 state and challenge ciphertext. -/ theorem advantage_cramerShoupGame3Half_game4_le - {Msg : Type} (encodeMsg : F.Desc × Msg → List Bool) + (hasAD : Bool) {Msg AD : Type} (encodeMsg : F.Desc × Msg → List Bool) + (encodeAD : F.Desc × AD → List Bool) (setup : ℕ → PMF F.Desc) (H : F.Desc × List Bool → List Bool) (isElem : F.Desc × List Bool → Bool) (hmem : ∀ d x, isElem (d, x) = true ↔ x ∈ Set.range (F.encode d)) - (adversary : CCA2AdversaryOf Msg) (bit : Bool) (n Q₂ m : ℕ) + (adversary : ADCCA2AdversaryOf Msg AD) (bit : Bool) (n Q₂ m : ℕ) (hm0 : 0 < m) (hm : ∀ d ∈ (setup n).support, m ≤ F.order d) - (hQ₂ : F.HonestPhase2QueryBound setup H isElem adversary n Q₂) : + (hQ₂ : F.HonestPhase2QueryBound hasAD encodeAD setup H isElem adversary n Q₂) : Game.advantage - (F.cramerShoupGame3Half encodeMsg setup H isElem adversary bit n) - (F.cramerShoupGame4 encodeMsg setup H isElem adversary bit n) ≤ + (F.cramerShoupGame3Half hasAD encodeMsg encodeAD setup H isElem adversary bit n) + (F.cramerShoupGame4 hasAD encodeMsg encodeAD setup H isElem adversary bit n) ≤ (Q₂ : ℝ≥0) / (m : ℝ≥0) := by unfold cramerShoupGame3Half cramerShoupGame4; simp only [PMF.monad_bind_eq_bind] refine advantage_bind_le_of_support (setup n) _ _ fun d hd => ?_ @@ -1265,12 +1580,12 @@ theorem advantage_cramerShoupGame3Half_game4_le refine advantage_bind_le_nnreal U _ _ fun δ₁ => ?_ rw [advantage_bind_comm U U] refine advantage_bind_le_nnreal U _ _ fun δ₂ => ?_ - have hcomm (k : ZMod (F.order d) → ZMod (F.order d) → (Msg × Msg) × List Bool → Game) : + have hcomm (k : ZMod (F.order d) → ZMod (F.order d) → ((Msg × Msg) × AD) × List Bool → Game) : (U.bind fun τ₁ => U.bind fun τ₂ => - runStatelessOracle (F.cramerShoupDHPhase1Reply H isElem d α δ δ₁ δ₂) + runStatelessOracle (F.cramerShoupDHPhase1Reply hasAD H isElem d α δ δ₁ δ₂) (adversary.choose n (F.encodeCramerShoupPublicKey d (F.encode d (α • F.generator d)) (F.skNatOfZMod d δ 0 δ₁ 0 δ₂ 0))) (k τ₁ τ₂)) = - runStatelessOracle (F.cramerShoupDHPhase1Reply H isElem d α δ δ₁ δ₂) + runStatelessOracle (F.cramerShoupDHPhase1Reply hasAD H isElem d α δ δ₁ δ₂) (adversary.choose n (F.encodeCramerShoupPublicKey d (F.encode d (α • F.generator d)) (F.skNatOfZMod d δ 0 δ₁ 0 δ₂ 0))) fun choice => U.bind fun τ₁ => U.bind fun τ₂ => k τ₁ τ₂ choice := by @@ -1279,17 +1594,18 @@ theorem advantage_cramerShoupGame3Half_game4_le exact bind_runStatelessOracle_comm _ _ _ _ rw [hcomm, hcomm] refine advantage_runStatelessOracle_le_of_support _ _ _ _ fun choiceOut hchoice => ?_ - by_cases hvalid : (isElem (d, encodeMsg (d, choiceOut.1.1.1)) && - isElem (d, encodeMsg (d, choiceOut.1.1.2))) = true + by_cases hvalid : (isElem (d, encodeMsg (d, choiceOut.1.1.1.1)) && + isElem (d, encodeMsg (d, choiceOut.1.1.1.2))) = true · simp only [hvalid, ↓reduceIte] - have hm_mem : isElem (d, if bit then encodeMsg (d, choiceOut.1.1.2) - else encodeMsg (d, choiceOut.1.1.1)) = true := by + have hm_mem : isElem (d, if bit then encodeMsg (d, choiceOut.1.1.1.2) + else encodeMsg (d, choiceOut.1.1.1.1)) = true := by rcases Bool.and_eq_true_iff.mp hvalid with ⟨h0, h1⟩; cases bit <;> assumption obtain ⟨mElem, hmElem⟩ := (hmem d _).mp hm_mem let v := F.encode d (β • F.generator d); let w := F.encode d (γ • F.generator d) - let m_msg := if bit then encodeMsg (d, choiceOut.1.1.2) else encodeMsg (d, choiceOut.1.1.1) + let m_msg := if bit then encodeMsg (d, choiceOut.1.1.1.2) else encodeMsg (d, choiceOut.1.1.1.1) + let ad := encodeAD (d, choiceOut.1.1.2) let e := F.add (d, F.encodeProjHash d (δ - α * τ).val τ.val v w, m_msg) - let ρ := F.cramerShoupHashMod H d (encBits3 (v, w, e)) + let ρ := F.cramerShoupHashMod H d (encBits4 (v, w, e, ad)) let ct₀ (ξ : ZMod (F.order d)) := encBits4 (v, w, e, F.encode d ((β * (δ₁ + ρ * δ₂) + (γ - α * β) * ξ) • F.generator d)) have hshift_one (f : ZMod (F.order d) → ZMod (F.order d) → Game) : @@ -1303,32 +1619,35 @@ theorem advantage_cramerShoupGame3Half_game4_le have hct_eq : ∀ τ₂ : ZMod (F.order d), F.encodeCramerShoupChallenge H d (F.skNatOfZMod d (δ - α * τ) τ (δ₁ - α * (ξ - ρ * τ₂)) (ξ - ρ * τ₂) - (δ₂ - α * τ₂) τ₂) v w m_msg = ct₀ ξ := - fun τ₂ => F.encodeCramerShoupChallenge_fiber_eq H d α β γ δ τ δ₁ δ₂ ξ τ₂ m_msg - dsimp only [v, w, m_msg] at hct_eq; simp_rw [hct_eq] + (δ₂ - α * τ₂) τ₂) v w m_msg ad = ct₀ ξ := + fun τ₂ => F.encodeCramerShoupChallenge_fiber_eq H d α β γ δ τ δ₁ δ₂ ξ τ₂ m_msg ad + dsimp only [v, w, m_msg, ad] at hct_eq; simp_rw [hct_eq] have he_eq : e = F.encode d (projHash F d (δ - α * τ) τ (β • F.generator d) (γ • F.generator d) + mElem) := by simp only [e, v, w, m_msg, ← hmElem, encodeProjHash_encode, F.add_encode, ZMod.natCast_zmod_val] have hq2 : (adversary.guess n choiceOut.1.2 (ct₀ ξ)).IsQueryBounded Q₂ := by - simpa only [ct₀, v, w, ρ, he_eq] using hQ₂ d hd α β γ δ τ δ₁ δ₂ ξ choiceOut hchoice mElem + simpa only [ct₀, v, w, ad, ρ, he_eq] using + hQ₂ d hd α β γ δ τ δ₁ δ₂ ξ choiceOut hchoice mElem exact (advantage_runStatelessOracle_bind_le U - (fun τ₂ => F.cramerShoupCollisionPhase2Reply H isElem d + (fun τ₂ => F.cramerShoupCollisionPhase2Reply hasAD H isElem d (F.skNatOfZMod d (δ - α * τ) τ (δ₁ - α * (ξ - ρ * τ₂)) (ξ - ρ * τ₂) - (δ₂ - α * τ₂) τ₂) (ct₀ ξ)) - (F.cramerShoupDHPhase2Reply H isElem d α δ δ₁ δ₂ (ct₀ ξ)) + (δ₂ - α * τ₂) τ₂) (ct₀ ξ) ad) + (F.cramerShoupDHPhase2Reply hasAD H isElem d α δ δ₁ δ₂ (ct₀ ξ) ad) (fun τ₂ req => - !((req == ct₀ ξ) || F.isCramerShoupCollision H d req (ct₀ ξ)) && + let q := splitCramerShoupQuery hasAD req + !(((q.1 == ct₀ ξ) && (q.2 == ad)) || F.isCramerShoupCollision H d q.1 q.2 (ct₀ ξ) ad) && F.isCramerShoupBadDH H isElem d α (F.skNatOfZMod d (δ - α * τ) τ (δ₁ - α * (ξ - ρ * τ₂)) (ξ - ρ * τ₂) - (δ₂ - α * τ₂) τ₂) req) + (δ₂ - α * τ₂) τ₂) q.1 q.2) (fun _ => PMF.pure) hq2 (fun τ₂ req hbad => - F.cramerShoupCollisionPhase2Reply_fiber_eq_of_not_bad H isElem hmem d - α δ τ δ₁ (ξ - ρ * τ₂) δ₂ τ₂ (ct₀ ξ) req hbad) + F.cramerShoupCollisionPhase2Reply_fiber_eq_of_not_bad hasAD H isElem hmem d + α δ τ δ₁ (ξ - ρ * τ₂) δ₂ τ₂ (ct₀ ξ) ad req hbad) (fun req => - F.tsum_isCramerShoupBadDH_phase2_le H isElem hmem d α β γ δ τ δ₁ δ₂ ξ e req)).trans + F.tsum_isCramerShoupBadDH_phase2_le H isElem hmem d α β γ δ τ δ₁ δ₂ ξ e ad + (splitCramerShoupQuery hasAD req).1 (splitCramerShoupQuery hasAD req).2)).trans (F.div_order_le_div_of_le hm0 (hm d hd)) · simp only [hvalid, Bool.false_eq_true, ↓reduceIte, PMF.bind_const, Game.advantage_self, zero_le] @@ -1337,51 +1656,54 @@ printed pp. 492–493, using Lemma 12.8, printed p. 488): replacing both Phase 1 decryption oracles with their DH-restricted counterparts changes the game advantage by at most `(Q₁ + Q₂) / m`. -/ theorem advantage_cramerShoupGame3_game4_le - {Msg : Type} (encodeMsg : F.Desc × Msg → List Bool) + (hasAD : Bool) {Msg AD : Type} (encodeMsg : F.Desc × Msg → List Bool) + (encodeAD : F.Desc × AD → List Bool) (setup : ℕ → PMF F.Desc) (H : F.Desc × List Bool → List Bool) (isElem : F.Desc × List Bool → Bool) (hmem : ∀ d x, isElem (d, x) = true ↔ x ∈ Set.range (F.encode d)) - (adversary : CCA2AdversaryOf Msg) (bit : Bool) (n Q₁ Q₂ m : ℕ) + (adversary : ADCCA2AdversaryOf Msg AD) (bit : Bool) (n Q₁ Q₂ m : ℕ) (hm0 : 0 < m) (hm : ∀ d ∈ (setup n).support, m ≤ F.order d) (hQ₁ : F.HonestPhase1QueryBound setup adversary n Q₁) - (hQ₂ : F.HonestPhase2QueryBound setup H isElem adversary n Q₂) : + (hQ₂ : F.HonestPhase2QueryBound hasAD encodeAD setup H isElem adversary n Q₂) : Game.advantage - (F.cramerShoupGame3 encodeMsg setup H isElem adversary bit n) - (F.cramerShoupGame4 encodeMsg setup H isElem adversary bit n) ≤ + (F.cramerShoupGame3 hasAD encodeMsg encodeAD setup H isElem adversary bit n) + (F.cramerShoupGame4 hasAD encodeMsg encodeAD setup H isElem adversary bit n) ≤ ((Q₁ + Q₂ : ℕ) : ℝ≥0) / (m : ℝ≥0) := by refine (Game.advantage_triangle _ - (F.cramerShoupGame3Half encodeMsg setup H isElem adversary bit n) _).trans ?_ + (F.cramerShoupGame3Half hasAD encodeMsg encodeAD setup H isElem adversary bit n) _).trans ?_ refine (add_le_add - (F.advantage_cramerShoupGame3_game3Half_le encodeMsg setup H isElem hmem + (F.advantage_cramerShoupGame3_game3Half_le hasAD encodeMsg encodeAD setup H isElem hmem adversary bit n Q₁ m hm0 hm hQ₁) - (F.advantage_cramerShoupGame3Half_game4_le encodeMsg setup H isElem hmem + (F.advantage_cramerShoupGame3Half_game4_le hasAD encodeMsg encodeAD setup H isElem hmem adversary bit n Q₂ m hm0 hm hQ₂)).trans_eq ?_ push_cast; ring /-- **Game 5** (corresponding to Game 6 of [BonehShoup2023], Section 12.5.3, printed p. 493, adapted -to $\mathcal{E}_{\mathrm{MCS}}$): replace the masked challenge component +to $\mathcal{E}_{\mathrm{MCS}}$ with associated data): replace the masked challenge component `e = projHash F d (δ - α * τ) τ v w + m` by a uniform independent group element `ζ • g`, making the entire experiment independent of the challenge bit `bit`. -/ noncomputable def cramerShoupGame5 - {Msg : Type} (encodeMsg : F.Desc × Msg → List Bool) + (hasAD : Bool) {Msg AD : Type} (encodeMsg : F.Desc × Msg → List Bool) + (encodeAD : F.Desc × AD → List Bool) (setup : ℕ → PMF F.Desc) (H : F.Desc × List Bool → List Bool) (isElem : F.Desc × List Bool → Bool) - (adversary : CCA2AdversaryOf Msg) (_bit : Bool) (n : ℕ) : Game := do + (adversary : ADCCA2AdversaryOf Msg AD) (_bit : Bool) (n : ℕ) : Game := do let d ← setup n; let U := PMF.uniformOfFintype (ZMod (F.order d)) let α ← U; let β ← U; let γ ← U; let δ ← U; let ζ ← U; let δ₁ ← U; let τ₁ ← U; let δ₂ ← U let τ₂ ← U let u := F.encode d (α • F.generator d); let v := F.encode d (β • F.generator d) let w := F.encode d (γ • F.generator d) let pk := F.encodeCramerShoupPublicKey d u (F.skNatOfZMod d δ 0 δ₁ 0 δ₂ 0) - runStatelessOracle (F.cramerShoupDHPhase1Reply H isElem d α δ δ₁ δ₂) + runStatelessOracle (F.cramerShoupDHPhase1Reply hasAD H isElem d α δ δ₁ δ₂) (adversary.choose n pk) fun choice => - let m₀ := encodeMsg (d, choice.1.1); let m₁ := encodeMsg (d, choice.1.2) + let m₀ := encodeMsg (d, choice.1.1.1); let m₁ := encodeMsg (d, choice.1.1.2) + let ad := encodeAD (d, choice.1.2) if isElem (d, m₀) && isElem (d, m₁) then let e := F.encode d (ζ • F.generator d) - let r := ofBits (H (d, encBits3 (v, w, e))) + let r := ofBits (H (d, encBits4 (v, w, e, ad))) let z' := F.encodeProjHash2 d (δ₁ - α * τ₁).val τ₁.val (δ₂ - α * τ₂).val τ₂.val r v w let ct := encBits4 (v, w, e, z') - runStatelessOracle (F.cramerShoupDHPhase2Reply H isElem d α δ δ₁ δ₂ ct) + runStatelessOracle (F.cramerShoupDHPhase2Reply hasAD H isElem d α δ δ₁ δ₂ ct ad) (adversary.guess n choice.2 ct) PMF.pure else PMF.pure false @@ -1389,12 +1711,13 @@ noncomputable def cramerShoupGame5 /-- In `cramerShoupGame5`, the challenge bit is unused, so the `false` and `true` experiments are identical. -/ theorem cramerShoupGame5_false_eq_true - {Msg : Type} (encodeMsg : F.Desc × Msg → List Bool) + (hasAD : Bool) {Msg AD : Type} (encodeMsg : F.Desc × Msg → List Bool) + (encodeAD : F.Desc × AD → List Bool) (setup : ℕ → PMF F.Desc) (H : F.Desc × List Bool → List Bool) (isElem : F.Desc × List Bool → Bool) - (adversary : CCA2AdversaryOf Msg) (n : ℕ) : - F.cramerShoupGame5 encodeMsg setup H isElem adversary false n = - F.cramerShoupGame5 encodeMsg setup H isElem adversary true n := + (adversary : ADCCA2AdversaryOf Msg AD) (n : ℕ) : + F.cramerShoupGame5 hasAD encodeMsg encodeAD setup H isElem adversary false n = + F.cramerShoupGame5 hasAD encodeMsg encodeAD setup H isElem adversary true n := rfl /-- **Game 4 $\to$ Game 5 bound** ([BonehShoup2023], Section 12.5.3, equations (12.23) and (12.28), @@ -1403,15 +1726,16 @@ whenever `γ ≠ α * β`, `projHash F d (δ - α * τ) τ (β • g) (γ • g) over `F.Carrier d` by `uniform_projHash_fiber_add` (Lemma 12.6), so `cramerShoupGame4` and `cramerShoupGame5` differ only on the event `γ = α * β` of probability `1 / F.order d ≤ 1 / m`. -/ theorem advantage_cramerShoupGame4_game5_le - {Msg : Type} (encodeMsg : F.Desc × Msg → List Bool) + (hasAD : Bool) {Msg AD : Type} (encodeMsg : F.Desc × Msg → List Bool) + (encodeAD : F.Desc × AD → List Bool) (setup : ℕ → PMF F.Desc) (H : F.Desc × List Bool → List Bool) (isElem : F.Desc × List Bool → Bool) (hmem : ∀ d x, isElem (d, x) = true ↔ x ∈ Set.range (F.encode d)) - (adversary : CCA2AdversaryOf Msg) (bit : Bool) (n m : ℕ) + (adversary : ADCCA2AdversaryOf Msg AD) (bit : Bool) (n m : ℕ) (hm0 : 0 < m) (hm : ∀ d ∈ (setup n).support, m ≤ F.order d) : Game.advantage - (F.cramerShoupGame4 encodeMsg setup H isElem adversary bit n) - (F.cramerShoupGame5 encodeMsg setup H isElem adversary bit n) ≤ + (F.cramerShoupGame4 hasAD encodeMsg encodeAD setup H isElem adversary bit n) + (F.cramerShoupGame5 hasAD encodeMsg encodeAD setup H isElem adversary bit n) ≤ 1 / (m : ℝ≥0) := by unfold cramerShoupGame4 cramerShoupGame5; simp only [PMF.monad_bind_eq_bind] refine advantage_bind_le_of_support (setup n) _ _ fun d hd => ?_ @@ -1430,32 +1754,33 @@ theorem advantage_cramerShoupGame4_game5_le refine congrArg _ (funext fun τ₂ => ?_) rw [bind_runStatelessOracle_comm, bind_runStatelessOracle_comm] refine congrArg _ (funext fun choice => ?_) - by_cases hvalid : (isElem (d, encodeMsg (d, choice.1.1)) && - isElem (d, encodeMsg (d, choice.1.2))) = true + by_cases hvalid : (isElem (d, encodeMsg (d, choice.1.1.1)) && + isElem (d, encodeMsg (d, choice.1.1.2))) = true · simp only [hvalid, ↓reduceIte] - have hm_mem : isElem (d, if bit then encodeMsg (d, choice.1.2) - else encodeMsg (d, choice.1.1)) = true := by + have hm_mem : isElem (d, if bit then encodeMsg (d, choice.1.1.2) + else encodeMsg (d, choice.1.1.1)) = true := by rcases Bool.and_eq_true_iff.mp hvalid with ⟨h0, h1⟩; cases bit <;> assumption obtain ⟨mElem, hmElem⟩ := (hmem d _).mp hm_mem let v := F.encode d (β • F.generator d); let w := F.encode d (γ • F.generator d) + let ad := encodeAD (d, choice.1.2) let k (e_elem : F.Carrier d) : Game := let e := F.encode d e_elem - let r := ofBits (H (d, encBits3 (v, w, e))) + let r := ofBits (H (d, encBits4 (v, w, e, ad))) let z' := F.encodeProjHash2 d (δ₁ - α * τ₁).val τ₁.val (δ₂ - α * τ₂).val τ₂.val r v w let ct := encBits4 (v, w, e, z') - runStatelessOracle (F.cramerShoupDHPhase2Reply H isElem d α δ δ₁ δ₂ ct) + runStatelessOracle (F.cramerShoupDHPhase2Reply hasAD H isElem d α δ δ₁ δ₂ ct ad) (adversary.guess n choice.2 ct) PMF.pure have h4 : (fun τ : ZMod (F.order d) => let sk := F.skNatOfZMod d (δ - α * τ) τ (δ₁ - α * τ₁) τ₁ (δ₂ - α * τ₂) τ₂ - let m_msg := if bit then encodeMsg (d, choice.1.2) else encodeMsg (d, choice.1.1) - let ct := F.encodeCramerShoupChallenge H d sk v w m_msg - runStatelessOracle (F.cramerShoupDHPhase2Reply H isElem d α δ δ₁ δ₂ ct) + let m_msg := if bit then encodeMsg (d, choice.1.1.2) else encodeMsg (d, choice.1.1.1) + let ct := F.encodeCramerShoupChallenge H d sk v w m_msg ad + runStatelessOracle (F.cramerShoupDHPhase2Reply hasAD H isElem d α δ δ₁ δ₂ ct ad) (adversary.guess n choice.2 ct) PMF.pure) = fun τ : ZMod (F.order d) => k (projHash F d (δ - α * τ) τ (β • F.generator d) (γ • F.generator d) + mElem) := by funext τ - simp only [encodeCramerShoupChallenge, skNatOfZMod, v, w, ← hmElem, encodeProjHash_encode, + simp only [encodeCramerShoupChallenge, skNatOfZMod, v, w, ad, ← hmElem, encodeProjHash_encode, F.add_encode, ZMod.natCast_zmod_val, k] rw [h4]; exact uniform_projHash_fiber_add F d hγ δ mElem k · simp only [hvalid, Bool.false_eq_true, ↓reduceIte] diff --git a/ComplexityTests/Crypto/CramerShoup.lean b/ComplexityTests/Crypto/CramerShoup.lean index 8dd8504728..301da83c9a 100644 --- a/ComplexityTests/Crypto/CramerShoup.lean +++ b/ComplexityTests/Crypto/CramerShoup.lean @@ -41,9 +41,9 @@ def isSchnorrElem (p : schnorrGroups.Desc × List Bool) : Bool := let a := ofBits p.2 (encNat a == p.2) && decide (0 < a ∧ a < p.1.1.1 ∧ a ^ p.1.1.2.1 % p.1.1.1 = 1) -/-- Concrete algebraic encryption/decryption roundtrip on `d23`: decrypting an honest encryption of -`m = 3 • g` under `α = 2`, `sk = ((1, 2), ((3, 4), (5, 6)))`, and randomness `β = 7` recovers -`some m`. -/ +/-- Concrete algebraic encryption/decryption roundtrip on `d23` (with and without associated data): +decrypting an honest encryption of `m = 3 • g` under `α = 2`, `sk = ((1, 2), ((3, 4), (5, 6)))`, +associated data `ad`, and randomness `β = 7` recovers `some m`. -/ example [∀ d, DecidableEq (schnorrGroups.Carrier d)] : let g := schnorrGroups.generator d23 let m := (3 : ZMod (schnorrGroups.order d23)) • g @@ -51,9 +51,9 @@ example [∀ d, DecidableEq (schnorrGroups.Carrier d)] : let sk := (((1 : ZMod (schnorrGroups.order d23)), (2 : ZMod (schnorrGroups.order d23))), (((3 : ZMod (schnorrGroups.order d23)), (4 : ZMod (schnorrGroups.order d23))), ((5 : ZMod (schnorrGroups.order d23)), (6 : ZMod (schnorrGroups.order d23))))) - let ct := schnorrGroups.cramerShoupEncrypt testHash d23 pk m 7 - schnorrGroups.cramerShoupDecrypt testHash d23 sk ct = some m := - schnorrGroups.cramerShoupDecrypt_cramerShoupEncrypt testHash d23 2 1 2 3 4 5 6 7 _ + let ct := schnorrGroups.cramerShoupEncrypt testHash d23 pk m [true, false] 7 + schnorrGroups.cramerShoupDecrypt testHash d23 sk ct [true, false] = some m := + schnorrGroups.cramerShoupDecrypt_cramerShoupEncrypt testHash d23 2 1 2 3 4 5 6 7 _ [true, false] /-- Tampering with the confirmation tag `z'` of an algebraic Cramer–Shoup ciphertext causes `cramerShoupDecrypt` to reject (`none`). -/ @@ -64,44 +64,52 @@ example [∀ d, DecidableEq (schnorrGroups.Carrier d)] : let sk := (((1 : ZMod (schnorrGroups.order d23)), (2 : ZMod (schnorrGroups.order d23))), (((3 : ZMod (schnorrGroups.order d23)), (4 : ZMod (schnorrGroups.order d23))), ((5 : ZMod (schnorrGroups.order d23)), (6 : ZMod (schnorrGroups.order d23))))) - let ct := schnorrGroups.cramerShoupEncrypt testHash d23 pk m 7 + let ct := schnorrGroups.cramerShoupEncrypt testHash d23 pk m [] 7 let badCt := (ct.1, ct.2.1, ct.2.2.1, ct.2.2.2 + g) - schnorrGroups.cramerShoupDecrypt testHash d23 sk badCt = none := by + schnorrGroups.cramerShoupDecrypt testHash d23 sk badCt [] = none := by simp [PrimeOrderGroupFamily.cramerShoupDecrypt, PrimeOrderGroupFamily.cramerShoupEncrypt, PrimeOrderGroupFamily.cramerShoupPublicKey, ProjectiveHash.projHash2Aux, ProjectiveHash.projHash2_smul_generator, schnorrGroups.generator_ne_zero d23] -/-- Framing roundtrips for `encBits3`, `encBits4`, and `extractCramerShoupTriple`, and randomised -word-level encryption (`b = 1` vs `b = 2` produce distinct ciphertexts on `d23`). -/ +/-- Framing roundtrips for `encBits3`, `encBits4`, `extractCramerShoupTuple`, and +`splitCramerShoupQuery`, and randomised word-level encryption (`b = 1` vs `b = 2` produce distinct +ciphertexts on `d23`). -/ example : decodeBits3 (encBits3 ([true], [false], [true, false])) = ([true], [false], [true, false]) ∧ decodeBits4 (encBits4 ([true], [false], [true, false], [])) = ([true], [false], [true, false], []) ∧ - extractCramerShoupTriple (encBits4 ([true], [false], [true, false], [])) = - encBits3 ([true], [false], [true, false]) ∧ - schnorrGroups.encodeCramerShoupEncrypt testHash d23 ([], [], [], []) [] 1 ≠ - schnorrGroups.encodeCramerShoupEncrypt testHash d23 ([], [], [], []) [] 2 := by + extractCramerShoupTuple (encBits4 ([true], [false], [true, false], [])) [true, true] = + encBits4 ([true], [false], [true, false], [true, true]) ∧ + splitCramerShoupQuery false [true, false] = ([true, false], []) ∧ + splitCramerShoupQuery true (encPair encBits encBits ([true], [false])) = ([true], [false]) ∧ + schnorrGroups.encodeCramerShoupEncrypt testHash d23 ([], [], [], []) [] [] 1 ≠ + schnorrGroups.encodeCramerShoupEncrypt testHash d23 ([], [], [], []) [] [] 2 := by refine ⟨decodeBits3_encBits3 _, decodeBits4_encBits4 _, - extractCramerShoupTriple_encBits4 _ _ _ _, ?_⟩ - decide + extractCramerShoupTuple_encBits4 _ _ _ _ _, rfl, ?_, ?_⟩ + · decide + · decide -/-- Word-level encryption and decryption roundtrip on `d23` (`u = 4 = 2^2`, `m = 3 ∈ G`), together -with rejection (`= none`) on a tampered confirmation tag, malformed framing, and an out-of-group -component. -/ +/-- Word-level encryption and decryption roundtrip on `d23` (`u = 4 = 2^2`, `m = 3 ∈ G`) with and +without associated data, together with rejection (`= none`) on mismatched associated data, a +tampered confirmation tag, malformed framing, and an out-of-group component. -/ example : let sk := ((1, 2), ((3, 4), (5, 6))) let u := (4 : ℕ).bits let pk := decodeBits4 (dropFrame (schnorrGroups.encodeCramerShoupPublicKey d23 u sk)) let m := (3 : ℕ).bits - let ct := schnorrGroups.encodeCramerShoupEncrypt testHash d23 pk m 7 + let ct := schnorrGroups.encodeCramerShoupEncrypt testHash d23 pk m [] 7 + let adHash (p : schnorrGroups.Desc × List Bool) : List Bool := p.2.reverse.take 3 + let adCt := schnorrGroups.encodeCramerShoupEncrypt adHash d23 pk m [true] 7 let parts := decodeBits4 ct let tamperedCt := encBits4 (parts.1, parts.2.1, parts.2.2.1, (1 : ℕ).bits) let nonGroupCt := encBits4 ([false], parts.2.1, parts.2.2.1, parts.2.2.2) - schnorrGroups.decodeCramerShoup testHash isSchnorrElem d23 sk ct = some m ∧ - schnorrGroups.decodeCramerShoup testHash isSchnorrElem d23 sk tamperedCt = none ∧ - schnorrGroups.decodeCramerShoup testHash isSchnorrElem d23 sk [true, false] = none ∧ - schnorrGroups.decodeCramerShoup testHash isSchnorrElem d23 sk nonGroupCt = none := by + schnorrGroups.decodeCramerShoup testHash isSchnorrElem d23 sk ct [] = some m ∧ + schnorrGroups.decodeCramerShoup adHash isSchnorrElem d23 sk adCt [true] = some m ∧ + schnorrGroups.decodeCramerShoup adHash isSchnorrElem d23 sk adCt [false] = none ∧ + schnorrGroups.decodeCramerShoup testHash isSchnorrElem d23 sk tamperedCt [] = none ∧ + schnorrGroups.decodeCramerShoup testHash isSchnorrElem d23 sk [true, false] [] = none ∧ + schnorrGroups.decodeCramerShoup testHash isSchnorrElem d23 sk nonGroupCt [] = none := by decide /-- Concrete two-sided checks for `decodeCramerShoupDH` and `isCramerShoupBadDH` on `d23`: @@ -114,26 +122,27 @@ example : let u := (4 : ℕ).bits let pk := decodeBits4 (dropFrame (schnorrGroups.encodeCramerShoupPublicKey d23 u sk)) let m := (3 : ℕ).bits - let dhCt := schnorrGroups.encodeCramerShoupEncrypt testHash d23 pk m 1 + let dhCt := schnorrGroups.encodeCramerShoupEncrypt testHash d23 pk m [] 1 let nonDH_v := (2 : ℕ).bits let nonDH_w := (3 : ℕ).bits let nonDH_e := (3 : ℕ).bits - let r := ofBits (testHash (d23, encBits3 (nonDH_v, nonDH_w, nonDH_e))) + let r := ofBits (testHash (d23, encBits4 (nonDH_v, nonDH_w, nonDH_e, []))) let nonDH_z' := schnorrGroups.encodeProjHash2 d23 3 0 5 0 r nonDH_v nonDH_w let nonDHCt := encBits4 (nonDH_v, nonDH_w, nonDH_e, nonDH_z') - schnorrGroups.decodeCramerShoupDH testHash isSchnorrElem d23 2 1 3 5 dhCt = some m ∧ - schnorrGroups.decodeCramerShoupDH testHash isSchnorrElem d23 2 1 3 5 nonDHCt = none ∧ - schnorrGroups.isCramerShoupBadDH testHash isSchnorrElem d23 2 sk dhCt = false ∧ - schnorrGroups.isCramerShoupBadDH testHash isSchnorrElem d23 2 sk nonDHCt = true := by + schnorrGroups.decodeCramerShoupDH testHash isSchnorrElem d23 2 1 3 5 dhCt [] = some m ∧ + schnorrGroups.decodeCramerShoupDH testHash isSchnorrElem d23 2 1 3 5 nonDHCt [] = none ∧ + schnorrGroups.isCramerShoupBadDH testHash isSchnorrElem d23 2 sk dhCt [] = false ∧ + schnorrGroups.isCramerShoupBadDH testHash isSchnorrElem d23 2 sk nonDHCt [] = true := by decide -/-- Concrete check of `isCramerShoupCollision` on `d23`: returns `false` when the `(v, w, e)` -triples are equal, and `true` when distinct `(v, w, e)` triples collide modulo `q = 11`. -/ +/-- Concrete check of `isCramerShoupCollision` on `d23`: returns `false` when the `(v, w, e, ad)` +4-tuples are equal, and `true` when distinct `(v, w, e, ad)` 4-tuples collide modulo `q = 11`. -/ example : let ct₁ := encBits4 ([true], [true], [true], [true]) let ct₂ := encBits4 ([false], [true], [true], [true]) - schnorrGroups.isCramerShoupCollision (fun _ => []) d23 ct₁ ct₁ = false ∧ - schnorrGroups.isCramerShoupCollision (fun _ => []) d23 ct₁ ct₂ = true := by + schnorrGroups.isCramerShoupCollision (fun _ => []) d23 ct₁ [] ct₁ [] = false ∧ + schnorrGroups.isCramerShoupCollision (fun _ => []) d23 ct₁ [] ct₂ [] = true ∧ + schnorrGroups.isCramerShoupCollision (fun _ => []) d23 ct₁ [true] ct₁ [false] = true := by decide open scoped NNReal @@ -156,8 +165,8 @@ example : /-- Polynomial-time computability certificates for `encodeCramerShoupPublicKey`, `encodeCramerShoupEncrypt`, `encodeCramerShoupChallenge`, `decodeCramerShoup`, `cramerShoupValid`, -`cramerShoupKeyGenProb`, `cramerShoupEncryptProb`, and `cramerShoupDecryptOpt` instantiated on -`schnorrGroups`. -/ +`cramerShoupKeyGenProb`, `cramerShoupADEncryptProb`, `cramerShoupADDecryptOpt`, +`cramerShoupEncryptProb`, and `cramerShoupDecryptOpt` instantiated on `schnorrGroups`. -/ example {H : schnorrGroups.Desc × List Bool → List Bool} {isElem : schnorrGroups.Desc × List Bool → Bool} {d : ℕ → schnorrGroups.Desc} @@ -170,24 +179,36 @@ example {H : schnorrGroups.Desc × List Bool → List Bool} (encPair schnorrGroups.encDesc (encPair encBits encCramerShoupSK)) encBits ∧ ComputableInPolyTime (fun p : schnorrGroups.Desc × (List Bool × List Bool × List Bool × List Bool) × - List Bool × ℕ => - schnorrGroups.encodeCramerShoupEncrypt H p.1 p.2.1 p.2.2.1 p.2.2.2) - (encPair schnorrGroups.encDesc (encPair encBits4 (encPair encBits encNat))) encBits ∧ + List Bool × List Bool × ℕ => + schnorrGroups.encodeCramerShoupEncrypt H p.1 p.2.1 p.2.2.1 p.2.2.2.1 p.2.2.2.2) + (encPair schnorrGroups.encDesc + (encPair encBits4 (encPair encBits (encPair encBits encNat)))) encBits ∧ ComputableInPolyTime (fun p : schnorrGroups.Desc × ((ℕ × ℕ) × ((ℕ × ℕ) × (ℕ × ℕ))) × - List Bool × List Bool × List Bool => - schnorrGroups.encodeCramerShoupChallenge H p.1 p.2.1 p.2.2.1 p.2.2.2.1 p.2.2.2.2) + List Bool × List Bool × List Bool × List Bool => + schnorrGroups.encodeCramerShoupChallenge H p.1 p.2.1 p.2.2.1 p.2.2.2.1 + p.2.2.2.2.1 p.2.2.2.2.2) (encPair schnorrGroups.encDesc - (encPair encCramerShoupSK (encPair encBits (encPair encBits encBits)))) encBits ∧ + (encPair encCramerShoupSK + (encPair encBits (encPair encBits (encPair encBits encBits))))) encBits ∧ ComputableInPolyTime - (fun p : schnorrGroups.Desc × ((ℕ × ℕ) × ((ℕ × ℕ) × (ℕ × ℕ))) × List Bool => - schnorrGroups.decodeCramerShoup H isElem p.1 p.2.1 p.2.2) - (encPair schnorrGroups.encDesc (encPair encCramerShoupSK encBits)) (encOption encBits) ∧ + (fun p : schnorrGroups.Desc × ((ℕ × ℕ) × ((ℕ × ℕ) × (ℕ × ℕ))) × + List Bool × List Bool => + schnorrGroups.decodeCramerShoup H isElem p.1 p.2.1 p.2.2.1 p.2.2.2) + (encPair schnorrGroups.encDesc + (encPair encCramerShoupSK (encPair encBits encBits))) (encOption encBits) ∧ ComputableInPolyTime (fun p : ℕ × List Bool × List Bool × List Bool => schnorrGroups.cramerShoupValid isElem d p.1 p.2.1 p.2.2.1 p.2.2.2) (encPair encUnary (encPair encBits (encPair encBits encBits))) encBool ∧ IsPPTOn encUnary (encPair encBits encBits) (schnorrGroups.cramerShoupKeyGenProb d) ∧ + IsPPTOn (encPair encUnary (encPair encBits (encPair encBits encBits))) encBits + (fun p => + schnorrGroups.cramerShoupADEncryptProb encBits H d p.1 p.2.1 p.2.2.1 p.2.2.2) ∧ + ComputableInPolyTime + (fun p : ℕ × List Bool × List Bool × List Bool => + schnorrGroups.cramerShoupADDecryptOpt H isElem d p.1 p.2.1 p.2.2.1 p.2.2.2) + (encPair encUnary (encPair encBits (encPair encBits encBits))) (encOption encBits) ∧ IsPPTOn (encPair encUnary (encPair encBits encBits)) encBits (fun p => schnorrGroups.cramerShoupEncryptProb H d p.1 p.2.1 p.2.2) ∧ ComputableInPolyTime @@ -200,6 +221,8 @@ example {H : schnorrGroups.Desc × List Bool → List Bool} schnorrGroups.computableInPolyTime_decodeCramerShoup hH hisElem, schnorrGroups.computableInPolyTime_cramerShoupValid hisElem hd, schnorrGroups.isPPTOn_cramerShoupKeyGenProb hd, + schnorrGroups.isPPTOn_cramerShoupADEncryptProb encBits hH hd, + schnorrGroups.computableInPolyTime_cramerShoupADDecryptOpt hH hisElem hd, schnorrGroups.isPPTOn_cramerShoupEncryptProb hH hd, schnorrGroups.computableInPolyTime_cramerShoupDecryptOpt hH hisElem hd⟩ @@ -208,32 +231,32 @@ Lemma 12.6 (`advantage_cramerShoupGame4_game5_le` and `cramerShoupGame5_false_eq on `schnorrGroups` at `d23` (`q = 11`). -/ example (hmem : ∀ (d : schnorrGroups.Desc) (x : List Bool), isSchnorrElem (d, x) = true ↔ x ∈ Set.range (schnorrGroups.encode d)) - (adversary : Encryption.CCA2AdversaryOf (List Bool)) (bit : Bool) (n Q₁ Q₂ : ℕ) + (adversary : Encryption.WordADCCA2Adversary (List Bool)) (bit : Bool) (n Q₁ Q₂ : ℕ) (hq1 : schnorrGroups.HonestPhase1QueryBound (fun _ => PMF.pure d23) adversary n Q₁) - (hq2 : schnorrGroups.HonestPhase2QueryBound (fun _ => PMF.pure d23) testHash isSchnorrElem - adversary n Q₂) : - (schnorrGroups.cramerShoupGame3 (fun p => p.2) (fun _ => PMF.pure d23) testHash isSchnorrElem - adversary bit n).advantage - (schnorrGroups.cramerShoupGame4 (fun p => p.2) (fun _ => PMF.pure d23) testHash isSchnorrElem - adversary bit n) ≤ ((Q₁ + Q₂ : ℕ) : ℝ≥0) / (11 : ℕ) ∧ - (schnorrGroups.cramerShoupGame4 (fun p => p.2) (fun _ => PMF.pure d23) testHash isSchnorrElem - adversary bit n).advantage - (schnorrGroups.cramerShoupGame5 (fun p => p.2) (fun _ => PMF.pure d23) testHash isSchnorrElem - adversary bit n) ≤ 1 / (11 : ℕ) ∧ - schnorrGroups.cramerShoupGame5 (fun p => p.2) (fun _ => PMF.pure d23) testHash isSchnorrElem - adversary false n = - schnorrGroups.cramerShoupGame5 (fun p => p.2) (fun _ => PMF.pure d23) testHash isSchnorrElem - adversary true n := by + (hq2 : schnorrGroups.HonestPhase2QueryBound true (fun p => p.2) (fun _ => PMF.pure d23) + testHash isSchnorrElem adversary n Q₂) : + (schnorrGroups.cramerShoupGame3 true (fun p => p.2) (fun p => p.2) (fun _ => PMF.pure d23) + testHash isSchnorrElem adversary bit n).advantage + (schnorrGroups.cramerShoupGame4 true (fun p => p.2) (fun p => p.2) (fun _ => PMF.pure d23) + testHash isSchnorrElem adversary bit n) ≤ ((Q₁ + Q₂ : ℕ) : ℝ≥0) / (11 : ℕ) ∧ + (schnorrGroups.cramerShoupGame4 true (fun p => p.2) (fun p => p.2) (fun _ => PMF.pure d23) + testHash isSchnorrElem adversary bit n).advantage + (schnorrGroups.cramerShoupGame5 true (fun p => p.2) (fun p => p.2) (fun _ => PMF.pure d23) + testHash isSchnorrElem adversary bit n) ≤ 1 / (11 : ℕ) ∧ + schnorrGroups.cramerShoupGame5 true (fun p => p.2) (fun p => p.2) (fun _ => PMF.pure d23) + testHash isSchnorrElem adversary false n = + schnorrGroups.cramerShoupGame5 true (fun p => p.2) (fun p => p.2) (fun _ => PMF.pure d23) + testHash isSchnorrElem adversary true n := by have hm : ∀ d ∈ (PMF.pure d23).support, 11 ≤ schnorrGroups.order d := by intro d hd simp only [PMF.support_pure, Set.mem_singleton_iff] at hd subst hd decide - refine ⟨?_, ?_, schnorrGroups.cramerShoupGame5_false_eq_true _ _ _ _ _ _⟩ - · exact schnorrGroups.advantage_cramerShoupGame3_game4_le (fun p => p.2) + refine ⟨?_, ?_, schnorrGroups.cramerShoupGame5_false_eq_true _ _ _ _ _ _ _ _⟩ + · exact schnorrGroups.advantage_cramerShoupGame3_game4_le true (fun p => p.2) (fun p => p.2) (fun _ => PMF.pure d23) testHash isSchnorrElem hmem adversary bit n Q₁ Q₂ 11 (by decide) hm hq1 hq2 - · exact schnorrGroups.advantage_cramerShoupGame4_game5_le (fun p => p.2) + · exact schnorrGroups.advantage_cramerShoupGame4_game5_le true (fun p => p.2) (fun p => p.2) (fun _ => PMF.pure d23) testHash isSchnorrElem hmem adversary bit n 11 (by decide) hm @@ -257,11 +280,24 @@ example : (schnorrGroups.encode d23 (schnorrGroups.generator d23)) (encNat 5) = false := by decide -/-- Concrete `cramerShoupPhase2Reply` rejection when the queried ciphertext equals the challenge -ciphertext `ct`. -/ +/-- Concrete `cramerShoupPhase2Reply` rejection when the queried `(ct, ad)` pair equals the +challenge `(ct, ad)` pair (and decryption when the same ciphertext is queried with distinct +associated data `ad' ≠ ad`). -/ example : - schnorrGroups.cramerShoupPhase2Reply testHash isSchnorrElem d23 - ((2, 3), ((4, 5), (6, 7))) [true] [true] = [] := by + let pk := schnorrGroups.cramerShoupPublicKey d23 4 2 3 4 5 6 7 + let pkWords := + (schnorrGroups.encode d23 pk.1, schnorrGroups.encode d23 pk.2.1, + schnorrGroups.encode d23 pk.2.2.1, schnorrGroups.encode d23 pk.2.2.2) + let m := schnorrGroups.encode d23 ((5 : ℕ) • schnorrGroups.generator d23) + let adCt := schnorrGroups.encodeCramerShoupEncrypt testHash d23 pkWords m [true] 8 + schnorrGroups.cramerShoupPhase2Reply false testHash isSchnorrElem d23 + ((2, 3), ((4, 5), (6, 7))) [true] [] [true] = [] ∧ + schnorrGroups.cramerShoupPhase2Reply true testHash isSchnorrElem d23 + ((2, 3), ((4, 5), (6, 7))) adCt [true] + (encPair encBits encBits (adCt, [true])) = [] ∧ + schnorrGroups.cramerShoupPhase2Reply true testHash isSchnorrElem d23 + ((2, 3), ((4, 5), (6, 7))) adCt [false] + (encPair encBits encBits (adCt, [true])) = encOption encBits (some m) := by decide /-- Concrete `cramerShoupPhase2Reply` decryption when the queried ciphertext is a distinct valid @@ -272,22 +308,33 @@ example : (schnorrGroups.encode d23 pk.1, schnorrGroups.encode d23 pk.2.1, schnorrGroups.encode d23 pk.2.2.1, schnorrGroups.encode d23 pk.2.2.2) let m := schnorrGroups.encode d23 ((5 : ℕ) • schnorrGroups.generator d23) - let ct := schnorrGroups.encodeCramerShoupEncrypt testHash d23 pkWords m 8 - schnorrGroups.cramerShoupPhase2Reply testHash isSchnorrElem d23 - ((2, 3), ((4, 5), (6, 7))) [true] ct = encOption encBits (some m) := by + let ct := schnorrGroups.encodeCramerShoupEncrypt testHash d23 pkWords m [] 8 + schnorrGroups.cramerShoupPhase2Reply false testHash isSchnorrElem d23 + ((2, 3), ((4, 5), (6, 7))) [true] [] ct = encOption encBits (some m) := by decide -/-- Uniform strict-PPT certificates for `cramerShoupToDDHDistinguisher`, +/-- Uniform strict-PPT certificates for `cramerShoupADToDDHDistinguisher`, +`cramerShoupADToCollisionFinder`, `cramerShoupToDDHDistinguisher`, `cramerShoupToCollisionFinder`, `cramerShoupKeyGenProb`, `cramerShoupEncryptProb`, and `cramerShoupDecryptOpt` on `schnorrGroups`. -/ example {H : schnorrGroups.Desc × List Bool → List Bool} {isElem : schnorrGroups.Desc × List Bool → Bool} {d : ℕ → schnorrGroups.Desc} + {adAdversary : Encryption.WordADCCA2Adversary (List Bool)} {adversary : Encryption.WordCCA2Adversary} (hH : ComputableInPolyTime H (encPair schnorrGroups.encDesc encBits) encBits) (hisElem : ComputableInPolyTime isElem (encPair schnorrGroups.encDesc encBits) encBool) (hd : ComputableInPolyTime d encUnary schnorrGroups.encDesc) + (hADA : adAdversary.IsPPT encBits) (hA : adversary.IsPPT) (bit : Bool) : + IsPPTOn + (encPair encUnary (encPair schnorrGroups.encDesc (encPair encBits (encPair encBits encBits)))) + encBool + (fun p => + schnorrGroups.cramerShoupADToDDHDistinguisher encBits H isElem adAdversary bit p.1 p.2) ∧ + IsPPTOn (encPair encUnary schnorrGroups.encDesc) (encPair encBits encBits) + (fun p => + schnorrGroups.cramerShoupADToCollisionFinder encBits H isElem adAdversary bit p.1 p.2) ∧ IsPPTOn (encPair encUnary (encPair schnorrGroups.encDesc (encPair encBits (encPair encBits encBits)))) encBool @@ -301,12 +348,59 @@ example {H : schnorrGroups.Desc × List Bool → List Bool} (fun p : ℕ × List Bool × List Bool => schnorrGroups.cramerShoupDecryptOpt H isElem d p.1 p.2.1 p.2.2) (encPair encUnary (encPair encBits encBits)) (encOption encBits) := - ⟨schnorrGroups.isPPTOn_cramerShoupToDDHDistinguisher hH hisElem hA bit, + ⟨schnorrGroups.isPPTOn_cramerShoupADToDDHDistinguisher hH hisElem hADA bit, + schnorrGroups.isPPTOn_cramerShoupADToCollisionFinder hH hisElem hADA bit, + schnorrGroups.isPPTOn_cramerShoupToDDHDistinguisher hH hisElem hA bit, schnorrGroups.isPPTOn_cramerShoupToCollisionFinder hH hisElem hA bit, schnorrGroups.isPPTOn_cramerShoupKeyGenProb hd, schnorrGroups.isPPTOn_cramerShoupEncryptProb hH hd, schnorrGroups.computableInPolyTime_cramerShoupDecryptOpt hH hisElem hd⟩ +/-- Concrete instantiation of Theorem 12.9 / Exercise 12.27 with associated data +(`advantage_cramerShoupADGroupCCA2Game_le`) on `schnorrGroups` at `d23` (`q = 11`). -/ +example (hmem : ∀ (d : schnorrGroups.Desc) (x : List Bool), + isSchnorrElem (d, x) = true ↔ x ∈ Set.range (schnorrGroups.encode d)) + (adversary : Encryption.WordADCCA2Adversary (List Bool)) (Q₁ Q₂ : ℕ → ℕ) + (hQ : adversary.IsQueryBounded Q₁ Q₂) (n : ℕ) : + Game.advantage + (schnorrGroups.cramerShoupADGroupCCA2Game encBits (fun _ => PMF.pure d23) testHash + isSchnorrElem adversary false n) + (schnorrGroups.cramerShoupADGroupCCA2Game encBits (fun _ => PMF.pure d23) testHash + isSchnorrElem adversary true n) ≤ + Game.advantage + (schnorrGroups.ddhRealGame (fun _ => PMF.pure d23) + (schnorrGroups.cramerShoupADToDDHDistinguisher encBits testHash isSchnorrElem + adversary false) n) + (schnorrGroups.ddhIdealGame (fun _ => PMF.pure d23) + (schnorrGroups.cramerShoupADToDDHDistinguisher encBits testHash isSchnorrElem + adversary false) n) + + Game.advantage + (schnorrGroups.ddhRealGame (fun _ => PMF.pure d23) + (schnorrGroups.cramerShoupADToDDHDistinguisher encBits testHash isSchnorrElem + adversary true) n) + (schnorrGroups.ddhIdealGame (fun _ => PMF.pure d23) + (schnorrGroups.cramerShoupADToDDHDistinguisher encBits testHash isSchnorrElem + adversary true) n) + + Game.succProbability + (collisionGameFor (fun _ => PMF.pure d23) (schnorrGroups.cramerShoupHashMod testHash) + (fun _ _ _ => true) + (schnorrGroups.cramerShoupADToCollisionFinder encBits testHash isSchnorrElem + adversary false) n) + + Game.succProbability + (collisionGameFor (fun _ => PMF.pure d23) (schnorrGroups.cramerShoupHashMod testHash) + (fun _ _ _ => true) + (schnorrGroups.cramerShoupADToCollisionFinder encBits testHash isSchnorrElem + adversary true) n) + + ((2 * Q₁ n + 2 * Q₂ n + 2 : ℕ) : ℝ≥0) / (11 : ℕ) + + 42 / 2 ^ n := by + have hm : ∀ d ∈ (PMF.pure d23).support, 11 ≤ schnorrGroups.order d := by + intro d hd + simp only [PMF.support_pure, Set.mem_singleton_iff] at hd + subst hd + decide + exact schnorrGroups.advantage_cramerShoupADGroupCCA2Game_le encBits (fun _ => PMF.pure d23) + testHash isSchnorrElem hmem adversary Q₁ Q₂ hQ n 11 (by decide) hm + /-- Concrete instantiation of Theorem 12.9 / Exercise 12.27 (`advantage_cramerShoupGroupCCA2Game_le`) on `schnorrGroups` at `d23` (`q = 11`). -/ example (hmem : ∀ (d : schnorrGroups.Desc) (x : List Bool), @@ -346,9 +440,11 @@ example (hmem : ∀ (d : schnorrGroups.Desc) (x : List Bool), exact schnorrGroups.advantage_cramerShoupGroupCCA2Game_le (fun _ => PMF.pure d23) testHash isSchnorrElem hmem adversary Q₁ Q₂ hQ n 11 (by decide) hm -/-- Conditional API client over a parameterized group family. This does not assert DDH security -for the fixed finite fixture `d23`; the group-order lower bound must grow with the parameter. -/ -example {H : schnorrGroups.Desc × List Bool → List Bool} +/-- Conditional API client over a parameterized group family (both `INDCCA2ADOn` with associated +data and `INDCCA2On` without associated data). This does not assert DDH security for the fixed +finite fixture `d23`; the group-order lower bound must grow with the parameter. -/ +example {AD : Type} (encAD : AD ↪ List Bool) + {H : schnorrGroups.Desc × List Bool → List Bool} {isElem : schnorrGroups.Desc × List Bool → Bool} {d : ℕ → schnorrGroups.Desc} (hH : ComputableInPolyTime H (encPair schnorrGroups.encDesc encBits) encBits) @@ -360,10 +456,14 @@ example {H : schnorrGroups.Desc × List Bool → List Bool} (schnorrGroups.cramerShoupHashMod H)) {m : ℕ → ℕ} (hm0 : ∀ n, 0 < m n) (hm : ∀ n, m n ≤ schnorrGroups.order (d n)) (hinv : Negligible fun n => (1 / m n : ℝ≥0)) : + INDCCA2ADOn (schnorrGroups.cramerShoupValid isElem d) encBits encAD encBits + (schnorrGroups.cramerShoupKeyGenProb d) (schnorrGroups.cramerShoupADEncryptProb encAD H d) + (schnorrGroups.cramerShoupADDecryptOpt H isElem d) ∧ INDCCA2On (schnorrGroups.cramerShoupValid isElem d) encBits encBits (schnorrGroups.cramerShoupKeyGenProb d) (schnorrGroups.cramerShoupEncryptProb H d) (schnorrGroups.cramerShoupDecryptOpt H isElem d) := - hDDH.indcca2On_cramerShoup hH hisElem hd hmem hCR hm0 hm hinv + ⟨hDDH.indcca2ADOn_cramerShoup encAD hH hisElem hd hmem hCR hm0 hm hinv, + hDDH.indcca2On_cramerShoup hH hisElem hd hmem hCR hm0 hm hinv⟩ -- A fixed nonzero inverse-order bound cannot supply the asymptotic security premise. example : ¬ Negligible (fun _ : ℕ => (1 / 11 : ℝ≥0)) :=