argon2_rust/lib.rs
1//! A pure-Rust port of the reference Argon2 implementation
2//! ([phc-winner-argon2](https://github.com/P-H-C/phc-winner-argon2)), with
3//! runtime-dispatched SIMD backends.
4//!
5//! Argon2 is the winner of the 2015 Password Hashing Competition and is
6//! specified in [RFC 9106](https://www.rfc-editor.org/rfc/rfc9106). Three
7//! variants exist, see [`Algorithm`]:
8//!
9//! * `Argon2d` — data-dependent addressing: fastest, but leaks a memory access
10//! pattern that depends on the password.
11//! * `Argon2i` — data-independent addressing: side-channel resistant.
12//! * `Argon2id` — independent for the first half-pass, dependent afterwards.
13//! The default, and what RFC 9106 recommends.
14//!
15//! # Example
16//!
17//! ```
18//! use argon2_rust::{Algorithm, Argon2, Error, Params, Version};
19//!
20//! // Default: 19 MiB, t=2, 1 lane, 32-byte tag (raw hash output). ~8 ms per
21//! // hash in release on M-series — cheap enough for a real doctest. Raise
22//! // `m_cost` until it fits your budget.
23//! let params = Params::default();
24//! let argon2 = Argon2::new(Algorithm::Argon2id, Version::V0x13, params);
25//!
26//! let mut tag = [0u8; 32];
27//! argon2.hash_into(b"password", b"somesalt", &mut tag)?;
28//! assert_eq!(argon2.verify(b"password", b"somesalt", &tag), Ok(()));
29//! assert_eq!(
30//! argon2.verify(b"wrong", b"somesalt", &tag),
31//! Err(Error::VerifyMismatch),
32//! );
33//! # Ok::<(), argon2_rust::Error>(())
34//! ```
35//!
36//! # SIMD backends
37//!
38//! The compression function is selected by **runtime** CPU feature detection,
39//! never by `cfg(target_feature)` alone, so one binary runs at full speed on
40//! every machine. Detection happens at most once per process and the result is
41//! cached; a hash call resolves one function pointer before entering its loops.
42//! Ask [`detected_backend`] what this CPU picked. The cost model is documented
43//! on the private `fill_block` module.
44//!
45//! | [`Backend`] | Requires | Source it was ported from |
46//! |---|---|---|
47//! | `Scalar` | — | `src/ref.c` |
48//! | `Neon` | `aarch64` | `src/opt.c` (128-bit path) |
49//! | `Sse2` | `x86`/`x86_64` + SSE2 | `src/opt.c` (128-bit path) |
50//! | `Avx2` | `x86_64` + AVX2 | `src/opt.c` (`__AVX2__`) |
51//! | `Avx512` | `x86_64` + AVX-512F | `src/opt.c` (`__AVX512F__`) |
52//!
53//! The unpadded standard-Base64 codec used by PHC strings has a separate,
54//! cached dispatch: AVX2, SSSE3, AArch64 NEON, wasm SIMD128, then scalar. Its
55//! vector kernels follow `base64-simd`/`aklomp/base64`, while inputs shorter
56//! than one vector stay on the original scalar loop. As in `base64-simd`,
57//! AVX-512-capable CPUs use this codec's AVX2 path; the wider backend remains
58//! specific to the Argon2 compression function above. Call [`encode_base64`]
59//! and [`decode_base64`] for that codec on its own; [`detected_base64_backend`]
60//! reports which implementation this CPU picked. The PHC string itself is
61//! [`encode_string`] / [`decode_string`] (C `encode_string` / `decode_string`)
62//! or [`decode_phc`] when the input may be a `@phc/format` / node-argon2
63//! string (`m,p,t` any order, optional `data=`).
64//!
65//! BLAKE2b (H0, the long expansion, and finalize) has its own cascade, x86
66//! only: AVX-512 → AVX2 → SSE4.1 → scalar. AArch64 stays on scalar; a NEON
67//! compress was measured slower. [`blake2b`] / [`blake2b_long`] are the
68//! one-shot entry points; [`detected_blake2b_backend`] reports the choice.
69//!
70//! # Features
71//!
72//! * `std` *(default)* — runtime CPU feature detection, including
73//! `memchr`'s SIMD cascade for PHC field scans.
74//! * `parallel` *(default, implies `std`)* — multi-threaded fill. One
75//! [`std::thread::scope`] for the **whole** fill, whose workers meet at a
76//! barrier at each of the `4 * t_cost` algorithmic sync points, rather than a
77//! fresh scope per sync point. Note that the thread count does **not** change
78//! the tag; only [`Params::lanes`] does.
79//! * `zeroize-memory` *(default)* — securely wipe internal buffers, the
80//! equivalent of `FLAG_clear_internal_memory` in the C.
81//! * `bump-alloc` — internal test/bench control. Together with `internal-api`,
82//! gives `memory::Workspace` a reusable bump allocator for measuring small
83//! scratch buffers. It does **not** change the stable hash/encode/verify paths,
84//! which deliberately keep their `Vec`s. Measured upper bound: 17 ns per hash,
85//! or 0.00013% of an RFC 9106 hash.
86//! * `internal-api` — exposes `__internal` for tests and benches. Not stable.
87//!
88//! The crate is `#![no_std]` and needs `alloc` plus [`memchr`]. That stays
89//! true with every feature turned on. Without `std`, backend selection (and
90//! `memchr`) fall back to compile-time `target_feature` cfgs.
91//!
92//! # Not a `password-hash` provider
93//!
94//! This is a port of the C reference, not an implementation of the RustCrypto
95//! `password-hash` traits: there is no `PasswordHasher` or `PasswordVerifier`
96//! here, and no dependency that would supply one. [`Params`] carries no `serde`
97//! impls either, though its full state round-trips through the accessors and
98//! [`Params::to_builder`]. Interoperation is at the string level — the PHC
99//! strings this crate reads and writes are the ones the `argon2` crate reads and
100//! writes. A future optional feature for those traits would be additive and
101//! would not change this default surface.
102//!
103//! # SemVer
104//!
105//! From `1.0.0`, the default-feature public API is covered by Semantic
106//! Versioning. The `internal-api` feature and its `__internal` module are not.
107//! MSRV may rise on a minor release. See the README "SemVer policy" section for
108//! the full list.
109//!
110//! # Reusing memory across hashes
111//!
112//! Each [`Argon2`] hash acquires its block arena once and releases it on the
113//! way out. A process that hashes repeatedly can instead keep a [`Hasher`],
114//! from [`Argon2::hasher`], which parks the arena between calls and wipes it on
115//! release, so the next call gets a zeroed arena that is **already mapped and
116//! already resident**.
117//!
118//! Skipping the `mmap`, the first-touch faults and the `munmap` is worth around
119//! -25% at `m_cost = 64 MiB`, and next to nothing below 1 MiB, where a hash is
120//! mostly BLAKE2b; [`Hasher`](Hasher#what-it-is-worth-measured) has the
121//! per-cost measurements.
122//!
123//! ```
124//! use argon2_rust::{Algorithm, Argon2, Hasher, Params, Version, params::Memory};
125//!
126//! // Nameable, so it can be a field, a `thread_local!` or a worker slot —
127//! // which is the only shape in which reuse is worth anything.
128//! struct Worker {
129//! hasher: Hasher,
130//! }
131//!
132//! let params = Params::builder().memory(Memory::kib(1 << 8)).passes(1).build()?;
133//! let mut worker = Worker {
134//! hasher: Argon2::new(Algorithm::Argon2id, Version::V0x13, params).hasher(),
135//! };
136//! let mut tag = [0u8; 32];
137//! worker.hasher.hash_into(b"password", b"somesalt", &mut tag)?;
138//! # Ok::<(), argon2_rust::Error>(())
139//! ```
140//!
141//! # Salts
142//!
143//! [`Argon2::hash_password_with_random_salt`] (and the pooled
144//! [`Hasher::hash_password_with_random_salt`]) draw a [`RANDOM_SALT_LEN`]-byte
145//! salt from the OS and put it in the returned PHC string, so nothing has to be
146//! stored alongside. The entropy comes from whichever entry point is correct
147//! for the target — `getrandom(2)`, `getentropy`, `CCRandomGenerateBytes`,
148//! `ProcessPrng`, WASI `random_get`, or `/dev/urandom` — each declared by hand,
149//! so this costs no dependency. Callers who already run a CSPRNG should keep
150//! passing their own salt.
151//!
152//! # Verifying strings you did not write
153//!
154//! `m_cost` in a PHC string is up to ten digits of decimal, and
155//! [`Argon2::verify_encoded`] will honour all of them — up to
156//! [`params::MAX_MEMORY`] KiB, which is 4 TiB — because `argon2_verify` does
157//! too. That is fine for a config file and a denial of service for a login
158//! endpoint. [`Argon2::verify_encoded_bounded`] takes a ceiling and rejects an
159//! over-large cost while it is still a number, before anything is allocated:
160//!
161//! ```
162//! use argon2_rust::{Algorithm, Argon2, Error, Params, params::Memory};
163//!
164//! let hostile = "$argon2id$v=19$m=4294967295,t=1,p=1$c29tZXNhbHQ$\
165//! CTFhFdXPJO1aFaMaO6Mm5c8y7cJHAph8ArZWb2GRPPc";
166//! let ceiling = Params::builder().memory(Memory::mib(64)).passes(8).lanes(4).build()?;
167//! assert_eq!(
168//! Argon2::verify_encoded_bounded(hostile, b"pw", Algorithm::Argon2id, &ceiling),
169//! Err(Error::MemoryTooMuch),
170//! );
171//! # Ok::<(), argon2_rust::Error>(())
172//! ```
173//!
174//! Memory is not the only resource the string spends. Decoding sets
175//! `threads = lanes` (C parity), so `p` also picks how many OS threads the
176//! verify spawns; the ceiling's own `threads` bounds that, and a ceiling that
177//! leaves [`ParamsBuilder::threads`](params::ParamsBuilder::threads) unset bounds
178//! it together with `lanes`. Set that one setter to accept wide strings without
179//! spawning wide. The clamp cannot change a verdict — only `lanes` feeds the tag.
180//!
181//! # Panics
182//!
183//! No fallible path in this crate panics: hashing, verifying, encoding,
184//! decoding and parameter validation all report failure as an [`Error`], whose
185//! numeric [`Error::as_c_code`] matches the C reference — except for the
186//! crate-specific codes below [`Error::MIN_C_CODE`], which the C has no
187//! equivalent for.
188//!
189//! There is exactly one intentional exception, and it is not on a fallible
190//! path: [`ParamsBuilder::build_or_panic`](params::ParamsBuilder::build_or_panic)
191//! panics on invalid parameters. It exists so a `const` item can turn bad
192//! parameters into a *compile* error, which is what a panic in a `const`
193//! evaluation is. Anywhere a runtime error is the right answer, use
194//! [`ParamsBuilder::build`](params::ParamsBuilder::build) — it is the normal way
195//! in.
196
197#![no_std]
198#![warn(missing_docs)]
199// A `pub` item that a downstream crate cannot *name* is only half-public: it
200// works in `let` bindings and nowhere else — not in a struct field, a function
201// signature, a `Vec`, or a `thread_local!`. `Hasher` shipped that way once
202// (`Argon2::hasher()` returned it, `lib.rs` never re-exported it), which broke
203// the one shape the reuse layer exists to serve: one hasher per worker, owned
204// by that worker's struct. This lint is the regression guard.
205#![warn(unnameable_types)]
206#![warn(clippy::undocumented_unsafe_blocks)]
207
208// NOTE FOR EVERY CONTRIBUTOR: this crate has a module named `core`, which
209// shadows the `core` crate *in this root module only*. Inside `src/lib.rs`
210// always write `::core::...`. Submodules are unaffected — bare `core::` there
211// still means the `core` crate.
212
213extern crate alloc;
214
215#[cfg(feature = "std")]
216extern crate std;
217
218pub mod error;
219pub mod params;
220
221// These modules are private, and a good deal of what they expose escapes the
222// crate only through `__internal` (below), which tests and benches enable. In a
223// plain build those items are legitimately unreachable, so `dead_code` would
224// fire on all of them.
225//
226// Rather than blanket-allowing `dead_code` — which would also hide code that is
227// dead by mistake — the allow is tied to `internal-api` being OFF. With the
228// feature ON, `__internal` re-exports the intended surface, so anything the
229// compiler still calls dead really is dead and gets reported.
230macro_rules! private_modules {
231 ($($name:ident),* $(,)?) => {
232 $(
233 #[cfg_attr(not(feature = "internal-api"), allow(dead_code))]
234 mod $name;
235 )*
236 };
237}
238
239private_modules!(base64, blake2b, block, core, encoding, fill_block, memory);
240
241// OS entropy for the convenience salt API; needs std for the syscall and the
242// /dev/urandom fallback. Declared per-platform inside the module.
243//
244// Deliberately not in `private_modules!`: everything here is reachable from
245// `Argon2::hash_password_with_random_salt` on every `std` build, so it needs no
246// `dead_code` allow, and should not have one hiding a future mistake.
247#[cfg(feature = "std")]
248mod random;
249
250pub use crate::core::{Argon2, BOUNDED_MAX_SALT_LEN, Hasher, constant_time_eq};
251// `RANDOM_SALT_LEN` is std-only because the API it describes is;
252// `BOUNDED_MAX_SALT_LEN` is not, because `verify_encoded_bounded` works without
253// `std` and a caller has to be able to name the bound it is being held to.
254pub use crate::base64::Base64Backend;
255pub use crate::blake2b::{Blake2bBackend, blake2b, blake2b_long};
256#[cfg(feature = "std")]
257pub use crate::core::RANDOM_SALT_LEN;
258pub use crate::encoding::{
259 Decoded, decode_base64, decode_phc, decode_string, encode_base64, encode_string,
260 encode_string_alloc, encoded_len, from_base64, to_base64,
261};
262pub use crate::error::Error;
263pub use crate::fill_block::Backend;
264pub use crate::params::{Algorithm, Params, Version};
265
266/// The [`Backend`] this CPU resolved to, cached after the first call.
267///
268/// Diagnostic only — the hashing entry points call this for you.
269///
270/// ```
271/// println!("argon2 backend: {}", argon2_rust::detected_backend());
272/// ```
273#[inline]
274#[must_use]
275pub fn detected_backend() -> Backend {
276 crate::fill_block::backend()
277}
278
279/// The Base64 [`Base64Backend`] this CPU resolved to, cached after the first call.
280///
281/// Diagnostic only — [`encode_base64`] and [`decode_base64`] call this for you.
282///
283/// ```
284/// println!("argon2 base64 backend: {}", argon2_rust::detected_base64_backend());
285/// ```
286#[inline]
287#[must_use]
288pub fn detected_base64_backend() -> Base64Backend {
289 crate::base64::base64_backend()
290}
291
292/// The BLAKE2b [`Blake2bBackend`] this CPU resolved to, cached after the first
293/// call.
294///
295/// Diagnostic only — [`blake2b`], [`blake2b_long`], and every Argon2 hash call
296/// this for you. On aarch64 this is always [`Blake2bBackend::Scalar`]: a NEON
297/// compress was measured slower than the portable path, so there is no ARM
298/// SIMD backend to pick.
299///
300/// ```
301/// println!("argon2 blake2b backend: {}", argon2_rust::detected_blake2b_backend());
302/// ```
303#[inline]
304#[must_use]
305pub fn detected_blake2b_backend() -> Blake2bBackend {
306 crate::blake2b::blake2b_backend()
307}
308
309/// Unstable internals, exposed for this crate's own tests and benches.
310///
311/// Gated behind the non-default `internal-api` feature. **No stability
312/// guarantees**: anything here can change in a patch release.
313///
314/// # Soundness
315///
316/// Unstable is not the same as unsound. Every entry point here that takes an
317/// explicit [`Backend`] or `Blake2bBackend` — including
318/// `fill_memory_blocks_traced`, `hash_traced`, `hash_with_backend`,
319/// `blake2b_with_backend`, and `blake2b_long_with_backend` — is an `unsafe fn`,
320/// and so is each backend's low-level entry point. They dispatch to a
321/// `#[target_feature(enable = ...)]` function, so running one whose feature
322/// this CPU lacks is undefined behaviour (`SIGILL` in practice), and only the
323/// caller can rule that out. Each backend type's `is_available` method is the
324/// portable way.
325///
326/// The safe entry points — [`Argon2`], [`detected_backend`], `blake2b`,
327/// `blake2b_long`, and `fill_memory_blocks` — never let a caller name the
328/// backend. They take it from the corresponding cached runtime cascade, which
329/// by construction only ever names a backend this CPU advertises. That is the
330/// whole reason they can be safe, and it is why turning on `internal-api`
331/// cannot make a `#![forbid(unsafe_code)]` program reachable by UB.
332#[cfg(feature = "internal-api")]
333#[doc(hidden)]
334pub mod __internal {
335 pub use crate::base64::{Base64Backend, base64_backend, detect_base64_backend};
336 pub use crate::blake2b::{
337 BLOCKBYTES, Blake2b, Blake2bBackend, IV, KEYBYTES, OUTBYTES, PERSONALBYTES, SALTBYTES,
338 blake2b, blake2b_backend, blake2b_long, blake2b_long_with_backend, blake2b_with_backend,
339 detect_blake2b_backend,
340 };
341 pub use crate::block::{Block, Instance, Position};
342 pub use crate::core::{
343 PassTrace, constant_time_eq, fill_first_blocks, fill_memory_blocks,
344 fill_memory_blocks_traced, finalize, hash_traced, hash_with_backend, index_alpha,
345 initial_hash,
346 };
347 pub use crate::encoding::{
348 Decoded, b64_len, decode_phc, decode_string, encode_string, encode_string_alloc,
349 encoded_len, from_base64, from_base64_with_backend, num_len, to_base64,
350 to_base64_with_backend,
351 };
352 pub use crate::fill_block::{Backend, FillSegmentFn, backend, detect, fill_segment_fn};
353 /// The arena release-path observation point, for
354 /// `tests/allocation_audit.rs`. See [`crate::memory::audit`].
355 #[cfg(feature = "std")]
356 pub use crate::memory::audit;
357 pub use crate::memory::{
358 ARENA_ALIGN, Arena, ArenaGuard, Workspace, clear_internal_memory,
359 clear_internal_memory_blocks, clear_internal_memory_u64, secure_wipe, secure_wipe_blocks,
360 secure_wipe_raw, secure_wipe_u64,
361 };
362 pub use crate::params::validate_inputs;
363
364 /// `bumpalo`, re-exported so callers can name the types
365 /// [`Workspace::bump`](crate::memory::Workspace::bump) hands back without
366 /// having to match this crate's exact dependency version.
367 ///
368 /// Only the `try_alloc_*` family is admissible: the infallible `alloc_*`
369 /// methods abort on allocation failure, and nothing reachable from this
370 /// crate's safe API is allowed to do that.
371 #[cfg(feature = "bump-alloc")]
372 pub use ::bumpalo;
373
374 /// Each backend's `fill_segment`, reachable directly so a differential test
375 /// can pit two backends against each other on the same arena.
376 pub mod backends {
377 pub use crate::fill_block::scalar;
378
379 #[cfg(target_arch = "aarch64")]
380 pub use crate::fill_block::neon;
381
382 #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
383 pub use crate::fill_block::sse2;
384
385 #[cfg(target_arch = "x86_64")]
386 pub use crate::fill_block::avx2;
387
388 #[cfg(target_arch = "x86_64")]
389 pub use crate::fill_block::avx512;
390 }
391}