use crate::{BLOCK_SIZE, Inner, ubc_check::RecompressFrom};
mod backend;
mod rounds;
pub(crate) use backend::Backend;
use rounds::{compression_w, recompression_step};
#[inline]
pub(crate) fn compress(ctx: &mut Inner, blocks: &[[u8; BLOCK_SIZE]]) {
let backend = ctx.backend;
for block in blocks {
let ihv1 = ctx.h;
let Inner {
h,
m1,
state_58,
state_65,
..
} = ctx;
backend.compress_spill(h, block, m1, state_58, state_65);
let candidates = if ctx.ubc_check {
crate::ubc_check::ubc_check(&ctx.m1, ctx.scalar_only)
} else {
!0
};
if candidates != 0 && attacked(backend, ihv1, ctx.h, ctx, candidates) {
ctx.found_collision = true;
if ctx.safe_hash {
let Inner { h, m1, .. } = ctx;
compression_w(h, m1);
compression_w(h, m1);
}
}
}
}
#[inline(always)]
fn xor(a: &[u32; 5], b: &[u32; 5]) -> u32 {
a.iter().zip(b).fold(0, |differs, (x, y)| differs | (x ^ y))
}
#[inline(never)]
fn attacked(
backend: Backend,
ihv1: [u32; 5],
chaining_out: [u32; 5],
ctx: &mut Inner,
candidates: u32,
) -> bool {
let Inner {
m1,
state_58,
state_65,
..
} = ctx;
backend.ensure_states(
&ihv1,
&chaining_out,
m1,
candidates & crate::ubc_check::STEP58_MASK != 0,
state_58,
state_65,
);
let mut remaining = candidates;
while remaining != 0 {
let bit = (remaining.trailing_zeros() & 31) as usize;
remaining &= remaining - 1;
let dv = &crate::ubc_check::SHA1_DVS[bit];
debug_assert_eq!(dv.mask_bit, bit as i32, "DV table is out of order");
let from = match dv.recompress_from {
RecompressFrom::Step58 => &ctx.state_58,
RecompressFrom::Step65 => &ctx.state_65,
};
if ctx.reduced_round_collision {
let mut ihv2 = [0u32; 5];
let mut ends_on = [0u32; 5];
recompression_step(
dv.recompress_from,
&mut ihv2,
&mut ends_on,
&ctx.m1,
&dv.dm,
from,
);
if xor(&ends_on, &chaining_out) == 0 || xor(&ihv1, &ihv2) == 0 {
return true;
}
} else if backend.is_attack(dv.recompress_from, &ctx.m1, &dv.dm, from, &chaining_out) {
return true;
}
}
false
}