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 pub const PASSES: u32 = 3;
21 pub const KIB: u32 = 4096;
22 pub const LANES: u32 = 1;
24 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#[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 pub fn default(v: Variant) -> Argon2 {
124 Argon2::new(defaults::PASSES, defaults::LANES, defaults::KIB, v)
125 .ok()
126 .unwrap()
127 }
128
129 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 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 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); for lane in 0..self.lanes {
229 self.fill_first_slice(&mut blocks, h0, lane);
230 }
231
232 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); 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); }
248
249 h_prime(out, &blocks.xor_column(self.lanelen - 1).as_u8());
250 }
251
252 fn fill_first_slice(&self, blks: &mut Matrix, mut h0: [u8; 72], lane: u32) {
272 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 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 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
365pub 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
379pub 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
393pub 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
426fn 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 let r: u32 = match (pass, slice, j2 % lanes == lane) {
440 (0, 0, _) => sliceidx - 1,
443
444 (0, _, false) => slice * slicelen - if sliceidx == 0 { 1 } else { 0 },
449
450 (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
517fn 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 for col in 0..8 {
528 p_col(col, dest);
529 }
530
531 *dest ^= (lhs, rhs);
532}
533
534fn 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 for col in 0..8 {
549 p_col(col, &mut tmp);
550 }
551
552 *dest ^= &tmp;
553}
554
555fn 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 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}