let update_block_st (a: alg) =
  s:state a ->
  dst:B.buffer uint8 { B.length dst = Spec.block_length a } ->
  src:B.buffer uint8 { B.length src = Spec.block_length a } ->
  Stack unit
    (requires (fun h0 ->
      B.live h0 src /\ B.live h0 dst /\
      B.(loc_disjoint (loc_buffer src) (footprint h0 s)) /\
      B.(loc_disjoint (loc_buffer dst) (footprint h0 s)) /\
      B.disjoint src dst /\
      invariant h0 s))
    (ensures (fun h0 _ h1 ->
      preserves_freeable s h0 h1 /\
      invariant h1 s /\
      B.(modifies (footprint_s (B.deref h0 s) `loc_union` loc_buffer dst) h0 h1) /\
      footprint h0 s == footprint h1 s /\
      (ctr h0 s < pow2 32 - 1 ==> ctr h1 s == ctr h0 s + 1) /\
      B.as_seq h1 dst == Spec.Loops.seq_map2 xor8 (B.as_seq h0 src)
        (Spec.ctr_block a (kv (B.deref h0 s)) (iv (B.deref h0 s)) (ctr h0 s))))
let update_block_st (a: alg) =

    s: state a ->
    dst: B.buffer uint8 {B.length dst = Spec.block_length a} ->
    src: B.buffer uint8 {B.length src = Spec.block_length a}
  -> Stack unit
      (requires
        (fun h0 ->
            B.live h0 src /\ B.live h0 dst /\ B.(loc_disjoint (loc_buffer src) (footprint h0 s)) /\
            B.(loc_disjoint (loc_buffer dst) (footprint h0 s)) /\ B.disjoint src dst /\
            invariant h0 s))
      (ensures
        (fun h0 _ h1 ->
            preserves_freeable s h0 h1 /\ invariant h1 s /\
            B.(modifies ((footprint_s (B.deref h0 s)) `loc_union` (loc_buffer dst)) h0 h1) /\
            footprint h0 s == footprint h1 s /\
            (ctr h0 s < pow2 32 - 1 ==> ctr h1 s == ctr h0 s + 1) /\
            B.as_seq h1 dst ==
            Spec.Loops.seq_map2 xor8
              (B.as_seq h0 src)
              (Spec.ctr_block a (kv (B.deref h0 s)) (iv (B.deref h0 s)) (ctr h0 s))))
NotEverCrypt.CTR.fsti.checked
Spec.Loops.fst.checked Spec.Agile.CTR.fst.checked prims.fst.checked LowStar.Buffer.fst.checked Lib.IntTypes.fsti.checked FStar.UInt32.fsti.checked FStar.Pervasives.fsti.checked FStar.HyperStack.ST.fsti.checked FStar.HyperStack.fst.checked FStar.Ghost.fsti.checked EverCrypt.Error.fsti.checked
NotEverCrypt.CTR.fsti
NotEverCrypt.CTR.alg NotEverCrypt.CTR.state LowStar.Buffer.buffer NotEverCrypt.CTR.uint8 Prims.b2t Prims.op_Equality Prims.int LowStar.Monotonic.Buffer.length LowStar.Buffer.trivial_preorder Spec.Agile.Cipher.block_length Prims.unit FStar.Monotonic.HyperStack.mem Prims.l_and LowStar.Monotonic.Buffer.live LowStar.Monotonic.Buffer.loc_disjoint LowStar.Monotonic.Buffer.loc_buffer NotEverCrypt.CTR.footprint LowStar.Monotonic.Buffer.disjoint NotEverCrypt.CTR.invariant NotEverCrypt.CTR.preserves_freeable LowStar.Monotonic.Buffer.modifies LowStar.Monotonic.Buffer.loc_union NotEverCrypt.CTR.footprint_s LowStar.Monotonic.Buffer.deref NotEverCrypt.CTR.state_s Prims.eq2 LowStar.Monotonic.Buffer.loc Prims.l_imp Prims.op_LessThan NotEverCrypt.CTR.ctr Prims.op_Subtraction Prims.pow2 Prims.op_Addition FStar.Seq.Base.seq LowStar.Monotonic.Buffer.as_seq Spec.Loops.seq_map2 Lib.IntTypes.uint_t Lib.IntTypes.U8 Lib.IntTypes.SEC NotEverCrypt.CTR.xor8 Spec.Agile.Cipher.ctr_block NotEverCrypt.CTR.kv NotEverCrypt.CTR.iv
module NotEverCrypt.CTR

module ST = FStar.HyperStack.ST
module HS = FStar.HyperStack
module B = LowStar.Buffer
module G = FStar.Ghost

module Spec = Spec.Agile.CTR

open FStar.HyperStack.ST
open EverCrypt.Error

unfold noextract
let alg = Spec.cipher_alg

/// Classic boilerplate
/// -------------------

[@CAbstractStruct]
val state_s: alg -> Type0

let state alg = B.pointer (state_s alg)

inline_for_extraction noextract
val freeable_s: #(a: alg) -> state_s a -> Type0

