aingle_argon2/
argon2.rs

1use blake2::digest::{Update, VariableOutput};
2use blake2::Blake2bVar;
3use crate::block::{self, Block, Matrix, ARGON2_BLOCK_BYTES};
4use crate::octword::u64x2;
5use std::error::Error;
6use std::fmt;
7
8#[derive(Eq, PartialEq, Copy, Clone, Debug)]
9pub enum Variant {
10    Argon2d = 0,
11    Argon2i = 1,
12    Argon2id = 2,
13}
14
15const DEF_B2HASH_LEN: usize = 64;
16const SLICES_PER_LANE: u32 = 4;
17
18pub mod defaults {
19    // from run.c
20    pub const PASSES: u32 = 3;
21    pub const KIB: u32 = 4096;
22    /// Default level of parallelism.
23    pub const LANES: u32 = 1;
24    /// The size of Argon2's hash output is adjustable. This is the default
25    /// length.
26    pub const LENGTH: usize = 32;
27}
28
29fn split_u64(n: u64) -> (u32, u32) {
30    ((n & 0xffffffff) as u32, (n >> 32) as u32)
31}
32
33fn as32le(k: u32) -> [u8; 4] {
34    k.to_le_bytes()
35}
36
37fn len32(t: &[u8]) -> [u8; 4] {
38    as32le(t.len() as u32)
39}
40
41macro_rules! b2hash {
42    ($($bytes: expr),*) => {
43        {
44            let mut out: [u8; DEF_B2HASH_LEN] = [0u8; DEF_B2HASH_LEN];
45            b2hash!(&mut out; $($bytes),*);
46            out
47        }
48    };
49    ($out: expr; $($bytes: expr),*) => {
50        {
51            let mut hasher = Blake2bVar::new($out.len()).unwrap();
52            $(hasher.update($bytes));*;
53            hasher.finalize_variable($out).unwrap();
54        }
55    };
56}
57
58fn h0(
59    lanes: u32,
60    hash_length: u32,
61    memory_kib: u32,
62    passes: u32,
63    version: u32,
64    variant: Variant,
65    p: &[u8],
66    s: &[u8],
67    k: &[u8],
68    x: &[u8],
69) -> [u8; 72] {
70    let mut rv = [0_u8; 72];
71    b2hash!(&mut rv[0..DEF_B2HASH_LEN];
72            &as32le(lanes), &as32le(hash_length), &as32le(memory_kib),
73            &as32le(passes), &as32le(version), &as32le(variant as u32),
74            &len32(p), p,
75            &len32(s), s,
76            &len32(k), k,
77            &len32(x), x);
78    rv
79}
80
81/// Main entry point for running Argon2 on customized parameters (cf. note for
82/// `Argon2::new`).
83#[derive(Debug, Eq, PartialEq)]
84pub struct Argon2 {
85    passes: u32,
86    lanes: u32,
87    lanelen: u32,
88    kib: u32,
89    variant: Variant,
90    version: Version,
91}
92
93#[derive(Debug, PartialEq, Eq, Clone, Copy)]
94pub enum Version {
95    _0x10 = 0x10,
96    _0x13 = 0x13,
97}
98
99#[derive(Debug, PartialEq, Eq, Clone, Copy)]
100pub enum ParamErr {
101    TooFewPasses,
102    TooFewLanes,
103    TooManyLanes,
104    MinKiB(u64),
105}
106
107impl fmt::Display for ParamErr {
108    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
109        use ParamErr::*;
110        match *self {
111            TooFewPasses => write!(f, "Argon2 requires one or more passes to be run."),
112            TooFewLanes | TooManyLanes => write!(f, "The number of lanes must be between one and 2^24 - 1."),
113            MinKiB(k) => write!(f, "Memory parameter must be >= {} KiB.", k),
114        }
115    }
116}
117
118impl Error for ParamErr {}
119
120impl Argon2 {
121    /// Returns an `Argon2` set to default input parameters. See below for a
122    /// description of these parameters.
123    pub fn default(v: Variant) -> Argon2 {
124        Argon2::new(defaults::PASSES, defaults::LANES, defaults::KIB, v)
125            .ok()
126            .unwrap()
127    }
128
129    /// Use this to customize Argon2's time and memory cost parameters.
130    /// Adjusting any of these will affect the value of the final hash result.
131    ///
132    /// `passes`: The number of block matrix iterations to perform. Increasing
133    /// this forces hashing to take longer. Must be between 1 and 2^32 - 1.
134    ///
135    /// `lanes`: The degree of parallelism by which memory is filled during hash
136    /// computation. Setting this to N instructs argon2min to partition the block
137    /// matrix into N lanes, simultaneously filling each lane in parallel with N
138    /// threads. Must be between 1 and 2^24 - 1.
139    ///
140    /// `kib`: Desired total size of block matrix, in kibibytes (1 KiB = 1024
141    /// bytes). Increasing this forces hashing to use more memory in order to
142    /// thwart ASIC-based attacks. Must be >= 8 * lanes.
143    ///
144    /// `variant`: Set this to `Variant::Argon2i` when hashing passwords.
145    pub fn new(passes: u32, lanes: u32, kib: u32, variant: Variant) -> Result<Argon2, ParamErr> {
146        Argon2::with_version(passes, lanes, kib, variant, Version::_0x13)
147    }
148
149    // This entry point exists to allow the verifier to verify hash encodings
150    // that were generated with legacy versions of Argon2.
151    pub(crate) fn with_version(
152        passes: u32,
153        lanes: u32,
154        kib: u32,
155        variant: Variant,
156        version: Version,
157    ) -> Result<Argon2, ParamErr> {
158        if passes < 1 {
159            Result::Err(ParamErr::TooFewPasses)
160        } else if lanes < 1 {
161            Result::Err(ParamErr::TooFewLanes)
162        } else if 0x00ffffff < lanes {
163            Result::Err(ParamErr::TooManyLanes)
164        } else if (kib as u64) < 8 * lanes as u64 {
165            Result::Err(ParamErr::MinKiB(8 * lanes as u64))
166        } else {
167            Result::Ok(Argon2 {
168                passes,
169                lanes,
170                lanelen: kib / (4 * lanes) * 4,
171                kib,
172                variant,
173                version,
174            })
175        }
176    }
177
178    /// Runs the selected Argon2 variant over provided inputs, writing the final
179    /// hash to the byte slice `out`. Note that the output length is assumed to
180    /// be `out.len()` and must be between 4 and 2^32 - 1. The inputs are:
181    ///
182    /// `p`, the byte slice containing, typically, the plaintext (length 0 to
183    /// 2^32-1);
184    ///
185    /// a salt `s` of length 8 to 2^32 - 1 bytes;
186    ///
187    /// `k`, an optional (length 0 to 32 bytes) secret value; and
188    ///
189    /// `x`, optional associated data length 0 to 2^32 - 1.
190    pub fn hash(&self, out: &mut [u8], p: &[u8], s: &[u8], k: &[u8], x: &[u8]) {
191        self.hash_impl(out, p, s, k, x, |_| {}, |_, _| {});
192    }
193
194    fn hash_impl<F, G>(
195        &self,
196        out: &mut [u8],
197        p: &[u8],
198        s: &[u8],
199        k: &[u8],
200        x: &[u8],
201        mut h0_fn: F,
202        mut pass_fn: G,
203    ) where
204        F: FnMut(&[u8]),
205        G: FnMut(u32, &Matrix),
206    {
207        assert!(4 <= out.len() && out.len() <= 0xffffffff);
208        assert!(p.len() <= 0xffffffff);
209        assert!(8 <= s.len() && s.len() <= 0xffffffff);
210        assert!(k.len() <= 32);
211        assert!(x.len() <= 0xffffffff);
212
213        let mut blocks = Matrix::new(self.lanes, self.lanelen);
214        let h0 = h0(
215            self.lanes,
216            out.len() as u32,
217            self.kib,
218            self.passes,
219            self.version as u32,
220            self.variant,
221            p,
222            s,
223            k,
224            x,
225        );
226        h0_fn(&h0); // kats
227
228        for lane in 0..self.lanes {
229            self.fill_first_slice(&mut blocks, h0, lane);
230        }
231
232        // finish first pass. slices have to be filled in sync.
233        for slice in 1..SLICES_PER_LANE {
234            for lane in 0..self.lanes {
235                self.fill_slice(&mut blocks, 0, lane, slice, 0);
236            }
237        }
238        pass_fn(0, &blocks); // kats
239
240        for p in 1..self.passes {
241            for slice in 0..SLICES_PER_LANE {
242                for lane in 0..self.lanes {
243                    self.fill_slice(&mut blocks, p, lane, slice, 0);
244                }
245            }
246            pass_fn(p, &blocks); // kats
247        }
248
249        h_prime(out, &blocks.xor_column(self.lanelen - 1).as_u8());
250    }
251
252    // `Matrix` is an array of 1-KiB blocks and organized as follows:
253    //
254    //     +------------------------ `lanelen` columns ------------------------+
255    //     |                                 +-----------------+               |
256    //     +--- slice ---+   +--- slice ---+ | +--- slice ---+ | +--- slice ---+
257    //     |             |   |             | | |             | | |             |
258    //  +- b b b b ...   b   b b b b ...   b | b b b b ...   b | b b b b ...   b
259    //  |  b                                 |                 |
260    //  |  ...                               |                 |
261    //  +- b b b b ...   b   b b b b ...   b | b b b b ...   b | b b b b ...   b
262    //  |                                    |                 |
263    //  +--`lane` rows                       +---- segment ----+
264    //
265    //  where each `b` represents a 1-KiB block.
266    //
267    //  Some invariants:
268    //  - There are always four slices.
269    //  - `lanelen * lane = self.kib`.
270    //  - Filling is done segment-by-segment.
271    fn fill_first_slice(&self, blks: &mut Matrix, mut h0: [u8; 72], lane: u32) {
272        // fill the first (of four) slice
273        h0[68..72].clone_from_slice(&as32le(lane));
274
275        h0[64..68].clone_from_slice(&as32le(0));
276        h_prime(blks[(lane, 0)].as_u8_mut(), &h0);
277
278        h0[64..68].clone_from_slice(&as32le(1));
279        h_prime(blks[(lane, 1)].as_u8_mut(), &h0);
280
281        // finish rest of first slice
282        self.fill_slice(blks, 0, lane, 0, 2);
283    }
284
285    fn fill_slice(&self, blks: &mut Matrix, pass: u32, lane: u32, slice: u32, offset: u32) {
286        let mut jgen = Gen2i::new(
287            offset as usize,
288            pass,
289            lane,
290            slice,
291            self.lanes * self.lanelen,
292            self.passes,
293            self.variant,
294        );
295        let slicelen = self.lanelen / SLICES_PER_LANE;
296
297        use Variant::*;
298
299        for idx in offset..slicelen {
300            let (j1, j2) = match self.variant {
301                Argon2i => jgen.nextj(),
302                Argon2d => {
303                    let col = self.prev(slice * slicelen + idx);
304                    split_u64((blks[(lane, col)])[0].0)
305                }
306                Argon2id if pass == 0 && slice < 2 => jgen.nextj(),
307                Argon2id => {
308                    let col = self.prev(slice * slicelen + idx);
309                    split_u64((blks[(lane, col)])[0].0)
310                }
311            };
312            self.fill_block(blks, pass, lane, slice, idx, j1, j2);
313        }
314    }
315
316    fn fill_block(
317        &self,
318        blks: &mut Matrix,
319        pass: u32,
320        lane: u32,
321        slice: u32,
322        idx: u32,
323        j1: u32,
324        j2: u32,
325    ) {
326        let slicelen = self.lanelen / SLICES_PER_LANE;
327        let ls = self.lanes;
328        let z = index_alpha(pass, lane, slice, ls, idx, slicelen, j1, j2);
329
330        let zth = match (pass, slice) {
331            (0, 0) => (lane, z),
332            _ => (j2 % self.lanes, z),
333        };
334
335        let cur = (lane, slice * slicelen + idx);
336        let pre = (lane, self.prev(cur.1));
337        let (wr, rd, refblk) = blks.get3(cur, pre, zth);
338        match self.version {
339            Version::_0x10 => g(wr, rd, refblk),
340            Version::_0x13 => g_xor(wr, rd, refblk),
341        }
342    }
343
344    fn prev(&self, n: u32) -> u32 {
345        if n > 0 {
346            n - 1
347        } else {
348            self.lanelen - 1
349        }
350    }
351
352    /// Provides read-only access to `(variant, kibibytes, passes, lanes,
353    /// version)`. The version should always be 0x13.
354    pub fn params(&self) -> (Variant, u32, u32, u32, Version) {
355        (
356            self.variant,
357            self.kib,
358            self.passes,
359            self.lanes,
360            self.version,
361        )
362    }
363}
364
365/// Convenience wrapper around Argon2i for the majority of use cases where only
366/// a password and salt are supplied. Note that a salt between 8 and 2^32 - 1
367/// bytes must be provided.
368pub fn argon2i_simple<P, S>(password: &P, salt: &S) -> [u8; defaults::LENGTH]
369where
370    P: AsRef<[u8]> + ?Sized,
371    S: AsRef<[u8]> + ?Sized,
372{
373    let mut out = [0; defaults::LENGTH];
374    let a2 = Argon2::default(Variant::Argon2i);
375    a2.hash(&mut out, password.as_ref(), salt.as_ref(), &[], &[]);
376    out
377}
378
379/// Convenience wrapper around Argon2d for the majority of use cases where only
380/// a password and salt are supplied. Note that a salt between 8 and 2^32 - 1
381/// bytes must be provided.
382pub fn argon2d_simple<P, S>(password: &P, salt: &S) -> [u8; defaults::LENGTH]
383where
384    P: AsRef<[u8]> + ?Sized,
385    S: AsRef<[u8]> + ?Sized,
386{
387    let mut out = [0; defaults::LENGTH];
388    let a2 = Argon2::default(Variant::Argon2d);
389    a2.hash(&mut out, password.as_ref(), salt.as_ref(), &[], &[]);
390    out
391}
392
393/// Convenience wrapper around Argon2i for the majority of use cases where only
394/// a password and salt are supplied. Note that a salt between 8 and 2^32 - 1
395/// bytes must be provided.
396pub fn argon2id_simple<P, S>(password: &P, salt: &S) -> [u8; defaults::LENGTH]
397    where
398        P: AsRef<[u8]> + ?Sized,
399        S: AsRef<[u8]> + ?Sized,
400{
401    let mut out = [0; defaults::LENGTH];
402    let a2 = Argon2::default(Variant::Argon2id);
403    a2.hash(&mut out, password.as_ref(), salt.as_ref(), &[], &[]);
404    out
405}
406
407fn h_prime(out: &mut [u8], input: &[u8]) {
408    if out.len() <= DEF_B2HASH_LEN {
409        b2hash!(out; &len32(out), input);
410    } else {
411        let mut tmp = b2hash!(&len32(out), input);
412        out[0..DEF_B2HASH_LEN].clone_from_slice(&tmp);
413        let mut wr_at: usize = 32;
414
415        while out.len() - wr_at > DEF_B2HASH_LEN {
416            b2hash!(&mut tmp; &tmp);
417            out[wr_at..wr_at + DEF_B2HASH_LEN].clone_from_slice(&tmp);
418            wr_at += DEF_B2HASH_LEN / 2;
419        }
420
421        let len = out.len() - wr_at;
422        b2hash!(&mut out[wr_at..wr_at + len]; &tmp);
423    }
424}
425
426// from opt.c
427fn index_alpha(
428    pass: u32,
429    lane: u32,
430    slice: u32,
431    lanes: u32,
432    sliceidx: u32,
433    slicelen: u32,
434    j1: u32,
435    j2: u32,
436) -> u32 {
437    let lanelen = slicelen * SLICES_PER_LANE;
438    // All quotes below taken from Section 3.3 ("Indexing") of the Argon2 spec.
439    let r: u32 = match (pass, slice, j2 % lanes == lane) {
440        // "If we work with the first slice and the first pass, then l is the
441        // current lane."
442        (0, 0, _) => sliceidx - 1,
443
444        // "If l is not the current lane, then R includes all blocks in the last
445        // S − 1 = 3 segments computed and finished in lane l. If B[i][j] is the
446        // first block of a segment, then the very last block from R is
447        // excluded."
448        (0, _, false) => slice * slicelen - if sliceidx == 0 { 1 } else { 0 },
449
450        // "If l is the current lane, then R includes all blocks computed in
451        // this lane, that are not overwritten yet, excluding B[i][j − 1]."
452        (0, _, true) => slice * slicelen + sliceidx - 1,
453
454        (_, _, false) => lanelen - slicelen - if sliceidx == 0 { 1 } else { 0 },
455        (_, _, true) => lanelen - slicelen + sliceidx - 1,
456    };
457
458    let (r_, j1_) = (r as u64, j1 as u64);
459    let relpos = (r_ - 1 - ((r_ * ((j1_ * j1_) >> 32)) >> 32)) as u32;
460
461    match (pass, slice) {
462        (0, _) | (_, 3) => relpos % lanelen,
463        _ => (slicelen * (slice + 1) + relpos) % lanelen,
464    }
465}
466
467struct Gen2i {
468    arg: Block,
469    pseudos: Block,
470    idx: usize,
471}
472
473impl Gen2i {
474    fn new(
475        start_at: usize,
476        pass: u32,
477        lane: u32,
478        slice: u32,
479        totblocks: u32,
480        totpasses: u32,
481        variant: Variant,
482    ) -> Gen2i {
483        use crate::block::zero;
484
485        let mut rv = Gen2i {
486            arg: zero(),
487            pseudos: zero(),
488            idx: start_at,
489        };
490        let args = [
491            (pass, lane),
492            (slice, totblocks),
493            (totpasses, variant as u32),
494        ];
495        for (k, (lo, hi)) in rv.arg.iter_mut().zip(args.into_iter()) {
496            *k = u64x2(lo as u64, hi as u64);
497        }
498        rv.more();
499        rv
500    }
501
502    fn more(&mut self) {
503        self.arg[3].0 += 1;
504        g_two(&mut self.pseudos, &self.arg);
505    }
506
507    fn nextj(&mut self) -> (u32, u32) {
508        let rv = split_u64(self.pseudos.as_u64()[self.idx]);
509        self.idx = (self.idx + 1) % per_kib!(u64);
510        if self.idx == 0 {
511            self.more();
512        }
513        rv
514    }
515}
516
517// g x y = let r = x `xor` y in p_col (p_row r) `xor` r,
518fn g(dest: &mut Block, lhs: &Block, rhs: &Block) {
519    for (d, (l, r)) in dest.iter_mut().zip(lhs.iter().zip(rhs.iter())) {
520        *d = *l ^ *r;
521    }
522
523    for row in 0..8 {
524        p_row(row, dest);
525    }
526    // column-wise, 2x u64 groups
527    for col in 0..8 {
528        p_col(col, dest);
529    }
530
531    *dest ^= (lhs, rhs);
532}
533
534// Identical to `g`, except that instead of overwriting the old block with the
535// new one, they are xor-ed together.
536fn g_xor(dest: &mut Block, lhs: &Block, rhs: &Block) {
537    let mut tmp: Block = block::zero();
538    let lr = lhs.iter().zip(rhs.iter());
539    for ((d, t), (l, r)) in dest.iter_mut().zip(tmp.iter_mut()).zip(lr) {
540        *t = *l ^ *r;
541        *d = *d ^ *t;
542    }
543
544    for row in 0..8 {
545        p_row(row, &mut tmp);
546    }
547    // column-wise, 2x u64 groups
548    for col in 0..8 {
549        p_col(col, &mut tmp);
550    }
551
552    *dest ^= &tmp;
553}
554
555/// ``` g2 y = let g' y = g 0 y in g' . g' ```
556/// Used for data-independent index generation.
557fn g_two(dest: &mut Block, src: &Block) {
558    *dest = src.clone();
559
560    for row in 0..8 {
561        p_row(row, dest);
562    }
563    for col in 0..8 {
564        p_col(col, dest);
565    }
566
567    *dest ^= src;
568
569    let tmp: Block = dest.clone();
570
571    for row in 0..8 {
572        p_row(row, dest);
573    }
574    for col in 0..8 {
575        p_col(col, dest);
576    }
577
578    *dest ^= &tmp;
579}
580
581macro_rules! p {
582    ($v0v1: expr, $v2v3: expr, $v4v5: expr, $v6v7: expr,
583     $v8v9: expr, $v10v11: expr, $v12v13: expr, $v14v15: expr) => {{
584        g_blake2b!($v0v1, $v4v5, $v8v9, $v12v13);
585        g_blake2b!($v2v3, $v6v7, $v10v11, $v14v15);
586
587        let (mut v7v4, mut v5v6) = $v4v5.cross_swap($v6v7);
588        let (mut v15v12, mut v13v14) = $v12v13.cross_swap($v14v15);
589
590        g_blake2b!($v0v1, v5v6, $v10v11, v15v12);
591        g_blake2b!($v2v3, v7v4, $v8v9, v13v14);
592
593        let (v4v5, v6v7) = v5v6.cross_swap(v7v4);
594        let (v12v13, v14v15) = v13v14.cross_swap(v15v12);
595        $v4v5 = v4v5;
596        $v6v7 = v6v7;
597        $v12v13 = v12v13;
598        $v14v15 = v14v15;
599    }};
600}
601
602macro_rules! g_blake2b {
603    ($a: expr, $b: expr, $c: expr, $d: expr) => {
604        $a = $a + $b + $a.lower_mult($b) * u64x2(2, 2);
605        $d = ($d ^ $a).rotate_right(32);
606        $c = $c + $d + $c.lower_mult($d) * u64x2(2, 2);
607        $b = ($b ^ $c).rotate_right(24);
608        $a = $a + $b + $a.lower_mult($b) * u64x2(2, 2);
609        $d = ($d ^ $a).rotate_right(16);
610        $c = $c + $d + $c.lower_mult($d) * u64x2(2, 2);
611        $b = ($b ^ $c).rotate_right(63);
612    };
613}
614
615#[inline(always)]
616fn p_row(row: usize, b: &mut Block) {
617    p!(
618        b[8 * row + 0],
619        b[8 * row + 1],
620        b[8 * row + 2],
621        b[8 * row + 3],
622        b[8 * row + 4],
623        b[8 * row + 5],
624        b[8 * row + 6],
625        b[8 * row + 7]
626    );
627}
628
629#[inline(always)]
630#[allow(clippy::erasing_op)]
631fn p_col(col: usize, b: &mut Block) {
632    p!(
633        b[8 * 0 + col],
634        b[8 * 1 + col],
635        b[8 * 2 + col],
636        b[8 * 3 + col],
637        b[8 * 4 + col],
638        b[8 * 5 + col],
639        b[8 * 6 + col],
640        b[8 * 7 + col]
641    );
642}
643
644#[cfg(test)]
645mod tests {
646    use super::Argon2;
647    use super::{Variant, Version};
648    use crate::block;
649    use std::fmt::Write;
650    use std::fs::File;
651    use std::io::Read;
652
653    // from genkat.c
654    const TEST_OUTLEN: usize = 32;
655    const TEST_PWDLEN: usize = 32;
656    const TEST_SALTLEN: usize = 16;
657    const TEST_SECRETLEN: usize = 8;
658    const TEST_ADLEN: usize = 12;
659
660    macro_rules! w { ($($args: expr),*) => { let _ = write!($($args),*); }; }
661    macro_rules! wl { ($($args: expr),*) => { let _ = writeln!($($args),*); }; }
662
663    fn u8info(prefix: &str, bytes: &[u8], print_length: bool) -> String {
664        let bs = bytes
665            .iter()
666            .fold(String::new(), |xs, b| xs + &format!("{:02x} ", b));
667        let len = match print_length {
668            false => ": ".to_string(),
669            true => format!("[{}]: ", bytes.len()),
670        };
671        prefix.to_string() + &len + &bs
672    }
673
674    fn block_info(i: usize, b: &block::Block) -> String {
675        let blk = b.as_u64();
676        blk.iter()
677            .enumerate()
678            .fold(String::new(), |xs, (j, octword)| {
679                xs + "Block "
680                    + &format!("{:004} ", i)
681                    + &format!("[{:>3}]: ", j)
682                    + &format!("{:0016x}", octword)
683                    + "\n"
684            })
685    }
686
687    fn run_and_collect(
688        arg: &Argon2,
689        out: &mut [u8],
690        p: &[u8],
691        s: &[u8],
692        k: &[u8],
693        x: &[u8],
694    ) -> (String, String) {
695        let (mut h0output, mut blockoutput) = (String::new(), String::new());
696
697        {
698            let h0fn = |h0: &[u8]| {
699                wl!(
700                    &mut h0output,
701                    "{}",
702                    u8info("Pre-hashing digest", &h0[..super::DEF_B2HASH_LEN], false)
703                );
704            };
705
706            let passfn = |p: u32, matrix: &block::Matrix| {
707                wl!(&mut blockoutput, "\n After pass {}:", p);
708                for (i, block) in matrix.iter().enumerate() {
709                    w!(&mut blockoutput, "{}", block_info(i, block));
710                }
711            };
712
713            arg.hash_impl(out, p, s, k, x, h0fn, passfn);
714        }
715
716        (h0output, blockoutput)
717    }
718
719    fn compare_kats(fexpected: &str, variant: Variant, vers: Version) {
720        let mut f = File::open(fexpected).unwrap();
721        let mut expected = String::new();
722        f.read_to_string(&mut expected).unwrap();
723
724        let (p, s) = (&[1; TEST_PWDLEN], &[2; TEST_SALTLEN]);
725        let (k, x) = (&[3; TEST_SECRETLEN], &[4; TEST_ADLEN]);
726        let mut out = [0 as u8; TEST_OUTLEN];
727        let a2 = Argon2::with_version(3, 4, 32, variant, vers).ok().unwrap();
728        let (h0, blocks) = run_and_collect(&a2, &mut out, p, s, k, x);
729
730        let mut rv = String::new();
731        wl!(rv, "=======================================");
732        wl!(
733            rv,
734            "{:?} version number {}",
735            a2.variant,
736            a2.version as usize
737        );
738        wl!(rv, "=======================================");
739        w!(rv, "Memory: {} KiB, Iterations: {}, ", a2.kib, a2.passes);
740        w!(rv, "Parallelism: {} lanes, ", a2.lanes);
741        wl!(rv, "Tag length: {} bytes", out.len());
742        wl!(rv, "{}", u8info("Password", p, true));
743        wl!(rv, "{}", u8info("Salt", s, true));
744        wl!(rv, "{}", u8info("Secret", k, true));
745        wl!(rv, "{}", u8info("Associated data", x, true));
746        w!(rv, "{}", h0 + &blocks);
747        wl!(rv, "{}", u8info("Tag", &out, false));
748
749        if expected.trim() != rv.trim() {
750            println!("{}", rv);
751            assert!(false);
752        }
753    }
754
755    #[test]
756    fn argon2i_kat() {
757        compare_kats("kats/0x10/argon2i", Variant::Argon2i, Version::_0x10);
758        compare_kats("kats/0x13/argon2i", Variant::Argon2i, Version::_0x13);
759    }
760
761    #[test]
762    fn argon2d_kat() {
763        compare_kats("kats/0x10/argon2d", Variant::Argon2d, Version::_0x10);
764        compare_kats("kats/0x13/argon2d", Variant::Argon2d, Version::_0x13);
765    }
766
767    #[test]
768    fn argon2id_kat() {
769        compare_kats("kats/0x10/argon2id", Variant::Argon2id, Version::_0x10);
770        compare_kats("kats/0x13/argon2id", Variant::Argon2id, Version::_0x13);
771    }
772}