06 — Hash, MAC, KDF
Hash functions (one-way, collision-resistant), Message Authentication Codes (MACs), Key Derivation Functions (KDFs). Fundamental components that appear in every protocol.
1. Cryptographic hash functions — properties
\(H: \{0,1\}^* \to \{0,1\}^n\) must satisfy:
| Property | Definition | Break |
|---|---|---|
| Pre-image resistance | Given \(h\), find \(m\) with \(H(m) = h\) | \(sim 2^n\) expected operations |
| Second-preimage resistance | Given \(m\), find \(m' neq m\) with \(H(m') = H(m)\) | \(sim 2^n\) |
| Collision resistance | Find \(m neq m'\) with \(H(m) = H(m')\) | \(sim 2^{n/2}\) (birthday) |
For \(n = 256\): a collision costs \(2^{128}\) (≥ AES-128). That is why SHA-256 offers "128 bits of security" against collisions.
Additional useful properties
- Pseudorandomness: output indistinguishable from random.
- Avalanche: 1 bit changes in the input → ~50% of output bits change.
- Length extension resistance (non-MD constructions have it).
- Indifferentiability from RO (sponge construction).
2. Historical hash functions
MD2 (Rivest 1989) ❌
128 bits, 8-bit operations. Slow. Collisions found 1995 (Rogier-Chauvaud). Do not use.
MD4 (Rivest 1990) ❌
128 bits, faster. Broken in 1995 (Dobbertin); collision in 1996, practical in minutes by 2007 (Wang). Still used in NTLM password hashing (Windows) — vulnerable.
MD5 (Rivest 1991) ❌
128 bits, Merkle-Damgård. RFC 1321. The workhorse of the 1990s.
History of breaks:
- 1993: Den Boer + Bosselaers — pseudo-collision.
- 1996: Dobbertin — collision in the compression function.
- 2004: Wang Xiaoyun + Yu — first practical collision (rump session, CRYPTO '04).
- 2005: Lenstra-Wang-Weger — forges X.509 certificates.
- 2008: Marc Stevens et al. — chosen-prefix collision; forges a rogue CA certificate signed by RapidSSL.
- 2012: Flame malware (discovered) uses an MD5 chosen-prefix collision to forge a Microsoft Windows Update certificate. Estimated to have been developed by a government.
Status: collision in seconds on a commodity CPU. Do not use for anything cryptographic. Still OK for non-adversarial checksums (file integrity on a mirror).
SHA-0 (NIST 1993) ❌
160 bits. FIPS 180. Withdrawn in 1995 with no public explanation (suspected NSA fix).
SHA-1 (NIST 1995) ❌
160 bits. FIPS 180-1. Workhorse 1995–2010.
Breaks:
- 2005: Wang+Yin+Yu — 269 operations (below the ideal 280).
- 2017: SHAttered (Stevens, Bursztein, Karpman, Albertini, Markov — Google + CWI) — first practical collision with chosen prefix. Cost ~110 GPU-years / US$110k in the cloud. Demo: two distinct PDFs with the same hash.
- 2020: Shambles (Leurent + Peyrin) — practical chosen-prefix collision at ~US$45k. Allows forging GPG/X.509 with an arbitrary identity.
Status: deprecated in any cryptographic context. Removed from TLS 1.3, Chrome/Firefox certs since 2017, OpenSSH since 2020. Acceptable only in HMAC-SHA1 (HMAC survives collisions, but migrate).
SHA-2 family (NIST 2001) ✅
FIPS 180-2 / 180-4. Merkle-Damgård with Davies-Meyer compression. Versions:
| Variant | Output | Block | Word | Notes |
|---|---|---|---|---|
| SHA-224 | 224 | 512 | 32 | truncated SHA-256 |
| SHA-256 | 256 | 512 | 32 | modern workhorse |
| SHA-384 | 384 | 1024 | 64 | truncated SHA-512 |
| SHA-512 | 512 | 1024 | 64 | faster on 64-bit CPUs |
| SHA-512/224 | 224 | 1024 | 64 | RFC 6234 |
| SHA-512/256 | 256 | 1024 | 64 |
Length extension attack: given \(H(m)\) and \(|m|\), an attacker can compute \(H(m \\\| pad \\\| m')\) without knowing \(m\). That is why HMAC uses a specific structure.
Status: SHA-256 and SHA-512 with no practical break in 23 years. The modern standard. Hardware: Intel SHA Extensions (since Goldmont 2016), ARM Crypto Extensions.
SHA-3 / Keccak (NIST 2015) ✅
FIPS 202. Keccak won the NIST SHA-3 competition (2007–2012, 64 candidates). Designers: Guido Bertoni, Joan Daemen, Michaël Peeters, Gilles Van Assche (STMicroelectronics).
Non-Merkle-Damgård — uses a sponge construction:
- Absorb: XOR plaintext into the "rate" of the state; permute (Keccak-f[1600]).
- Squeeze: extract bits from the rate; permute for more.
The Keccak-f[1600] permutation = a 1600-bit state, 24 rounds of θρπχι.
| Variant | Output | Capacity | Security |
|---|---|---|---|
| SHA-3-224 | 224 | 448 | 112 |
| SHA-3-256 | 256 | 512 | 128 |
| SHA-3-384 | 384 | 768 | 192 |
| SHA-3-512 | 512 | 1024 | 256 |
| SHAKE128 (XOF) | variable | 256 | 128 |
| SHAKE256 (XOF) | variable | 512 | 256 |
XOF (Extendable Output Function): arbitrary-length output. Used in padding (Kyber, Dilithium), key stretching.
Immune to length extension by design (sponge absorb always XORs-with-padding before the last permutation).
Performance: software ~2× slower than SHA-256 (without hardware accel). Cheap in hardware/ASICs.
BLAKE2 / BLAKE3
BLAKE was a SHA-3 finalist (lost to Keccak). BLAKE2 (Aumasson, Neves, Wilcox-O'Hearn, Winnerlein 2012) — a refinement.
- BLAKE2b: 64-bit ops, output up to 64 bytes.
- BLAKE2s: 32-bit ops, output up to 32 bytes.
- Built-in keyed mode (replaces HMAC); personalization; tree mode.
- ~3× faster than SHA-256 in software, ~1.5× faster than SHA-512.
- RFC 7693.
BLAKE3 (O'Connor, Aumasson, Neves, Wilcox-O'Hearn 2020):
- Tree-based, arbitrary parallelism.
- ~10× faster than SHA-256 on a modern CPU.
- ~SHA-3 internals (ChaCha-like) with tree mode.
- XOF output up to 2^64 bytes.
- Used in recent IPFS, OpenZFS performance forks, age (partially).
RIPEMD-160 / RIPEMD-256 / RIPEMD-320
A European response in the mid-1990s. RIPEMD-160 is still used in Bitcoin addresses (HASH160 = RIPEMD160(SHA256(pubkey))). RIPEMD-128 broken; RIPEMD-160 has no practical attack but only 80 bits of collision security — below SHA-256.
Whirlpool (Rijmen + Barreto 2000)
A 512-bit hash based on an AES-like Miyaguchi-Preneel construction. ISOIEC 10118-3. Little practical use; superseded by SHA-23.
Streebog (GOST R 34.11-2012, Russia)
256/512 bit. A mandatory standard in Russian state systems.
SM3 (China GM/T 0004-2012)
256 bits, Merkle-Damgård. Mandatory in Chinese systems. Similar to SHA-256 in structure.
3. Hash recommendations (2026)
| Need | Algorithm |
|---|---|
| General cryptographic hash | SHA-256 or SHA-3-256 |
| File hash (fast + secure) | BLAKE3 |
| Hardware-accelerated | SHA-256 (Intel SHA Extensions, ARM Crypto) |
| Length-extension resistance | SHA-3 or BLAKE2/3 |
| FIPS-compliant | SHA-256384512, SHA-3 |
| Post-quantum (Grover) | SHA-384 or SHA-3-384 minimum (classical collision \(2^{192}\), but Grover gives \(2^{n/3}\) — debated) |
| Non-cryptographic (hashmap, non-adversarial checksum) | xxHash, CityHash, MurmurHash3, SipHash (DoS-resistant) |
Anti-patterns
❌ MD5, SHA-1 in a cryptographic context. ❌ Truncating SHA-256 below 128 bits without analysis. ❌ Using a hash without salt for password storage (see §KDFs). ❌ Using a cryptographic hash in an internal hashmap (DoS via collision flooding). ❌ Assuming that the hash of a known string is a "secret" (rainbow tables).
4. MACs — Message Authentication Codes
A MAC guarantees integrity + authenticity (but not non-repudiation, because the receiver can forge).
Interface: \(\text{MAC}_k(m) = T\); verification compares the tag.
HMAC (RFC 2104, 1996)
Bellare + Canetti + Krawczyk. The universal standard. FIPS 198-1.
where \(opad = 0x5C5C\dots\), \(ipad = 0x3636\dots\).
- Works with any hash (HMAC-SHA256, HMAC-SHA3-256, HMAC-BLAKE2b).
- Resistant to length extension (double structure).
- Provably secure under the assumption that the hash is a PRF (Bellare 2006).
- HMAC-SHA-1 is still OK because a collision in the hash ≠ an HMAC forge.
KMAC (NIST SP 800-185, 2016)
A native MAC for Keccak/SHA-3. More efficient than HMAC-SHA3 (no double compression).
Poly1305
Bernstein 2005. A polynomial MAC over GF(\(2^{130} - 5\)). One-time MAC — requires a unique nonce. Always used in combination: ChaCha20-Poly1305, AES-GCM-Poly1305 variants. Blazing fast (3 cyclesbyte on ARM, ~1 cyclebyte with VEC).
CMAC (NIST SP 800-38B, RFC 4493)
A CBC-MAC variant with subkeys. AES-CMAC is the standard.
GMAC
The tag-only mode of GCM (no encryption). Used when only authenticity is required.
SipHash (Aumasson + Bernstein 2012)
Not for long messages; optimized for short inputs (up to a few KB). Resists hash flooding (DoS against a hashmap). The default keyed hash in the internal hashmaps of Rust, Python (3.4+), Ruby, Go, Perl, Haskell.
Anti-patterns
❌ MAC truncated below 64 bits. ❌ MAC without a nonce/IV in encrypt-then-MAC. ❌ Non-constant-time comparison (timing leak in verification) — use subtle.ConstantTimeCompare / crypto.timingSafeEqual. ❌ Using the same key for encrypt and MAC (derive via HKDF).
5. KDFs — Key Derivation Functions
KDF for cryptographic keys: HKDF
HKDF (Krawczyk + Eronen, RFC 5869, 2010). Two-phase:
prk = HMAC(salt, ikm) // Extract
okm = HMAC(prk, info \\\| 0x01)
\\\| HMAC(prk, okm[0..] \\\| info \\\| 0x02) ... // Expand- salt: public, ideally random (but zero is OK).
- ikm: input keying material (DH shared secret, master key).
- info: context binding (a string identifier for the use).
- okm: output, any length.
Used in the TLS 1.3 key schedule, Signal Protocol, Noise, MLS. The modern standard.
Other KDFs
- NIST SP 800-108 — KDF in counter mode, feedback, double pipeline.
- X9.63 KDF — used in legacy ECIES.
- Concat KDF — simple; used in JOSE.
Password-based KDFs (PBKDF)
Passwords have low entropy (human-memorable). An attacker runs a dictionary attack. A password KDF makes this intentionally expensive (memory, CPU).
PBKDF2 (RSA Labs 2000; RFC 8018)
PBKDF2(P, S, c, dkLen) = HMAC(P, S \\\| 1) \\\| HMAC(P, S \\\| 2) ...- \(c\) = iterations (600k recommended for SHA-256 in 2026; OWASP).
- Easy GPU acceleration → weak against a modern attacker.
- Still OK for legacy compatibility.
- FIPS-approved.
bcrypt (Provos + Mazières 1999)
Based on a modified Blowfish key schedule (expensive to initialize). Memory: ~4 KB (fixed).
- Cost parameter \(\text{cost}\): \(2^{\text{cost}}\) iterations.
- Password limit: 72 bytes (truncation).
- Not FIPS.
- Massive adoption: legacy Linux PAM, Ruby/Rails default, PHP
password_hash.
scrypt (Percival 2009; RFC 7914)
Memory-hard: forces the attacker to use memory, not just CPU. Anti-ASIC initially.
- Parameters: \(N\) (CPU/memory cost), \(r\) (block size), \(p\) (parallelism).
- 2026 OWASP: \(N = 2^{17}\), \(r = 8\), \(p = 1\) → ~64 MB memory.
- Taken from Litecoin proof-of-work.
Argon2 (Biryukov + Dinu + Khovratovich 2015)
Winner of the PHC (Password Hashing Competition 2013–2015). RFC 9106.
Variants:
- Argon2d: data-dependent memory access (resistant to TMTO; vulnerable to side-channel).
- Argon2i: data-independent (resistant to side-channel; less resistant to TMTO).
- Argon2id: recommended — hybrid: 1st pass data-independent, then data-dependent.
Parameters: memory \(m\), iterations \(t\), parallelism \(p\).
OWASP 2026 recommendation: Argon2id, \(m = 19\) MiB, \(t = 2\), \(p = 1\) (minimum) — tune for current hardware with a "1 second per verify" limit as the benchmark.
Comparison
| KDF | Memory | GPU/ASIC | FIPS | Recommendation |
|---|---|---|---|---|
| PBKDF2 | low | easy | ✅ | only legacy/FIPS-required |
| bcrypt | medium (4KB) | hard | ❌ | robust legacy |
| scrypt | configurable | hard | ❌ | still OK; Argon2 better |
| Argon2id | configurable | hard | ❌ (but PHC standard) | modern standard |
| Balloon | configurable | hard | ❌ | academic niche |
6. Related constructions
Merkle trees
A hash tree. Leaves = \(H(\text{leaf}_i)\); nodes = \(H(\text{left} \\\| \text{right})\). The root is a commitment to all the data.
Inclusion proof: \(O(\log n)\) size. Lets you prove that \(x\) is in the set without revealing the whole set.
Applications:
- Git commit hashes (tree of blobs).
- Bitcoin block merkle root.
- Certificate Transparency (Google).
- Verkle trees proposed in Ethereum.
- BitTorrent (BEP 30).
HMAC-based One-Time Password (HOTP, RFC 4226)
Counter-based OTP. Moved on to event-based (Yubikey HOTP) and time-based (TOTP).
TOTP (RFC 6238)
\(\text{TOTP} = \text{HOTP}(k, \lfloor T/30 \rfloor)\). Google Authenticator, Authy, FreeOTP, 1Password, etc.
KDF for SRP, OPAQUE
PAKE (Password-Authenticated Key Exchange) protocols use a specific KDF construction to avoid an offline dictionary attack on the server side.
7. Hashes based on hard problems (RPM-friendly)
- Pedersen commitments: \(\text{Com}(m, r) = g^m h^r\) — perfectly hiding, computationally binding (under DLP). Used in ZK.
- VDF (Verifiable Delay Function): a hash that takes \(T\) sequential seconds (irreducible in parallel). Wesolowski 2019, Pietrzak 2019. Used in Ethereum lottery seeding, Filecoin proof-of-replication.
- VRF (Verifiable Random Function): a hash with a proof; publicly verifiable and deterministic. RFC 9381. Used in DNSSEC (NSEC5), blockchains.
8. Summary table — what to use where
| Use | 2026 standard |
|---|---|
| File checksum (non-adversarial) | BLAKE3, xxHash |
| Content-addressable storage | BLAKE3 or SHA-256 |
| Git object IDs | SHA-1 today, SHA-256 in transition (Git 2.29+) |
| Password storage | Argon2id with local tuning |
| Legacy password storage | bcrypt |
| Key derivation from DH/ECDH | HKDF-SHA256 |
| MAC | HMAC-SHA256 or Poly1305 |
| MAC for short messages | SipHash (DoS resistance) |
| Hash-based signature (PQ) | SHA-256 base; SLH-DSA uses SHA-256 and SHAKE256 |
| 2FA token | TOTP (RFC 6238) with HMAC-SHA1 or SHA-256 |
| Certificate fingerprint | SHA-256 (not SHA-1) |
| TLS handshake hash | SHA-384 (TLSAES256GCMSHA384) or SHA-256 |
| Blockchain (Bitcoin) | double-SHA256 (legacy), Schnorr+SHA256 (Taproot) |
| Blockchain (Ethereum) | Keccak-256 (NOT SHA-3-256, but Keccak-256 with original padding) |
9. Cross-reference
- Block ciphers used in CBC-MAC, GMAC:
04-symmetric.md. - Hash-based signatures (SLH-DSA, XMSS, LMS):
08-post-quantum.md. - Side-channel in HMAC (timing):
11-attacks.md. - Collision attacks (MD5 chosen-prefix, SHA-1 SHAttered):
13-incidents.md.