%% Copyright (c) 2011 Hunter Morris
%% Distributed under the MIT license; see LICENSE for details.
%% @doc The OpenBSD Blowfish password hashing algorithm wrapper module.
-module(bcrypt).
-author('Hunter Morris <hunter.morris@smarkets.com>').
%% API
-export([start/0, stop/0]).
-export([mechanism/0]).
-export([gen_salt/0, gen_salt/1, hashpw/2, is_worker_available/0]).
-type mechanism() :: nif | port.
-type rounds() :: 4..31.
-type pwerr() :: invalid_salt | invalid_salt_length | invalid_rounds.
-export_type([ mechanism/0, rounds/0, pwerr/0 ]).
%% @doc Starts `Application' `bcrypt'.
%% <b>See also:</b>
%% [http://erlang.org/doc/man/application.html#start-1 application:start/1].
start() -> application:start(bcrypt).
%% @doc Stops `Application' `bcrypt'.
%% <b>See also:</b>
%% [http://erlang.org/doc/man/application.html#stop-1 application:stop/1].
stop() -> application:stop(bcrypt).
%% @doc Get environment setting of hash generation.
-spec mechanism() -> mechanism().
mechanism() ->
{ok, M} = application:get_env(bcrypt, mechanism),
M.
%% @doc Returns a random string data.
-spec gen_salt() -> Result when
Result :: {ok, Salt},
Salt :: [byte()].
gen_salt() ->
do_gen_salt(mechanism()).
%% @doc Generate a random string data.
-spec gen_salt( Rounds ) -> Result when
Rounds :: rounds(),
Result :: {ok, Salt},
Salt :: [byte()].
gen_salt(Rounds) when is_integer(Rounds), Rounds < 32, Rounds > 3 ->
do_gen_salt(mechanism(), Rounds).
%% @doc Make hash string based on `Password' and `Salt'.
-spec hashpw( Password, Salt ) -> Result when
Password :: [byte()] | binary(),
Salt :: [byte()] | binary(),
Result :: {ok, Hash} | {error, ErrorDescription},
Hash :: [byte()],
ErrorDescription :: pwerr().
hashpw(Password, Salt) ->
do_hashpw(mechanism(), Password, Salt).
%% @doc Is at least one bcrypt worker currently available for work?
-spec is_worker_available() -> Result when
Result :: boolean().
is_worker_available() ->
do_is_worker_available(mechanism()).
%% @private
-spec do_gen_salt(nif | port) -> Result when
Result :: {ok, Salt},
Salt :: [byte()].
do_gen_salt(nif) -> bcrypt_nif_worker:gen_salt();
do_gen_salt(port) -> bcrypt_pool:gen_salt().
%% @private
-spec do_gen_salt(nif | port, Rounds) -> Result when
Rounds :: rounds(),
Result :: {ok, Salt},
Salt :: [byte()].
do_gen_salt(nif, Rounds) -> bcrypt_nif_worker:gen_salt(Rounds);
do_gen_salt(port, Rounds) -> bcrypt_pool:gen_salt(Rounds).
%% @private
-spec do_hashpw(nif | port, Password, Salt) -> Result when
Password :: [byte()] | binary(),
Salt :: [byte()],
Result :: {ok, Hash} | {error, ErrorDescription},
Hash :: [byte()],
ErrorDescription :: pwerr().
do_hashpw(nif, Password, Salt) -> bcrypt_nif_worker:hashpw(Password, Salt);
do_hashpw(port, Password, Salt) -> bcrypt_pool:hashpw(Password, Salt).
-spec do_is_worker_available(nif | port) -> Result when
Result :: boolean().
do_is_worker_available(nif) -> bcrypt_nif_worker:is_worker_available();
do_is_worker_available(port) -> bcrypt_pool:is_worker_available().