const S: [u32; 64] = [
7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9,
14, 20, 5, 9, 14, 20, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 6, 10, 15,
21, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21,
];
pub(crate) fn digest(data: &[u8]) -> [u8; 16] {
const K: [u32; 64] = [
0xd76a_a478,
0xe8c7_b756,
0x2420_70db,
0xc1bd_ceee,
0xf57c_0faf,
0x4787_c62a,
0xa830_4613,
0xfd46_9501,
0x6980_98d8,
0x8b44_f7af,
0xffff_5bb1,
0x895c_d7be,
0x6b90_1122,
0xfd98_7193,
0xa679_438e,
0x49b4_0821,
0xf61e_2562,
0xc040_b340,
0x265e_5a51,
0xe9b6_c7aa,
0xd62f_105d,
0x0244_1453,
0xd8a1_e681,
0xe7d3_fbc8,
0x21e1_cde6,
0xc337_07d6,
0xf4d5_0d87,
0x455a_14ed,
0xa9e3_e905,
0xfcef_a3f8,
0x676f_02d9,
0x8d2a_4c8a,
0xfffa_3942,
0x8771_f681,
0x6d9d_6122,
0xfde5_380c,
0xa4be_ea44,
0x4bde_cfa9,
0xf6bb_4b60,
0xbebf_bc70,
0x289b_7ec6,
0xeaa1_27fa,
0xd4ef_3085,
0x0488_1d05,
0xd9d4_d039,
0xe6db_99e5,
0x1fa2_7cf8,
0xc4ac_5665,
0xf429_2244,
0x432a_ff97,
0xab94_23a7,
0xfc93_a039,
0x655b_59c3,
0x8f0c_cc92,
0xffef_f47d,
0x8584_5dd1,
0x6fa8_7e4f,
0xfe2c_e6e0,
0xa301_4314,
0x4e08_11a1,
0xf753_7e82,
0xbd3a_f235,
0x2ad7_d2bb,
0xeb86_d391,
];
let mut state: [u32; 4] = [0x6745_2301, 0xefcd_ab89, 0x98ba_dcfe, 0x1032_5476];
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_le_bytes());
for block in message.as_chunks::<64>().0 {
let mut m = [0u32; 16];
for (index, word) in m.iter_mut().enumerate() {
let start = index * 4;
*word = u32::from_le_bytes([
block[start],
block[start + 1],
block[start + 2],
block[start + 3],
]);
}
let [mut a, mut b, mut c, mut d] = state;
for index in 0..64 {
let (f, g) = match index {
0..=15 => ((b & c) | ((!b) & d), index),
16..=31 => ((d & b) | ((!d) & c), (5 * index + 1) % 16),
32..=47 => (b ^ c ^ d, (3 * index + 5) % 16),
_ => (c ^ (b | (!d)), (7 * index) % 16),
};
let temp = d;
d = c;
c = b;
let sum = a.wrapping_add(f).wrapping_add(K[index]).wrapping_add(m[g]);
b = b.wrapping_add(sum.rotate_left(S[index]));
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);
}
let mut out = [0u8; 16];
for (chunk, word) in out.as_chunks_mut::<4>().0.iter_mut().zip(state) {
chunk.copy_from_slice(&word.to_le_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""), "d41d8cd98f00b204e9800998ecf8427e");
assert_eq!(hex(b"abc"), "900150983cd24fb0d6963f7d28e17f72");
assert_eq!(hex(b"message digest"), "f96b697d7cb7938d525a2f31aaf161d0");
assert_eq!(
hex(b"abcdefghijklmnopqrstuvwxyz"),
"c3fcd3d76192e4007dfb496cca67e13b"
);
assert_eq!(
hex(b"The quick brown fox jumps over the lazy dog"),
"9e107d9d372bb6826bd81d3542a419d6"
);
}
#[test]
fn a_longer_message_spanning_many_blocks_matches() {
let data =
b"12345678901234567890123456789012345678901234567890123456789012345678901234567890";
assert_eq!(hex(data), "57edf4a22be3c955ac49da2e2107b67a");
}
}