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 *)

(* oracleMap hardcodes acc for compFold as nil, so generalize it *)
Theorem compFold_acc : forall numCalls0 bits acc state,
comp_spec
(fun x y : list (list (Bvector eta)) * KV =>
hd_error (fst x) = Some bits /\ tl (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 s d; [r, s0]<-2 z; ret (rs ++ r :: nil, s0))
(bits :: acc, state) (replicate numCalls0 blocksPerCall))
(compFold (pair_EqDec (list_EqDec (list_EqDec eqdbv)) eqDecState)
(fun (acc : list (list (Bvector eta)) * KV) (d : nat) =>
[rs, s]<-2 acc;
z <-$ Generate s d; [r, s0]<-2 z; ret (rs ++ r :: nil, s0))
(acc, state) (replicate numCalls0 blocksPerCall)).