Skip to main content

Crate argon2_rust

Crate argon2_rust 

Source
Expand description

A pure-Rust port of the reference Argon2 implementation (phc-winner-argon2), with runtime-dispatched SIMD backends.

Argon2 is the winner of the 2015 Password Hashing Competition and is specified in RFC 9106. Three variants exist, see Algorithm:

  • Argon2d — data-dependent addressing: fastest, but leaks a memory access pattern that depends on the password.
  • Argon2i — data-independent addressing: side-channel resistant.
  • Argon2id — independent for the first half-pass, dependent afterwards. The default, and what RFC 9106 recommends.

§Example

use argon2_rust::{Algorithm, Argon2, Error, Params, Version};

// Default: 19 MiB, t=2, 1 lane, 32-byte tag (raw hash output). ~8 ms per
// hash in release on M-series — cheap enough for a real doctest. Raise
// `m_cost` until it fits your budget.
let params = Params::default();
let argon2 = Argon2::new(Algorithm::Argon2id, Version::V0x13, params);

let mut tag = [0u8; 32];
argon2.hash_into(b"password", b"somesalt", &mut tag)?;
assert_eq!(argon2.verify(b"password", b"somesalt", &tag), Ok(()));
assert_eq!(
    argon2.verify(b"wrong", b"somesalt", &tag),
    Err(Error::VerifyMismatch),
);

§SIMD backends

The compression function is selected by runtime CPU feature detection, never by cfg(target_feature) alone, so one binary runs at full speed on every machine. Detection happens at most once per process and the result is cached; a hash call resolves one function pointer before entering its loops. Ask detected_backend what this CPU picked. The cost model is documented on the private fill_block module.

BackendRequiresSource it was ported from
Scalarsrc/ref.c
Neonaarch64src/opt.c (128-bit path)
Sse2x86/x86_64 + SSE2src/opt.c (128-bit path)
Avx2x86_64 + AVX2src/opt.c (__AVX2__)
Avx512x86_64 + AVX-512Fsrc/opt.c (__AVX512F__)

The unpadded standard-Base64 codec used by PHC strings has a separate, cached dispatch: AVX2, SSSE3, AArch64 NEON, wasm SIMD128, then scalar. Its vector kernels follow base64-simd/aklomp/base64, while inputs shorter than one vector stay on the original scalar loop. As in base64-simd, AVX-512-capable CPUs use this codec’s AVX2 path; the wider backend remains specific to the Argon2 compression function above. Call encode_base64 and decode_base64 for that codec on its own; detected_base64_backend reports which implementation this CPU picked. The PHC string itself is encode_string / decode_string (C encode_string / decode_string) or decode_phc when the input may be a @phc/format / node-argon2 string (m,p,t any order, optional data=).

BLAKE2b (H0, the long expansion, and finalize) has its own cascade, x86 only: AVX-512 → AVX2 → SSE4.1 → scalar. AArch64 stays on scalar; a NEON compress was measured slower. blake2b / blake2b_long are the one-shot entry points; detected_blake2b_backend reports the choice.

§Features

  • std (default) — runtime CPU feature detection, including memchr’s SIMD cascade for PHC field scans.
  • parallel (default, implies std) — multi-threaded fill. One std::thread::scope for the whole fill, whose workers meet at a barrier at each of the 4 * t_cost algorithmic sync points, rather than a fresh scope per sync point. Note that the thread count does not change the tag; only Params::lanes does.
  • zeroize-memory (default) — securely wipe internal buffers, the equivalent of FLAG_clear_internal_memory in the C.
  • bump-alloc — internal test/bench control. Together with internal-api, gives memory::Workspace a reusable bump allocator for measuring small scratch buffers. It does not change the stable hash/encode/verify paths, which deliberately keep their Vecs. Measured upper bound: 17 ns per hash, or 0.00013% of an RFC 9106 hash.
  • internal-api — exposes __internal for tests and benches. Not stable.

The crate is #![no_std] and needs alloc plus memchr. That stays true with every feature turned on. Without std, backend selection (and memchr) fall back to compile-time target_feature cfgs.

§Not a password-hash provider

This is a port of the C reference, not an implementation of the RustCrypto password-hash traits: there is no PasswordHasher or PasswordVerifier here, and no dependency that would supply one. Params carries no serde impls either, though its full state round-trips through the accessors and Params::to_builder. Interoperation is at the string level — the PHC strings this crate reads and writes are the ones the argon2 crate reads and writes. A future optional feature for those traits would be additive and would not change this default surface.

§SemVer

From 1.0.0, the default-feature public API is covered by Semantic Versioning. The internal-api feature and its __internal module are not. MSRV may rise on a minor release. See the README “SemVer policy” section for the full list.

§Reusing memory across hashes

Each Argon2 hash acquires its block arena once and releases it on the way out. A process that hashes repeatedly can instead keep a Hasher, from Argon2::hasher, which parks the arena between calls and wipes it on release, so the next call gets a zeroed arena that is already mapped and already resident.

Skipping the mmap, the first-touch faults and the munmap is worth around -25% at m_cost = 64 MiB, and next to nothing below 1 MiB, where a hash is mostly BLAKE2b; Hasher has the per-cost measurements.

