use crate::{BLOCK_SIZE, Schedule, State, ubc_check::RecompressFrom};
mod backend;
mod rounds;
pub(crate) use backend::Backend;
use rounds::{compression_w, recompression_step};
struct Scratch {
m1: Schedule,
state_58: [u32; 5],
state_65: [u32; 5],
}
#[inline]
pub(crate) fn compress(ctx: &mut State, blocks: &[u8]) {
debug_assert_eq!(blocks.len() % BLOCK_SIZE, 0);
let backend = ctx.backend;
let mut s = Scratch {
m1: Schedule::zeroed(),
state_58: [0; 5],
state_65: [0; 5],
};
for block in blocks.chunks_exact(BLOCK_SIZE) {
let block: &[u8; BLOCK_SIZE] = block.try_into().unwrap();
let ihv1 = ctx.h;
backend.compress_spill(
&mut ctx.h,
block,
&mut s.m1,
&mut s.state_58,
&mut s.state_65,
);
let candidates = if ctx.ubc_check {
crate::ubc_check::ubc_check(&s.m1, ctx.scalar_only)
} else {
!0
};
if candidates != 0
&& attacked(
backend,
ihv1,
ctx.h,
&mut s,
ctx.reduced_round_collision,
candidates,
)
{
ctx.found_collision = true;
if ctx.safe_hash {
compression_w(&mut ctx.h, &s.m1);
compression_w(&mut ctx.h, &s.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],
s: &mut Scratch,
reduced_round_collision: bool,
candidates: u32,
) -> bool {
backend.ensure_states(
&s.m1,
candidates & crate::ubc_check::STEP58_MASK != 0,
&mut s.state_58,
&mut s.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 => &s.state_58,
RecompressFrom::Step65 => &s.state_65,
};
if reduced_round_collision {
let mut ihv2 = [0u32; 5];
let mut ends_on = [0u32; 5];
recompression_step(
dv.recompress_from,
&mut ihv2,
&mut ends_on,
&s.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, &s.m1, &dv.dm, from, &chaining_out) {
return true;
}
}
false
}