fn md4(message: &[u8]) -> [u8; 16] {
let mut a: u32 = 0x6745_2301;
let mut b: u32 = 0xefcd_ab89;
let mut c: u32 = 0x98ba_dcfe;
let mut d: u32 = 0x1032_5476;
let bit_length = (message.len() as u64).wrapping_mul(8);
let mut padded = message.to_vec();
padded.push(0x80);
while padded.len() % 64 != 56 {
padded.push(0);
}
padded.extend_from_slice(&bit_length.to_le_bytes());
for chunk in padded.chunks_exact(64) {
let mut words = [0u32; 16];
for (index, word) in words.iter_mut().enumerate() {
let base = index * 4;
*word = u32::from_le_bytes(chunk[base..base + 4].try_into().unwrap());
}
let (mut aa, mut bb, mut cc, mut dd) = (a, b, c, d);
let f = |x: u32, y: u32, z: u32| (x & y) | (!x & z);
let g = |x: u32, y: u32, z: u32| (x & y) | (x & z) | (y & z);
let h = |x: u32, y: u32, z: u32| x ^ y ^ z;
for &index in &[0usize, 4, 8, 12] {
aa = aa
.wrapping_add(f(bb, cc, dd))
.wrapping_add(words[index])
.rotate_left(3);
dd = dd
.wrapping_add(f(aa, bb, cc))
.wrapping_add(words[index + 1])
.rotate_left(7);
cc = cc
.wrapping_add(f(dd, aa, bb))
.wrapping_add(words[index + 2])
.rotate_left(11);
bb = bb
.wrapping_add(f(cc, dd, aa))
.wrapping_add(words[index + 3])
.rotate_left(19);
}
for &index in &[0usize, 1, 2, 3] {
aa = aa
.wrapping_add(g(bb, cc, dd))
.wrapping_add(words[index])
.wrapping_add(0x5a82_7999)
.rotate_left(3);
dd = dd
.wrapping_add(g(aa, bb, cc))
.wrapping_add(words[index + 4])
.wrapping_add(0x5a82_7999)
.rotate_left(5);
cc = cc
.wrapping_add(g(dd, aa, bb))
.wrapping_add(words[index + 8])
.wrapping_add(0x5a82_7999)
.rotate_left(9);
bb = bb
.wrapping_add(g(cc, dd, aa))
.wrapping_add(words[index + 12])
.wrapping_add(0x5a82_7999)
.rotate_left(13);
}
for &index in &[0usize, 2, 1, 3] {
aa = aa
.wrapping_add(h(bb, cc, dd))
.wrapping_add(words[index])
.wrapping_add(0x6ed9_eba1)
.rotate_left(3);
dd = dd
.wrapping_add(h(aa, bb, cc))
.wrapping_add(words[index + 8])
.wrapping_add(0x6ed9_eba1)
.rotate_left(9);
cc = cc
.wrapping_add(h(dd, aa, bb))
.wrapping_add(words[index + 4])
.wrapping_add(0x6ed9_eba1)
.rotate_left(11);
bb = bb
.wrapping_add(h(cc, dd, aa))
.wrapping_add(words[index + 12])
.wrapping_add(0x6ed9_eba1)
.rotate_left(15);
}
a = a.wrapping_add(aa);
b = b.wrapping_add(bb);
c = c.wrapping_add(cc);
d = d.wrapping_add(dd);
}
let mut digest = [0u8; 16];
digest[0..4].copy_from_slice(&a.to_le_bytes());
digest[4..8].copy_from_slice(&b.to_le_bytes());
digest[8..12].copy_from_slice(&c.to_le_bytes());
digest[12..16].copy_from_slice(&d.to_le_bytes());
digest
}
pub fn block_checksum(data: &[u8]) -> u32 {
let digest = md4(data);
let word = |offset: usize| u32::from_le_bytes(digest[offset..offset + 4].try_into().unwrap());
word(0) ^ word(4) ^ word(8) ^ word(12)
}