blob: a93211de610fd3d8ad7f5a4d97b7a07a1c1cb19a [file] [log] [blame]
// Copyright (c) 2017 Martijn Rijkeboer <mrr@sru-systems.com>
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use crate::block::Block;
use crate::common;
use crate::context::Context;
use crate::memory::Memory;
use crate::variant::Variant;
use crate::version::Version;
use blake2b_simd::Params;
use crossbeam_utils::thread::scope;
use std::mem;
/// Position of the block currently being operated on.
#[derive(Clone, Debug)]
struct Position {
pass: u32,
lane: u32,
slice: u32,
index: u32,
}
/// Initializes the memory.
pub fn initialize(context: &Context, memory: &mut Memory) {
fill_first_blocks(context, memory, &mut h0(context));
}
/// Fills all the memory blocks.
pub fn fill_memory_blocks(context: &Context, memory: &mut Memory) {
if context.config.uses_sequential() {
fill_memory_blocks_st(context, memory);
} else {
fill_memory_blocks_mt(context, memory);
}
}
/// Calculates the final hash and returns it.
pub fn finalize(context: &Context, memory: &Memory) -> Vec<u8> {
let mut blockhash = memory[context.lane_length - 1].clone();
for l in 1..context.config.lanes {
let last_block_in_lane = l * context.lane_length + (context.lane_length - 1);
blockhash ^= &memory[last_block_in_lane];
}
let mut hash = vec![0u8; context.config.hash_length as usize];
hprime(hash.as_mut_slice(), blockhash.as_u8());
hash
}
fn blake2b(out: &mut [u8], input: &[&[u8]]) {
let mut blake = Params::new().hash_length(out.len()).to_state();
for slice in input {
blake.update(slice);
}
out.copy_from_slice(blake.finalize().as_bytes());
}
fn f_bla_mka(x: u64, y: u64) -> u64 {
let m = 0xFFFF_FFFFu64;
let xy = (x & m) * (y & m);
x.wrapping_add(y.wrapping_add(xy.wrapping_add(xy)))
}
fn fill_block(prev_block: &Block, ref_block: &Block, next_block: &mut Block, with_xor: bool) {
let mut block_r = ref_block.clone();
block_r ^= prev_block;
let mut block_tmp = block_r.clone();
// Now block_r = ref_block + prev_block and block_tmp = ref_block + prev_block
if with_xor {
// Saving the next block contents for XOR over
block_tmp ^= next_block;
// Now block_r = ref_block + prev_block and
// block_tmp = ref_block + prev_block + next_block
}
// Apply Blake2 on columns of 64-bit words: (0,1,...,15) , then
// (16,17,..31)... finally (112,113,...127)
for i in 0..8 {
let mut v0 = block_r[16 * i];
let mut v1 = block_r[16 * i + 1];
let mut v2 = block_r[16 * i + 2];
let mut v3 = block_r[16 * i + 3];
let mut v4 = block_r[16 * i + 4];
let mut v5 = block_r[16 * i + 5];
let mut v6 = block_r[16 * i + 6];
let mut v7 = block_r[16 * i + 7];
let mut v8 = block_r[16 * i + 8];
let mut v9 = block_r[16 * i + 9];
let mut v10 = block_r[16 * i + 10];
let mut v11 = block_r[16 * i + 11];
let mut v12 = block_r[16 * i + 12];
let mut v13 = block_r[16 * i + 13];
let mut v14 = block_r[16 * i + 14];
let mut v15 = block_r[16 * i + 15];
p(
&mut v0, &mut v1, &mut v2, &mut v3, &mut v4, &mut v5, &mut v6, &mut v7, &mut v8,
&mut v9, &mut v10, &mut v11, &mut v12, &mut v13, &mut v14, &mut v15,
);
block_r[16 * i] = v0;
block_r[16 * i + 1] = v1;
block_r[16 * i + 2] = v2;
block_r[16 * i + 3] = v3;
block_r[16 * i + 4] = v4;
block_r[16 * i + 5] = v5;
block_r[16 * i + 6] = v6;
block_r[16 * i + 7] = v7;
block_r[16 * i + 8] = v8;
block_r[16 * i + 9] = v9;
block_r[16 * i + 10] = v10;
block_r[16 * i + 11] = v11;
block_r[16 * i + 12] = v12;
block_r[16 * i + 13] = v13;
block_r[16 * i + 14] = v14;
block_r[16 * i + 15] = v15;
}
// Apply Blake2 on rows of 64-bit words: (0,1,16,17,...112,113), then
// (2,3,18,19,...,114,115).. finally (14,15,30,31,...,126,127)
for i in 0..8 {
let mut v0 = block_r[2 * i];
let mut v1 = block_r[2 * i + 1];
let mut v2 = block_r[2 * i + 16];
let mut v3 = block_r[2 * i + 17];
let mut v4 = block_r[2 * i + 32];
let mut v5 = block_r[2 * i + 33];
let mut v6 = block_r[2 * i + 48];
let mut v7 = block_r[2 * i + 49];
let mut v8 = block_r[2 * i + 64];
let mut v9 = block_r[2 * i + 65];
let mut v10 = block_r[2 * i + 80];
let mut v11 = block_r[2 * i + 81];
let mut v12 = block_r[2 * i + 96];
let mut v13 = block_r[2 * i + 97];
let mut v14 = block_r[2 * i + 112];
let mut v15 = block_r[2 * i + 113];
p(
&mut v0, &mut v1, &mut v2, &mut v3, &mut v4, &mut v5, &mut v6, &mut v7, &mut v8,
&mut v9, &mut v10, &mut v11, &mut v12, &mut v13, &mut v14, &mut v15,
);
block_r[2 * i] = v0;
block_r[2 * i + 1] = v1;
block_r[2 * i + 16] = v2;
block_r[2 * i + 17] = v3;
block_r[2 * i + 32] = v4;
block_r[2 * i + 33] = v5;
block_r[2 * i + 48] = v6;
block_r[2 * i + 49] = v7;
block_r[2 * i + 64] = v8;
block_r[2 * i + 65] = v9;
block_r[2 * i + 80] = v10;
block_r[2 * i + 81] = v11;
block_r[2 * i + 96] = v12;
block_r[2 * i + 97] = v13;
block_r[2 * i + 112] = v14;
block_r[2 * i + 113] = v15;
}
block_tmp.copy_to(next_block);
*next_block ^= &block_r;
}
fn fill_first_blocks(context: &Context, memory: &mut Memory, h0: &mut [u8]) {
for lane in 0..context.config.lanes {
let start = common::PREHASH_DIGEST_LENGTH;
// H'(H0||0||i)
h0[start..(start + 4)].clone_from_slice(&u32_as_32le(0));
h0[(start + 4)..(start + 8)].clone_from_slice(&u32_as_32le(lane));
hprime(memory[(lane, 0)].as_u8_mut(), &h0);
// H'(H0||1||i)
h0[start..(start + 4)].clone_from_slice(&u32_as_32le(1));
hprime(memory[(lane, 1)].as_u8_mut(), &h0);
}
}
fn fill_memory_blocks_mt(context: &Context, memory: &mut Memory) {
for p in 0..context.config.time_cost {
for s in 0..common::SYNC_POINTS {
let _ = scope(|scoped| {
for (l, mem) in (0..context.config.lanes).zip(memory.as_lanes_mut()) {
let position = Position {
pass: p,
lane: l,
slice: s,
index: 0,
};
scoped.spawn(move |_| {
fill_segment(context, &position, mem);
});
}
});
}
}
}
fn fill_memory_blocks_st(context: &Context, memory: &mut Memory) {
for p in 0..context.config.time_cost {
for s in 0..common::SYNC_POINTS {
for l in 0..context.config.lanes {
let position = Position {
pass: p,
lane: l,
slice: s,
index: 0,
};
fill_segment(context, &position, memory);
}
}
}
}
fn fill_segment(context: &Context, position: &Position, memory: &mut Memory) {
let mut position = position.clone();
let data_independent_addressing = (context.config.variant == Variant::Argon2i)
|| (context.config.variant == Variant::Argon2id && position.pass == 0)
&& (position.slice < (common::SYNC_POINTS / 2));
let zero_block = Block::zero();
let mut input_block = Block::zero();
let mut address_block = Block::zero();
if data_independent_addressing {
input_block[0] = position.pass as u64;
input_block[1] = position.lane as u64;
input_block[2] = position.slice as u64;
input_block[3] = context.memory_blocks as u64;
input_block[4] = context.config.time_cost as u64;
input_block[5] = context.config.variant.as_u64();
}
let mut starting_index = 0u32;
if position.pass == 0 && position.slice == 0 {
starting_index = 2;
// Don't forget to generate the first block of addresses:
if data_independent_addressing {
next_addresses(&mut address_block, &mut input_block, &zero_block);
}
}
let mut curr_offset = (position.lane * context.lane_length)
+ (position.slice * context.segment_length)
+ starting_index;
let mut prev_offset = if curr_offset % context.lane_length == 0 {
// Last block in this lane
curr_offset + context.lane_length - 1
} else {
curr_offset - 1
};
let mut pseudo_rand;
for i in starting_index..context.segment_length {
// 1.1 Rotating prev_offset if needed
if curr_offset % context.lane_length == 1 {
prev_offset = curr_offset - 1;
}
// 1.2 Computing the index of the reference block
// 1.2.1 Taking pseudo-random value from the previous block
if data_independent_addressing {
if i % common::ADDRESSES_IN_BLOCK == 0 {
next_addresses(&mut address_block, &mut input_block, &zero_block);
}
pseudo_rand = address_block[(i % common::ADDRESSES_IN_BLOCK) as usize];
} else {
pseudo_rand = memory[(prev_offset)][0];
}
// 1.2.2 Computing the lane of the reference block
// If (position.pass == 0) && (position.slice == 0): can not reference other lanes yet
let ref_lane = if (position.pass == 0) && (position.slice == 0) { position.lane as u64 } else { (pseudo_rand >> 32) % context.config.lanes as u64 };
// 1.2.3 Computing the number of possible reference block within the lane.
position.index = i;
let pseudo_rand_u32 = (pseudo_rand & 0xFFFF_FFFF) as u32;
let same_lane = ref_lane == (position.lane as u64);
let ref_index = index_alpha(context, &position, pseudo_rand_u32, same_lane);
// 2 Creating a new block
let index = context.lane_length as u64 * ref_lane + ref_index as u64;
let mut curr_block = memory[curr_offset].clone();
{
let prev_block = &memory[prev_offset];
let ref_block = &memory[index];
if context.config.version == Version::Version10 || position.pass == 0 {
fill_block(prev_block, ref_block, &mut curr_block, false);
} else {
fill_block(prev_block, ref_block, &mut curr_block, true);
}
}
memory[curr_offset] = curr_block;
curr_offset += 1;
prev_offset += 1;
}
}
fn g(a: &mut u64, b: &mut u64, c: &mut u64, d: &mut u64) {
*a = f_bla_mka(*a, *b);
*d = rotr64(*d ^ *a, 32);
*c = f_bla_mka(*c, *d);
*b = rotr64(*b ^ *c, 24);
*a = f_bla_mka(*a, *b);
*d = rotr64(*d ^ *a, 16);
*c = f_bla_mka(*c, *d);
*b = rotr64(*b ^ *c, 63);
}
fn h0(context: &Context) -> [u8; common::PREHASH_SEED_LENGTH] {
let input = [
&u32_as_32le(context.config.lanes),
&u32_as_32le(context.config.hash_length),
&u32_as_32le(context.config.mem_cost),
&u32_as_32le(context.config.time_cost),
&u32_as_32le(context.config.version.as_u32()),
&u32_as_32le(context.config.variant.as_u32()),
&len_as_32le(context.pwd),
context.pwd,
&len_as_32le(context.salt),
context.salt,
&len_as_32le(context.config.secret),
context.config.secret,
&len_as_32le(context.config.ad),
context.config.ad,
];
let mut out = [0u8; common::PREHASH_SEED_LENGTH];
blake2b(&mut out[0..common::PREHASH_DIGEST_LENGTH], &input);
out
}
fn hprime(out: &mut [u8], input: &[u8]) {
let out_len = out.len();
if out_len <= common::BLAKE2B_OUT_LENGTH {
blake2b(out, &[&u32_as_32le(out_len as u32), input]);
} else {
let ai_len = 32;
let mut out_buffer = [0u8; common::BLAKE2B_OUT_LENGTH];
let mut in_buffer = [0u8; common::BLAKE2B_OUT_LENGTH];
blake2b(&mut out_buffer, &[&u32_as_32le(out_len as u32), input]);
out[0..ai_len].clone_from_slice(&out_buffer[0..ai_len]);
let mut out_pos = ai_len;
let mut to_produce = out_len - ai_len;
while to_produce > common::BLAKE2B_OUT_LENGTH {
in_buffer.clone_from_slice(&out_buffer);
blake2b(&mut out_buffer, &[&in_buffer]);
out[out_pos..out_pos + ai_len].clone_from_slice(&out_buffer[0..ai_len]);
out_pos += ai_len;
to_produce -= ai_len;
}
blake2b(&mut out[out_pos..out_len], &[&out_buffer]);
}
}
fn index_alpha(context: &Context, position: &Position, pseudo_rand: u32, same_lane: bool) -> u32 {
// Pass 0:
// - This lane: all already finished segments plus already constructed blocks in this segment
// - Other lanes: all already finished segments
// Pass 1+:
// - This lane: (SYNC_POINTS - 1) last segments plus already constructed blocks in this segment
// - Other lanes : (SYNC_POINTS - 1) last segments
let reference_area_size: u32 = if position.pass == 0 {
// First pass
if position.slice == 0 {
// First slice
position.index - 1
} else if same_lane {
// The same lane => add current segment
position.slice * context.segment_length + position.index - 1
} else if position.index == 0 {
position.slice * context.segment_length - 1
} else {
position.slice * context.segment_length
}
} else {
// Second pass
if same_lane {
context.lane_length - context.segment_length + position.index - 1
} else if position.index == 0 {
context.lane_length - context.segment_length - 1
} else {
context.lane_length - context.segment_length
}
};
let reference_area_size = reference_area_size as u64;
let mut relative_position = pseudo_rand as u64;
relative_position = (relative_position * relative_position) >> 32;
relative_position = reference_area_size - 1 - ((reference_area_size * relative_position) >> 32);
// 1.2.5 Computing starting position
let start_position: u32 = if position.pass != 0 {
if position.slice == common::SYNC_POINTS - 1 {
0u32
} else {
(position.slice + 1) * context.segment_length
}
} else {
0u32
};
let start_position = start_position as u64;
// 1.2.6. Computing absolute position
((start_position + relative_position) % context.lane_length as u64) as u32
}
fn len_as_32le(slice: &[u8]) -> [u8; 4] {
u32_as_32le(slice.len() as u32)
}
fn next_addresses(address_block: &mut Block, input_block: &mut Block, zero_block: &Block) {
input_block[6] += 1;
fill_block(zero_block, input_block, address_block, false);
fill_block(zero_block, &address_block.clone(), address_block, false);
}
fn p(
v0: &mut u64,
v1: &mut u64,
v2: &mut u64,
v3: &mut u64,
v4: &mut u64,
v5: &mut u64,
v6: &mut u64,
v7: &mut u64,
v8: &mut u64,
v9: &mut u64,
v10: &mut u64,
v11: &mut u64,
v12: &mut u64,
v13: &mut u64,
v14: &mut u64,
v15: &mut u64,
) {
g(v0, v4, v8, v12);
g(v1, v5, v9, v13);
g(v2, v6, v10, v14);
g(v3, v7, v11, v15);
g(v0, v5, v10, v15);
g(v1, v6, v11, v12);
g(v2, v7, v8, v13);
g(v3, v4, v9, v14);
}
fn rotr64(w: u64, c: u32) -> u64 {
(w >> c) | (w << (64 - c))
}
fn u32_as_32le(val: u32) -> [u8; 4] {
unsafe { mem::transmute(val.to_le()) }
}