https://github.com/project-everest/hacl-star
Raw File
Tip revision: 2ca2d4dce9eb1dc7ac015a04cd6af6ac0e926e59 authored by Santiago Zanella-Beguelin on 17 July 2018, 13:56:19 UTC
WIP replicating LowStar library in Lib
Tip revision: 2ca2d4d
Spec.Chacha20_vec1.Lemmas.fst
module Spec.Chacha20_vec1.Lemmas

open FStar.HyperStack.All

module ST = FStar.HyperStack.ST

open FStar.Mul
open FStar.Seq
open FStar.UInt32
open FStar.Endianness
open Spec.Lib

open Seq.Create
open Spec.Chacha20
open Spec.Chacha20_vec

module S = Spec.Chacha20
module V = Spec.Chacha20_vec

let state = S.state
let vec_state = V.state


#set-options "--max_fuel 0 --z3rlimit 100"


let state_to_vec_state (s:state) : Tot vec_state =
  let s0 = slice s 0  4  in
  let s1 = slice s 4  8  in
  let s2 = slice s 8  12 in
  let s3 = slice s 12 16 in
  create_4 s0 s1 s2 s3


let vec_state_to_state (s:vec_state) : Tot state =
  let s0 = index s 0 in
  let s1 = index s 1 in
  let s2 = index s 2 in
  let s3 = index s 3 in
  s0 @| s1 @| s2 @| s3


val lemma_state (s:state) : Lemma (vec_state_to_state (state_to_vec_state s) == s)
let lemma_state s =
  let s0 = slice s 0  4  in
  let s1 = slice s 4  8  in
  let s2 = slice s 8  12 in
  let s3 = slice s 12 16 in
  lemma_eq_intro (s0 @| s1 @| s2 @| s3) s


val lemma_vec_state (s:vec_state) : Lemma (state_to_vec_state (vec_state_to_state s) == s)
let lemma_vec_state s =
  let s0 = index s 0 in
  let s1 = index s 1 in
  let s2 = index s 2 in
  let s3 = index s 3 in
  let s' = s0 @| s1 @| s2 @| s3 in
  lemma_eq_intro (slice s' 0   4) s0;
  lemma_eq_intro (slice s' 4   8) s1;
  lemma_eq_intro (slice s' 8  12) s2;
  lemma_eq_intro (slice s' 12 16) s3;
  lemma_eq_intro (create_4 s0 s1 s2 s3) (s)


unfold let eq_states' (s:state) (s':vec_state) : GTot Type0 =
   let s0' = index (index s' 0) 0 in   let s1' = index (index s' 0) 1 in
   let s2' = index (index s' 0) 2 in   let s3' = index (index s' 0) 3 in
   let s4' = index (index s' 1) 0 in   let s5' = index (index s' 1) 1 in
   let s6' = index (index s' 1) 2 in   let s7' = index (index s' 1) 3 in
   let s8' = index (index s' 2) 0 in   let s9' = index (index s' 2) 1 in
   let s10' = index (index s' 2) 2 in  let s11' = index (index s' 2) 3 in
   let s12' = index (index s' 3) 0 in  let s13' = index (index s' 3) 1 in
   let s14' = index (index s' 3) 2 in  let s15' = index (index s' 3) 3 in
   let s0 = index s 0 in   let s1 = index s 1 in
   let s2 = index s 2 in   let s3 = index s 3 in
   let s4 = index s 4 in   let s5 = index s 5 in
   let s6 = index s 6 in   let s7 = index s 7 in
   let s8 = index s 8 in   let s9 = index s 9 in
   let s10 = index s 10 in  let s11 = index s 11 in
   let s12 = index s 12 in  let s13 = index s 13 in
   let s14 = index s 14 in  let s15 = index s 15 in
   s0' == s0 /\ s1' == s1 /\ s2' == s2 /\ s3' == s3
   /\ s4' == s4 /\ s5' == s5 /\ s6' == s6 /\ s7' == s7
   /\ s8' == s8 /\ s9' == s9 /\ s10' == s10 /\ s11' == s11
   /\ s12' == s12 /\ s13' == s13 /\ s14' == s14 /\ s15' == s15


let eq_states (s:state) (s':vec_state) : GTot Type0 =
  vec_state_to_state s' == s


val lemma_eq_states_intro: s:state -> s':vec_state -> Lemma
  (requires (eq_states' s s'))
  (ensures (eq_states s s'))
let lemma_eq_states_intro s s' =
  lemma_eq_intro (vec_state_to_state s') s


let quarter_round_vec (s:vec_state) : Tot vec_state =
  let s = V.line 0 1 3 16ul s in
  let s = V.line 2 3 1 12ul s in
  let s = V.line 0 1 3 8ul  s in
  let s = V.line 2 3 1 7ul  s in
  s


let lined (a:t) (b:t) (c:t) (d:t) (a1:t) (b1:t) (c1:t) (d1:t) : GTot Type0 =
  let open FStar.UInt32 in
  let a' = a +%^ b in
  let d' = Spec.Lib.rotate_left (d ^^ a') 16ul in
  let c' = c +%^ d' in
  let b' = Spec.Lib.rotate_left (b ^^ c') 12ul in
  let a'' = a' +%^ b' in
  let d'' = Spec.Lib.rotate_left (d' ^^ a'') 8ul in
  let c'' = c' +%^ d'' in
  let b'' = Spec.Lib.rotate_left (b' ^^ c'') 7ul in
  a1 == a'' /\ b1 == b'' /\ c1 == c'' /\ d1 == d''


val line_: a:S.idx -> b:S.idx -> d:S.idx -> ss:UInt32.t {v ss > 0 /\ v ss < 32} -> s:state -> Tot (s':state{s' == S.line a b d ss s})
let line_ a b d s m =
  let open FStar.UInt32 in
  let m = upd m a (index m a +%^ index m b) in
  let m = upd m d (Spec.Lib.rotate_left (index m d ^^  index m a) s) in
  m


#reset-options "--max_fuel 0 --z3rlimit 500"

let new_line (s:state) (a:S.idx) (b:S.idx) (d:S.idx{a <> b /\ a <> d /\ b <> d}) (ss:UInt32.t{v ss > 0 /\ v ss < 32}) : Tot (s':state{
  let sa = index s a in let sb = index s b in let sd = index s d in
  let sa' = index s' a in let sb' = index s' b in let sd' = index s' d in
  let open FStar.UInt32 in
  let a' = sa +%^ sb in
  let d' = Spec.Lib.rotate_left (sd ^^ a') ss in
  sa' == a' /\ sd' == d'
  /\ (forall (i:nat). {:pattern (index s' i)} (i < 16 /\ i <> a /\ i <> d) ==> index s' i == index s i)
  })
  = line_ a b d ss s


let quarter_round_standard
  (s:state) (a:S.idx) (b:S.idx) (c:S.idx) (d:S.idx{a <> b /\ a <> c /\ a <> d /\ b <> c /\ b <> d /\ c <> d}) :
  Tot (s':state{s' == S.quarter_round a b c d s}) =
  let s = new_line s a b d 16ul in
  let s = new_line s c d b 12ul in
  let s = new_line s a b d 8ul  in
  let s = new_line s c d b 7ul  in
  s


#reset-options "--max_fuel 0 --z3rlimit 500"

let lemma_quarter_round_standard
  (s:state) (a:S.idx) (b:S.idx) (c:S.idx) (d:S.idx{a <> b /\ a <> c /\ a <> d /\ b <> c /\ b <> d /\ c <> d}) :
  Lemma
  (let s' = quarter_round_standard s a b c d in
   let sa = index s a in let sb = index s b in let sc = index s c in let sd = index s d in
   let sa' = index s' a in let sb' = index s' b in let sc' = index s' c in let sd' = index s' d in
   lined sa sb sc sd sa' sb' sc' sd'
   /\ (forall (i:nat). {:pattern (Seq.index s' i)} (i < 16 /\ i <> a /\ i <> b /\ i <> c /\ i <> d
     ==> index s' i == index s i)))
   = ()


#reset-options "--max_fuel 0 --z3rlimit 1000"

let lemma_quarter_round_vectorized (s:vec_state) : Lemma
  (let s' = quarter_round_vec s in
   let s0 = index (index s 0) 0 in   let s1 = index (index s 0) 1 in
   let s2 = index (index s 0) 2 in   let s3 = index (index s 0) 3 in
   let s4 = index (index s 1) 0 in   let s5 = index (index s 1) 1 in
   let s6 = index (index s 1) 2 in   let s7 = index (index s 1) 3 in
   let s8 = index (index s 2) 0 in   let s9 = index (index s 2) 1 in
   let s10 = index (index s 2) 2 in  let s11 = index (index s 2) 3 in
   let s12 = index (index s 3) 0 in  let s13 = index (index s 3) 1 in
   let s14 = index (index s 3) 2 in  let s15 = index (index s 3) 3 in
   let s0' = index (index s' 0) 0 in   let s1' = index (index s' 0) 1 in
   let s2' = index (index s' 0) 2 in   let s3' = index (index s' 0) 3 in
   let s4' = index (index s' 1) 0 in   let s5' = index (index s' 1) 1 in
   let s6' = index (index s' 1) 2 in   let s7' = index (index s' 1) 3 in
   let s8' = index (index s' 2) 0 in   let s9' = index (index s' 2) 1 in
   let s10' = index (index s' 2) 2 in  let s11' = index (index s' 2) 3 in
   let s12' = index (index s' 3) 0 in  let s13' = index (index s' 3) 1 in
   let s14' = index (index s' 3) 2 in  let s15' = index (index s' 3) 3 in
   lined s0 s4 s8 s12 s0' s4' s8' s12'
   /\ lined s1 s5 s9 s13 s1' s5' s9' s13'
   /\ lined s2 s6 s10 s14 s2' s6' s10' s14'
   /\ lined s3 s7 s11 s15 s3' s7' s11' s15')
   = ()


#reset-options "--max_fuel 0 --z3rlimit 5"

val lemma_forall_elim: p:(nat -> Type) ->  q:(nat -> Type) -> j:nat{p j} ->
  Lemma (requires (forall (i:nat). p i ==> q i))
        (ensures (q j))
let lemma_forall_elim p q j = ()

