let bn_lt_mask_st (t:limb_t) (len:size_t) =
    a:lbignum t len
  -> b:lbignum t len ->
  Stack (limb t)
  (requires fun h -> live h a /\ live h b)
  (ensures  fun h0 r h1 -> modifies0 h0 h1 /\
    r == S.bn_lt_mask (as_seq h0 a) (as_seq h0 b))
let bn_lt_mask_st (t: limb_t) (len: size_t) =
a: lbignum t len -> b: lbignum t len
  -> Stack (limb t)
      (requires fun h -> live h a /\ live h b)
      (ensures fun h0 r h1 -> modifies0 h0 h1 /\ r == S.bn_lt_mask (as_seq h0 a) (as_seq h0 b))
Hacl.Bignum.fsti.checked
prims.fst.checked Lib.LoopCombinators.fsti.checked Lib.IntTypes.fsti.checked Lib.Buffer.fsti.checked Hacl.Spec.Bignum.fsti.checked Hacl.Bignum.Definitions.fst.checked Hacl.Bignum.Base.fst.checked FStar.Tactics.Typeclasses.fsti.checked FStar.Pervasives.Native.fst.checked FStar.Pervasives.fsti.checked FStar.Mul.fst.checked FStar.HyperStack.ST.fsti.checked FStar.HyperStack.fst.checked
Hacl.Bignum.fsti
Hacl.Bignum.Definitions.limb_t Lib.IntTypes.size_t Hacl.Bignum.Definitions.lbignum Hacl.Bignum.Definitions.limb FStar.Monotonic.HyperStack.mem Prims.l_and Lib.Buffer.live Lib.Buffer.MUT Lib.Buffer.modifies0 Prims.eq2 Hacl.Spec.Bignum.Definitions.limb Hacl.Spec.Bignum.bn_lt_mask Lib.IntTypes.v Lib.IntTypes.U32 Lib.IntTypes.PUB Lib.Buffer.as_seq
module Hacl.Bignum

open FStar.HyperStack
open FStar.HyperStack.ST
open FStar.Mul

open Lib.IntTypes
open Lib.Buffer

open Hacl.Bignum.Base
open Hacl.Bignum.Definitions

module S = Hacl.Spec.Bignum
module ST = FStar.HyperStack.ST
module Loops = Lib.LoopCombinators


#reset-options "--z3rlimit 50 --fuel 0 --ifuel 0"

inline_for_extraction noextract
val bn_add1:
    #t:limb_t
  -> aLen:size_t{0 < v aLen}
  -> a:lbignum t aLen
  -> b1:limb t
  -> res:lbignum t aLen ->
  Stack (carry t)
  (requires fun h ->
    live h a /\ live h res /\ eq_or_disjoint a res)
  (ensures  fun h0 c_out h1 -> modifies (loc res) h0 h1 /\
    (let c, r = S.bn_add1 (as_seq h0 a) b1 in
    c_out == c /\ as_seq h1 res == r))


inline_for_extraction noextract
val bn_sub1:
    #t:limb_t
  -> aLen:size_t{0 < v aLen}
  -> a:lbignum t aLen
  -> b1:limb t
  -> res:lbignum t aLen ->
  Stack (carry t)
  (requires fun h ->
    live h a /\ live h res /\ eq_or_disjoint a res)
  (ensures  fun h0 c_out h1 -> modifies (loc res) h0 h1 /\
    (let c, r = S.bn_sub1 (as_seq h0 a) b1 in
    c_out == c /\ as_seq h1 res == r))


inline_for_extraction noextract
let bn_add_eq_len_st (t:limb_t) (len:size_t) =
     a:lbignum t len
  -> b:lbignum t len
  -> res:lbignum t len ->
  Stack (carry t)
  (requires fun h ->
    live h a /\ live h b /\ live h res /\
    eq_or_disjoint a b /\ eq_or_disjoint a res /\ eq_or_disjoint b res)
  (ensures  fun h0 c_out h1 -> modifies (loc res) h0 h1 /\
    (c_out, as_seq h1 res) == S.bn_add (as_seq h0 a) (as_seq h0 b))


