val poly1305_update (text: bytes) (acc r: felem) : Tot felem
let poly1305_update (text:bytes) (acc:felem) (r:felem) : Tot felem =
  repeat_blocks #uint8 #felem size_block text
    (poly1305_update1 r size_block)
    (poly1305_update_last r)
  acc
val poly1305_update (text: bytes) (acc r: felem) : Tot felem
let poly1305_update (text: bytes) (acc r: felem) : Tot felem =
repeat_blocks #uint8
  #felem
  size_block
  text
  (poly1305_update1 r size_block)
  (poly1305_update_last r)
  acc
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
Lib.ByteSequence.bytes Spec.Poly1305.felem Lib.Sequence.repeat_blocks Lib.IntTypes.uint8 Spec.Poly1305.size_block Spec.Poly1305.poly1305_update1 Spec.Poly1305.poly1305_update_last
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)

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

let poly1305_finish (k:key) (acc:felem) : Tot tag =
  let s = nat_from_bytes_le (slice k 16 32) in
  let n = (from_felem acc + s) % pow2 128 in
  nat_to_bytes_le 16 n

let poly1305_update_last (r:felem) (l:size_nat{l < 16}) (b:lbytes l) (acc:felem) =
  if l = 0 then acc else poly1305_update1 r l b acc
val poly1305_update (text: bytes) (acc r: felem) : Tot felem
text: Lib.ByteSequence.bytes -> acc: Spec.Poly1305.felem -> r: Spec.Poly1305.felem
  -> Spec.Poly1305.felem