argon2 clib Security

Argon2 password hashing. argon2id/i/d, PHC string format, verify, timing-safe, GPU-resistant.

ppm install argon2

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.