val poly1305_update1 (r: felem) (len: size_nat{len <= size_block}) (b: lbytes len) (acc: felem)
    : Tot felem
let poly1305_update1 (r:felem) (len:size_nat{len <= size_block}) (b:lbytes len) (acc:felem) : Tot felem =
  (encode len b `fadd` acc) `fmul` r
val poly1305_update1 (r: felem) (len: size_nat{len <= size_block}) (b: lbytes len) (acc: felem)
    : Tot felem
let poly1305_update1 (r: felem) (len: size_nat{len <= size_block}) (b: lbytes len) (acc: felem)
    : Tot felem =
((encode len b) `fadd` acc) `fmul` r
Spec.Poly1305.fst.checked
prims.fst.checked Lib.Sequence.fsti.checked Lib.IntTypes.fsti.checked Lib.ByteSequence.fsti.checked FStar.Pervasives.Native.fst.checked FStar.Pervasives.fsti.checked FStar.Mul.fst.checked FStar.Math.Lemmas.fst.checked
Spec.Poly1305.fst
Spec.Poly1305.felem Lib.IntTypes.size_nat Prims.b2t Prims.op_LessThanOrEqual Spec.Poly1305.size_block Lib.ByteSequence.lbytes Spec.Poly1305.fmul Spec.Poly1305.fadd Spec.Poly1305.encode
module Spec.Poly1305

#reset-options "--z3rlimit 60 --initial_fuel 0 --max_fuel 0 --initial_ifuel 0 --max_ifuel 0"

open FStar.Mul

open Lib.IntTypes
open Lib.Sequence
open Lib.ByteSequence

/// Constants and Types

(* Field types and parameters *)
let prime : pos =
  let p = pow2 130 - 5 in
  assert_norm (p > 0);
  p

let felem = x:nat{x < prime}
let fadd (x:felem) (y:felem) : felem = (x + y) % prime
let fmul (x:felem) (y:felem) : felem = (x * y) % prime

let to_felem (x:nat{x < prime}) : felem = x
let from_felem (x:felem) : nat = x
let zero : felem = to_felem 0

(* Poly1305 parameters *)
let size_block : size_nat = 16
let size_key   : size_nat = 32

type block = lbytes size_block
type tag   = lbytes size_block
type key   = lbytes size_key

/// Specification

let poly1305_encode_r (rb:block) : Tot felem =
  let lo = uint_from_bytes_le (sub rb 0 8) in
  let hi = uint_from_bytes_le (sub rb 8 8) in
  let mask0 = u64 0x0ffffffc0fffffff in
  let mask1 = u64 0x0ffffffc0ffffffc in
  let lo = lo &. mask0 in
  let hi = hi &. mask1 in
  assert_norm (pow2 128 < prime);
  to_felem (uint_v hi * pow2 64 + uint_v lo)

let poly1305_init (k:key) : Tot (felem & felem) =
  let r = poly1305_encode_r (slice k 0 16) in
  zero, r

let encode (len:size_nat{len <= size_block}) (b:lbytes len) : Tot felem =
  Math.Lemmas.pow2_le_compat 128 (8 * len);
  assert_norm (pow2 128 + pow2 128 < prime);
  fadd (pow2 (8 * len)) (nat_from_bytes_le b)
val poly1305_update1 (r: felem) (len: size_nat{len <= size_block}) (b: lbytes len) (acc: felem)
    : Tot felem

    r: Spec.Poly1305.felem ->
    len: Lib.IntTypes.size_nat{len <= Spec.Poly1305.size_block} ->
    b: Lib.ByteSequence.lbytes len ->
    acc: Spec.Poly1305.felem
  -> Spec.Poly1305.felem