Skip to main content

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