Overview
Argon2 won the Password Hashing Competition in 2015 and is the current OWASP-recommended algorithm for storing passwords. It is memory-hard — attackers need large amounts of RAM to attack hashes in parallel, making GPU/ASIC attacks impractical. Use argon2id for all new applications.
CLib package
Ubuntu/Debian: sudo apt install libargon2-dev
Hash & Verify (common case)
uses argon2;
{ hash a password at registration — store the result in your DB }
var hash := Hash('user-password-here');
WriteLn(hash);
{ $argon2id$v=19$m=65536,t=3,p=4$$ }
{ verify at login — timing-safe, always returns True/False }
if Verify(hash, 'user-password-here') then
WriteLn('Login OK')
else
WriteLn('Wrong password');
Custom Parameters
uses argon2;
{ tune parameters for your hardware and security requirements }
var params := InteractiveParams(); { good default for login: < 0.5s }
params.TimeCost := 4; { more iterations }
params.MemoryCost := 131072; { 128 MB instead of 64 MB }
params.Parallelism:= 2;
var result := HashEx('my-password', params);
WriteLn(result.PHCString); { store this }
WriteLn(ToHex(result.RawHash)); { raw bytes if needed }
WriteLn(ToHex(result.Salt));
{ verify works with any argon2 PHC string regardless of params }
WriteLn(Verify(result.PHCString, 'my-password'));
Rehash on Login
uses argon2;
{ best practice: upgrade weak hashes transparently at login time }
var storedHash := GetHashFromDB(userId);
if Verify(storedHash, password) then begin
WriteLn('Login OK');
{ check if hash needs upgrading (old params or old algorithm) }
if NeedsRehash(storedHash) then begin
var newHash := Hash(password); { rehash with current defaults }
SaveHashToDB(userId, newHash);
WriteLn('Hash upgraded');
end;
end;
Key Derivation
uses argon2, libsodium;
{ derive an encryption key from a password + salt
(do NOT use Hash() for this — it returns a PHC string, not raw bytes) }
var salt := RandomBytes(16); { from libsodium }
var params := SensitiveParams(); { stronger params for key material }
var key := DeriveKey('master-password', salt, 32, params);
{ key is now 32 bytes suitable for SecretBoxEncrypt }
var nonce := SecretBoxNonce();
var cipher := SecretBoxEncrypt(sensitiveData, nonce, key);
{ to decrypt later: same password + stored salt → same key }
var key2 := DeriveKey('master-password', salt, 32, params);
var plain := SecretBoxDecrypt(cipher, nonce, key2);
Benchmark & Presets
uses argon2;
{ measure how long your params take on this machine }
var params := InteractiveParams();
var ms := BenchmarkParams(params);
WriteLn('Interactive preset: ', ms, ' ms');
var sens := SensitiveParams();
ms := BenchmarkParams(sens);
WriteLn('Sensitive preset: ', ms, ' ms');
{ preset comparison }
{ InteractiveParams: t=3, m=64MB, p=4 — login forms }
{ SensitiveParams: t=4, m=1GB, p=8 — API keys, master passwords }
{ MinParams: t=1, m=8MB, p=1 — high-volume, low-risk }
{ inspect params of a stored hash }
var stored := '$argon2id$v=19$m=65536,t=3,p=4$...';
var p := ParsePHC(stored);
WriteLn('Memory: ', p.MemoryCost, ' KB Iterations: ', p.TimeCost);
Variants
uses argon2;
{ argon2id — recommended for all new code }
var h1 := HashVariant('password', argon2id);
{ argon2i — side-channel resistant, for constrained environments }
var h2 := HashVariant('password', argon2i);
{ argon2d — maximum GPU resistance, for crypto applications }
var h3 := HashVariant('password', argon2d);
{ detect variant of a stored hash }
var variant := GetVariant(storedHash);
if variant = argon2id then
WriteLn('Modern hash');
Package Info
Version1.0.0
Typeclib
CategorySecurity
Authorgustavo
StandardPHC winner 2015
API
- Hash(password)
- HashEx(password,params)
- HashWithSalt(pwd,salt,params)
- HashVariant(pwd,variant)
- Verify(hash,password)
- VerifyVariant(hash,pwd,v)
- NeedsRehash(hash)
- NeedsRehashEx(hash,params)
- DeriveKey(pwd,salt,len,params)
- DeriveKeyDefault(pwd,salt,len)
- ParsePHC(hash)
- GetVariant(hash)
- BenchmarkParams(params)
- InteractiveParams()
- SensitiveParams()
- MinParams()
Never use MD5/SHA for passwords
Plain SHA-256 can be cracked at 10 billion hashes/sec on a GPU. Argon2id at default params: ~1 hash/sec. That's the difference.