Password hashing
Argon2id is a memory-hard password hashing and password-based key derivation function (KDF). It takes the following parameters:
- A password.
- An iteration count.
- A memory size in bytes.
- 1.Set the iteration count to 3.
- 2.Set the memory size as high as possible (minimum of 64 MiB) for a reasonable delay (e.g. 0.1 seconds to 1 second) on the type of device your application will run on.
- 3.If the delay is lower than you would like, increase the iterations.
Fills a span with output keying material computed from a password, a random salt, an iteration count, and a memory size in bytes.
Argon2id.DeriveKey(Span<byte> outputKeyingMaterial, ReadOnlySpan<byte> password, ReadOnlySpan<byte> salt, int iterations, int memorySize)
outputKeyingMaterial
has a length less than MinKeySize
.salt
has a length not equal to SaltSize
.iterations
is less than MinIterations
.memorySize
is less than MinMemorySize
.Insufficient memory to perform key derivation.
Fills a span with an encoded password hash computed from a password, an iteration count, and a memory size in bytes.
Argon2id.ComputeHash(Span<byte> hash, ReadOnlySpan<byte> password, int iterations, int memorySize)
You can convert the hash into a string for storage in a database using Encoding.UTF8.GetString(). Only this hash needs to be stored as the parameters are encoded.
hash
has a length not equal to MaxHashSize
.iterations
is less than MinIterations
.memorySize
is less than MinMemorySize
.Insufficient memory to perform password hashing.
Verifies that an encoded password hash is correct for a given password. It returns
true
if the hash is valid and false
otherwise.Argon2id.VerifyHash(ReadOnlySpan<byte> hash, ReadOnlySpan<byte> password)
hash
has a length less than MinHashSize
or greater than MaxHashSize
.Invalid encoded password hash prefix.
Determines if an encoded password hash matches the expected iteration count and memory size. It returns
true
if the hash does not match and false
if the hash matches.Argon2id.NeedsRehash(ReadOnlySpan<byte> hash, int iterations, int memorySize)
hash
has a length less than MinHashSize
or greater than MaxHashSize
.iterations
is less than MinIterations
.memorySize
is less than MinMemorySize
.Invalid encoded password hash.
These are used for validation and/or save you defining your own constants.
public const int KeySize = 32;
public const int SaltSize = 16;
public const int MinKeySize = 16;
public const int MinIterations = 1;
public const int MinMemorySize = 8192;
public const int MinHashSize = 93;
public const int MaxHashSize = 128;
The best defence against password cracking will always be to use strong passwords. For example, diceware with 6+ words.
- Interactive scenario (e.g. online login): 50-100 ms.
- Semi-interactive scenario (e.g. file encryption): 250-1000 ms.
- Non-interactive (e.g. disk encryption): 1500-5000 ms.
Here are some example parameters for different scenarios:
Source | Iterations | Memory (bytes) | *Delay (ms) |
---|---|---|---|
libsodium's interactive | 2 | 67108864 | 51 |
RFC second recommended option | 3 | 67108864 | 72 |
libsodium's moderate | 3 | 268435456 | 314 |
libsodium's sensitive | 4 | 1073741824 | 1745 |
*These delays are for my desktop (a gaming PC). You should perform benchmarks on a typical device for your application using BenchmarkDotNet.
More memory is better than more iterations. However, you will need to increase the iterations in most cases because there should be a limit on how much memory your application uses.
Too high of an iteration count/memory size on a server could lead to denial-of-service (DoS) attacks. You can do client-side password hashing as well as server-side password hashing to help, sometimes called server relief.
The parallelism is always 1 for deriving keys/hashes in libsodium. However, hashes with a parallelism greater than 1 can be verified.
Libsodium also supports Argon2i, which is more side-channel resistant but less GPU resistant. However, Geralt only supports Argon2id because it is the mandatory and recommended variant in the RFC.
Last modified 1d ago