Section PRF_Encryption_IND_CPA_concrete.
Variable State : Set.
Hypothesis State_EqDec : EqDec State.
Variable A1 : OracleComp Plaintext Ciphertext (Plaintext * Plaintext * State).
Variable A2 : State -> Ciphertext -> OracleComp Plaintext Ciphertext bool.
Hypothesis A1_wf : well_formed_oc A1.
Hypothesis A2_wf : forall x y, well_formed_oc (A2 x y).
Variable q1 q2 : nat.
Hypothesis A1_qam : queries_at_most A1 q1.
Hypothesis A2_qam : forall s c, queries_at_most (A2 s c) q2.
Definition PRFE_EncryptOracle := EncryptOracle PRFE_Encrypt _.
Local Open Scope nat_scope.
(* Step 1: Inline some definitions and simplify. *)
Definition G1 :=
key <-$ PRFE_KeyGen;
[b, _] <-$2
(
[p0, p1, s_A] <--$3 A1;
b <--$$ {0, 1};
pb <- if b then p1 else p0;
c <--$$ PRFE_Encrypt key pb;
b' <--$ (A2 s_A c);
$ ret (eqb b b')
)
_ _
(PRFE_EncryptOracle key) tt;
ret b.
reflexivity.
Qed.
(* Step 2 : Replace the PRF with a random function. *)
Definition PRFE_RandomFunc := @randomFunc (Bvector eta) (Bvector eta) ({0,1}^eta) _.
Definition RF_Encrypt s p :=
r <-$ {0, 1} ^ eta;
[pad, s] <-$2 PRFE_RandomFunc s r;
ret (r, p xor pad, s).
Definition G2 :=
[a, o] <-$2 A1 _ _ (RF_Encrypt) nil;
[p0, p1, s_A] <-3 a;
b <-$ {0, 1};
pb <- if b then p1 else p0;
[c, o] <-$2 RF_Encrypt o pb;
[b', o] <-$2 (A2 s_A c) _ _ RF_Encrypt o;
ret (eqb b b').
(* To prove this step, we put the game in the form of a computation that access the PRF as an oracle.  This allows us to apply the PRF definition. *)
Definition PRFE_Encrypt_OC (x : unit)(p : Plaintext) : OracleComp (Bvector eta) (Bvector eta) (Ciphertext * unit) :=
r <--$$ {0, 1} ^ eta;
pad <--$ OC_Query _ r;
$ (ret (r, p xor pad, tt)).
Definition PRF_A : OracleComp (Bvector eta) (Bvector eta) bool :=
[a, n] <--$2 OC_Run _ _ _ A1 PRFE_Encrypt_OC tt;
[p0, p1, s_A] <-3 a;
b <--$$ {0, 1};
pb <- if b then p1 else p0;
r <--$$ {0, 1} ^ eta;
pad <--$ OC_Query _ r;
c <- (r, pb xor pad);
z <--$ OC_Run  _ _ _ (A2 s_A c) PRFE_Encrypt_OC n;
[b', _] <-2 z;
$ ret (eqb b b').
(* Later we will need to reason about the cost of PRF_A, so we make an equivalent procedure without some of the complications. *)
Definition PRF_A' :=
z <--$
OC_Run _ _ _ A1 PRFE_Encrypt_OC tt;
b <--$$
(m <-$ { 0 , 1 }^1; ret Vector.hd m);
r <--$$ { 0 , 1 }^eta;
pad <--$ OC_Query (Vector.t bool eta) r;
z0 <--$
OC_Run _ _ _ (A2 (snd (fst z)) (r,
(if b then  (snd (fst (fst z)))  else (fst (fst (fst z))))
xor pad)) PRFE_Encrypt_OC (snd z);
$ ret eqb b (fst z0).
intuition.
unfold PRF_A, PRF_A'.
do 5 (comp_simp; simpl;
comp_skip; try eapply eqRat_refl).
comp_simp; simpl.
comp_simp.
intuition.
Qed.
Definition f_oracle(k : Bvector eta)(x : unit)(v : Bvector eta) :=
ret (f k v, tt).
apply (oneVector eta, oneVector eta).
Qed.
Hint Resolve Ciphertext_inh : inhabited.
intuition.
unfold PRFE_EncryptOracle, EncryptOracle, PRFE_Encrypt.
simpl.
prog_inline_l.
do 2 prog_inline_r.
comp_skip.
prog_simp.
unfold f_oracle.
do 2 (prog_inline_r;
prog_simp).
eapply comp_spec_ret.
simpl.
intuition.
Qed.

Theorem RF_EncryptOracle_spec : forall a x2,
comp_spec
(fun (y1 : Ciphertext * list (Bvector eta * Bvector eta))
(y2 : Ciphertext * (unit * list (Bvector eta * Bvector eta))) =>
fst y1 = fst y2 /\ snd y1 = snd (snd y2)) (RF_Encrypt (snd x2) a)
(p <-$
(PRFE_Encrypt_OC (fst x2) a) (list (Bvector eta * Bvector eta))
(list_EqDec (pair_EqDec (Bvector_EqDec eta) (Bvector_EqDec eta)))
PRFE_RandomFunc (snd x2); ret (fst (fst p), (snd (fst p), snd p))).