/// One Argon2 memory block is 1024 bytes = 128 × u64. pub(super) const BLOCK_WORDS: usize = 128; /// One Argon2 memory block is 1024 bytes. pub(super) const BLOCK_SIZE: usize = 1024; /// A single 1024-byte Argon2 memory block. #[derive(Clone, Copy)] pub(super) struct Block(pub(super) [u64; BLOCK_WORDS]); impl Block { pub(super) const ZERO: Self = Self([0u64; BLOCK_WORDS]); } /// Argon2 compression function G. /// /// R = X XOR Y /// Z = P(R) /// output = Z XOR R /// /// On passes after the first, the old contents of the destination block are also XORed into the result. pub(super) fn compress_block(previous: &Block, reference: &Block, old: Option<&Block>) -> Block { let mut r = [0u64; BLOCK_WORDS]; for (dst, (&a, &b)) in r.iter_mut().zip(previous.0.iter().zip(reference.0.iter())) { *dst = a ^ b; } /* * P(R), first over rows and then over columns. */ let mut z = r; /* * Row-wise permutation. */ for row in 0..8 { let start = row * 16; let mut words = [0u64; 16]; words.copy_from_slice(&z[start..start + 16]); round(&mut words); z[start..start + 16].copy_from_slice(&words); } /* * Column-wise permutation. * * Each permutation operates on two adjacent 64-bit * columns from each of the eight rows. */ for column_pair in 0..8 { let mut words = [0u64; 16]; for row in 0..8 { let source = row * 16 + column_pair * 2; words[row * 2] = z[source]; words[row * 2 + 1] = z[source + 1]; } round(&mut words); for row in 0..8 { let destination = row * 16 + column_pair * 2; z[destination] = words[row * 2]; z[destination + 1] = words[row * 2 + 1]; } } let mut output = [0u64; BLOCK_WORDS]; for i in 0..BLOCK_WORDS { output[i] = z[i] ^ r[i] ^ old.map_or(0, |block| block.0[i]); } Block(output) } /// The Argon2/BLAKE2b quarter-round. /// /// This is the BlaMka operation used by Argon2. #[inline(always)] fn g(v: &mut [u64; 16], a: usize, b: usize, c: usize, d: usize) { v[a] = blamka(v[a], v[b]); v[d] = (v[d] ^ v[a]).rotate_right(32); v[c] = blamka(v[c], v[d]); v[b] = (v[b] ^ v[c]).rotate_right(24); v[a] = blamka(v[a], v[b]); v[d] = (v[d] ^ v[a]).rotate_right(16); v[c] = blamka(v[c], v[d]); v[b] = (v[b] ^ v[c]).rotate_right(63); } /// BlaMka addition: /// /// a + b + 2 * low32(a) * low32(b) mod 2^64. #[inline(always)] fn blamka(a: u64, b: u64) -> u64 { let product = 2u64 .wrapping_mul(a & 0xffff_ffff) .wrapping_mul(b & 0xffff_ffff); a.wrapping_add(b).wrapping_add(product) } /// One BLAKE2b style 16 word round used by Argon2. #[inline(always)] fn round(v: &mut [u64; 16]) { // Column step. g(v, 0, 4, 8, 12); g(v, 1, 5, 9, 13); g(v, 2, 6, 10, 14); g(v, 3, 7, 11, 15); // Diagonal step. g(v, 0, 5, 10, 15); g(v, 1, 6, 11, 12); g(v, 2, 7, 8, 13); g(v, 3, 4, 9, 14); }