Skip to main content

argon2_rust/
params.rs

1//! Limits, [`Algorithm`], [`Version`] and [`Params`].
2//!
3//! Every constant here is transcribed from `phc-winner-argon2/include/argon2.h`
4//! and `phc-winner-argon2/src/core.h`. [`validate_inputs`] reproduces
5//! `validate_inputs()` from `src/core.c` **in the same order**, because the
6//! order decides which error code surfaces first.
7
8use crate::error::Error;
9
10// ---------------------------------------------------------------------------
11// Limits from include/argon2.h
12// ---------------------------------------------------------------------------
13
14/// `ARGON2_MIN_LANES`.
15pub const MIN_LANES: u32 = 1;
16/// `ARGON2_MAX_LANES`.
17pub const MAX_LANES: u32 = 0x00FF_FFFF;
18
19/// `ARGON2_MIN_THREADS`.
20pub const MIN_THREADS: u32 = 1;
21/// `ARGON2_MAX_THREADS`.
22pub const MAX_THREADS: u32 = 0x00FF_FFFF;
23
24/// `ARGON2_SYNC_POINTS`: synchronisation points between lanes per pass.
25pub const SYNC_POINTS: u32 = 4;
26
27/// `ARGON2_MIN_OUTLEN`.
28pub const MIN_OUTLEN: u32 = 4;
29/// `ARGON2_MAX_OUTLEN`.
30pub const MAX_OUTLEN: u32 = 0xFFFF_FFFF;
31
32/// `ARGON2_MIN_MEMORY` = `2 * ARGON2_SYNC_POINTS` (two blocks per slice).
33pub const MIN_MEMORY: u32 = 2 * SYNC_POINTS;
34
35/// `ARGON2_MAX_MEMORY_BITS` = `min(32, sizeof(void*) * CHAR_BIT - 10 - 1)`.
36///
37/// 32 on a 64-bit target, 21 on a 32-bit target.
38pub const MAX_MEMORY_BITS: u32 = {
39    let ptr_bits = (size_of::<*const u8>() * 8) as u32;
40    let bits = ptr_bits - 10 - 1;
41    if bits < 32 { bits } else { 32 }
42};
43
44/// `ARGON2_MAX_MEMORY` = `min(0xFFFFFFFF, 1 << ARGON2_MAX_MEMORY_BITS)`.
45///
46/// `0xFFFF_FFFF` on a 64-bit target, `0x0020_0000` on a 32-bit target.
47/// Verified against the C preprocessor on `aarch64-apple-darwin`.
48pub const MAX_MEMORY: u32 = {
49    let candidate: u64 = 1u64 << MAX_MEMORY_BITS;
50    if candidate < 0xFFFF_FFFF {
51        candidate as u32
52    } else {
53        0xFFFF_FFFF
54    }
55};
56
57/// `ARGON2_MIN_TIME`.
58pub const MIN_TIME: u32 = 1;
59/// `ARGON2_MAX_TIME`.
60pub const MAX_TIME: u32 = 0xFFFF_FFFF;
61
62/// `ARGON2_MIN_PWD_LENGTH`.
63pub const MIN_PWD_LENGTH: u32 = 0;
64/// `ARGON2_MAX_PWD_LENGTH`.
65pub const MAX_PWD_LENGTH: u32 = 0xFFFF_FFFF;
66
67/// `ARGON2_MIN_AD_LENGTH`.
68pub const MIN_AD_LENGTH: u32 = 0;
69/// `ARGON2_MAX_AD_LENGTH`.
70pub const MAX_AD_LENGTH: u32 = 0xFFFF_FFFF;
71
72/// `ARGON2_MIN_SALT_LENGTH`.
73pub const MIN_SALT_LENGTH: u32 = 8;
74/// `ARGON2_MAX_SALT_LENGTH`.
75pub const MAX_SALT_LENGTH: u32 = 0xFFFF_FFFF;
76
77/// `ARGON2_MIN_SECRET`.
78pub const MIN_SECRET: u32 = 0;
79/// `ARGON2_MAX_SECRET`.
80pub const MAX_SECRET: u32 = 0xFFFF_FFFF;
81
82// ---------------------------------------------------------------------------
83// Internal constants from src/core.h
84// ---------------------------------------------------------------------------
85
86/// `ARGON2_BLOCK_SIZE`: memory block size in bytes.
87pub const BLOCK_SIZE: usize = 1024;
88/// `ARGON2_QWORDS_IN_BLOCK`: 64-bit words per block.
89pub const QWORDS_IN_BLOCK: usize = BLOCK_SIZE / 8;
90/// `ARGON2_OWORDS_IN_BLOCK`: 128-bit lanes per block (SSE2).
91pub const OWORDS_IN_BLOCK: usize = BLOCK_SIZE / 16;
92/// `ARGON2_HWORDS_IN_BLOCK`: 256-bit lanes per block (AVX2).
93pub const HWORDS_IN_BLOCK: usize = BLOCK_SIZE / 32;
94/// `ARGON2_512BIT_WORDS_IN_BLOCK`: 512-bit lanes per block (AVX-512).
95pub const BITS512_WORDS_IN_BLOCK: usize = BLOCK_SIZE / 64;
96
97/// `ARGON2_ADDRESSES_IN_BLOCK`: pseudo-random values one address block holds.
98pub const ADDRESSES_IN_BLOCK: usize = 128;
99
100/// `ARGON2_PREHASH_DIGEST_LENGTH`: length of `H0`.
101pub const PREHASH_DIGEST_LENGTH: usize = 64;
102/// `ARGON2_PREHASH_SEED_LENGTH`: `H0` plus the 4-byte block index and 4-byte lane index.
103pub const PREHASH_SEED_LENGTH: usize = 72;
104
105// ---------------------------------------------------------------------------
106// Limits from src/encoding.h
107// ---------------------------------------------------------------------------
108//
109// Mirrored for completeness, and unused — the C defines all three in
110// `encoding.h:22-24` and then never reads them, so `decode_string` here does
111// not either. Keeping them (rather than dropping them) is what makes the
112// header-for-header correspondence with the C checkable; do not add a use for
113// them without checking the C grew one first.
114//
115// `#[cfg(test)]`, and deliberately NOT public. Each one's own documentation
116// says not to bounds-check against it, which is disqualifying for a stable
117// export: the names read like enforced limits, they sit next to the `MIN_`/
118// `MAX_` constants that really are enforced, and `MIN_DECODED_SALT_LEN` even
119// holds the same value as the real bound today. A caller who reaches for one
120// gets a limit the decoder does not apply. They stay here so
121// `decoded_mirrors_are_not_decoder_bounds` can keep pinning the gap.
122
123/// `ARGON2_MAX_DECODED_LANES`.
124///
125/// Mirrored from `encoding.h:22`, and **not a bound this crate enforces**. The
126/// C defines the macro there and then never reads it, in `encoding.c` or
127/// anywhere else in the tree, so `decode_string` here does not read it either.
128/// What actually bounds the `p=` field of a decoded PHC string is
129/// [`MAX_LANES`] (`0x00FF_FFFF`), applied by [`validate_inputs`] inside
130/// `decode_string`.
131///
132/// Do not use this constant to bounds-check decoded input: a well-formed
133/// string can carry a `p` far above 255 and will decode and verify. Measured
134/// against this crate, `p=300` round-trips through `hash_encoded` and
135/// `verify_encoded`:
136///
137/// ```text
138/// $argon2id$v=19$m=2400,t=1,p=300$c29tZXNhbHQ$tPLI8hre65Crk/uP5eIGCZzn3TQ7RzRoXIkGzt5jQoI
139/// ```
140///
141/// (`m=2400` because [`validate_inputs`] requires `m_cost >= 8 * lanes`, not
142/// because 255 played any part.) The `decoded_mirrors_are_not_decoder_bounds`
143/// test pins the gap between this value and the bound that is real.
144#[cfg(test)]
145const MAX_DECODED_LANES: u32 = 255;
146/// `ARGON2_MIN_DECODED_SALT_LEN`.
147///
148/// Mirrored from `encoding.h:23`, and unread for the same reason: the C
149/// defines it and never consults it, so `decode_string` here does not either.
150/// The salt of a decoded string is bounded by [`MIN_SALT_LENGTH`], applied by
151/// [`validate_inputs`].
152///
153/// The two happen to hold the same value (8) today, which is exactly what
154/// makes this constant easy to mistake for the enforced minimum. It is not the
155/// enforced minimum, and nothing ties the two together: they come from
156/// different headers (`encoding.h` and `argon2.h`), and if [`MIN_SALT_LENGTH`]
157/// ever moves the decoder moves with it while this value stays at 8. Check
158/// decoded salts against [`MIN_SALT_LENGTH`].
159#[cfg(test)]
160const MIN_DECODED_SALT_LEN: u32 = 8;
161/// `ARGON2_MIN_DECODED_OUT_LEN`.
162///
163/// Mirrored from `encoding.h:24`, and likewise never read by the C, so
164/// `decode_string` here does not read it either. The tag length of a decoded
165/// string is bounded by [`MIN_OUTLEN`], applied by [`validate_inputs`].
166///
167/// Do not use this constant to bounds-check decoded input: [`MIN_OUTLEN`] is
168/// 4, so a decoded tag can legitimately undershoot 12. Measured against this
169/// crate, an 8-byte tag round-trips through `hash_encoded` and
170/// `verify_encoded`:
171///
172/// ```text
173/// $argon2id$v=19$m=2400,t=1,p=1$c29tZXNhbHQ$kQGQLZpZJIk
174/// ```
175#[cfg(test)]
176const MIN_DECODED_OUT_LEN: u32 = 12;
177
178// ---------------------------------------------------------------------------
179// Algorithm
180// ---------------------------------------------------------------------------
181
182/// The Argon2 primitive type (`argon2_type`).
183///
184/// The numeric values matter: `initial_hash` hashes them, and `fill_segment`
185/// puts `instance->type` into `input_block.v[5]`.
186#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
187#[repr(u32)]
188pub enum Algorithm {
189    /// `Argon2_d` (0): data-dependent addressing.
190    Argon2d = 0,
191    /// `Argon2_i` (1): data-independent addressing.
192    Argon2i = 1,
193    /// `Argon2_id` (2): first half-pass independent, rest dependent. The default.
194    #[default]
195    Argon2id = 2,
196}
197
198impl Algorithm {
199    /// The `argon2_type` numeric value.
200    #[inline]
201    #[must_use]
202    pub const fn as_u32(self) -> u32 {
203        self as u32
204    }
205
206    /// Parse an `argon2_type` numeric value.
207    #[inline]
208    #[must_use]
209    pub const fn from_u32(value: u32) -> Option<Algorithm> {
210        match value {
211            0 => Some(Algorithm::Argon2d),
212            1 => Some(Algorithm::Argon2i),
213            2 => Some(Algorithm::Argon2id),
214            _ => None,
215        }
216    }
217
218    /// `argon2_type2string(type, 0)`: the lowercase name used in PHC strings.
219    ///
220    /// Note `"argon2i"` is a prefix of `"argon2id"`; the C decoder relies on
221    /// the *next* character failing to parse, and the Rust decoder must too.
222    #[inline]
223    #[must_use]
224    pub const fn as_str(self) -> &'static str {
225        match self {
226            Algorithm::Argon2d => "argon2d",
227            Algorithm::Argon2i => "argon2i",
228            Algorithm::Argon2id => "argon2id",
229        }
230    }
231
232    /// `argon2_type2string(type, 1)`: the capitalised name (used by genkat).
233    #[inline]
234    #[must_use]
235    pub const fn as_str_uppercase(self) -> &'static str {
236        match self {
237            Algorithm::Argon2d => "Argon2d",
238            Algorithm::Argon2i => "Argon2i",
239            Algorithm::Argon2id => "Argon2id",
240        }
241    }
242
243    /// All three variants, in `argon2_type` order.
244    pub const ALL: [Algorithm; 3] = [Algorithm::Argon2d, Algorithm::Argon2i, Algorithm::Argon2id];
245}
246
247// ---------------------------------------------------------------------------
248// Version
249// ---------------------------------------------------------------------------
250
251/// The Argon2 version (`argon2_version`).
252#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
253#[repr(u32)]
254pub enum Version {
255    /// `ARGON2_VERSION_10` (0x10). Blocks are always overwritten, never XORed.
256    V0x10 = 0x10,
257    /// `ARGON2_VERSION_13` (0x13) — `ARGON2_VERSION_NUMBER`, the default.
258    #[default]
259    V0x13 = 0x13,
260}
261
262impl Version {
263    /// `ARGON2_VERSION_NUMBER`.
264    pub const DEFAULT: Version = Version::V0x13;
265
266    /// Both variants, ascending.
267    pub const ALL: [Version; 2] = [Version::V0x10, Version::V0x13];
268
269    /// The `argon2_version` numeric value.
270    #[inline]
271    #[must_use]
272    pub const fn as_u32(self) -> u32 {
273        self as u32
274    }
275
276    /// Parse an `argon2_version` numeric value.
277    #[inline]
278    #[must_use]
279    pub const fn from_u32(value: u32) -> Option<Version> {
280        match value {
281            0x10 => Some(Version::V0x10),
282            0x13 => Some(Version::V0x13),
283            _ => None,
284        }
285    }
286}
287
288// ---------------------------------------------------------------------------
289// validate_inputs
290// ---------------------------------------------------------------------------
291
292/// `validate_inputs()` from `src/core.c`, in the exact same order.
293///
294/// The order is load-bearing: when several inputs are invalid, the C reference
295/// returns the error for whichever check runs first, and the differential tests
296/// compare error codes.
297///
298/// Checks the C performs that are omitted here, with the reason:
299///
300/// * `context == NULL` → `ARGON2_INCORRECT_PARAMETER`: no null contexts in Rust.
301/// * `out == NULL` → `ARGON2_OUTPUT_PTR_NULL`: no null slices in Rust.
302/// * the four `*_PTR_MISMATCH` checks: a Rust slice always has a valid pointer.
303/// * `ARGON2_MIN_PWD_LENGTH > pwdlen`, `ARGON2_MIN_AD_LENGTH > adlen`,
304///   `ARGON2_MIN_SECRET > secretlen`: those minima are all 0, so the checks can
305///   never fire (and would be tautological comparisons in Rust).
306/// * the two allocator-callback checks: this crate has no allocator callbacks.
307///
308/// Note the C computes `8 * context->lanes` in `uint32_t`, *before* `lanes` has
309/// been range-checked, so it can wrap. [`u32::wrapping_mul`] reproduces that:
310/// `lanes = 0xFFFF_FFFF` yields `MemoryTooLittle`, not `LanesTooMany`.
311///
312/// # Prefer [`Params::validate_for`]
313///
314/// This free function is the escape hatch, not the main path.
315/// [`Params::validate_for`] calls it with five of the nine arguments filled in
316/// from the receiver: the tag length (`out_len`, from [`Params::output_len`])
317/// and the four cost values (`m_cost`, `t_cost`, `lanes`, `threads`). It leaves
318/// the caller exactly the four buffer lengths, `pwd_len`, `salt_len`,
319/// `secret_len` and `ad_len`. Those five values come from a [`Params`] that a
320/// constructor already ran through this function, so they cannot drift from the
321/// costs the hash will actually run with, and `core` takes that route on every
322/// hash.
323///
324/// Reach for this function directly only when the C's exact check ordering is
325/// what is wanted, which is the one thing the `Params` route cannot give you:
326/// [`Params::new`] and [`Params::new_with_threads`] validate the cost parameters
327/// at construction time, so a caller who supplies both a bad `m_cost` and a
328/// short salt sees the `m_cost` error where the C reports
329/// `ARGON2_SALT_TOO_SHORT` (the divergence note on [`Params`] spells this out).
330/// `decode_string` is the in-crate example: it calls this function directly on
331/// the decoded fields and only builds its `Params` afterwards, so that a
332/// malformed PHC string yields the same error code `validate_inputs()`
333/// (`core.c:388-513`) yields in the C.
334///
335/// ```
336/// use argon2_rust::Error;
337/// use argon2_rust::params::{Params, validate_inputs};
338///
339/// let params = Params::new(19_456, 2, 1, 32)?;
340///
341/// // Four arguments. The tag length and the four costs come from `params`.
342/// assert_eq!(params.validate_for(8, 16, 0, 0), Ok(()));
343///
344/// // The same check spelled out. The five values `params` would have supplied
345/// // have to be repeated by hand and kept in step with it.
346/// assert_eq!(validate_inputs(32, 8, 16, 0, 0, 19_456, 2, 1, 1), Ok(()));
347///
348/// // `out_len` and `pwd_len` transposed, which is the pair `validate_for`
349/// // takes off the call site entirely. Both are `usize` and adjacent, so this
350/// // compiles, and there is no error to notice: the password length 8 is now
351/// // the tag length, 8 clears `MIN_OUTLEN` (4), and the call says `Ok(())`
352/// // while agreeing to a 64-bit tag.
353/// assert_eq!(validate_inputs(8, 32, 16, 0, 0, 19_456, 2, 1, 1), Ok(()));
354///
355/// // The method form cannot be told that. `out_len` is not one of its four
356/// // arguments; it comes from the `Params`, which holds it at 32.
357/// assert_eq!(params.output_len(), 32);
358/// assert_eq!(params.validate_for(32, 16, 0, 0), Ok(()));
359/// # Ok::<(), Error>(())
360/// ```
361// `MAX_TIME` is `u32::MAX`, and so is `MAX_MEMORY` on a 64-bit target, which
362// makes those two upper-bound checks tautologically false there. They are kept
363// verbatim so the check order matches the C exactly, and because `MAX_MEMORY` is
364// `0x20_0000` on a 32-bit target, where the check is real.
365#[allow(clippy::absurd_extreme_comparisons)]
366// Nine parameters, one per `argon2_context` field the C checks. Grouping them
367// would obscure the 1:1 correspondence with `validate_inputs()`.
368#[allow(clippy::too_many_arguments)]
369pub const fn validate_inputs(
370    out_len: usize,
371    pwd_len: usize,
372    salt_len: usize,
373    secret_len: usize,
374    ad_len: usize,
375    m_cost: u32,
376    t_cost: u32,
377    lanes: u32,
378    threads: u32,
379) -> Result<(), Error> {
380    // Validate output length.
381    if out_len < MIN_OUTLEN as usize {
382        return Err(Error::OutputTooShort);
383    }
384    if out_len > MAX_OUTLEN as usize {
385        return Err(Error::OutputTooLong);
386    }
387
388    // Validate password (required param).
389    if pwd_len > MAX_PWD_LENGTH as usize {
390        return Err(Error::PwdTooLong);
391    }
392
393    // Validate salt (required param). Note the C checks the length even when
394    // `salt == NULL`, so an empty salt is `SaltTooShort`, not a ptr mismatch.
395    if salt_len < MIN_SALT_LENGTH as usize {
396        return Err(Error::SaltTooShort);
397    }
398    if salt_len > MAX_SALT_LENGTH as usize {
399        return Err(Error::SaltTooLong);
400    }
401
402    // Validate secret (optional param).
403    if secret_len > MAX_SECRET as usize {
404        return Err(Error::SecretTooLong);
405    }
406
407    // Validate associated data (optional param).
408    if ad_len > MAX_AD_LENGTH as usize {
409        return Err(Error::AdTooLong);
410    }
411
412    // Validate memory cost. Three checks, in this order.
413    if m_cost < MIN_MEMORY {
414        return Err(Error::MemoryTooLittle);
415    }
416    if m_cost > MAX_MEMORY {
417        return Err(Error::MemoryTooMuch);
418    }
419    if m_cost < 8u32.wrapping_mul(lanes) {
420        return Err(Error::MemoryTooLittle);
421    }
422
423    // Validate time cost.
424    if t_cost < MIN_TIME {
425        return Err(Error::TimeTooSmall);
426    }
427    if t_cost > MAX_TIME {
428        return Err(Error::TimeTooLarge);
429    }
430
431    // Validate lanes.
432    if lanes < MIN_LANES {
433        return Err(Error::LanesTooFew);
434    }
435    if lanes > MAX_LANES {
436        return Err(Error::LanesTooMany);
437    }
438
439    // Validate threads.
440    if threads < MIN_THREADS {
441        return Err(Error::ThreadsTooFew);
442    }
443    if threads > MAX_THREADS {
444        return Err(Error::ThreadsTooMany);
445    }
446
447    Ok(())
448}
449
450// ---------------------------------------------------------------------------
451// Params
452// ---------------------------------------------------------------------------
453
454/// Validated Argon2 cost parameters.
455///
456/// Holds exactly the fields of `argon2_context` that are *not* byte buffers:
457/// `m_cost`, `t_cost`, `lanes`, `threads` and `outlen`. Password, salt, secret
458/// and associated data are passed per call.
459///
460/// A `Params` value can only be built through a constructor that runs
461/// [`validate_inputs`], so `lanes >= 1` always holds and the derived values
462/// below never divide by zero.
463///
464/// # Known divergence from the C reference
465///
466/// The constructors validate the cost parameters immediately, whereas the C
467/// checks salt length *before* `m_cost`. If a caller supplies both a bad
468/// `m_cost` and a short salt, this crate reports the `m_cost` error at
469/// `Params` construction time while the C reports `ARGON2_SALT_TOO_SHORT`.
470/// Call [`validate_inputs`] directly to reproduce the C ordering exactly.
471#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
472pub struct Params {
473    m_cost: u32,
474    t_cost: u32,
475    lanes: u32,
476    threads: u32,
477    output_len: u32,
478}
479
480impl Params {
481    /// Default memory cost in KiB (19 MiB), per the OWASP Argon2id guidance.
482    pub const DEFAULT_M_COST: u32 = 19456;
483    /// Default number of passes.
484    pub const DEFAULT_T_COST: u32 = 2;
485    /// Default degree of parallelism.
486    pub const DEFAULT_LANES: u32 = 1;
487    /// Default tag length in bytes.
488    pub const DEFAULT_OUTPUT_LEN: usize = 32;
489
490    /// Validate and build parameters, with `threads == lanes`.
491    ///
492    /// This matches `argon2_hash()`, which sets both `context.lanes` and
493    /// `context.threads` from its single `parallelism` argument.
494    ///
495    /// # Errors
496    ///
497    /// Any of the cost-parameter errors from [`validate_inputs`].
498    pub const fn new(
499        m_cost: u32,
500        t_cost: u32,
501        lanes: u32,
502        output_len: usize,
503    ) -> Result<Params, Error> {
504        Params::new_with_threads(m_cost, t_cost, lanes, lanes, output_len)
505    }
506
507    /// Validate and build parameters with an explicit thread count.
508    ///
509    /// `threads` is a pure performance knob: it does **not** affect the tag.
510    /// Only `lanes` does. The effective count is `min(threads, lanes)`, see
511    /// [`Params::effective_threads`].
512    ///
513    /// ```
514    /// use argon2_rust::{Algorithm, Argon2, Params, Version};
515    ///
516    /// // Four lanes of work, but never more than two OS threads to run them.
517    /// let budgeted = Params::new_with_threads(64, 1, 4, 2, 32)?;
518    /// assert_eq!((budgeted.lanes(), budgeted.threads()), (4, 2));
519    /// assert_eq!(budgeted.effective_threads(), 2);
520    ///
521    /// // Asking for more threads than lanes is legal, and the extra workers
522    /// // simply have no lane to claim.
523    /// let oversubscribed = Params::new_with_threads(64, 1, 2, 8, 32)?;
524    /// assert_eq!(oversubscribed.effective_threads(), 2);
525    ///
526    /// // `Params::new` is exactly this call with `threads == lanes`.
527    /// let full = Params::new(64, 1, 4, 32)?;
528    /// assert_eq!(full, Params::new_with_threads(64, 1, 4, 4, 32)?);
529    ///
530    /// // And the knob really is free of the tag: same `lanes`, same bytes,
531    /// // whichever thread budget produced them.
532    /// let two_workers = Argon2::new(Algorithm::Argon2id, Version::V0x13, budgeted);
533    /// let four_workers = Argon2::new(Algorithm::Argon2id, Version::V0x13, full);
534    /// assert_eq!(
535    ///     two_workers.hash(b"password", b"somesalt")?,
536    ///     four_workers.hash(b"password", b"somesalt")?,
537    /// );
538    /// # Ok::<(), argon2_rust::Error>(())
539    /// ```
540    ///
541    /// # Errors
542    ///
543    /// Any of the cost-parameter errors from [`validate_inputs`].
544    pub const fn new_with_threads(
545        m_cost: u32,
546        t_cost: u32,
547        lanes: u32,
548        threads: u32,
549        output_len: usize,
550    ) -> Result<Params, Error> {
551        // Feed placeholder lengths that always pass their own checks, so the
552        // *relative* order of the checks that do apply is exactly the C's.
553        match validate_inputs(
554            output_len,
555            0,
556            MIN_SALT_LENGTH as usize,
557            0,
558            0,
559            m_cost,
560            t_cost,
561            lanes,
562            threads,
563        ) {
564            Ok(()) => {}
565            Err(e) => return Err(e),
566        }
567        Ok(Params {
568            m_cost,
569            t_cost,
570            lanes,
571            threads,
572            // `output_len <= MAX_OUTLEN == u32::MAX` was just checked.
573            output_len: output_len as u32,
574        })
575    }
576
577    /// Run the full `validate_inputs()` sequence for a concrete call.
578    ///
579    /// `core` calls this on every hash so the salt/password/secret/ad checks
580    /// fire in the C's order.
581    ///
582    /// # Errors
583    ///
584    /// Any error from [`validate_inputs`].
585    pub const fn validate_for(
586        &self,
587        pwd_len: usize,
588        salt_len: usize,
589        secret_len: usize,
590        ad_len: usize,
591    ) -> Result<(), Error> {
592        validate_inputs(
593            self.output_len as usize,
594            pwd_len,
595            salt_len,
596            secret_len,
597            ad_len,
598            self.m_cost,
599            self.t_cost,
600            self.lanes,
601            self.threads,
602        )
603    }
604
605    /// Requested memory in KiB (`context.m_cost`).
606    #[inline]
607    #[must_use]
608    pub const fn m_cost(&self) -> u32 {
609        self.m_cost
610    }
611
612    /// Number of passes (`context.t_cost`, `instance.passes`).
613    #[inline]
614    #[must_use]
615    pub const fn t_cost(&self) -> u32 {
616        self.t_cost
617    }
618
619    /// Degree of parallelism (`context.lanes`). Affects the tag.
620    #[inline]
621    #[must_use]
622    pub const fn lanes(&self) -> u32 {
623        self.lanes
624    }
625
626    /// Requested worker threads (`context.threads`). Does not affect the tag.
627    #[inline]
628    #[must_use]
629    pub const fn threads(&self) -> u32 {
630        self.threads
631    }
632
633    /// Tag length in bytes (`context.outlen`).
634    #[inline]
635    #[must_use]
636    pub const fn output_len(&self) -> usize {
637        self.output_len as usize
638    }
639
640    /// `min(threads, lanes)`, as `argon2_ctx` computes it.
641    #[inline]
642    #[must_use]
643    pub const fn effective_threads(&self) -> u32 {
644        if self.threads > self.lanes {
645            self.lanes
646        } else {
647            self.threads
648        }
649    }
650
651    /// Step 2 of `argon2_ctx()`: align the memory size.
652    ///
653    /// ```text
654    /// memory_blocks = m_cost;
655    /// if (memory_blocks < 2 * SYNC_POINTS * lanes)
656    ///     memory_blocks = 2 * SYNC_POINTS * lanes;
657    /// segment_length = memory_blocks / (lanes * SYNC_POINTS);
658    /// memory_blocks  = segment_length * (lanes * SYNC_POINTS);
659    /// lane_length    = segment_length * SYNC_POINTS;
660    /// ```
661    ///
662    /// Returns `(memory_blocks, segment_length, lane_length)`. No overflow is
663    /// possible: `lanes <= MAX_LANES` (`0xFF_FFFF`), so `lanes * SYNC_POINTS`
664    /// fits comfortably in `u32`, and `segment_length * lanes * SYNC_POINTS`
665    /// is bounded by the original `memory_blocks <= MAX_MEMORY`.
666    #[inline]
667    #[must_use]
668    pub const fn memory_layout(&self) -> (u32, u32, u32) {
669        let lanes_x_sync = self.lanes * SYNC_POINTS;
670        let min_blocks = 2 * SYNC_POINTS * self.lanes;
671
672        let mut memory_blocks = self.m_cost;
673        if memory_blocks < min_blocks {
674            memory_blocks = min_blocks;
675        }
676
677        let segment_length = memory_blocks / lanes_x_sync;
678        memory_blocks = segment_length * lanes_x_sync;
679        let lane_length = segment_length * SYNC_POINTS;
680
681        (memory_blocks, segment_length, lane_length)
682    }
683
684    /// Number of 1 KiB blocks the arena needs (`instance.memory_blocks`).
685    #[inline]
686    #[must_use]
687    pub const fn memory_blocks(&self) -> u32 {
688        self.memory_layout().0
689    }
690
691    /// Blocks per segment (`instance.segment_length`).
692    #[inline]
693    #[must_use]
694    pub const fn segment_length(&self) -> u32 {
695        self.memory_layout().1
696    }
697
698    /// Blocks per lane (`instance.lane_length` = `segment_length * SYNC_POINTS`).
699    #[inline]
700    #[must_use]
701    pub const fn lane_length(&self) -> u32 {
702        self.memory_layout().2
703    }
704}
705
706impl Default for Params {
707    /// `m_cost = 19456` KiB, `t_cost = 2`, `lanes = 1`, `output_len = 32`.
708    fn default() -> Params {
709        Params {
710            m_cost: Params::DEFAULT_M_COST,
711            t_cost: Params::DEFAULT_T_COST,
712            lanes: Params::DEFAULT_LANES,
713            threads: Params::DEFAULT_LANES,
714            output_len: Params::DEFAULT_OUTPUT_LEN as u32,
715        }
716    }
717}
718
719#[cfg(test)]
720mod tests {
721    use super::*;
722
723    #[test]
724    fn constants_match_the_c_preprocessor() {
725        // Printed by compiling include/argon2.h on aarch64-apple-darwin:
726        //   MAX_MEMORY_BITS = 32, MAX_MEMORY = 4294967295, MIN_MEMORY = 8,
727        //   MAX_OUTLEN = 4294967295, MAX_LANES = 16777215
728        assert_eq!(MIN_MEMORY, 8);
729        assert_eq!(MAX_LANES, 16_777_215);
730        assert_eq!(MAX_OUTLEN, 4_294_967_295);
731        if size_of::<*const u8>() == 8 {
732            assert_eq!(MAX_MEMORY_BITS, 32);
733            assert_eq!(MAX_MEMORY, 4_294_967_295);
734        }
735        assert_eq!(BLOCK_SIZE, 1024);
736        assert_eq!(QWORDS_IN_BLOCK, 128);
737        assert_eq!(OWORDS_IN_BLOCK, 64);
738        assert_eq!(HWORDS_IN_BLOCK, 32);
739        assert_eq!(BITS512_WORDS_IN_BLOCK, 16);
740        assert_eq!(PREHASH_SEED_LENGTH - PREHASH_DIGEST_LENGTH, 8);
741    }
742
743    #[test]
744    fn decoded_mirrors_are_not_decoder_bounds() {
745        // The three `encoding.h` mirrors are read by nobody: not by the C, and
746        // so not by `decode_string` here either. Each one's doc tells a caller
747        // not to bounds-check against it. This pins the gap that makes that
748        // advice true, so the prose cannot go stale.
749        //
750        // In `const` blocks so the checks run at compile time: every operand is
751        // a constant, so a violation is a build error rather than a red test,
752        // and clippy::assertions_on_constants stays quiet.
753
754        // `lanes` is bounded by `MAX_LANES` (16_777_215), roughly 65_000x this
755        // value. Measured: `$argon2id$v=19$m=2400,t=1,p=300$...` encodes and
756        // verifies, with `p` well past 255. A strict `<` is the point — if the
757        // two ever met, "this is not the bound" would be false.
758        const { assert!(MAX_DECODED_LANES < MAX_LANES) }
759
760        // The mirror sits ABOVE the enforced minimum (12 against 4), which is
761        // what lets a decoded tag legitimately undershoot it. Measured: an
762        // 8-byte tag round-trips.
763        const { assert!(MIN_DECODED_OUT_LEN > MIN_OUTLEN) }
764
765        // The salt mirror is the nastiest of the three because it agrees with
766        // the enforced bound today, which is exactly why it reads like the
767        // enforced bound. Pinned as equal on purpose: the day `MIN_SALT_LENGTH`
768        // moves, this fails and sends the next reader to the doc above that
769        // says "they happen to hold the same value (8) today" — prose that
770        // would otherwise quietly become wrong.
771        const { assert!(MIN_DECODED_SALT_LEN == MIN_SALT_LENGTH) }
772    }
773
774    /// Pins the two PHC strings the docs on `MAX_DECODED_LANES` and
775    /// `MIN_DECODED_OUT_LEN` quote as evidence.
776    ///
777    /// Each of those docs tells a caller not to bounds-check decoded input
778    /// against the constant, and each backs the advice with a measured string
779    /// that breaks the constant and round-trips anyway: `p=300` against a
780    /// documented 255, and an 8-byte tag against a documented 12. The strings
781    /// were pasted in from a run of this crate and nothing recomputed them, so
782    /// a change to `encode_string`, to the tag derivation, or to what
783    /// `Params::new` does with `m_cost` would leave the docs quoting output the
784    /// crate no longer produces, with no test failing. Reproduced here from the
785    /// parameters those docs state, byte for byte, and verified back through
786    /// `verify_encoded` so the word "round-trips" is pinned too.
787    #[test]
788    fn decoded_bound_docs_quote_strings_this_crate_still_produces() {
789        use crate::Argon2;
790
791        // `MAX_DECODED_LANES` is 255 and this is `p=300`. `m=2400` is forced by
792        // `validate_inputs`' `m_cost >= 8 * lanes` rule, not by 255.
793        let params = Params::new(2400, 1, 300, 32).unwrap();
794        let argon2 = Argon2::new(Algorithm::Argon2id, Version::V0x13, params);
795        let encoded = argon2.hash_encoded(b"password", b"somesalt").unwrap();
796        assert_eq!(
797            encoded,
798            "$argon2id$v=19$m=2400,t=1,p=300$c29tZXNhbHQ$tPLI8hre65Crk/uP5eIGCZzn3TQ7RzRoXIkGzt5jQoI"
799        );
800        assert_eq!(
801            Argon2::verify_encoded(&encoded, b"password", Algorithm::Argon2id),
802            Ok(())
803        );
804
805        // `MIN_DECODED_OUT_LEN` is 12 and this tag is 8 bytes, which `MIN_OUTLEN`
806        // (4) allows. Same `m` and salt as above so the two strings differ only
807        // where the docs say they do.
808        let params = Params::new(2400, 1, 1, 8).unwrap();
809        let argon2 = Argon2::new(Algorithm::Argon2id, Version::V0x13, params);
810        let encoded = argon2.hash_encoded(b"password", b"somesalt").unwrap();
811        assert_eq!(
812            encoded,
813            "$argon2id$v=19$m=2400,t=1,p=1$c29tZXNhbHQ$kQGQLZpZJIk"
814        );
815        assert_eq!(
816            Argon2::verify_encoded(&encoded, b"password", Algorithm::Argon2id),
817            Ok(())
818        );
819    }
820
821    #[test]
822    fn default_params_are_valid() {
823        let d = Params::default();
824        assert!(d.validate_for(0, 8, 0, 0).is_ok());
825    }
826
827    #[test]
828    fn validate_order_salt_before_m_cost() {
829        // Both are bad; the C checks salt first.
830        assert_eq!(
831            validate_inputs(32, 0, 0, 0, 0, 0, 1, 1, 1),
832            Err(Error::SaltTooShort)
833        );
834    }
835
836    #[test]
837    fn validate_order_out_len_first() {
838        assert_eq!(
839            validate_inputs(0, 0, 0, 0, 0, 0, 0, 0, 0),
840            Err(Error::OutputTooShort)
841        );
842    }
843
844    #[test]
845    fn m_cost_lanes_product_wraps_like_c() {
846        // 8 * 0xFFFF_FFFF wraps to 0xFFFF_FFF8, so any sane m_cost is "too
847        // little" and LanesTooMany never gets a chance to fire.
848        assert_eq!(
849            validate_inputs(32, 0, 8, 0, 0, 1 << 16, 1, 0xFFFF_FFFF, 1),
850            Err(Error::MemoryTooLittle)
851        );
852        // With lanes in range, the 8*lanes rule is the third memory check.
853        assert_eq!(
854            validate_inputs(32, 0, 8, 0, 0, 16, 1, 4, 4),
855            Err(Error::MemoryTooLittle)
856        );
857        assert_eq!(validate_inputs(32, 0, 8, 0, 0, 32, 1, 4, 4), Ok(()));
858    }
859
860    #[test]
861    fn lanes_zero_is_lanes_too_few() {
862        // 8 * 0 == 0, so the memory checks pass and LanesTooFew surfaces.
863        assert_eq!(
864            validate_inputs(32, 0, 8, 0, 0, 8, 1, 0, 1),
865            Err(Error::LanesTooFew)
866        );
867    }
868
869    #[test]
870    fn memory_layout_matches_argon2_ctx() {
871        // m_cost below the floor gets bumped to 2 * SYNC_POINTS * lanes.
872        let p = Params::new(8, 1, 1, 32).unwrap();
873        assert_eq!(p.memory_layout(), (8, 2, 8));
874
875        // 1 << 16 KiB, one lane: 65536 blocks, 16384 per segment.
876        let p = Params::new(1 << 16, 2, 1, 32).unwrap();
877        assert_eq!(p.memory_layout(), (65536, 16384, 65536));
878
879        // Four lanes: segment_length = 65536 / 16 = 4096, lane_length = 16384.
880        let p = Params::new(1 << 16, 2, 4, 32).unwrap();
881        assert_eq!(p.memory_layout(), (65536, 4096, 16384));
882
883        // Not a multiple of lanes * SYNC_POINTS: truncated down.
884        let p = Params::new(100, 1, 3, 32).unwrap();
885        let (blocks, seg, lane) = p.memory_layout();
886        assert_eq!(seg, 100 / 12);
887        assert_eq!(blocks, seg * 12);
888        assert_eq!(lane, seg * 4);
889    }
890
891    #[test]
892    fn effective_threads_is_min() {
893        let p = Params::new_with_threads(1 << 16, 1, 2, 8, 32).unwrap();
894        assert_eq!(p.threads(), 8);
895        assert_eq!(p.effective_threads(), 2);
896    }
897
898    #[test]
899    fn algorithm_and_version_round_trip() {
900        for a in Algorithm::ALL {
901            assert_eq!(Algorithm::from_u32(a.as_u32()), Some(a));
902        }
903        assert_eq!(Algorithm::from_u32(3), None);
904        assert_eq!(Algorithm::Argon2id.as_str(), "argon2id");
905        assert_eq!(Algorithm::Argon2i.as_str_uppercase(), "Argon2i");
906        for v in Version::ALL {
907            assert_eq!(Version::from_u32(v.as_u32()), Some(v));
908        }
909        assert_eq!(Version::from_u32(0x11), None);
910        assert_eq!(Version::default(), Version::V0x13);
911        assert_eq!(Algorithm::default(), Algorithm::Argon2id);
912    }
913}