Argon2id Tuning in Practice: Why 64 MB × 3 Iterations Was the Right Call

How we picked Argon2id parameters for ByteGuard — benchmarks on real iPhones, the OWASP recommendation, the UX vs security trade-off, and what 1Password and Bitwarden chose.

Published: 2026-05-05 · 9 min read

KDF 调参为什么是头等大事

大多数密码管理器把用户的主密码缩短成一个固定长度的加密密钥,靠的是密钥派生函数(KDF)。这个 KDF 是用户被偷走密码库文件之后、攻击者把它变成你明文登录信息之间的最后一道墙。

KDF 弱了,整个加密栈连 AES-256-GCM 一起垮掉。AES 强不强不重要 — 如果 Argon2id 每次只跑 0.1 ms,攻击者租一台 A100 GPU 一小时就能对你的密码库试 100 亿个候选密码。

KDF 强过头了,手机每次按 Face ID 都要等 5 秒才能解锁 App。用户直接卸载。

整个游戏就是:在保证正常解锁延迟的前提下,让攻击者每次猜测付出最大代价。

Tech Note

"每次猜测的代价"是不对称的。你的手机每次解锁只跑一次 KDF — 体感受单次延迟约束。攻击者要跑几十亿次 — 体感受总成本约束。每次猜测代价提高 10 倍,攻击者代价就是用户代价的 10 倍。

为什么选 Argon2id(而不是 PBKDF2 或 scrypt)

Argon2id 是 2015 年 密码哈希竞赛 的冠军,从那以后一直是新系统的推荐 KDF。它有 PBKDF2 没有的三个性质:

  1. 内存困难性(memory-hard)。Argon2id 每次猜测都要占用 N 兆字节内存。PBKDF2 几乎不占内存,所以攻击者能在单卡 GPU 上同时跑几千个并行猜测。内存困难性把这条路彻底封死 — 每次猜测都得有自己专用的内存块。
  2. 抗侧信道("id" 模式是混合:第一遍抗侧信道,后续遍抗时间-内存权衡攻击)。
  3. 参数化工作量。内存成本(m)、迭代次数(t)、并行度(p)都能独立调。PBKDF2 只能调迭代次数。

在攻击规模下,代价差异大约是 1000 倍。一台现代 GPU 矿机每秒能跑约 10⁹ 次 PBKDF2-SHA256,但只能跑约 10⁶ 次 Argon2id (64 MB)。这就是"8 字符密码一夜破解" vs "8 字符密码要花一千夜破解"的差别。

"内存困难"到底是什么意思

具体来说:Argon2id 申请一块 64 MB 内存,用派生字节填满,每个字节都依赖之前所有字节。要算出最终哈希值,算法以一种防止"边算边丢不存"优化的模式遍历这块内存。攻击者没法用空间换时间。

对比 GPU 经济:

  • PBKDF2-SHA256:每次猜测约需 32 字节状态。A100 有 40 GB HBM2 → 能跑约 12.5 亿次并行猜测。
  • Argon2id (64 MB):每次猜测要 64 MB 状态。同一张 A100 → 只能跑 625 次并行猜测。

同样的硬件,并行槽位少了 200 万倍。这就是内存困难型 KDF 砌起来的结构性护城河。

OWASP 推荐配置(以及我们为什么照搬)

OWASP 密码存储最佳实践 对移动端 / 交互式场景的推荐:

OWASP Argon2id 基线
m = 64 MB
·
t = 3 iter
·
p = 1 thread

这就是 ByteGuard 用的配置。不是因为我们想保守 — 而是把数据跑了一遍,OWASP 的选择正好落在 iOS 的甜蜜区。

为什么不是 128 MB × 2 iter,或 32 MB × 4?

总工作量大致是 m × t。128×2(256 MB·iter)和 64×3(192 MB·iter)都比 32×4(128 MB·iter)强。光看纸面,128×2 应该是最大代价。但有三个因素把天平拉回 64×3:

  1. iOS 内存压力。iOS 会激进地杀掉占内存高的后台 App。解锁瞬间临时申请 128 MB 在 iPhone SE / iPhone 12 mini(总 RAM 3-4 GB,可用更少)上会触发 memory warning。64 MB 是个干净的选择。
  2. UX 延迟。Argon2id 的内存访问比迭代次数更主导挂钟时间。同设备上 128 MB × 2 比 64 MB × 3 慢,尽管工作产出相近。
  3. 面向未来的方向。内存困难性随攻击者 GPU 内存增长而扩展。GPU RAM 增加时,调高 m,不是 t。从 64 MB 起步,给后续升级到 128 MB / 256 MB 留足头部空间,无需 UX 大手术。

