Section PRG.
(* note: the domain of the f is now Blist, not an abstract D the key type is now also Bvector eta, since HMAC specifies that the key has the same size as the output (simplified) *)
Variable eta : nat.
Variable (*Hypothesis*) eta_nonzero : eta <> 0%nat.
Definition RndK : Comp (Bvector eta) := {0,1}^eta.
Definition RndV : Comp (Bvector eta) := {0,1}^eta.
Ltac kv_exist := try apply (oneVector eta, oneVector eta).
Variable f : Bvector eta -> Blist -> Bvector eta.
Definition KV : Set := (Bvector eta * Bvector eta)%type.
Variable eqDecState : EqDec KV.
(* Variable eqdbv : EqDec (Bvector eta). *)
Definition eqdbv := Bvector_EqDec eta.
(* Variable eqdbl : EqDec Blist. *)
Definition eqdbl := list_EqDec bool_EqDec.
(* Opaque eqdbl. *)
(* injection is to_list. TODO prove this *)
Definition injD : Bvector eta -> Blist := Vector.to_list.
(* Generate + Update *)
(* This has oracle type: state: k, v input: n output: list (Bvector eta) state: k, v *)
(* Spec says "V || 0x00"; here we will use a list of 8 bits of 0 (a byte) *)
Fixpoint replicate {A} (n : nat) (a : A) : list A :=
match n with
| O => nil
| S n' => a :: replicate n' a
end.
Definition zeroes : list bool := replicate 8 false.
(* oracle 1 *)
(* do not use; here as reference implementation so we can prove [Generate, Generate, ...] = [Generate_noV, Generate_v, Genupdate, ...] *)
Definition Generate (state : KV) (n : nat) :
Comp (list (Bvector eta) * KV) :=
[k, v] <-2 state;
[bits, v'] <-2 Gen_loop k v n;
k' <- f k (to_list v' ++ zeroes);
v'' <- f k' (to_list v');
ret (bits, (k', v'')).
(* want to change to this, and prove the outputs are the same. the other Generate_vs don't use this version *)
Definition Generate_v (state : KV) (n : nat) :
Comp (list (Bvector eta) * KV) :=
[k, v] <-2 state;
v' <- f k (to_list v);
[bits, v''] <-2 Gen_loop k v' n;
k' <- f k (to_list v'' ++ zeroes);
ret (bits, (k', v'')).
(* use this for the first call *)
Definition Generate_noV (state : KV) (n : nat) :
Comp (list (Bvector eta) * KV) :=
[k, v] <-2 state;
[bits, v'] <-2 Gen_loop k v n;
k' <- f k (to_list v' ++ zeroes);
ret (bits, (k', v')).
(* --- End my HMAC-DRBG spec --- *)
(* well, this doesn't include the "sequence of executions" business, which happens in the games *)
(* oracle 2: all PRFs replaced with random bits *)
(* TODO: intermediate oracles, each with random functions *)
(* intermediates have unnecessary state and updating of the state to match earlier ones *)
Fixpoint Gen_loop_rb_intermediate (k : Bvector eta) (v : Bvector eta) (n : nat)
: Comp (list (Bvector eta) * Bvector eta) :=
match n with
| O => ret (nil, v)
| S n' =>
v' <-$ {0,1}^eta;
[bits, v''] <-$2 Gen_loop_rb_intermediate k v' n';
ret (v' :: bits, v'')
end.
(* final versions (without unnecessary (k, v) updating) *)
Fixpoint Gen_loop_rb (n : nat) : Comp (list (Bvector eta)) :=
match n with
| O => ret nil
| S n' =>
v' <-$ {0,1}^eta;
bits <-$ Gen_loop_rb n';
ret (v' :: bits)
end.
(* passes the state around to match the types in Oi_oc' *)
(* Old version: did not update v *)
(* Definition Generate_rb_intermediate (state : KV) (n : nat) *)
(*   : Comp (list (Bvector eta) * KV) := *)
(*   bits <-$ Gen_loop_rb n; *)
(*   ret (bits, state). *)
(* New version: updates state vector v to be the last element of bits. Doesn't matter b/c all bits sampled randomly unless n=0 *)
(* New new version: exactly like Generate_v but with f replaced by uniform random sampling *)
(* needs this for the proof of Gi_prog_equiv_rb_oracle *)
Definition Generate_rb_intermediate (state : KV) (n : nat)
: Comp (list (Bvector eta) * KV) :=
[k, v] <-2 state;
v' <-$ {0,1}^eta;
[bits, v''] <-$2 Gen_loop_rb_intermediate k v' n;
ret (bits, (k, v'')).
Definition Generate_rb_oracle (tt : unit) (n : nat) : Comp (list (Bvector eta) * unit) :=
bits <-$ Gen_loop_rb n;
ret (bits, tt).
Definition Generate_rb (n : nat) : Comp (list (Bvector eta)) :=
bits <-$ Gen_loop_rb n;
ret bits.
(* TODO: prove well_formed for the oracles *)
(* Non-adaptive adversary. Consequently, does not use OracleComp, because it won't be adjusting its input to the Generate_v oracle (number of blocks requested from Gen_loop) based on the Generate_v oracle's output, because the number of blocks and queries is fixed. *)
Variable A : list (list (Bvector eta)) -> Comp bool.
Hypothesis A_wf : forall ls, well_formed_comp (A ls).
Variable blocksPerCall : nat.       (* blocks generated by GenLoop *)
Variable numCalls : nat.        (* number of calls to Generate_v *)
Variable (*Hypothesis*) H_numCalls : numCalls > 0. (* need this for Generate_v equivalence? *)
Variable (*Hypothesis*) H_blocksPerCall : blocksPerCall > 0.
(* need this hypothesis in oracle-to-RB (oracleMap_oracleCompMap_equiv_modified_calls_gt_i) and collision bound (Gi_rb_collisions_inner_eq_general_i_eq0) *)
(* TODO do casework on whether blocksPerCall = 0 *)
Definition requestList : list nat := replicate numCalls blocksPerCall.
(* Change to an abstract, nonempty list. *)
(* TODO: change name *)
(* Parameter firstCall : nat. *)
(* Parameter blocksForCalls_2 : list nat. *)
(* Definition requestList : list nat := firstCall :: blocksForCalls_2. *)
(* used with oracleMap: call the oracle numCalls times, each time requesting blocksPerCall blocks *)
(* only first call uses Generate_noV; assumes numCalls > 0 *)
Definition G1_prg : Comp bool :=
[k, v] <-$2 Instantiate;
[head_bits, state'] <-$2 Generate_noV (k, v) blocksPerCall;
[tail_bits, _] <-$2 oracleMap _ _ Generate_v state' (tail requestList);
A (head_bits :: tail_bits).
(* TODO: backtracking resistance? for nonadaptive adversary *)
(* Definition G1_prg_br : Comp bool := blocksForEachCall <- A1; (* implicitly compromises after that # calls *)
[k, v] <-$2 Instantiate;
[head_bits, state'] <-$2 Generate_noV (k, v) (head blocksForEachCall);
[tail_bits, state''] <-$2 oracleMap _ _ Generate_v state' (tail blocksForEachCall);
[k', v'] <-$ UpdateV state'';
A2 (head_bits :: tail_bits, (k', v')). *)
(* rand bits *)
(* Definition G_ideal_br : Comp bool := blocksForEachCall <- A1; [bits, state'] <-$2 oracleMap _ _ Generate_rb (k, v) blocksPerCall; k <- {0,1}^eta; v <- {0,1}^eta; A2 (head_bits :: tail_bits, (k, v)). *)
(* --------------------- *)
(* Prove v-update move equivalence *)
(* calling (Generate, Generate, ...) should have the same output as calling (Generate_noV, Generate_v, Generate_v, ...) which moves the v-update to the beginning of the next oracle call *)
(* proof outline: G_real = G_real_split ~ G1_prg *)
Definition G_real : Comp bool :=
[k, v] <-$2 Instantiate;
[bits, _] <-$2 oracleMap _ _ Generate (k, v) requestList;
A bits.
(* make the form closer to G1_prg by splitting off first call only *)
Definition G_real_split : Comp bool :=
[k, v] <-$2 Instantiate;
[head_bits, state'] <-$2 Generate (k, v) blocksPerCall;
[tail_bits, _] <-$2 oracleMap _ _ Generate state' (tail requestList);
A (head_bits :: tail_bits).
(* use version that's better for induction *)
(* End proofs of v-update equivalence *)
(* ------------------------------------------------ *)
(* TODO: intermediate games with random functions and random bits *)
(* proving Pr[G_ideal'] == Pr[G_ideal''] could be hard (new intermediate game) *)
Definition G_ideal'' : Comp bool :=
kv <-$ Instantiate;           (* OK to instantiate them? *)
[bits, _] <-$2 oracleMap _ _ Generate_rb_intermediate kv requestList;
A bits.
(* uses simplified RB versions *)
Definition G_ideal' : Comp bool :=
[bits, _] <-$2 oracleMap _ _ Generate_rb_oracle tt requestList;
A bits.
(* simpler version of Generate_v only requires compMap. prove the two games equivalent *)
Definition G_ideal : Comp bool :=
bits <-$ compMap _ Generate_rb requestList;
A bits.
(* oracle i *)
(* number of calls: first call is 0, last call is (numCalls - 1) for numCalls calls total G0: PRF PRF PRF G1: RB  PRF PRF G2: RB  RB  PRF G3: RB  RB  RB there should be (S numCalls) games, so games are numbered from 0 through numCalls *)
Definition choose_Generate (i : nat) (sn : nat * KV) (n : nat)
: Comp (list (Bvector eta) * (nat * KV)) :=
[callsSoFar, state] <-2 sn;
let Generate_v_choose := if lt_dec callsSoFar i (* callsSoFar < i (override all else) *)
then Generate_rb_intermediate
(* first call does not update v, to make proving equiv. easier*)
else if beq_nat callsSoFar O then Generate_noV
else Generate_v in
(* note: have to use intermediate, not final Generate_rb here *)
[bits, state'] <-$2 Generate_v_choose state n;
ret (bits, (S callsSoFar, state')).
(* game i (Gi 0 = G1 and Gi q = G2) *)
Definition Gi_prg (i : nat) : Comp bool :=
[k, v] <-$2 Instantiate;
[bits, _] <-$2 oracleMap _ _ (choose_Generate i) (O, (k, v)) requestList;
A bits.
(* ------------------------------- *)
(* G1 is equal to first hybrid *)
(* move the non-nil init state inside compFold's init acc. otherwise, same compFold as in oracleMap *)
Definition G1_prg_fold : Comp bool :=
[k, v] <-$2 Instantiate;
[head_bits, state'] <-$2 Generate_noV (k, v) blocksPerCall;
(* unfolding the oracleMap *)
(* [tail_bits, _] <-$2 oracleMap _ _ Generate_v state' (tail requestList); *)
[bits, _] <-$2 compFold _
(fun acc d => [rs, s] <-2 acc;
[r, s] <-$2 Generate_v s d;
ret (rs ++ r :: nil, s))
(head_bits :: nil, state') (tail requestList);
A bits.
(* ----- G2 is equal to last hybrid. Helper lemmas *)
Open Scope nat.
Open Scope nat.
Close Scope nat.
(* ---------------------------------- *)
(* For PRF adversary:  Gen_loop_oc: takes an oracle in place of (f k)  Generate_v_oc: takes an oracle in place of (f k)  choose_Generate_rf: if n > i then query Generate_rb, OC version else if n = i then query Gen_loop_oc with the given oracle (RF) else query Generate_v, OC version (using PRF)  PRF_Adversary: gives the Oi oracle the (f k) oracle it's given by Gi, and queries the resulting oracle `maxCalls` times, querying `numBlocks` each time. passes the result to the existing (non-adaptive) Generate_v adversary  Gi_rf: gives the PRF_Adversary the random function oracle and returns what PRF_Adversary returns  PRF_Advantage: defined in terms of PRF_Adversary, indexed by i (but PRF_Advantage should be the same for all i) *)
(* Versions of Gen_loop and Generate_v with that query the oracle in place of (f k) *)
(* this is slightly different from Adam's version:  Fixpoint PRF_DRBG_f_G2 (v : D)(n : nat) : OracleComp D (Bvector eta) (list (Bvector eta)) := match n with | O => $ ret nil | S n' => r <--$ (OC_Query _ v); ls' <--$ (PRF_DRBG_f_G2 (injD r) n'); $ ret (r :: ls') end. *)
Fixpoint Gen_loop_oc (v : Bvector eta) (n : nat)
: OracleComp (list bool) (Bvector eta) (list (Bvector eta) * Bvector eta) :=
match n with
| O => $ ret (nil, v)
| S n' =>
v' <--$ (OC_Query _ (to_list v)); (* ORACLE USE *)
[bits, v''] <--$2 Gen_loop_oc v' n';
$ ret (v' :: bits, v'')
end.
(* TODO trying to figure out dependencies for PRF_DRBG. can i instantiate key D etc.? *)
(*Check dupProb_const. Check PRF_DRBG_G3_bad_4_small. Print PRF_DRBG_G3_bad_4.*)
(* takes in key but doesn't use it, to match the type of other Generate_vs *)
Definition Generate_v_oc (state : KV) (n : nat) :
OracleComp (list bool) (Bvector eta) (list (Bvector eta) * KV) :=
[k, v_0] <-2 state;
v <--$ (OC_Query _ (to_list v_0)); (* ORACLE USE *)
[bits, v'] <--$2 Gen_loop_oc v n;
(* TODO what's the state type here? and the global Generate_v_oc return type? *)
k' <--$ (OC_Query _ (to_list v' ++ zeroes)); (* ORACLE USE *)
$ ret (bits, (k', v')).
(* should use the oracle and ignore the passed-in k *)
Definition Generate_noV_oc (state : KV) (n : nat) :
OracleComp (list bool) (Bvector eta)  (list (Bvector eta) * KV) :=
[k, v] <-2 state;
[bits, v'] <--$2 Gen_loop_oc v n;
(* TODO what's the state type here? and the global Generate_v_oc return type? *)
k' <--$ (OC_Query _ (to_list v' ++ zeroes)); (* ORACLE USE *)
$ ret (bits, (k', v')).
(* doesn't use the oracle, uses the PRF *)
Definition Generate_v_PRF_oc (state : KV) (n : nat) :
OracleComp (list bool) (Bvector eta) (list (Bvector eta) * KV) :=
[k, v] <-2 state;
v' <- f k (to_list v);
[bits, v''] <-2 Gen_loop k v' n;
k' <- f k (to_list v'' ++ zeroes);
$ ret (bits, (k', v'')).
(* doesn't use the state or oracle *)
(* intermediates have unnecessary state and updating of the state to match earlier ones *)
(* old version *)
(* Definition Generate_rb_intermediate_oc (state : KV) (n : nat)  *)
(*   : OracleComp (list bool) (Bvector eta) (list (Bvector eta) * KV) := *)
(*   bits <--$ $ Gen_loop_rb n;    (* promote comp to oraclecomp, then remove from o.c. *) *)
(*   $ ret (bits, state). *)
(* @v new version: uses last v and updates (k,v) anyway *)
Definition Generate_rb_intermediate_oc (state : KV) (n : nat)
: OracleComp (list bool) (Bvector eta) (list (Bvector eta) * KV) :=
[k, v] <-2 state;
v' <--$ $ {0,1}^eta;
[bits, v''] <--$2 $ Gen_loop_rb_intermediate k v' n;    (* promote comp to oraclecomp, then remove from o.c. *)
$ ret (bits, (k, v'')).
(* same as choose_Generate but each Generate_v in it has been converted to OracleComp *)
(* number of calls starts at 0 and ends at q. e.g. G1:      RB  PRF PRF Gi_rf 1: RB  RF  PRF (i = 1 here) G2:      RB  RB  PRF *)
(* number of calls: first call is 0, last call is (numCalls - 1) for numCalls calls total G0: PRF PRF PRF <-- Gi_prf 0 RF  PRF PRF <-- Gi_rf 0 G1: RB  PRF PRF <-- Gi_prf 1 RB  RF  PRF <-- Gi_rf 1 G2: RB  RB  PRF w    RB  RB  RF G3: RB  RB  RB  <-- note that there is no oracle slot to replace here RB  RB  RB  <-- likewise there should be (S numCalls) games, so games are numbered from 0 through numCalls *)
(* there is always an oracle slot to replace until i >= numCalls *)
(* not entirely sure these are all the right cases / in the right order *)
(* i: index for oracle; callsSoFar; n *)
Definition Oi_oc' (i : nat) (sn : nat * KV) (n : nat)
: OracleComp Blist (Bvector eta) (list (Bvector eta) * (nat * KV)) :=
[callsSoFar, state] <-2 sn;
let Generate_v_choose :=
(* this behavior (applied with f_oracle) needs to match that of choose_Generate's *)
if lt_dec callsSoFar i (* callsSoFar < i (override all else) *)
then Generate_rb_intermediate_oc (* this implicitly has no v to update *)
else if beq_nat callsSoFar O (* use oracle on 1st call w/o updating v *)
then Generate_noV_oc
else if beq_nat callsSoFar i (* callsSoFar = i *)
then Generate_v_oc    (* uses provided oracle (PRF or RF) *)
else Generate_v_PRF_oc in        (* uses PRF with (k,v) updating *)
[bits, state'] <--$2 Generate_v_choose state n;
$ ret (bits, (S callsSoFar, state')).
(* oracleCompMap_inner repeatedly applies the given oracle on the list of inputs (given an initial oracle state), collecting the outputs and final state *)
(* Fixpoint oracleCompMap_inner_acc {D R OracleIn OracleOut : Set}  *)
(*            (e1 : EqDec ((list R) * (nat * KV)))  *)
(*            (e2 : EqDec (list R)) *)
(*            (* this is an oracleComp, not an oracle *) *)
(*            (* the oracle has type (D * R) -> D -> Comp (R, (D * R)) *) *)
(*            (oracleComp : (nat * KV) -> D -> OracleComp OracleIn OracleOut (R * (nat * KV)))  *)
(*            (state : (nat * KV)) *)
(*            (init : list R) *)
(*            (inputs : list D) : OracleComp OracleIn OracleOut (list R * (nat * KV)) := *)
(*   match inputs with *)
(*   | nil => $ ret (init, state) *)
(*   | input :: inputs' =>  *)
(*     [res, state'] <--$2 oracleComp state input; (* doesn't use the init *) *)
(*     [resList, state''] <--$2 oracleCompMap_inner_acc _ _ oracleComp state' init inputs'; *)
(*     $ ret (init ++ res :: resList, state'') *)
(*   end. *)
(* Print compFold. *)
(* Print oracleMap. *)
(* Print oc_compMap. *)
(* compare to oc_compMap *)
(* maybe i don't even need to rewrite oracleCompMap_inner. what theorem do i really want? TODO *)
Fixpoint oracleCompMap_inner {D R OracleIn OracleOut : Set}
(e1 : EqDec ((list R) * (nat * KV)))
(e2 : EqDec (list R))
(* this is an oracleComp, not an oracle *)
(* the oracle has type (D * R) -> D -> Comp (R, (D * R)) *)
(oracleComp : (nat * KV) -> D -> OracleComp OracleIn OracleOut (R * (nat * KV)))
(state : (nat * KV)) (* note this state type -- it is EXPLICITLY being passed around *)
(inputs : list D) : OracleComp OracleIn OracleOut (list R * (nat * KV)) :=
match inputs with
| nil => $ ret (nil, state)
| input :: inputs' =>
[res, state'] <--$2 oracleComp state input;
[resList, state''] <--$2 oracleCompMap_inner _ _ oracleComp state' inputs';
$ ret (res :: resList, state'')
end.
(* hides the oracle state from the caller. instantates the initial state and does not return the end state. need this, otherwise the PRF adversary has to generate the key and initial value (and can see it, which it shouldn't be able to) *)
Definition oracleCompMap_outer {D R OracleIn OracleOut : Set}
(e1 : EqDec ((list R) * (nat * KV)))
(e2 : EqDec (list R))
(oracleComp : (nat * KV) -> D -> OracleComp OracleIn OracleOut (R * (nat * KV)))
(inputs : list D) : OracleComp OracleIn OracleOut (list R) :=
[k, v] <--$2 $ Instantiate;   (* generate state inside, instead of being passed state *)
[bits, _] <--$2 oracleCompMap_inner _ _ oracleComp (O, (k, v)) inputs;
(* the "_" here has type (nat * KV) *)
$ ret bits.                    (* don't return the state to the PRF adversary *)
(* see long comment above this section *)
Definition PRF_Adversary (i : nat) : OracleComp Blist (Bvector eta) bool :=
bits <--$ oracleCompMap_outer _ _ (Oi_oc' i) requestList;
$ A bits.
(* ith game: use RF oracle *)
Definition Gi_rf (i : nat) : Comp bool :=
[b, _] <-$2 PRF_Adversary i _ _ (randomFunc ({0,1}^eta) eqdbl) nil;
ret b.
(* ith game: use PRF oracle *)
Definition Gi_prf (i : nat) : Comp bool :=
k <-$ RndK;
[b, _] <-$2 PRF_Adversary i _ _ (f_oracle f _ k) tt;
ret b.
(* Expose the bad events *)
Definition hasInputDups (state : list (Blist * Bvector eta)) : bool :=
hasDups _ (fst (split state)).
(* ith game: use RF oracle *)
Definition Gi_rf_bad (i : nat) : Comp (bool * bool) :=
[b, state] <-$2 PRF_Adversary i _ _ (randomFunc ({0,1}^eta) eqdbl) nil;
ret (b, hasInputDups state).
Definition rb_oracle (state : list (Blist * Bvector eta)) (input : Blist) :=
output <-$ ({0,1}^eta);
ret (output, (input, output) :: state).
Definition Gi_rb (i : nat) : Comp bool :=
[b, state] <-$2 PRF_Adversary i _ _ rb_oracle nil;
let rbInputs := fst (split state) in
ret b.
(* adam wrote a new game here -- bad event is repetition in the random INPUTS INPUTS = v :: (first n of outputs)? *)
(* pass in the RB oracle that records its inputs what about preceding/following RB and (especially) PRF inputs/outputs? *)
Definition Gi_rb_bad (i : nat) : Comp (bool * bool) :=
[b, state] <-$2 PRF_Adversary i _ _ rb_oracle nil;
ret (b, hasInputDups state). (* assumes ith element will exist, otherwise hasDups nil (default) = false *)
(* replace requestList with a list we can evaluate on *)
Definition PRF_Adversary_l (i : nat) (l : list nat) : OracleComp Blist (Bvector eta) bool :=
bits <--$ oracleCompMap_outer _ _ (Oi_oc' i) l;
$ A bits.
(* also replaced with hasDups: my hypothesis is that adam was right *)
Definition Gi_rf_bad_l (i : nat) (l : list nat) : Comp (bool * bool) :=
[b, state] <-$2 PRF_Adversary_l i l _ _ (randomFunc ({0,1}^eta) eqdbl) nil;
ret (b, hasInputDups state). (* assumes ith element will exist, otherwise hasDups nil (default) = false *)
Definition Gi_rb_bad_l (i : nat) (l : list nat) : Comp (bool * bool) :=
[b, state] <-$2 PRF_Adversary_l i l _ _ rb_oracle nil;
ret (b, hasDups _ state). (* assumes ith element will exist, otherwise hasDups nil (default) = false *)
(* ----------------Begin PRF advantage reduction section *)
Definition PRF_Advantage_Game i : Rat :=
PRF_Advantage RndK ({0,1}^eta) f eqdbl eqdbv (PRF_Adversary i).
(*   | Pr  [PRF_G_A RndK f eqdbv (PRF_Adversary 0) ] - Pr  [PRF_G_B ({ 0 , 1 }^eta) eqdbl eqdbv (PRF_Adversary 0) ] | = | Pr  [PRF_G_A RndK f eqdbv (PRF_Adversary i) ] - Pr  [PRF_G_B ({ 0 , 1 }^eta) eqdbl eqdbv (PRF_Adversary i) ] | *)
(* TODO: are these lemmas even true? PA uses the existing adversary against the output? here, numCalls = 4  PA 0: using given oracle for call 0 GA: PRF PRF PRF PRF? GB:  RF PRF PRF PRF  PA 1: using given oracle for call 1 GA: RB  PRF PRF PRF? GB: RB  RF  PRF PRF? (do the PRF_advantages add?)  PA n-2: using given oracle for call (n-2) = 2 GA: RB  RB  PRF PRF GB: RB  RB  RF  PRF  PA n-1: using given oracle for call (n-1) = 3 GA: RB  RB  RB  PRF GB: RB  RB  RB  RF  PA n: using given oracle for call n = 4 (note: there is no oracle to replace, so PRF_Advantage = 0) GA: RB  RB  RB  RB GB: RB  RB  RB  RB  forall i, i != n -> PRF_Advantage_Game i = PRF_Advantage_Game j PRF_Advantage_Game n = 0  thus, forall i, PRF_Advantage_Game i <= PRF_Advantage_Game 0 *)
Open Scope nat.
Close Scope nat.
intuition.
rattac.
destruct (le_dec (n * x) (n0 * x0))%nat.
trivial.
exfalso.
eapply H.
rattac.
Qed.
Fixpoint argMax(f : nat -> Rat) (n : nat) :=
match n with
| O => O
| S n' => let p := (argMax f n') in
if (le_Rat_dec (f (S n')) (f p)) then p else (S n')
end.
induction n; intuition; simpl in *.
assert (n' = O) by omega; subst.
intuition.
destruct (eq_nat_dec n' (S n)).
subst.
destruct (le_Rat_dec (f0 (S n)) (f0 (argMax f0 n)));
intuition.
assert (n' <= n)%nat by omega.
destruct (le_Rat_dec (f0 (S n)) (f0 (argMax f0 n))).
eapply IHn; eauto.
eapply leRat_trans.
eapply IHn.
trivial.
eapply gtRat_impl_leRat; eauto.
Qed.
intuition.
apply argMax_correct; trivial.
Qed.
Definition PRF_Advantage_Max := PRF_Advantage_Game (argMax PRF_Advantage_Game numCalls).
(* The theorem above can be used in some of the arguments below, replacing 0 with (argMax PRF_Advantage_Game numCalls *)
(* TODO moved to end for testing *)
(* (* Step 1 *)
(* Gi_prf 2: RB RB PRF PRF PRF Gi_rf 2:  RB RB RF  PRF PRF need to use `Gi_prf i` instead of `Gi_prg i` because this matches the form of `Gi_rf` closer so we can match the form of PRF_Advantage*)4 Lemma Gi_replace_prf_with_rf_oracle_i : forall (i : nat), | Pr[Gi_prf i] - Pr[Gi_rf i] | <= PRF_Advantage_Game i.  Lemma Gi_replace_prf_with_rf_oracle : forall (i : nat), (i <= numCalls)%nat -> | Pr[Gi_prf i] - Pr[Gi_rf i] | <= PRF_Advantage_Max.  (* ------------------------------- *)
(* Step 2 *)
(* TODO use Adam's existing theorem. not sure if this is the right bound. should be a function of [blocksPerCall + 1] (for the extra v-update) *)
Definition Pr_collisions := (S blocksPerCall)^2 / 2^eta.
(* may need to update this w/ new proof *)
Definition Gi_Gi_plus_1_bound := PRF_Advantage_Max + Pr_collisions.
(* These are all lemmas to rewrite games so I can apply identical until bad *)
Definition fst3 {A B C : Type} (abc : A * B * C) : A :=
let (ab, c) := abc in
let (a, b) := ab in
a.
Open Scope nat.
(* These examples take a long time to check because of `simplify`. Commented out for now. *)
(* new postcondition *)
Definition bitsVEq {A B : Type} (x : A * (nat * KV)) (y : A * (nat * KV) * B) :=
let (bits_x, state_x) := x in
let (calls_x, kv_x) := state_x in
let (k_x, v_x) := kv_x in
let (bits_y, state_y) := fst y in
let (calls_y, kv_y) := state_y in
let (k_y, v_y) := kv_y in
(* no statement about keys being equal for now *)
bits_x = bits_y /\ v_x = v_y /\ calls_x = calls_y.
Ltac breakdown x := simpl in x; decompose [and] x; clear x; subst.
(* -------- *)
(* Rewrite gen_loop and updates computationally, and prove equivalence *)
Fixpoint Gen_loop_comp (k : Bvector eta) (v : Bvector eta) (n : nat)
: Comp (list (Bvector eta) * Bvector eta) :=
match n with
| O => ret (nil, v)
| S n' =>
v' <- f k (to_list v);
[bits, v''] <-$2 Gen_loop_comp k v' n';
ret (v' :: bits, v'')
end.
(* want to change to this, and prove the outputs are the same. the other Generate_vs don't use this version *)
Definition Generate_v_comp (state : KV) (n : nat) :
Comp (list (Bvector eta) * KV) :=
[k, v] <-2 state;
v' <- f k (to_list v);
[bits, v''] <-$2 Gen_loop_comp k v' n;
k' <- f k (to_list v'' ++ zeroes);
ret (bits, (k', v'')).
(* use this for the first call *)
Definition Generate_noV_comp (state : KV) (n : nat) :
Comp (list (Bvector eta) * KV) :=
[k, v] <-2 state;
[bits, v'] <-$2 Gen_loop_comp k v n;
k' <- f k (to_list v' ++ zeroes);
ret (bits, (k', v')).

Lemma Gen_loop_oc_eq : forall n k v,
comp_spec
(fun (x : list (Bvector eta) * Bvector eta)
(y : list (Bvector eta) * Bvector eta * unit) =>
fst x = fst (fst y) /\ snd x = snd (fst y))
(Gen_loop_comp k v n)
((Gen_loop_oc v n) unit unit_EqDec (f_oracle f eqdbv k) tt).