use argon2_rust::{Algorithm, Argon2, Hasher, Params, Version, params::Memory};

// Nameable, so it can be a field, a `thread_local!` or a worker slot —
// which is the only shape in which reuse is worth anything.
struct Worker {
    hasher: Hasher,
}

let params = Params::builder().memory(Memory::kib(1 << 8)).passes(1).build()?;
let mut worker = Worker {
    hasher: Argon2::new(Algorithm::Argon2id, Version::V0x13, params).hasher(),
};
let mut tag = [0u8; 32];
worker.hasher.hash_into(b"password", b"somesalt", &mut tag)?;

§Salts

Argon2::hash_password_with_random_salt (and the pooled Hasher::hash_password_with_random_salt) draw a RANDOM_SALT_LEN-byte salt from the OS and put it in the returned PHC string, so nothing has to be stored alongside. The entropy comes from whichever entry point is correct for the target — getrandom(2), getentropy, CCRandomGenerateBytes, ProcessPrng, WASI random_get, or /dev/urandom — each declared by hand, so this costs no dependency. Callers who already run a CSPRNG should keep passing their own salt.

§Verifying strings you did not write

m_cost in a PHC string is up to ten digits of decimal, and Argon2::verify_encoded will honour all of them — up to params::MAX_MEMORY KiB, which is 4 TiB — because argon2_verify does too. That is fine for a config file and a denial of service for a login endpoint. Argon2::verify_encoded_bounded takes a ceiling and rejects an over-large cost while it is still a number, before anything is allocated:

use argon2_rust::{Algorithm, Argon2, Error, Params, params::Memory};

let hostile = "$argon2id$v=19$m=4294967295,t=1,p=1$c29tZXNhbHQ$\
               CTFhFdXPJO1aFaMaO6Mm5c8y7cJHAph8ArZWb2GRPPc";
let ceiling = Params::builder().memory(Memory::mib(64)).passes(8).lanes(4).build()?;
assert_eq!(
    Argon2::verify_encoded_bounded(hostile, b"pw", Algorithm::Argon2id, &ceiling),
    Err(Error::MemoryTooMuch),
);

Memory is not the only resource the string spends. Decoding sets threads = lanes (C parity), so p also picks how many OS threads the verify spawns; the ceiling’s own threads bounds that, and a ceiling that leaves ParamsBuilder::threads unset bounds it together with lanes. Set that one setter to accept wide strings without spawning wide. The clamp cannot change a verdict — only lanes feeds the tag.

§Panics

No fallible path in this crate panics: hashing, verifying, encoding, decoding and parameter validation all report failure as an Error, whose numeric Error::as_c_code matches the C reference — except for the crate-specific codes below Error::MIN_C_CODE, which the C has no equivalent for.

There is exactly one intentional exception, and it is not on a fallible path: ParamsBuilder::build_or_panic panics on invalid parameters. It exists so a const item can turn bad parameters into a compile error, which is what a panic in a const evaluation is. Anywhere a runtime error is the right answer, use ParamsBuilder::build — it is the normal way in.

Re-exports§

pub use crate::error::Error;
pub use crate::params::Algorithm;
pub use crate::params::Params;
pub use crate::params::Version;

Modules§

error
Error codes, mirroring argon2_error_codes from phc-winner-argon2/include/argon2.h.
params
Limits, Algorithm, Version and Params.

Structs§

Argon2
A configured Argon2 hasher.
Decoded
The fields a PHC string yields.
Hasher
An Argon2 that keeps its block arena between calls.

Enums§

Backend
Which fill_segment implementation to use.
Base64Backend
A Base64 SIMD implementation compiled into this crate.
Blake2bBackend
The compression implementations available to BLAKE2b on this target.

Constants§

BOUNDED_MAX_SALT_LEN
Longest salt the *_bounded verify entry points will accept, in bytes.
RANDOM_SALT_LEN
Salt length used by the *_with_random_salt entry points, in bytes.

Functions§

blake2b
blake2b(out, outlen, in, inlen, NULL, 0): the one-shot unkeyed digest.
blake2b_long
blake2b_long(out, outlen, in, inlen): Argon2’s variable-length extension.
constant_time_eq
argon2_compare() from src/argon2.c: a constant-time byte comparison.
decode_base64
Decode unpadded standard Base64, the alphabet PHC strings use.
decode_phc
Decode a PHC string, detecting the algorithm from the $argon2* prefix.
decode_string
decode_string(ctx, str, type).
detected_backend
The Backend this CPU resolved to, cached after the first call.
detected_base64_backend
The Base64 Base64Backend this CPU resolved to, cached after the first call.
detected_blake2b_backend
The BLAKE2b Blake2bBackend this CPU resolved to, cached after the first call.
encode_base64
Encode src as unpadded standard Base64, the alphabet PHC strings use.
encode_string
encode_string(dst, dst_len, ctx, type).
encode_string_alloc
encode_string into a freshly allocated String.
encoded_len
argon2_encodedlen(...) from src/argon2.c.
from_base64
from_base64(dst, dst_len, src).
to_base64
to_base64(dst, dst_len, src, src_len).