Packages

Modern Argon2 password hashing for Erlang

Current section

Files

Jump to
jargon src jargon.erl
Raw

src/jargon.erl

-module(jargon).
-author("Isaac Harris-Holt").
-export([hash/7, verify/2]).
-on_load init/0.
-define(APPNAME, jargon).
-define(LIBNAME, jargon).
init() ->
SoName =
case code:priv_dir(?APPNAME) of
{error, bad_name} ->
case filelib:is_dir(
filename:join(["..", priv]))
of
true ->
filename:join(["..", priv, ?LIBNAME]);
_ ->
filename:join([priv, ?LIBNAME])
end;
Dir ->
filename:join(Dir, ?LIBNAME)
end,
erlang:load_nif(SoName, 0).
%% @doc
%% Hash a password with Argon2.
%%
%% @param Password The password to hash.
%% @param Salt The salt to use for hashing. Must be at least 32 bytes (8 base64 characters).
%% @param Algorithm The Argon2 algorithm to use. Must be one of 'argon2d', 'argon2i', or 'argon2id'.
%% @param TimeCost The number of iterations to use.
%% @param MemoryCost The amount of memory to use.
%% @param Parallelism The number of threads to use.
%% @param HashLen The length of the hash to return.
%%
-spec hash(Password :: binary(), Salt :: binary(), Algorithm :: argon2d | argon2i | argon2id, TimeCost :: pos_integer(), MemoryCost :: pos_integer(), Parallelism :: pos_integer(), HashLen :: pos_integer()) -> {ok, RawHash :: binary(), EncodedHash :: binary()} | {error, Error :: atom()}.
hash(Password, Salt, Algorithm, TimeCost, MemoryCost, Parallelism, HashLen) ->
AlgorithmInt =
case Algorithm of
argon2d ->
0;
argon2i ->
1;
argon2id ->
2;
_ ->
3
end,
case AlgorithmInt of
3 ->
{error, invalid_algorithm};
_ ->
case hash_nif(Password, Salt, AlgorithmInt, TimeCost, MemoryCost, Parallelism, HashLen)
of
{ok, RawHash, EncodedHash} ->
{ok, RawHash, EncodedHash};
{error, Error} ->
{error, hash_error_code_to_string(Error)}
end
end.
hash_error_code_to_string(Error) when is_integer(Error), Error =:= -1 ->
output_pointer_is_null;
hash_error_code_to_string(Error) when is_integer(Error), Error =:= -2 ->
output_too_short;
hash_error_code_to_string(Error) when is_integer(Error), Error =:= -3 ->
output_too_long;
hash_error_code_to_string(Error) when is_integer(Error), Error =:= -4 ->
password_too_short;
hash_error_code_to_string(Error) when is_integer(Error), Error =:= -5 ->
password_too_long;
hash_error_code_to_string(Error) when is_integer(Error), Error =:= -6 ->
salt_too_short;
hash_error_code_to_string(Error) when is_integer(Error), Error =:= -7 ->
salt_too_long;
hash_error_code_to_string(Error) when is_integer(Error), Error =:= -8 ->
associated_data_too_short;
hash_error_code_to_string(Error) when is_integer(Error), Error =:= -9 ->
associated_data_too_long;
hash_error_code_to_string(Error) when is_integer(Error), Error =:= -10 ->
secret_too_short;
hash_error_code_to_string(Error) when is_integer(Error), Error =:= -11 ->
secret_too_long;
hash_error_code_to_string(Error) when is_integer(Error), Error =:= -12 ->
time_cost_too_small;
hash_error_code_to_string(Error) when is_integer(Error), Error =:= -13 ->
time_cost_too_large;
hash_error_code_to_string(Error) when is_integer(Error), Error =:= -14 ->
memory_cost_too_small;
hash_error_code_to_string(Error) when is_integer(Error), Error =:= -15 ->
memory_cost_too_large;
hash_error_code_to_string(Error) when is_integer(Error), Error =:= -16 ->
too_few_lanes;
hash_error_code_to_string(Error) when is_integer(Error), Error =:= -17 ->
too_many_lanes;
hash_error_code_to_string(Error) when is_integer(Error), Error =:= -18 ->
password_pointer_mismatch;
hash_error_code_to_string(Error) when is_integer(Error), Error =:= -19 ->
salt_pointer_mismatch;
hash_error_code_to_string(Error) when is_integer(Error), Error =:= -20 ->
secret_pointer_mismatch;
hash_error_code_to_string(Error) when is_integer(Error), Error =:= -21 ->
associated_data_pointer_mismatch;
hash_error_code_to_string(Error) when is_integer(Error), Error =:= -22 ->
memory_allocation_error;
hash_error_code_to_string(Error) when is_integer(Error), Error =:= -23 ->
free_memory_callback_null;
hash_error_code_to_string(Error) when is_integer(Error), Error =:= -24 ->
allocate_memory_callback_null;
hash_error_code_to_string(Error) when is_integer(Error), Error =:= -25 ->
incorrect_parameter;
hash_error_code_to_string(Error) when is_integer(Error), Error =:= -26 ->
incorrect_type;
hash_error_code_to_string(Error) when is_integer(Error), Error =:= -27 ->
output_pointer_mismatch;
hash_error_code_to_string(Error) when is_integer(Error), Error =:= -28 ->
too_few_threads;
hash_error_code_to_string(Error) when is_integer(Error), Error =:= -29 ->
too_many_threads;
hash_error_code_to_string(Error) when is_integer(Error), Error =:= -30 ->
not_enough_memory;
hash_error_code_to_string(Error) when is_integer(Error), Error =:= -31 ->
encoding_failed;
hash_error_code_to_string(Error) when is_integer(Error), Error =:= -32 ->
decoding_failed;
hash_error_code_to_string(Error) when is_integer(Error), Error =:= -33 ->
thread_failure;
hash_error_code_to_string(Error) when is_integer(Error), Error =:= -34 ->
decoding_length_failure;
hash_error_code_to_string(Error) when is_integer(Error), Error =:= -35 ->
verification_mismatch;
hash_error_code_to_string(_) ->
unknown_error_code.
hash_nif(_Password, _Salt, _Algorithm, _TimeCost, _MemoryCost, _Parallelism, _HashLen) ->
erlang:nif_error(nif_library_not_loaded).
%% @doc
%% Verify an encoded hash against a password.
%%
-spec verify(EncodedHash :: binary(), Password :: binary()) -> {ok, true} | {ok, false} | {error, Error :: atom()}.
verify(EncodedHash, Password) ->
AlgorithmInt =
case EncodedHash of
%% Starts with $argon2i$
<<"$argon2d$", _/binary>> ->
0;
%% Starts with $argon2d$
<<"$argon2i$", _/binary>> ->
1;
%% Starts with $argon2id$
<<"$argon2id$", _/binary>> ->
2;
_ ->
3
end,
case AlgorithmInt of
3 ->
{error, invalid_algorithm};
_ ->
case verify_nif(EncodedHash, Password, AlgorithmInt) of
{ok, true} ->
{ok, true};
{ok, false} ->
{ok, false};
{error, Error} ->
{error, hash_error_code_to_string(Error)}
end
end.
verify_nif(_EncodedHash, _Password, _Algorithm) ->
erlang:nif_error(nif_library_not_loaded).