135 lines
3.0 KiB
Rust
135 lines
3.0 KiB
Rust
/// 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);
|
||
}
|