inline_for_extraction noextract
val bn_add_eq_len: #t:limb_t -> len:size_t -> bn_add_eq_len_st t len


inline_for_extraction noextract
let bn_sub_eq_len_st (t:limb_t) (len:size_t) =
     a:lbignum t len
  -> b:lbignum t len
  -> res:lbignum t len ->
  Stack (carry t)
  (requires fun h ->
    live h a /\ live h b /\ live h res /\
    eq_or_disjoint a b /\ eq_or_disjoint a res /\ eq_or_disjoint b res)
  (ensures  fun h0 c_out h1 -> modifies (loc res) h0 h1 /\
   (c_out, as_seq h1 res) == S.bn_sub (as_seq h0 a) (as_seq h0 b))


inline_for_extraction noextract
val bn_sub_eq_len: #t:limb_t -> len:size_t -> bn_sub_eq_len_st t len


inline_for_extraction noextract
let bn_add_st (t:limb_t) =
    aLen:size_t
  -> a:lbignum t aLen
  -> bLen:size_t{v bLen <= v aLen}
  -> b:lbignum t bLen
  -> res:lbignum t aLen ->
  Stack (carry t)
  (requires fun h ->
    live h a /\ live h b /\ live h res /\
    disjoint a b /\ eq_or_disjoint a res /\ disjoint b res)
  (ensures  fun h0 c_out h1 -> modifies (loc res) h0 h1 /\
    (c_out, as_seq h1 res) == S.bn_add (as_seq h0 a) (as_seq h0 b))


inline_for_extraction noextract
val bn_add: #t:limb_t -> bn_add_st t


inline_for_extraction noextract
let bn_sub_st (t:limb_t) =
    aLen:size_t
  -> a:lbignum t aLen
  -> bLen:size_t{v bLen <= v aLen}
  -> b:lbignum t bLen
  -> res:lbignum t aLen ->
  Stack (carry t)
  (requires fun h ->
    live h a /\ live h b /\ live h res /\
    disjoint a b /\ eq_or_disjoint a res /\ disjoint b res)
  (ensures  fun h0 c_out h1 -> modifies (loc res) h0 h1 /\
   (c_out, as_seq h1 res) == S.bn_sub (as_seq h0 a) (as_seq h0 b))


inline_for_extraction noextract
val bn_sub: #t:limb_t -> bn_sub_st t


inline_for_extraction noextract
val bn_reduce_once:
    #t:limb_t
  -> aLen:size_t{v aLen > 0}
  -> n:lbignum t aLen
  -> c:carry t
  -> a:lbignum t aLen ->
  Stack unit
  (requires fun h -> live h a /\ live h n /\ disjoint a n)
  (ensures  fun h0 c_out h1 -> modifies (loc a) h0 h1 /\
    as_seq h1 a == S.bn_reduce_once (as_seq h0 n) c (as_seq h0 a))


inline_for_extraction noextract
let bn_add_mod_n_st (t:limb_t) (len:size_t{v len > 0}) =
    n:lbignum t len
  -> a:lbignum t len
  -> b:lbignum t len
  -> res:lbignum t len ->
  Stack unit
  (requires fun h ->
    live h n /\ live h a /\ live h b /\ live h res /\
    disjoint n a /\ disjoint n b /\ disjoint n res /\
    eq_or_disjoint a b /\ eq_or_disjoint a res /\ eq_or_disjoint b res)
  (ensures  fun h0 _ h1 -> modifies (loc res) h0 h1 /\
    as_seq h1 res == S.bn_add_mod_n (as_seq h0 n) (as_seq h0 a) (as_seq h0 b))


