random and totp stuff

This commit is contained in:
Umgeher Torgersen 2022-12-27 22:21:05 +00:00
parent 93e1091145
commit f01796548d
3 changed files with 121 additions and 0 deletions

105
src/utools.erl Normal file
View file

@ -0,0 +1,105 @@
-module(utools).
-export([b32_decode/1]).
-export([b32_encode/1]).
-export([hotp/2]).
-export([rand_bytes/0, rand_bytes/1]).
-export([rand_chars/1]).
-export([rand_hash/0]).
-export([totp/1, totp/2]).
-export([totp_generate/0]).
b32_decode({<<V, "======">>, Bits}) ->
<<Bits/bits, (b32_std_dec(V) bsr 2):3>>;
b32_decode({<<V, "====">>, Bits}) ->
<<Bits/bits, (b32_std_dec(V) bsr 4):1>>;
b32_decode({<<V, "===">>, Bits}) ->
<<Bits/bits, (b32_std_dec(V) bsr 1):4>>;
b32_decode({<<V, "=">>, Bits}) ->
<<Bits/bits, (b32_std_dec(V) bsr 3):2>>;
b32_decode({<<V, R/binary>>, Bits}) ->
b32_decode({R, <<Bits/bits, (b32_std_dec(V)):5>>});
b32_decode({<<>>, Bits}) ->
Bits;
b32_decode(V) when is_list(V) ->
b32_decode(list_to_binary(V));
b32_decode(V) when is_binary(V) ->
b32_decode({V, <<>>}).
b32_encode({body, V}) ->
O = 5 * (byte_size(V) div 5),
<<Body:O/binary, R/binary>> = V,
{<< <<(b32_std_enc(X))>> || <<X:5>> <= Body>>, R};
b32_encode({rest, V}) ->
O = 5 * (bit_size(V) div 5),
<<Body:O/bits, Rest/bits>> = V,
B0 = << <<(b32_std_enc(X))>> || <<X:5>> <= Body>>,
{B1, Pad} = case Rest of
<<I:3>> -> {<<(b32_std_enc(I bsl 2))>>, 6};
<<I:1>> -> {<<(b32_std_enc(I bsl 4))>>, 4};
<<I:4>> -> {<<(b32_std_enc(I bsl 1))>>, 3};
<<I:2>> -> {<<(b32_std_enc(I bsl 3))>>, 1};
<<>> -> {<<>>, 0}
end,
{<<B0/binary, B1/binary>>, Pad};
b32_encode(V) when is_list(V) ->
b32_encode(list_to_binary(V));
b32_encode(V) when is_binary(V) ->
{EB, _R} = b32_encode({body, V}),
{ER, P} = b32_encode({rest, V}),
Padding = list_to_binary(lists:duplicate(P, $=)),
<<EB/binary, ER/binary, Padding/binary>>.
b32_std_dec(I) when I >= $2 andalso I =< $7 ->
I - 24;
b32_std_dec(I) when I >= $a andalso I =< $z ->
I - $a;
b32_std_dec(I) when I >= $A andalso I =< $Z ->
I - $A.
b32_std_enc(I) when is_integer(I) andalso I >= 26 andalso I =< 31 ->
I + 24;
b32_std_enc(I) when is_integer(I) andalso I >= 0 andalso I =< 25 ->
I + $a.
hotp(Token, Time) ->
K = b32_decode(Token),
M = <<Time:8/big-unsigned-integer-unit:8>>,
D = crypto:mac(hmac, sha, K, M),
<<_:19/binary, _O:8>> = D,
O = _O band 15,
<<_TB:4/integer-unit:8>> = binary:part(D, O, 4),
TB = _TB band 16#7fffffff,
_T = TB rem trunc(math:pow(10, 6)),
T = integer_to_binary(_T),
P = << <<48:8>> || _ <- lists:seq(1, 6 - byte_size(T)) >>,
{ok, <<P/binary, T/binary>>}.
rand_bytes() ->
rand_bytes(8).
rand_bytes(N) when is_integer(N) ->
{ok, crypto:strong_rand_bytes(N)}.
rand_chars({0, Swap}) ->
{ok, Swap};
rand_chars({Size, <<Swap/binary>>}) when Size > 0 ->
Char = rand:uniform(26) + 96,
rand_chars({Size - 1, <<Swap/binary, Char>>});
rand_chars(Size) when Size > 0 ->
rand_chars({Size, <<"">>}).
rand_hash() ->
{ok, R} = rand_chars(64),
{ok, binary:encode_hex(crypto:hash(sha256, <<"utools:rand_hash(", R/binary, ")">>))}.
totp_generate() ->
rand_chars(16).
totp(<<Token/binary>>) ->
totp(Token, erlang:timestamp()).
totp(<<Token/binary>>, {M, S, _}) ->
T = (M * 1000000 + S) / 30,
Time = trunc(T),
hotp(Token, Time).