Theory Elgamal

(* Title: Elgamal.thy
  Author: Andreas Lochbihler, ETH Zurich *)

section β€ΉCryptographic constructions and their securityβ€Ί

theory Elgamal imports
  CryptHOL.Cyclic_Group_SPMF
  CryptHOL.Computational_Model
  Diffie_Hellman
  IND_CPA_PK_Single
  CryptHOL.Negligible
begin

subsection β€ΉElgamal encryption schemeβ€Ί

locale elgamal_base =
  fixes 𝒒 :: "'grp cyclic_group" (structure)
begin

type_synonym 'grp' pub_key = "'grp'"
type_synonym 'grp' priv_key = nat
type_synonym 'grp' plain = 'grp'
type_synonym 'grp' cipher = "'grp' Γ— 'grp'"

definition key_gen :: "('grp pub_key Γ— 'grp priv_key) spmf"
where 
  "key_gen = do {
     x ← sample_uniform (order 𝒒);
     return_spmf (g [^] x, x)
  }"

lemma key_gen_alt:
  "key_gen = map_spmf (Ξ»x. (g [^] x, x)) (sample_uniform (order 𝒒))"
by(simp add: map_spmf_conv_bind_spmf key_gen_def)

definition aencrypt :: "'grp pub_key β‡’ 'grp β‡’ 'grp cipher spmf"
where
  "aencrypt Ξ± msg = do {
    y ← sample_uniform (order 𝒒);
    return_spmf (g [^] y, (Ξ± [^] y) βŠ— msg)
  }"

lemma aencrypt_alt:
  "aencrypt Ξ± msg = map_spmf (Ξ»y. (g [^] y, (Ξ± [^] y) βŠ— msg)) (sample_uniform (order 𝒒))"
by(simp add: map_spmf_conv_bind_spmf aencrypt_def)

definition adecrypt :: "'grp priv_key β‡’ 'grp cipher β‡’ 'grp option"
where
  "adecrypt x = (Ξ»(Ξ², ΞΆ). Some (ΞΆ βŠ— (inv (Ξ² [^] x))))"

abbreviation valid_plains :: "'grp β‡’ 'grp β‡’ bool"
where "valid_plains msg1 msg2 ≑ msg1 ∈ carrier 𝒒 ∧ msg2 ∈ carrier 𝒒"

sublocale ind_cpa: ind_cpa key_gen aencrypt adecrypt valid_plains .
sublocale ddh: ddh 𝒒 .

fun elgamal_adversary :: "('grp pub_key, 'grp plain, 'grp cipher, 'state) ind_cpa.adversary β‡’ 'grp ddh.adversary"
where
  "elgamal_adversary (π’œ1, π’œ2) Ξ± Ξ² Ξ³ = TRY do {
    b ← coin_spmf;
    ((msg1, msg2), Οƒ) ← π’œ1 Ξ±;
    ― β€Ήhave to check that the attacker actually sends two elements from the group; otherwise flip a coinβ€Ί
    _ :: unit ← assert_spmf (valid_plains msg1 msg2);
    guess ← π’œ2 (Ξ², Ξ³ βŠ— (if b then msg1 else msg2)) Οƒ;
    return_spmf (guess = b)
  } ELSE coin_spmf"

end

locale elgamal = elgamal_base + cyclic_group 𝒒
begin 

theorem advantage_elgamal: "ind_cpa.advantage π’œ = ddh.advantage (elgamal_adversary π’œ)"
  including monad_normalisation
proof -
  obtain π’œ1 and π’œ2 where "π’œ = (π’œ1, π’œ2)" by(cases π’œ)
  note [simp] = this order_gt_0_iff_finite finite_carrier try_spmf_bind_out split_def o_def spmf_of_set bind_map_spmf weight_spmf_le_1 scale_bind_spmf bind_spmf_const
    and [cong] = bind_spmf_cong_simp
  have "ddh.ddh_1 (elgamal_adversary π’œ) = TRY do {
       x ← sample_uniform (order 𝒒);
       y ← sample_uniform (order 𝒒);
       ((msg1, msg2), Οƒ) ← π’œ1 (g [^] x);
       _ :: unit ← assert_spmf (valid_plains msg1 msg2);
       b ← coin_spmf;
       z ← map_spmf (Ξ»z. g [^] z βŠ— (if b then msg1 else msg2)) (sample_uniform (order 𝒒));
       guess ← π’œ2 (g [^] y, z) Οƒ;
       return_spmf (guess ⟷ b)
     } ELSE coin_spmf"
    by(simp add: ddh.ddh_1_def)
  also have "… = TRY do {
       x ← sample_uniform (order 𝒒);
       y ← sample_uniform (order 𝒒);
       ((msg1, msg2), Οƒ) ← π’œ1 (g [^] x);
       _ :: unit ← assert_spmf (valid_plains msg1 msg2);
       z ← map_spmf (Ξ»z. g [^] z) (sample_uniform (order 𝒒));
       guess ← π’œ2 (g [^] y, z) Οƒ;
       map_spmf ((=) guess) coin_spmf
     } ELSE coin_spmf"
    by(simp add: sample_uniform_one_time_pad map_spmf_conv_bind_spmf[where p=coin_spmf])
  also have "… = coin_spmf"
    by(simp add: map_eq_const_coin_spmf try_bind_spmf_lossless2')
  also have "ddh.ddh_0 (elgamal_adversary π’œ) = ind_cpa.ind_cpa π’œ"
    by(simp add: ddh.ddh_0_def IND_CPA_PK_Single.ind_cpa.ind_cpa_def key_gen_def aencrypt_def nat_pow_pow eq_commute)
  ultimately show ?thesis by(simp add: ddh.advantage_def ind_cpa.advantage_def)
qed

end
  
locale elgamal_asymp = 
  fixes 𝒒 :: "security β‡’ 'grp cyclic_group"
  assumes elgamal: "β‹€Ξ·. elgamal (𝒒 Ξ·)"
begin
  
sublocale elgamal "𝒒 Ξ·" for Ξ· by(simp add: elgamal)
    
theorem elgamal_secure:
  "negligible (λη. ind_cpa.advantage Ξ· (π’œ Ξ·))" if "negligible (λη. ddh.advantage Ξ· (elgamal_adversary Ξ· (π’œ Ξ·)))"
  by(simp add: advantage_elgamal that)

end

context elgamal_base begin

lemma lossless_key_gen [simp]: "lossless_spmf (key_gen) ⟷ 0 < order 𝒒"
by(simp add: key_gen_def Let_def)

lemma lossless_aencrypt [simp]:
  "lossless_spmf (aencrypt key plain) ⟷ 0 < order 𝒒"
by(simp add: aencrypt_def Let_def)

lemma lossless_elgamal_adversary:
  "⟦ ind_cpa.lossless π’œ; 0 < order 𝒒 ⟧
  ⟹ ddh.lossless (elgamal_adversary π’œ)"
by(cases π’œ)(simp add: ddh.lossless_def ind_cpa.lossless_def Let_def split_def)

end

end