%%%============================================================================= %%% @copyright (C) 2019, Hans Svensson %%% @doc %%% BLAKE2 implementation in Erlang - for details see: https://blake2.net %%% @end %%%============================================================================= -module(eblake2). %% API exports -export([ blake2b/2 , blake2b/3 , blake2s/2 , blake2s/3 , hmac/3 ]). -define(MAXINT_32, 16#FFFFffff). -define(MAXINT_64, 16#FFFFffffFFFFffff). %%==================================================================== %% API functions %%==================================================================== -spec blake2b(HashLen :: integer(), Msg :: binary()) -> {ok, binary()}. blake2b(HashLen, Msg) -> blake2b(HashLen, Msg, <<>>). -spec blake2b(HashLen :: integer(), Msg :: binary(), Key :: binary()) -> {ok, binary()}. blake2b(HashLen, Msg0, Key) -> %% If message should be keyed, prepend message with padded key. Msg = <<(pad(128, Key))/binary, Msg0/binary>>, %% Set up the initial state Init = (16#01010000 + (byte_size(Key) bsl 8) + HashLen), <> = blake2b_iv(), H = <<(H0 bxor Init):64, H1_7/binary>>, %% Perform the compression - message will be chopped into 128-byte chunks. State = blake2b_compress(H, Msg, 0), %% Just return the requested part of the hash {ok, binary_part(to_little_endian(64, State), {0, HashLen})}. -spec blake2s(HashLen :: integer(), Msg :: binary()) -> {ok, binary()}. blake2s(HashLen, Msg) -> blake2s(HashLen, Msg, <<>>). -spec blake2s(HashLen :: integer(), Msg :: binary(), Key :: binary()) -> {ok, binary()}. blake2s(HashLen, Msg0, Key) -> %% If message should be keyed, prepend message with padded key. Msg = <<(pad(64, Key))/binary, Msg0/binary>>, %% Set up the initial state Init = (16#01010000 + (byte_size(Key) bsl 8) + HashLen), <> = blake2s_iv(), H = <<(H0 bxor Init):32, H1_7/binary>>, %% Perform the compression - message will be chopped into 64-byte chunks. State = blake2s_compress(H, Msg, 0), %% Just return the requested part of the hash {ok, binary_part(to_little_endian(32, State), {0, HashLen})}. -spec hmac(Hash :: blake2b | blake2s, Key :: binary(), Data :: binary()) -> {ok, binary()}. hmac(blake2b, Key, Data) -> hmac(128, fun(D) -> {ok, H} = blake2b(64, D), H end, Key, Data); hmac(blake2s, Key, Data) -> hmac(64, fun(D) -> {ok, H} = blake2s(32, D), H end, Key, Data). %%==================================================================== %% Internal functions %%==================================================================== %% %% Blake2b blake2b_compress(H, <>, BCompr) when Rest /= <<>> -> H1 = blake2b_compress(H, <>, BCompr + 128, false), blake2b_compress(H1, Rest, BCompr + 128); blake2b_compress(H, SmallChunk, BCompr) -> Size = byte_size(SmallChunk), FillSize = (128 - Size) * 8, blake2b_compress(H, <>, BCompr + Size, true). blake2b_compress(H, Chunk0, BCompr, Last) -> Chunk = to_big_endian(64, Chunk0), <> = <>, V12_ = V12 bxor (BCompr band ?MAXINT_64), V13_ = V13 bxor ((BCompr bsr 64) band ?MAXINT_64), V14_ = case Last of false -> V14; true -> V14 bxor ?MAXINT_64 end, V = <>, <> = lists:foldl(fun(Round, Vx) -> blake2b_mix(Round, Chunk, Vx) end, V, lists:seq(0, 11)), <> = H, <<((HInt bxor VLow) bxor VHigh):(8*64)>>. blake2b_mix(Rnd, Chunk, V) -> <> = V, <> = Chunk, Ms = {M0, M1, M2, M3, M4, M5, M6, M7, M8, M9, M10, M11, M12, M13, M14, M15}, {S0, S1, S2, S3, S4, S5, S6, S7, S8, S9, S10, S11, S12, S13, S14, S15} = sigma(Rnd rem 10, Ms), {Vx0, Vx4, Vx8, Vx12} = blake2b_mix(V0, V4, V8, V12, S0, S1), {Vx1, Vx5, Vx9, Vx13} = blake2b_mix(V1, V5, V9, V13, S2, S3), {Vx2, Vx6, Vx10, Vx14} = blake2b_mix(V2, V6, V10, V14, S4, S5), {Vx3, Vx7, Vx11, Vx15} = blake2b_mix(V3, V7, V11, V15, S6, S7), {Vy0, Vy5, Vy10, Vy15} = blake2b_mix(Vx0, Vx5, Vx10, Vx15, S8, S9), {Vy1, Vy6, Vy11, Vy12} = blake2b_mix(Vx1, Vx6, Vx11, Vx12, S10, S11), {Vy2, Vy7, Vy8, Vy13} = blake2b_mix(Vx2, Vx7, Vx8, Vx13, S12, S13), {Vy3, Vy4, Vy9, Vy14} = blake2b_mix(Vx3, Vx4, Vx9, Vx14, S14, S15), <>. blake2b_mix(Va, Vb, Vc, Vd, X, Y) -> Va1 = (Va + Vb + X) band ?MAXINT_64, Vd1 = rotr64(32, Vd bxor Va1), Vc1 = (Vc + Vd1) band ?MAXINT_64, Vb1 = rotr64(24, Vb bxor Vc1), Va2 = (Va1 + Vb1 + Y) band ?MAXINT_64, Vd2 = rotr64(16, Va2 bxor Vd1), Vc2 = (Vc1 + Vd2) band ?MAXINT_64, Vb2 = rotr64(63, Vb1 bxor Vc2), {Va2, Vb2, Vc2, Vd2}. blake2b_iv() -> IV0 = 16#6A09E667F3BCC908, IV1 = 16#BB67AE8584CAA73B, IV2 = 16#3C6EF372FE94F82B, IV3 = 16#A54FF53A5F1D36F1, IV4 = 16#510E527FADE682D1, IV5 = 16#9B05688C2B3E6C1F, IV6 = 16#1F83D9ABFB41BD6B, IV7 = 16#5BE0CD19137E2179, <>. sigma(0, {E0, E1, E2, E3, E4, E5, E6, E7, E8, E9, E10, E11, E12, E13, E14, E15}) -> { E0, E1, E2, E3, E4, E5, E6, E7, E8, E9, E10, E11, E12, E13, E14, E15}; sigma(1, {E0, E1, E2, E3, E4, E5, E6, E7, E8, E9, E10, E11, E12, E13, E14, E15}) -> {E14, E10, E4, E8, E9, E15, E13, E6, E1, E12, E0, E2, E11, E7, E5, E3}; sigma(2, {E0, E1, E2, E3, E4, E5, E6, E7, E8, E9, E10, E11, E12, E13, E14, E15}) -> {E11, E8, E12, E0, E5, E2, E15, E13, E10, E14, E3, E6, E7, E1, E9, E4}; sigma(3, {E0, E1, E2, E3, E4, E5, E6, E7, E8, E9, E10, E11, E12, E13, E14, E15}) -> { E7, E9, E3, E1, E13, E12, E11, E14, E2, E6, E5, E10, E4, E0, E15, E8}; sigma(4, {E0, E1, E2, E3, E4, E5, E6, E7, E8, E9, E10, E11, E12, E13, E14, E15}) -> { E9, E0, E5, E7, E2, E4, E10, E15, E14, E1, E11, E12, E6, E8, E3, E13}; sigma(5, {E0, E1, E2, E3, E4, E5, E6, E7, E8, E9, E10, E11, E12, E13, E14, E15}) -> { E2, E12, E6, E10, E0, E11, E8, E3, E4, E13, E7, E5, E15, E14, E1, E9}; sigma(6, {E0, E1, E2, E3, E4, E5, E6, E7, E8, E9, E10, E11, E12, E13, E14, E15}) -> {E12, E5, E1, E15, E14, E13, E4, E10, E0, E7, E6, E3, E9, E2, E8, E11}; sigma(7, {E0, E1, E2, E3, E4, E5, E6, E7, E8, E9, E10, E11, E12, E13, E14, E15}) -> {E13, E11, E7, E14, E12, E1, E3, E9, E5, E0, E15, E4, E8, E6, E2, E10}; sigma(8, {E0, E1, E2, E3, E4, E5, E6, E7, E8, E9, E10, E11, E12, E13, E14, E15}) -> { E6, E15, E14, E9, E11, E3, E0, E8, E12, E2, E13, E7, E1, E4, E10, E5}; sigma(9, {E0, E1, E2, E3, E4, E5, E6, E7, E8, E9, E10, E11, E12, E13, E14, E15}) -> {E10, E2, E8, E4, E7, E6, E1, E5, E15, E11, E9, E14, E3, E12, E13, E0}. rotr64(N, I64) -> <> = rotr641(N, <>), I64rot. rotr641(16, <>) -> <>; rotr641(24, <>) -> <>; rotr641(32, <>) -> <>; rotr641(63, <>) -> <>. %% Blake2s blake2s_iv() -> IV0 = 16#6A09E667, IV1 = 16#BB67AE85, IV2 = 16#3C6EF372, IV3 = 16#A54FF53A, IV4 = 16#510E527F, IV5 = 16#9B05688C, IV6 = 16#1F83D9AB, IV7 = 16#5BE0CD19, <>. blake2s_compress(H, <>, BCompr) when Rest /= <<>> -> H1 = blake2s_compress(H, <>, BCompr + 64, false), blake2s_compress(H1, Rest, BCompr + 64); blake2s_compress(H, SmallChunk, BCompr) -> Size = byte_size(SmallChunk), FillSize = (64 - Size) * 8, blake2s_compress(H, <>, BCompr + Size, true). blake2s_compress(H, Chunk0, BCompr, Last) -> Chunk = to_big_endian(32, Chunk0), <> = <>, V12_ = V12 bxor (BCompr band ?MAXINT_32), V13_ = V13 bxor ((BCompr bsr 32) band ?MAXINT_32), V14_ = case Last of false -> V14; true -> V14 bxor ?MAXINT_32 end, V = <>, <> = lists:foldl(fun(Round, Vx) -> blake2s_mix(Round, Chunk, Vx) end, V, lists:seq(0, 9)), <> = H, <<((HInt bxor VLow) bxor VHigh):(8*32)>>. blake2s_mix(Rnd, Chunk, V) -> <> = V, <> = Chunk, Ms = {M0, M1, M2, M3, M4, M5, M6, M7, M8, M9, M10, M11, M12, M13, M14, M15}, {S0, S1, S2, S3, S4, S5, S6, S7, S8, S9, S10, S11, S12, S13, S14, S15} = sigma(Rnd rem 10, Ms), {Vx0, Vx4, Vx8, Vx12} = blake2s_mix(V0, V4, V8, V12, S0, S1), {Vx1, Vx5, Vx9, Vx13} = blake2s_mix(V1, V5, V9, V13, S2, S3), {Vx2, Vx6, Vx10, Vx14} = blake2s_mix(V2, V6, V10, V14, S4, S5), {Vx3, Vx7, Vx11, Vx15} = blake2s_mix(V3, V7, V11, V15, S6, S7), {Vy0, Vy5, Vy10, Vy15} = blake2s_mix(Vx0, Vx5, Vx10, Vx15, S8, S9), {Vy1, Vy6, Vy11, Vy12} = blake2s_mix(Vx1, Vx6, Vx11, Vx12, S10, S11), {Vy2, Vy7, Vy8, Vy13} = blake2s_mix(Vx2, Vx7, Vx8, Vx13, S12, S13), {Vy3, Vy4, Vy9, Vy14} = blake2s_mix(Vx3, Vx4, Vx9, Vx14, S14, S15), <>. blake2s_mix(Va, Vb, Vc, Vd, X, Y) -> Va1 = (Va + Vb + X) band ?MAXINT_32, Vd1 = rotr32(16, Vd bxor Va1), Vc1 = (Vc + Vd1) band ?MAXINT_32, Vb1 = rotr32(12, Vb bxor Vc1), Va2 = (Va1 + Vb1 + Y) band ?MAXINT_32, Vd2 = rotr32(8, Va2 bxor Vd1), Vc2 = (Vc1 + Vd2) band ?MAXINT_32, Vb2 = rotr32(7, Vb1 bxor Vc2), {Va2, Vb2, Vc2, Vd2}. rotr32(N, I32) -> <> = rotr321(N, <>), I32rot. rotr321(16, <>) -> <>; rotr321(12, <>) -> <>; rotr321(8, <>) -> <>; rotr321(7, <>) -> <>. pad(N, Bin) -> case (N - (byte_size(Bin) rem N)) rem N of 0 -> Bin; Pad -> <> end. to_big_endian(X, Bin) -> to_big_endian(X, Bin, <<>>). to_big_endian(_X, <<>>, Acc) -> Acc; to_big_endian(X, Bin, Acc) -> <> = Bin, to_big_endian(X, Rest, <>). to_little_endian(X, Bin) -> to_little_endian(X, Bin, <<>>). to_little_endian(_X, <<>>, Acc) -> Acc; to_little_endian(X, Bin, Acc) -> <> = Bin, to_little_endian(X, Rest, <>). %% HMAC hmac(BLen, HFun, Key, Data) -> Block1 = hmac_format_key(HFun, Key, 16#36, BLen), Hash1 = HFun(<>), Block2 = hmac_format_key(HFun, Key, 16#5C, BLen), HFun(<>). hmac_format_key(HFun, Key0, Pad, BLen) -> Key1 = case byte_size(Key0) =< BLen of true -> Key0; false -> HFun(Key0) end, Key2 = pad(Key1, BLen, 0), <> = <>, << <<(Word bxor PadWord):32>> || <> <= Key2 >>. pad(Data, MinSize, PadByte) -> case byte_size(Data) of N when N >= MinSize -> Data; N -> PadData = << <> || _ <- lists:seq(1, MinSize - N) >>, <> end.