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')).
(* ------- *)
Transparent oracleMap.
Transparent oracleCompMap_inner.
Transparent choose_Generate.
Transparent Oi_oc'.
Definition randomFunc_withDups (ls : list (Blist * Bvector eta)) (x : Blist) :
Comp (Bvector eta * list (Blist * Bvector eta)) :=
y <-$
(match (arrayLookup _ ls x) with
| Some y => ret y
| None => {0,1}^eta
end);
ret (y, (x, y) :: ls).
(* ith game: use RF oracle *)
Definition Gi_rf_dups_bad (i : nat) : Comp (bool * bool) :=
[b, state] <-$2 PRF_Adversary i _ _ randomFunc_withDups nil;
ret (b, hasInputDups state).
induction blocks; intuition; simpl; fcf_simp.
apply comp_spec_ret; intuition.
fcf_simp.
fcf_skip.
eapply oc_comp_spec_eq.
apply H0.
intuition.
fcf_simp.
simpl in *.
intuition.
pairInv.
fcf_skip.
simpl in *.
destruct b3.
simpl in *. subst.
fcf_simp.
apply comp_spec_ret.
simpl.
intuition.
Qed.
clear f.
intuition.
unfold randomFunc_withDups, randomFunc.
case_eq (arrayLookup _ s1 a); intuition.
rewrite <- H.
rewrite -> H0.
fcf_simp.
apply comp_spec_ret.
intuition.
simpl.
case_eq (eqb a0 a); intuition.
rewrite eqb_leibniz in H1.
subst. trivial.
rewrite <- H.
rewrite H0.
fcf_skip.
apply oneVector.
apply oneVector.
eapply comp_spec_ret.
intuition.
simpl.
rewrite H.
trivial.
Qed.
(* ---------------------------------- *)
(* Assuming the Gi_prog_equiv_rb_oracle stuff starts here *)
(* used in Oi_oc''. the difference between this function and Generate_v_oc is that it hardcodes the oracle to be RB oracle, and moves the k update to be the first line, rather than being after the bit generation, because k no longer depends on the new v *)
(* takes in key but doesn't use it, to match the type of other Generate_vs *)
Definition Generate_v_oc_instantiate (state : KV) (n : nat) :
OracleComp (list bool) (Bvector eta) (list (Bvector eta) * KV) :=
[k, v_0] <-2 state;
k' <--$ $ {0,1}^eta;
v <--$ $ {0,1}^eta;
[bits, v'] <--$2 Gen_loop_oc v n;
$ ret (bits, (k', v')).
(* used in Oi_oc''. the difference between this function and Generate_rb_intermediate_oc is that it now updates v (but still does not update k). why do we need to update v in the normal RB cases??? *)
(* okay, i see that we're trying to match the form of Generate_v_oc_instantiate *)
(* at a high level, we're trying to bridge Generate_v_oc_instantiate and Generate_rb_intermediate_oc *)
(* now unused *)
Definition Generate_rb_intermediate_oc_noV (state : KV) (n : nat)
: OracleComp (list bool) (Bvector eta) (list (Bvector eta) * KV) :=
[k, v] <-2 state;
[bits, v'] <--$2 $ Gen_loop_rb_intermediate k v n;    (* promote comp to oraclecomp, then remove from o.c. *)
$ ret (bits, (k, v')).
(* does not carry the k state around *)
(* TODO: this function and the next theorem aren't used anywhere?? *)
Fixpoint Gen_loop_rb_intermediate_v (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_v v' n';
ret (v' :: bits, v'')
end.
(* used in Oi_oc''. the difference between this function and Generate_noV is that 1. we assume the oracle passed in is the RB oracle 2. because we make that assumption, k is independent of v, so we resample it before the bits are generated (for ease of skipping) rather than after *)
(* note oracle state won't be the same as Generate_noV_oc *)
Definition Generate_noV_oc_k (state : KV) (n : nat) :
OracleComp (list bool) (Bvector eta)  (list (Bvector eta) * KV) :=
[k, v] <-2 state;
k' <--$ $ {0,1}^eta;
[bits, v'] <--$2 Gen_loop_oc v n;
$ ret (bits, (k', v')).
(* Print Oi_oc'. Print Gen_loop_oc.*)
(* (let Generate_v_choose := *)
(*    if lt_dec callsSoFar i *)
(*    then Generate_rb_intermediate_oc *)
(*    else *)
(*     if beq_nat callsSoFar 0 *)
(*     then Generate_noV_oc *)
(*     else if beq_nat callsSoFar i then Generate_v_oc else Generate_v_PRF_oc in *)
(*  z <--$ Generate_v_choose state n; *)
(*  [bits, state']<-2 z; $ ret (bits, (S callsSoFar, state'))) *)
(* hardcode oracle everywhere to be RB oracle *)
(* moves k sampling to the beginning of each relevant function *)
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 :=
if lt_dec callsSoFar i
then Generate_rb_intermediate_oc (* CHANGE: uses the last v *)
else if beq_nat callsSoFar O
then Generate_noV_oc_k (* CHANGE: k pulled to beginning; does use last v *)
else if beq_nat callsSoFar i (* callsSoFar = i *)
then Generate_v_oc_instantiate   (* CHANGE: kv pulled to beginning; does use last v *)
else Generate_v_PRF_oc in
[bits, state'] <--$2 Generate_v_choose state n;
$ ret (bits, (S callsSoFar, state')).
(* uses genupdate rb on any call <= i *)
(* removes the extra k or k,v sampling before bit gen *)
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 :=
if lt_dec callsSoFar i
then Generate_rb_intermediate_oc
else if beq_nat callsSoFar O
then Generate_rb_intermediate_oc_noV (* no k-sampling or v-sampling *)
(* CHANGE: diff b/t Generate_v_oc_k and this fn is, this one removes the k sampling before bit gen *)
else if beq_nat callsSoFar i (* callsSoFar = i *)
then Generate_rb_intermediate_oc (* no k-sampling, only v-sampling *)
(* diff b/t Generate_v_oc_instantiate and this fn is, this one removes the k sampling before bit gen *)
else Generate_v_PRF_oc in
[bits, state'] <--$2 Generate_v_choose state n;
$ ret (bits, (S callsSoFar, state')).
(* note the double prime here: this replaces OC_Query w Instantiate *)
(* also in Generate_noV_oc_k the one k call is moved up *)
(* note i switched the order of k and v in Generate_v_oc_instantiate *)
Notation "A = B = C" := (A = B /\ B = C).
(* note Oi_oc': need to rewrite w first theorem in outer *)
Ltac rewrite_l := apply comp_spec_symm; eapply comp_spec_eq_trans_l.
(* note Oi_oc': need to rewrite w first theorem in outer *)
Transparent choose_Generate.
(* ----------------------- *Identical until bad section *)
clear f.
induction a; intuition; simpl; fcf_well_formed.
Qed.
intros.
unfold Generate_rb_intermediate_oc.
fcf_simp.
fcf_well_formed.
apply Gen_loop_rb_intermediate_wf.
Qed.
clear f.
induction a; intuition; simpl; fcf_well_formed.
Qed.
intros.
unfold Generate_noV_oc.
fcf_simp.
fcf_well_formed.
apply Gen_loop_oc_wf.
Qed.
clear f.
intros.
unfold Generate_v_oc.
fcf_simp.
fcf_well_formed.
apply Gen_loop_oc_wf.
Qed.
intros.
unfold Generate_v_PRF_oc.
fcf_simp.
fcf_well_formed.
Qed.
induction inputs; intuition; simpl;
fcf_well_formed.
destruct (lt_dec a0 i).
apply Generate_rb_intermediate_oc_wf.
destruct (beq_nat a0 0).
apply Generate_noV_oc_wf.
destruct (beq_nat a0 i).
apply Generate_v_oc_wf.
eapply Generate_v_PRF_oc_wf.
Qed.
(* TODO fix args *)
(* let (bits_rb, state_rb) := y1 in *)
(* let (bits_rf, state_rf) := y2 in *)
(* let (inputs_rb, outputs_rb) := (fst (split state_rb), snd (split state_rb)) in *)
(* let (inputs_rf, output_rf) := (fst (split state_rf), snd (split state_rf)) in *)
(* true -- if there are no duplicates, then the random function behaves exactly like RB, so the key is randomly sampled AND the v (going into the PRF) is also randomly sampled. so the outputs should be the same. in fact, if there are no dups, PRF_Adv rf i = PRF_Adv rb i. so, this means the comp_spec above is true? does PRHL act like giving each the same "tape" of randomness for equality? *)
(* "distribution of the value of interest is the same in c_1 and c_2 when the bad event does not happen" -- the two are basically the same if the bad event doesn't happen, so it's true. differences: 1. PRF re-keyed using RF vs. randomly sampled. but PRF re-keyed using something of length > eta, so it is effectively randomly sampled. 2. the v going into the next call (if it exists) is randomly sampled vs. resulting from a RF call, but it doesn't matter *)
(* TODO: both of these proofs (in PRF_DRBG) rely on the PRF_Adv comp_spec lemma, which is proven by a bunch of casework. need another comp_spec lemma here on genupdate/prfadv? that adversary isn't even here anymore... *)
Close Scope nat.
(* ----------- End identical until bad section *)
(* ---------- Begin collision probability bound section *)
(* bad event is repetition in the random INPUTS. INPUTS = v :: (first n of outputs)? *)
(* modified PRF_Adversary to just return bits *)
Definition callMapWith (i : nat) : OracleComp Blist (Bvector eta) (list (list (Bvector eta))) :=
bits <--$ oracleCompMap_outer _ _ (Oi_oc' i) requestList;
$ ret bits.
(* throw away the first input and the adversary, focus on bad event only *)
Definition Gi_rb_bad_no_adv (i : nat) : Comp bool :=
[_, state] <-$2 callMapWith i _ _ rb_oracle nil;
ret (hasInputDups state).
Close Scope nat.
(* match the form in dupProb_const *)
Definition compMap_v (ls : list nat) (v : Bvector eta) :=
x <-$ compMap _ (fun _ => {0,1}^eta) ls;
ret hasDups _ (v :: x).
(* match the general form in Gi_rb_collisions_inner_eq_general_induct_irr_l's induction *)
Definition compMap_v_init (ls : list nat) (v : Bvector eta) (init : list (Blist * Bvector eta)) :=
x <-$ compMap _ (fun _ => {0,1}^eta) ls;
ret hasDups _ ((map (@to_list _ _) (v :: x)) ++ (map (@fst _ _ ) init)).
(* generalized version of case_on_i for induction hyp *)
Definition case_on_i_gen (i listLen callsSoFar nblocks : nat) (init : list (Blist * Bvector eta)) (v : Bvector eta) :=
if ge_dec i (listLen + callsSoFar) then (ret (hasInputDups init))
(* i = 0, so the last v is not an input. this case is exactly equivalent to adam’s gen_loop *)
else if zerop i then (compMap_v_init (forNats (pred nblocks)) v init)
else (compMap_v_init (forNats nblocks) v init).
Definition case_on_i (i ncalls nblocks : nat) (v : Bvector eta) :=
if ge_dec i ncalls then (ret false)
(* i = 0, so the last v is not an input. this case is exactly equivalent to adam’s gen_loop *)
else if zerop i then (compMap_v (forNats (pred nblocks)) v)
else (compMap_v (forNats nblocks) v).
Open Scope nat.
(* TODO split this out into a separate module (CompMap_v_equiv.v) *)
(* Transparent hasDups. *)
(*Require Import FCF.CompMap_v_equiv.*)
(* TODO see if I can prove a more general version of compmap_v_eq using compMap_v_eq_h *)
(* From email: See compMap_v_eq_h in the file I attached on Apr 22. I think this is what you want. Note that this fact only holds if v and w are both not in init (you can also prove it when both are in init by removing the compMap statements with fcc_irr). *)
(* adding init on the right *)
Opaque hasDups.
(* these two assumptions come from compMap_v_eq_init_list_h *)
(* these two assumptions come from compMap_v_eq_init_list_h *)
(* probability of bad event happening in RB game is bounded by the probability of collisions in a list of length (n+1) of randomly-sampled (Bvector eta) *)
intuition.
Transparent hasDups.
simpl.
destruct (in_dec (EqDec_dec eqd) a ls); intuition.
Qed.
clear f.
intuition.
fcf_skip.
rewrite hasDups_cons_orb.
intuition.
Qed.
intuition.
rewrite compMap_hasDups_cons_orb.
rewrite evalDist_orb_le.
rewrite FixedInRndList_prob.
rewrite dupProb.
remember (length ls) as a.
rewrite <- ratAdd_den_same.
eapply leRat_terms; trivial.
simpl.
apply le_S.
apply plus_le_compat; try omega.
apply mult_le_compat; omega.
Qed.
Close Scope nat.

(* Main theorem (modeled on PRF_DRBG_G3_G4_close) *)
(* Gi_rf 0:  RF  PRF PRF Gi_prg 1:    RB PRF PRF  Gi_rf  1:    RB  RF PRF Gi_prg 2:    RB  RB  PRF  Gi_rf  2:    RB  RB  RF Gi_prg 3:    RB  RB  RB *)
Lemma Gi_replace_rf_with_rb_oracle : forall (i : nat), (* not true for i = 0 (and not needed) *)
| Pr[Gi_rf i] - Pr[Gi_prg (S i)] | <= Pr_collisions.