Section ElGamal.
(* Assume a finite cyclic group.   This command brings several names into the environment, including 'order' which describes the order of the group. *)
Context`{FCG : FiniteCyclicGroup}.
(* FiniteCyclicGroup is from a generic group theory library.  To use any type in FCF, equality must be decidable for that type.  Assume that equality is decidable for group elements. This fact will be located automatically when needed. *)
Hypothesis GroupElement_EqDec : EqDec GroupElement.
(* The ElGamal key generation procedure.  The notation [ .. order) is from the RndNat library, and it produces a uniformly-random natural number in the specified interval.  *)
Definition ElGamalKeygen :=
m <-$ [0 .. order);
ret (m, g^m).
(* The ElGamal encryption procedure. *)
Definition ElGamalEncrypt(msg key : GroupElement) :=
m <-$ [0 .. order);
ret (g^m, key^m * msg).
(* The ElGamal decryption procedure. *)
Definition ElGamalDecrypt(key : nat)(ct : GroupElement * GroupElement) :=
[c1, c2] <-2 ct;
s <- c1^key;
(inverse s) * c2.
unfold ElGamalKeygen, ElGamalEncrypt, ElGamalDecrypt.
intuition.
(* simp_in_support is an automated tactic that breaks down hypotheses that state that some value is in the support of some distribution.  The resulting hypotheses will be more atomic and more directly useful.  *)
repeat simp_in_support.
rewrite <- associativity.
repeat rewrite groupExp_mult.
rewrite mult_comm.
rewrite left_inverse.
apply left_identity.
Qed.
(* Assume an adversary composed of two procedures that share state.  We also need to assume that equality is decidable for the type of this state. *)
Variable A_State : Set.
Hypothesis A_State_EqDec : EqDec A_State.
Variable A1 : GroupElement -> Comp (GroupElement * GroupElement * A_State).
Variable A2 : (GroupElement * GroupElement * A_State) -> Comp bool.
(* It is necessary to assume that the adversary procedures are "well-formed."  That is, they terminate with probability 1. *)
Hypothesis wfA1 : forall x, well_formed_comp (A1 x).
Hypothesis wfA2 : forall x, well_formed_comp (A2 x).
(* Build an adversary from A1 and A2 that can win DDH *)
Definition B(g_xyz : (GroupElement * GroupElement * GroupElement)) : Comp bool :=
[gx, gy, gz] <-3 g_xyz;
[p0, p1, s] <-$3 A1(gx);
b <-$ {0,1};
pb <- if b then p0 else p1;
c <- (gy, gz * pb);
b' <-$ (A2 (c, s));
ret (b ?= b').
(* Specialize the DDH games to our group.  Note that definitions can also used modules or classes to make these explicit specializations unnecessary. *)
Definition DDH0 := (@DDH0 _ _ _ _ _ g order).
Definition DDH1 := (@DDH1 _ _ _ _ _ g order).
Definition DDH_Advantage := (@DDH_Advantage _ _ _ _ _ g order).

(* This is what we want to (eventually) prove. *)
Theorem ElGamal_IND_CPA_Advantage :
(IND_CPA_Advantage ElGamalKeygen ElGamalEncrypt A1 A2) ==
(DDH_Advantage B).