Hacl.Impl.Blake2b.fst
module Hacl.Impl.Blake2b
open FStar.Mul
open FStar.HyperStack
open FStar.HyperStack.ST
open Lib.IntTypes
open Lib.Buffer
open Lib.ByteBuffer
open Lib.LoopCombinators
module ST = FStar.HyperStack.ST
module Seq = Lib.Sequence
module Loops = Lib.LoopCombinators
module Spec = Spec.Blake2
#set-options "--z3rlimit 50 --max_ifuel 0 --max_fuel 0"
(* Define algorithm parameters *)
type word_t = normalize_term (Spec.word_t Spec.Blake2B)
type vector_wp = lbuffer word_t (size Spec.size_block_w)
type block_wp = lbuffer word_t (size Spec.size_block_w)
type block_p = lbuffer uint8 (size (Spec.size_block Spec.Blake2B))
type hash_wp = lbuffer word_t (size Spec.size_hash_w)
type index_t = n:size_t{size_v n < 16}
inline_for_extraction
let size_word : size_t = size 8
inline_for_extraction
let size_block : x:size_t{v x = Spec.size_block Spec.Blake2B /\ v x <= Spec.max_limb Spec.Blake2B} = (size Spec.size_block_w) *. size_word
noextract inline_for_extraction
let rounds_nat: x:size_nat{x == Spec.rounds Spec.Blake2.Blake2B} = 12
noextract inline_for_extraction
let rounds_t: x:size_t{x == size rounds_nat} = 12ul
/// Constants
let const_iv: x:ilbuffer (uint_t U64 PUB) 8ul{witnessed x (Spec.Blake2.ivTable Spec.Blake2.Blake2B) /\ recallable x} =
createL_global Spec.Blake2.list_iv_B
let const_sigma: x:ilbuffer Spec.Blake2.sigma_elt_t 160ul{witnessed x Spec.Blake2.const_sigma /\ recallable x} =
createL_global Spec.Blake2.list_sigma
let rTable_B: x:ilbuffer (rotval U64) 4ul{witnessed x (Spec.Blake2.rTable Spec.Blake2.Blake2B) /\ recallable x} =
createL_global Spec.Blake2.rTable_list_B
/// Accessors for constants
val get_iv:
s: size_t{size_v s < 8} ->
Stack word_t
(requires (fun h -> True))
(ensures (fun h0 z h1 -> h0 == h1 /\
uint_v z == uint_v (Seq.index (Spec.ivTable Spec.Blake2B) (size_v s))))
[@ Substitute ]
let get_iv s =
recall_contents const_iv (Spec.ivTable Spec.Blake2B);
let r = index const_iv s in
secret r
val set_iv:
hash: hash_wp ->
Stack unit
(requires (fun h -> live h hash))
(ensures (fun h0 _ h1 -> modifies1 hash h0 h1
/\ h1.[|hash|] == Seq.map secret (Spec.ivTable Spec.Blake2B)))
[@ Substitute ]
let set_iv hash =
recall_contents const_iv (Spec.ivTable Spec.Blake2B);
let h0 = ST.get() in
mapT (size (Spec.size_hash_w)) hash secret const_iv
val set_iv_sub:
b: lbuffer word_t 16ul ->
Stack unit
(requires (fun h -> live h b))
(ensures (fun h0 _ h1 -> modifies1 b h0 h1
/\ (let b0: Seq.lseq word_t 16 = h0.[|b|] in
let b1: Seq.lseq word_t 16 = h1.[|b|] in
let src = Seq.map secret (Spec.ivTable Spec.Blake2B) in
b1 == Seq.update_sub b0 8 8 src)))
[@ Substitute ]
let set_iv_sub b =
let h0 = ST.get () in
let half0 = sub b (size 0) (size 8) in
let half1 = sub b (size 8) (size 8) in
let h1 = ST.get () in
set_iv half1;
let h2 = ST.get () in
Seq.eq_intro h2.[|b|] (Seq.concat h2.[|half0|] h2.[|half1|]);
Seq.eq_intro h2.[|b|] (Seq.update_sub h0.[|b|] 8 8 (Seq.map secret (Spec.ivTable Spec.Blake2B)))
val get_sigma:
s: size_t{v s < 160} ->
Stack Spec.sigma_elt_t
(requires (fun h -> True))
(ensures (fun h0 z h1 -> h0 == h1 /\ v z == v (Seq.index Spec.const_sigma (v s))))
[@ Substitute ]
let get_sigma s =
recall_contents const_sigma Spec.const_sigma;
index const_sigma s
val get_sigma_sub:
start: size_t ->
i: size_t{v i < 16 /\ v start + v i < 160} ->
Stack Spec.sigma_elt_t
(requires (fun h -> True))
(ensures (fun h0 z h1 -> h0 == h1 /\ v z == v (Seq.index Spec.const_sigma (v start + v i))))
[@ Substitute ]
let get_sigma_sub start i = get_sigma (start +. i)
val get_r:
s: size_t{v s < 4} ->
Stack (rotval U64)
(requires (fun h -> True))
(ensures (fun h0 z h1 -> h0 == h1 /\ v z == v (Seq.index (Spec.rTable Spec.Blake2B) (v s))))
[@ Substitute ]
let get_r s =
recall_contents rTable_B (Spec.rTable Spec.Blake2B);
index rTable_B s
/// Define algorithm functions
val g1: wv:vector_wp -> a:index_t -> b:index_t -> r:rotval U64 ->
Stack unit
(requires (fun h -> live h wv))
(ensures (fun h0 _ h1 -> modifies1 wv h0 h1
/\ h1.[|wv|] == Spec.g1 Spec.Blake2B h0.[|wv|] (v a) (v b) r))
let g1 wv a b r =
let wv_a = wv.(a) in
let wv_b = wv.(b) in
wv.(a) <- (wv_a ^. wv_b) >>>. r
val g2: wv:vector_wp -> a:index_t -> b:index_t -> x:word_t ->
Stack unit
(requires (fun h -> live h wv))
(ensures (fun h0 _ h1 -> modifies1 wv h0 h1
/\ h1.[|wv|] == Spec.g2 Spec.Blake2B h0.[|wv|] (v a) (v b) x))
let g2 wv a b x =
let wv_a = wv.(a) in
let wv_b = wv.(b) in
wv.(a) <- add_mod #U64 (wv_a +. wv_b) x
val blake2_mixing : wv:vector_wp -> a:index_t -> b:index_t -> c:index_t -> d:index_t -> x:word_t -> y:word_t ->
Stack unit
(requires (fun h -> live h wv))
(ensures (fun h0 _ h1 -> modifies1 wv h0 h1
/\ h1.[|wv|] == Spec.blake2_mixing Spec.Blake2B h0.[|wv|] (v a) (v b) (v c) (v d) x y))
let blake2_mixing wv a b c d x y =
let r0 = get_r (size 0) in
let r1 = get_r (size 1) in
let r2 = get_r (size 2) in
let r3 = get_r (size 3) in
g2 wv a b x;
g1 wv d a r0;
g2 wv c d (u64 0);
g1 wv b c r1;
g2 wv a b y;
g1 wv d a r2;
g2 wv c d (u64 0);
g1 wv b c r3
val blake2_round1 : wv:vector_wp -> m:block_wp -> i:size_t{v i <= rounds_nat -1} ->
Stack unit
(requires (fun h -> live h wv /\ live h m
/\ disjoint wv m))
(ensures (fun h0 _ h1 -> modifies1 wv h0 h1
/\ h1.[|wv|] == Spec.blake2_round1 Spec.Blake2B h0.[|wv|] h0.[|m|] (v i)))
[@ Substitute ]
let blake2_round1 wv m i =
let start_idx = (i %. size 10) *. size 16 in
assert (v start_idx == (v i % 10) * 16);
let s0 = get_sigma_sub start_idx (size 0) in
let s1 = get_sigma_sub start_idx (size 1) in
let s2 = get_sigma_sub start_idx (size 2) in
let s3 = get_sigma_sub start_idx (size 3) in
let s4 = get_sigma_sub start_idx (size 4) in
let s5 = get_sigma_sub start_idx (size 5) in
let s6 = get_sigma_sub start_idx (size 6) in
let s7 = get_sigma_sub start_idx (size 7) in
blake2_mixing wv (size 0) (size 4) (size 8) (size 12) m.(s0) m.(s1);
blake2_mixing wv (size 1) (size 5) (size 9) (size 13) m.(s2) m.(s3);
blake2_mixing wv (size 2) (size 6) (size 10) (size 14) m.(s4) m.(s5);
blake2_mixing wv (size 3) (size 7) (size 11) (size 15) m.(s6) m.(s7)
val blake2_round2 : wv:vector_wp -> m:block_wp -> i:size_t{v i <= rounds_nat - 1} ->
Stack unit
(requires (fun h -> live h wv /\ live h m
/\ disjoint wv m))
(ensures (fun h0 _ h1 -> modifies1 wv h0 h1
/\ h1.[|wv|] == Spec.blake2_round2 Spec.Blake2B h0.[|wv|] h0.[|m|] (v i)))
[@ Substitute ]
let blake2_round2 wv m i =
let start_idx = (i %. size 10) *. size 16 in
assert (v start_idx == (v i % 10) * 16);
let s0 = get_sigma_sub start_idx (size 8) in
let s1 = get_sigma_sub start_idx (size 9) in
let s2 = get_sigma_sub start_idx (size 10) in
let s3 = get_sigma_sub start_idx (size 11) in
let s4 = get_sigma_sub start_idx (size 12) in
let s5 = get_sigma_sub start_idx (size 13) in
let s6 = get_sigma_sub start_idx (size 14) in
let s7 = get_sigma_sub start_idx (size 15) in
blake2_mixing wv (size 0) (size 5) (size 10) (size 15) m.(s0) m.(s1);
blake2_mixing wv (size 1) (size 6) (size 11) (size 12) m.(s2) m.(s3);
blake2_mixing wv (size 2) (size 7) (size 8) (size 13) m.(s4) m.(s5);
blake2_mixing wv (size 3) (size 4) (size 9) (size 14) m.(s6) m.(s7)
val blake2_round: wv:vector_wp -> m:block_wp -> i:size_t{v i <= rounds_nat - 1} ->
Stack unit
(requires (fun h -> live h wv /\ live h m
/\ disjoint wv m))
(ensures (fun h0 _ h1 -> modifies1 wv h0 h1
/\ h1.[|wv|] == Spec.blake2_round Spec.Blake2B h0.[|m|] (v i) h0.[|wv|]))
let blake2_round wv m i =
blake2_round1 wv m i;
blake2_round2 wv m i
val blake2_compress1:
wv: vector_wp
-> s: hash_wp
-> m: block_wp
-> offset: uint128
-> flag: bool ->
Stack unit
(requires (fun h -> live h s /\ live h m /\ live h wv
/\ disjoint wv s /\ disjoint wv m))
(ensures (fun h0 _ h1 -> modifies1 wv h0 h1
/\ h1.[|wv|] == Spec.blake2_compress1 Spec.Blake2B h0.[|wv|] h0.[|s|] h0.[|m|] offset flag))
[@ Substitute ]
let blake2_compress1 wv s m offset flag =
update_sub wv (size 0) (size 8) s;
set_iv_sub wv;
[@inline_let]
let low_offset = Spec.limb_to_word Spec.Blake2B offset in
[@inline_let]
let high_offset = Spec.limb_to_word Spec.Blake2B (offset >>. (size (bits (Spec.wt Spec.Blake2B)))) in
let wv_12 = logxor wv.(size 12) low_offset in
let wv_13 = logxor wv.(size 13) high_offset in
let wv_14 = logxor wv.(size 14) (ones (Spec.wt Spec.Blake2B) SEC) in
wv.(size 12) <- wv_12;
wv.(size 13) <- wv_13;
(if flag then wv.(size 14) <- wv_14)
#reset-options "--z3rlimit 100 --max_ifuel 1 --max_fuel 1"
val blake2_compress2 :
wv: vector_wp
-> m: block_wp ->
Stack unit
(requires (fun h -> live h wv /\ live h m /\ disjoint wv m))
(ensures (fun h0 _ h1 -> modifies1 wv h0 h1
/\ h1.[|wv|] == Spec.blake2_compress2 Spec.Blake2B h0.[|wv|] h0.[|m|]))
[@ Substitute ]
let blake2_compress2 wv m =
let h0 = ST.get () in
[@inline_let]
let spec h = Spec.blake2_round Spec.Blake2B h.[|m|] in
loop1 h0 rounds_t wv spec
(fun i ->
Loops.unfold_repeati rounds_nat (spec h0) h0.[|wv|] (v i);
blake2_round wv m i)
val blake2_compress3_inner :
wv: vector_wp
-> i: size_t{v i < 8}
-> s: hash_wp ->
Stack unit
(requires (fun h -> live h s /\ live h wv
/\ disjoint s wv /\ disjoint wv s))
(ensures (fun h0 _ h1 -> modifies1 s h0 h1
/\ h1.[|s|] == Spec.blake2_compress3_inner Spec.Blake2B h0.[|wv|] (v i) h0.[|s|]))
[@ Substitute ]
let blake2_compress3_inner wv i s =
let i_plus_8 = i +. (size 8) in
let hi_xor_wvi = s.(i) ^. wv.(i) in
let hi = logxor #U64 hi_xor_wvi wv.(i_plus_8) in
s.(i) <- hi
val blake2_compress3 :
wv: vector_wp
-> s: hash_wp ->
Stack unit
(requires (fun h -> live h s /\ live h wv
/\ disjoint wv s /\ disjoint s wv))
(ensures (fun h0 _ h1 -> modifies1 s h0 h1
/\ h1.[|s|] == Spec.blake2_compress3 Spec.Blake2B h0.[|wv|] h0.[|s|]))
[@ Substitute ]
let blake2_compress3 wv s =
[@inline_let]
let spec h = Spec.blake2_compress3_inner Spec.Blake2B h.[|wv|] in
let h0 = ST.get () in
loop1 h0 (size 8) s
(fun h -> spec h)
(fun i ->
Loops.unfold_repeati 8 (spec h0) (as_seq h0 s) (v i);
blake2_compress3_inner wv i s)
val blake2_compress :
s: hash_wp
-> m: block_wp
-> offset: uint128
-> flag: bool ->
Stack unit
(requires (fun h -> live h s /\ live h m
/\ disjoint s m /\ disjoint m s))
(ensures (fun h0 _ h1 -> modifies1 s h0 h1
/\ h1.[|s|] == Spec.blake2_compress Spec.Blake2B h0.[|s|] h0.[|m|] offset flag))
let blake2_compress s m offset flag =
let h0 = ST.get () in
[@inline_let]
let spec _ h1 = live h1 s /\ h1.[|s|] == Spec.blake2_compress Spec.Blake2B h0.[|s|] h0.[|m|] offset flag in
salloc1 h0 (size 16) (u64 0) (Ghost.hide (loc s)) spec
(fun wv ->
blake2_compress1 wv s m offset flag;
blake2_compress2 wv m;
blake2_compress3 wv s)
val blake2b_update_block:
hash: hash_wp
-> prev: uint128{uint_v prev <= Spec.Blake2.max_limb Spec.Blake2B}
-> d: block_p ->
Stack unit
(requires (fun h -> live h hash /\ live h d /\ disjoint hash d))
(ensures (fun h0 _ h1 -> modifies1 hash h0 h1
/\ h1.[|hash|] == Spec.blake2_update_block Spec.Blake2B (uint_v prev) h0.[|d|] h0.[|hash|]))
let blake2b_update_block hash prev d =
let h0 = ST.get () in
[@inline_let]
let spec _ h1 = live h1 hash /\ h1.[|hash|] == Spec.blake2_update_block Spec.Blake2B (uint_v prev) h0.[|d|] h0.[|hash|] in
salloc1 h0 (size 16) (u64 0) (Ghost.hide (loc hash)) spec
(fun block_w ->
uints_from_bytes_le block_w d;
let offset = prev in
blake2_compress hash block_w offset false)
val blake2b_init_hash:
hash: hash_wp
-> kk: size_t{v kk <= 64}
-> nn: size_t{1 <= v nn /\ v nn <= 64} ->
Stack unit
(requires (fun h -> live h hash))
(ensures (fun h0 _ h1 -> modifies1 hash h0 h1
/\ h1.[|hash|] == Spec.blake2_init_hash Spec.Blake2B (v kk) (v nn)))
[@ Substitute]
let blake2b_init_hash hash kk nn =
set_iv hash;
let s0 = hash.(size 0) in
let kk_shift_8 = shift_left #U64 (size_to_uint64 kk) (size 8) in
let s0' = s0 ^. (u64 0x01010000) ^. kk_shift_8 ^. size_to_uint64 nn in
hash.(size 0) <- s0'
val blake2b_init_branching:
hash: hash_wp
-> key_block: lbuffer uint8 size_block
-> kk: size_t{v kk <= 64}
-> k: lbuffer uint8 kk
-> nn: size_t{1 <= v nn /\ v nn <= 64} ->
Stack unit
(requires (fun h -> live h hash /\ live h k /\ live h key_block
/\ disjoint hash k /\ disjoint hash key_block /\ disjoint key_block k))
(ensures (fun h0 _ h1 -> modifies2 hash key_block h0 h1
/\ (if (v kk) = 0 then h1.[|hash|] == h0.[|hash|] else
let key_block1: Spec.block_s Spec.Blake2B = Seq.update_sub h0.[|key_block|] 0 (v kk) h0.[|k|] in
h1.[|hash|] == Spec.blake2_update_block Spec.Blake2B (Spec.size_block Spec.Blake2B) key_block1 h0.[|hash|])))
[@ Substitute ]
let blake2b_init_branching hash key_block kk k nn =
let h0 = ST.get () in
if kk <>. (size 0) then
begin
update_sub key_block (size 0) kk k;
assert(uint_v (secret size_block) <= Spec.max_limb Spec.Blake2B);
let prev64:uint64 = to_u64 (secret size_block) in
[@inline_let]
let prev = Spec.word_to_limb Spec.Blake2B prev64 in
blake2b_update_block hash prev key_block
end
val blake2b_init:
hash: hash_wp
-> kk: size_t{v kk <= 64}
-> k: lbuffer uint8 kk
-> nn: size_t{1 <= v nn /\ v nn <= 64} ->
Stack unit
(requires (fun h -> live h hash
/\ live h k
/\ disjoint hash k /\ disjoint k hash))
(ensures (fun h0 _ h1 -> modifies1 hash h0 h1
/\ h1.[|hash|] == Spec.Blake2.blake2_init Spec.Blake2B (v kk) h0.[|k|] (v nn)))
[@ Substitute ]
let blake2b_init hash kk k nn =
let h0 = ST.get () in
salloc1 h0 size_block (u8 0) (Ghost.hide (loc hash))
(fun _ h1 -> live h1 hash /\ h1.[|hash|] == Spec.blake2_init Spec.Blake2B (v kk) h0.[|k|] (v nn))
(fun key_block ->
blake2b_init_hash hash kk nn;
blake2b_init_branching hash key_block kk k nn)
val blake2b_update_last:
hash: hash_wp
-> prev: uint128{uint_v prev <= Spec.Blake2.max_limb Spec.Blake2B}
-> len: size_t{v len <= Spec.size_block Spec.Blake2B}
-> last: lbuffer uint8 len ->
Stack unit
(requires (fun h -> live h hash /\ live h last /\ disjoint hash last))
(ensures (fun h0 _ h1 -> modifies1 hash h0 h1
/\ h1.[|hash|] == Spec.Blake2.blake2_update_last Spec.Blake2B (uint_v prev) (v len) h0.[|last|] h0.[|hash|]))
let blake2b_update_last hash prev len last =
push_frame ();
let last_block = create size_block (u8 0) in
let last_block_w = create 16ul (u64 0) in
update_sub last_block (size 0) len last;
uints_from_bytes_le last_block_w last_block;
let offset = prev in
blake2_compress hash last_block_w offset true;
pop_frame ()
val blake2b_finish:
nn: size_t{1 <= v nn /\ v nn <= 64}
-> output: lbuffer uint8 nn
-> hash: hash_wp ->
Stack unit
(requires (fun h -> live h hash
/\ live h output /\ disjoint output hash /\ disjoint hash output))
(ensures (fun h0 _ h1 -> modifies1 output h0 h1
/\ h1.[|output|] == Spec.Blake2.blake2_finish Spec.Blake2B h0.[|hash|] (v nn)))
let blake2b_finish nn output hash =
let h0 = ST.get () in
salloc1 h0 (size 64) (u8 0) (Ghost.hide (loc output))
(fun _ h1 -> live h1 output /\ h1.[|output|] == Spec.Blake2.blake2_finish Spec.Blake2B h0.[|hash|] (v nn))
(fun full ->
uints_to_bytes_le (size 8) full hash;
let final = sub full (size 0) nn in
copy output final)
noextract inline_for_extraction
let spec_prev1 (klen:size_nat{klen == 0 \/ klen == 1})
(dlen:size_nat{if klen = 0 then dlen < pow2 128 else dlen + 128 < pow2 128})
(i:size_nat{i < dlen/128}) : r:nat{r < pow2 128} = (klen + i + 1) * 128
noextract inline_for_extraction
let spec_prev2 (klen:size_nat{klen == 0 \/ klen == 1})
(dlen:size_nat{if klen = 0 then dlen < pow2 128 else dlen + 128 < pow2 128})
(i:size_nat{i == dlen/128}) : r:nat{r < pow2 128} = (klen * 128) + dlen
#push-options "--z3rlimit 150 --max_fuel 1"
noextract inline_for_extraction
let prev1 (klen:size_t{v klen == 0 \/ v klen == 1})
(dlen:size_t{if v klen = 0 then v dlen < pow2 128 else v dlen + 128 < pow2 128})
(i:size_t{v i < v dlen / 128}) :
(prev:uint128{uint_v prev == spec_prev1 (v klen) (v dlen) (v i)})
=
let p1: size_t = klen +. i +. (size 1) in
let p64: uint64 = (to_u64 p1) *. (u64 128) in
let p: uint128 = to_u128 p64 in
assert (uint_v p == spec_prev1 (v klen) (v dlen) (v i));
p
#pop-options
noextract inline_for_extraction
let prev2 (klen:size_t{v klen == 0 \/ v klen == 1})
(dlen:size_t{if v klen = 0 then v dlen < pow2 128 else v dlen + 128 < pow2 128})
(i:size_t{v i == v dlen/128}) :
(prev:uint128{uint_v prev == spec_prev2 (v klen) (v dlen) (v i)})
= to_u128 dlen +. to_u128 (klen *. size 128)
#set-options "--z3rlimit 150"
inline_for_extraction
val blake2b_update:
hash: hash_wp
-> ll: size_t
-> d: lbuffer uint8 ll
-> kk: size_t{v kk <= 64 /\ (if v kk = 0 then v ll < pow2 128 else v ll + 128 < pow2 128)} ->
Stack unit
(requires (fun h -> live h hash /\ live h d /\ disjoint hash d))
(ensures (fun h0 _ h1 -> modifies1 hash h0 h1
/\ h1.[|hash|] == Spec.blake2_update Spec.Blake2B h0.[|hash|] h0.[|d|] (v kk)))
let blake2b_update hash ll d kk =
let h0 = ST.get() in
let klen = if kk =. size 0 then size 0 else size 1 in
loopi_blocks size_block ll d
(Spec.spec_update_block Spec.Blake2B ((v klen + 1) * (Spec.size_block Spec.Blake2B)))
(Spec.spec_update_last Spec.Blake2B (v klen * (Spec.size_block Spec.Blake2B) + v ll))
(fun i block hash -> blake2b_update_block hash (prev1 klen ll i) block)
(fun i len last hash -> blake2b_update_last hash (prev2 klen ll i) len last)
hash
#set-options "--z3rlimit 50"
val blake2b:
nn:size_t{1 <= v nn /\ v nn <= 64}
-> output: lbuffer uint8 nn
-> ll: size_t
-> d: lbuffer uint8 ll
-> kk: size_t{v kk <= 64 /\ (if v kk = 0 then v ll < pow2 128 else v ll + 128 < pow2 128)}
-> k: lbuffer uint8 kk ->
Stack unit
(requires (fun h -> live h output
/\ live h d
/\ live h k
/\ disjoint output d
/\ disjoint output k))
(ensures (fun h0 _ h1 -> modifies (loc output) h0 h1
/\ h1.[|output|] == Spec.Blake2.blake2b h0.[|d|] (v kk) h0.[|k|] (v nn)))
let blake2b nn output ll d kk k =
let h0 = ST.get () in
salloc1 h0 (size 8) (u64 0) (Ghost.hide (loc output))
(fun _ h1 -> live h1 output /\ h1.[|output|] == Spec.Blake2.blake2b h0.[|d|] (v kk) h0.[|k|] (v nn))
(fun hash ->
blake2b_init hash kk k nn;
blake2b_update hash ll d kk;
blake2b_finish nn output hash)