pub fn compress(state: &mut [u32; 5], block: &[u8]) {
let mut w = [0u32; 80];
for (slot, bytes) in w[..16].iter_mut().zip(block.chunks_exact(4)) {
*slot = u32::from_be_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]);
}
for i in 16..80 {
w[i] = (w[i - 3] ^ w[i - 8] ^ w[i - 14] ^ w[i - 16]).rotate_left(1);
}
let [mut a, mut b, mut c, mut d, mut e] = *state;
for (i, word) in w.iter().enumerate() {
let (f, k) = match i {
0..=19 => (((b & c) | ((!b) & d)), 0x5a82_7999),
20..=39 => (b ^ c ^ d, 0x6ed9_eba1),
40..=59 => (((b & c) | (b & d) | (c & d)), 0x8f1b_bcdc),
_ => (b ^ c ^ d, 0xca62_c1d6),
};
let temp = a
.rotate_left(5)
.wrapping_add(f)
.wrapping_add(e)
.wrapping_add(k)
.wrapping_add(*word);
e = d;
d = c;
c = b.rotate_left(30);
b = a;
a = temp;
}
for (slot, value) in state.iter_mut().zip([a, b, c, d, e]) {
*slot = slot.wrapping_add(value);
}
}
pub const H0: [u32; 5] = [
0x6745_2301,
0xefcd_ab89,
0x98ba_dcfe,
0x1032_5476,
0xc3d2_e1f0,
];
#[must_use]
pub(crate) fn sha1_words(input: &[u8]) -> [u32; 5] {
let mut state = H0;
let mut blocks = input.chunks_exact(64);
for block in &mut blocks {
compress(&mut state, block);
}
let rem = blocks.remainder();
let bit_len = (input.len() as u64).wrapping_mul(8);
let mut tail = [0u8; 128];
tail[..rem.len()].copy_from_slice(rem);
tail[rem.len()] = 0x80;
let total = if rem.len() < 56 { 64 } else { 128 };
tail[total - 8..total].copy_from_slice(&bit_len.to_be_bytes());
for block in tail[..total].chunks_exact(64) {
compress(&mut state, block);
}
state
}