iPhone 真机基准

我们在测试矩阵上的设备测了 Argon2id 64 MB × 3 iter(libsodium crypto_pwhash_str,OPSLIMIT_INTERACTIVE):

设备年代SoC解锁延迟(100 次平均)
iPhone 15 Pro2023A17 Pro180 ms
iPhone 142022A15240 ms
iPhone 13 mini2021A15250 ms
iPhone 122020A14290 ms
iPhone SE (3rd gen)2022A15250 ms
iPhone 112019A13340 ms
iPhone XS2018A12410 ms

UX 研究文献把"快速反馈"定为 100 ms 以内、"无可感知延迟"定为 200 ms 以内。我们在当前支持设备上停在 180–290 ms — Face ID 之后小但可接受的等待。超过 300 ms 用户开始能感觉到。iPhone XS 落在 410 ms,但 Face ID + Argon2id 一起完成总共还在 700 ms 内,完全在 Apple 自己的 Touch ID / Face ID + Keychain 解锁预算之内。

Tech Note

"100 次平均"很重要,因为冷启动后第一次调用慢 50–100 ms(64 MB 申请的 page-fault)。我们在 App 启动时预热分配器,让用户感知的第一次解锁走的是热路径。

1Password 和 Bitwarden 怎么选的

产品KDF参数备注
ByteGuardArgon2id64 MB × 3 iter × 1 thread按 OWASP 移动端推荐。固定。
1PasswordPBKDF2-SHA256650,000 iterations由他们的 128-bit Secret Key 作为第二因子补偿。
BitwardenPBKDF2-SHA256 或 Argon2idPBKDF2: 600,000 / Argon2id: 64 MB × 32023 起用户可选。Argon2id 是 opt-in,默认还是 PBKDF2。
Apple Keychain(内部)(未公开)Apple Platform Security Guide 没有公开 iCloud Keychain KDF 参数。

1Password 的选择是合理的,因为他们的 Secret Key 提供了 128 bit 熵,永远不离开设备。攻击者偷到密码库但拿不到 Secret Key,无论密码强弱都要面对 2¹²⁸ 个候选密钥。ByteGuard 用了同样的架构模式 — 我们的 Secret Key 是 12 词 BIP39 助记词 — 所以 Secret Key 收益加上 Argon2id 内存困难性,叠加优势。

什么时候(以及怎么)我们会重调参数

有两个触发器会强制我们重调:

  1. 硬件代际推进。当中位支持的 iPhone 比当前新 2 代(即 2027 年基线 = iPhone 13 / A15 起步),我们会考虑把内存提到 96 MB 或 128 MB。Argon2id 的 CPU 成本相对单核性能基本恒定 — 变化的是内存带宽。
  2. GPU 内存增长。如果消费级 GPU 跳到 128+ GB HBM(目前 A100 是 40 / 80 GB,H100 是 80 GB),64 MB Argon2id 的并行攻击吞吐量会增加。我们会按比例调高 m。

协议本身就内置了版本化:每个 Argon2id 哈希在编码字符串里都带参数($argon2id$v=19$m=65536,t=3,p=1$...)。参数变更后旧密码库在下次解锁时自动重哈希,迁移是增量、静默的。

关键要点

  • KDF 强度是其他所有加密保证的基础。随便挑就是 bug。
  • Argon2id 在攻击规模下比 PBKDF2 强约 1000 倍,靠的是内存困难性,不是迭代次数。
  • OWASP 的 64 MB × 3 iter × 1 thread 是移动端务实的甜蜜区 — 不是最强配置,但是 iPhone 13 / A15 上能在 300 ms 内跑完的最强配置。
  • iOS 内存压力是真问题。解锁瞬间申请 128 MB 在低端设备上有 OS 杀掉风险。
  • 不要相信"我们用 Argon2id"作为营销话术。问参数:m、t、p。没有这三个数字,这句话毫无意义。

ByteGuard 完整的密钥层级与 iOS Keychain 集成在 安全白皮书 中有详细记录。

Download ByteGuard on the App Store