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.
59//!
60//! # Features
61//!
62//! * `std` *(default)* — runtime CPU feature detection.
63//! * `parallel` *(default, implies `std`)* — multi-threaded fill. One
64//! [`std::thread::scope`] for the **whole** fill, whose workers meet at a
65//! barrier at each of the `4 * t_cost` algorithmic sync points, rather than a
66//! fresh scope per sync point. Note that the thread count does **not** change
67//! the tag; only [`Params::lanes`] does.
68//! * `zeroize-memory` *(default)* — securely wipe internal buffers, the
69//! equivalent of `FLAG_clear_internal_memory` in the C.
70//! * `bump-alloc` — internal test/bench control. Together with `internal-api`,
71//! gives `memory::Workspace` a reusable bump allocator for measuring small
72//! scratch buffers. It does **not** change the stable hash/encode/verify paths,
73//! which deliberately keep their `Vec`s. Measured upper bound: 17 ns per hash,
74//! or 0.00013% of an RFC 9106 hash.
75//! * `internal-api` — exposes `__internal` for tests and benches. Not stable.
76//!
77//! The crate is `#![no_std]` and needs only `alloc`; that stays true with every
78//! feature turned on. Without `std`, backend selection falls back to
79//! compile-time `target_feature` cfgs.
80//!
81//! # Not a `password-hash` provider
82//!
83//! This is a port of the C reference, not an implementation of the RustCrypto
84//! `password-hash` traits: there is no `PasswordHasher` or `PasswordVerifier`
85//! here, and no dependency that would supply one. [`Params`] carries no `serde`
86//! impls either, though its full state round-trips through the accessors and
87//! [`Params::to_builder`]. Interoperation is at the string level — the PHC
88//! strings this crate reads and writes are the ones the `argon2` crate reads and
89//! writes. A future optional feature for those traits would be additive and
90//! would not change this default surface.
91//!
92//! # SemVer
93//!
94//! From `1.0.0`, the default-feature public API is covered by Semantic
95//! Versioning. The `internal-api` feature and its `__internal` module are not.
96//! MSRV may rise on a minor release. See the README "SemVer policy" section for
97//! the full list.
98//!
99//! # Reusing memory across hashes
100//!
101//! Each [`Argon2`] hash acquires its block arena once and releases it on the
102//! way out. A process that hashes repeatedly can instead keep a [`Hasher`],
103//! from [`Argon2::hasher`], which parks the arena between calls and wipes it on
104//! release, so the next call gets a zeroed arena that is **already mapped and
105//! already resident**.
106//!
107//! Skipping the `mmap`, the first-touch faults and the `munmap` is worth around
108//! -25% at `m_cost = 64 MiB`, and next to nothing below 1 MiB, where a hash is
109//! mostly BLAKE2b; [`Hasher`](Hasher#what-it-is-worth-measured) has the
110//! per-cost measurements.
111//!
112//! ```
113//! use argon2_rust::{Algorithm, Argon2, Hasher, Params, Version, params::Memory};
114//!
115//! // Nameable, so it can be a field, a `thread_local!` or a worker slot —
116//! // which is the only shape in which reuse is worth anything.
117//! struct Worker {
118//! hasher: Hasher,
119//! }
120//!
121//! let params = Params::builder().memory(Memory::kib(1 << 8)).passes(1).build()?;
122//! let mut worker = Worker {
123//! hasher: Argon2::new(Algorithm::Argon2id, Version::V0x13, params).hasher(),
124//! };
125//! let mut tag = [0u8; 32];
126//! worker.hasher.hash_into(b"password", b"somesalt", &mut tag)?;
127//! # Ok::<(), argon2_rust::Error>(())
128//! ```
129//!
130//! # Salts
131//!
132//! [`Argon2::hash_password_with_random_salt`] (and the pooled
133//! [`Hasher::hash_password_with_random_salt`]) draw a [`RANDOM_SALT_LEN`]-byte
134//! salt from the OS and put it in the returned PHC string, so nothing has to be
135//! stored alongside. The entropy comes from whichever entry point is correct
136//! for the target — `getrandom(2)`, `getentropy`, `CCRandomGenerateBytes`,
137//! `ProcessPrng`, WASI `random_get`, or `/dev/urandom` — each declared by hand,
138//! so this costs no dependency. Callers who already run a CSPRNG should keep
139//! passing their own salt.
140//!
141//! # Verifying strings you did not write
142//!
143//! `m_cost` in a PHC string is up to ten digits of decimal, and
144//! [`Argon2::verify_encoded`] will honour all of them — up to
145//! [`params::MAX_MEMORY`] KiB, which is 4 TiB — because `argon2_verify` does
146//! too. That is fine for a config file and a denial of service for a login
147//! endpoint. [`Argon2::verify_encoded_bounded`] takes a ceiling and rejects an
148//! over-large cost while it is still a number, before anything is allocated:
149//!
150//! ```
151//! use argon2_rust::{Algorithm, Argon2, Error, Params, params::Memory};
152//!
153//! let hostile = "$argon2id$v=19$m=4294967295,t=1,p=1$c29tZXNhbHQ$\
154//! CTFhFdXPJO1aFaMaO6Mm5c8y7cJHAph8ArZWb2GRPPc";
155//! let ceiling = Params::builder().memory(Memory::mib(64)).passes(8).lanes(4).build()?;
156//! assert_eq!(
157//! Argon2::verify_encoded_bounded(hostile, b"pw", Algorithm::Argon2id, &ceiling),
158//! Err(Error::MemoryTooMuch),
159//! );
160//! # Ok::<(), argon2_rust::Error>(())
161//! ```
162//!
163//! Memory is not the only resource the string spends. Decoding sets
164//! `threads = lanes` (C parity), so `p` also picks how many OS threads the
165//! verify spawns; the ceiling's own `threads` bounds that, and a ceiling that
166//! leaves [`ParamsBuilder::threads`](params::ParamsBuilder::threads) unset bounds
167//! it together with `lanes`. Set that one setter to accept wide strings without
168//! spawning wide. The clamp cannot change a verdict — only `lanes` feeds the tag.
169//!
170//! # Panics
171//!
172//! No fallible path in this crate panics: hashing, verifying, encoding,
173//! decoding and parameter validation all report failure as an [`Error`], whose
174//! numeric [`Error::as_c_code`] matches the C reference — except for the
175//! crate-specific codes below [`Error::MIN_C_CODE`], which the C has no
176//! equivalent for.
177//!
178//! There is exactly one intentional exception, and it is not on a fallible
179//! path: [`ParamsBuilder::build_or_panic`](params::ParamsBuilder::build_or_panic)
180//! panics on invalid parameters. It exists so a `const` item can turn bad
181//! parameters into a *compile* error, which is what a panic in a `const`
182//! evaluation is. Anywhere a runtime error is the right answer, use
183//! [`ParamsBuilder::build`](params::ParamsBuilder::build) — it is the normal way
184//! in.
185
186#![no_std]
187#![warn(missing_docs)]
188// A `pub` item that a downstream crate cannot *name* is only half-public: it
189// works in `let` bindings and nowhere else — not in a struct field, a function
190// signature, a `Vec`, or a `thread_local!`. `Hasher` shipped that way once
191// (`Argon2::hasher()` returned it, `lib.rs` never re-exported it), which broke
192// the one shape the reuse layer exists to serve: one hasher per worker, owned
193// by that worker's struct. This lint is the regression guard.
194#![warn(unnameable_types)]
195#![warn(clippy::undocumented_unsafe_blocks)]
196
197// NOTE FOR EVERY CONTRIBUTOR: this crate has a module named `core`, which
198// shadows the `core` crate *in this root module only*. Inside `src/lib.rs`
199// always write `::core::...`. Submodules are unaffected — bare `core::` there
200// still means the `core` crate.
201
202extern crate alloc;
203
204#[cfg(feature = "std")]
205extern crate std;
206
207pub mod error;
208pub mod params;
209
210// These modules are private, and a good deal of what they expose escapes the
211// crate only through `__internal` (below), which tests and benches enable. In a
212// plain build those items are legitimately unreachable, so `dead_code` would
213// fire on all of them.
214//
215// Rather than blanket-allowing `dead_code` — which would also hide code that is
216// dead by mistake — the allow is tied to `internal-api` being OFF. With the
217// feature ON, `__internal` re-exports the intended surface, so anything the
218// compiler still calls dead really is dead and gets reported.
219macro_rules! private_modules {
220 ($($name:ident),* $(,)?) => {
221 $(
222 #[cfg_attr(not(feature = "internal-api"), allow(dead_code))]
223 mod $name;
224 )*
225 };
226}
227
228private_modules!(base64, blake2b, block, core, encoding, fill_block, memory);
229
230// OS entropy for the convenience salt API; needs std for the syscall and the
231// /dev/urandom fallback. Declared per-platform inside the module.
232//
233// Deliberately not in `private_modules!`: everything here is reachable from
234// `Argon2::hash_password_with_random_salt` on every `std` build, so it needs no
235// `dead_code` allow, and should not have one hiding a future mistake.
236#[cfg(feature = "std")]
237mod random;
238
239pub use crate::core::{Argon2, BOUNDED_MAX_SALT_LEN, Hasher};
240// `RANDOM_SALT_LEN` is std-only because the API it describes is;
241// `BOUNDED_MAX_SALT_LEN` is not, because `verify_encoded_bounded` works without
242// `std` and a caller has to be able to name the bound it is being held to.
243#[cfg(feature = "std")]
244pub use crate::core::RANDOM_SALT_LEN;
245pub use crate::encoding::encoded_len;
246pub use crate::error::Error;
247pub use crate::fill_block::Backend;
248pub use crate::params::{Algorithm, Params, Version};
249
250/// The [`Backend`] this CPU resolved to, cached after the first call.
251///
252/// Diagnostic only — the hashing entry points call this for you.
253///
254/// ```
255/// println!("argon2 backend: {}", argon2_rust::detected_backend());
256/// ```
257#[inline]
258#[must_use]
259pub fn detected_backend() -> Backend {
260 crate::fill_block::backend()
261}
262
263/// Unstable internals, exposed for this crate's own tests and benches.
264///
265/// Gated behind the non-default `internal-api` feature. **No stability
266/// guarantees**: anything here can change in a patch release.
267///
268/// # Soundness
269///
270/// Unstable is not the same as unsound. Every entry point here that takes an
271/// explicit [`Backend`] or `Blake2bBackend` — including
272/// `fill_memory_blocks_traced`, `hash_traced`, `hash_with_backend`,
273/// `blake2b_with_backend`, and `blake2b_long_with_backend` — is an `unsafe fn`,
274/// and so is each backend's low-level entry point. They dispatch to a
275/// `#[target_feature(enable = ...)]` function, so running one whose feature
276/// this CPU lacks is undefined behaviour (`SIGILL` in practice), and only the
277/// caller can rule that out. Each backend type's `is_available` method is the
278/// portable way.
279///
280/// The safe entry points — [`Argon2`], [`detected_backend`], `blake2b`,
281/// `blake2b_long`, and `fill_memory_blocks` — never let a caller name the
282/// backend. They take it from the corresponding cached runtime cascade, which
283/// by construction only ever names a backend this CPU advertises. That is the
284/// whole reason they can be safe, and it is why turning on `internal-api`
285/// cannot make a `#![forbid(unsafe_code)]` program reachable by UB.
286#[cfg(feature = "internal-api")]
287#[doc(hidden)]
288pub mod __internal {
289 pub use crate::base64::{Base64Backend, base64_backend, detect_base64_backend};
290 pub use crate::blake2b::{
291 BLOCKBYTES, Blake2b, Blake2bBackend, IV, KEYBYTES, OUTBYTES, PERSONALBYTES, SALTBYTES,
292 blake2b, blake2b_backend, blake2b_long, blake2b_long_with_backend, blake2b_with_backend,
293 detect_blake2b_backend,
294 };
295 pub use crate::block::{Block, Instance, Position};
296 pub use crate::core::{
297 PassTrace, constant_time_eq, fill_first_blocks, fill_memory_blocks,
298 fill_memory_blocks_traced, finalize, hash_traced, hash_with_backend, index_alpha,
299 initial_hash,
300 };
301 pub use crate::encoding::{
302 Decoded, b64_len, decode_string, encode_string, encode_string_alloc, encoded_len,
303 from_base64, from_base64_with_backend, num_len, to_base64, to_base64_with_backend,
304 };
305 pub use crate::fill_block::{Backend, FillSegmentFn, backend, detect, fill_segment_fn};
306 /// The arena release-path observation point, for
307 /// `tests/allocation_audit.rs`. See [`crate::memory::audit`].
308 #[cfg(feature = "std")]
309 pub use crate::memory::audit;
310 pub use crate::memory::{
311 ARENA_ALIGN, Arena, ArenaGuard, Workspace, clear_internal_memory,
312 clear_internal_memory_blocks, clear_internal_memory_u64, secure_wipe, secure_wipe_blocks,
313 secure_wipe_raw, secure_wipe_u64,
314 };
315 pub use crate::params::validate_inputs;
316
317 /// `bumpalo`, re-exported so callers can name the types
318 /// [`Workspace::bump`](crate::memory::Workspace::bump) hands back without
319 /// having to match this crate's exact dependency version.
320 ///
321 /// Only the `try_alloc_*` family is admissible: the infallible `alloc_*`
322 /// methods abort on allocation failure, and nothing reachable from this
323 /// crate's safe API is allowed to do that.
324 #[cfg(feature = "bump-alloc")]
325 pub use ::bumpalo;
326
327 /// Each backend's `fill_segment`, reachable directly so a differential test
328 /// can pit two backends against each other on the same arena.
329 pub mod backends {
330 pub use crate::fill_block::scalar;
331
332 #[cfg(target_arch = "aarch64")]
333 pub use crate::fill_block::neon;
334
335 #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
336 pub use crate::fill_block::sse2;
337
338 #[cfg(target_arch = "x86_64")]
339 pub use crate::fill_block::avx2;
340
341 #[cfg(target_arch = "x86_64")]
342 pub use crate::fill_block::avx512;
343 }
344}