Considering @kornel's advice, I decided to benchmark these crates. This isn't entirely straightforward:
- The Argon2 algorithm lets you adjust the number of iterations, the amount of memory used, and the number of threads to busy, to make sure password hashing takes long enough to make attacks as impractical as you need. Obviously, you need to run all three crates with equivalent parameters to make a useful comparison.
- Some crates have features to enable the use of SIMD.
I think I managed to get things lined up. Here are my results:
running 3 tests
test bench_argon2rs ... bench: 216,181,118 ns/iter (+/- 6,126,616)
test bench_argonautica ... bench: 189,841,017 ns/iter (+/- 2,232,554)
test bench_rust_argon2 ... bench: 520,689,832 ns/iter (+/- 4,013,588)
I enabled the "simd"
feature for Argonautica. I tried to enable the "simd"
feature on argon2rs
, but it didn't compile. rust_argon2
has no SIMD feature.
Here's my Cargo.toml
:
[package]
name = "play-argon"
version = "0.1.0"
authors = ["Jim Blandy <jimb@...>"]
edition = "2018"
[dependencies]
argon2rs = "0.2.5" # SIMD doesn't build (unrecognized `extern "platform-intrinsic"` function)
argonautica = { version = "0.2", features = ["simd"] }
rust-argon2 = "0.8.2"
Here's my code:
#![feature(test)]
extern crate test;
use test::Bencher;
const PASSWORD: &str = "correct horse battery staple";
const SALT: &str = "Along Russian's 'Road of Bones,' Relics of Suffering and Despair";
#[bench]
fn bench_argon2rs(b: &mut Bencher) {
use argon2rs::{Argon2, Variant};
let argon2 = Argon2::new(192, 8, 4096, Variant::Argon2d).unwrap();
let mut out = [0_u8; 32];
b.iter(|| argon2.hash(&mut out, PASSWORD.as_bytes(), SALT.as_bytes(), &[], &[]))
}
#[bench]
fn bench_argonautica(b: &mut Bencher) {
use argonautica::Hasher;
use argonautica::config::Variant;
b.iter(|| {
Hasher::default()
.configure_iterations(192)
.configure_lanes(8)
.configure_memory_size(4096)
.configure_variant(Variant::Argon2d)
.opt_out_of_secret_key(true)
.with_password(PASSWORD)
.with_salt(SALT)
.hash()
.unwrap()
})
}
#[bench]
fn bench_rust_argon2(b: &mut Bencher) {
use argon2::{self, Config, Variant};
let config = Config {
time_cost: 192,
lanes: 8,
mem_cost: 4096,
variant: Variant::Argon2d,
.. Config::default()
};
b.iter(|| {
argon2::hash_encoded(PASSWORD.as_bytes(), SALT.as_bytes(), &config).unwrap()
});
}
fn main() {}
If there's something I can improve to make this a better comparison, please let me know.