inline_for_extraction noextract
val bn_add_mod_n: #t:limb_t -> len:size_t{v len > 0} -> bn_add_mod_n_st t len


inline_for_extraction noextract
let bn_sub_mod_n_st (t:limb_t) (len:size_t{v len > 0}) =
    n:lbignum t len
  -> a:lbignum t len
  -> b:lbignum t len
  -> res:lbignum t len ->
  Stack unit
  (requires fun h ->
    live h n /\ live h a /\ live h b /\ live h res /\
    disjoint n a /\ disjoint n b /\ disjoint n res /\
    eq_or_disjoint a b /\ eq_or_disjoint a res /\ eq_or_disjoint b res)
  (ensures  fun h0 _ h1 -> modifies (loc res) h0 h1 /\
    as_seq h1 res == S.bn_sub_mod_n (as_seq h0 n) (as_seq h0 a) (as_seq h0 b))


inline_for_extraction noextract
val bn_sub_mod_n: #t:limb_t -> len:size_t{v len > 0} -> bn_sub_mod_n_st t len


inline_for_extraction noextract
val bn_mul1:
    #t:limb_t
  -> aLen:size_t
  -> a:lbignum t aLen
  -> l:limb t
  -> res:lbignum t aLen ->
  Stack (limb t)
  (requires fun h ->
    live h a /\ live h res /\ eq_or_disjoint res a)
  (ensures  fun h0 c_out h1 -> modifies (loc res) h0 h1 /\
    (c_out, as_seq h1 res) == S.bn_mul1 (as_seq h0 a) l)


inline_for_extraction noextract
let bn_karatsuba_mul_st (t:limb_t) (len:size_t{0 < v len /\ 4 * v len <= max_size_t}) (a:lbignum t len) =
    b:lbignum t len
  -> res:lbignum t (len +! len) ->
  Stack unit
  (requires fun h ->
    live h a /\ live h b /\ live h res /\
    disjoint res a /\ disjoint res b /\ eq_or_disjoint a b)
  (ensures  fun h0 _ h1 -> modifies (loc res) h0 h1 /\
    as_seq h1 res == S.bn_mul (as_seq h0 a) (as_seq h0 b))


inline_for_extraction noextract
val bn_karatsuba_mul:
    #t:limb_t
  -> len:size_t{0 < v len /\ 4 * v len <= max_size_t}
  -> a:lbignum t len ->
  bn_karatsuba_mul_st t len a


inline_for_extraction noextract
let bn_mul_st (t:limb_t) (aLen:size_t) (bLen:size_t{v aLen + v bLen <= max_size_t}) (a:lbignum t aLen) =
    b:lbignum t bLen
  -> res:lbignum t (aLen +! bLen) ->
  Stack unit
  (requires fun h ->
    live h a /\ live h b /\ live h res /\
    disjoint res a /\ disjoint res b /\ eq_or_disjoint a b)
  (ensures  fun h0 _ h1 -> modifies (loc res) h0 h1 /\
    as_seq h1 res == S.bn_mul (as_seq h0 a) (as_seq h0 b))


inline_for_extraction noextract
val bn_mul:
    #t:limb_t
  -> aLen:size_t
  -> bLen:size_t{v aLen + v bLen <= max_size_t}
  -> a:lbignum t aLen ->
  bn_mul_st t aLen bLen a


inline_for_extraction noextract
let bn_karatsuba_sqr_st (t:limb_t) (len:size_t{0 < v len /\ 4 * v len <= max_size_t}) (a:lbignum t len) =
  res:lbignum t (len +! len) ->
  Stack unit
  (requires fun h -> live h a /\ live h res /\ disjoint res a)
  (ensures  fun h0 _ h1 -> modifies (loc res) h0 h1 /\
    as_seq h1 res == S.bn_mul (as_seq h0 a) (as_seq h0 a))


