pub fn sha1(data: &[u8]) -> [u8; 20] {
let mut h: [u32; 5] = [0x67452301, 0xEFCDAB89, 0x98BADCFE, 0x10325476, 0xC3D2E1F0];
let bit_len = (data.len() as u64).wrapping_mul(8);
let mut chunks = data.chunks_exact(64);
for block in &mut chunks {
process(&mut h, block.try_into().unwrap());
}
let rem = chunks.remainder();
let mut tail = [0u8; 128];
tail[..rem.len()].copy_from_slice(rem);
tail[rem.len()] = 0x80;
let tail_len = if rem.len() + 1 + 8 <= 64 { 64 } else { 128 };
tail[tail_len - 8..tail_len].copy_from_slice(&bit_len.to_be_bytes());
for block in tail[..tail_len].chunks_exact(64) {
process(&mut h, block.try_into().unwrap());
}
let mut out = [0u8; 20];
for (i, word) in h.iter().enumerate() {
out[i * 4..i * 4 + 4].copy_from_slice(&word.to_be_bytes());
}
out
}
fn process(h: &mut [u32; 5], block: &[u8; 64]) {
let mut w = [0u32; 80];
for i in 0..16 {
w[i] = u32::from_be_bytes(block[i * 4..i * 4 + 4].try_into().unwrap());
}
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] = *h;
for (i, &wi) in w.iter().enumerate() {
let (f, k) = match i {
0..=19 => ((b & c) | ((!b) & d), 0x5A827999),
20..=39 => (b ^ c ^ d, 0x6ED9EBA1),
40..=59 => ((b & c) | (b & d) | (c & d), 0x8F1BBCDC),
_ => (b ^ c ^ d, 0xCA62C1D6),
};
let tmp = a
.rotate_left(5)
.wrapping_add(f)
.wrapping_add(e)
.wrapping_add(k)
.wrapping_add(wi);
e = d;
d = c;
c = b.rotate_left(30);
b = a;
a = tmp;
}
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);
}
#[cfg(test)]
mod tests {
use super::sha1;
fn hex(d: &[u8]) -> String {
d.iter().map(|b| format!("{b:02x}")).collect()
}
#[test]
fn known_vectors() {
assert_eq!(hex(&sha1(b"")), "da39a3ee5e6b4b0d3255bfef95601890afd80709");
assert_eq!(
hex(&sha1(b"abc")),
"a9993e364706816aba3e25717850c26c9cd0d89d"
);
assert_eq!(
hex(&sha1(b"The quick brown fox jumps over the lazy dog")),
"2fd4e1c67a2d28fced849ee1bb76e7391b93eb12"
);
let long = vec![b'a'; 1000];
assert_eq!(
hex(&sha1(&long)),
"291e9a6c66994949b57ba5e650361e98fc36b1ba"
);
}
}