inline_for_extraction noextract
let freeable (#a: alg) (h: HS.mem) (p: state a) =
  B.freeable p /\ freeable_s (B.deref h p)

inline_for_extraction noextract
let preserves_freeable #a (s: state a) (h0 h1: HS.mem): Type0 =
  freeable h0 s ==> freeable h1 s

val footprint_s: #a:alg -> state_s a -> GTot B.loc
let footprint (#a:alg) (m: HS.mem) (s: state a) =
  B.(loc_union (loc_addr_of_buffer s) (footprint_s (B.deref m s)))

inline_for_extraction noextract
val invariant_s: (#a:alg) -> HS.mem -> state_s a -> Type0

inline_for_extraction noextract
let invariant (#a:alg) (m: HS.mem) (s: state a) =
  B.live m s /\
  B.(loc_disjoint (loc_addr_of_buffer s) (footprint_s (B.deref m s))) /\
  invariant_s m (B.get m s 0)

val invariant_loc_in_footprint
  (#a: alg)
  (s: state a)
  (m: HS.mem)
: Lemma
  (requires (invariant m s))
  (ensures (B.loc_in (footprint m s) m))
  [SMTPat (invariant m s)]

val frame_invariant: #a:alg -> l:B.loc -> s:state a -> h0:HS.mem -> h1:HS.mem -> Lemma
  (requires (
    invariant h0 s /\
    B.loc_disjoint l (footprint h0 s) /\
    B.modifies l h0 h1))
  (ensures (
    invariant h1 s /\
    footprint h0 s == footprint h1 s))
  [ SMTPat (invariant h1 s); SMTPat (B.modifies l h0 h1) ]

/// Ghost accessors
/// ---------------

/// We keep ghost values for the key and the iv, meaning that their value
/// doesn't depend on the heap, once we dereference past the initial pointer.
/// This should give preservation of key and iv automatically to clients.
val kv: #a:alg -> state_s a -> GTot (Spec.key a)
val iv: #a:alg -> state_s a -> GTot (Spec.nonce a)

/// ctr is mutated from call to call
val ctr: #a:alg -> h:HS.mem -> state a -> GTot Spec.ctr

/// Stateful API
/// ------------

let uint8 = Lib.IntTypes.uint8
let xor8 = Lib.IntTypes.((^.) #U8 #SEC)

let e_alg = G.erased alg

val alg_of_state: a:e_alg -> (
  let a = G.reveal a in
  s: state a -> Stack alg
  (fun h0 -> invariant h0 s)
  (fun h0 a' h1 -> h0 == h1 /\ a' == a))

inline_for_extraction noextract
let create_in_st (a:alg) =
  r:HS.rid ->
  dst:B.pointer (B.pointer_or_null (state_s a)) ->
  k:B.buffer uint8 { B.length k = Spec.Agile.CTR.key_length a } ->
  nonce: B.buffer uint8 ->
  nonce_len: UInt32.t { Spec.nonce_bound a (UInt32.v nonce_len) /\ B.len nonce = nonce_len } ->
  c: UInt32.t ->
  ST error_code
    (requires fun h0 ->
      ST.is_eternal_region r /\
      B.live h0 dst /\ B.live h0 k /\ B.live h0 nonce)
    (ensures fun h0 e h1 ->
      match e with
      | UnsupportedAlgorithm ->
          B.(modifies loc_none h0 h1)
      | InvalidIVLength ->
          B.(modifies loc_none h0 h1) /\ UInt32.v nonce_len < 12
      | Success ->
          let s = B.deref h1 dst in
          // Sanity
          not (B.g_is_null s) /\
          invariant h1 s /\

          // Memory stuff
          B.(modifies (loc_buffer dst) h0 h1) /\
          B.fresh_loc (footprint h1 s) h0 h1 /\
          B.(loc_includes (loc_region_only true r) (footprint h1 s)) /\
          freeable h1 s /\

          // Useful stuff
          kv (B.deref h1 s) == B.as_seq h0 k /\
          iv (B.deref h1 s) == B.as_seq h0 nonce /\
          ctr h1 s = UInt32.v c
      | _ -> False)

val create_in: a:alg -> create_in_st a

/// Initializes state to start at a given counter. This allows client to
/// generate a counter-block directly for a given counter, or to just start at
/// zero and do encryption block-by-block. Note that we use a C-like API where
/// the length comes after the buffer.
val init: a:e_alg -> (
  let a = G.reveal a in
  s:state a ->
  k:B.buffer uint8 { B.length k = Spec.Agile.CTR.key_length a } ->
  nonce: B.buffer uint8 ->
  nonce_len: UInt32.t { Spec.nonce_bound a (UInt32.v nonce_len) /\ B.len nonce = nonce_len } ->
  c: UInt32.t ->
  Stack unit
    (requires (fun h0 ->
      B.live h0 k /\
      B.live h0 nonce /\
      B.(loc_disjoint (loc_buffer k) (footprint h0 s)) /\
      B.(loc_disjoint (loc_buffer nonce) (footprint h0 s)) /\
      invariant h0 s))
    (ensures (fun h0 _ h1 ->
      preserves_freeable #a s h0 h1 /\
      invariant #a h1 s /\
      footprint h0 s == footprint #a h1 s /\
      B.(modifies (footprint #a h0 s) h0 h1) /\
      kv (B.deref h1 s) == B.as_seq h0 k /\
      iv (B.deref h1 s) == B.as_seq h0 nonce /\
      ctr h1 s = UInt32.v c
      )))

/// Process exactly one block, incrementing the counter contained in the state
/// in passing. The expected usage is repeated calls to update_block as more
/// data comes in, followed by a call to update_last.
inline_for_extraction noextract
val update_block_st : a: NotEverCrypt.CTR.alg -> Type0
a: NotEverCrypt.CTR.alg -> Type0