Packages
pow
1.0.24
1.0.39
1.0.38
1.0.37
1.0.36
1.0.35
1.0.34
1.0.33
1.0.32
1.0.31
1.0.30
1.0.29
1.0.28
1.0.27
1.0.26
1.0.25
1.0.24
1.0.23
1.0.22
1.0.21
1.0.20
1.0.19
1.0.18
1.0.17
1.0.16
1.0.15
1.0.14
1.0.13
1.0.12
1.0.11
1.0.10
1.0.9
1.0.8
1.0.7
1.0.6
1.0.5
1.0.4
1.0.3
1.0.2
1.0.1
1.0.0
1.0.0-rc.4
1.0.0-rc.3
1.0.0-rc.2
1.0.0-rc.1
1.0.0-rc.0
0.1.0-rc.1
0.1.0-alpha.8
0.1.0-alpha.7
retired
0.1.0-alpha.6
0.1.0-alpha.5
0.1.0-alpha.4
0.1.0-alpha.3
0.1.0-alpha.2
0.1.0-alpha.1
0.1.0-alpha
Robust user authentication solution
Security advisory:
This version has known vulnerabilities.
View advisories
Current section
Files
Jump to
Current section
Files
lib/pow/ecto/schema/password/pbkdf2.ex
defmodule Pow.Ecto.Schema.Password.Pbkdf2 do
@moduledoc """
The Pbkdf2 hash generation code is pulled from
[Plug.Crypto.KeyGenerator](https://github.com/elixir-plug/plug_crypto/blob/v1.2.1/lib/plug/crypto/key_generator.ex)
and is under Apache 2 license.
"""
use Bitwise
@doc """
Compares the two binaries in constant-time to avoid timing attacks.
"""
@spec compare(binary(), binary()) :: boolean()
def compare(left, right) when is_binary(left) and is_binary(right) do
byte_size(left) == byte_size(right) and compare(left, right, 0)
end
defp compare(<<x, left::binary>>, <<y, right::binary>>, acc) do
xorred = bxor(x, y)
compare(left, right, acc ||| xorred)
end
defp compare(<<>>, <<>>, acc) do
acc === 0
end
@max_length bsl(1, 32) - 1
@doc """
Returns a derived key suitable for use.
"""
@spec generate(binary(), binary(), integer(), integer(), atom()) :: binary()
def generate(secret, salt, iterations, length, digest) do
cond do
not is_integer(iterations) or iterations < 1 ->
raise ArgumentError, "iterations must be an integer >= 1"
length > @max_length ->
raise ArgumentError, "length must be less than or equal to #{@max_length}"
true ->
generate(mac_fun(digest, secret), salt, iterations, length, 1, [], 0)
end
rescue
e ->
stacktrace =
case __STACKTRACE__ do
[{mod, fun, [_ | _] = args, info} | rest] ->
[{mod, fun, length(args), info} | rest]
stacktrace ->
stacktrace
end
reraise e, stacktrace
end
defp generate(_fun, _salt, _iterations, max_length, _block_index, acc, length)
when length >= max_length do
acc
|> IO.iodata_to_binary()
|> binary_part(0, max_length)
end
defp generate(fun, salt, iterations, max_length, block_index, acc, length) do
initial = fun.(<<salt::binary, block_index::integer-size(32)>>)
block = iterate(fun, iterations - 1, initial, initial)
length = byte_size(block) + length
generate(
fun,
salt,
iterations,
max_length,
block_index + 1,
[acc | block],
length
)
end
defp iterate(_fun, 0, _prev, acc), do: acc
defp iterate(fun, iteration, prev, acc) do
next = fun.(prev)
iterate(fun, iteration - 1, next, :crypto.exor(next, acc))
end
# TODO: Remove when OTP 22.1 is required
if Code.ensure_loaded?(:crypto) and function_exported?(:crypto, :mac, 4) do
defp mac_fun(digest, secret), do: &:crypto.mac(:hmac, digest, secret, &1)
else
defp mac_fun(digest, secret), do: &:crypto.hmac(digest, secret, &1)
end
end