Packages

Argon2 password hashing library for Gleam, using the reference C implementation.

Current section

Files

Jump to
argus src argus.gleam
Raw

src/argus.gleam

pub type Argon2Algorithm {
Argon2d
Argon2i
Argon2id
}
pub opaque type Hasher {
Hasher(
algorithm: Argon2Algorithm,
time_cost: Int,
memory_cost: Int,
parallelism: Int,
hash_length: Int,
)
}
pub type Hashes {
Hashes(raw_hash: BitArray, encoded_hash: String)
}
/// All possible Argon2 hashing errors.
/// Most are unlikely to occur, but it's good to be aware of them.
pub type HashError {
OutputPointerIsNull
OutputTooShort
OutputTooLong
PasswordTooShort
PasswordTooLong
SaltTooShort
SaltTooLong
AssociatedDataTooShort
AssociatedDataTooLong
SecretTooShort
SecretTooLong
TimeCostTooSmall
TimeCostTooLarge
MemoryCostTooSmall
MemoryCostTooLarge
TooFewLanes
TooManyLanes
PasswordPointerMismatch
SaltPointerMismatch
SecretPointerMismatch
AssociatedDataPointerMismatch
MemoryAllocationError
FreeMemoryCallbackNull
AllocateMemoryCallbackNull
IncorrectParameter
IncorrectType
InvalidAlgorithm
OutputPointerMismatch
TooFewThreads
TooManyThreads
NotEnoughMemory
EncodingFailed
DecodingFailed
ThreadFailure
DecodingLengthFailure
VerificationFailure
UnknownErrorCode
}
/// Create a new hasher with default settings based on the
/// [OWASP recommendations](https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html#argon2id).
///
/// Note: if you change the algorithm to Argon2i, you will need to change the
/// `memory_cost` to 12_228 (12 mebibytes) or less for performance reasons.
///
/// The `hasher_argon2i` function is provided with the recommended settings for
/// Argon2i.
pub fn hasher() -> Hasher {
Hasher(
Argon2id,
2,
// 19 mebibytes
19_456,
1,
32,
)
}
/// Create a new hasher with default settings based on the
/// [OWASP recommendations](https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html#argon2id) for
/// Argon2i.
pub fn hasher_argon2i() -> Hasher {
Hasher(
Argon2i,
3,
// 12 mebibytes
12_228,
1,
32,
)
}
/// Set the algorithm to use for the hasher.
pub fn algorithm(hasher: Hasher, algorithm: Argon2Algorithm) -> Hasher {
Hasher(..hasher, algorithm: algorithm)
}
/// Set the time cost to use for the hasher.
pub fn time_cost(hasher: Hasher, time_cost: Int) -> Hasher {
Hasher(..hasher, time_cost: time_cost)
}
/// Set the memory cost to use for the hasher.
pub fn memory_cost(hasher: Hasher, memory_cost: Int) -> Hasher {
Hasher(..hasher, memory_cost: memory_cost)
}
/// Set the parallelism to use for the hasher.
pub fn parallelism(hasher: Hasher, parallelism: Int) -> Hasher {
Hasher(..hasher, parallelism: parallelism)
}
/// Set the hash length to use for the hasher.
pub fn hash_length(hasher: Hasher, hash_length: Int) -> Hasher {
Hasher(..hasher, hash_length: hash_length)
}
/// Hash a password using the provided hasher.
///
/// ## Examples
///
/// ```gleam
/// import argus
///
/// let assert Ok(hashes) =
/// argus.hasher()
/// |> argus.algorithm(argus.Argon2id)
/// |> argus.time_cost(3)
/// |> argus.memory_cost(12228)
/// |> argus.parallelism(1)
/// |> argus.hash_length(32)
/// |> argus.hash("password", gen_salt())
///
/// let assert Ok(True) = argus.verify(hashes.encoded_hash, "password")
/// ```
pub fn hash(
hasher: Hasher,
password: String,
salt: String,
) -> Result(Hashes, HashError) {
let result =
jargon_hash(
password,
salt,
hasher.algorithm,
hasher.time_cost,
hasher.memory_cost,
hasher.parallelism,
hasher.hash_length,
)
case result {
Ok(#(raw_hash, encoded_hash)) -> Ok(Hashes(raw_hash, encoded_hash))
Error(error) -> Error(error)
}
}
/// Verify a password using the provided encoded hash.
pub fn verify(encoded_hash: String, password: String) -> Result(Bool, HashError) {
jargon_verify(encoded_hash, password)
}
/// Generate a random 16-byte salt.
@external(erlang, "argus_nif", "gen_salt")
pub fn gen_salt() -> String
@external(erlang, "argus_nif", "hash")
fn jargon_hash(
password: String,
salt: String,
algorithm: Argon2Algorithm,
time_cost: Int,
memory_cost: Int,
parallelism: Int,
hash_length: Int,
) -> Result(#(BitArray, String), HashError)
@external(erlang, "jargon", "verify")
fn jargon_verify(
encoded_hash: String,
password: String,
) -> Result(Bool, HashError)