module Crypto::Argon2
argon2id password hashes (RFC 9106), through the RustCrypto argon2
crate: what a server stores for a password, as a PHC string such as
$argon2id$v=19$m=19456,t=2,p=1$<salt>$<hash> (the salt and the
32-byte hash in unpadded Base64), which names its parameters, so that
stored hashes stay readable when the parameters change.
hash = Crypto::Argon2.hash("correct horse")
hash.start_with?("$argon2id$v=19$m=19456,t=2,p=1$") # => true
Crypto::Argon2.verify?("correct horse", hash) # => true
Crypto::Argon2.verify?("wrong horse", hash) # => false
Crypto::Argon2.needs_rehash?(hash) # => false
A hash with Params::DEFAULT takes tens of milliseconds and 19 MiB
(memory_kib), on a helper thread: only the calling task waits. Each
hash at once holds its memory, so a server that hashes many passwords
at once (logins) should bound how many it runs together.
Only argon2id, version 19, without a secret or associated data:
verify? refuses argon2i and argon2d hashes. bcrypt and scrypt are
not here; Digest::PBKDF2 is, where PBKDF2 is required.
Classes and modules
Crypto::Argon2::ParamsThe cost of a hash: memory_kib KiB of memory, iterations passes over it, parallelism lanes. A data value: immutable and Shareable.
Class methods
def self.hash(password: String | Bytes, params: Crypto::Argon2::Params = …, salt: Bytes? = …) -> String
The argon2id PHC string of password with params, and salt
(16 to 48 bytes, ArgumentError otherwise), a fresh random 16 bytes
unless given. Two hashes of one password differ by their salts.
salt = Bytes.from((0...16).to_a)
cheap = Crypto::Argon2::Params.new(memory_kib: 64, iterations: 1, parallelism: 1)
Crypto::Argon2.hash("pw", params: cheap, salt: salt) == Crypto::Argon2.hash("pw", params: cheap, salt: salt) # => true
def self.verify?(password: String | Bytes, hash: String) -> Bool
Whether password is the one hash (an argon2id PHC string, with
the parameters it names) was made of: false for a wrong password,
and the comparison takes the same time wherever they differ.
Crypto::Error for a string that is not an argon2id v=19 hash
(another scheme or variant, another version, a parameter other than
m, t and p, one out of Params's bounds, a string that does not
parse), so that stored garbage is noticed rather than taken for a
wrong password; its message never includes the password. Runs on a
helper thread, as hash does.
def self.needs_rehash?(hash: String, params: Crypto::Argon2::Params = …) -> Bool
Whether hash should be made again with params, the next time
the password is at hand (after verify? succeeds): true when one of
its parameters is below params's, and for a string of another
scheme (a $pbkdf2-sha256$ one, argon2i), so that a caller rehashes
whatever it finds. Crypto::Error for an argon2id string that does
not parse.
old = "$argon2id$v=19$m=4096,t=3,p=1$c2FsdHNhbHRzYWx0c2FsdA$fxbFVdPGPQ1NJoy87CaTabyrXOKZepZ9SGBFwPkPJ28"
Crypto::Argon2.needs_rehash?(old) # => true
Crypto::Argon2.needs_rehash?(old, Crypto::Argon2::Params.of(old)) # => false
Crypto::Argon2.needs_rehash?("$pbkdf2-sha256$...") # => true