Require Import VST.floyd.proofauto.
Import ListNotations.
Local Open Scope logic.
Require Import VST.zlist.sublist.

Require Import sha.HMAC256_functional_prog.
Require Import sha.general_lemmas.
Require Import sha.spec_sha.

Require Import hmacdrbg.entropy.
Require Import hmacdrbg.entropy_lemmas.
Require Import hmacdrbg.HMAC256_DRBG_functional_prog.
Require Import hmacdrbg.hmac_drbg.
Require Import hmacdrbg.DRBG_functions.
Require Import hmacdrbg.HMAC_DRBG_algorithms.
Require Import hmacdrbg.HMAC_DRBG_pure_lemmas.
Require Import hmacdrbg.spec_hmac_drbg.
Require Import hmacdrbg.drbg_protocol_specs.
Require Import hmacdrbg.spec_hmac_drbg_pure_lemmas.
Require Import hmacdrbg.HMAC_DRBG_common_lemmas.
Require Import hmacdrbg.verif_hmac_drbg_WF.

Opaque HMAC256.
Opaque hmac256drbgabs_generate.
Opaque HMAC256_DRBG_generate_function.
Opaque mbedtls_HMAC256_DRBG_generate_function.

Lemma while_loop_post_incremental_snd:
  forall key0 V0 n out_len,
    0 <= (n * 32)%Z <= out_len ->
    (n * 32)%Z <> out_len ->
 snd (HMAC_DRBG_generate_helper_Z HMAC256 key0 V0 (n * 32)%Z) ++
 fst
   (HMAC_DRBG_generate_helper_Z HMAC256 key0 V0
      ((n * 32)%Z + Z.min 32 (out_len - (n * 32)%Z))) =
 snd
   (HMAC_DRBG_generate_helper_Z HMAC256 key0 V0
      ((n * 32)%Z + Z.min 32 (out_len - (n * 32)%Z))).
Proof.
  intros.
  rewrite Zmin_spec.
  destruct (Z_lt_ge_dec 32 (out_len - (n * 32))) as [Hmin | Hmin].
  {
    rewrite zlt_true by assumption.
    apply HMAC_DRBG_generate_helper_Z_incremental_snd; auto; lia.
  }
  {
    rewrite zlt_false by assumption.
    assert (0 < out_len - (n * 32)%Z <= 32).
    {
      split.
      rewrite <- Z2Nat.id in *; try lia.
      remember (Z.to_nat (out_len - n * 32)) as n'; destruct n'.
      {
        (* contradiction. out_len - n <> 0 *)
        assert (0 = out_len - n * 32).
        {
          symmetry;
          apply Z2Nat_inj_0.
          lia.
          symmetry; assumption.
        }
        assert (out_len = (n * 32)%Z) by lia.
        lia.
      }
      lia.
    }
    assert (exists n', (n * 32)%Z = (n' * 32)%Z).
    {
      exists n; reflexivity.
    }
    rewrite HMAC_DRBG_generate_helper_Z_incremental_equiv; auto; try lia.
    apply HMAC_DRBG_generate_helper_Z_incremental_snd; auto; lia.
  }
Qed.

Lemma while_loop_post_incremental_fst:
  forall key0 V0 n out_len,
    0 <= (n * 32)%Z <= out_len ->
    (n * 32)%Z <> out_len ->
  fst (HMAC_DRBG_generate_helper_Z HMAC256 key0 V0
      ((n * 32)%Z + Z.min 32 (out_len - (n * 32)%Z))) =
 HMAC256 (fst (HMAC_DRBG_generate_helper_Z HMAC256 key0 V0 (n * 32)%Z)) key0.
Proof.
  intros.
  rewrite Zmin_spec.
  destruct (Z_lt_ge_dec 32 (out_len - (n * 32))) as [Hmin | Hmin].
  {
    rewrite zlt_true by assumption.
    symmetry; apply HMAC_DRBG_generate_helper_Z_incremental_fst; auto; lia.
  }
  {
    rewrite zlt_false by assumption.
    assert (0 < out_len - (n * 32)%Z <= 32).
    {
      split.
      rewrite <- Z2Nat.id in *; try lia.
      remember (Z.to_nat (out_len - n * 32)) as n'; destruct n'.
      {
        (* contradiction. out_len - n <> 0 *)
        assert (0 = out_len - n * 32).
        {
          symmetry;
          apply Z2Nat_inj_0.
          lia.
          symmetry; assumption.
        }
        assert (out_len = (n * 32)%Z) by lia.
        lia.
      }
      lia.
    }
    assert (exists n', (n * 32)%Z = (n' * 32)%Z).
    {
      exists n; reflexivity.
    }
    rewrite HMAC_DRBG_generate_helper_Z_incremental_equiv; auto; try lia.
    symmetry; apply HMAC_DRBG_generate_helper_Z_incremental_fst; auto; lia.
  }
Qed.

Lemma while_loop_post_sublist_app:
  forall key0 V0 n out_len,
    0 <= (n * 32)%Z <= out_len ->
    Zlength V0 = 32 ->
  sublist 0 (n * 32)
     (snd (HMAC_DRBG_generate_helper_Z HMAC256 key0 V0 (n * 32))) ++
   sublist 0 (Z.min 32 (out_len - n * 32))
     (fst
        (HMAC_DRBG_generate_helper_Z HMAC256 key0 V0
           (n * 32 + Z.min 32 (out_len - n * 32)))) =
   sublist 0 (n * 32 + Z.min 32 (out_len - n * 32))
     (snd (HMAC_DRBG_generate_helper_Z HMAC256 key0 V0 (n * 32)) ++
      fst
        (HMAC_DRBG_generate_helper_Z HMAC256 key0 V0
           (n * 32 + Z.min 32 (out_len - n * 32)))).
Proof.
  intros.
  remember (snd (HMAC_DRBG_generate_helper_Z HMAC256 key0 V0 (n * 32))) as A.
  assert (HlengthA: Zlength A = (n * 32)%Z).
  {
    subst.
    apply HMAC_DRBG_generate_helper_Z_Zlength_snd.
    lia.
    apply hmac_common_lemmas.HMAC_Zlength.
    exists n; reflexivity.
  }
  clear HeqA.
  remember (fst
        (HMAC_DRBG_generate_helper_Z HMAC256 key0 V0
           (n * 32 + Z.min 32 (out_len - n * 32)))) as B.
  assert (HlengthB: Zlength B = 32).
  {
    subst.
    apply HMAC_DRBG_generate_helper_Z_Zlength_fst.
    rewrite Zmin_spec.
    destruct (Z_lt_ge_dec 32 (out_len - (n * 32))) as [Hmin | Hmin].
    rewrite zlt_true by assumption; lia.
    rewrite zlt_false by assumption; lia.
    assumption.
    apply hmac_common_lemmas.HMAC_Zlength.
  }
  clear HeqB.
  rewrite <- HlengthA in *.
  rewrite <- HlengthB in *.
  clear - H HlengthB.
  rewrite sublist_same; auto.
  rewrite sublist_app; try now (
    rewrite Zmin_spec;
    destruct (Z_lt_ge_dec (Zlength B) (out_len - (Zlength A))) as [Hmin | Hmin]; [rewrite zlt_true by assumption| rewrite zlt_false by assumption]; lia).
  assert (Hmin0: Z.min 0 (Zlength A) = 0).
  {
    rewrite Zmin_spec.
    rewrite <- (Z2Nat.id (Zlength A)) in *; try apply Zlength_nonneg.
    destruct (Z.to_nat (Zlength A)).
    reflexivity.
    reflexivity.
  }
  rewrite Hmin0.
  assert (HminA: (Z.min (Zlength A + Z.min (Zlength B) (out_len - Zlength A)) (Zlength A)) = Zlength A).
  {
    rewrite Zmin_spec.
    rewrite zlt_false; auto.
    destruct (Z.min_dec (Zlength B) (out_len - Zlength A)) as [Hmin | Hmin]; rewrite Hmin; lia.
  }
  rewrite HminA.
  rewrite sublist_same with (hi:=Zlength A); try lia.
  assert (Hmax0: (Z.max (0 - Zlength A) 0) = 0).
  {
    rewrite Zmax_spec.
    rewrite zlt_false; auto; lia.
  }
  rewrite Hmax0.
  replace (Zlength A + Z.min (Zlength B) (out_len - Zlength A) - Zlength A) with (Z.min (Zlength B) (out_len - Zlength A)) by lia.
  assert (HmaxB: (Z.max (Z.min (Zlength B) (out_len - Zlength A)) 0) = (Z.min (Zlength B) (out_len - Zlength A))).
  {
    rewrite <- (Z2Nat.id (out_len - Zlength A)) in *; try lia.
  }
  rewrite HmaxB.
  reflexivity.
Qed.

