密钥派生函数(KDF)
KDF 从已有 secret/keying material 派生一个或多个用途隔离密钥;HKDF 面向高熵 key material 做 extract/expand,而 PBKDF2/Argon2 属于 password-based KDF,专门用 salt 与高计算/内存成本抵抗低熵口令猜测。
[!info] related notes
- Hash: 密码学哈希函数
- HMAC: HMAC
- TLS: TLS 1.3 Key Schedule
- Password Storage: Password Storage & Verification
- Password Algorithms: Argon2id, PBKDF2, bcrypt
- Salt/Pepper: Salt vs Pepper
- Password vault: 零知识加密架构
密钥派生函数(KDF)
一句话定义
KDF 的目标是:
从已有 secret / keying material 派生出一个或多个适合具体用途的 cryptographic key。
但 KDF 并不只有一种。
至少要区分:
- general-purpose key derivation;
- password-based key derivation。
把所有 KDF 都理解成“故意很慢的哈希”是错误的。
General-Purpose KDF
这类 KDF 处理的输入通常已经具有足够 entropy,例如:
- ECDHE shared secret;
- master secret;
- device secret;
- root key。
典型目标:
- extract entropy;
- expand 成多个 subkeys;
- domain separation;
- key hierarchy;
- protocol context binding。
代表:
- HKDF。
它通常追求:
快速、确定、可证明地派生用途隔离 key。
而不是故意拖慢攻击者。
HKDF
HKDF 常分两步:
Extract
→ 把输入 keying material 压缩/规范成 pseudorandom key
Expand
→ 按 context/info 派生一个或多个 output key
典型用途:
- TLS 1.3 key schedule;
- protocol subkeys;
- session key derivation。
因此:
HKDF 不是 password hashing function。
Password-Based KDF
Password entropy 通常很低。
人类密码例如:
- dictionary word;
- short phrase;
- reused password;
不能直接作为 AES key。
Password-based KDF 要额外加入:
- salt;
- configurable work factor;
- 某些算法的 memory cost;
- parallelism control。
代表:
bcrypt 也属于 password hashing scheme,但其成本模型和输入边界与以上算法不同。
这里“慢”是设计目标之一。
Salt 的角色
随机 salt 主要解决:
- 相同 password 产生不同 derived result;
- 破坏通用 rainbow table;
- 迫使 attacker 对每个 salt 重新计算。
Salt 通常不需要保密。
它与 secret pepper 不同。
Work Factor
Password KDF 的目标是让:
one password guess
具有可控成本。
对合法用户:
- 几十到几百毫秒可能可接受。
对 attacker:
- billions of guesses 会显著变贵。
参数需要随着硬件能力变化定期重新评估。
Memory-Hard KDF
Argon2id / scrypt 等还让攻击者为每次猜测付出显著 memory cost。
这削弱 GPU/ASIC 大规模并行爆破的成本优势。
KDF 与 Hash
Hash:
message → fixed-length digest
KDF:
secret/keying material + salt/context
→ key material
KDF 可能内部使用 hash / HMAC,但目标和安全模型不同。
KDF 与 Encryption
KDF 不负责把 plaintext 变成 ciphertext。
常见链路:
password
→ Argon2id
→ encryption key
→ AEAD
→ ciphertext
或:
ECDHE shared secret
→ HKDF
→ traffic keys
→ AEAD
→ TLS records
Domain Separation
从一个 root secret 派生多个用途 key 时,应该用不同 context/info:
root secret
→ KDF(“encryption”) → encryption key
→ KDF(“authentication”) → auth key
而不是把同一个 key 同时拿去做多个独立用途。
Bitwarden 场景
Password vault 常使用 password KDF:
master password
→ PBKDF2 / Argon2id
→ master key material
然后再保护 vault encryption key。
这里与 TLS 的 HKDF 完全不是同一 threat model。
常见误区
KDF 都必须故意很慢
错误。HKDF 这类 general-purpose KDF 不以慢为目标。
Password hash 只要 SHA-256 多算几次就够
不稳定。应使用标准 password KDF。
derived key 越多越危险
只要使用标准 KDF、正确 context/domain separation,从 root secret 派生多个独立用途 key 正是常见设计。
Salt 是 secret
通常不是。Salt 主要要求 unique/random,而非保密。
学习检查
- general-purpose KDF 与 password-based KDF 的 threat model 有什么不同?
- HKDF 为什么不应该故意很慢?
- PBKDF2/Argon2 为什么需要 work factor?
- Salt 解决什么问题,为什么通常不需要保密?
- KDF 与普通 Hash 的目标有什么差异?
- domain separation 为什么能减少 key reuse 风险?
- TLS 1.3 与 password vault 为什么虽然都用 KDF,但算法目标完全不同?