Argon2id benchmark data

This dataset is being compiled while we research realistic Argon2id parameter choices for the Who Technologies mTOTP protocol; we're publishing the results because the data is likely useful to the broader community working with Argon2. The full raw dataset (one row per measured hash) will be released free for anyone to use once ready.


loading…
within ±50% of target faster than target (darker = more so) slower than target (darker = more so) estimated OOM on some shown devices OOM on every shown device no data
Display options
Heatmap filters — affect heatmap only


Defender vs Attacker
How long does hashing take on a user's device, and how long would it take an attacker to crack passwords at the same parameters?
Defender — hash time (min / median / max across devices)
Parameters
Attacker — GPU hours to crack (average case, fastest GPU in dataset)

About this data

Each cell is one (m × t) parameter point at p = 1. Defender platforms (Android, iOS, Desktop/Server) hash 1-3 times (depending on the speed of the hash) which are averaged together for that device. Attacker (GPU) uses hashcat --speed-only to measure steady-state throughput. The heatmap always shows one platform at a time so defender and attacker results are never mixed.

Libraries

Android: lazysodium-android 5.1.0 (libsodium JNI bindings). Desktop/Server: PyNaCl (libsodium Python bindings; version varies by host). GPU: hashcat v7.1.2 (patched), mode 34000.

Hashcat patches

Three patches were applied to upstream hashcat v7.1.2:

Estimation and blank cells

On all platforms, once a measured hash exceeds a wall-clock cutoff the benchmark stops measuring further (more expensive) cells in that memory column and extrapolates from prior data instead. These estimated rows (stopped_reason = "estimated_timeout") are shown with a diagonal-stripe overlay. Estimates are calculated as:

pass_1_cost = ns(lowest_t in column)
per_pass_cost = avg( (ns(t) - pass_1_cost) / (t - lowest_t) ) for all measured t > lowest_t
estimate(t_target) = pass_1_cost + (t_target - lowest_t) * per_pass_cost
...which takes into account the slower first pass. From testing, this estimate is more than 99% accurate at the higher m difficulties. For lower m difficulties where timing noise plays a larger role, we directly test each cell to get more accurate data. When both real and estimated data exist for a cell, the real measurement is used.

Blank cells at high memory mean the device ran out of memory (OS OOM-kill on Android/iOS/Desktop; VRAM exhaustion on GPU).

OOM markers

OOM events are bucketed per memory column (memory cost drives peak allocation, not time cost). The marker counts devices in the current filter with any OOM in that column:

Columns with no OOM events get no marker, even if data is missing. If you're selecting Argon2id parameters for a mobile app, you probably want to stay a reasonable distance away from these yellow and red zones.

Aggregation and colors

When multiple devices match the filters, each cell aggregates across all matching runs. The target value sets the color dividing line: ±50% of target renders blue, faster is green, slower is red (darker = further from target). Switching between H/s and ms converts the target and relabels the aggregation options automatically.

Measurement precision

Early Android data is millisecond-precision only; newer Android builds and GPU runs include a nanosecond-precision field. Integer-only cells (e.g. 5) could be anywhere in [5, 6) ms; cells with decimals have sub-ms precision. When both precisions exist for a cell the ns value is used. Sub-ms cells with no ns data show as <1.