(*
Lemma generate_correct:
  forall should_reseed non_empty_additional s initial_state_abs out_len contents,
    hmac256drbgabs_reseed_interval initial_state_abs = 10000 ->
    hmac256drbgabs_entropy_len initial_state_abs = 32 ->
    out_len >? 1024 = false ->
    Zlength contents >? 256 = false ->
    (should_reseed = true -> exists entropy_bytes s', get_entropy 256 (hmac256drbgabs_entropy_len initial_state_abs) (hmac256drbgabs_entropy_len initial_state_abs) (hmac256drbgabs_prediction_resistance initial_state_abs) s = ENTROPY.success entropy_bytes s') ->
    should_reseed = (hmac256drbgabs_prediction_resistance initial_state_abs
                       || (hmac256drbgabs_reseed_counter initial_state_abs >? hmac256drbgabs_reseed_interval initial_state_abs))%bool ->
    non_empty_additional = (if should_reseed
                            then false
                            else
                              match contents with
                                | [] => false
                                | _ :: _ => true
                              end) ->
  mbedtls_HMAC256_DRBG_generate_function s initial_state_abs out_len contents
  = ENTROPY.success (
        (sublist 0 out_len
                 (snd
                    (HMAC_DRBG_generate_helper_Z HMAC256
                       (hmac256drbgabs_key
                          (if non_empty_additional
                           then
                            hmac256drbgabs_hmac_drbg_update initial_state_abs
                              contents
                           else
                            if should_reseed
                            then
                             hmac256drbgabs_reseed initial_state_abs s
                               contents
                            else initial_state_abs))
                       (hmac256drbgabs_value
                          (if non_empty_additional
                           then
                            hmac256drbgabs_hmac_drbg_update initial_state_abs
                              contents
                           else
                            if should_reseed
                            then
                             hmac256drbgabs_reseed initial_state_abs s
                               contents
                            else initial_state_abs)) out_len))),
        (hmac256drbgabs_to_state_handle (hmac256drbgabs_increment_reseed_counter (hmac256drbgabs_hmac_drbg_update
           (hmac256drbgabs_update_value
              (if non_empty_additional
               then
                hmac256drbgabs_hmac_drbg_update initial_state_abs contents
               else
                if should_reseed
                then hmac256drbgabs_reseed initial_state_abs s contents
                else initial_state_abs)
              (fst
                 (HMAC_DRBG_generate_helper_Z HMAC256
                    (hmac256drbgabs_key
                       (if non_empty_additional
                        then
                         hmac256drbgabs_hmac_drbg_update initial_state_abs
                           contents
                        else
                         if should_reseed
                         then
                          hmac256drbgabs_reseed initial_state_abs s contents
                         else initial_state_abs))
                    (hmac256drbgabs_value
                       (if non_empty_additional
                        then
                         hmac256drbgabs_hmac_drbg_update initial_state_abs
                           contents
                        else
                         if should_reseed
                         then
                          hmac256drbgabs_reseed initial_state_abs s contents
                         else initial_state_abs)) out_len)))
           (if should_reseed then [] else contents))))
                      ) (if should_reseed
         then
          get_stream_result
            (mbedtls_HMAC256_DRBG_reseed_function s initial_state_abs
               contents)
         else s).
Proof.
  intros until contents.
  intros Hreseed_interval Hentropy_len Hout_lenb HZlength_contentsb Hget_entropy Hshould_reseed Hnon_empty_additional.
  destruct initial_state_abs.
  simpl in *.
  unfold hmac256drbgabs_reseed.
  unfold mbedtls_HMAC256_DRBG_reseed_function.
  unfold mbedtls_HMAC256_DRBG_generate_function.
  unfold HMAC256_DRBG_generate_function, HMAC256_DRBG_reseed_function.
  unfold DRBG_generate_function, DRBG_reseed_function.
  unfold DRBG_generate_function_helper.
  unfold HMAC256_DRBG_generate_algorithm.
  unfold HMAC_DRBG_generate_algorithm.
  unfold hmac256drbgabs_key.
  unfold hmac256drbgabs_value.
  unfold hmac256drbgabs_update_value.
  unfold hmac256drbgabs_hmac_drbg_update.
  unfold HMAC256_DRBG_update.
  unfold HMAC_DRBG_update.
  unfold hmac256drbgabs_increment_reseed_counter.
  unfold hmac256drbgabs_to_state_handle.
  rewrite Hout_lenb.
  change (0 >? 256) with false.
  rewrite HZlength_contentsb.
  rewrite andb_negb_r.
  unfold sublist.
  unfold skipn.
  replace (out_len - 0) with out_len by lia.

  destruct prediction_resistance.
  {
    (* pr = true *)
    subst.
    destruct Hget_entropy as [entropy_bytes [s' Hget_entropy]]; auto.
    rewrite Hget_entropy.
    destruct entropy_bytes.
    {
      (* contradiction, can't get 0 bytes back as entropy *)
      assert (contra: Zlength (@nil Z) = 32).
      {
        eapply get_bytes_Zlength.
        lia.
        unfold get_entropy in Hget_entropy.
        subst.
        symmetry; apply Hget_entropy;auto.

      }
      change (Zlength (@nil Z)) with 0 in contra.
      inversion contra.
    }
    simpl.
    remember (HMAC_DRBG_generate_helper_Z HMAC256
              (HMAC256
                 (HMAC256 V
                    (HMAC256 (V ++ 0 :: z :: entropy_bytes ++ contents) key) ++
                  1 :: z :: entropy_bytes ++ contents)
                 (HMAC256 (V ++ 0 :: z :: entropy_bytes ++ contents) key))
              (HMAC256
                 (HMAC256 V
                    (HMAC256 (V ++ 0 :: z :: entropy_bytes ++ contents) key))
                 (HMAC256
                    (HMAC256 V
                       (HMAC256 (V ++ 0 :: z :: entropy_bytes ++ contents)
                          key) ++ 1 :: z :: entropy_bytes ++ contents)
                    (HMAC256 (V ++ 0 :: z :: entropy_bytes ++ contents) key)))
              out_len) as generate_helper_result; destruct generate_helper_result.
    reflexivity.
  }
  (* pr = false *)
  subst reseed_interval.
  unfold HMAC_DRBG_update.
  rewrite HZlength_contentsb.

  destruct (reseed_counter >? 10000).
  {
    (* must reseed *)
    subst.
    destruct Hget_entropy as [entropy_bytes [s' Hget_entropy]]; auto.
    rewrite Hget_entropy.
    destruct entropy_bytes.
    {
      (* contradiction, can't get 0 bytes back as entropy *)
      assert (contra: Zlength (@nil Z) = 32).
      {
        eapply get_bytes_Zlength.
        lia.
        unfold get_entropy in Hget_entropy.
        subst.
        symmetry; apply Hget_entropy; auto.

      }
      change (Zlength (@nil Z)) with 0 in contra.
      inversion contra.
    }
    simpl.
    remember (HMAC_DRBG_generate_helper_Z HMAC256
              (HMAC256
                 (HMAC256 V
                    (HMAC256 (V ++ 0 :: z :: entropy_bytes ++ contents) key) ++
                  1 :: z :: entropy_bytes ++ contents)
                 (HMAC256 (V ++ 0 :: z :: entropy_bytes ++ contents) key))
              (HMAC256
                 (HMAC256 V
                    (HMAC256 (V ++ 0 :: z :: entropy_bytes ++ contents) key))
                 (HMAC256
                    (HMAC256 V
                       (HMAC256 (V ++ 0 :: z :: entropy_bytes ++ contents)
                          key) ++ 1 :: z :: entropy_bytes ++ contents)
                    (HMAC256 (V ++ 0 :: z :: entropy_bytes ++ contents) key)))
              out_len) as generate_helper_result; destruct generate_helper_result.
    reflexivity.
  }
  simpl in Hshould_reseed; subst should_reseed.
  destruct contents.
  {
    (* contents empty *)
    subst.
    simpl.
    remember (HMAC_DRBG_generate_helper_Z HMAC256 key V out_len) as generate_helper_result; destruct generate_helper_result.
    reflexivity.
  }
  (* contents not empty *)
  subst.
  destruct (HMAC_DRBG_generate_helper_Z HMAC256
                (HMAC256
                   (HMAC256 V (HMAC256 (V ++ [0] ++ z :: contents) key) ++
                    [1] ++ z :: contents)
                   (HMAC256 (V ++ [0] ++ z :: contents) key))
                (HMAC256
                   (HMAC256 V (HMAC256 (V ++ [0] ++ z :: contents) key))
                   (HMAC256
                      (HMAC256 V (HMAC256 (V ++ [0] ++ z :: contents) key) ++
                       [1] ++ z :: contents)
                      (HMAC256 (V ++ [0] ++ z :: contents) key))) out_len).
  reflexivity.
Qed.
*)(*
Require Import hmacdrbg.verif_gen_c1.
Declare Module M :Continuation1.
*)

(*
Definition postReseedCtx s key V reseed_counter entropy_len prediction_resistance reseed_interval additional contents mc1 mc2 mc3 b i: mpred :=
  match mbedtls_HMAC256_DRBG_reseed_function s
               (HMAC256DRBGabs key V reseed_counter entropy_len prediction_resistance
                  reseed_interval) (contents_with_add additional (Zlength contents) contents)
  with ENTROPY.success (RSVal, RSKey, aa, bb, cc) s0 =>
       data_at Tsh t_struct_hmac256drbg_context_st
           (mc1, (mc2, mc3),
           (map Vint (map Int.repr RSVal),
           (Vint (Int.repr aa),
           (Vint (Int.repr entropy_len), (bool2val cc, Vint (Int.repr reseed_interval))))))
           (Vptr b i)
   | _ => FF
  end.
*)
(*
Definition postReseedCtx (CTX:reptype t_struct_hmac256drbg_context_st) s key V reseed_counter entropy_len prediction_resistance reseed_interval additional contents (mc:mdstate): Prop :=
  match mbedtls_HMAC256_DRBG_reseed_function s
               (HMAC256DRBGabs key V reseed_counter entropy_len prediction_resistance
                  reseed_interval) (contents_with_add additional (Zlength contents) contents)
  with ENTROPY.success (RSVal, RSKey, aa, bb, cc) s0 => CTX =
           (mc,
           (map Vint (map Int.repr RSVal),
           (Vint (Int.repr aa),
           (Vint (Int.repr entropy_len), (bool2val cc, Vint (Int.repr reseed_interval))))))
   | _ => False
  end.

Definition postReseedKey s key V reseed_counter entropy_len prediction_resistance reseed_interval additional contents mc1 mc2 mc3: mpred :=
  match mbedtls_HMAC256_DRBG_reseed_function s
               (HMAC256DRBGabs key V reseed_counter entropy_len prediction_resistance
                  reseed_interval) (contents_with_add additional (Zlength contents) contents)
  with ENTROPY.success (RSVal, RSKey, aa, bb, cc) s0 => md_full RSKey (mc1, (mc2, mc3))
  | _ => FF
  end.

Definition postReseedStream s key V reseed_counter entropy_len prediction_resistance reseed_interval additional contents: mpred :=
  match mbedtls_HMAC256_DRBG_reseed_function s
               (HMAC256DRBGabs key V reseed_counter entropy_len prediction_resistance
                  reseed_interval) (contents_with_add additional (Zlength contents) contents)
  with ENTROPY.success (RSVal, RSKey, aa, bb, cc) s0 => Stream s0
  | _ => FF
  end.

Definition mkCTX0 (should_reseed:bool) (initial_state:reptype t_struct_hmac256drbg_context_st) s key V reseed_counter entropy_len prediction_resistance reseed_interval additional contents mc p: mpred :=
  EX myctx:reptype t_struct_hmac256drbg_context_st%type,
           (!!(if should_reseed
                then postReseedCtx myctx s key V reseed_counter entropy_len prediction_resistance reseed_interval additional contents mc
                else myctx = initial_state)) &&
             data_at Tsh t_struct_hmac256drbg_context_st myctx p.*)
(*alternative definitions that don't help
Definition myMpred0 (should_reseed:bool) initial_state s key V reseed_counter entropy_len prediction_resistance reseed_interval additional contents mc p: mpred :=
  EX myctx:(val * (val * val) * (list val * (val * (val * (val * val)))))%type,
           (!!(if should_reseed
                then postReseedCtx myctx s key V reseed_counter entropy_len prediction_resistance reseed_interval additional contents mc
                else myctx = initial_state)) &&
             data_at Tsh t_struct_hmac256drbg_context_st myctx p.
Definition myMpred1 (should_reseed:bool) initial_state s key V reseed_counter entropy_len prediction_resistance reseed_interval additional contents mc p: mpred :=
  EX q1:val, EX q2:val, EX q3:val, EX q4:list val, EX q5:val, EX q6:val, EX q7:val, EX q8:val,
           (!!(if should_reseed
                then postReseedCtx (q1, (q2, q3), (q4, (q5, (q6, (q7, q8))))) s key V reseed_counter entropy_len prediction_resistance reseed_interval additional contents mc
                else (q1, (q2, q3), (q4, (q5, (q6, (q7, q8))))) = initial_state)) &&
             data_at Tsh t_struct_hmac256drbg_context_st (q1, (q2, q3), (q4, (q5, (q6, (q7, q8))))) p.
*)
(*
Definition mkCTX1 (should_reseed:bool) (ctx ctx1:reptype t_struct_hmac256drbg_context_st)
            s key V reseed_counter entropy_len prediction_resistance reseed_interval additional contents mc: Prop :=
     if should_reseed
     then match mbedtls_HMAC256_DRBG_reseed_function s
        (HMAC256DRBGabs key V reseed_counter entropy_len prediction_resistance reseed_interval)
        (contents_with_add additional (Zlength contents) contents)
       with
      | ENTROPY.success (RSVal, _, aa, _, cc) _ =>
           ctx1 = (mc,
                  (map Vint (map Int.repr RSVal),
                  (Vint (Int.repr aa),
                  (Vint (Int.repr entropy_len), (bool2val cc, Vint (Int.repr reseed_interval))))))
      | ENTROPY.error _ _ => False
       end
     else ctx1 = ctx.

Definition mkKEY1 (should_reseed:bool) s key V reseed_counter entropy_len prediction_resistance reseed_interval additional contents key1 : Prop:=
  if should_reseed
  then match mbedtls_HMAC256_DRBG_reseed_function s
        (HMAC256DRBGabs key V reseed_counter entropy_len prediction_resistance reseed_interval)
        (contents_with_add additional (Zlength contents) contents)
    with
    | ENTROPY.success (_, RSKey, _, _, _) _ => key1 = RSKey
    | ENTROPY.error _ _ => False
    end
  else key1=key.*)

Definition mkSTREAM1 (should_reseed:bool) s key V reseed_counter entropy_len prediction_resistance reseed_interval additional contents stream1 : Prop :=
  if should_reseed
          then
           match
             mbedtls_HMAC256_DRBG_reseed_function s
               (HMAC256DRBGabs key V reseed_counter entropy_len prediction_resistance
                  reseed_interval)
               (contents_with_add additional (Zlength contents) contents)
           with
           | ENTROPY.success (_, _, _, _, _) s0 => stream1 = s0
           | ENTROPY.error _ _ => False
           end
          else stream1 = s.

(* IT'S PATHETIC THAT WE NEED TO INTRDUCE ctx2' AND a predicate CTXeq to wotk around FLOYD'S typechecker!!*)
(*Definition CTXeq (c:reptype t_struct_hmac256drbg_context_st)
                 (c':(val * (val * val) * (list val * (val * (val * (val * val)))))%type) : Prop := c'=c.
*)
Definition is_multiple (multiple base: Z) : Prop := exists i, multiple = (i * base)%Z.

Lemma entailment1: forall (contents : list byte) (additional: val) (sha: share) (output : val) (sho: share)
  (out_len : Z) (b : block) (i : ptrofs) (shc: share) (mc1 mc2 mc3 : val) (key V : list byte)
  (reseed_counter entropy_len : Z) (prediction_resistance : bool)
  (reseed_interval : Z) (gv : globals) (Info : md_info_state)
  (s : ENTROPY.stream)
  (I := HMAC256DRBGabs key V reseed_counter entropy_len
                       prediction_resistance reseed_interval : hmac256drbgabs)
(*(RI : reseed_interval = 10000)*)
  (a := (mc1, (mc2, mc3),
                 (map Vubyte V,
                 (Vint (Int.repr reseed_counter),
                 (Vint (Int.repr entropy_len),
                 (bool2val prediction_resistance,
                 Vint (Int.repr reseed_interval))))))
              : mdstate * (list val * (val * (val * (val * val)))))
  (WFI: WF I)
  (Hout_lenb : (out_len >? 1024) = false)
  (ZLa : (Zlength (contents_with_add additional (Zlength contents) contents) >? 256) =
      false)
  (Hshould_reseed : (prediction_resistance || (reseed_counter >? reseed_interval))%bool =
                 true)
(*  (F : (0 >? 256) = false)*)
  (F32 : (32 >? 32) = false)
  (return_value : int)
  (Hrv : negb (Int.eq return_value (Int.repr 0)) = true)
  (Hadd_lenb : (Zlength contents >? 256) = false)
  (Hadd_len: 0 <= Zlength contents <= 256)
  (EL1: entropy_len + Zlength contents <= 384)
   (*ZLc' : Zlength contents' = 0 \/ Zlength contents' = Zlength contents*)
(*  (EL: entropy_len = 32)*),
reseedPOST (Vint return_value) contents additional sha (Zlength contents) s
  I (Vptr b i) shc Info gv a *
data_at_ sho (tarray tuchar out_len) output
|-- !! return_value_relate_result
         (mbedtls_HMAC256_DRBG_generate_function s I out_len
            (contents_with_add additional (Zlength contents) contents))
         (Vint return_value) &&
    (match
       mbedtls_HMAC256_DRBG_generate_function s I out_len
         (contents_with_add additional (Zlength contents) contents)
     with
     | ENTROPY.success (bytes, _) _ =>
         data_at sho (tarray tuchar out_len) (map Vubyte bytes)
           output
     | ENTROPY.error _ _ => data_at_ sho (tarray tuchar out_len) output
     end *
     da_emp sha (tarray tuchar (Zlength contents))
       (map Vubyte contents) additional *
     Stream
       (get_stream_result
          (mbedtls_HMAC256_DRBG_generate_function s I out_len  (contents_with_add additional (Zlength contents) contents))) *
     AREP shc gv (hmac256drbgabs_generate I s out_len  (contents_with_add additional (Zlength contents) contents)) (Vptr b i)).
Proof. intros.
 unfold reseedPOST. apply Zgt_is_gt_bool_f in Hadd_lenb.
 remember ((zlt 256 (Zlength contents)
   || zlt 384 (hmac256drbgabs_entropy_len I + Zlength contents))%bool) as d.
 destruct (zlt 256 (Zlength contents)); simpl in Heqd. lia. clear g.
 destruct (zlt 384 (entropy_len + Zlength contents)); simpl in Heqd; subst d. lia.
 normalize.
      remember (mbedtls_HMAC256_DRBG_reseed_function s I
        (contents_with_add additional (Zlength contents) contents)) as MRS.
      unfold return_value_relate_result in H.
      destruct MRS.
      { exfalso.  inv H. simpl in Hrv; discriminate. }
      unfold hmac256drbgabs_common_mpreds.
      remember (hmac256drbgabs_reseed I s
        (contents_with_add additional (Zlength contents) contents)) as RS.
      unfold hmac256drbgabs_reseed in HeqRS. rewrite <- HeqMRS in HeqRS.
      assert (HRS: RS = I) by (subst I; apply HeqRS).
      clear HeqRS; subst RS.
      remember (hmac256drbgabs_generate I s out_len
                (contents_with_add additional (Zlength contents) contents)) as Gen.
      remember (mbedtls_HMAC256_DRBG_generate_function s I out_len
             (contents_with_add additional (Zlength contents) contents)) as MGen.
      Transparent hmac256drbgabs_generate.
      Transparent mbedtls_HMAC256_DRBG_generate_function.
      unfold hmac256drbgabs_generate in HeqGen. rewrite <- HeqMGen in HeqGen.
      unfold mbedtls_HMAC256_DRBG_generate_function in HeqMGen. subst I.
      simpl in HeqMGen.
      Transparent HMAC256_DRBG_generate_function. unfold HMAC256_DRBG_generate_function in HeqMGen.
      unfold mbedtls_HMAC256_DRBG_reseed_function in HeqMRS.
      unfold DRBG_generate_function in HeqMGen.
      rewrite Hout_lenb, ZLa, andb_negb_r, F32 in HeqMGen.
      unfold DRBG_generate_function_helper in HeqMGen. rewrite <- HeqMRS in HeqMGen. subst Gen.
      simpl. Intros.
      destruct prediction_resistance; simpl in *.
      + rewrite ZLa in *. subst MGen.
        unfold return_value_relate_result.
        apply andp_right. apply prop_right. repeat split; trivial.
        simpl; cancel. unfold AREP, REP. Exists Info. Exists a.
        unfold hmac256drbg_relate; simpl. entailer!.
      + rewrite Hshould_reseed, ZLa in *.
        destruct (get_entropy 32(*256*) entropy_len entropy_len false s); try discriminate.
        inv HeqMRS. unfold return_value_relate_result; simpl.
        apply andp_right. apply prop_right. repeat split; trivial.
        cancel. unfold AREP, REP. Exists Info. Exists a.
        unfold hmac256drbg_relate; simpl. entailer!.
Qed.

Opaque hmac256drbgabs_generate.
Opaque HMAC256_DRBG_generate_function.
Opaque mbedtls_HMAC256_DRBG_generate_function.

Lemma entailment2: forall
key0 V0 reseed_counter0 entropy_len0 prediction_resistance0 reseed_interval0
(contents : list byte)
(additional : val) (sha: share)
(output : val) (sho: share)
(out_len : Z)
(b : block) (i : ptrofs) (shc: share)
(key V : list byte)
(reseed_counter entropy_len : Z)
(prediction_resistance : bool)
(reseed_interval : Z)
(gv: globals)
(s : ENTROPY.stream)
(I := HMAC256DRBGabs key V reseed_counter entropy_len prediction_resistance
       reseed_interval : hmac256drbgabs)
(H1 : Zlength (hmac256drbgabs_value I) = 32)
(H3 : 0 < hmac256drbgabs_entropy_len I)
(H4 : hmac256drbgabs_entropy_len I + Zlength contents <= 384)
(Hreseed_interval : RI_range (hmac256drbgabs_reseed_interval I))
(Hreseed_counter_in_range : 0 <= hmac256drbgabs_reseed_counter I <
                           Int.max_signed)
(Info : md_info_state)
(mc1 mc2 mc3 : val)
(WFI : WF I)
(Hout_lenb : (out_len >? 1024) = false)
(contents' := contents_with_add additional (Zlength contents) contents
          : list byte)
(ZLa : (Zlength contents' >? 256) = false)
(should_reseed := (prediction_resistance
                  || (reseed_counter >? reseed_interval))%bool : bool)
(after_reseed_add_len := if should_reseed then 0 else Zlength contents : Z)
(stream1 : ENTROPY.stream)
(STREAM1 : mkSTREAM1 should_reseed s key V reseed_counter entropy_len
            prediction_resistance reseed_interval additional contents stream1)
(na := (negb (eq_dec additional nullval) &&
       negb (eq_dec (if should_reseed then 0 else Zlength contents) 0))%bool
   : bool)
(after_reseed_state_abs := if should_reseed
                          then
                           hmac256drbgabs_reseed I s
                             (contents_with_add additional (Zlength contents)
                                contents)
                          else I : hmac256drbgabs)
(after_update_state_abs := if na
                          then hmac256drbgabs_hmac_drbg_update I contents
                          else after_reseed_state_abs : hmac256drbgabs)
(AUV := hmac256drbgabs_value after_update_state_abs : list byte)
(AUK := hmac256drbgabs_key after_update_state_abs : list byte)
(HLP := HMAC_DRBG_generate_helper_Z HMAC256 AUK AUV : Z -> list byte * list byte)
(HeqABS3 : HMAC256DRBGabs key0 V0 reseed_counter0 entropy_len0
            prediction_resistance0 reseed_interval0 =
          hmac256drbgabs_update_value after_update_state_abs
            (fst (HLP out_len)))
(key1 V1 : list byte)
(reseed_counter1 entropy_len1 : Z)
(prediction_resistance1 : bool)
(reseed_interval1 : Z)
(HeqABS4 : HMAC256DRBGabs key1 V1 reseed_counter1 entropy_len1
            prediction_resistance1 reseed_interval1 =
          hmac256drbgabs_hmac_drbg_update
            (HMAC256DRBGabs key0 V0 reseed_counter0 entropy_len0
               prediction_resistance0 reseed_interval0)
            (contents_with_add additional after_reseed_add_len contents)),
field_at shc t_struct_hmac256drbg_context_st [StructField _md_ctx]
  (mc1, (mc2, mc3)) (Vptr b i) *
(field_at shc t_struct_hmac256drbg_context_st [StructField _V]
   (map Vubyte V1) (Vptr b i) *
 (field_at shc t_struct_hmac256drbg_context_st [StructField _entropy_len]
    (Vint (Int.repr entropy_len1)) (Vptr b i) *
  (field_at shc t_struct_hmac256drbg_context_st
     [StructField _prediction_resistance]
     (bool2val prediction_resistance1) (Vptr b i) *
   (field_at shc t_struct_hmac256drbg_context_st
      [StructField _reseed_interval] (Vint (Int.repr reseed_interval1))
      (Vptr b i) * (data_at shc t_struct_mbedtls_md_info Info mc1 * emp))))) *
(md_full key1 (mc1, (mc2, mc3)) *
 (da_emp sha (tarray tuchar (Zlength contents))
    (map Vubyte contents) additional *
  (K_vector gv *
   (Stream stream1 *
    (data_at sho (tarray tuchar out_len)
       (map Vubyte (sublist 0 out_len (snd (HLP out_len))))
       output * emp) * emp)))) *
field_at shc t_struct_hmac256drbg_context_st [StructField _reseed_counter]
  (Vint (Int.repr (reseed_counter1 + 1))) (Vptr b i)
|-- !! return_value_relate_result
         (mbedtls_HMAC256_DRBG_generate_function s I out_len contents')
         (Vint (Int.repr 0)) &&
    (match mbedtls_HMAC256_DRBG_generate_function s I out_len contents' with
     | ENTROPY.success (bytes, _) _ =>
         data_at sho (tarray tuchar out_len) (map Vubyte bytes)
           output
     | ENTROPY.error _ _ => data_at_ sho (tarray tuchar out_len) output
     end *
     da_emp sha (tarray tuchar (Zlength contents))
       (map Vubyte contents) additional *
     Stream
       (get_stream_result
          (mbedtls_HMAC256_DRBG_generate_function s I out_len contents')) *
     ((EX a : hmac256drbgstate,
       !! (WF (hmac256drbgabs_generate I s out_len contents')/\ fst a =(mc1, (mc2, mc3)))&&
       data_at shc t_struct_hmac256drbg_context_st a (Vptr b i) *
       hmac256drbg_relate (hmac256drbgabs_generate I s out_len contents') a *
       data_at shc t_struct_mbedtls_md_info Info
         (hmac256drbgstate_md_info_pointer a) * K_vector gv))).
Proof. intros. assert (H6:=I).
  unfold hmac256drbgabs_common_mpreds, hmac256drbg_relate, hmac256drbgstate_md_info_pointer.
  simpl. Intros.
  set (Gen := hmac256drbgabs_generate I s out_len  contents') in *.
Transparent  hmac256drbgabs_generate.
  unfold hmac256drbgabs_generate in Gen.
Opaque hmac256drbgabs_generate.
  assert (F32: 32 >? 32 = false) by reflexivity.
  simpl in HeqABS4.
  remember (HMAC256_DRBG_update (contents_with_add additional after_reseed_add_len contents) key0 V0) as UPD.
  destruct UPD; inv HeqABS4.
  remember after_update_state_abs as AUSA.
  destruct AUSA. simpl in AUV, AUK, HeqABS3. subst AUV AUK; inv HeqABS3.
  unfold hmac256drbgabs_reseed in after_reseed_state_abs.
  unfold mkSTREAM1 in STREAM1.
  subst I.
  set (MGen := mbedtls_HMAC256_DRBG_generate_function s (HMAC256DRBGabs key V reseed_counter entropy_len prediction_resistance reseed_interval) out_len
             contents') in *.
  set (MRES := mbedtls_HMAC256_DRBG_reseed_function s (HMAC256DRBGabs key V reseed_counter entropy_len prediction_resistance reseed_interval)
                 (contents_with_add additional (Zlength contents) contents)) in *.
  simpl in Gen.
  remember should_reseed as sr.
  destruct sr.
  + subst should_reseed after_reseed_add_len. simpl in na.
    remember MRES as MRES'. destruct MRES'; try contradiction. subst MRES.
    destruct d as [[[[? ?] ?] ?] ?]. subst s0.
    simpl in H1, H3, H4, H6, Hreseed_counter_in_range.
    remember na as naa.
    destruct naa.
    - subst na. rewrite andb_false_r in Heqnaa. discriminate.
    - subst na. rewrite andb_false_r in Heqnaa. clear Heqnaa.
      subst after_reseed_state_abs. inv HeqAUSA.
      unfold mbedtls_HMAC256_DRBG_reseed_function in HeqMRES'.
Transparent mbedtls_HMAC256_DRBG_generate_function.
Transparent HMAC256_DRBG_generate_function.
      unfold mbedtls_HMAC256_DRBG_generate_function, HMAC256_DRBG_generate_function, DRBG_generate_function in MGen.
Opaque mbedtls_HMAC256_DRBG_generate_function.
Opaque HMAC256_DRBG_generate_function.
      remember MGen as MGen'. subst MGen. subst contents'.
      rewrite Hout_lenb, F32, ZLa, andb_negb_r in HeqMGen'.
      unfold DRBG_generate_function_helper in HeqMGen'.
      rewrite <- HeqMRES' in *.
      unfold HMAC256_DRBG_reseed_function, DRBG_reseed_function in HeqMRES'.
      rewrite andb_negb_r, ZLa in HeqMRES'.
      remember( get_entropy 32(*256*) entropy_len entropy_len  prediction_resistance s) as ENT.
      destruct ENT; inversion HeqMRES'; clear HeqMRES'. subst z0 b0 s0.
      unfold HMAC256_DRBG_update in HeqUPD.
      remember (HMAC_DRBG_update HMAC256 (l3 ++ contents_with_add additional (Zlength contents) contents) key V) as UPD'.
      destruct UPD'; inversion H0; clear H0. subst z l1 l2.
      assert (RI: 1 >? reseed_interval = false).
      { apply Zgt_is_gt_bool_f. simpl in Hreseed_interval. destruct Hreseed_interval. lia. }
      destruct prediction_resistance.
      * simpl in HeqMGen'. rewrite RI in *.
        remember (HMAC_DRBG_generate_helper_Z HMAC256 l4 l5 out_len) as GH.
        destruct GH. subst MGen'. subst Gen. simpl.
        (*apply andp_right. apply prop_right; trivial. cancel.
        unfold AREP, REP. Exists Info. *)
        Exists ((mc1, (mc2, mc3)),
           (map Vubyte (HMAC256 l1 (HMAC256 (l1 ++ [Byte.zero]) l4)),
              (Vint (Int.repr 2), (Vint (Int.repr entropy_len), (Vtrue, Vint (Int.repr reseed_interval)))))).
        unfold hmac256drbgstate_md_info_pointer; simpl. entailer!.
        { destruct WFI as [WFI1 [WFI2 [WFI3 WFI4]]]. red in WFI3; simpl in *; repeat split; simpl; trivial; try lia.
           apply hmac_common_lemmas.HMAC_Zlength.
           apply hmac_common_lemmas.HMAC_Zlength.  }
        rewrite sublist_firstn. cancel.
        unfold_data_at 3%nat. cancel.
        subst HLP. simpl in *.
        unfold HMAC_DRBG_update in HeqUPD, HeqUPD'.
        remember (l3 ++ contents_with_add additional (Zlength contents) contents).
        destruct l6; inv HeqUPD'.
        ++ symmetry in Heql6. apply app_eq_nil in Heql6. destruct Heql6; subst l3.
           unfold get_entropy in HeqENT. apply get_bytes_length in HeqENT. simpl in HeqENT. exfalso. clear - HeqENT H3.
           symmetry in HeqENT.  apply Z2Nat_inj_0 in HeqENT. lia. lia.
        ++ assert (CONT: contents_with_add additional 0 contents = []).
           { unfold contents_with_add; simpl. rewrite andb_false_r; trivial. }
           rewrite CONT in *. inv HeqUPD.
           rewrite <- HeqGH; simpl. cancel.
      * rewrite orb_false_l in Heqsr.
        simpl in HeqMGen'. simpl in *. (*subst reseed_interval.*) rewrite <- Heqsr, ZLa, <- HeqENT, <- HeqUPD' in HeqMGen'.
        simpl in HeqMGen'. rewrite RI in *.
        remember (HMAC_DRBG_generate_helper_Z HMAC256 l4 l5 out_len) as GH.
        destruct GH. subst MGen'. subst Gen. simpl. Intros.
        Exists ((mc1, (mc2, mc3)),
           (map Vubyte (HMAC256 l1 (HMAC256 (l1 ++ [Byte.zero]) l4)),
              (Vint (Int.repr 2), (Vint (Int.repr entropy_len), (Vfalse, Vint (Int.repr reseed_interval)))))).
        unfold hmac256drbgstate_md_info_pointer; simpl.
        entailer!.
        { destruct WFI as [WFI1 [WFI2 [WFI3 WFI4]]]. red in WFI3; simpl in *; repeat split; simpl; trivial; try lia.
           apply hmac_common_lemmas.HMAC_Zlength.
           apply hmac_common_lemmas.HMAC_Zlength. }
        rewrite sublist_firstn. cancel.
        simpl in *.
        unfold HMAC_DRBG_update in HeqUPD, HeqUPD'.
        remember (l3 ++ contents_with_add additional (Zlength contents) contents).
        destruct l6; inv HeqUPD'.
        ++ symmetry in Heql6. apply app_eq_nil in Heql6. destruct Heql6; subst l3.
           unfold get_entropy in HeqENT. apply get_bytes_length in HeqENT. simpl in HeqENT. exfalso. clear - HeqENT H3.
           symmetry in HeqENT.  apply Z2Nat_inj_0 in HeqENT. lia. lia.
        ++ assert (CONT: contents_with_add additional 0 contents = []).
           { unfold contents_with_add; simpl. rewrite andb_false_r; trivial. }
           rewrite CONT in *. inv HeqUPD. cancel.
           unfold_data_at 3%nat. cancel. subst HLP; rewrite <- HeqGH; simpl. cancel.
  + subst should_reseed after_reseed_add_len. symmetry in Heqsr.
    apply orb_false_iff in Heqsr. destruct Heqsr; subst prediction_resistance. simpl in na.
    simpl in H1, H3, H4, H6, Hreseed_counter_in_range.
    subst after_reseed_state_abs.
    unfold mbedtls_HMAC256_DRBG_reseed_function, HMAC256_DRBG_reseed_function, DRBG_reseed_function in MRES.
    remember MRES as MRES'. subst MRES.
    subst contents'. rewrite andb_negb_r, ZLa in HeqMRES'.
    remember (get_entropy 32(*256*) entropy_len entropy_len false s) as ENT.
    destruct ENT.
    - subst MRES'.
      remember  MGen as MGen'. subst MGen.
Transparent mbedtls_HMAC256_DRBG_generate_function.
Transparent HMAC256_DRBG_generate_function.
      unfold mbedtls_HMAC256_DRBG_generate_function, HMAC256_DRBG_generate_function, DRBG_generate_function in HeqMGen'.
Opaque mbedtls_HMAC256_DRBG_generate_function.
Opaque HMAC256_DRBG_generate_function.
      rewrite Hout_lenb, F32, ZLa, andb_negb_r in HeqMGen'.
      unfold DRBG_generate_function_helper in HeqMGen'. simpl in HeqMGen'.
      simpl in *. (*subst reseed_interval. *) rewrite H0 in HeqMGen'.
      remember (contents_with_add additional (Zlength contents) contents) as CONT.
      subst HLP.
(*      unfold AREP, REP. Exists Info.*)
      destruct CONT.
      * (*clear C' ZLc'.*) subst stream1.
        rewrite Zlength_nil, <- HeqENT(*, F*) in HeqMGen'. simpl in HeqMGen'.
        remember (HMAC_DRBG_generate_helper_Z HMAC256 key V out_len) as p. destruct p.
        remember (HMAC_DRBG_update HMAC256 [] key l2) as q. destruct q.
        subst MGen'. subst Gen.
        Exists (mc1, (mc2, mc3),
             (map Vubyte (HMAC256 l2 (HMAC256 (l2 ++ [Byte.zero]) key)),
             (Vint (Int.repr (reseed_counter + 1)),
             (Vint (Int.repr entropy_len),
             (Vfalse, Vint (Int.repr reseed_interval)))))).
        unfold contents_with_add in HeqCONT.
        destruct (eq_dec (Zlength contents) 0); simpl in HeqCONT.
        ++ rewrite e in *. rewrite (Zlength_nil_inv _ e) in *.
           simpl in na. destruct (EqDec_Z (Zlength contents) 0); try solve [lia]; simpl in na.
           subst na; rewrite andb_false_r in *.
           assert (F: (negb (EqDec_val additional nullval) &&
                            false)%bool = false).
           { rewrite andb_false_r. trivial. }
           subst after_update_state_abs; rewrite F in *.
           inv HeqAUSA. simpl.
           rewrite hmac_common_lemmas.HMAC_Zlength.
           inv Heqq. inv HeqUPD.
           unfold hmac256drbgstate_md_info_pointer; simpl in *. entailer!.
           { destruct WFI as [WFI1 [WFI2 [WFI3 WFI4]]]. red in Hreseed_interval. red in WFI3; simpl in *; repeat split; simpl; trivial; try lia.
             apply hmac_common_lemmas.HMAC_Zlength. }
           { destruct WFI as [WFI1 [WFI2 [WFI3 WFI4]]]. red in Hreseed_interval.
           rewrite <- Heqp, sublist_firstn; simpl. cancel.
           unfold_data_at 1%nat. cancel.
           }
        ++ destruct (EqDec_val additional nullval); simpl in na, HeqCONT.
           2: subst contents; elim n; apply Zlength_nil.
           subst na. simpl in *.
           inv HeqUPD. inv HeqAUSA. inv Heqq.
           apply andp_right. { apply prop_right; trivial. }
           rewrite hmac_common_lemmas.HMAC_Zlength.
           entailer!.
           { destruct WFI as [WFI1 [WFI2 [WFI3 WFI4]]]. red in Hreseed_interval. red in WFI3; simpl in *; repeat split; simpl; trivial; try lia.
             apply hmac_common_lemmas.HMAC_Zlength. }
           rewrite sublist_firstn, <- Heqp; simpl. cancel.
           unfold_data_at 1%nat. cancel.
     * unfold contents_with_add in HeqCONT.
       remember ((negb (eq_dec additional nullval) && negb (eq_dec (Zlength contents) 0))%bool) as f.
       destruct f; try discriminate. symmetry in Heqf; apply andb_true_iff in Heqf.
       destruct Heqf as [Heqf1 Heqf2]. apply negb_true_iff in Heqf1. apply negb_true_iff in Heqf2.
       destruct (eq_dec additional nullval); try discriminate.
       destruct (eq_dec (Zlength contents) 0); try discriminate.
       destruct (EqDec_val additional nullval). { subst additional. elim n; trivial. }
       destruct (EqDec_Z (Zlength contents) 0); simpl in na. { lia. }
       subst na. simpl in HeqAUSA.
       Exists (mc1, (mc2, mc3),
             (map Vubyte l0,
             (Vint (Int.repr (reseed_counter + 1)),
             (Vint (Int.repr entropy_len),
             (Vfalse, Vint (Int.repr reseed_interval)))))).
       unfold HMAC256_DRBG_update in *. subst stream1 contents.
       rename i0 into z.
       remember (HMAC_DRBG_update HMAC256 (z::CONT) key V) as p; destruct p. inv HeqAUSA.
       remember (HMAC_DRBG_generate_helper_Z HMAC256 l2 l3 out_len) as w; destruct w.
       simpl in HeqUPD. inv HeqUPD.
       remember (HMAC_DRBG_update HMAC256 (z :: CONT) l2 l4) as q; destruct q.
       subst Gen.
       apply andp_right. apply prop_right. repeat split; trivial.
       simpl.
       rewrite sublist_firstn. cancel.
       unfold HMAC_DRBG_update in Heqq. inv Heqq. simpl. entailer!.
       { destruct WFI as [WFI1 [WFI2 [WFI3 WFI4]]]. red in Hreseed_interval. red in WFI3; simpl in *; repeat split; simpl; trivial; try lia.
         apply hmac_common_lemmas.HMAC_Zlength.
         apply hmac_common_lemmas.HMAC_Zlength. }
       unfold_data_at 1%nat. cancel.
  - subst HLP MRES'.
      remember  MGen as MGen'. subst MGen.
Transparent mbedtls_HMAC256_DRBG_generate_function.
Transparent HMAC256_DRBG_generate_function.
      unfold mbedtls_HMAC256_DRBG_generate_function, HMAC256_DRBG_generate_function, DRBG_generate_function in HeqMGen'.
Opaque mbedtls_HMAC256_DRBG_generate_function.
Opaque HMAC256_DRBG_generate_function.
      rewrite Hout_lenb, (*F,*) ZLa, andb_negb_r in HeqMGen'.
      unfold DRBG_generate_function_helper in HeqMGen'. simpl in HeqMGen'.
      simpl in *. (*subst reseed_interval.*) rewrite H0 in HeqMGen'.
      remember (contents_with_add additional (Zlength contents) contents) as CONT.
(*      unfold AREP, REP. Exists Info.*)
      destruct CONT.
      * (*clear C' ZLc'.*) subst stream1.
        rewrite Zlength_nil, <- HeqENT(*, F*) in HeqMGen'. simpl in HeqMGen'.
        remember (HMAC_DRBG_generate_helper_Z HMAC256 key V out_len) as p. destruct p.
        remember (HMAC_DRBG_update HMAC256 [] key l2) as q. destruct q.
        Exists (mc1, (mc2, mc3),
             (map Vubyte (HMAC256 l1 (HMAC256 (l1 ++ [Byte.zero]) key)),
             (Vint (Int.repr (reseed_counter + 1)),
             (Vint (Int.repr entropy_len),
             (Vfalse, Vint (Int.repr reseed_interval)))))).
        subst MGen'. subst Gen.
        unfold contents_with_add in HeqCONT.
        destruct (eq_dec (Zlength contents) 0); simpl in HeqCONT.
        ++ rewrite e0 in *. rewrite (Zlength_nil_inv _ e0) in *.
           simpl in na. destruct (EqDec_Z (Zlength contents) 0); try solve [lia]; simpl in na.
           subst na; rewrite andb_false_r in *.
           assert (F: (negb (EqDec_val additional nullval) &&
                            false)%bool = false).
           { rewrite andb_false_r. trivial. }
           subst after_update_state_abs; rewrite F in *.
           inv HeqAUSA. simpl.
           rewrite hmac_common_lemmas.HMAC_Zlength.
           inv Heqq. inv HeqUPD.
           unfold hmac256drbgstate_md_info_pointer; simpl in *. entailer!.
           { destruct WFI as [WFI1 [WFI2 [WFI3 WFI4]]]. red in Hreseed_interval. red in WFI3; simpl in *; repeat split; simpl; trivial; try lia.
             apply hmac_common_lemmas.HMAC_Zlength. }
           rewrite <- Heqp, sublist_firstn; simpl. cancel.
           unfold_data_at 1%nat. cancel.
        ++ destruct (EqDec_val additional nullval); simpl in na, HeqCONT.
           2: subst contents; elim n; apply Zlength_nil.
           subst na. simpl in *.
           inv HeqUPD. inv HeqAUSA. inv Heqq.
           apply andp_right. apply prop_right. repeat split; trivial.
           rewrite hmac_common_lemmas.HMAC_Zlength.
           entailer!.
           { destruct WFI as [WFI1 [WFI2 [WFI3 WFI4]]]. red in Hreseed_interval. red in WFI3; simpl in *; repeat split; simpl; trivial; try lia.
             apply hmac_common_lemmas.HMAC_Zlength. }
           rewrite sublist_firstn, <- Heqp; simpl. cancel.
           unfold_data_at 1%nat. cancel.
     * unfold contents_with_add in HeqCONT.
       remember ((negb (eq_dec additional nullval) && negb (eq_dec (Zlength contents) 0))%bool) as f.
       destruct f; try discriminate. symmetry in Heqf; apply andb_true_iff in Heqf.
       destruct Heqf as [Heqf1 Heqf2]. apply negb_true_iff in Heqf1. apply negb_true_iff in Heqf2.
       destruct (eq_dec additional nullval); try discriminate.
       destruct (eq_dec (Zlength contents) 0); try discriminate.
       destruct (EqDec_val additional nullval). { subst additional. elim n; trivial. }
       destruct (EqDec_Z (Zlength contents) 0); simpl in na. { lia. }
       subst na. simpl in HeqAUSA.
       Exists (mc1, (mc2, mc3),
             (map Vubyte l0,
             (Vint (Int.repr (reseed_counter + 1)),
             (Vint (Int.repr entropy_len),
             (Vfalse, Vint (Int.repr reseed_interval)))))).
       unfold HMAC256_DRBG_update in *. subst stream1 contents.
       rename i0 into z.
       remember (HMAC_DRBG_update HMAC256 (z::CONT) key V) as p; destruct p. inv HeqAUSA.
       remember (HMAC_DRBG_generate_helper_Z HMAC256 l1 l2 out_len) as w; destruct w.
       simpl in HeqUPD. inv HeqUPD.
       remember (HMAC_DRBG_update HMAC256 (z :: CONT) l1 l3) as q; destruct q.
       subst Gen.
       apply andp_right. apply prop_right. repeat split; trivial.
       simpl.
       rewrite sublist_firstn. cancel.
       unfold HMAC_DRBG_update in Heqq. inv Heqq. simpl. entailer!.
       { destruct WFI as [WFI1 [WFI2 [WFI3 WFI4]]]. red in Hreseed_interval. red in WFI3; simpl in *; repeat split; simpl; trivial; try lia.
         apply hmac_common_lemmas.HMAC_Zlength.
         apply hmac_common_lemmas.HMAC_Zlength. }
       unfold_data_at 1%nat. cancel.
Time Qed. (*laptop 11s, desktop25s*)

Opaque mbedtls_HMAC256_DRBG_reseed_function.
Opaque mbedtls_HMAC256_DRBG_generate_function.

Lemma loopbody_explicit (StreamAdd:list mpred) : forall (Espec : OracleKind)
(contents : list byte)
(additional : val)
(add_len : Z)
(output : val) (sho: share)
(out_len : Z)
(b : block) (i : ptrofs) (shc: share)
(mc1 mc2 mc3 : val)
(key V : list byte)
(reseed_counter entropy_len : Z)
(prediction_resistance : bool)
(reseed_interval : Z)
(gv: globals)
(Info : md_info_state)
(s : ENTROPY.stream)
(*Delta_specs := abbreviate : PTree.t funspec*)
(Haddlen : 0 <= add_len <= Int.max_unsigned)
(Houtlen : 0 <= out_len <= Int.max_unsigned)
(LengthV : Zlength V = 32)
(AddLenC : add_len = Zlength contents)
(Hent_len_nonneg : 0 < entropy_len)
(Hentlen : entropy_len + Zlength contents <= 384)
(Hreseed_interval : RI_range reseed_interval)
(Hreseed_counter_in_range : 0 <= reseed_counter < Int.max_signed)
(I := (HMAC256DRBGabs key V reseed_counter entropy_len prediction_resistance
       reseed_interval) : hmac256drbgabs)
(initial_state := (mc1, (mc2, mc3),
                 (map Vubyte V,
                 (Vint (Int.repr reseed_counter),
                 (Vint (Int.repr entropy_len),
                 (bool2val prediction_resistance,
                 Vint (Int.repr reseed_interval))))))
              : mdstate * (list val * (val * (val * (val * val)))))
(PNadditional : is_pointer_or_null additional)
(Pmc1 : isptr mc1)
(Hout_len : 0 <= out_len <= 1024)
(Hout_lenb : (out_len >? 1024) = false)
(Hadd_len : 0 <= add_len <= 256)
(Hadd_lenb : (add_len >? 256) = false)
(contents' := contents_with_add additional add_len contents : list byte)
(ZLa : (Zlength contents' >? 256) = false)
(should_reseed := (prediction_resistance
                  || (reseed_counter >? reseed_interval))%bool : bool)
(after_reseed_add_len := if should_reseed then 0 else add_len : Z)
(C' : contents' = [] \/ contents' = contents)
(ZLc' : Zlength contents' = 0 \/ Zlength contents' = Zlength contents)
(*(stream1 : ENTROPY.stream)*)
(na := (negb (eq_dec additional nullval) &&
       negb (eq_dec (if should_reseed then 0 else Zlength contents) 0))%bool
   : bool)
(*Delta := abbreviate : tycontext*)
(after_reseed_state_abs := if should_reseed
                          then
                           hmac256drbgabs_reseed I s
                             (contents_with_add additional add_len contents)
                          else I : hmac256drbgabs)
(ZLength_ARSA_val : Zlength (hmac256drbgabs_value after_reseed_state_abs) = 32)
(after_update_state_abs := if na
                          then hmac256drbgabs_hmac_drbg_update I contents
                          else after_reseed_state_abs : hmac256drbgabs)
(AUV := hmac256drbgabs_value after_update_state_abs : list byte)
(ZLength_AUSA_val : Zlength AUV = 32)
(*(TR : mkSTREAM1 (prediction_resistance || (reseed_counter >? reseed_interval))
       s key V reseed_counter entropy_len prediction_resistance
       reseed_interval additional contents stream1)*)
(*(StreamAdd := abbreviate : list mpred)*)
(Poutput : isptr output)
(AUK := hmac256drbgabs_key after_update_state_abs : list byte)
(HLP := HMAC_DRBG_generate_helper_Z HMAC256 AUK AUV : Z -> list byte * list byte)
(done : Z)
(HRE : Int.repr (out_len - done) <> Int.repr 0)
(H : 0 <= done <= out_len)
(H0 : is_multiple done 32 \/ done = out_len)
(Hsho: writable_share sho)
(Hshc: writable_share shc)
(WFI : drbg_protocol_specs.WF
        (HMAC256DRBGabs key V reseed_counter entropy_len
           prediction_resistance reseed_interval)),
@semax hmac_drbg_compspecs.CompSpecs Espec
     (func_tycontext f_mbedtls_hmac_drbg_random_with_add HmacDrbgVarSpecs
        HmacDrbgFunSpecs nil)
  (PROP ( )
   LOCAL (temp _md_len (Vint (Int.repr 32)); temp _info mc1;
   temp _reseed_interval (Vint (Int.repr reseed_interval));
   temp _reseed_counter (Vint (Int.repr reseed_counter));
   temp _prediction_resistance (bool2val prediction_resistance);
   temp _out (offset_val done output);
   temp _left (Vint (Int.repr (out_len - done))); temp _ctx (Vptr b i);
   temp _p_rng (Vptr b i); temp _output output;
   temp _out_len (Vint (Int.repr out_len)); temp _additional additional;
   temp _add_len (Vint (Int.repr after_reseed_add_len)); gvars gv)
   SEP (hmac256drbgabs_common_mpreds shc
          (hmac256drbgabs_update_value after_update_state_abs
             (fst (HLP done))) initial_state (Vptr b i) Info; FRZL StreamAdd;
   data_at sho (tarray tuchar out_len)
     (map Vubyte (sublist 0 done (snd (HLP done))) ++
      repeat Vundef (Z.to_nat (out_len - done))) output; K_vector gv))
  (Ssequence
     (Ssequence
        (Sifthenelse
           (Ebinop Ogt (Etempvar _left tuint) (Etempvar _md_len tuint) tint)
           (Sset _t'6 (Ecast (Etempvar _md_len tuint) tuint))
           (Sset _t'6 (Ecast (Etempvar _left tuint) tuint)))
        (Sset _use_len (Etempvar _t'6 tuint)))
     (Ssequence
        (Scall None
           (Evar _mbedtls_md_hmac_reset
              (Tfunction
                 (Tcons (tptr (Tstruct _mbedtls_md_context_t noattr)) Tnil)
                 tint cc_default))
           [Eaddrof
              (Efield
                 (Ederef
                    (Etempvar _ctx
                       (tptr (Tstruct _mbedtls_hmac_drbg_context noattr)))
                    (Tstruct _mbedtls_hmac_drbg_context noattr)) _md_ctx
                 (Tstruct _mbedtls_md_context_t noattr))
              (tptr (Tstruct _mbedtls_md_context_t noattr))])
        (Ssequence
           (Scall None
              (Evar _mbedtls_md_hmac_update
                 (Tfunction
                    (Tcons (tptr (Tstruct _mbedtls_md_context_t noattr))
                       (Tcons (tptr tuchar) (Tcons tuint Tnil))) tint
                    cc_default))
              [Eaddrof
                 (Efield
                    (Ederef
                       (Etempvar _ctx
                          (tptr (Tstruct _mbedtls_hmac_drbg_context noattr)))
                       (Tstruct _mbedtls_hmac_drbg_context noattr)) _md_ctx
                    (Tstruct _mbedtls_md_context_t noattr))
                 (tptr (Tstruct _mbedtls_md_context_t noattr));
              Efield
                (Ederef
                   (Etempvar _ctx
                      (tptr (Tstruct _mbedtls_hmac_drbg_context noattr)))
                   (Tstruct _mbedtls_hmac_drbg_context noattr)) _V
                (tarray tuchar 32); Etempvar _md_len tuint])
           (Ssequence
              (Scall None
                 (Evar _mbedtls_md_hmac_finish
                    (Tfunction
                       (Tcons (tptr (Tstruct _mbedtls_md_context_t noattr))
                          (Tcons (tptr tuchar) Tnil)) tint cc_default))
                 [Eaddrof
                    (Efield
                       (Ederef
                          (Etempvar _ctx
                             (tptr
                                (Tstruct _mbedtls_hmac_drbg_context noattr)))
                          (Tstruct _mbedtls_hmac_drbg_context noattr))
                       _md_ctx (Tstruct _mbedtls_md_context_t noattr))
                    (tptr (Tstruct _mbedtls_md_context_t noattr));
                 Efield
                   (Ederef
                      (Etempvar _ctx
                         (tptr (Tstruct _mbedtls_hmac_drbg_context noattr)))
                      (Tstruct _mbedtls_hmac_drbg_context noattr)) _V
                   (tarray tuchar 32)])
              (Ssequence
                 (Scall None
                    (Evar _memcpy
                       (Tfunction
                          (Tcons (tptr tvoid)
                             (Tcons (tptr tvoid) (Tcons tuint Tnil)))
                          (tptr tvoid) cc_default))
                    [Etempvar _out (tptr tuchar);
                    Efield
                      (Ederef
                         (Etempvar _ctx
                            (tptr (Tstruct _mbedtls_hmac_drbg_context noattr)))
                         (Tstruct _mbedtls_hmac_drbg_context noattr)) _V
                      (tarray tuchar 32); Etempvar _use_len tuint])
                 (Ssequence
                    (Sset _out
                       (Ebinop Oadd (Etempvar _out (tptr tuchar))
                          (Etempvar _use_len tuint) (tptr tuchar)))
                    (Sset _left
                       (Ebinop Osub (Etempvar _left tuint)
                          (Etempvar _use_len tuint) tuint))))))))
  (normal_ret_assert
     (EX a : Z,
      PROP (0 <= a <= out_len; is_multiple a 32 \/ a = out_len)
      LOCAL (temp _md_len (Vint (Int.repr 32)); temp _info mc1;
      temp _reseed_interval (Vint (Int.repr reseed_interval));
      temp _reseed_counter (Vint (Int.repr reseed_counter));
      temp _prediction_resistance (bool2val prediction_resistance);
      temp _out (offset_val a output);
      temp _left (Vint (Int.repr (out_len - a))); temp _ctx (Vptr b i);
      temp _p_rng (Vptr b i); temp _output output;
      temp _out_len (Vint (Int.repr out_len)); temp _additional additional;
      temp _add_len (Vint (Int.repr after_reseed_add_len));
      gvars gv)
      SEP (hmac256drbgabs_common_mpreds shc
             (hmac256drbgabs_update_value after_update_state_abs
                (fst (HLP a))) initial_state (Vptr b i) Info; FRZL StreamAdd;
      data_at sho (tarray tuchar out_len)
        (map Vubyte (sublist 0 a (snd (HLP a))) ++
         repeat Vundef (Z.to_nat (out_len - a))) output; K_vector gv))%assert
(*
     (overridePost
        (EX a : Z,
         PROP (typed_false tint
                 (bool2val
                    (negb (Int.eq (Int.repr (out_len - a)) (Int.repr 0))));
         0 <= a <= out_len; is_multiple a 32 \/ a = out_len)
         LOCAL (temp _md_len (Vint (Int.repr 32)); temp _info mc1;
         temp _reseed_interval (Vint (Int.repr reseed_interval));
         temp _reseed_counter (Vint (Int.repr reseed_counter));
         temp _prediction_resistance (bool2val prediction_resistance);
         temp _out (offset_val a output);
         temp _left (Vint (Int.repr (out_len - a))); temp _ctx (Vptr b i);
         temp _p_rng (Vptr b i); temp _output output;
         temp _out_len (Vint (Int.repr out_len));
         temp _additional additional;
         temp _add_len (Vint (Int.repr after_reseed_add_len));
         gvar sha._K256 kv)
         SEP (hmac256drbgabs_common_mpreds
                (hmac256drbgabs_update_value after_update_state_abs
                   (fst (HLP a))) initial_state (Vptr b i) Info;
         FRZL StreamAdd;
         data_at Tsh (tarray tuchar out_len)
           (map Vint (map Int.repr (sublist 0 a (snd (HLP a)))) ++
            repeat Vundef (Z.to_nat (out_len - a))) output; K_vector kv))%assert
        (function_body_ret_assert tint
           (fun a : environ =>
            EX x : val,
            (PROP ( )
             LOCAL (temp ret_temp x)
             SEP (!! return_value_relate_result
                       (mbedtls_HMAC256_DRBG_generate_function s I out_len
                          contents') x &&
                  (match
                     mbedtls_HMAC256_DRBG_generate_function s I out_len
                       contents'
                   with
                   | ENTROPY.success (bytes, _) _ =>
                       data_at Tsh (tarray tuchar out_len)
                         (map Vint (map Int.repr bytes)) output
                   | ENTROPY.error _ _ =>
                       data_at_ Tsh (tarray tuchar out_len) output
                   end *
                   hmac256drbgabs_common_mpreds
                     (hmac256drbgabs_generate I s out_len contents')
                     initial_state (Vptr b i) Info *
                   da_emp Tsh (tarray tuchar add_len)
                     (map Vint (map Int.repr contents)) additional *
                   Stream
                     (get_stream_result
                        (mbedtls_HMAC256_DRBG_generate_function s I out_len
                           contents')) * K_vector kv))) a)))
*)).
Proof. intros.
    rename H into Hdone.
    destruct H0 as [Hmultiple | Hcontra]; [| subst done; elim HRE; f_equal; lia].
    destruct Hmultiple as [n Hmultiple].
    unfold hmac256drbgabs_common_mpreds.
    normalize.
    assert (Hfield_md_ctx: forall ctx', isptr ctx' -> field_compatible t_struct_hmac256drbg_context_st [StructField _md_ctx] ctx' -> ctx' = field_address t_struct_hmac256drbg_context_st [StructField _md_ctx] ctx').
    {
      intros ctx'' Hisptr Hfc.
      unfold field_address.
      destruct (field_compatible_dec t_struct_hmac256drbg_context_st); [|contradiction].
      simpl. change (Int.repr 0) with Int.zero. rewrite offset_val_force_ptr.
      destruct ctx''; inversion Hisptr. reflexivity.
    }
    unfold_data_at 1%nat.

    freeze [2;3;4;5] FR_unused_struct_fields.
    freeze [0;3;5] FR1.

    rewrite (field_at_data_at _ _ [StructField _md_ctx]).
    rewrite (field_at_data_at _ _ [StructField _V]).

    unfold hmac256drbg_relate. subst I.

    destruct after_update_state_abs.
    unfold hmac256drbgabs_update_value.
(*    rewrite Heqinitial_state.*)
    unfold hmac256drbgabs_to_state.
(*    rewrite Heqafter_update_key.*)
    simpl in AUV, AUK. subst AUV AUK.
    unfold md_full. subst initial_state.
    cbv beta iota zeta.
    normalize.

    (* size_t use_len = left > md_len ? md_len : left; *)
    forward_if (temp _t'6 (Vint (Int.repr (Z.min (Z.of_nat SHA256.DigestLength) (out_len - done))))).
    {
      (* md_len < left *)
      forward.
      entailer!.
      rewrite Z.min_l; [reflexivity | simpl; lia].
    }
    {
      (* md_len >= left *)
      forward.
      entailer!.
      rewrite Z.min_r; [reflexivity | simpl; lia].
    }
    forward.

    (* mbedtls_md_hmac_reset( &ctx->md_ctx ); *)
    assert_PROP (field_compatible (Tarray tuchar 32 noattr)
          []
          (field_address t_struct_hmac256drbg_context_st [StructField _V] (*ctx*)(Vptr b i))) as FC_V by entailer!.
    assert_PROP (field_compatible t_struct_hmac256drbg_context_st
         [StructField _md_ctx] (Vptr b i)) as FC_M by entailer.
    forward_call (field_address t_struct_hmac256drbg_context_st [StructField _md_ctx] (*ctx*)(Vptr b i),  (*md_ctx'*)(mc1,(mc2,mc3)), shc, key0, gv).
    { unfold md_full; simpl. cancel. }
    (* mbedtls_md_hmac_update( &ctx->md_ctx, ctx->V, md_len ); *)
    rename H into HZlength_V.
    assert_PROP (field_compatible t_struct_hmac256drbg_context_st [StructField _V] (Vptr b i)) as FCV by entailer!.

    forward_call (key0, field_address t_struct_hmac256drbg_context_st [StructField _md_ctx] (*ctx*)(Vptr b i),
                  (*md_ctx'*)(mc1,(mc2,mc3)), shc,
                  field_address t_struct_hmac256drbg_context_st [StructField _V] (*ctx*)(Vptr b i), shc,
                  @nil byte, (fst (HLP done)), gv).

    { apply prop_right. rewrite HZlength_V, field_address_offset; simpl; trivial. f_equal.
      unfold field_address. rewrite if_true; trivial.
    }
    { simpl; simpl in HZlength_V; rewrite HZlength_V (*, <- Hmultiple*).
      cancel.
    }
    { simpl; simpl in HZlength_V; rewrite HZlength_V.
      compute; reflexivity.
    }

    (*Intros vret; subst vret.*)
    rewrite app_nil_l.

    replace_SEP 2 (memory_block shc 32 (field_address t_struct_hmac256drbg_context_st [StructField _V] (*ctx*)(Vptr b i))).
    {
      entailer!.
      simpl in HZlength_V.
      unfold hmac256drbgabs_value.
      rewrite HZlength_V.
      apply data_at_memory_block.
    }

    (* mbedtls_md_hmac_finish( &ctx->md_ctx, ctx->V ); *)
    forward_call ((fst(HLP done)), key0,
               field_address t_struct_hmac256drbg_context_st [StructField _md_ctx] (*ctx*)(Vptr b i),
               (*md_ctx'*)(mc1, (mc2, mc3)), shc,
               field_address t_struct_hmac256drbg_context_st [StructField _V] (*ctx*)(Vptr b i), shc, gv).
    {
      rewrite <- memory_block_data_at_ by trivial. cancel.
    }
    assert_PROP (field_compatible (tarray tuchar out_len) [] output) as
        Hfield_compat_output by entailer!.
    replace_SEP 5 (
        data_at sho (tarray tuchar done) (map Vubyte (sublist 0 done (snd (HLP done)))) output *
        data_at sho (tarray tuchar (out_len - done)) (repeat Vundef (Z.to_nat (out_len - done))) (offset_val done output)
    ).
    {
      entailer!.
      apply derives_refl'.

      assert (HZlength1: Zlength (map Vubyte (sublist 0 (n * 32)%Z (snd (HLP (n * 32)%Z)))) = (n * 32)%Z).
      {
        rewrite Zlength_map.
        rewrite Zlength_sublist; [lia|lia|]. subst HLP.
        rewrite HMAC_DRBG_generate_helper_Z_Zlength_snd; auto; try lia.
        apply hmac_common_lemmas.HMAC_Zlength.
        exists n; reflexivity.
      }

      apply data_at_complete_split; try rewrite HZlength1; try rewrite Zlength_repeat; auto; try lia.
      (*simpl. simpl in HZlength1. rewrite HZlength1.*)
      replace ((n * 32)%Z + (out_len - (n * 32)%Z)) with out_len by lia. assumption.
    }
    normalize.

    remember (offset_val done output) as done_output.
    remember (Z.min 32 (out_len - done)) as use_len.
    assert_PROP (field_compatible (tarray tuchar (out_len - done)) [] done_output) as Hfield_compat_done_output.
    {
      clear Heqdone_output Hmultiple.
      entailer!.
    }
    Intros.
    replace_SEP 6 (
        data_at sho (tarray tuchar use_len) (repeat Vundef (Z.to_nat use_len)) done_output *
        data_at sho (tarray tuchar (out_len - done - use_len)) (repeat Vundef (Z.to_nat (out_len - done - use_len))) (offset_val use_len done_output)
    ).
    {
      clear Hmultiple Heqdone_output.
      entailer!.
      apply derives_refl'.
      rewrite Zmin_spec.
      if_tac.
      { apply data_at_complete_split; repeat rewrite Zlength_repeat; auto; try lia.
        replace (32 + (out_len - done - 32)) with (out_len - done) by lia; assumption.
        rewrite <- repeat_app.
        rewrite <- Z2Nat.inj_add; try lia.
        replace (32 + (out_len - done - 32)) with (out_len - done) by lia; reflexivity.
      }
      {
        apply data_at_complete_split; repeat rewrite Zlength_repeat; auto; try lia.
        replace (out_len - done + (out_len - done - (out_len - done))) with (out_len - done) by lia; assumption.
        replace (out_len - done - (out_len - done)) with 0 by lia; simpl; rewrite app_nil_r; reflexivity.
      }
    }
    Intros.

    replace_SEP 6 (memory_block sho use_len done_output).
    {
      clear Hmultiple.
      entailer!.
      eapply derives_trans; [apply data_at_memory_block|].
      replace (sizeof (*cenv_cs*) (tarray tuchar (Z.min 32 (out_len - done)))) with (Z.min 32 (out_len - done)).
      apply derives_refl.
      simpl.
      destruct (Z.min_dec 32 (out_len - done));
      rewrite Zmax0r; lia.
    }
    set (H256 := HMAC256 (fst (HLP done)) key0) in *.
    assert (ZL_H256: Zlength H256 = 32).
    { subst H256. apply hmac_common_lemmas.HMAC_Zlength. }
    replace_SEP 3 (data_at shc (tarray tuchar use_len)
                      (sublist 0 use_len (map Vubyte H256))
                      (field_address t_struct_hmac256drbg_context_st [StructField _V] (*ctx*)(Vptr b i)) *
                   data_at shc (tarray tuchar (32 - use_len))
                      (sublist use_len 32 (map Vubyte (H256)))
                      (offset_val use_len (field_address t_struct_hmac256drbg_context_st [StructField _V] (*ctx*)(Vptr b i)))).
    {
      clear Hmultiple.
      entailer!.
      apply derives_refl'.
      remember (fst (HLP done)) as V0'; clear HeqV0'.
      rewrite Zmin_spec.
      destruct (Z_lt_ge_dec 32 (out_len - done)) as [Hmin | Hmin].
      {
        rewrite zlt_true by assumption.
        apply data_at_complete_split; repeat rewrite Zlength_sublist; repeat rewrite Zlength_map; repeat rewrite hmac_common_lemmas.HMAC_Zlength; auto; try lia.
        rewrite sublist_nil.
        rewrite app_nil_r.
        symmetry; apply sublist_same.
        reflexivity.
        repeat rewrite Zlength_map; rewrite ZL_H256; reflexivity.
      }
      {
        rewrite zlt_false by assumption.
        apply data_at_complete_split; repeat rewrite Zlength_sublist; repeat rewrite Zlength_map; repeat rewrite hmac_common_lemmas.HMAC_Zlength; auto; try lia.
        replace (out_len - done - 0 + (32 - (out_len - done))) with 32 by lia; auto.
        rewrite sublist_rejoin; repeat rewrite Zlength_map; try rewrite hmac_common_lemmas.HMAC_Zlength; try lia.
        rewrite sublist_same; try reflexivity; repeat rewrite Zlength_map; try rewrite hmac_common_lemmas.HMAC_Zlength; try lia.
      }
    }
    (* memcpy( out, ctx->V, use_len ); *)
    forward_call ((shc, sho), done_output,
                  field_address t_struct_hmac256drbg_context_st [StructField _V] (*ctx*)(Vptr b i),
                  use_len,
                  sublist 0 use_len (map Int.repr (map Byte.unsigned H256))).
    { apply prop_right. subst; simpl. rewrite field_address_offset; trivial. }
    { entailer!. simpl. rewrite !sublist_map, !map_map. cancel. }

    simpl.
    gather_SEP (data_at _ _ _ (field_address _ [StructField _V] _))
                      (data_at _ _ _ (offset_val _ (field_address _ [StructField _V] _))).
    replace_SEP 0 (data_at shc (tarray tuchar 32) (map Vubyte H256)
                               (field_address t_struct_hmac256drbg_context_st [StructField _V] (*ctx*)(Vptr b i))).
    {
      (*clear Hmultiple.*)
      entailer!.
      apply derives_refl'.
      rewrite <- sublist_map.
      remember (fst (HLP (n*32)%Z)) as V0'; clear HeqV0'.
      symmetry.
      rewrite Zmin_spec.
      destruct (Z_lt_ge_dec 32 (out_len - (*done*)(n*32)%Z)) as [Hmin | Hmin].
      { clear - Hmin ZL_H256 Hdone FC_V.
        rewrite zlt_true by assumption. simpl.
        rewrite sublist_same; repeat rewrite Zlength_map; try rewrite hmac_common_lemmas.HMAC_Zlength; try lia.
        remember (map Vubyte (HMAC256 V0' key0)) as data.
        apply data_at_complete_split; subst data; autorewrite with sublist;
        repeat rewrite Zlength_map; try rewrite ZL_H256, Zlength_nil; autorewrite with sublist; auto; try lia.
        rewrite ZL_H256. auto.
        unfold Vubyte. rewrite !map_map. reflexivity.
      }
      {
        rewrite zlt_false by assumption.
        remember (sublist 0 (out_len - (*done*)(n*32)%Z) (map Vubyte (HMAC256 V0' key0))) as data_left.
        remember (sublist (out_len - (*done*)(n*32)%Z) 32
        (map Vubyte (HMAC256 V0' key0))) as data_right.
        apply data_at_complete_split; subst data_left data_right; repeat rewrite Zlength_sublist; repeat rewrite Zlength_map; repeat rewrite hmac_common_lemmas.HMAC_Zlength; auto; try lia.
        autorewrite with sublist.
        replace (out_len - (*done*)(n*32)%Z + (32 - (out_len - (*done*)(n*32)%Z))) with 32 by lia; auto.
        list_solve.
        unfold Vubyte.
        rewrite !sublist_map, !map_map. rewrite <- map_app. f_equal.
        rewrite sublist_rejoin; repeat rewrite Zlength_map; try rewrite hmac_common_lemmas.HMAC_Zlength; try lia.
        rewrite sublist_same; try reflexivity; repeat rewrite Zlength_map; try rewrite hmac_common_lemmas.HMAC_Zlength; try lia.
      }
    }

    gather_SEP  (data_at sho (tarray tuchar use_len) _ _)
                       (data_at sho (tarray tuchar (out_len - _ - _)) _ _).
    replace_SEP 0 (data_at sho (tarray tuchar (out_len - done))
         ( (map Vubyte (sublist 0 use_len H256))
           ++ (repeat Vundef (Z.to_nat (out_len - done - use_len))))
         done_output).
    {
      (*clear Heqdone_output Hmultiple*)
      entailer!.
      apply derives_refl'.
      rewrite Zmin_spec in *.
      symmetry.
      if_tac.
      {
        erewrite ( data_at_complete_split
                           (map Vint (sublist 0 32 (map Int.repr (map Byte.unsigned H256))))
                           (repeat Vundef (Z.to_nat (out_len - n * 32 - 32)))); try reflexivity.
        2: autorewrite with sublist; replace (_ + _) with (out_len - n*32) by lia; solve [auto].
        2: autorewrite with sublist; lia.
        2: f_equal; autorewrite with sublist; rewrite !map_map; reflexivity.
        autorewrite with sublist; rewrite ZL_H256, offset_offset_val; reflexivity.
      }
      {
        rewrite !sublist_map. rewrite !map_map.
        erewrite (data_at_complete_split
            (map (fun x : byte => Vint (Int.repr (Byte.unsigned x))) (sublist 0 (out_len - n * 32) H256))
            (repeat Vundef (Z.to_nat (out_len - n * 32 - (out_len - n * 32))))).
        3: reflexivity. 3: reflexivity. 4: reflexivity.
        + rewrite Zlength_map, Zlength_sublist, Zlength_repeat, Z.sub_0_r, offset_offset_val; try lia.
          trivial.
        + rewrite Zlength_map, Zlength_sublist, Zlength_repeat, Zminus_diag, Z.sub_0_r, Z.add_0_r; try lia. trivial.
        + rewrite Zlength_map, Zlength_sublist, Zlength_repeat; try lia.
        + unfold Vubyte. f_equal.
      }
    }

    gather_SEP (data_at sho (tarray tuchar (n*32)) _ _) (data_at sho (tarray tuchar (out_len - done)) _ _).
    replace_SEP 0 (
                  data_at sho (tarray tuchar out_len)
                    ((map Vubyte (sublist 0 done (snd (HLP done)))) ++
                     (map Vubyte (sublist 0 use_len H256) ++
                      repeat Vundef (Z.to_nat (out_len - done - use_len)))) output).
    {
      entailer!.
      apply derives_refl'.
      symmetry.
      assert (HZlength1: Zlength ((snd (HLP (n * 32)%Z))) = (n * 32)%Z).
      { subst HLP.
        rewrite HMAC_DRBG_generate_helper_Z_Zlength_snd; auto; try lia.
        apply hmac_common_lemmas.HMAC_Zlength.
        exists n; reflexivity.
      }
      rewrite Zmin_spec. simpl in *.
      if_tac.
      apply data_at_complete_split;
      repeat rewrite Zlength_app; repeat rewrite Zlength_map; try rewrite HZlength1; repeat rewrite Zlength_repeat; repeat rewrite Zlength_sublist; repeat rewrite Zlength_map; try rewrite hmac_common_lemmas.HMAC_Zlength; auto; try lia;
      try rewrite HZlength_V.
      replace ((n * 32)%Z - 0 + (32 - 0 + (out_len - (n * 32)%Z - 32))) with out_len by lia;
      assumption.
      replace ((n * 32)%Z - 0 + (out_len - (n * 32)%Z - 0 + (out_len - (n * 32)%Z - (out_len - (n * 32)%Z)))) with out_len by lia.
      apply data_at_complete_split;
      repeat rewrite Zlength_app; repeat rewrite Zlength_map; try rewrite HZlength1; repeat rewrite Zlength_repeat; repeat rewrite Zlength_sublist; repeat rewrite Zlength_map; try rewrite hmac_common_lemmas.HMAC_Zlength; auto; try lia;
      try rewrite HZlength_V.
      replace (n * 32 - 0 + (out_len - n * 32 - 0 + (out_len - n * 32 - (out_len - n * 32)))) with
         out_len by lia.
      assumption.
    }

    (* out += use_len; *)
    forward.

    (* left -= use_len; *)
    forward.
    {
      go_lower.
      Exists (done + use_len).
      unfold hmac256drbgabs_common_mpreds; normalize.

      unfold_data_at 4%nat.
      rewrite (field_at_data_at _ _ [StructField _md_ctx]);
      rewrite (field_at_data_at _ _ [StructField _V]).

      unfold md_full.

      thaw FR1.
      thaw FR_unused_struct_fields.
      assert (DD: 0 <= done + use_len).
      { subst. rewrite Zmin_spec.
        destruct (Z_lt_ge_dec 32 (out_len - (n * 32)%Z)) as [Hmin | Hmin]; [rewrite zlt_true by assumption | rewrite zlt_false by assumption]; repeat split; try lia. }
      assert (XX: is_multiple (done + use_len) 32 \/ done + use_len = out_len).
      { subst.
        rewrite Zmin_spec.
        destruct (Z_lt_ge_dec 32 (out_len - (n * 32)%Z)) as [Hmin | Hmin]; [rewrite zlt_true by assumption | rewrite zlt_false by assumption]; repeat split; try lia.
        left; exists (n + 1); lia. }
      autorewrite with norm.
      apply andp_right.
      { apply prop_right. repeat split; trivial.
        + subst. rewrite Zmin_spec.
          destruct (Z_lt_ge_dec 32 (out_len - (n * 32)%Z)) as [Hmin | Hmin]; [rewrite zlt_true by assumption | rewrite zlt_false by assumption]; lia.
        + subst done_output. simpl. destruct output; simpl; auto.
            f_equal. autorewrite with norm.
           assert (0 <= use_len <= 32).
            subst use_len; clear - Hdone DD.
            destruct (Z.min_spec 32 (out_len - done)) as [[? ?]|[? ?]]; lia.
            f_equal. f_equal. subst use_len. trivial.
        + autorewrite with norm; f_equal; f_equal. lia.
        + subst HLP. apply HMAC_DRBG_generate_helper_Z_Zlength_fst; trivial. apply hmac_common_lemmas.HMAC_Zlength. }

      subst done use_len. cancel.

      (*Rest as with "ideal proof"*)
      unfold md_full. simpl.
      replace H256 with (fst (HLP (n * 32 + Z.min 32 (out_len - n * 32))))%Z.
      rewrite app_assoc.
      replace (map Vubyte
        (
           (sublist 0 (n * 32)%Z
              (snd (HLP (n * 32)%Z)))) ++
        map Vubyte
         (sublist 0 (Z.min 32 (out_len - (n * 32)%Z))
           (
              (fst
                 (HLP ((n * 32)%Z + Z.min 32 (out_len - (n * 32)%Z))))))) with
       (map Vubyte
        (
           (sublist 0 ((n * 32)%Z + Z.min 32 (out_len - (n * 32)%Z))
              (snd
                 (HLP ((n * 32)%Z + Z.min 32 (out_len - (n * 32)%Z))))))).
      replace (out_len - (n * 32)%Z - Z.min 32 (out_len - (n * 32)%Z)) with (out_len - ((n * 32)%Z + Z.min 32 (out_len - (n * 32)%Z))) by lia.
      cancel.
      rewrite <- map_app.
      replace (sublist 0 ((n * 32)%Z + Z.min 32 (out_len - (n * 32)%Z))
           (snd
              (HLP ((n * 32)%Z + Z.min 32 (out_len - (n * 32)%Z))))) with (sublist 0 (n * 32)%Z
           (snd (HLP (n * 32)%Z)) ++
         sublist 0 (Z.min 32 (out_len - (n * 32)%Z))
           (fst
              (HLP ((n * 32)%Z + Z.min 32 (out_len - (n * 32)%Z))))).
      reflexivity.
      replace (snd
              (HLP ((n * 32)%Z + Z.min 32 (out_len - (n * 32)%Z))))
      with (snd (HLP (n * 32)%Z) ++
            fst (HLP ((n * 32)%Z + Z.min 32 (out_len - (n * 32)%Z)))).
      {
        apply while_loop_post_sublist_app; auto.
      }
      {
        apply while_loop_post_incremental_snd; auto.
        intros contra; rewrite contra, Zminus_diag in HRE. clear - HRE.
        elim HRE; trivial.
      }
      {
        apply while_loop_post_incremental_fst; auto.
        intros contra; rewrite contra, Zminus_diag in HRE. clear - HRE.
        elim HRE; trivial.
      }
    }
Time Qed. (*Coq8.10.1: 8.9s; was: 27s*)

Opaque mbedtls_HMAC256_DRBG_generate_function.

Lemma generate_loopbody: forall (StreamAdd: list mpred)
(Espec : OracleKind)
(contents : list byte)
(additional : val)
(add_len : Z)
(output : val) (sho: share)
(out_len : Z)
(b : block) (i : ptrofs) (shc: share)
(key V : list byte)
(reseed_counter entropy_len : Z)
(prediction_resistance : bool)
(reseed_interval : Z)
(gv : globals)
(s : ENTROPY.stream)
(Haddlen : 0 <= add_len <= Int.max_unsigned)
(Houtlen : 0 <= out_len <= Int.max_unsigned)
(AddLenC : add_len = Zlength contents)
(I := HMAC256DRBGabs key V reseed_counter entropy_len prediction_resistance
       reseed_interval : hmac256drbgabs)
(Hentlen : hmac256drbgabs_entropy_len I + Zlength contents <= 384)
(Info : md_info_state)
(mc1 mc2 mc3 : val)
(WFI : WF I)
(Hreseed_counter_in_range : 0 <= hmac256drbgabs_reseed_counter I <
                           Int.max_signed)
(Hreseed_interval : RI_range (hmac256drbgabs_reseed_interval I))
(ZlengthV : Zlength V = 32)
(PNadditional : is_pointer_or_null additional)
(a := (mc1, (mc2, mc3),
     (map Vubyte V,
     (Vint (Int.repr reseed_counter),
     (Vint (Int.repr entropy_len),
     (bool2val prediction_resistance, Vint (Int.repr reseed_interval))))))
  : mdstate * (list val * (val * (val * (val * val)))))
(Pmc1 : isptr mc1)
(Hout_len : 0 <= out_len <= 1024)
(Hout_lenb : (out_len >? 1024) = false)
(Hadd_len : 0 <= add_len <= 256)
(Hadd_lenb : (add_len >? 256) = false)
(contents' := contents_with_add additional add_len contents : list byte)
(ZLa : (Zlength contents' >? 256) = false)
(should_reseed := (prediction_resistance
                  || (reseed_counter >? reseed_interval))%bool : bool)
(after_reseed_add_len := if should_reseed then 0 else add_len : Z)
(C' : contents' = [] \/ contents' = contents)
(ZLc' : Zlength contents' = 0 \/ Zlength contents' = Zlength contents)
(na := (negb (eq_dec additional nullval) &&
       negb (eq_dec (if should_reseed then 0 else Zlength contents) 0))%bool
   : bool)
(after_reseed_state_abs := if should_reseed
                          then
                           hmac256drbgabs_reseed I s
                             (contents_with_add additional add_len contents)
                          else I : hmac256drbgabs)
(ZLength_ARSA_val : Zlength (hmac256drbgabs_value after_reseed_state_abs) = 32)
(after_update_state_abs := if na
                          then hmac256drbgabs_hmac_drbg_update I contents
                          else after_reseed_state_abs : hmac256drbgabs)
(AUV := hmac256drbgabs_value after_update_state_abs : list byte)
(ZLength_AUSA_val : Zlength AUV = 32)
(Poutput : isptr output)
(AUK := hmac256drbgabs_key after_update_state_abs : list byte)
(HLP := HMAC_DRBG_generate_helper_Z HMAC256 AUK AUV : Z -> list byte * list byte)
(done : Z)
(HRE : Int.repr (out_len - done) <> Int.repr 0)
(Hsho: writable_share sho)
(Hshc: writable_share shc)
(H : 0 <= done <= out_len)
(H0 : is_multiple done 32 \/ done = out_len),
@semax hmac_drbg_compspecs.CompSpecs Espec
  (func_tycontext f_mbedtls_hmac_drbg_random_with_add HmacDrbgVarSpecs
        HmacDrbgFunSpecs nil)
  (PROP ( )
   LOCAL (temp _md_len (Vint (Int.repr 32)); temp _info mc1;
   temp _reseed_interval (Vint (Int.repr reseed_interval));
   temp _reseed_counter (Vint (Int.repr reseed_counter));
   temp _prediction_resistance (bool2val prediction_resistance);
   temp _out (offset_val done output);
   temp _left (Vint (Int.repr (out_len - done))); temp _ctx (Vptr b i);
   temp _p_rng (Vptr b i); temp _output output;
   temp _out_len (Vint (Int.repr out_len)); temp _additional additional;
   temp _add_len (Vint (Int.repr after_reseed_add_len)); gvars gv)
   SEP (hmac256drbgabs_common_mpreds shc
          (hmac256drbgabs_update_value after_update_state_abs
             (fst (HLP done))) a (Vptr b i) Info; FRZL StreamAdd;
   data_at sho (tarray tuchar out_len)
     (map Vubyte (sublist 0 done (snd (HLP done))) ++
      repeat Vundef (Z.to_nat (out_len - done))) output; K_vector gv))
  (Ssequence
     (Ssequence
        (Sifthenelse
           (Ebinop Ogt (Etempvar _left tuint) (Etempvar _md_len tuint) tint)
           (Sset _t'6 (Ecast (Etempvar _md_len tuint) tuint))
           (Sset _t'6 (Ecast (Etempvar _left tuint) tuint)))
        (Sset _use_len (Etempvar _t'6 tuint)))
     (Ssequence
        (Scall None
           (Evar _mbedtls_md_hmac_reset
              (Tfunction
                 (Tcons (tptr (Tstruct _mbedtls_md_context_t noattr)) Tnil)
                 tint cc_default))
           [Eaddrof
              (Efield
                 (Ederef
                    (Etempvar _ctx
                       (tptr (Tstruct _mbedtls_hmac_drbg_context noattr)))
                    (Tstruct _mbedtls_hmac_drbg_context noattr)) _md_ctx
                 (Tstruct _mbedtls_md_context_t noattr))
              (tptr (Tstruct _mbedtls_md_context_t noattr))])
        (Ssequence
           (Scall None
              (Evar _mbedtls_md_hmac_update
                 (Tfunction
                    (Tcons (tptr (Tstruct _mbedtls_md_context_t noattr))
                       (Tcons (tptr tuchar) (Tcons tuint Tnil))) tint
                    cc_default))
              [Eaddrof
                 (Efield
                    (Ederef
                       (Etempvar _ctx
                          (tptr (Tstruct _mbedtls_hmac_drbg_context noattr)))
                       (Tstruct _mbedtls_hmac_drbg_context noattr)) _md_ctx
                    (Tstruct _mbedtls_md_context_t noattr))
                 (tptr (Tstruct _mbedtls_md_context_t noattr));
              Efield
                (Ederef
                   (Etempvar _ctx
                      (tptr (Tstruct _mbedtls_hmac_drbg_context noattr)))
                   (Tstruct _mbedtls_hmac_drbg_context noattr)) _V
                (tarray tuchar 32); Etempvar _md_len tuint])
           (Ssequence
              (Scall None
                 (Evar _mbedtls_md_hmac_finish
                    (Tfunction
                       (Tcons (tptr (Tstruct _mbedtls_md_context_t noattr))
                          (Tcons (tptr tuchar) Tnil)) tint cc_default))
                 [Eaddrof
                    (Efield
                       (Ederef
                          (Etempvar _ctx
                             (tptr
                                (Tstruct _mbedtls_hmac_drbg_context noattr)))
                          (Tstruct _mbedtls_hmac_drbg_context noattr))
                       _md_ctx (Tstruct _mbedtls_md_context_t noattr))
                    (tptr (Tstruct _mbedtls_md_context_t noattr));
                 Efield
                   (Ederef
                      (Etempvar _ctx
                         (tptr (Tstruct _mbedtls_hmac_drbg_context noattr)))
                      (Tstruct _mbedtls_hmac_drbg_context noattr)) _V
                   (tarray tuchar 32)])
              (Ssequence
                 (Scall None
                    (Evar _memcpy
                       (Tfunction
                          (Tcons (tptr tvoid)
                             (Tcons (tptr tvoid) (Tcons tuint Tnil)))
                          (tptr tvoid) cc_default))
                    [Etempvar _out (tptr tuchar);
                    Efield
                      (Ederef
                         (Etempvar _ctx
                            (tptr (Tstruct _mbedtls_hmac_drbg_context noattr)))
                         (Tstruct _mbedtls_hmac_drbg_context noattr)) _V
                      (tarray tuchar 32); Etempvar _use_len tuint])
                 (Ssequence
                    (Sset _out
                       (Ebinop Oadd (Etempvar _out (tptr tuchar))
                          (Etempvar _use_len tuint) (tptr tuchar)))
                    (Sset _left
                       (Ebinop Osub (Etempvar _left tuint)
                          (Etempvar _use_len tuint) tuint))))))))
  (normal_ret_assert
     (EX a0 : Z,
      PROP (0 <= a0 <= out_len; is_multiple a0 32 \/ a0 = out_len)
      LOCAL (temp _md_len (Vint (Int.repr 32)); temp _info mc1;
      temp _reseed_interval (Vint (Int.repr reseed_interval));
      temp _reseed_counter (Vint (Int.repr reseed_counter));
      temp _prediction_resistance (bool2val prediction_resistance);
      temp _out (offset_val a0 output);
      temp _left (Vint (Int.repr (out_len - a0))); temp _ctx (Vptr b i);
      temp _p_rng (Vptr b i); temp _output output;
      temp _out_len (Vint (Int.repr out_len)); temp _additional additional;
      temp _add_len (Vint (Int.repr after_reseed_add_len));
      gvars gv)
      SEP (hmac256drbgabs_common_mpreds shc
             (hmac256drbgabs_update_value after_update_state_abs
                (fst (HLP a0))) a (Vptr b i) Info; FRZL StreamAdd;
      data_at sho (tarray tuchar out_len)
        (map Vubyte (sublist 0 a0 (snd (HLP a0))) ++
         repeat Vundef (Z.to_nat (out_len - a0))) output; K_vector gv))%assert).
Proof. intros.
eapply semax_post_flipped'.
apply (loopbody_explicit StreamAdd); try assumption;
    subst I; red in WFI; simpl in *; lia.
apply andp_left2.
go_lowerx.
Time Qed. (*2s*)


(* Monad nat : Set. But we may not be able to generate totality (let alone goodness)
for it. The current totality proof needs irrel for indices.
Can we get rid of that usage?
Clearly this Monad cannot be encoded as an IWTS because there the index type I had type set.
but Set does not have type Set.
 *)
Inductive  Monad : forall (A:Set) , Set := ret : Monad nat.

(*
Inductive  MonadLarge : forall (A:Set) , Set :=
  retl : forall A:Set, MonadLarge (list A).
Error: Large non-propositional inductive types must be in Type.
 *)

(* MonadLarge nat is in type Type. So we don't need to generate any goodness.
  Also, for now, it is not a part of the language of interest, because it mentions Type *)
Inductive  MonadLarge : forall (A:Set) , Type :=
  retl : forall A:Set, MonadLarge (list A).