inline_for_extraction noextract
val bn_karatsuba_sqr:
    #t:limb_t
  -> len:size_t{0 < v len /\ 4 * v len <= max_size_t}
  -> a:lbignum t len ->
  bn_karatsuba_sqr_st t len a


inline_for_extraction noextract
let bn_sqr_st (t:limb_t) (len:size_t{0 < v len /\ v len + v len <= max_size_t}) (a:lbignum t len) =
  res:lbignum t (len +! len) ->
  Stack unit
  (requires fun h -> live h a /\ live h res /\ disjoint res a)
  (ensures  fun h0 _ h1 -> modifies (loc res) h0 h1 /\
    as_seq h1 res == S.bn_mul (as_seq h0 a) (as_seq h0 a))


inline_for_extraction noextract
val bn_sqr:
    #t:limb_t
  -> len:size_t{0 < v len /\ v len + v len <= max_size_t}
  -> a:lbignum t len ->
  bn_sqr_st t len a


inline_for_extraction noextract
val bn_mul1_lshift_add_in_place:
    #t:limb_t
  -> aLen:size_t
  -> a:lbignum t aLen
  -> b:limb t
  -> resLen:size_t
  -> j:size_t{v j + v aLen <= v resLen}
  -> res:lbignum t resLen ->
  Stack (limb t)
  (requires fun h -> live h a /\ live h res /\ disjoint res a)
  (ensures  fun h0 c_out h1 -> modifies (loc res) h0 h1 /\
    (c_out, as_seq h1 res) == S.bn_mul1_lshift_add (as_seq h0 a) b (v j) (as_seq h0 res))


inline_for_extraction noextract
val bn_rshift:
    #t:limb_t
  -> len:size_t
  -> b:lbignum t len
  -> i:size_t{v i < v len}
  -> res:lbignum t (len -! i) ->
  Stack unit
  (requires fun h -> live h b /\ live h res /\ disjoint res b)
  (ensures  fun h0 _ h1 -> modifies (loc res) h0 h1 /\
    as_seq h1 res == S.bn_rshift (as_seq h0 b) (v i))


inline_for_extraction noextract
val bn_sub_mask:
    #t:limb_t
  -> len:size_t{v len > 0}
  -> n:lbignum t len
  -> a:lbignum t len ->
  Stack unit
  (requires fun h -> live h n /\ live h a /\ disjoint n a)
  (ensures  fun h0 _ h1 -> modifies (loc a) h0 h1 /\
    as_seq h1 a == S.bn_sub_mask (as_seq h0 n) (as_seq h0 a))

///
///  Get and set i-th bit of a bignum
///

inline_for_extraction noextract
val bn_get_ith_bit:
    #t:limb_t
  -> len:size_t
  -> b:lbignum t len
  -> i:size_t{v i / bits t < v len} ->
  Stack (limb t)
  (requires fun h -> live h b)
  (ensures  fun h0 r h1 -> h0 == h1 /\
    r == S.bn_get_ith_bit (as_seq h0 b) (v i))


inline_for_extraction noextract
val bn_get_bits:
    #t:limb_t
  -> len:size_t
  -> b:lbignum t len
  -> i:size_t
  -> l:size_t{v l < bits t /\ v i / bits t < v len} ->
  Stack (limb t)
  (requires fun h -> live h b)
  (ensures  fun h0 r h1 -> h0 == h1 /\
    r == S.bn_get_bits (as_seq h0 b) (v i) (v l))


inline_for_extraction noextract
val bn_set_ith_bit:
    #t:limb_t
  -> len:size_t
  -> b:lbignum t len
  -> i:size_t{v i / bits t < v len} ->
  Stack unit
  (requires fun h -> live h b)
  (ensures  fun h0 _ h1 -> modifies (loc b) h0 h1 /\
    as_seq h1 b == S.bn_set_ith_bit (as_seq h0 b) (v i))


