187 lines
5.2 KiB
OCaml
187 lines
5.2 KiB
OCaml
module By = Digestif_by
|
|
module Bi = Digestif_bi
|
|
|
|
let nist_padding = 0x06L
|
|
let keccak_padding = 0x01L
|
|
|
|
module Int64 = struct
|
|
include Int64
|
|
|
|
let ( lsl ) = Int64.shift_left
|
|
let ( lsr ) = Int64.shift_right_logical
|
|
let ( asr ) = Int64.shift_right
|
|
let ( lor ) = Int64.logor
|
|
let ( land ) = Int64.logand
|
|
let ( lxor ) = Int64.logxor
|
|
let ( + ) = Int64.add
|
|
let ror64 a n = (a lsr n) lor (a lsl (64 - n))
|
|
let rol64 a n = (a lsl n) lor (a lsr (64 - n))
|
|
end
|
|
|
|
module Unsafe (P : sig
|
|
val padding : int64
|
|
end) =
|
|
struct
|
|
type ctx = {
|
|
q : int64 array;
|
|
rsize : int;
|
|
(* block size *)
|
|
mdlen : int;
|
|
(* output size *)
|
|
mutable pt : int;
|
|
}
|
|
|
|
let dup ctx =
|
|
{ q = Array.copy ctx.q; rsize = ctx.rsize; mdlen = ctx.mdlen; pt = ctx.pt }
|
|
|
|
let init mdlen =
|
|
let rsize = 200 - (2 * mdlen) in
|
|
{ q = Array.make 25 0L; rsize; mdlen; pt = 0 }
|
|
|
|
let keccakf_rounds = 24
|
|
|
|
let keccaft_rndc : int64 array =
|
|
[|
|
|
0x0000000000000001L; 0x0000000000008082L; 0x800000000000808aL;
|
|
0x8000000080008000L; 0x000000000000808bL; 0x0000000080000001L;
|
|
0x8000000080008081L; 0x8000000000008009L; 0x000000000000008aL;
|
|
0x0000000000000088L; 0x0000000080008009L; 0x000000008000000aL;
|
|
0x000000008000808bL; 0x800000000000008bL; 0x8000000000008089L;
|
|
0x8000000000008003L; 0x8000000000008002L; 0x8000000000000080L;
|
|
0x000000000000800aL; 0x800000008000000aL; 0x8000000080008081L;
|
|
0x8000000000008080L; 0x0000000080000001L; 0x8000000080008008L;
|
|
|]
|
|
|
|
let keccaft_rotc : int array =
|
|
[|
|
|
1; 3; 6; 10; 15; 21; 28; 36; 45; 55; 2; 14; 27; 41; 56; 8; 25; 43; 62; 18;
|
|
39; 61; 20; 44;
|
|
|]
|
|
|
|
let keccakf_piln : int array =
|
|
[|
|
|
10; 7; 11; 17; 18; 3; 5; 16; 8; 21; 24; 4; 15; 23; 19; 13; 12; 2; 20; 14;
|
|
22; 9; 6; 1;
|
|
|]
|
|
|
|
let sha3_keccakf (q : int64 array) =
|
|
for r = 0 to keccakf_rounds - 1 do
|
|
let ( lxor ) = Int64.( lxor ) in
|
|
let lnot = Int64.lognot in
|
|
let ( land ) = Int64.( land ) in
|
|
(* Theta *)
|
|
let bc =
|
|
Array.init 5 (fun i ->
|
|
q.(i) lxor q.(i + 5) lxor q.(i + 10) lxor q.(i + 15) lxor q.(i + 20))
|
|
in
|
|
for i = 0 to 4 do
|
|
let t = bc.((i + 4) mod 5) lxor Int64.rol64 bc.((i + 1) mod 5) 1 in
|
|
for k = 0 to 4 do
|
|
let j = k * 5 in
|
|
q.(j + i) <- q.(j + i) lxor t
|
|
done
|
|
done ;
|
|
|
|
(* Rho Pi *)
|
|
let t = ref q.(1) in
|
|
let _ =
|
|
Array.iteri
|
|
(fun i rotc ->
|
|
let j = keccakf_piln.(i) in
|
|
bc.(0) <- q.(j) ;
|
|
q.(j) <- Int64.rol64 !t rotc ;
|
|
t := bc.(0))
|
|
keccaft_rotc in
|
|
|
|
(* Chi *)
|
|
for k = 0 to 4 do
|
|
let j = k * 5 in
|
|
let bc = Array.init 5 (fun i -> q.(j + i)) in
|
|
for i = 0 to 4 do
|
|
q.(j + i) <-
|
|
q.(j + i) lxor (lnot bc.((i + 1) mod 5) land bc.((i + 2) mod 5))
|
|
done
|
|
done ;
|
|
|
|
(* Iota *)
|
|
q.(0) <- q.(0) lxor keccaft_rndc.(r)
|
|
done
|
|
|
|
let masks =
|
|
[|
|
|
0xffffffffffffff00L; 0xffffffffffff00ffL; 0xffffffffff00ffffL;
|
|
0xffffffff00ffffffL; 0xffffff00ffffffffL; 0xffff00ffffffffffL;
|
|
0xff00ffffffffffffL; 0x00ffffffffffffffL;
|
|
|]
|
|
|
|
let feed :
|
|
type a. get_uint8:(a -> int -> int) -> ctx -> a -> int -> int -> unit =
|
|
fun ~get_uint8 ctx buf off len ->
|
|
let ( && ) = ( land ) in
|
|
|
|
let ( lxor ) = Int64.( lxor ) in
|
|
let ( land ) = Int64.( land ) in
|
|
let ( lor ) = Int64.( lor ) in
|
|
let ( lsr ) = Int64.( lsr ) in
|
|
let ( lsl ) = Int64.( lsl ) in
|
|
|
|
let j = ref ctx.pt in
|
|
|
|
for i = 0 to len - 1 do
|
|
let v =
|
|
(ctx.q.(!j / 8) land (0xffL lsl ((!j && 0x7) * 8))) lsr ((!j && 0x7) * 8)
|
|
in
|
|
let v = v lxor Int64.of_int (get_uint8 buf (off + i)) in
|
|
ctx.q.(!j / 8) <-
|
|
ctx.q.(!j / 8) land masks.(!j && 0x7) lor (v lsl ((!j && 0x7) * 8)) ;
|
|
incr j ;
|
|
if !j >= ctx.rsize
|
|
then (
|
|
sha3_keccakf ctx.q ;
|
|
j := 0)
|
|
done ;
|
|
|
|
ctx.pt <- !j
|
|
|
|
let unsafe_feed_bytes ctx buf off len =
|
|
let get_uint8 buf off = Char.code (By.get buf off) in
|
|
feed ~get_uint8 ctx buf off len
|
|
|
|
let unsafe_feed_bigstring : ctx -> Bi.t -> int -> int -> unit =
|
|
fun ctx buf off len ->
|
|
let get_uint8 buf off = Char.code (Bi.get buf off) in
|
|
feed ~get_uint8 ctx buf off len
|
|
|
|
let unsafe_get ctx =
|
|
let ( && ) = ( land ) in
|
|
|
|
let ( lxor ) = Int64.( lxor ) in
|
|
let ( lsl ) = Int64.( lsl ) in
|
|
|
|
let v = ctx.q.(ctx.pt / 8) in
|
|
let v = v lxor (P.padding lsl ((ctx.pt && 0x7) * 8)) in
|
|
ctx.q.(ctx.pt / 8) <- v ;
|
|
|
|
let v = ctx.q.((ctx.rsize - 1) / 8) in
|
|
let v = v lxor (0x80L lsl (((ctx.rsize - 1) && 0x7) * 8)) in
|
|
ctx.q.((ctx.rsize - 1) / 8) <- v ;
|
|
|
|
sha3_keccakf ctx.q ;
|
|
|
|
(* Get hash *)
|
|
(* if the hash size in bytes is not a multiple of 8 (meaning it is
|
|
not composed of whole int64 words, like for sha3_224), we
|
|
extract the whole last int64 word from the state [ctx.st] and
|
|
cut the hash at the right size after conversion to bytes. *)
|
|
let n =
|
|
let r = ctx.mdlen mod 8 in
|
|
ctx.mdlen + if r = 0 then 0 else 8 - r in
|
|
|
|
let hash = By.create n in
|
|
for i = 0 to (n / 8) - 1 do
|
|
By.unsafe_set_64 hash (i * 8)
|
|
(if Sys.big_endian then By.swap64 ctx.q.(i) else ctx.q.(i))
|
|
done ;
|
|
|
|
By.sub hash 0 ctx.mdlen
|
|
end
|