aboutsummaryrefslogtreecommitdiff
path: root/server/_build/default/lib/bcrypt/src/bcrypt_port.erl
diff options
context:
space:
mode:
authorCalvin Morrison <calvin@pobox.com>2025-09-03 21:15:36 -0400
committerCalvin Morrison <calvin@pobox.com>2025-09-03 21:15:36 -0400
commit49fa5aa2a127bdf8924d02bf77e5086b39c7a447 (patch)
tree61d86a7705dacc9fddccc29fa79d075d83ab8059 /server/_build/default/lib/bcrypt/src/bcrypt_port.erl
i vibe coded itHEADmaster
Diffstat (limited to 'server/_build/default/lib/bcrypt/src/bcrypt_port.erl')
-rw-r--r--server/_build/default/lib/bcrypt/src/bcrypt_port.erl157
1 files changed, 157 insertions, 0 deletions
diff --git a/server/_build/default/lib/bcrypt/src/bcrypt_port.erl b/server/_build/default/lib/bcrypt/src/bcrypt_port.erl
new file mode 100644
index 0000000..6365de6
--- /dev/null
+++ b/server/_build/default/lib/bcrypt/src/bcrypt_port.erl
@@ -0,0 +1,157 @@
+%% @copyright 2011 Hunter Morris
+%% @doc Implementation of `gen_server' behaviour.
+%% @end
+%% Distributed under the MIT license; see LICENSE for details.
+-module(bcrypt_port).
+-author('Hunter Morris <hunter.morris@smarkets.com>').
+
+-behaviour(gen_server).
+
+%% API
+-export([start_link/0, stop/0]).
+-export([gen_salt/1, gen_salt/2]).
+-export([hashpw/3]).
+
+%% gen_server callbacks
+-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2,
+ code_change/3]).
+
+-record(state, {
+ port :: port(),
+ default_log_rounds :: non_neg_integer(),
+ cmd_from :: {pid(), term()} | undefined
+ }).
+
+-type state() :: #state{port :: port(),
+ default_log_rounds :: non_neg_integer(),
+ cmd_from :: {pid(), term()} | undefined}.
+
+-define(CMD_SALT, 0).
+-define(CMD_HASH, 1).
+-define(BCRYPT_ERROR(F, D), error_logger:error_msg(F, D)).
+-define(BCRYPT_WARNING(F, D), error_logger:warning_msg(F, D)).
+
+-spec start_link() -> Result when
+ Result :: {ok,Pid} | ignore | {error,Error},
+ Pid :: pid(),
+ Error :: {already_started,Pid} | term(),
+ Pid :: pid().
+start_link() ->
+ Dir = case code:priv_dir(bcrypt) of
+ {error, bad_name} ->
+ case code:which(bcrypt) of
+ Filename when is_list(Filename) ->
+ filename:join(
+ [filename:dirname(Filename), "../priv"]);
+ _ ->
+ "../priv"
+ end;
+ Priv -> Priv
+ end,
+ Port = filename:join(Dir, "bcrypt"),
+ gen_server:start_link(?MODULE, [Port], []).
+
+-spec stop() -> Result when
+ Result :: {stop, normal, ok, state()}.
+stop() -> gen_server:call(?MODULE, stop).
+
+-spec gen_salt(Pid) -> Result when
+ Pid :: pid(),
+ Result :: {ok, Salt},
+ Salt :: [byte()].
+gen_salt(Pid) ->
+ R = crypto:strong_rand_bytes(16),
+ gen_server:call(Pid, {encode_salt, R}, infinity).
+
+-spec gen_salt(Pid, LogRounds) -> Result when
+ Pid :: pid(),
+ LogRounds :: bcrypt:rounds(),
+ Result :: {ok, Salt},
+ Salt :: [byte()].
+gen_salt(Pid, LogRounds) ->
+ R = crypto:strong_rand_bytes(16),
+ gen_server:call(Pid, {encode_salt, R, LogRounds}, infinity).
+
+-spec hashpw(Pid, Password, Salt) -> Result when
+ Pid :: pid(),
+ Password :: [byte()],
+ Salt :: [byte()],
+ Result :: [byte()].
+hashpw(Pid, Password, Salt) ->
+ gen_server:call(Pid, {hashpw, Password, Salt}, infinity).
+
+%%====================================================================
+%% gen_server callbacks
+%%====================================================================
+%% @private
+
+init([Filename]) ->
+ case file:read_file_info(Filename) of
+ {ok, _Info} ->
+ Port = open_port(
+ {spawn, Filename}, [{packet, 2}, binary, exit_status]),
+ ok = bcrypt_pool:available(self()),
+ {ok, Rounds} = application:get_env(bcrypt, default_log_rounds),
+ {ok, #state{port = Port, default_log_rounds = Rounds}};
+ {error, Reason} ->
+ ?BCRYPT_ERROR("Can't open file ~p: ~p", [Filename, Reason]),
+ {stop, error_opening_bcrypt_file}
+ end.
+
+%% @private
+
+terminate(_Reason, #state{port=Port}) ->
+ catch port_close(Port),
+ ok.
+
+%% @private
+
+handle_call({encode_salt, R}, From, #state{default_log_rounds = LogRounds} = State) ->
+ handle_call({encode_salt, R, LogRounds}, From, State);
+handle_call({encode_salt, R, LogRounds}, From, #state{ cmd_from = undefined } = State) ->
+ Port = State#state.port,
+ Data = term_to_binary({?CMD_SALT, {iolist_to_binary(R), LogRounds}}),
+ erlang:port_command(Port, Data),
+ {noreply, State#state{ cmd_from = From }};
+handle_call({encode_salt, _R, _Rounds}, From, #state{ cmd_from = CmdFrom } = State) ->
+ ?BCRYPT_ERROR("bcrypt: Salt request from ~p whilst busy for ~p", [ From, CmdFrom ]),
+ {reply, {error, {busy, From}}, State};
+handle_call({hashpw, Password, Salt}, From, #state{ cmd_from = undefined } = State) ->
+ Port = State#state.port,
+ Data = term_to_binary({?CMD_HASH, {iolist_to_binary(Password), iolist_to_binary(Salt)}}),
+ erlang:port_command(Port, Data),
+ {noreply, State#state{ cmd_from = From }};
+handle_call({hashpw, _Password, _Salt}, From, #state{ cmd_from = CmdFrom } = State) ->
+ ?BCRYPT_ERROR("bcrypt: Hash request from ~p whilst busy for ~p", [ From, CmdFrom ]),
+ {reply, {error, {busy, From}}, State};
+handle_call(stop, _From, State) ->
+ {stop, normal, ok, State};
+handle_call(Msg, _, State) ->
+ {stop, {unknown_call, Msg}, State}.
+
+handle_cast(Msg, State) ->
+ {stop, {unknown_cast, Msg}, State}.
+
+code_change(_OldVsn, State, _Extra) ->
+ {ok, State}.
+
+handle_info({Port, {data, Data}}, #state{ port = Port, cmd_from = From } = State) ->
+ Reply =
+ case binary_to_term(Data) of
+ {_, Error} when is_atom(Error) ->
+ {error, Error};
+ {?CMD_SALT, Result} when is_binary(Result) ->
+ {ok, binary_to_list(Result)};
+ {?CMD_HASH, Result} when is_binary(Result) ->
+ {ok, binary_to_list(Result)}
+ end,
+ gen_server:reply(From, Reply),
+ ok = bcrypt_pool:available(self()),
+ {noreply, State#state{ cmd_from = undefined }};
+handle_info({Port, {exit_status, Status}}, #state{port=Port}=State) ->
+ %% Rely on whomever is supervising this process to restart.
+ ?BCRYPT_WARNING("Port died: ~p", [Status]),
+ {stop, port_died, State};
+handle_info(Msg, _) ->
+ exit({unknown_info, Msg}).
+