Rename main NIF module to bcrypt_nif and use rebar-generated .app file
diff --git a/.gitignore b/.gitignore
index 69211a7..f2f2a9a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,5 +1,6 @@
*.beam
ebin/*.beam
+ebin/bcrypt.app
_build/
lib/bcrypt/bcrypt
diff --git a/c_src/bcrypt_nif.c b/c_src/bcrypt_nif.c
index 27a7087..5dcf564 100644
--- a/c_src/bcrypt_nif.c
+++ b/c_src/bcrypt_nif.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2009 Hunter Morris <huntermorris@gmail.com>
+ * Copyright (c) 2011 Hunter Morris <hunter.morris@smarkets.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -48,7 +48,7 @@
encode_salt((char *)bin.data, (u_int8_t*)csalt.data, csalt.size, log_rounds);
enif_release_binary(&csalt);
-
+
return enif_make_string(env, (char *)bin.data, ERL_NIF_LATIN1);
}
@@ -57,10 +57,10 @@
char pw[1024];
char salt[1024];
char *ret = NULL;
-
+
(void)memset(&pw, '\0', sizeof(pw));
(void)memset(&salt, '\0', sizeof(salt));
-
+
if (enif_get_string(env, argv[0], pw, sizeof(pw), ERL_NIF_LATIN1) < 1)
return enif_make_badarg(env);
@@ -70,7 +70,7 @@
if (NULL == (ret = bcrypt(pw, salt)) || 0 == strcmp(ret, ":")) {
return enif_make_badarg(env);
}
-
+
return enif_make_string(env, ret, ERL_NIF_LATIN1);
}
@@ -80,4 +80,4 @@
{"hashpw", 2, hashpw}
};
-ERL_NIF_INIT(bcrypt, bcrypt_nif_funcs, NULL, NULL, NULL, NULL)
+ERL_NIF_INIT(bcrypt_nif, bcrypt_nif_funcs, NULL, NULL, NULL, NULL)
diff --git a/ebin/.gitignore b/ebin/.gitignore
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/ebin/.gitignore
diff --git a/ebin/bcrypt.app b/ebin/bcrypt.app
deleted file mode 100644
index d58b8c7..0000000
--- a/ebin/bcrypt.app
+++ /dev/null
@@ -1,12 +0,0 @@
-%%% This is the application resource file (.app file) for the bcrypt
-%%% application.
-{application, bcrypt,
- [{description, "An Erlang wrapper for the OpenBSD password scheme, bcrypt."},
- {vsn, "0.2.0"},
- {modules, [bcrypt]},
- {registered, [bcrypt]},
- {env, []},
- {applications, [kernel, stdlib, crypto]},
- {mod, {bcrypt, []}}
- ]
-}.
diff --git a/rebar.config b/rebar.config
new file mode 100644
index 0000000..87ccef3
--- /dev/null
+++ b/rebar.config
@@ -0,0 +1,3 @@
+%% -*- mode: erlang;erlang-indent-level: 2;indent-tabs-mode: nil -*-
+{so_name, "bcrypt_nif.so"}.
+{erl_opts, [debug_info]}.
diff --git a/src/bcrypt.app.src b/src/bcrypt.app.src
new file mode 100644
index 0000000..b43f3ea
--- /dev/null
+++ b/src/bcrypt.app.src
@@ -0,0 +1,12 @@
+%% -*- mode: erlang;erlang-indent-level: 2;indent-tabs-mode: nil -*-
+{application, bcrypt,
+ [{description, "An Erlang wrapper (NIF or port program) for the OpenBSD password scheme, bcrypt."},
+ {vsn, "0.3.0"},
+ {registered, []},
+ {applications, [kernel, stdlib, crypto]},
+ {env, [
+ % Default number of 'rounds', defining the hashing complexity
+ {default_log_rounds, 12}
+ ]}
+ ]
+}.
diff --git a/src/bcrypt.erl b/src/bcrypt.erl
index 2a65f35..b69f28d 100644
--- a/src/bcrypt.erl
+++ b/src/bcrypt.erl
@@ -1,5 +1,5 @@
-%% @author Hunter Morris <huntermorris@gmail.com>
-%% @copyright 2009 Hunter Morris
+%% @author Hunter Morris <hunter.morris@smarkets.com>
+%% @copyright 2011 Hunter Morris
%%
%% @doc Wrapper around the OpenBSD Blowfish password hashing algorithm, as
%% described in "A Future-Adaptable Password Scheme" by Niels Provos and
@@ -37,7 +37,13 @@
%% @end
%%--------------------------------------------------------------------
init() ->
- erlang:load_nif(filename:join(code:priv_dir(?MODULE), atom_to_list(?MODULE) ++ "_drv"), 0).
+ Dir = case code:priv_dir(bcrypt) of
+ {error, bad_name} -> "../priv";
+ Priv -> Priv
+ end,
+ erlang:load_nif(filename:join(Dir, "bcrypt"), 0).
+
+nif_stub_error(Line) -> erlang:nif_error({nif_not_loaded,module,?MODULE,line,Line}).
%%--------------------------------------------------------------------
%% @doc Generate a salt with the default number of rounds, 12.
@@ -62,7 +68,7 @@
encode_salt(R, LogRounds).
encode_salt(_R, _LogRounds) ->
- nif_error(?LINE).
+ nif_stub_error(?LINE).
%%--------------------------------------------------------------------
%% @doc Hash the specified password and the salt using the OpenBSD
@@ -74,7 +80,4 @@
hashpw(Password, Salt).
hashpw(_Password, _Salt) ->
- nif_error(?LINE).
-
-nif_error(Line) ->
- exit({nif_not_loaded, module, ?MODULE, line, Line}).
+ nif_stub_error(?LINE).
diff --git a/src/bcrypt_nif.erl b/src/bcrypt_nif.erl
new file mode 100644
index 0000000..5c34901
--- /dev/null
+++ b/src/bcrypt_nif.erl
@@ -0,0 +1,91 @@
+%% @author Hunter Morris <hunter.morris@smarkets.com>
+%% @copyright 2011 Hunter Morris
+%%
+%% @doc Wrapper around the OpenBSD Blowfish password hashing algorithm, as
+%% described in "A Future-Adaptable Password Scheme" by Niels Provos and
+%% David Mazieres: http://www.openbsd.org/papers/bcrypt-paper.ps
+%% @end
+%%
+%% Permission to use, copy, modify, and distribute this software for any
+%% purpose with or without fee is hereby granted, provided that the above
+%% copyright notice and this permission notice appear in all copies.
+
+%% THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+%% WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+%% MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+%% ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+%% WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+%% ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+%% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+-module(bcrypt_nif).
+-author('Hunter Morris <hunter.morris@smarkets.com>').
+
+%% API
+-export([init/0]).
+-export([gen_salt/0, gen_salt/1]).
+-export([hash/2, hashpw/2]).
+
+-define(DEFAULT_LOG_ROUNDS, 12).
+-define(MAX_LOG_ROUNDS(L), L < 32).
+-define(MIN_LOG_ROUNDS(L), L > 3).
+
+-on_load(init/0).
+
+%%--------------------------------------------------------------------
+%% @doc Load the bcrypt NIFs
+%% @spec init() -> ok
+%% @end
+%%--------------------------------------------------------------------
+init() ->
+ 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,
+ erlang:load_nif(filename:join(Dir, "bcrypt_nif"), 0).
+
+nif_stub_error(Line) ->
+ erlang:nif_error({nif_not_loaded, module, ?MODULE, line, Line}).
+
+%%--------------------------------------------------------------------
+%% @doc Generate a salt with the default number of rounds, 12.
+%% @see gen_salt/1
+%% @spec gen_salt() -> string()
+%% @end
+%%--------------------------------------------------------------------
+gen_salt() ->
+ gen_salt(?DEFAULT_LOG_ROUNDS).
+
+%%--------------------------------------------------------------------
+%% @doc Generate a random text salt for use with hashpw/3. LogRounds
+%% defines the complexity of the hashing, increasing the cost as
+%% 2^log_rounds.
+%% @spec gen_salt(integer()) -> string()
+%% @end
+%%--------------------------------------------------------------------
+gen_salt(LogRounds) when is_integer(LogRounds),
+ ?MAX_LOG_ROUNDS(LogRounds),
+ ?MIN_LOG_ROUNDS(LogRounds) ->
+ R = crypto:rand_bytes(16),
+ encode_salt(R, LogRounds).
+
+encode_salt(_R, _LogRounds) ->
+ nif_stub_error(?LINE).
+
+%%--------------------------------------------------------------------
+%% @doc Hash the specified password and the salt using the OpenBSD
+%% Blowfish password hashing algorithm. Returns the hashed password.
+%% @spec hash(Password::binary(), Salt::binary()) -> string()
+%% @end
+%%--------------------------------------------------------------------
+hash(Password, Salt) when is_binary(Password), is_binary(Salt) ->
+ hashpw(Password, Salt).
+
+hashpw(_Password, _Salt) ->
+ nif_stub_error(?LINE).