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::new_with_threads`]. 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};
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::new(1 << 8, 1, 1, 32)?;
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};
146//!
147//! let hostile = "$argon2id$v=19$m=4294967295,t=1,p=1$c29tZXNhbHQ$\
148//!                CTFhFdXPJO1aFaMaO6Mm5c8y7cJHAph8ArZWb2GRPPc";
149//! let ceiling = Params::new(1 << 16, 8, 4, 32)?;
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 from
160//! [`Params::new`] bounds it together with `lanes`. Use
161//! [`Params::new_with_threads`] to accept wide strings without spawning wide.
162//! The clamp cannot change a verdict — only `lanes` feeds the tag.
163//!
164//! # Panics
165//!
166//! Nothing reachable through the public API panics. Every failure is an
167//! [`Error`], whose numeric [`Error::as_c_code`] matches the C reference —
168//! except for the crate-specific codes below [`Error::MIN_C_CODE`], which the C
169//! has no equivalent for.
170
171#![no_std]
172#![warn(missing_docs)]
173// A `pub` item that a downstream crate cannot *name* is only half-public: it
174// works in `let` bindings and nowhere else — not in a struct field, a function
175// signature, a `Vec`, or a `thread_local!`. `Hasher` shipped that way once
176// (`Argon2::hasher()` returned it, `lib.rs` never re-exported it), which broke
177// the one shape the reuse layer exists to serve: one hasher per worker, owned
178// by that worker's struct. This lint is the regression guard.
179#![warn(unnameable_types)]
180#![warn(clippy::undocumented_unsafe_blocks)]
181
182// NOTE FOR EVERY CONTRIBUTOR: this crate has a module named `core`, which
183// shadows the `core` crate *in this root module only*. Inside `src/lib.rs`
184// always write `::core::...`. Submodules are unaffected — bare `core::` there
185// still means the `core` crate.
186
187extern crate alloc;
188
189#[cfg(feature = "std")]
190extern crate std;
191
192pub mod error;
193pub mod params;
194
195// These modules are private, and a good deal of what they expose escapes the
196// crate only through `__internal` (below), which tests and benches enable. In a
197// plain build those items are legitimately unreachable, so `dead_code` would
198// fire on all of them.
199//
200// Rather than blanket-allowing `dead_code` — which would also hide code that is
201// dead by mistake — the allow is tied to `internal-api` being OFF. With the
202// feature ON, `__internal` re-exports the intended surface, so anything the
203// compiler still calls dead really is dead and gets reported.
204macro_rules! private_modules {
205    ($($name:ident),* $(,)?) => {
206        $(
207            #[cfg_attr(not(feature = "internal-api"), allow(dead_code))]
208            mod $name;
209        )*
210    };
211}
212
213private_modules!(base64, blake2b, block, core, encoding, fill_block, memory);
214
215// OS entropy for the convenience salt API; needs std for the syscall and the
216// /dev/urandom fallback. Declared per-platform inside the module.
217//
218// Deliberately not in `private_modules!`: everything here is reachable from
219// `Argon2::hash_password_with_random_salt` on every `std` build, so it needs no
220// `dead_code` allow, and should not have one hiding a future mistake.
221#[cfg(feature = "std")]
222mod random;
223
224pub use crate::core::{Argon2, BOUNDED_MAX_SALT_LEN, Hasher};
225// `RANDOM_SALT_LEN` is std-only because the API it describes is;
226// `BOUNDED_MAX_SALT_LEN` is not, because `verify_encoded_bounded` works without
227// `std` and a caller has to be able to name the bound it is being held to.
228#[cfg(feature = "std")]
229pub use crate::core::RANDOM_SALT_LEN;
230pub use crate::encoding::encoded_len;
231pub use crate::error::Error;
232pub use crate::fill_block::Backend;
233pub use crate::params::{Algorithm, Params, Version};
234
235/// The [`Backend`] this CPU resolved to, cached after the first call.
236///
237/// Diagnostic only — the hashing entry points call this for you.
238///
239/// ```
240/// println!("argon2 backend: {}", argon2_rust::detected_backend());
241/// ```
242#[inline]
243#[must_use]
244pub fn detected_backend() -> Backend {
245    crate::fill_block::backend()
246}
247
248/// Unstable internals, exposed for this crate's own tests and benches.
249///
250/// Gated behind the non-default `internal-api` feature. **No stability
251/// guarantees**: anything here can change in a patch release.
252///
253/// # Soundness
254///
255/// Unstable is not the same as unsound. Every entry point here that takes an
256/// explicit [`Backend`] or `Blake2bBackend` — including
257/// `fill_memory_blocks_traced`, `hash_traced`, `hash_with_backend`,
258/// `blake2b_with_backend`, and `blake2b_long_with_backend` — is an `unsafe fn`,
259/// and so is each backend's low-level entry point. They dispatch to a
260/// `#[target_feature(enable = ...)]` function, so running one whose feature
261/// this CPU lacks is undefined behaviour (`SIGILL` in practice), and only the
262/// caller can rule that out. Each backend type's `is_available` method is the
263/// portable way.
264///
265/// The safe entry points — [`Argon2`], [`detected_backend`], `blake2b`,
266/// `blake2b_long`, and `fill_memory_blocks` — never let a caller name the
267/// backend. They take it from the corresponding cached runtime cascade, which
268/// by construction only ever names a backend this CPU advertises. That is the
269/// whole reason they can be safe, and it is why turning on `internal-api`
270/// cannot make a `#![forbid(unsafe_code)]` program reachable by UB.
271#[cfg(feature = "internal-api")]
272#[doc(hidden)]
273pub mod __internal {
274    pub use crate::base64::{Base64Backend, base64_backend, detect_base64_backend};
275    pub use crate::blake2b::{
276        BLOCKBYTES, Blake2b, Blake2bBackend, IV, KEYBYTES, OUTBYTES, PERSONALBYTES, SALTBYTES,
277        blake2b, blake2b_backend, blake2b_long, blake2b_long_with_backend, blake2b_with_backend,
278        detect_blake2b_backend,
279    };
280    pub use crate::block::{Block, Instance, Position};
281    pub use crate::core::{
282        PassTrace, constant_time_eq, fill_first_blocks, fill_memory_blocks,
283        fill_memory_blocks_traced, finalize, hash_traced, hash_with_backend, index_alpha,
284        initial_hash,
285    };
286    pub use crate::encoding::{
287        Decoded, b64_len, decode_string, encode_string, encode_string_alloc, encoded_len,
288        from_base64, from_base64_with_backend, num_len, to_base64, to_base64_with_backend,
289    };
290    pub use crate::fill_block::{Backend, FillSegmentFn, backend, detect, fill_segment_fn};
291    /// The arena release-path observation point, for
292    /// `tests/allocation_audit.rs`. See [`crate::memory::audit`].
293    #[cfg(feature = "std")]
294    pub use crate::memory::audit;
295    pub use crate::memory::{
296        ARENA_ALIGN, Arena, ArenaGuard, Workspace, clear_internal_memory,
297        clear_internal_memory_blocks, clear_internal_memory_u64, secure_wipe, secure_wipe_blocks,
298        secure_wipe_raw, secure_wipe_u64,
299    };
300    pub use crate::params::validate_inputs;
301
302    /// `bumpalo`, re-exported so callers can name the types
303    /// [`Workspace::bump`](crate::memory::Workspace::bump) hands back without
304    /// having to match this crate's exact dependency version.
305    ///
306    /// Only the `try_alloc_*` family is admissible: the infallible `alloc_*`
307    /// methods abort on allocation failure, and nothing reachable from this
308    /// crate's safe API is allowed to do that.
309    #[cfg(feature = "bump-alloc")]
310    pub use ::bumpalo;
311
312    /// Each backend's `fill_segment`, reachable directly so a differential test
313    /// can pit two backends against each other on the same arena.
314    pub mod backends {
315        pub use crate::fill_block::scalar;
316
317        #[cfg(target_arch = "aarch64")]
318        pub use crate::fill_block::neon;
319
320        #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
321        pub use crate::fill_block::sse2;
322
323        #[cfg(target_arch = "x86_64")]
324        pub use crate::fill_block::avx2;
325
326        #[cfg(target_arch = "x86_64")]
327        pub use crate::fill_block::avx512;
328    }
329}