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.
intuition.
unfold RF_Encrypt, PRFE_RandomFunc, randomFunc.
simpl.
do 2 prog_inline_r.
comp_skip.
prog_simp.
prog_inline_r.
comp_skip.
prog_inline_r.
prog_simp.
eapply comp_spec_ret.
simpl.
intuition.
Qed.
Opaque PRFE_Encrypt_OC.
unfold G1, PRF_A, PRFE_KeyGen.
comp_skip.
simpl.
inline_first.
eapply comp_spec_impl_eq.
eapply (@comp_spec_seq _ _
(fun a1 a2 => fst a1 = fst a2 /\ snd a1 = fst (snd a2))); eauto with inhabited.
eapply (@oc_comp_spec_eq _ _ _ _ _ _ _ _ _ _ _ _ _ _ (fun a b => a = fst b)).
trivial.
intuition.
subst.
eapply PRFE_EncryptOracle_spec.
intuition.
destruct b2.
simpl in *.
subst.
prog_simp.
simpl.
do 2 prog_inline_l.
do 2 prog_inline_r.
comp_skip.
prog_simp.
unfold PRFE_Encrypt.
do 3 prog_inline_l.
do 2 (prog_inline_r; prog_simp).
comp_skip.
prog_simp.
unfold f_oracle.
prog_inline_l.
do 3 (prog_inline_r; prog_simp).
eapply comp_spec_seq; eauto with inhabited.
eapply (@oc_comp_spec_eq _ _ _ _ _ _ _ _ _ _ _ _ _ _
(fun a b => a = fst b)).
trivial.
intuition.
subst.
eapply PRFE_EncryptOracle_spec.
intuition.
destruct b4; simpl in *; subst.
prog_simp.
simpl.
prog_inline_l; prog_simp.
prog_inline_r; prog_simp.
eapply comp_spec_ret.
intuition.
Qed.
intuition.
comp_skip.
comp_skip.
eapply PRF_A'_equiv.
Qed.
pose proof RF_EncryptOracle_spec as RFE_spec.
unfold G2, PRF_A.
simpl.
inline_first.
eapply comp_spec_impl_eq.
eapply (@comp_spec_seq _ _
(fun a1 a2 => fst a1 = fst a2 /\ snd a1 = snd (snd a2))); eauto with inhabited.
eapply comp_spec_consequence.
eapply (@oc_comp_spec_eq _ _ _ _ _ _ _ _ _ _ _ _ _ _ (fun a b => a = snd b)).
trivial.
intuition.
subst.
eapply RFE_spec.
intuition.
intuition.
prog_simp.
simpl.
do 2 prog_inline_r.
comp_skip.
unfold RF_Encrypt at 1.
prog_simp.
prog_inline_l.
do 2 prog_inline_r.
comp_skip.
prog_simp.
simpl in *.
subst.
prog_inline_l.
prog_inline_r.
comp_skip.
inversion H3; clear H3; subst.
do 2 prog_inline_r.
eapply comp_spec_seq; eauto with inhabited.
eapply (@oc_comp_spec_eq _ _ _ _ _ _ _ _ _ _ _ _ _ _ (fun x y => x = snd y)).
trivial.
intuition.
subst.
eapply RFE_spec.
intuition.
prog_simp.
simpl.
prog_inline_r.
simpl.
prog_simp.
eapply comp_spec_ret.
simpl in *.
intuition.
subst.
intuition.
subst; intuition.
Qed.
intuition.
comp_skip.
eapply PRF_A'_equiv.
Qed.
rewrite G1_PRF_A_equiv.
rewrite G1_PRF_A'_equiv.
rewrite G2_PRF_A_equiv.
rewrite G2_PRF_A'_equiv.
reflexivity.
Qed.
(* Step 3 : replace the PRF output with random value *)
(* This is identical to G2 as long as the adversary does not encounter the encryption nonce during a query. *)
Definition G3 :=
[a, o] <-$2 A1 _ _ (RF_Encrypt) nil;
[p0, p1, s_A] <-3 a;
b <-$ {0, 1};
pb <- if b then p1 else p0;
r <-$ {0, 1}^eta;
pad <-$ {0, 1}^eta;
c <- (r, pb xor pad);
[b', o] <-$2 (A2 s_A c) _ _ RF_Encrypt o;
ret (eqb b b').
(* start with G2 and move the challenge encryption sampling to the front. *)
Definition G2_1 :=
r <-$ {0, 1}^eta;
[a, o] <-$2 A1 _ _ (RF_Encrypt) nil;
[p0, p1, s_A] <-3 a;
b <-$ {0, 1};
pb <- if b then p1 else p0;
[pad, o] <-$2 PRFE_RandomFunc o r;
c <- (r, pb xor pad);
[b', o] <-$2 (A2 s_A c) _ _ RF_Encrypt o;
ret (eqb b b').
unfold G2, G2_1.
do 2 (comp_swap_r; comp_skip; comp_simp).
unfold RF_Encrypt.
do 2(inline_first; comp_skip; comp_simp).
intuition.
Qed.
(* We will modify the procedure one adversary call at a time.  Start with the first one. *)
(* make the bad event visible. *)
Definition G2_2 :=
r <-$ {0, 1}^eta;
[a, o] <-$2 A1 _ _ (RF_Encrypt) nil;
bad <- if (arrayLookup _ o r) then true else false;
[p0, p1, s_A] <-3 a;
b <-$ {0, 1};
pb <- if b then p1 else p0;
[pad, o] <-$2 PRFE_RandomFunc o r;
c <- (r, pb xor pad);
[b', o] <-$2 (A2 s_A c) _ _ RF_Encrypt o;
ret (eqb b b', bad).
unfold G2_1, G2_2.
do 5 (inline_first; comp_skip; comp_simp).
simpl; intuition.
Qed.
(* When r is not in the domain of the random function, we can sample the pad randomly, then add it to the function. *)
Definition G2_3 :=
r <-$ {0, 1}^eta;
[a, o] <-$2 A1 _ _ (RF_Encrypt) nil;
bad <- if (arrayLookup _ o r) then true else false;
[p0, p1, s_A] <-3 a;
b <-$ {0, 1};
pb <- if b then p1 else p0;
pad <-$ {0, 1}^ eta;
c <- (r, pb xor pad);
[b', o] <-$2 (A2 s_A c) _ _ RF_Encrypt ((r, pad) :: o);
ret (eqb b b', bad).
Local Open Scope rat_scope.
intuition.
unfold RF_Encrypt.
wftac.
eapply randomFunc_wf.
wftac.
Qed.
Hint Resolve RF_Encrypt_wf : wftac.
pose proof RF_Encrypt_wf.
intuition.
unfold G2_2, G2_3.
do 3 (comp_skip; comp_simp).
unfold PRFE_RandomFunc, randomFunc.
case_eq (arrayLookup (Bvector_EqDec eta) l x ); intuition.
comp_simp.
comp_irr_r; wftac.
comp_irr_l; wftac.
eapply oc_comp_wf; intuition.
comp_irr_r; wftac.
eapply oc_comp_wf; intuition.
comp_simp.
Transparent evalDist.
Transparent getSupport.
dist_compute.
inline_first.
comp_skip.
Qed.
pose proof RF_Encrypt_wf.
unfold G2_2, G2_3.
do 3 (inline_first; comp_skip; comp_simp).
comp_inline_l.
comp_irr_l; wftac.
eapply randomFunc_wf; wftac.
comp_inline_r.
comp_irr_r; wftac.
comp_simp.
comp_inline_l.
comp_irr_l; wftac.
eapply oc_comp_wf; intuition.
comp_inline_r.
comp_irr_r; wftac.
eapply oc_comp_wf; intuition.
comp_simp.
simpl.
intuition.
Qed.
(* provide a simplified form of the same with the same probability of badness. *)
Definition G2_2_bad :=
[a, o] <-$2 A1 _ _ (RF_Encrypt) nil;
ls <- fst (split o);
r <-$ {0, 1}^eta;
ret if (in_dec (EqDec_dec _) r ls) then true else false.
induction ls; intuition; simpl in *.
discriminate.
case_eq (eqb a a0); intuition.
rewrite H0 in H.
inversion H; clear H; subst.
rewrite eqb_leibniz in H0.
subst.
remember (split ls) as x.
destruct x.
simpl.
intuition.
rewrite H0 in H.
remember (split ls) as x.
destruct x.
simpl.
right.
eapply IHls.
eauto.
Qed.
induction ls; intuition; simpl in *.
case_eq (eqb a a0); intuition;
rewrite H1 in H.
discriminate.
remember (split ls) as x.
destruct x.
simpl in *.
intuition; subst.
rewrite eqb_refl in H1.
discriminate.
eauto.
Qed.
pose proof RF_Encrypt_wf.
rewrite G2_2_3_badness_same.
unfold G2_3, G2_2_bad.
inline_first.
comp_at comp_inline leftc 1%nat.
comp_swap_l.
comp_skip.
comp_simp.
comp_skip.
do 3 (comp_inline_l;
comp_irr_l; wftac).
eapply oc_comp_wf; intuition.
comp_simp.
eapply evalDist_ret_eq; simpl.
case_eq (arrayLookup (Bvector_EqDec eta) l x); intuition;
destruct (in_dec (EqDec_dec (Bvector_EqDec eta)) x (fst (split l))); intuition.
exfalso.
eauto using arrayLookup_In_split.
exfalso.
eapply (@arrayLookup_not_In_split _ _ (Bvector_EqDec eta)).
eauto.
trivial.
Qed.
intuition.
unfold RF_Encrypt in *.
repeat simp_in_support.
destruct x0.
repeat simp_in_support.
unfold PRFE_RandomFunc, randomFunc in *.
destruct (arrayLookup (Bvector_EqDec eta) c x); repeat simp_in_support;
simpl in *; omega.
Qed.
pose proof RF_Encrypt_wf.
rewrite G2_2_bad_equiv.
unfold G2_2_bad.
comp_irr_l.
eapply oc_comp_wf; intuition.
comp_simp.
assert (length l <= q1)%nat.
eapply qam_count;
eauto.
eapply RF_Encrypt_length_incr.
simpl; intuition.
eapply RndInList_prob.
rewrite split_length_l.
trivial.
Qed.
intuition.
eapply leRat_trans.
eapply fundamental_lemma_h.
eapply G2_2_3_badness_same.
eapply G2_2_3_eq_until_bad.
eapply G2_2_bad_small.
Qed.
(* Now switch the the second adversary call *)
(* for this part we need an oracle that makes the bad event visible *)
Definition RF_Encrypt_bad (x : Bvector eta)(z : list (Bvector eta * Bvector eta) * bool) (p : Bvector eta):=
[s, bad] <-2 z;
r <-$ { 0 , 1 }^eta;
z <-$ PRFE_RandomFunc s r;
[pad, s0]<-2 z;
ret (r, p xor pad, (s0, bad || eqb x r)).
Definition G2_4 :=
r <-$ {0, 1}^eta;
[a, o] <-$2 A1 _ _ (RF_Encrypt) nil;
[p0, p1, s_A] <-3 a;
b <-$ {0, 1};
pb <- if b then p1 else p0;
pad <-$ {0, 1}^ eta;
c <- (r, pb xor pad);
[b', o] <-$2 (A2 s_A c) _ _ (RF_Encrypt_bad r) ((r, pad) :: o, false);
ret (eqb b b', (snd o)).
unfold G2_3, G2_4.
do 4 (inline_first; comp_skip; comp_simp).
inline_first.
eapply comp_spec_impl_eq.
eapply comp_spec_seq; eauto with inhabited.
eapply (@oc_comp_spec_eq _ _ _ _ _ _ _ _ _ _ _ _ _ _ (fun a b => a = fst b)); intuition.
destruct x3; simpl in *; subst.
unfold RF_Encrypt.
clear H2.
comp_skip.
comp_skip.
eapply comp_spec_ret; simpl in *; intuition.
intuition.
simpl in *; subst.
prog_simp.
eapply comp_spec_ret; simpl in *; intuition.
Qed.
(* When this procedure doesn't go bad, we can remove the challenge pad from the function. *)
Definition G2_5 :=
r <-$ {0, 1}^eta;
[a, o] <-$2 A1 _ _ (RF_Encrypt) nil;
[p0, p1, s_A] <-3 a;
b <-$ {0, 1};
pb <- if b then p1 else p0;
pad <-$ {0, 1}^ eta;
c <- (r, pb xor pad);
[b', o] <-$2 (A2 s_A c) _ _ (RF_Encrypt_bad r) (o, false);
ret (eqb b b', snd o).
intuition.
unfold RF_Encrypt_bad in *.
repeat simp_in_support.
destruct x0.
repeat simp_in_support.
eapply orb_true_l.
Qed.
intuition.
unfold RF_Encrypt_bad; wftac.
eapply randomFunc_wf; wftac.
Qed.

Theorem G2_4_5_eq_until_bad :
forall z,
evalDist G2_4 (z, false) == evalDist G2_5 (z, false).