ArteMisc / libsalty

Elixir bindings for libsodium (NIF)
Apache License 2.0
21 stars 25 forks source link

Pwhash implementation #7

Open daveed-al opened 6 years ago

daveed-al commented 6 years ago

work in progress for pwhash implementation. the libsodium crypto_pwhash function https://github.com/jedisct1/libsodium/blob/master/src/libsodium/crypto_pwhash/crypto_pwhash.c#L129 has a switch / case for the algorithm, values can either be can either be pwhash_ALG_ARGON2ID13 or pwhash_ALG_ARGON2I13 when I call the Elixir function using Salty.Nif.pwhash_ALG_ARGON2ID13 it works when I use Salty.Nif.pwhash_ALG_ARGON2I13 it errors.

here is the code I use to test:

require Salty.Nif

### pwhash
outlen = 16
password = "thepassword"
{:ok, password_salt} = Salty.Nif.randombytes_buf(16)
opslimit = 2
memlimit = Salty.Nif.pwhash_argon2id_MEMLIMIT_INTERACTIVE
# alg can either be pwhash_ALG_ARGON2ID13 or pwhash_ALG_ARGON2I13
alg = Salty.Nif.pwhash_ALG_ARGON2ID13
# alg = Salty.Nif.pwhash_ALG_ARGON2I13
{:ok, pwhash} = Salty.Nif.pwhash(outlen, password, password_salt, opslimit, memlimit, alg)

also the code for str_verify and needs_rehash is in progress as well - I pull request to reach out for help and get feedback and understand what I am missing. thanks

ArteMisc commented 6 years ago

Hi there, and thanks for taking the time to work on this!

The approach to high level APIs in this library is to not bind them directly, but provide a method "primitive()" that returns the module that implements the default (high level) primitive. Take the secretbox API as an example.

In case of Salty.PwHash, that would lead to exposing the pwhash/pwhash_str/str_verify/str_needs_rehash methods in the modules that implement the primitives (Salty.PwHash.Scrypt, Salty.PwHash.Argon2i, Salty.PwHash.Argon2id), and returning the Argon2id module from the Salty.PwHash.primitive() method. This removes the requirement to work with algorithm identifiers altogether, and lets you call the primitives' implementations directly in salty_nif.c instead.

As a special case, the str_verify/str_needs_rehash methods could be implemented in the Salty.PwHash module as well, as they do not need any algorithm specific input parameters.

That aside, what is the error message that you're getting? Also, which version of libsodium are you using, and on which system are you testing?

On another note, in order to use pwhash within Elixir (or Erlang's VM for that matter), the methods for pwhash, str and str_verify will need to use the dirty scheduler in order for applications to be able to use it without messing up the scheduler.

I hope this answers some of your questions, let me know if there is anything else.

daveed-al commented 6 years ago

Hi @ArteMisc - I apologize for the delay and thanks a lot for your detailed explanation. I am still going through it. The error I am getting (when alg == Salty.Nif.pwhash_ALG_ARGON2I13 == 1) is ** (MatchError) no match of right hand side value: {:error, :salty_error_unknown}

If I try a few more times I have a segmentation fault:

iex(18)> {:ok, pwhash} = Salty.Nif.pwhash(outlen, password, password_salt, opslimit, memlimit, 1)
** (MatchError) no match of right hand side value: {:error, :salty_error_unknown}

iex(18)> {:ok, pwhash} = Salty.Nif.pwhash(outlen, password, password_salt, opslimit, memlimit, 1)
** (MatchError) got FunctionClauseError with message "no function clause matching in Inspect.Any.inspect/2" while retrieving Exception.message/1 for %Inspect.Error{message: "got FunctionClauseError with message \"no function clause matching in Inspect.Any.inspect/2\" while inspecting %{__exception__: true, __struct__: MatchError, term: {:error, :salty_error_unknown}}"}

iex(18)> {:ok, pwhash} = Salty.Nif.pwhash(outlen, password, password_salt, opslimit, memlimit, 1)
[1]    29426 segmentation fault  iex -S mix

I am using libsodium 1.0.16 on macOS High Sierra 10.13.2 I was using Elixir 1.7.0 but have the same issue when now using Elixir 1.6.0.

Hopefully that helps troubleshooting. Thanks

daveed-al commented 6 years ago

Implementing primitives now @ArteMisc to respect high level API approach.

daveed-al commented 6 years ago

updated the pull request - the seg fault no longer happens. Now using the following code to test:

outlen = 16
password = "thepassword"
{:ok, password_salt} = Salty.Nif.randombytes_buf(16)
opslimit = Salty.PwHash.Argon2i.opslimit_interactive
memlimit = Salty.PwHash.Argon2i.memlimit_interactive
alg = Salty.PwHash.Argon2i.alg
{:ok, pwhash} = Salty.PwHash.Argon2i.pwhash(outlen, password, password_salt, opslimit, memlimit, alg)
daveed-al commented 6 years ago

any updates @ArteMisc ? thanks