101 lines
3.1 KiB
Erlang
101 lines
3.1 KiB
Erlang
%%%=============================================================================
|
|
%%% @copyright (C) 2019, Aeternity Anstalt
|
|
%%% @doc
|
|
%%% SipHash-2-4 without standard IV xor and specialized to precomputed key and 8 byte nonces
|
|
%%% @end
|
|
%%%=============================================================================
|
|
|
|
-module(gmminer_siphash24).
|
|
|
|
-export([create_keys/1,
|
|
hash/5]).
|
|
|
|
-ifdef(TEST).
|
|
-compile([export_all, nowarn_export_all]).
|
|
-endif.
|
|
|
|
-define(MAX64, 16#ffffffffffffffff).
|
|
|
|
-type hashable() :: integer().
|
|
-type siphash_key() :: integer().
|
|
-type sip_quadruple() :: {integer(), integer(), integer(), integer()}.%% in fact, uint64
|
|
|
|
-export_type([hashable/0,
|
|
siphash_key/0]).
|
|
|
|
%%%=============================================================================
|
|
%%% API
|
|
%%%=============================================================================
|
|
|
|
-spec create_keys(binary()) ->
|
|
{gmminer_siphash24:siphash_key(),
|
|
gmminer_siphash24:siphash_key(),
|
|
gmminer_siphash24:siphash_key(),
|
|
gmminer_siphash24:siphash_key()}.
|
|
create_keys(Header) ->
|
|
AuxHash = <<_:32/binary>> = gmminer_blake2b_256:hash(Header),
|
|
<<K0:64/little-unsigned,
|
|
K1:64/little-unsigned,
|
|
K2:64/little-unsigned,
|
|
K3:64/little-unsigned>> = AuxHash,
|
|
{K0, K1, K2, K3}.
|
|
|
|
-spec hash(siphash_key(), siphash_key(), siphash_key(), siphash_key(), hashable()) -> hashable().
|
|
hash(K0, K1, K2, K3, Nonce) ->
|
|
V0 = K0,
|
|
V1 = K1,
|
|
V2 = K2,
|
|
V3 = K3 bxor Nonce,
|
|
{V01, V11, V21, V31} =
|
|
sip_round(sip_round(sip_round(sip_round(sip_change(Nonce, sip_round(sip_round({V0, V1, V2, V3}))))))),
|
|
rotl64(((V01 bxor V11) bxor (V21 bxor V31)), 17) band 16#ffffffffffffffff.
|
|
|
|
|
|
%%%=============================================================================
|
|
%%% Internal functions
|
|
%%%=============================================================================
|
|
|
|
%% 1:
|
|
%% v0 += v1; v2 += v3; v1 = ROTL(v1,13); \
|
|
%% v3 = ROTL(v3,16);
|
|
|
|
%% 2:
|
|
%% v1 ^= v0; v3 ^= v2; \
|
|
%% v0 = ROTL(v0,32);
|
|
|
|
%% 3:
|
|
%% v2 += v1; v0 += v3; \
|
|
%% v1 = ROTL(v1,17); v3 = ROTL(v3,21); \
|
|
|
|
%% 4:
|
|
%% v1 ^= v2; v3 ^= v0; v2 = ROTL(v2,32); \
|
|
|
|
-spec sip_round(sip_quadruple()) -> sip_quadruple().
|
|
sip_round({_V0, _V1, _V2, _V3} = Vs) ->
|
|
sip_round4(sip_round3(sip_round2(sip_round1(Vs)))).
|
|
|
|
-spec sip_round1(sip_quadruple()) -> sip_quadruple().
|
|
sip_round1({V0, V1, V2, V3}) ->
|
|
{(V0 + V1) band ?MAX64, rotl64(V1, 13), (V2 + V3) band ?MAX64, rotl64(V3, 16)}.
|
|
|
|
-spec sip_round2(sip_quadruple()) -> sip_quadruple().
|
|
sip_round2({V0, V1, V2, V3}) ->
|
|
{rotl64(V0, 32), V1 bxor V0, V2, V3 bxor V2}.
|
|
|
|
-spec sip_round3(sip_quadruple()) -> sip_quadruple().
|
|
sip_round3({V0, V1, V2, V3}) ->
|
|
{(V0 + V3) band ?MAX64, rotl64(V1, 17), (V2 + V1) band ?MAX64, rotl64(V3, 21)}.
|
|
|
|
-spec sip_round4(sip_quadruple()) -> sip_quadruple().
|
|
sip_round4({V0, V1, V2, V3}) ->
|
|
{V0, V1 bxor V2, rotl64(V2, 32), V3 bxor V0}.
|
|
|
|
-spec sip_change(integer(), sip_quadruple()) -> sip_quadruple().
|
|
sip_change(Nonce, {V0, V1, V2, V3}) ->
|
|
{V0 bxor Nonce, V1, V2 bxor 16#ff, V3}.
|
|
|
|
-spec rotl64(integer(), integer()) -> integer().
|
|
rotl64(X, B) ->
|
|
((X bsl B) bor (X bsr (64 - B))) band 16#ffffffffffffffff.
|
|
|