#[allow(
clippy::indexing_slicing,
clippy::cast_possible_truncation,
clippy::many_single_char_names,
reason = "the schedule and chunk indices are bounded by the fixed 80-word/64-byte block sizes; \
the casts isolate the intended low bits; the single-letter names are the digest's own \
working-variable notation"
)]
#[must_use]
pub fn hex(data: &[u8]) -> String {
const HEX: [u8; 16] = *b"0123456789abcdef";
let mut h: [u32; 5] = [
0x6745_2301,
0xEFCD_AB89,
0x98BA_DCFE,
0x1032_5476,
0xC3D2_E1F0,
];
let bit_len = (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_len.to_be_bytes());
for block in message.chunks_exact(64) {
let mut w = [0u32; 80];
for (index, word) in block.chunks_exact(4).enumerate() {
w[index] = u32::from_be_bytes(word.try_into().unwrap_or([0; 4]));
}
for index in 16..80 {
w[index] = (w[index - 3] ^ w[index - 8] ^ w[index - 14] ^ w[index - 16]).rotate_left(1);
}
let [mut a, mut b, mut c, mut d, mut e] = h;
for (index, &word) in w.iter().enumerate() {
let (f, k) = match index {
0..=19 => ((b & c) | ((!b) & d), 0x5A82_7999u32),
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;
}
h[0] = h[0].wrapping_add(a);
h[1] = h[1].wrapping_add(b);
h[2] = h[2].wrapping_add(c);
h[3] = h[3].wrapping_add(d);
h[4] = h[4].wrapping_add(e);
}
let mut out = String::with_capacity(40);
for word in h {
for byte in word.to_be_bytes() {
out.push(HEX[usize::from(byte >> 4)] as char);
out.push(HEX[usize::from(byte & 0x0f)] as char);
}
}
out
}
#[cfg(test)]
mod tests {
use super::hex;
#[test]
fn matches_known_vectors() {
assert_eq!(hex(b""), "da39a3ee5e6b4b0d3255bfef95601890afd80709");
assert_eq!(hex(b"abc"), "a9993e364706816aba3e25717850c26c9cd0d89d");
assert_eq!(
hex(b"The quick brown fox jumps over the lazy dog"),
"2fd4e1c67a2d28fced849ee1bb76e7391b93eb12"
);
}
}