pub(crate) fn digest(data: &[u8]) -> [u8; 20] {
let mut state: [u32; 5] = [
0x6745_2301,
0xefcd_ab89,
0x98ba_dcfe,
0x1032_5476,
0xc3d2_e1f0,
];
let bit_length = (data.len() as u64).wrapping_mul(8);
let mut message = data.to_vec();
message.push(0x80);
while message.len() % 64 != 56 {
message.push(0);
}
message.extend_from_slice(&bit_length.to_be_bytes());
for block in message.as_chunks::<64>().0 {
let mut words = [0u32; 80];
for (index, word) in words.iter_mut().take(16).enumerate() {
let start = index * 4;
*word = u32::from_be_bytes([
block[start],
block[start + 1],
block[start + 2],
block[start + 3],
]);
}
for index in 16..80 {
words[index] =
(words[index - 3] ^ words[index - 8] ^ words[index - 14] ^ words[index - 16])
.rotate_left(1);
}
let [mut a, mut b, mut c, mut d, mut e] = state;
for (index, word) in words.iter().enumerate() {
let (f, k) = match index {
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;
}
state[0] = state[0].wrapping_add(a);
state[1] = state[1].wrapping_add(b);
state[2] = state[2].wrapping_add(c);
state[3] = state[3].wrapping_add(d);
state[4] = state[4].wrapping_add(e);
}
let mut out = [0u8; 20];
for (index, value) in state.iter().enumerate() {
out[index * 4..index * 4 + 4].copy_from_slice(&value.to_be_bytes());
}
out
}
#[cfg(test)]
mod tests {
use super::*;
fn hex(data: &[u8]) -> String {
let mut out = String::new();
for byte in digest(data) {
out.push(char::from_digit(u32::from(byte >> 4), 16).expect("nibble"));
out.push(char::from_digit(u32::from(byte & 0x0f), 16).expect("nibble"));
}
out
}
#[test]
fn the_published_test_vectors_match() {
assert_eq!(hex(b""), "da39a3ee5e6b4b0d3255bfef95601890afd80709");
assert_eq!(hex(b"abc"), "a9993e364706816aba3e25717850c26c9cd0d89d");
assert_eq!(
hex(b"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"),
"84983e441c3bd26ebaae4aa1f95129e5e54670f1"
);
assert_eq!(
hex(b"The quick brown fox jumps over the lazy dog"),
"2fd4e1c67a2d28fced849ee1bb76e7391b93eb12"
);
}
#[test]
fn a_longer_message_spanning_many_blocks_matches() {
let data = vec![b'a'; 1_000_000];
assert_eq!(hex(&data), "34aa973cd4c4daa4f61eeb2bdbad27316534016f");
}
}