Section hF.
Variable b k c : nat.
Variable h : Bvector c -> Bvector b -> Bvector c.
Variable F : Bvector k -> list (Bvector b) -> Bvector b.
Definition hF key m :=
[k_Out, k_In] <-2 splitVector c k key;
h k_Out (F k_In m).
Variable A : OracleComp (list (Bvector b)) (Bvector c) bool.
Hypothesis A_wf : well_formed_oc A.
(* Step 0: inline and simplify *)
Definition G0 :=
k_in <-$ {0, 1}^k;
k_out <-$ {0, 1}^c;
[b, _] <-$2 A _ _ (fun (_ : unit) m => ret (h k_out (F k_in m), tt)) tt;
ret b.
(* Step 1: replace h with a random function *)
Definition G1 :=
k_in <-$ {0, 1}^k;
[b, _] <-$2 A _ _
(fun (s : list (Bvector b * Bvector c)) m =>
randomFunc ({0, 1}^c) _ s (F k_in m)) nil;
ret b.
(* Step 2: from cAU, identical random function inputs imply identical messages.  So this is equivalent to a random function with the domain of messages. *)
Definition G2 :=
[b, _] <-$2 A _ _
(fun (s : list (list (Bvector b) * Bvector c)) m =>
randomFunc ({0, 1}^c) _ s m) nil;
ret b.
Definition G0_1 :=
[k_out, k_in] <-$2 (
key <-$ {0, 1}^(c + k);
ret splitVector c k key);
[b, _] <-$2 A _ _ (fun (_ : unit) m => ret (h k_out (F k_in m), tt)) tt;
ret b.
unfold PRF_G_A, G0_1.
inline_first.
comp_skip.
unfold f_oracle, hF.
remember (splitVector c k x) as z.
comp_simp.
reflexivity.
Qed.
unfold G0_1, G0.
eapply eqRat_trans.
eapply evalDist_seq_eq.
eapply Rnd_split_equiv.
intros.
eapply eqRat_refl.
inline_first.
comp_swap_r.
comp_skip.
inline_first.
comp_skip.
Qed.
rewrite G0_1_equiv.
apply G0_1_G0_equiv.
Qed.
Definition hF_oracle k_in (s : unit) m : OracleComp (Bvector b) (Bvector c) (Bvector c * unit) :=
r <--$ OC_Query _ (F k_in m);
$ ret (r, tt).
(* Need to use OC_Run here because hF_oracle calls F before querying the oracle. *)
Definition PRF_h_A : OracleComp (Bvector b) (Bvector c) bool :=
k_in <--$$ {0, 1}^k;
[r, _] <--$2 OC_Run _ _ _ A (hF_oracle k_in) tt;
$ ret r.
Definition G1_1 :=
k_out <-$ { 0 , 1 }^c;
[b, _] <-$2 PRF_h_A _ _ (f_oracle h _ k_out) tt;
ret b.
Definition G1_2 :=
[b, _] <-$2 PRF_h_A _ _ (randomFunc ({0, 1}^c) _) nil;
ret b.
Local Opaque evalDist.
unfold G0, G1_1.
comp_swap_l.
comp_skip.
unfold PRF_h_A.
simpl.
inline_first.
comp_skip.
comp_simp.
inline_first.
unfold f_oracle.
eapply comp_spec_eq_impl_eq.
comp_skip.
eapply (@oc_comp_spec_eq _ _ _ _ _ _ _ _ _ _ _ _ _ _ (fun a b => True)).
trivial.
intuition.
inline_first.
comp_simp.
inline_first.
comp_simp.
simpl.
eapply comp_spec_ret; intuition.
simpl in *.
intuition; subst.
comp_simp.
simpl.
inline_first.
comp_simp.
eapply comp_spec_eq_refl.
Qed.
reflexivity.
Qed.
unfold G1_2, G1.
unfold PRF_h_A.
simpl.
inline_first.
comp_skip.
inline_first.
eapply comp_spec_eq_impl_eq.
comp_skip.
eapply (@oc_comp_spec_eq _ _ _ _ _ _ _ _ _ _ _ _ _ _ (fun a b => snd a = b)).
trivial.
intuition.
subst.
inline_first.
simpl.
eapply comp_spec_eq_trans_r.
Focus 2.
eapply comp_spec_right_ident.
comp_skip.
apply (oneVector c).
apply (oneVector c).
inline_first.
simpl.
eapply comp_spec_ret; intuition.
simpl in *.
intuition; subst.
inline_first.
comp_simp.
simpl.
eapply comp_spec_eq_refl.
Qed.
rewrite G0_G1_1_equiv.
rewrite <- G1_2_G1_equiv.
eapply G1_1_2_close.
Qed.
Definition randomFunc_mem (D R : Set)(eqdd : EqDec D)(eqdr : EqDec R)(RndR : Comp R) (ls : list (D * R))d :=
match (arrayLookup _ ls d) with
| Some r =>
ret (r, (d, r) :: ls)
| None =>
r <-$ RndR; ret (r, (d, r) :: ls)
end.
intuition.
unfold randomFunc, randomFunc_mem.
rewrite H.
case_eq (arrayLookup eqdd x2 a ); intuition.
eapply comp_spec_ret; intuition.
simpl.
case_eq (eqb x a); intuition.
rewrite eqb_leibniz in H1.
subst.
intuition.
comp_skip.
eapply comp_base_exists; intuition.
eapply comp_base_exists; intuition.
eapply comp_spec_ret; intuition.
simpl.
rewrite H.
trivial.
Qed.
Definition G2_1 :=
k_in <-$ {0, 1}^k;
[b, _] <-$2 A _ _ (fun s d => randomFunc_mem _ _ ({0, 1}^c) s (F k_in d)) nil;
ret b.
unfold G1, G2_1.
comp_skip.
eapply comp_spec_eq_impl_eq.
comp_skip.
eapply (@oc_comp_spec_eq _ _ _ _ _ _ _ _ _ _ _ _ _ _ (fun a b => forall x, arrayLookup _ a x = arrayLookup _ b x));
intuition.
eapply comp_spec_symm.
eapply comp_spec_consequence.
eapply randomFunc_mem_spec; intuition.
intuition.
comp_simp.
simpl in *.
intuition.
subst.
eapply comp_spec_eq_refl.
Qed.
Fixpoint arrayLookup_f (A B C : Set)(eqd : EqDec B)(ls : list ((A * B) * C))(a : B) :=
match ls with
| nil => None
| ((x, z), y) :: ls' =>
if (eqb a z) then (Some y) else (arrayLookup_f _ ls' a)
end.
Definition F_randomFunc k_in f d :=
match arrayLookup_f _ f (F k_in d) with
| Some r => ret (r, ((d, F k_in d), r) :: f)
| None => r <-$ {0, 1}^c; ret (r, ((d, F k_in d), r) :: f)
end.
Definition G2_2 :=
k_in <-$ {0, 1}^k;
[b, _] <-$2 A _ _ (F_randomFunc k_in) nil;
ret b.
unfold G2_1, G2_2.
comp_skip.
eapply comp_spec_eq_impl_eq.
comp_skip.
eapply (@oc_comp_spec_eq _ _ _ _ _ _ _ _ _ _ _ _ _ _ (fun a b => list_pred (fun c d =>  fst c = (snd (fst d)) /\ snd c = snd d) a b)).
econstructor.
intuition.
unfold randomFunc_mem, F_randomFunc.
induction x1; inversion 1; intuition; simpl in *.
subst.
destruct a.
destruct a2.
simpl in *.
subst.
destruct p.
simpl.
destruct (eqb a0 b0 ); intuition.
Qed.
erewrite arrayLookup_f_equiv ; eauto.
destruct (arrayLookup_f (Bvector_EqDec b) x2 (F x a) ); intuition.
eapply comp_spec_ret; intuition.
simpl.
econstructor.
simpl.
intuition.
trivial.
comp_skip.
apply (oneVector c).
apply (oneVector c).
eapply comp_spec_ret; intuition.
simpl.
econstructor.
intuition.
eauto.
comp_simp.
simpl in *; intuition.
subst.
eapply comp_spec_eq_refl.
Qed.
Fixpoint findCollision_1 (A B : Set)(eqda : EqDec A)(eqdb : EqDec B)(ls : list (A * B))(a : A)(b : B) :=
match ls with
| nil => None
| (a', b') :: ls' => if
(eqb b b' && (negb (eqb a a')))
then (Some a') else (findCollision_1 _ _ ls' a b)
end.
Definition collidesWith (A B : Set)(eqda : EqDec A)(eqdb : EqDec B)(ls : list (A * B))(a : A) b :=
if (findCollision_1 _ _ ls a b) then true else false.
Fixpoint findCollision(A B : Set)(eqda : EqDec A)(eqdb : EqDec B)(ls : list (A * B)) : option (A * A * B) :=
match ls with
| nil => None
| (a, b) :: ls' => let a' := (findCollision_1 _ _ ls' a b) in
match a' with
| None =>  (findCollision _ _ ls')
| Some p' => Some (a, p', b)
end
end.
Definition funcCollision (A B : Set)(eqda : EqDec A)(eqdb : EqDec B)(ls : list (A * B)) :=
if (findCollision _ _ ls) then true else false.
Definition G2_3 :=
k_in <-$ {0, 1}^k;
[b, f] <-$2 A _ _ (F_randomFunc k_in) nil;
ret (b, funcCollision _ _ (fst (split f))).
unfold G2_2, G2_3.
inline_first.
comp_skip.
inline_first.
comp_skip.
comp_simp.
simpl.
intuition.
Qed.
Definition G2_4 :=
k_in <-$ {0, 1}^k;
[b, f] <-$2 A _ _ (fun s a => b <- F k_in a; randomFunc_mem _ _ ({0, 1}^c) s (a, b)) nil;
ret (b, funcCollision _ _ (fst (split f))).
induction x1; intuition; simpl in *.
remember (split x1) as z.
destruct z.
simpl in *.
unfold collidesWith in *.
simpl in *.
case_eq (eqb b3 b1); intuition.
rewrite H1 in H0.
simpl in *.
case_eq (eqb a0 a); intuition.
rewrite H2 in H0.
simpl in *.
rewrite eqb_leibniz in H1.
rewrite eqb_leibniz in H2.
subst.
unfold eqbPair in *.
simpl in *.
repeat rewrite eqb_refl in H.
simpl in *.
trivial.
rewrite H2 in H0.
simpl in *.
discriminate.
rewrite H1 in H0.
simpl in *.
unfold eqbPair in *.
simpl in *.
rewrite H1 in H.
case_eq (eqb a0 a); intuition.
rewrite H2 in H.
simpl in *.
eapply IHx1; eauto.
rewrite H2 in H.
simpl in *.
eapply IHx1; eauto.
Qed.
unfold collidesWith in *.
induction ls; intuition; simpl in *.
intuition.
pairInv.
case_eq (eqb a' a); intuition.
rewrite  eqb_leibniz in H.
subst.
intuition.
rewrite eqb_refl.
simpl in *.
trivial.
case_eq (eqb b1 b0); intuition.
case_eq (eqb a' a0); intuition.
simpl.
eapply IHls; intuition.
eauto.
subst; intuition.
simpl.
eapply IHls; eauto.
Qed.
induction ls; intuition; simpl in *.
intuition.
pairInv.
unfold collidesWith, funcCollision in *.
simpl in *.
repeat rewrite eqb_refl.
simpl.
case_eq (findCollision_1 eqda eqdb ls a b1); intuition.
rewrite H0 in H.
discriminate.
unfold collidesWith, funcCollision in *.
simpl in *.
case_eq (eqb a0 a); intuition.
rewrite eqb_leibniz in H0.
subst.
rewrite eqb_refl.
simpl.
case_eq (findCollision_1 eqda eqdb ls a b0); intuition;
rewrite H0 in H.
discriminate.
case_eq (eqb b1 b0); intuition.
simpl.
eapply IHls; intuition.
simpl.
eapply IHls; intuition.
assert (a <> a0).
intuition.
subst.
rewrite eqb_refl in H0.
discriminate.
case_eq (eqb a a0); intuition.
rewrite eqb_leibniz in H3.
subst.
intuition.
simpl.
case_eq (eqb b1 b0); intuition.
simpl.
rewrite eqb_leibniz in H4.
subst.
specialize (in_impl_collidesWith  _ _ ls _ H1 H2); intuition.
unfold collidesWith in H4.
destruct (findCollision_1 eqda eqdb ls a0 b0);
discriminate.
simpl.
case_eq (findCollision_1 eqda eqdb ls a0 b0); intuition.
rewrite H5 in H.
discriminate.
rewrite H5 in H.
eapply IHls; intuition.
Qed.
induction arr; intuition; simpl in *.
discriminate.
remember (split arr) as z.
destruct z.
simpl.
case_eq (eqb a a0); intuition.
rewrite eqb_leibniz in H0.
intuition.
rewrite H0 in H.
right.
eapply IHarr; intuition.
eauto.
Qed.
induction x1; intuition; simpl in *.
remember (split x1) as z.
destruct z.
simpl in *.
unfold collidesWith in *.
simpl in *.
case_eq (eqb b2 b1); intuition.
rewrite H1 in H.
rewrite H1 in H0.
inversion H; clear H; subst.
simpl in *.
case_eq (eqb a0 a); intuition.
rewrite H in H0.
simpl in *.
rewrite eqb_leibniz in H1.
rewrite eqb_leibniz in H.
subst.
unfold eqbPair.
simpl.
repeat rewrite eqb_refl.
simpl.
trivial.
rewrite H in H0.
simpl in *.
discriminate.
rewrite H1 in H0.
rewrite H1 in H.
simpl in *.
unfold eqbPair.
simpl.
rewrite H1.
case_eq (eqb a0 a); intuition.
simpl.
eapply IHx1; eauto.
simpl.
eapply IHx1; eauto.
Qed.
unfold funcCollision.
intuition.
simpl in *.
destruct (findCollision_1 eqda eqdb ls a0 b0).
trivial.
trivial.
Qed.
intuition.
unfold F_randomFunc in *.
case_eq (arrayLookup_f (Bvector_EqDec b) c0 (F x d)); intuition.
rewrite H2 in H1.
repeat simp_in_support.
simpl in *.
remember (split c0) as z.
destruct z.
simpl in *.
eapply funcCollision_true_cons; eauto.
rewrite H2 in H1.
repeat simp_in_support.
simpl in *.
remember (split c0) as z.
destruct z.
simpl in *.
eapply funcCollision_true_cons; eauto.
Qed.
intuition.
unfold randomFunc_mem in *.
destruct (arrayLookup _ c0 (a, b0)).
repeat simp_in_support.
simpl in *.
remember (split c0) as z.
destruct z.
simpl in *.
eapply funcCollision_true_cons; eauto.
repeat simp_in_support.
simpl in *.
remember (split c0) as z.
destruct z.
simpl in *.
eapply funcCollision_true_cons; eauto.
Qed.
intuition.
unfold F_randomFunc.
destruct (arrayLookup_f _ y (F x z)); wftac.
Qed.
intuition.
unfold randomFunc_mem.
destruct (arrayLookup x w v); intuition; wftac.
Qed.
intuition.
eapply comp_spec_consequence.
eapply (@oc_comp_spec_eq_until_bad _ _ _ _ _ _ _ _ _ _ _ _ _
(fun z => funcCollision _ _ (fst (split z)))
(fun z => funcCollision _ _ (fst (split z)))
(fun a b => a = b)).
intuition.
eapply F_randomFunc_wf.
intuition.
eapply randomFunc_mem_wf.
wftac.
intuition; subst.
unfold F_randomFunc.
unfold randomFunc_mem.
case_eq ( arrayLookup
(pair_EqDec (list_EqDec (Bvector_EqDec b)) (Bvector_EqDec b)) x2
(a, F x a)); intuition.
case_eq (arrayLookup_f (Bvector_EqDec b) x2 (F x a)); intuition.
eapply comp_spec_ret; intuition.
simpl in *.
remember (split x2) as z.
destruct z.
simpl in *.
trivial.
simpl in *.
remember (split x2) as z.
destruct z.
simpl in *.
erewrite arrayLookup_noCollision_eq in H2; eauto.
inversion H2; clear H2; subst.
trivial.
erewrite arrayLookup_noCollision_eq in H2; eauto.
inversion H2; clear H2; subst.
trivial.
eapply funcCollision_false_impl_collidesWith_false.
unfold funcCollision in *.
simpl in *.
destruct (findCollision_1 (list_EqDec (Bvector_EqDec b))
(Bvector_EqDec b) l a (F x a)).
discriminate.
assert (l = fst (split x2)).
rewrite <- Heqz.
trivial.
subst.
eauto.
eapply arrayLookup_Some_In_split.
eauto.
assert (l = (fst (split x2))).
rewrite <- Heqz.
trivial.
subst.
unfold funcCollision in *.
simpl in *.
unfold collidesWith.
destruct (findCollision_1 (list_EqDec (Bvector_EqDec b))
(Bvector_EqDec b) (fst (split x2)) a (F x a)).
discriminate.
trivial.
simpl in *.
remember (split x2) as z.
destruct z.
simpl in *.
erewrite arrayLookup_noCollision_eq in H2; eauto.
inversion H2; clear H2; subst.
intuition.
unfold funcCollision, collidesWith in *.
simpl in *.
assert (l = fst (split x2)).
rewrite <- Heqz.
trivial.
subst.
destruct (findCollision_1 (list_EqDec (Bvector_EqDec b))
(Bvector_EqDec b) (fst (split x2)) a (F x a)).
discriminate.
trivial.
comp_irr_l.
eapply comp_spec_ret; intuition.
simpl in *.
remember (split x2) as z.
destruct z.
simpl.
trivial.
simpl in *.
remember (split x2) as z.
destruct z.
simpl in *.
erewrite arrayLookup_noCollision_eq in H2; eauto.
discriminate.
assert (l = fst (split x2)).
rewrite <- Heqz.
trivial.
subst.
eapply funcCollision_false_impl_collidesWith_false.
unfold funcCollision in *.
simpl in *.
destruct ( findCollision_1 (list_EqDec (Bvector_EqDec b))
(Bvector_EqDec b) (fst (split x2)) a (F x a)).
discriminate.
eauto.
eapply arrayLookup_Some_In_split.
eauto.
simpl in *.
remember (split x2) as z.
destruct z.
simpl in *.
erewrite arrayLookup_noCollision_eq in H2; eauto.
discriminate.
assert (l = fst (split x2)).
rewrite <- Heqz.
trivial.
subst.
eapply funcCollision_false_impl_collidesWith_false.
unfold funcCollision in *.
simpl in *.
destruct ( findCollision_1 (list_EqDec (Bvector_EqDec b))
(Bvector_EqDec b) (fst (split x2)) a (F x a)).
discriminate.
eauto.
eapply arrayLookup_Some_In_split.
eauto.
case_eq (arrayLookup_f (Bvector_EqDec b) x2 (F x a)); intuition.
comp_irr_r.
eapply comp_spec_ret; intuition.
simpl.
remember (split x2) as z.
destruct z.
simpl.
trivial.
simpl in *.
remember (split x2) as z.
destruct z.
simpl in *.
erewrite arrayLookup_noCollision_eq_f in H; eauto.
discriminate.
unfold funcCollision, collidesWith in *.
simpl in *.
assert (l = (fst (split x2))).
rewrite <- Heqz.
trivial.
subst.
destruct (findCollision_1 (list_EqDec (Bvector_EqDec b))
(Bvector_EqDec b) (fst (split x2)) a (F x a)).
discriminate.
trivial.
simpl in *.
remember (split x2) as z.
destruct z.
simpl in *.
erewrite arrayLookup_noCollision_eq_f in H; eauto.
discriminate.
unfold funcCollision, collidesWith in *.
simpl in *.
assert (l = (fst (split x2))).
rewrite <- Heqz.
trivial.
subst.
destruct (findCollision_1 (list_EqDec (Bvector_EqDec b))
(Bvector_EqDec b) (fst (split x2)) a (F x a)).
discriminate.
trivial.
comp_skip.
apply (oneVector c).
apply (oneVector c).
eapply comp_spec_ret; intuition.
intuition.
eapply F_randomFunc_preserves_coll;
eauto.
intuition.
eapply randomFunc_mem_preserves_coll; eauto.
trivial.
trivial.
intuition.
simpl in *; subst.
destruct b1; trivial.
Grab Existential Variables.
trivial.
Qed.
unfold G2_3, G2_4.
inline_first.
comp_skip.
inline_first.
eapply comp_spec_eq_impl_eq.
comp_skip.
eapply A_randomFunc_F_spec.
simpl in *.
comp_simp.
simpl.
eapply comp_spec_ret; intuition.
Qed.
intuition.
unfold G2_3, G2_4.
inline_first.
comp_skip.
inline_first.
eapply comp_spec_impl_eq.
comp_skip.
eapply A_randomFunc_F_spec.
simpl in *.
comp_simp.
eapply comp_spec_ret; intuition.
pairInv.
intuition.
pairInv.
trivial.
pairInv.
simpl in *.
rewrite H3 in H4.
intuition.
pairInv.
trivial.
Qed.
Definition F_randomFunc_O f d :
OracleComp
(list (Bvector b))
(Bvector b)
(Bvector c * list ((list (Bvector b) * Bvector b) * Bvector c))  :=
fd <--$ OC_Query _ d;
match arrayLookup_f _ f fd with
| Some r => $ ret (r, ((d, fd), r) :: f)
| None => r <--$$ {0, 1}^c; $ ret (r, ((d, fd), r) :: f)
end.
Definition au_F_A : OracleComp (list (Bvector b)) (Bvector b) _ :=
[_, p ] <--$2 OC_Run _ _ _ A F_randomFunc_O nil;
let coll :=
match (findCollision _ _ (fst (split p)) ) with
| None => (nil, nil)
| Some p => fst p
end in
$ ret (coll).
Definition G2_3_bad :=
k_in <-$ {0, 1}^k;
[p, _] <-$2 au_F_A _ _ (WCR_Oracle _ F k_in) tt;
[d1, d2] <-2 p;
ret (negb (eqb d1 d2) && eqb (F k_in d1) (F k_in d2)).
reflexivity.
Qed.

Theorem G2_3_bad_equiv :
Pr[x <-$ G2_3; ret (snd x)] == Pr[G2_3_bad].