-module(pinkdf2). -compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch]). -export([get_salt/0, with_defaults/2, with_config/5]). -export_type([pbkdf2_keys/0, pbkdf2_algorithm/0, pbkdf2_error/0]). -if(?OTP_RELEASE >= 27). -define(MODULEDOC(Str), -moduledoc(Str)). -define(DOC(Str), -doc(Str)). -else. -define(MODULEDOC(Str), -compile([])). -define(DOC(Str), -compile([])). -endif. ?MODULEDOC(" Gleam bindings to fast_pbkdf2 NIF of PBKDF2 (Password-Based Key Derivation Function 2) for Erlang.\n"). -type pbkdf2_keys() :: {pbkdf2_keys, bitstring(), binary()}. -type pbkdf2_algorithm() :: sha224 | sha256 | sha384 | sha512. -type pbkdf2_error() :: iterations_value_not_positive | derived_key_length_value_not_positive. -file("src/pinkdf2.gleam", 67). ?DOC( " Generates a base64-encoded salt with a minimum size of 64 bytes.\n" " It is provided here for convenience, but it is based on the same underlying Erlang function as `crypto.strong_rand_bytes`.\n" ). -spec get_salt() -> binary(). get_salt() -> extern:get_salt(). -file("src/pinkdf2.gleam", 69). -spec map_algorithm(pbkdf2_algorithm()) -> gleam@crypto:hash_algorithm(). map_algorithm(Alg) -> case Alg of sha224 -> sha224; sha256 -> sha256; sha384 -> sha384; sha512 -> sha512 end. -file("src/pinkdf2.gleam", 78). -spec is_positive(integer()) -> boolean(). is_positive(Num) -> Num > 0. -file("src/pinkdf2.gleam", 24). ?DOC( " Derives a key from a password and salt with default settings based on the\n" " (OWASP recommendations)[https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html#pbkdf2].\n" ). -spec with_defaults(binary(), binary()) -> {ok, pbkdf2_keys()} | {error, pbkdf2_error()}. with_defaults(Password, Salt) -> Raw = extern:fp_with_defaults(Password, Salt), {ok, {pbkdf2_keys, Raw, gleam_stdlib:bit_array_base64_encode(Raw, false)}}. -file("src/pinkdf2.gleam", 45). ?DOC( " Derives a key using the provided configuration.\n" "\n" " `iterations` is the number of times to run the algorithm. Must be a positive integer.\n" " `d_len` is the target derived key length in bytes. Must be a positive integer.\n" "\n" " ## Examples\n" "\n" " ```gleam\n" " import pinkdf2.{Sha512}\n" "\n" " let salt = pinkdf2.get_salt()\n" " let assert Ok(key) = pinkdf2.with_config(Sha512, \"password\", salt, 210_000, 32)\n" " ```\n" ). -spec with_config(pbkdf2_algorithm(), binary(), binary(), integer(), integer()) -> {ok, pbkdf2_keys()} | {error, pbkdf2_error()}. with_config(Alg, Password, Salt, Iterations, D_len) -> case {is_positive(Iterations), is_positive(D_len)} of {false, _} -> {error, iterations_value_not_positive}; {_, false} -> {error, derived_key_length_value_not_positive}; {_, _} -> Raw = begin _pipe = map_algorithm(Alg), fast_pbkdf2:pbkdf2(_pipe, Password, Salt, Iterations, D_len) end, {ok, {pbkdf2_keys, Raw, gleam_stdlib:bit_array_base64_encode(Raw, false)}} end.