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.

(* oraclemap with intermediate rb is same as oi_prg with i = numcalls *)
Lemma oracleMap_rb_eq : forall calls k v res n,
n + length calls = numCalls ->
comp_spec
(fun (x : list (list (Bvector eta)) * KV)
(y : list (list (Bvector eta)) * (nat * KV)) =>
fst x = fst y)
(compFold (pair_EqDec (list_EqDec (list_EqDec eqdbv)) eqDecState)
(fun (acc : list (list (Bvector eta)) * KV) (d : nat) =>
[rs, s]<-2 acc;
z <-$ Generate_rb_intermediate s d;
[r, s0]<-2 z; ret (rs ++ r :: nil, s0)) (res, (k, v))
calls)
(compFold
(pair_EqDec (list_EqDec (list_EqDec eqdbv))
(pair_EqDec nat_EqDec eqDecState))
(fun (acc : list (list (Bvector eta)) * (nat * KV)) (d : nat) =>
[rs, s]<-2 acc;
z <-$ choose_Generate numCalls s d; [r, s0]<-2 z; ret (rs ++ r :: nil, s0))
(res, (n, (k, v))) calls).