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) mix of measured and estimated data estimated data 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 time 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. Estimated cells are shown with a grey diagonal-stripe overlay running the opposite way to the OOM markers: a thin stripe when the cell blends measured devices with estimated ones, a slightly thicker one when every value in the cell is an estimate (hover a cell for the exact counts). 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 mean no measurement exists for that parameter combination — the cell was not tested (or was dropped for lost precision).

OOM markers

OOM events are bucketed per memory column (memory cost drives peak allocation, not time cost). The lowest memory level at which a device has ever OOMed is its memory ceiling: Argon2id's footprint is set by memory cost alone, so the device cannot run any column at or above it, and the marker covers every column from the ceiling to the end of the grid even where no run recorded an OOM. The marker counts devices in the current filter whose ceiling is at or below the column:

Columns below every shown device's ceiling get no marker, even if data is missing. A cell that is both estimated and inside an OOM-marked column carries both hatches, crossing: the grey estimate stripes run one way, the coloured OOM stripes the other. If you're selecting Argon2id parameters for a mobile app, you probably want to stay a reasonable distance away from these orange 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.