Version 4 of Comeonin, a password hashing library for Elixir, was recently released. The changes made in this version are summarized below.

  • support for Argon2
  • Argon2, Bcrypt and Pbkdf2 are all optional dependencies
    • these libraries can be used with Comeonin or on their own
    • on their own, they provide a lower-level and a mid-level api
  • more scheduler-friendly implementation of Bcrypt
  • additional higher-level functions
    • add_hash and check_pass
  • improved statistics reporting

The following sections provide more details about these changes.

Argon2 is now supported

Argon2, the winner of the 2015 Password Hashing Competition (PHC), is now supported, as an optional dependency.

With Bcrypt and Pbkdf2, attackers can use GPUs, or other dedicated hardware, to try hashing multiple passwords in parallel. Argon2, though, has been designed as a memory-hard function, and its high use of memory makes it highly resistant to GPU attacks.

To use Argon2, you need to be using a version of Erlang with dirty scheduler support, which is enabled by default in Erlang 20.

Algorithms are now optional dependencies

The three supported algorithms (Argon2, Bcrypt and Pbkdf2) are now optional dependencies.

This means that you need to choose which algorithm you want to use and then also add that algorithm as a dependency. See the Choosing an algorithm section in the Comeonin documentation for more information about each algorithm.

More scheduler-friendly implementation of Bcrypt

Version 1.0 of bcrypt_elixir uses dirty schedulers, making it a little more scheduler-friendly than earlier versions. To use this version, you need to be using a version of Erlang with dirty scheduler support, which is enabled by default in Erlang 20.

If you are using an earlier version of Erlang, you will need to use version 0.12 of bcrypt_elixir, which will continue to be maintained for the foreseeable future.

Additional higher-level functions

Two higher-level functions, add_hash and check_pass, have been added.

add_hash takes the password as input and returns a map with the password hash added and the password set to nil. This can be used with the changes map in an Ecto changeset, as in the example below.

defp put_pass_hash(%Ecto.Changeset{valid?: true, changes:
    %{password: password}} = changeset) do
  change(changeset, Comeonin.Argon2.add_hash(password))
end
defp put_pass_hash(changeset), do: changeset

check_pass takes a map, usually an Ecto user struct, as input and returns {:ok, user} (user being the map or user struct) or {:error, message}.

The following is an example of a function that can be called in the create function of your session controller.

def verify(attrs) do
  MyApp.Accounts.get_by(attrs)
  |> Comeonin.Argon2.check_pass(password)
end

Improved statistics reporting

A report function has been added. This calls the dependency’s Stats.report function, which provides useful information about the time the algorithm takes, and how it can be configured to take more or less time.

Increasing the time that a password hash function takes makes it more difficult for an attacker to find the correct password. However, it also inconveniences valid users. Depending on the nature of your application, you might want the password hashing function to take between 250 milliseconds and a second.

With Argon2, you also need to configure how much memory, and how much parallelism, is used. See the Argon2.Stats documentation for more information.