Theory IND_CPA
theory IND_CPA imports
  CryptHOL.Generative_Probabilistic_Value
  CryptHOL.Computational_Model
  CryptHOL.Negligible
begin
subsection ‹The IND-CPA game for symmetric encryption schemes›
locale ind_cpa = 
  fixes key_gen :: "'key spmf" 
  and encrypt :: "'key ⇒ 'plain ⇒ 'cipher spmf"  
  and decrypt :: "'key ⇒ 'cipher ⇒ 'plain option" 
  and valid_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 oracle has to ensure that the received
  plaintexts are valid.
›
type_synonym ('plain', 'cipher', 'state) adversary = 
  "(('plain' × 'plain') × 'state, 'plain', 'cipher') gpv
   × ('cipher' ⇒ 'state ⇒ (bool, 'plain', 'cipher') gpv)"
definition encrypt_oracle :: "'key ⇒ unit ⇒ 'plain ⇒ ('cipher × unit) spmf"
where
  "encrypt_oracle key σ plain = do {
     cipher ← encrypt key plain;
     return_spmf (cipher, ())
  }"
definition ind_cpa :: "('plain, 'cipher, 'state) adversary ⇒ bool spmf"
where
  "ind_cpa 𝒜 = do {
     let (𝒜1, 𝒜2) = 𝒜;
     key ← key_gen;
     b ← coin_spmf;
     (guess, _) ← exec_gpv (encrypt_oracle key) (do {
         ((m0, m1), σ) ← 𝒜1;
         if valid_plain m0 ∧ valid_plain m1 then do {
           cipher ← lift_spmf (encrypt key (if b then m0 else m1));
           𝒜2 cipher σ
         } else lift_spmf coin_spmf
       }) ();
     return_spmf (guess = b)
  }"
definition advantage :: "('plain, 'cipher, 'state) adversary ⇒ real"
where "advantage 𝒜 = ¦spmf (ind_cpa 𝒜) True - 1/2¦"
lemma advantage_nonneg: "advantage 𝒜 ≥ 0" by(simp add: advantage_def)
definition ibounded_by :: "('plain, 'cipher, 'state) adversary ⇒ enat ⇒ bool"
where 
  "ibounded_by = (λ(𝒜1, 𝒜2) q. 
  (∃q1 q2. interaction_any_bounded_by 𝒜1 q1 ∧ (∀cipher σ. interaction_any_bounded_by (𝒜2 cipher σ) q2) ∧ q1 + q2 ≤ q))"
lemma ibounded_byE [consumes 1, case_names ibounded_by, elim?]:
  assumes "ibounded_by (𝒜1, 𝒜2) q"
  obtains q1 q2
  where "q1 + q2 ≤ q"
  and "interaction_any_bounded_by 𝒜1 q1"
  and "⋀cipher σ. interaction_any_bounded_by (𝒜2 cipher σ) q2"
using assms by(auto simp add: ibounded_by_def)
lemma ibounded_byI [intro?]:
  "⟦ interaction_any_bounded_by 𝒜1 q1; ⋀cipher σ. interaction_any_bounded_by (𝒜2 cipher σ) q2; q1 + q2 ≤ q ⟧
  ⟹ ibounded_by (𝒜1, 𝒜2) q"
by(auto simp add: ibounded_by_def)
definition lossless :: "('plain, 'cipher, 'state) adversary ⇒ bool"
where "lossless = (λ(𝒜1, 𝒜2). lossless_gpv ℐ_full 𝒜1 ∧ (∀cipher σ. lossless_gpv ℐ_full (𝒜2 cipher σ)))"
end
end