inline_for_extraction noextract
val cswap2:
    #t:limb_t
  -> len:size_t
  -> bit:limb t
  -> b1:lbignum t len
  -> b2:lbignum t len ->
  Stack unit
  (requires fun h -> live h b1 /\ live h b2 /\ disjoint b1 b2)
  (ensures  fun h0 _ h1 -> modifies (loc b1 |+| loc b2) h0 h1 /\
    (as_seq h1 b1, as_seq h1 b2) == S.cswap2 bit (as_seq h0 b1) (as_seq h0 b2))


/// Start of the len-based specialization infrastructure.
///
/// Essentially, we wish to describe a type class of basic bignum operations
/// needed to implement Montgomery reduction. This will allow us to generate
/// specialized versions of modular exponentiation for a given bignum size.
///
/// This is done "by hand" because the specialization pattern is more
/// complicated than other pieces of code (chacha, poly, curve), meaning I don't
/// (yet) know how to automate it with a tactic. So, a type class seems like the
/// most efficient way to go about it.
///
/// The operations of the type class were found by essentially reading the code,
/// writing down which operations from Hacl.Bignum are (transitively) needed to
/// implement modular exponentiation, and figuring out for each call-site
/// whether they can be specialized for the bignum size, or whether they have to
/// been inline-for-extraction'd.

inline_for_extraction noextract
let meta_len (t:limb_t) = len:size_t{0 < v len /\ 2 * bits t * v len <= max_size_t}

/// This type class is entirely meta-level and will not appear after partial
/// evaluation in the resulting C code. Clients can take this type class as a
/// parameter if they want the benefits of a function set specialized for a
/// given bignum length.
inline_for_extraction noextract
class bn (t:limb_t) = {
  len: meta_len t;
  add: bn_add_eq_len_st t len;
  sub: bn_sub_eq_len_st t len;
  add_mod_n: bn_add_mod_n_st t len;
  sub_mod_n: bn_sub_mod_n_st t len;
  mul: a:lbignum t len -> bn_karatsuba_mul_st t len a;
  sqr: a:lbignum t len -> bn_karatsuba_sqr_st t len a;
}


inline_for_extraction noextract
val mk_runtime_bn: t:limb_t -> len:meta_len t -> bn t

val mk_runtime_bn_len_lemma: t:limb_t -> len:meta_len t ->
  Lemma ((mk_runtime_bn t len).len == len) [SMTPat (mk_runtime_bn t len)]

///
///  Bignum comparison and test functions
///

inline_for_extraction noextract
val bn_is_odd:
    #t:limb_t
  -> len:size_t{v len > 0}
  -> a:lbignum t len ->
  Stack (limb t)
  (requires fun h -> live h a)
  (ensures  fun h0 r h1 -> modifies0 h0 h1 /\
    r == S.bn_is_odd (as_seq h0 a))


inline_for_extraction noextract
let bn_eq_mask_st (t:limb_t) (len:size_t) =
    a:lbignum t len
  -> b:lbignum t len ->
  Stack (limb t)
  (requires fun h -> live h a /\ live h b)
  (ensures  fun h0 r h1 -> modifies0 h0 h1 /\
    r == S.bn_eq_mask (as_seq h0 a) (as_seq h0 b))


inline_for_extraction noextract
val bn_eq_mask: #t:limb_t -> len:size_t -> bn_eq_mask_st t len


inline_for_extraction noextract
val bn_is_zero_mask:
    #t:limb_t
  -> len:size_t{v len > 0}
  -> a:lbignum t len ->
  Stack (limb t)
  (requires fun h -> live h a)
  (ensures  fun h0 r h1 -> modifies0 h0 h1 /\
    r == S.bn_is_zero_mask (as_seq h0 a))


inline_for_extraction noextract
val bn_lt_mask_st : t: Hacl.Bignum.Definitions.limb_t -> len: Lib.IntTypes.size_t -> Type0
t: Hacl.Bignum.Definitions.limb_t -> len: Lib.IntTypes.size_t -> Type0