val lemma_column_round_standard_1: s:state -> s':state -> Lemma
  (requires ((forall (i:nat). {:pattern (index s' i)} (i < 16 /\ i <> 0 /\ i <> 4 /\ i <> 8 /\ i <> 12)  ==> index s' i == index s i)))
  (ensures (
   let s0 = index s 0 in   let s1 = index s 1 in
   let s2 = index s 2 in   let s3 = index s 3 in
   let s4 = index s 4 in   let s5 = index s 5 in
   let s6 = index s 6 in   let s7 = index s 7 in
   let s8 = index s 8 in   let s9 = index s 9 in
   let s10 = index s 10 in  let s11 = index s 11 in
   let s12 = index s 12 in  let s13 = index s 13 in
   let s14 = index s 14 in  let s15 = index s 15 in
   let s0' = index s' 0 in   let s1' = index s' 1 in
   let s2' = index s' 2 in   let s3' = index s' 3 in
   let s4' = index s' 4 in   let s5' = index s' 5 in
   let s6' = index s' 6 in   let s7' = index s' 7 in
   let s8' = index s' 8 in   let s9' = index s' 9 in
   let s10' = index s' 10 in  let s11' = index s' 11 in
   let s12' = index s' 12 in  let s13' = index s' 13 in
   let s14' = index s' 14 in  let s15' = index s' 15 in
   s1 == s1' /\ s2 == s2' /\ s3 == s3'
   /\ s5 == s5' /\ s6 == s6' /\ s7 == s7'
   /\ s9 == s9' /\ s10 == s10' /\ s11 == s11'
   /\ s13 == s13' /\ s14 == s14' /\ s15 == s15'))
let lemma_column_round_standard_1 s s' =
  let p = fun (i:nat) -> (i < 16 /\ i <> 0 /\ i <> 4 /\ i <> 8 /\ i <> 12) in
  let q = fun (i:nat) -> (i < 16 /\ index s' i == index s i) in
  lemma_forall_elim p q 1;
  lemma_forall_elim p q 2;
  lemma_forall_elim p q 3;
  lemma_forall_elim p q 5;
  lemma_forall_elim p q 6;
  lemma_forall_elim p q 7;
  lemma_forall_elim p q 9;
  lemma_forall_elim p q 10;
  lemma_forall_elim p q 11;
  lemma_forall_elim p q 13;
  lemma_forall_elim p q 14;
  lemma_forall_elim p q 15


val column_round_standard_1: s:state -> Tot (s':state{
  (let s'' = S.quarter_round 0 4 8 12 s in
   let s0 = index s 0 in   let s1 = index s 1 in
   let s2 = index s 2 in   let s3 = index s 3 in
   let s4 = index s 4 in   let s5 = index s 5 in
   let s6 = index s 6 in   let s7 = index s 7 in
   let s8 = index s 8 in   let s9 = index s 9 in
   let s10 = index s 10 in  let s11 = index s 11 in
   let s12 = index s 12 in  let s13 = index s 13 in
   let s14 = index s 14 in  let s15 = index s 15 in
   let s0' = index s' 0 in   let s1' = index s' 1 in
   let s2' = index s' 2 in   let s3' = index s' 3 in
   let s4' = index s' 4 in   let s5' = index s' 5 in
   let s6' = index s' 6 in   let s7' = index s' 7 in
   let s8' = index s' 8 in   let s9' = index s' 9 in
   let s10' = index s' 10 in  let s11' = index s' 11 in
   let s12' = index s' 12 in  let s13' = index s' 13 in
   let s14' = index s' 14 in  let s15' = index s' 15 in
   lined s0 s4 s8 s12 s0' s4' s8' s12'
   /\ s'' == s'
   /\ s1 == s1' /\ s2 == s2' /\ s3 == s3'
   /\ s5 == s5' /\ s6 == s6' /\ s7 == s7'
   /\ s9 == s9' /\ s10 == s10' /\ s11 == s11'
   /\ s13 == s13' /\ s14 == s14' /\ s15 == s15')})
let column_round_standard_1 s =
  lemma_quarter_round_standard s 0 4 8 12;
  let s' = quarter_round_standard s 0 4 8 12 in
  cut (lined (index s 0) (index s 4) (index s 8) (index s 12) (index s' 0) (index s' 4) (index s' 8) (index s' 12));
  lemma_column_round_standard_1 s s';
  s'


val lemma_column_round_standard_2: s:state -> s':state -> Lemma
  (requires ((forall (i:nat). {:pattern (index s' i)} (i < 16 /\ i <> 1 /\ i <> 5 /\ i <> 9 /\ i <> 13)  ==> index s' i == index s i)))
  (ensures (
   let s0 = index s 0 in   let s1 = index s 1 in
   let s2 = index s 2 in   let s3 = index s 3 in
   let s4 = index s 4 in   let s5 = index s 5 in
   let s6 = index s 6 in   let s7 = index s 7 in
   let s8 = index s 8 in   let s9 = index s 9 in
   let s10 = index s 10 in  let s11 = index s 11 in
   let s12 = index s 12 in  let s13 = index s 13 in
   let s14 = index s 14 in  let s15 = index s 15 in
   let s0' = index s' 0 in   let s1' = index s' 1 in
   let s2' = index s' 2 in   let s3' = index s' 3 in
   let s4' = index s' 4 in   let s5' = index s' 5 in
   let s6' = index s' 6 in   let s7' = index s' 7 in
   let s8' = index s' 8 in   let s9' = index s' 9 in
   let s10' = index s' 10 in  let s11' = index s' 11 in
   let s12' = index s' 12 in  let s13' = index s' 13 in
   let s14' = index s' 14 in  let s15' = index s' 15 in
   s0 == s0' /\ s2 == s2' /\ s3 == s3'
   /\ s4 == s4' /\ s6 == s6' /\ s7 == s7'
   /\ s8 == s8' /\ s10 == s10' /\ s11 == s11'
   /\ s12 == s12' /\ s14 == s14' /\ s15 == s15'))
let lemma_column_round_standard_2 s s' =
  let p = fun (i:nat) -> (i < 16 /\ i <> 1 /\ i <> 5 /\ i <> 9 /\ i <> 13) in
  let q = fun (i:nat) -> (i < 16 /\ index s' i == index s i) in
  lemma_forall_elim p q 0;
  lemma_forall_elim p q 2;
  lemma_forall_elim p q 3;
  lemma_forall_elim p q 4;
  lemma_forall_elim p q 6;
  lemma_forall_elim p q 7;
  lemma_forall_elim p q 8;
  lemma_forall_elim p q 10;
  lemma_forall_elim p q 11;
  lemma_forall_elim p q 12;
  lemma_forall_elim p q 14;
  lemma_forall_elim p q 15


val column_round_standard_2: s:state -> Tot (s':state{
  (let s'' = S.quarter_round 0 4 8 12 s in
   let s'' = S.quarter_round 1 5 9 13 s'' in
   let s0 = index s 0 in   let s1 = index s 1 in
   let s2 = index s 2 in   let s3 = index s 3 in
   let s4 = index s 4 in   let s5 = index s 5 in
   let s6 = index s 6 in   let s7 = index s 7 in
   let s8 = index s 8 in   let s9 = index s 9 in
   let s10 = index s 10 in  let s11 = index s 11 in
   let s12 = index s 12 in  let s13 = index s 13 in
   let s14 = index s 14 in  let s15 = index s 15 in
   let s0' = index s' 0 in   let s1' = index s' 1 in
   let s2' = index s' 2 in   let s3' = index s' 3 in
   let s4' = index s' 4 in   let s5' = index s' 5 in
   let s6' = index s' 6 in   let s7' = index s' 7 in
   let s8' = index s' 8 in   let s9' = index s' 9 in
   let s10' = index s' 10 in  let s11' = index s' 11 in
   let s12' = index s' 12 in  let s13' = index s' 13 in
   let s14' = index s' 14 in  let s15' = index s' 15 in
   lined s0 s4 s8 s12 s0' s4' s8' s12'
   /\ s'' == s'
   /\ lined s1 s5 s9 s13 s1' s5' s9' s13'
   /\ s2 == s2' /\ s3 == s3'
   /\ s6 == s6' /\ s7 == s7'
   /\ s10 == s10' /\ s11 == s11'
   /\ s14 == s14' /\ s15 == s15')})
let column_round_standard_2 s =
  let s' = column_round_standard_1 s in
  lemma_quarter_round_standard s' 1 5 9 13;
  let s'' = quarter_round_standard s' 1 5 9 13 in
  lemma_column_round_standard_2 s' s'';
  s''


val lemma_column_round_standard_3: s:state -> s':state -> Lemma
  (requires ((forall (i:nat). {:pattern (index s' i)} (i < 16 /\ i <> 2 /\ i <> 6 /\ i <> 10 /\ i <> 14)  ==> index s' i == index s i)))
  (ensures (
   let s0 = index s 0 in   let s1 = index s 1 in
   let s2 = index s 2 in   let s3 = index s 3 in
   let s4 = index s 4 in   let s5 = index s 5 in
   let s6 = index s 6 in   let s7 = index s 7 in
   let s8 = index s 8 in   let s9 = index s 9 in
   let s10 = index s 10 in  let s11 = index s 11 in
   let s12 = index s 12 in  let s13 = index s 13 in
   let s14 = index s 14 in  let s15 = index s 15 in
   let s0' = index s' 0 in   let s1' = index s' 1 in
   let s2' = index s' 2 in   let s3' = index s' 3 in
   let s4' = index s' 4 in   let s5' = index s' 5 in
   let s6' = index s' 6 in   let s7' = index s' 7 in
   let s8' = index s' 8 in   let s9' = index s' 9 in
   let s10' = index s' 10 in  let s11' = index s' 11 in
   let s12' = index s' 12 in  let s13' = index s' 13 in
   let s14' = index s' 14 in  let s15' = index s' 15 in
   s0 == s0' /\ s1 == s1' /\ s3 == s3'
   /\ s4 == s4' /\ s5 == s5' /\ s7 == s7'
   /\ s8 == s8' /\ s9 == s9' /\ s11 == s11'
   /\ s12 == s12' /\ s13 == s13' /\ s15 == s15'))
let lemma_column_round_standard_3 s s' =
  let p = fun (i:nat) -> (i < 16 /\ i <> 2 /\ i <> 6 /\ i <> 10 /\ i <> 14) in
  let q = fun (i:nat) -> (i < 16 /\ index s' i == index s i) in
  lemma_forall_elim p q 0;
  lemma_forall_elim p q 1;
  lemma_forall_elim p q 3;
  lemma_forall_elim p q 4;
  lemma_forall_elim p q 5;
  lemma_forall_elim p q 7;
  lemma_forall_elim p q 8;
  lemma_forall_elim p q 9;
  lemma_forall_elim p q 11;
  lemma_forall_elim p q 12;
  lemma_forall_elim p q 13;
  lemma_forall_elim p q 15


val column_round_standard_3: s:state -> Tot (s':state{
  (let s'' = S.quarter_round 0 4 8 12 s in
   let s'' = S.quarter_round 1 5 9 13 s'' in
   let s'' = S.quarter_round 2 6 10 14 s'' in
   let s0 = index s 0 in   let s1 = index s 1 in
   let s2 = index s 2 in   let s3 = index s 3 in
   let s4 = index s 4 in   let s5 = index s 5 in
   let s6 = index s 6 in   let s7 = index s 7 in
   let s8 = index s 8 in   let s9 = index s 9 in
   let s10 = index s 10 in  let s11 = index s 11 in
   let s12 = index s 12 in  let s13 = index s 13 in
   let s14 = index s 14 in  let s15 = index s 15 in
   let s0' = index s' 0 in   let s1' = index s' 1 in
   let s2' = index s' 2 in   let s3' = index s' 3 in
   let s4' = index s' 4 in   let s5' = index s' 5 in
   let s6' = index s' 6 in   let s7' = index s' 7 in
   let s8' = index s' 8 in   let s9' = index s' 9 in
   let s10' = index s' 10 in  let s11' = index s' 11 in
   let s12' = index s' 12 in  let s13' = index s' 13 in
   let s14' = index s' 14 in  let s15' = index s' 15 in
   lined s0 s4 s8 s12 s0' s4' s8' s12'
   /\ lined s1 s5 s9 s13 s1' s5' s9' s13'
   /\ lined s2 s6 s10 s14 s2' s6' s10' s14'
   /\ s'' == s'
   /\ s3 == s3'
   /\ s7 == s7'
   /\ s11 == s11'
   /\ s15 == s15')})
let column_round_standard_3 s =
  let s'' = column_round_standard_2 s in
  lemma_quarter_round_standard s'' 2 6 10 14;
  let s''' = quarter_round_standard s'' 2 6 10 14 in
  lemma_column_round_standard_3 s'' s''';
  s'''


val lemma_column_round_standard_4: s:state -> s':state -> Lemma
  (requires ((forall (i:nat). {:pattern (index s' i)} (i < 16 /\ i <> 3 /\ i <> 7 /\ i <> 11 /\ i <> 15)  ==> index s' i == index s i)))
  (ensures (
   let s0 = index s 0 in   let s1 = index s 1 in
   let s2 = index s 2 in   let s3 = index s 3 in
   let s4 = index s 4 in   let s5 = index s 5 in
   let s6 = index s 6 in   let s7 = index s 7 in
   let s8 = index s 8 in   let s9 = index s 9 in
   let s10 = index s 10 in  let s11 = index s 11 in
   let s12 = index s 12 in  let s13 = index s 13 in
   let s14 = index s 14 in  let s15 = index s 15 in
   let s0' = index s' 0 in   let s1' = index s' 1 in
   let s2' = index s' 2 in   let s3' = index s' 3 in
   let s4' = index s' 4 in   let s5' = index s' 5 in
   let s6' = index s' 6 in   let s7' = index s' 7 in
   let s8' = index s' 8 in   let s9' = index s' 9 in
   let s10' = index s' 10 in  let s11' = index s' 11 in
   let s12' = index s' 12 in  let s13' = index s' 13 in
   let s14' = index s' 14 in  let s15' = index s' 15 in
   s0 == s0' /\ s1 == s1' /\ s2 == s2'
   /\ s4 == s4' /\ s5 == s5' /\ s6 == s6'
   /\ s8 == s8' /\ s9 == s9' /\ s10 == s10'
   /\ s12 == s12' /\ s13 == s13' /\ s14 == s14'))
let lemma_column_round_standard_4 s s' =
  let p = fun (i:nat) -> (i < 16 /\ i <> 3 /\ i <> 7 /\ i <> 11 /\ i <> 15) in
  let q = fun (i:nat) -> (i < 16 /\ index s' i == index s i) in
  lemma_forall_elim p q 0;
  lemma_forall_elim p q 1;
  lemma_forall_elim p q 2;
  lemma_forall_elim p q 4;
  lemma_forall_elim p q 5;
  lemma_forall_elim p q 6;
  lemma_forall_elim p q 8;
  lemma_forall_elim p q 9;
  lemma_forall_elim p q 10;
  lemma_forall_elim p q 12;
  lemma_forall_elim p q 13;
  lemma_forall_elim p q 14


#reset-options "--max_fuel 0"

val lemma_column_round_def: s:state -> Lemma
  (let s' = S.quarter_round 0 4 8 12 s in
   let s' = S.quarter_round 1 5 9 13 s' in
   let s' = S.quarter_round 2 6 10 14 s' in
   let s' = S.quarter_round 3 7 11 15 s' in
   s' == S.column_round s)
let lemma_column_round_def s = ()

val column_round_standard: s:state -> Tot (s':state{
  (s' == S.column_round s
   /\ (let s0 = index s 0 in   let s1 = index s 1 in
   let s2 = index s 2 in   let s3 = index s 3 in
   let s4 = index s 4 in   let s5 = index s 5 in
   let s6 = index s 6 in   let s7 = index s 7 in
   let s8 = index s 8 in   let s9 = index s 9 in
   let s10 = index s 10 in  let s11 = index s 11 in
   let s12 = index s 12 in  let s13 = index s 13 in
   let s14 = index s 14 in  let s15 = index s 15 in
   let s0' = index s' 0 in   let s1' = index s' 1 in
   let s2' = index s' 2 in   let s3' = index s' 3 in
   let s4' = index s' 4 in   let s5' = index s' 5 in
   let s6' = index s' 6 in   let s7' = index s' 7 in
   let s8' = index s' 8 in   let s9' = index s' 9 in
   let s10' = index s' 10 in  let s11' = index s' 11 in
   let s12' = index s' 12 in  let s13' = index s' 13 in
   let s14' = index s' 14 in  let s15' = index s' 15 in
   lined s0 s4 s8 s12 s0' s4' s8' s12'
   /\ lined s1 s5 s9 s13 s1' s5' s9' s13'
   /\ lined s2 s6 s10 s14 s2' s6' s10' s14'
   /\ lined s3 s7 s11 s15 s3' s7' s11' s15'))})
let column_round_standard s =
  let s''' = column_round_standard_3 s in
  lemma_quarter_round_standard s''' 3 7 11 15;
  let s'''' = quarter_round_standard s''' 3 7 11 15 in
  lemma_column_round_standard_4 s''' s'''';
  lemma_column_round_def s;
  s''''


#reset-options "--max_fuel 0 --z3rlimit 100"

val shuffle_right: x:vec -> n:V.idx -> Tot (x':vec{
  index x' 0 == index x (n % 4)
  /\ index x' 1 == index x ((n+1) % 4)
  /\ index x' 2 == index x ((n+2) % 4)
  /\ index x' 3 == index x ((n+3) % 4)})
let shuffle_right x n =
  V.shuffle_right x n


#reset-options "--max_fuel 0 --z3rlimit 100"

val shuffle_rous_1: s:vec_state -> Tot (s':vec_state{
   let s0' = index (index s' 0) 0 in   let s1' = index (index s' 0) 1 in
   let s2' = index (index s' 0) 2 in   let s3' = index (index s' 0) 3 in
   let s4' = index (index s' 1) 0 in   let s5' = index (index s' 1) 1 in
   let s6' = index (index s' 1) 2 in   let s7' = index (index s' 1) 3 in
   let s8' = index (index s' 2) 0 in   let s9' = index (index s' 2) 1 in
   let s10' = index (index s' 2) 2 in  let s11' = index (index s' 2) 3 in
   let s12' = index (index s' 3) 0 in  let s13' = index (index s' 3) 1 in
   let s14' = index (index s' 3) 2 in  let s15' = index (index s' 3) 3 in
   let s0 = index (index s 0) 0 in   let s1 = index (index s 0) 1 in
   let s2 = index (index s 0) 2 in   let s3 = index (index s 0) 3 in
   let s4 = index (index s 1) 0 in   let s5 = index (index s 1) 1 in
   let s6 = index (index s 1) 2 in   let s7 = index (index s 1) 3 in
   let s8 = index (index s 2) 0 in   let s9 = index (index s 2) 1 in
   let s10 = index (index s 2) 2 in  let s11 = index (index s 2) 3 in
   let s12 = index (index s 3) 0 in  let s13 = index (index s 3) 1 in
   let s14 = index (index s 3) 2 in  let s15 = index (index s 3) 3 in
   s0 == s0' /\ s1 == s1' /\ s2 == s2' /\ s3 == s3'
   /\ s4' == s5 /\ s5' == s6 /\ s6' == s7 /\ s7' == s4
   /\ s8' == s10 /\ s9' == s11 /\ s10' == s8 /\ s11' == s9
   /\ s12' == s15 /\ s13' == s12 /\ s14' == s13 /\ s15' == s14
   /\ (let s'' = V.shuffle_row 1 1 s in
     let s'' = V.shuffle_row 2 2 s'' in
     let s'' = V.shuffle_row 3 3 s'' in
     s'' == s')
   })
let shuffle_rous_1 s =
  let s' = shuffle_row 1 1 s in
  lemma_eq_intro (shuffle_right (index s 1) 1) (index s' 1);
  lemma_eq_intro (index s 0) (index s' 0);
  lemma_eq_intro (index s 2) (index s' 2);
  lemma_eq_intro (index s 3) (index s' 3);
  let s'' = shuffle_row 2 2 s' in
  lemma_eq_intro (shuffle_right (index s' 2) 2) (index s'' 2);
  lemma_eq_intro (index s' 0) (index s'' 0);
  lemma_eq_intro (index s' 1) (index s'' 1);
  lemma_eq_intro (index s' 3) (index s'' 3);
  let s''' = shuffle_row 3 3 s'' in
  lemma_eq_intro (shuffle_right (index s'' 3) 3) (index s''' 3);
  lemma_eq_intro (index s'' 0) (index s''' 0);
  lemma_eq_intro (index s'' 1) (index s''' 1);
  lemma_eq_intro (index s'' 2) (index s''' 2);
  s'''


val shuffle_rous_2: s:vec_state -> Tot (s':vec_state{
   let s0' = index (index s' 0) 0 in   let s1' = index (index s' 0) 1 in
   let s2' = index (index s' 0) 2 in   let s3' = index (index s' 0) 3 in
   let s4' = index (index s' 1) 0 in   let s5' = index (index s' 1) 1 in
   let s6' = index (index s' 1) 2 in   let s7' = index (index s' 1) 3 in
   let s8' = index (index s' 2) 0 in   let s9' = index (index s' 2) 1 in
   let s10' = index (index s' 2) 2 in  let s11' = index (index s' 2) 3 in
   let s12' = index (index s' 3) 0 in  let s13' = index (index s' 3) 1 in
   let s14' = index (index s' 3) 2 in  let s15' = index (index s' 3) 3 in
   let s0 = index (index s 0) 0 in   let s1 = index (index s 0) 1 in
   let s2 = index (index s 0) 2 in   let s3 = index (index s 0) 3 in
   let s4 = index (index s 1) 0 in   let s5 = index (index s 1) 1 in
   let s6 = index (index s 1) 2 in   let s7 = index (index s 1) 3 in
   let s8 = index (index s 2) 0 in   let s9 = index (index s 2) 1 in
   let s10 = index (index s 2) 2 in  let s11 = index (index s 2) 3 in
   let s12 = index (index s 3) 0 in  let s13 = index (index s 3) 1 in
   let s14 = index (index s 3) 2 in  let s15 = index (index s 3) 3 in
   s0 == s0' /\ s1 == s1' /\ s2 == s2' /\ s3 == s3'
   /\ s4' == s7 /\ s5' == s4 /\ s6' == s5 /\ s7' == s6
   /\ s8' == s10 /\ s9' == s11 /\ s10' == s8 /\ s11' == s9
   /\ s12' == s13 /\ s13' == s14 /\ s14' == s15 /\ s15' == s12
   /\ (let s'' = V.shuffle_row 1 3 s in
     let s'' = V.shuffle_row 2 2 s'' in
     let s'' = V.shuffle_row 3 1 s'' in
     s'' == s')
   })
let shuffle_rous_2 s =
  let s' = shuffle_row 1 3 s in
  lemma_eq_intro (shuffle_right (index s 1) 3) (index s' 1);
  lemma_eq_intro (index s 0) (index s' 0);
  lemma_eq_intro (index s 2) (index s' 2);
  lemma_eq_intro (index s 3) (index s' 3);
  let s'' = shuffle_row 2 2 s' in
  lemma_eq_intro (shuffle_right (index s' 2) 2) (index s'' 2);
  lemma_eq_intro (index s' 0) (index s'' 0);
  lemma_eq_intro (index s' 1) (index s'' 1);
  lemma_eq_intro (index s' 3) (index s'' 3);
  let s''' = shuffle_row 3 1 s'' in
  lemma_eq_intro (shuffle_right (index s'' 3) 1) (index s''' 3);
  lemma_eq_intro (index s'' 0) (index s''' 0);
  lemma_eq_intro (index s'' 1) (index s''' 1);
  lemma_eq_intro (index s'' 2) (index s''' 2);
  s'''


#reset-options "--max_fuel 0 --z3rlimit 5"

val lemma_diagonal_round_standard_1: s:state -> s':state -> Lemma
  (requires ((forall (i:nat). {:pattern (index s' i)} (i < 16 /\ i <> 0 /\ i <> 5 /\ i <> 10 /\ i <> 15)  ==> index s' i == index s i)))
  (ensures (
   let s0 = index s 0 in   let s1 = index s 1 in
   let s2 = index s 2 in   let s3 = index s 3 in
   let s4 = index s 4 in   let s5 = index s 5 in
   let s6 = index s 6 in   let s7 = index s 7 in
   let s8 = index s 8 in   let s9 = index s 9 in
   let s10 = index s 10 in  let s11 = index s 11 in
   let s12 = index s 12 in  let s13 = index s 13 in
   let s14 = index s 14 in  let s15 = index s 15 in
   let s0' = index s' 0 in   let s1' = index s' 1 in
   let s2' = index s' 2 in   let s3' = index s' 3 in
   let s4' = index s' 4 in   let s5' = index s' 5 in
   let s6' = index s' 6 in   let s7' = index s' 7 in
   let s8' = index s' 8 in   let s9' = index s' 9 in
   let s10' = index s' 10 in  let s11' = index s' 11 in
   let s12' = index s' 12 in  let s13' = index s' 13 in
   let s14' = index s' 14 in  let s15' = index s' 15 in
   s1 == s1' /\ s2 == s2' /\ s3 == s3'
   /\ s4 == s4' /\ s6 == s6' /\ s7 == s7'
   /\ s9 == s9' /\ s8 == s8' /\ s11 == s11'
   /\ s13 == s13' /\ s14 == s14' /\ s12 == s12'))
let lemma_diagonal_round_standard_1 s s' =
  let p = fun (i:nat) -> (i < 16 /\ i <> 0 /\ i <> 5 /\ i <> 10 /\ i <> 15) in
  let q = fun (i:nat) -> (i < 16 /\ index s' i == index s i) in
  lemma_forall_elim p q 1;
  lemma_forall_elim p q 2;
  lemma_forall_elim p q 3;
  lemma_forall_elim p q 4;
  lemma_forall_elim p q 6;
  lemma_forall_elim p q 7;
  lemma_forall_elim p q 9;
  lemma_forall_elim p q 8;
  lemma_forall_elim p q 11;
  lemma_forall_elim p q 13;
  lemma_forall_elim p q 14;
  lemma_forall_elim p q 12


val diagonal_round_standard_1: s:state -> Tot (s':state{
  (let s'' = S.quarter_round 0 5 10 15 s in
   let s0 = index s 0 in   let s1 = index s 1 in
   let s2 = index s 2 in   let s3 = index s 3 in
   let s4 = index s 4 in   let s5 = index s 5 in
   let s6 = index s 6 in   let s7 = index s 7 in
   let s8 = index s 8 in   let s9 = index s 9 in
   let s10 = index s 10 in  let s11 = index s 11 in
   let s12 = index s 12 in  let s13 = index s 13 in
   let s14 = index s 14 in  let s15 = index s 15 in
   let s0' = index s' 0 in   let s1' = index s' 1 in
   let s2' = index s' 2 in   let s3' = index s' 3 in
   let s4' = index s' 4 in   let s5' = index s' 5 in
   let s6' = index s' 6 in   let s7' = index s' 7 in
   let s8' = index s' 8 in   let s9' = index s' 9 in
   let s10' = index s' 10 in  let s11' = index s' 11 in
   let s12' = index s' 12 in  let s13' = index s' 13 in
   let s14' = index s' 14 in  let s15' = index s' 15 in
   lined s0 s5 s10 s15 s0' s5' s10' s15'
   /\ s'' == s'
   /\ s1 == s1' /\ s2 == s2' /\ s3 == s3'
   /\ s4 == s4' /\ s6 == s6' /\ s7 == s7'
   /\ s9 == s9' /\ s8 == s8' /\ s11 == s11'
   /\ s13 == s13' /\ s14 == s14' /\ s12 == s12')})
let diagonal_round_standard_1 s =
  lemma_quarter_round_standard s 0 5 10 15;
  let s' = quarter_round_standard s 0 5 10 15 in
  cut (lined (index s 0) (index s 5) (index s 10) (index s 15) (index s' 0) (index s' 5) (index s' 10) (index s' 15));
  lemma_diagonal_round_standard_1 s s';
  s'


val lemma_diagonal_round_standard_2: s:state -> s':state -> Lemma
  (requires ((forall (i:nat). {:pattern (index s' i)} (i < 16 /\ i <> 1 /\ i <> 6 /\ i <> 11 /\ i <> 12)  ==> index s' i == index s i)))
  (ensures (
   let s0 = index s 0 in   let s1 = index s 1 in
   let s2 = index s 2 in   let s3 = index s 3 in
   let s4 = index s 4 in   let s5 = index s 5 in
   let s6 = index s 6 in   let s7 = index s 7 in
   let s8 = index s 8 in   let s9 = index s 9 in
   let s10 = index s 10 in  let s11 = index s 11 in
   let s12 = index s 12 in  let s13 = index s 13 in
   let s14 = index s 14 in  let s15 = index s 15 in
   let s0' = index s' 0 in   let s1' = index s' 1 in
   let s2' = index s' 2 in   let s3' = index s' 3 in
   let s4' = index s' 4 in   let s5' = index s' 5 in
   let s6' = index s' 6 in   let s7' = index s' 7 in
   let s8' = index s' 8 in   let s9' = index s' 9 in
   let s10' = index s' 10 in  let s11' = index s' 11 in
   let s12' = index s' 12 in  let s13' = index s' 13 in
   let s14' = index s' 14 in  let s15' = index s' 15 in
   s0 == s0' /\ s2 == s2' /\ s3 == s3'
   /\ s4 == s4' /\ s5 == s5' /\ s7 == s7'
   /\ s8 == s8' /\ s10 == s10' /\ s9 == s9'
   /\ s13 == s13' /\ s14 == s14' /\ s15 == s15'))
let lemma_diagonal_round_standard_2 s s' =
  let p = fun (i:nat) -> (i < 16 /\ i <> 1 /\ i <> 6 /\ i <> 11 /\ i <> 12) in
  let q = fun (i:nat) -> (i < 16 /\ index s' i == index s i) in
  lemma_forall_elim p q 0;
  lemma_forall_elim p q 2;
  lemma_forall_elim p q 3;
  lemma_forall_elim p q 4;
  lemma_forall_elim p q 5;
  lemma_forall_elim p q 7;
  lemma_forall_elim p q 8;
  lemma_forall_elim p q 10;
  lemma_forall_elim p q 9;
  lemma_forall_elim p q 13;
  lemma_forall_elim p q 14;
  lemma_forall_elim p q 15


val diagonal_round_standard_2: s:state -> Tot (s':state{
  (let s'' = S.quarter_round 0 5 10 15 s in
   let s'' = S.quarter_round 1 6 11 12 s'' in
   let s0 = index s 0 in   let s1 = index s 1 in
   let s2 = index s 2 in   let s3 = index s 3 in
   let s4 = index s 4 in   let s5 = index s 5 in
   let s6 = index s 6 in   let s7 = index s 7 in
   let s8 = index s 8 in   let s9 = index s 9 in
   let s10 = index s 10 in  let s11 = index s 11 in
   let s12 = index s 12 in  let s13 = index s 13 in
   let s14 = index s 14 in  let s15 = index s 15 in
   let s0' = index s' 0 in   let s1' = index s' 1 in
   let s2' = index s' 2 in   let s3' = index s' 3 in
   let s4' = index s' 4 in   let s5' = index s' 5 in
   let s6' = index s' 6 in   let s7' = index s' 7 in
   let s8' = index s' 8 in   let s9' = index s' 9 in
   let s10' = index s' 10 in  let s11' = index s' 11 in
   let s12' = index s' 12 in  let s13' = index s' 13 in
   let s14' = index s' 14 in  let s15' = index s' 15 in
   lined s0 s5 s10 s15 s0' s5' s10' s15'
   /\ s'' == s'
   /\ lined s1 s6 s11 s12 s1' s6' s11' s12'
   /\ s2 == s2' /\ s3 == s3'
   /\ s4 == s4' /\ s7 == s7'
   /\ s8 == s8' /\ s9 == s9'
   /\ s14 == s14' /\ s13 == s13')})
let diagonal_round_standard_2 s =
  let s' = diagonal_round_standard_1 s in
  lemma_quarter_round_standard s' 1 6 11 12;
  let s'' = quarter_round_standard s' 1 6 11 12 in
  lemma_diagonal_round_standard_2 s' s'';
  s''


val lemma_diagonal_round_standard_3: s:state -> s':state -> Lemma
  (requires ((forall (i:nat). {:pattern (index s' i)} (i < 16 /\ i <> 2 /\ i <> 7 /\ i <> 8 /\ i <> 13)  ==> index s' i == index s i)))
  (ensures (
   let s0 = index s 0 in   let s1 = index s 1 in
   let s2 = index s 2 in   let s3 = index s 3 in
   let s4 = index s 4 in   let s5 = index s 5 in
   let s6 = index s 6 in   let s7 = index s 7 in
   let s8 = index s 8 in   let s9 = index s 9 in
   let s10 = index s 10 in  let s11 = index s 11 in
   let s12 = index s 12 in  let s13 = index s 13 in
   let s14 = index s 14 in  let s15 = index s 15 in
   let s0' = index s' 0 in   let s1' = index s' 1 in
   let s2' = index s' 2 in   let s3' = index s' 3 in
   let s4' = index s' 4 in   let s5' = index s' 5 in
   let s6' = index s' 6 in   let s7' = index s' 7 in
   let s8' = index s' 8 in   let s9' = index s' 9 in
   let s10' = index s' 10 in  let s11' = index s' 11 in
   let s12' = index s' 12 in  let s13' = index s' 13 in
   let s14' = index s' 14 in  let s15' = index s' 15 in
   s0 == s0' /\ s1 == s1' /\ s3 == s3'
   /\ s4 == s4' /\ s5 == s5' /\ s6 == s6'
   /\ s10 == s10' /\ s9 == s9' /\ s11 == s11'
   /\ s12 == s12' /\ s14 == s14' /\ s15 == s15'))
let lemma_diagonal_round_standard_3 s s' =
  let p = fun (i:nat) -> (i < 16 /\ i <> 2 /\ i <> 7 /\ i <> 8 /\ i <> 13) in
  let q = fun (i:nat) -> (i < 16 /\ index s' i == index s i) in
  lemma_forall_elim p q 0;
  lemma_forall_elim p q 1;
  lemma_forall_elim p q 3;
  lemma_forall_elim p q 4;
  lemma_forall_elim p q 5;
  lemma_forall_elim p q 6;
  lemma_forall_elim p q 10;
  lemma_forall_elim p q 9;
  lemma_forall_elim p q 11;
  lemma_forall_elim p q 12;
  lemma_forall_elim p q 14;
  lemma_forall_elim p q 15


val diagonal_round_standard_3: s:state -> Tot (s':state{
  (let s'' = S.quarter_round 0 5 10 15 s in
   let s'' = S.quarter_round 1 6 11 12 s'' in
   let s'' = S.quarter_round 2 7  8 13 s'' in
   let s0 = index s 0 in   let s1 = index s 1 in
   let s2 = index s 2 in   let s3 = index s 3 in
   let s4 = index s 4 in   let s5 = index s 5 in
   let s6 = index s 6 in   let s7 = index s 7 in
   let s8 = index s 8 in   let s9 = index s 9 in
   let s10 = index s 10 in  let s11 = index s 11 in
   let s12 = index s 12 in  let s13 = index s 13 in
   let s14 = index s 14 in  let s15 = index s 15 in
   let s0' = index s' 0 in   let s1' = index s' 1 in
   let s2' = index s' 2 in   let s3' = index s' 3 in
   let s4' = index s' 4 in   let s5' = index s' 5 in
   let s6' = index s' 6 in   let s7' = index s' 7 in
   let s8' = index s' 8 in   let s9' = index s' 9 in
   let s10' = index s' 10 in  let s11' = index s' 11 in
   let s12' = index s' 12 in  let s13' = index s' 13 in
   let s14' = index s' 14 in  let s15' = index s' 15 in
   lined s0 s5 s10 s15 s0' s5' s10' s15'
   /\ lined s1 s6 s11 s12 s1' s6' s11' s12'
   /\ lined s2 s7 s8 s13 s2' s7' s8' s13'
   /\ s'' == s'
   /\ s3 == s3'
   /\ s4 == s4'
   /\ s9 == s9'
   /\ s14 == s14')})
let diagonal_round_standard_3 s =
  let s'' = diagonal_round_standard_2 s in
  lemma_quarter_round_standard s'' 2 7 8 13;
  let s''' = quarter_round_standard s'' 2 7 8 13 in
  lemma_diagonal_round_standard_3 s'' s''';
  s'''


val lemma_diagonal_round_standard_4: s:state -> s':state -> Lemma
  (requires ((forall (i:nat). {:pattern (index s' i)} (i < 16 /\ i <> 3 /\ i <> 4 /\ i <> 9 /\ i <> 14)  ==> index s' i == index s i)))
  (ensures (
   let s0 = index s 0 in   let s1 = index s 1 in
   let s2 = index s 2 in   let s3 = index s 3 in
   let s4 = index s 4 in   let s5 = index s 5 in
   let s6 = index s 6 in   let s7 = index s 7 in
   let s8 = index s 8 in   let s9 = index s 9 in
   let s10 = index s 10 in  let s11 = index s 11 in
   let s12 = index s 12 in  let s13 = index s 13 in
   let s14 = index s 14 in  let s15 = index s 15 in
   let s0' = index s' 0 in   let s1' = index s' 1 in
   let s2' = index s' 2 in   let s3' = index s' 3 in
   let s4' = index s' 4 in   let s5' = index s' 5 in
   let s6' = index s' 6 in   let s7' = index s' 7 in
   let s8' = index s' 8 in   let s9' = index s' 9 in
   let s10' = index s' 10 in  let s11' = index s' 11 in
   let s12' = index s' 12 in  let s13' = index s' 13 in
   let s14' = index s' 14 in  let s15' = index s' 15 in
   s0 == s0' /\ s1 == s1' /\ s2 == s2'
   /\ s6 == s6' /\ s5 == s5' /\ s7 == s7'
   /\ s8 == s8' /\ s11 == s11' /\ s10 == s10'
   /\ s12 == s12' /\ s13 == s13' /\ s15 == s15'))
let lemma_diagonal_round_standard_4 s s' =
  let p = fun (i:nat) -> (i < 16 /\ i <> 3 /\ i <> 4 /\ i <> 9 /\ i <> 14) in
  let q = fun (i:nat) -> (i < 16 /\ index s' i == index s i) in
  lemma_forall_elim p q 0;
  lemma_forall_elim p q 1;
  lemma_forall_elim p q 2;
  lemma_forall_elim p q 7;
  lemma_forall_elim p q 5;
  lemma_forall_elim p q 6;
  lemma_forall_elim p q 8;
  lemma_forall_elim p q 11;
  lemma_forall_elim p q 10;
  lemma_forall_elim p q 12;
  lemma_forall_elim p q 13;
  lemma_forall_elim p q 15


#reset-options "--max_fuel 0 --z3rlimit 10"

val lemma_diagonal_round_def: s:state -> Lemma
  (let s' = S.quarter_round 0 5 10 15 s in
   let s' = S.quarter_round 1 6 11 12 s' in
   let s' = S.quarter_round 2 7 8  13 s' in
   let s' = S.quarter_round 3 4 9  14 s' in
   s' == S.diagonal_round s)
let lemma_diagonal_round_def s = ()

val diagonal_round_standard: s:state -> Tot (s':state{
  (s' == S.diagonal_round s
   /\ (let s0 = index s 0 in   let s1 = index s 1 in
   let s2 = index s 2 in   let s3 = index s 3 in
   let s4 = index s 4 in   let s5 = index s 5 in
   let s6 = index s 6 in   let s7 = index s 7 in
   let s8 = index s 8 in   let s9 = index s 9 in
   let s10 = index s 10 in  let s11 = index s 11 in
   let s12 = index s 12 in  let s13 = index s 13 in
   let s14 = index s 14 in  let s15 = index s 15 in
   let s0' = index s' 0 in   let s1' = index s' 1 in
   let s2' = index s' 2 in   let s3' = index s' 3 in
   let s4' = index s' 4 in   let s5' = index s' 5 in
   let s6' = index s' 6 in   let s7' = index s' 7 in
   let s8' = index s' 8 in   let s9' = index s' 9 in
   let s10' = index s' 10 in  let s11' = index s' 11 in
   let s12' = index s' 12 in  let s13' = index s' 13 in
   let s14' = index s' 14 in  let s15' = index s' 15 in
   lined s0 s5 s10 s15 s0' s5' s10' s15'
   /\ lined s1 s6 s11 s12 s1' s6' s11' s12'
   /\ lined s2 s7 s8 s13 s2' s7' s8' s13'
   /\ lined s3 s4 s9 s14 s3' s4' s9' s14'))})
let diagonal_round_standard s =
  let s''' = diagonal_round_standard_3 s in
  lemma_quarter_round_standard s''' 3 4 9 14;
  let s'''' = quarter_round_standard s''' 3 4 9 14 in
  lemma_diagonal_round_standard_4 s''' s'''';
  lemma_diagonal_round_def s;
  s''''


val diagonal_round_vectorized: s:vec_state -> Tot (s':vec_state{
  (let s0 = index (index s 0) 0 in   let s1 = index (index s 0) 1 in
   let s2 = index (index s 0) 2 in   let s3 = index (index s 0) 3 in
   let s4 = index (index s 1) 0 in   let s5 = index (index s 1) 1 in
   let s6 = index (index s 1) 2 in   let s7 = index (index s 1) 3 in
   let s8 = index (index s 2) 0 in   let s9 = index (index s 2) 1 in
   let s10 = index (index s 2) 2 in  let s11 = index (index s 2) 3 in
   let s12 = index (index s 3) 0 in  let s13 = index (index s 3) 1 in
   let s14 = index (index s 3) 2 in  let s15 = index (index s 3) 3 in
   let s0' = index (index s' 0) 0 in   let s1' = index (index s' 0) 1 in
   let s2' = index (index s' 0) 2 in   let s3' = index (index s' 0) 3 in
   let s4' = index (index s' 1) 0 in   let s5' = index (index s' 1) 1 in
   let s6' = index (index s' 1) 2 in   let s7' = index (index s' 1) 3 in
   let s8' = index (index s' 2) 0 in   let s9' = index (index s' 2) 1 in
   let s10' = index (index s' 2) 2 in  let s11' = index (index s' 2) 3 in
   let s12' = index (index s' 3) 0 in  let s13' = index (index s' 3) 1 in
   let s14' = index (index s' 3) 2 in  let s15' = index (index s' 3) 3 in
   lined s0 s5 s10 s15 s0' s5' s10' s15'
   /\ lined s1 s6 s11 s12 s1' s6' s11' s12'
   /\ lined s2 s7 s8 s13 s2' s7' s8' s13'
   /\ lined s3 s4 s9 s14 s3' s4' s9' s14')})
let diagonal_round_vectorized s =
  let s' = shuffle_rous_1 s in
  lemma_quarter_round_vectorized s';
  let s'' = quarter_round_vec s' in
  let s''' = shuffle_rous_2 s'' in
  s'''


#reset-options "--max_fuel 0 --z3rlimit 100"


val lemma_quarter_round: s:vec_state -> Lemma
  (quarter_round_vec s == V.round s)
let lemma_quarter_round s = ()

val lemma_column_round: s:vec_state -> Lemma
  (quarter_round_vec s == V.column_round s)
let lemma_column_round s = ()

val lemma_shuffle_rows_1: s:vec_state -> Lemma
  (shuffle_rous_1 s == V.shuffle_rows_0123 s)
let lemma_shuffle_rows_1 s = ()

val lemma_shuffle_rows_2: s:vec_state -> Lemma
  (shuffle_rous_2 s == V.shuffle_rows_0321 s)
let lemma_shuffle_rows_2 s = ()

val lemma_diagonal_round_vec: s:vec_state -> Lemma
  (diagonal_round_vectorized s == V.diagonal_round s)
let lemma_diagonal_round_vec s =
  lemma_shuffle_rows_1 s;
  let s' = shuffle_rous_1 s in
  lemma_quarter_round s';
  let s'' = quarter_round_vec s' in
  lemma_shuffle_rows_2 s''

val double_round_vec: s:vec_state -> Tot vec_state
let double_round_vec s =
  let s' = quarter_round_vec s in
  let s'' = diagonal_round_vectorized s' in
  s''

val lemma_double_round_def: s:vec_state -> Lemma
  (double_round_vec s == V.double_round s)
let lemma_double_round_def s =
  lemma_column_round s;
  let s = quarter_round_vec s in
  lemma_diagonal_round_vec s

(* #reset-options "--initial_fuel 1 --max_fuel 1 --z3rlimit 100" *)

(* // TODO: move somewhere else *)
(* private val lemma_seq_of_list: (#a:Type) -> (l:list a) -> Lemma *)
(*   (forall (i:nat). {:pattern (Seq.index (Seq.seq_of_list l) i)} i < List.Tot.length l *)
(*              ==> Seq.index (Seq.seq_of_list l) i == List.Tot.index l i) *)
(* let rec lemma_seq_of_list #a l = *)
(*   match l with *)
(*   | [] -> Seq.lemma_eq_intro (Seq.seq_of_list l) Seq.createEmpty *)
(*   | hd::tl -> ( *)
(*     lemma_seq_of_list #a tl; *)
(*     Seq.lemma_eq_intro (Seq.seq_of_list l) (Seq.cons hd (Seq.seq_of_list tl)) *)
(*   ) *)

#reset-options "--max_fuel 0 --z3rlimit 100"

val lemma_setup_standard_1: k:S.key -> n:S.nonce -> c:S.counter -> Lemma
  (let s = S.setup k n c in
   let s0 = index s 0 in   let s1 = index s 1 in
   let s2 = index s 2 in   let s3 = index s 3 in
   s0 == 0x61707865ul /\ s1 == 0x3320646eul /\ s2 == 0x79622d32ul /\ s3 == 0x6b206574ul)
let lemma_setup_standard_1 k n c = ()

val lemma_setup_standard_2: k:S.key -> n:S.nonce -> c:S.counter -> Lemma
  (let s = S.setup k n c in
   let k = uint32s_from_le 8 k in
   let s4 = index s 4 in   let s5 = index s 5 in
   let s6 = index s 6 in   let s7 = index s 7 in
   let s8 = index s 8 in   let s9 = index s 9 in
   let s10 = index s 10 in  let s11 = index s 11 in
   s4 == index k 0 /\ s5 == index k 1 /\ s6 == index k 2 /\ s7 == index k 3
   /\ s8 == index k 4 /\ s9 == index k 5 /\ s10 == index k 6 /\ s11 == index k 7)
let lemma_setup_standard_2 k n c =
  lemma_eq_intro (slice (S.setup k n c) 4 12) (uint32s_from_le 8 k)


val lemma_setup_standard_3: k:S.key -> n:S.nonce -> c:S.counter -> Lemma
  (let s = S.setup k n c in
   let n = uint32s_from_le 3 n in
   let s12 = index s 12 in  let s13 = index s 13 in
   let s14 = index s 14 in  let s15 = index s 15 in
   s12 == UInt32.uint_to_t c /\ s13 == index n 0 /\ s14 == index n 1 /\ s15 == index n 2)
let lemma_setup_standard_3 k n c =
  lemma_eq_intro (slice (S.setup k n c) 12 13) (singleton (UInt32.uint_to_t c));
  lemma_eq_intro (slice (S.setup k n c) 13 16) (uint32s_from_le 3 n)


val lemma_setup_standard: k:S.key -> n:S.nonce -> c:S.counter -> Lemma
  (let s = S.setup k n c in
   let k = uint32s_from_le 8 k in
   let n = uint32s_from_le 3 n in
   let s0 = index s 0 in   let s1 = index s 1 in
   let s2 = index s 2 in   let s3 = index s 3 in
   let s4 = index s 4 in   let s5 = index s 5 in
   let s6 = index s 6 in   let s7 = index s 7 in
   let s8 = index s 8 in   let s9 = index s 9 in
   let s10 = index s 10 in  let s11 = index s 11 in
   let s12 = index s 12 in  let s13 = index s 13 in
   let s14 = index s 14 in  let s15 = index s 15 in
   s0 == 0x61707865ul /\ s1 == 0x3320646eul /\ s2 == 0x79622d32ul /\ s3 == 0x6b206574ul
   /\ s4 == index k 0 /\ s5 == index k 1 /\ s6 == index k 2 /\ s7 == index k 3
   /\ s8 == index k 4 /\ s9 == index k 5 /\ s10 == index k 6 /\ s11 == index k 7
   /\ s12 == UInt32.uint_to_t c /\ s13 == index n 0 /\ s14 == index n 1 /\ s15 == index n 2)
let lemma_setup_standard k n c =
  lemma_setup_standard_1 k n c;
  lemma_setup_standard_2 k n c;
  lemma_setup_standard_3 k n c


val lemma_setup_vec_1: k:V.key -> n:V.nonce -> c:V.counter -> Lemma
  (let s = V.setup k n c in
   let s0 = index (index s 0) 0 in   let s1 = index (index s 0) 1 in
   let s2 = index (index s 0) 2 in   let s3 = index (index s 0) 3 in
   let s4 = index (index s 1) 0 in   let s5 = index (index s 1) 1 in
   let s6 = index (index s 1) 2 in   let s7 = index (index s 1) 3 in
   let s8 = index (index s 2) 0 in   let s9 = index (index s 2) 1 in
   let s10 = index (index s 2) 2 in  let s11 = index (index s 2) 3 in
   let s12 = index (index s 3) 0 in  let s13 = index (index s 3) 1 in
   let s14 = index (index s 3) 2 in  let s15 = index (index s 3) 3 in
   s0 == 0x61707865ul /\ s1 == 0x3320646eul /\ s2 == 0x79622d32ul /\ s3 == 0x6b206574ul)
let lemma_setup_vec_1 k n c = ()


val lemma_setup_vec_2: k:S.key -> n:S.nonce -> c:S.counter -> Lemma
  (let s = V.setup k n c in
   let k = uint32s_from_le 8 k in
   let s4 = index (index s 1) 0 in   let s5 = index (index s 1) 1 in
   let s6 = index (index s 1) 2 in   let s7 = index (index s 1) 3 in
   let s8 = index (index s 2) 0 in   let s9 = index (index s 2) 1 in
   let s10 = index (index s 2) 2 in  let s11 = index (index s 2) 3 in
   s4 == index k 0 /\ s5 == index k 1 /\ s6 == index k 2 /\ s7 == index k 3
   /\ s8 == index k 4 /\ s9 == index k 5 /\ s10 == index k 6 /\ s11 == index k 7)
let lemma_setup_vec_2 k n c =
  lemma_uint32s_from_le_slice 8 k 4;
  let key_part_1:vec = uint32s_from_le 4 (Seq.slice k 0 16)  in
  let key_part_2:vec = uint32s_from_le 4 (Seq.slice k 16 32) in
  lemma_eq_intro (key_part_1 @| key_part_2) (uint32s_from_le 8 k);
  let nonce    :vec = Seq.cons (UInt32.uint_to_t c) (uint32s_from_le 3 n) in
  lemma_eq_intro (index (V.setup k n c) 1) (uint32s_from_le 4 (slice k 0 16));
  lemma_eq_intro (index (V.setup k n c) 2) (uint32s_from_le 4 (slice k 16 32))


val lemma_setup_vec_3: k:V.key -> n:V.nonce -> c:V.counter -> Lemma
  (let s = V.setup k n c in
   let n = uint32s_from_le 3 n in
   let s12 = index (index s 3) 0 in  let s13 = index (index s 3) 1 in
   let s14 = index (index s 3) 2 in  let s15 = index (index s 3) 3 in
   s12 == UInt32.uint_to_t c /\ s13 == index n 0 /\ s14 == index n 1 /\ s15 == index n 2)
let lemma_setup_vec_3 k n c =
  let nonce    :vec = Seq.cons (UInt32.uint_to_t c) (uint32s_from_le 3 n) in
  lemma_eq_intro (slice nonce 1 4) (uint32s_from_le 3 n)


val lemma_setup_vec: k:V.key -> n:V.nonce -> c:V.counter -> Lemma
  (let s = V.setup k n c in
   let k = uint32s_from_le 8 k in
   let n = uint32s_from_le 3 n in
   let s0 = index (index s 0) 0 in   let s1 = index (index s 0) 1 in
   let s2 = index (index s 0) 2 in   let s3 = index (index s 0) 3 in
   let s4 = index (index s 1) 0 in   let s5 = index (index s 1) 1 in
   let s6 = index (index s 1) 2 in   let s7 = index (index s 1) 3 in
   let s8 = index (index s 2) 0 in   let s9 = index (index s 2) 1 in
   let s10 = index (index s 2) 2 in  let s11 = index (index s 2) 3 in
   let s12 = index (index s 3) 0 in  let s13 = index (index s 3) 1 in
   let s14 = index (index s 3) 2 in  let s15 = index (index s 3) 3 in
   s0 == 0x61707865ul /\ s1 == 0x3320646eul /\ s2 == 0x79622d32ul /\ s3 == 0x6b206574ul
   /\ s4 == index k 0 /\ s5 == index k 1 /\ s6 == index k 2 /\ s7 == index k 3
   /\ s8 == index k 4 /\ s9 == index k 5 /\ s10 == index k 6 /\ s11 == index k 7
   /\ s12 == UInt32.uint_to_t c /\ s13 == index n 0 /\ s14 == index n 1 /\ s15 == index n 2)
let lemma_setup_vec k n c =
  lemma_setup_vec_1 k n c;
  lemma_setup_vec_2 k n c;
  lemma_setup_vec_3 k n c


val lemma_setup: k:S.key -> n:S.nonce -> c:S.counter -> Lemma
  (let sv = V.setup k n c in let s = S.setup k n c in
   eq_states' s sv)
let lemma_setup k n c =
  lemma_setup_standard k n c; lemma_setup_vec k n c


val lemma_iter_state: s:state -> sv:vec_state{eq_states' s sv} ->
  f:(state -> Tot state) -> fv:(vec_state -> Tot vec_state)  -> n:nat -> Lemma
  (requires (forall (s:state) (sv:vec_state). eq_states' s sv ==> eq_states' (f s) (fv sv)))
  (ensures (eq_states' (iter n f s) (iter n fv sv)))
  (decreases n)
#reset-options "--initial_fuel 1 --max_fuel 1 --z3rlimit 100"
let rec lemma_iter_state s sv f fv n =
  if n = 0 then ()
  else (
    lemma_iter_state (f s) (fv sv) f fv (n-1)
  )


#reset-options "--max_fuel 0 --z3rlimit 10"

val lemma_column_round_eq : s:state -> sv:vec_state{eq_states' s sv} -> Lemma
  (let s = S.column_round s in let sv = V.column_round sv in eq_states' s sv)
let lemma_column_round_eq s sv =
  cut (eq_states' s sv);
  let s' = column_round_standard s in
  lemma_column_round sv;
  let sv' = quarter_round_vec sv in
  let s0 = index s 0 in   let s1 = index s 1 in
  let s2 = index s 2 in   let s3 = index s 3 in
  let s4 = index s 4 in   let s5 = index s 5 in
  let s6 = index s 6 in   let s7 = index s 7 in
  let s8 = index s 8 in   let s9 = index s 9 in
  let s10 = index s 10 in  let s11 = index s 11 in
  let s12 = index s 12 in  let s13 = index s 13 in
  let s14 = index s 14 in  let s15 = index s 15 in
  let sv0 = index (index sv 0) 0 in   let sv1 = index (index sv 0) 1 in
  let sv2 = index (index sv 0) 2 in   let sv3 = index (index sv 0) 3 in
  let sv4 = index (index sv 1) 0 in   let sv5 = index (index sv 1) 1 in
  let sv6 = index (index sv 1) 2 in   let sv7 = index (index sv 1) 3 in
  let sv8 = index (index sv 2) 0 in   let sv9 = index (index sv 2) 1 in
  let sv10 = index (index sv 2) 2 in  let sv11 = index (index sv 2) 3 in
  let sv12 = index (index sv 3) 0 in  let sv13 = index (index sv 3) 1 in
  let sv14 = index (index sv 3) 2 in  let sv15 = index (index sv 3) 3 in
  cut (sv0 == s0); cut (sv1 == s1); cut (sv2 == s2); cut (sv3 == s3);
  cut (sv4 == s4); cut (sv5 == s5); cut (sv6 == s6); cut (sv7 == s7);
  cut (sv8 == s8); cut (sv9 == s9); cut (sv10 == s10); cut (sv11 == s11);
  cut (sv12 == s12); cut (sv13 == s13); cut (sv14 == s14); cut (sv15 == s15);
  let s0' = index s' 0 in   let s1' = index s' 1 in
  let s2' = index s' 2 in   let s3' = index s' 3 in
  let s4' = index s' 4 in   let s5' = index s' 5 in
  let s6' = index s' 6 in   let s7' = index s' 7 in
  let s8' = index s' 8 in   let s9' = index s' 9 in
  let s10' = index s' 10 in  let s11' = index s' 11 in
  let s12' = index s' 12 in  let s13' = index s' 13 in
  let s14' = index s' 14 in  let s15' = index s' 15 in
  let sv0' = index (index sv' 0) 0 in   let sv1' = index (index sv' 0) 1 in
  let sv2' = index (index sv' 0) 2 in   let sv3' = index (index sv' 0) 3 in
  let sv4' = index (index sv' 1) 0 in   let sv5' = index (index sv' 1) 1 in
  let sv6' = index (index sv' 1) 2 in   let sv7' = index (index sv' 1) 3 in
  let sv8' = index (index sv' 2) 0 in   let sv9' = index (index sv' 2) 1 in
  let sv10' = index (index sv' 2) 2 in  let sv11' = index (index sv' 2) 3 in
  let sv12' = index (index sv' 3) 0 in  let sv13' = index (index sv' 3) 1 in
  let sv14' = index (index sv' 3) 2 in  let sv15' = index (index sv' 3) 3 in
  cut (lined s0 s4 s8 s12 s0' s4' s8' s12');
  cut (lined s1 s5 s9 s13 s1' s5' s9' s13');
  cut (lined s2 s6 s10 s14 s2' s6' s10' s14');
  cut (lined s3 s7 s11 s15 s3' s7' s11' s15');
  lemma_quarter_round_vectorized sv;
  cut (lined sv0 sv4 sv8 sv12 sv0' sv4' sv8' sv12');
  cut (lined sv1 sv5 sv9 sv13 sv1' sv5' sv9' sv13');
  cut (lined sv2 sv6 sv10 sv14 sv2' sv6' sv10' sv14');
  cut (lined sv3 sv7 sv11 sv15 sv3' sv7' sv11' sv15');
  cut (sv' == V.column_round sv);
  cut (s' == S.column_round s)


val lemma_diagonal_round_eq : s:state -> sv:vec_state{eq_states' s sv} -> Lemma
  (let s = S.diagonal_round s in let sv = V.diagonal_round sv in eq_states' s sv)
let lemma_diagonal_round_eq s sv =
  cut (eq_states' s sv);
  let s' = diagonal_round_standard s in
  lemma_diagonal_round_vec sv;
  let sv' = diagonal_round_vectorized sv in
  let s0 = index s 0 in   let s1 = index s 1 in
  let s2 = index s 2 in   let s3 = index s 3 in
  let s4 = index s 4 in   let s5 = index s 5 in
  let s6 = index s 6 in   let s7 = index s 7 in
  let s8 = index s 8 in   let s9 = index s 9 in
  let s10 = index s 10 in  let s11 = index s 11 in
  let s12 = index s 12 in  let s13 = index s 13 in
  let s14 = index s 14 in  let s15 = index s 15 in
  let sv0 = index (index sv 0) 0 in   let sv1 = index (index sv 0) 1 in
  let sv2 = index (index sv 0) 2 in   let sv3 = index (index sv 0) 3 in
  let sv4 = index (index sv 1) 0 in   let sv5 = index (index sv 1) 1 in
  let sv6 = index (index sv 1) 2 in   let sv7 = index (index sv 1) 3 in
  let sv8 = index (index sv 2) 0 in   let sv9 = index (index sv 2) 1 in
  let sv10 = index (index sv 2) 2 in  let sv11 = index (index sv 2) 3 in
  let sv12 = index (index sv 3) 0 in  let sv13 = index (index sv 3) 1 in
  let sv14 = index (index sv 3) 2 in  let sv15 = index (index sv 3) 3 in
  cut (sv0 == s0); cut (sv1 == s1); cut (sv2 == s2); cut (sv3 == s3);
  cut (sv4 == s4); cut (sv5 == s5); cut (sv6 == s6); cut (sv7 == s7);
  cut (sv8 == s8); cut (sv9 == s9); cut (sv10 == s10); cut (sv11 == s11);
  cut (sv12 == s12); cut (sv13 == s13); cut (sv14 == s14); cut (sv15 == s15);
  let s0' = index s' 0 in   let s1' = index s' 1 in
  let s2' = index s' 2 in   let s3' = index s' 3 in
  let s4' = index s' 4 in   let s5' = index s' 5 in
  let s6' = index s' 6 in   let s7' = index s' 7 in
  let s8' = index s' 8 in   let s9' = index s' 9 in
  let s10' = index s' 10 in  let s11' = index s' 11 in
  let s12' = index s' 12 in  let s13' = index s' 13 in
  let s14' = index s' 14 in  let s15' = index s' 15 in
  let sv0' = index (index sv' 0) 0 in   let sv1' = index (index sv' 0) 1 in
  let sv2' = index (index sv' 0) 2 in   let sv3' = index (index sv' 0) 3 in
  let sv4' = index (index sv' 1) 0 in   let sv5' = index (index sv' 1) 1 in
  let sv6' = index (index sv' 1) 2 in   let sv7' = index (index sv' 1) 3 in
  let sv8' = index (index sv' 2) 0 in   let sv9' = index (index sv' 2) 1 in
  let sv10' = index (index sv' 2) 2 in  let sv11' = index (index sv' 2) 3 in
  let sv12' = index (index sv' 3) 0 in  let sv13' = index (index sv' 3) 1 in
  let sv14' = index (index sv' 3) 2 in  let sv15' = index (index sv' 3) 3 in
  cut (lined s0 s5 s10 s15 s0' s5' s10' s15');
  cut (lined s1 s6 s11 s12 s1' s6' s11' s12');
  cut (lined s2 s7 s8 s13 s2' s7' s8' s13');
  cut (lined s3 s4 s9 s14 s3' s4' s9' s14');
  cut (lined sv0 sv5 sv10 sv15 sv0' sv5' sv10' sv15');
  cut (lined sv1 sv6 sv11 sv12 sv1' sv6' sv11' sv12');
  cut (lined sv2 sv7 sv8 sv13 sv2' sv7' sv8' sv13');
  cut (lined sv3 sv4 sv9 sv14 sv3' sv4' sv9' sv14');
  cut (sv' == V.diagonal_round sv);
  cut (s' == S.diagonal_round s)


val lemma_double_round_eq: s:state -> sv:vec_state{eq_states' s sv} -> Lemma
  (let s = S.double_round s in let sv = V.double_round sv in eq_states' s sv)
let lemma_double_round_eq s sv =
  let s' = S.column_round s in
  let s'' = S.diagonal_round s' in
  cut (s'' == S.double_round s);
  let sv' = V.column_round sv in
  let sv'' = V.diagonal_round sv' in
  cut (sv'' == V.double_round sv);
  lemma_column_round_eq s sv;
  cut (eq_states' s' sv');
  lemma_diagonal_round_eq s' sv'


val lemma_double_round_eq_: s:state -> sv:vec_state -> Lemma
  (requires (eq_states' s sv))
  (ensures (let s = S.double_round s in let sv = V.double_round sv in eq_states' s sv))
let lemma_double_round_eq_ s sv = lemma_double_round_eq s sv

val lemma_double_round_eq': s:state -> sv:vec_state -> Lemma
  (eq_states' s sv ==> (let s = S.double_round s in let sv = V.double_round sv in eq_states' s sv))
let lemma_double_round_eq' s sv =
  Classical.move_requires (lemma_double_round_eq_ s) sv

val lemma_double_round_eq_forall_1: s:state -> Lemma
  (forall sv. eq_states' s sv ==> eq_states' (S.double_round s) (V.double_round sv))
let lemma_double_round_eq_forall_1 s =
  let post (sv:vec_state) =
    eq_states' s sv ==> eq_states' (S.double_round s) (V.double_round sv) in
  FStar.Classical.forall_intro #_ #post (lemma_double_round_eq' s)

val lemma_double_round_eq_forall: unit -> Lemma
  (forall s sv. eq_states' s sv ==> eq_states' (S.double_round s) (V.double_round sv))
let lemma_double_round_eq_forall () =
  let post (s:state) =
    (forall sv. eq_states' s sv ==> eq_states' (S.double_round s) (V.double_round sv)) in
  FStar.Classical.forall_intro #_ #post lemma_double_round_eq_forall_1

#reset-options "--max_fuel 0 --z3rlimit 5"

val lemma_chacha_rounds_vec: s:state -> sv:vec_state{eq_states' s sv} -> Lemma
  (let s' = S.rounds s in let sv' = V.rounds sv in
   eq_states' s' sv')
let lemma_chacha_rounds_vec s sv =
  lemma_double_round_eq_forall ();
  lemma_iter_state s sv S.double_round V.double_round 10;
  cut (eq_states' (iter 10 S.double_round s) (iter 10 V.double_round sv));
  cut (S.rounds s == iter 10 S.double_round s);
  cut (V.rounds sv == iter 10 V.double_round sv)


val lemma_chacha_core_std: s:state -> s':state -> Lemma
  (let s'' = Spec.Loops.seq_map2 FStar.UInt32.op_Plus_Percent_Hat s' s in
  let s0 = index s 0 in   let s1 = index s 1 in
  let s2 = index s 2 in   let s3 = index s 3 in
  let s4 = index s 4 in   let s5 = index s 5 in
  let s6 = index s 6 in   let s7 = index s 7 in
  let s8 = index s 8 in   let s9 = index s 9 in
  let s10 = index s 10 in  let s11 = index s 11 in
  let s12 = index s 12 in  let s13 = index s 13 in
  let s14 = index s 14 in  let s15 = index s 15 in
  let s0' = index s' 0 in   let s1' = index s' 1 in
  let s2' = index s' 2 in   let s3' = index s' 3 in
  let s4' = index s' 4 in   let s5' = index s' 5 in
  let s6' = index s' 6 in   let s7' = index s' 7 in
  let s8' = index s' 8 in   let s9' = index s' 9 in
  let s10' = index s' 10 in  let s11' = index s' 11 in
  let s12' = index s' 12 in  let s13' = index s' 13 in
  let s14' = index s' 14 in  let s15' = index s' 15 in
  let s0'' = index s'' 0 in   let s1'' = index s'' 1 in
  let s2'' = index s'' 2 in   let s3'' = index s'' 3 in
  let s4'' = index s'' 4 in   let s5'' = index s'' 5 in
  let s6'' = index s'' 6 in   let s7'' = index s'' 7 in
  let s8'' = index s'' 8 in   let s9'' = index s'' 9 in
  let s10'' = index s'' 10 in  let s11'' = index s'' 11 in
  let s12'' = index s'' 12 in  let s13'' = index s'' 13 in
  let s14'' = index s'' 14 in  let s15'' = index s'' 15 in
  FStar.UInt32.(
  s0'' == s0' +%^ s0
  /\ s1'' == s1' +%^ s1
  /\ s2'' == s2' +%^ s2
  /\ s3'' == s3' +%^ s3
  /\ s4'' == s4' +%^ s4
  /\ s5'' == s5' +%^ s5
  /\ s6'' == s6' +%^ s6
  /\ s7'' == s7' +%^ s7
  /\ s8'' == s8' +%^ s8
  /\ s9'' == s9' +%^ s9
  /\ s10'' == s10' +%^ s10
  /\ s11'' == s11' +%^ s11
  /\ s12'' == s12' +%^ s12
  /\ s13'' == s13' +%^ s13
  /\ s14'' == s14' +%^ s14
  /\ s15'' == s15' +%^ s15))
#reset-options "--max_fuel 0 --z3rlimit 100"
let lemma_chacha_core_std s s' = ()

val lemma_chacha_core_vec: s:vec_state -> s':vec_state -> Lemma
  (let s'' = Spec.Loops.seq_map2 V.op_Plus_Percent_Hat s' s in
  let s0 = index (index s 0) 0 in   let s1 = index (index s 0) 1 in
  let s2 = index (index s 0) 2 in   let s3 = index (index s 0) 3 in
  let s4 = index (index s 1) 0 in   let s5 = index (index s 1) 1 in
  let s6 = index (index s 1) 2 in   let s7 = index (index s 1) 3 in
  let s8 = index (index s 2) 0 in   let s9 = index (index s 2) 1 in
  let s10 = index (index s 2) 2 in  let s11 = index (index s 2) 3 in
  let s12 = index (index s 3) 0 in  let s13 = index (index s 3) 1 in
  let s14 = index (index s 3) 2 in  let s15 = index (index s 3) 3 in
  let s0' = index (index s' 0) 0 in   let s1' = index (index s' 0) 1 in
  let s2' = index (index s' 0) 2 in   let s3' = index (index s' 0) 3 in
  let s4' = index (index s' 1) 0 in   let s5' = index (index s' 1) 1 in
  let s6' = index (index s' 1) 2 in   let s7' = index (index s' 1) 3 in
  let s8' = index (index s' 2) 0 in   let s9' = index (index s' 2) 1 in
  let s10' = index (index s' 2) 2 in  let s11' = index (index s' 2) 3 in
  let s12' = index (index s' 3) 0 in  let s13' = index (index s' 3) 1 in
  let s14' = index (index s' 3) 2 in  let s15' = index (index s' 3) 3 in
  let s0'' = index (index s'' 0) 0 in   let s1'' = index (index s'' 0) 1 in
  let s2'' = index (index s'' 0) 2 in   let s3'' = index (index s'' 0) 3 in
  let s4'' = index (index s'' 1) 0 in   let s5'' = index (index s'' 1) 1 in
  let s6'' = index (index s'' 1) 2 in   let s7'' = index (index s'' 1) 3 in
  let s8'' = index (index s'' 2) 0 in   let s9'' = index (index s'' 2) 1 in
  let s10'' = index (index s'' 2) 2 in  let s11'' = index (index s'' 2) 3 in
  let s12'' = index (index s'' 3) 0 in  let s13'' = index (index s'' 3) 1 in
  let s14'' = index (index s'' 3) 2 in  let s15'' = index (index s'' 3) 3 in
  FStar.UInt32.(
  s0'' == s0' +%^ s0
  /\ s1'' == s1' +%^ s1
  /\ s2'' == s2' +%^ s2
  /\ s3'' == s3' +%^ s3
  /\ s4'' == s4' +%^ s4
  /\ s5'' == s5' +%^ s5
  /\ s6'' == s6' +%^ s6
  /\ s7'' == s7' +%^ s7
  /\ s8'' == s8' +%^ s8
  /\ s9'' == s9' +%^ s9
  /\ s10'' == s10' +%^ s10
  /\ s11'' == s11' +%^ s11
  /\ s12'' == s12' +%^ s12
  /\ s13'' == s13' +%^ s13
  /\ s14'' == s14' +%^ s14
  /\ s15'' == s15' +%^ s15))
#reset-options "--max_fuel 0 --z3rlimit 100"
let lemma_chacha_core_vec s s' = ()

val lemma_chacha_core: s:state -> sv:vec_state{eq_states' s sv} -> Lemma
  (let s = S.chacha20_core s in let sv = V.chacha20_core sv in
   eq_states' s sv)
let lemma_chacha_core s sv =
  lemma_chacha_rounds_vec s sv;
  let s' = S.rounds s in
  let sv' = V.rounds sv in
  lemma_chacha_core_std s s'; lemma_chacha_core_vec sv sv'


private let lemma_append_assoc (#a:Type) (x:seq a) (y:seq a) (z:seq a) : Lemma
  (x @| (y @| z) == (x @| y) @| z) = lemma_eq_intro ((x @| y) @| z) (x @| (y @| z))


val lemma_chacha_block_slice: s:state -> Lemma
  (uint32s_to_le 16 s == uint32s_to_le 4 (slice s 0 4)
                         @| uint32s_to_le 4 (slice s 4  8)
                         @| uint32s_to_le 4 (slice s 8  12)
                         @| uint32s_to_le 4 (slice s 12 16))
#reset-options "--max_fuel 0 --z3rlimit 100"
let lemma_chacha_block_slice b =
  lemma_uint32s_to_le_slice 16 b 8;
  let b0 = slice b 0 8 in
  let b1 = slice b 8 16 in
  cut (uint32s_to_le 16 b == uint32s_to_le 8 b0 @| uint32s_to_le 8 b1);
  lemma_uint32s_to_le_slice 8 b0 4;
  lemma_uint32s_to_le_slice 8 b1 4;
  cut (uint32s_to_le 8 b0 == uint32s_to_le 4 (slice b0 0 4) @| uint32s_to_le 4 (slice b0 4 8));
  cut (uint32s_to_le 8 b1 == uint32s_to_le 4 (slice b1 0 4) @| uint32s_to_le 4 (slice b1 4 8));
  lemma_eq_intro (slice b0 0 4) (slice b 0 4);
  lemma_eq_intro (slice b0 4 8) (slice b 4 8);
  lemma_eq_intro (slice b1 0 4) (slice b 8 12);
  lemma_eq_intro (slice b1 4 8) (slice b 12 16);
  cut (uint32s_to_le 8 b0 == uint32s_to_le 4 (slice b 0 4) @| uint32s_to_le 4 (slice b 4 8));
  cut (uint32s_to_le 8 b1 == uint32s_to_le 4 (slice b 8 12) @| uint32s_to_le 4 (slice b 12 16));
  cut (uint32s_to_le 16 b == (uint32s_to_le 4 (slice b 0 4) @| uint32s_to_le 4 (slice b 4 8))
                             @| (uint32s_to_le 4 (slice b 8 12) @| uint32s_to_le 4 (slice b 12 16)));
  lemma_append_assoc (uint32s_to_le 4 (slice b 0 4) @| uint32s_to_le 4 (slice b 4 8))
                     (uint32s_to_le 4 (slice b 8 12)) (uint32s_to_le 4 (slice b 12 16));
  lemma_append_assoc (uint32s_to_le 4 (slice b 0 4)) (uint32s_to_le 4 (slice b 4 8))
                     (uint32s_to_le 4 (slice b 8 12) @| uint32s_to_le 4 (slice b 12 16))


val lemma_chacha20_block: k:key -> n:nonce -> c:counter ->
  Lemma (let b = S.chacha20_block k n c in let b' = V.chacha20_block k n c in  b == b')
let lemma_chacha20_block k n c =
  lemma_setup k n c;
  let s = S.setup k n c in
  let sv = V.setup k n c in
  cut (eq_states' s sv);
  lemma_chacha_core s sv;
  let s' = S.chacha20_core s in
  let sv' = V.chacha20_core sv in
  lemma_chacha_block_slice s';
  lemma_eq_states_intro s' sv';
  lemma_eq_intro (index sv' 0) (slice s' 0 4);
  lemma_eq_intro (index sv' 1) (slice s' 4 8);
  lemma_eq_intro (index sv' 2) (slice s' 8 12);
  lemma_eq_intro (index sv' 3) (slice s' 12 16);
  lemma_eq_intro (uint32s_to_le 4 (index sv' 0) @|  uint32s_to_le 4 (index sv' 1) @|  uint32s_to_le 4 (index sv' 2) @|  uint32s_to_le 4 (index sv' 3)) (uint32s_to_le 16 s')


val lemma_chacha20_counter_mode_blocks: k:key -> n:nonce -> c:counter -> m:bytes{c + 1 * (length m / 64) < pow2 32 /\
    length m % (64 * 1) = 0} -> Lemma
  (requires (True))
  (ensures (Spec.CTR.counter_mode_blocks S.chacha20_ctx S.chacha20_cipher k n c m ==
            Spec.CTR.counter_mode_blocks V.chacha20_ctx V.chacha20_cipher k n c m))
  (decreases (length m))
#reset-options "--initial_fuel 1 --max_fuel 1 --z3rlimit 50"
let rec lemma_chacha20_counter_mode_blocks k n c m =
  if length m = 0 then ()
  else (
    let len = length m in
    let len' = len / (64 * 1) in
    Math.Lemmas.lemma_div_mod len (64 * 1);
    let prefix, block = split m (len - 64 * 1) in    
      (* TODO: move to a single lemma for clarify *)
      Math.Lemmas.lemma_mod_plus (length prefix) 1 (64 * 1);
      Math.Lemmas.lemma_div_le (length prefix) len 64;
      Spec.CTR.Lemmas.lemma_div len (64 * 1);
      (* End TODO *)
    lemma_chacha20_counter_mode_blocks k n c prefix;
    lemma_chacha20_block k n ((c + (len / 64 - 1)) * 1)
  )
  

val lemma_chacha20_encrypt_bytes: k:key -> n:nonce -> c:counter -> m:bytes{c + length m / 64 < pow2 32} -> Lemma
  (requires (True))
  (ensures (V.chacha20_encrypt_bytes k n c m == S.chacha20_encrypt_bytes k n c m))
  (decreases (length m))
#reset-options "--max_fuel 0 --z3rlimit 100"
let rec lemma_chacha20_encrypt_bytes k n c m =
  let len = length m in
  let blocks_len = (1 * 64) * (len / (64 * 1)) in
  let part_len   = len % (64 * 1) in
  Math.Lemmas.lemma_div_mod len (64 * 1);
  Math.Lemmas.multiple_modulo_lemma (len / (64 * 1)) (64 * 1);
  Math.Lemmas.lemma_div_le (blocks_len) len 64;
  let blocks, last_block = split m blocks_len in
  lemma_chacha20_counter_mode_blocks k n c blocks;
  if part_len > 0
  then
    lemma_chacha20_block k n (c+1*(length m / 64))
  else ()
back to top