Theory IND_CPA_PK
theory IND_CPA_PK imports
  CryptHOL.Computational_Model
  CryptHOL.Negligible
begin
subsection ‹The IND-CPA game for public-key encryption with oracle access›
locale ind_cpa_pk = 
  fixes key_gen :: "('pubkey × 'privkey, 'call, 'ret) gpv" 
  and aencrypt :: "'pubkey ⇒ 'plain ⇒ ('cipher, 'call, 'ret) gpv"  
  and adecrypt :: "'privkey ⇒ 'cipher ⇒ ('plain, 'call, 'ret) gpv" 
  and valid_plains :: "'plain ⇒ 'plain ⇒ bool" 
begin
text ‹
  We cannot incorporate the predicate @{term valid_plain} in the type @{typ "'plain"} of plaintexts,
  because the single @{typ "'plain"} must contain plaintexts for all values of the security parameter,
  as HOL does not have dependent types.  Consequently, the game has to ensure that the received
  plaintexts are valid.
›
type_synonym ('pubkey', 'plain', 'cipher', 'call', 'ret', 'state) adversary = 
  "('pubkey' ⇒ (('plain' × 'plain') × 'state, 'call', 'ret') gpv)
   × ('cipher' ⇒ 'state ⇒ (bool, 'call', 'ret') gpv)"
fun ind_cpa :: "('pubkey, 'plain, 'cipher, 'call, 'ret, 'state) adversary ⇒ (bool, 'call, 'ret) gpv"
where
  "ind_cpa (𝒜1, 𝒜2) = TRY do {
     (pk, sk) ← key_gen;
     b ← lift_spmf coin_spmf;
     ((m0, m1), σ) ← (𝒜1 pk);
     assert_gpv (valid_plains m0 m1);
     cipher ← aencrypt pk (if b then m0 else m1);
     guess ← 𝒜2 cipher σ; 
     Done (guess = b)
   } ELSE lift_spmf coin_spmf"
definition advantage :: "('σ ⇒ 'call ⇒ ('ret × 'σ) spmf) ⇒ 'σ ⇒ ('pubkey, 'plain, 'cipher, 'call, 'ret, 'state) adversary ⇒ real"
where "advantage oracle σ 𝒜 = ¦spmf (run_gpv oracle (ind_cpa 𝒜) σ) True - 1/2¦"
lemma advantage_nonneg: "advantage oracle σ 𝒜 ≥ 0" by(simp add: advantage_def)
definition ibounded_by :: "('call ⇒ bool) ⇒ ('pubkey, 'plain, 'cipher, 'call, 'ret, 'state) adversary ⇒ enat ⇒ bool"
where 
  "ibounded_by consider = (λ(𝒜1, 𝒜2) q. 
  (∃q1 q2. (∀pk. interaction_bounded_by consider (𝒜1 pk) q1) ∧ (∀cipher σ. interaction_bounded_by consider (𝒜2 cipher σ) q2) ∧ q1 + q2 ≤ q))"
lemma ibounded_by'E [consumes 1, case_names ibounded_by', elim?]:
  assumes "ibounded_by consider (𝒜1, 𝒜2) q"
  obtains q1 q2
  where "q1 + q2 ≤ q"
  and "⋀pk. interaction_bounded_by consider (𝒜1 pk) q1"
  and "⋀cipher σ. interaction_bounded_by consider (𝒜2 cipher σ) q2"
using assms by(auto simp add: ibounded_by_def)
lemma ibounded_byI [intro?]:
  "⟦ ⋀pk. interaction_bounded_by consider (𝒜1 pk) q1; ⋀cipher σ. interaction_bounded_by consider (𝒜2 cipher σ) q2; q1 + q2 ≤ q ⟧
  ⟹ ibounded_by consider (𝒜1, 𝒜2) q"
by(auto simp add: ibounded_by_def)
definition lossless :: "('pubkey, 'plain, 'cipher, 'call, 'ret, 'state) adversary ⇒ bool"
where "lossless = (λ(𝒜1, 𝒜2). (∀pk. lossless_gpv ℐ_full (𝒜1 pk)) ∧ (∀cipher σ. lossless_gpv ℐ_full (𝒜2 cipher σ)))"
end
end