#[derive(Clone)]
pub struct Sha1 {
state: [u32; 5],
buf: [u8; 64],
buf_len: usize,
total: u64,
}
impl Default for Sha1 {
fn default() -> Self {
Self::new()
}
}
impl Sha1 {
pub const OUT_LEN: usize = 20;
pub const BLOCK_LEN: usize = 64;
pub const fn new() -> Sha1 {
Sha1 { state: [0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476, 0xc3d2e1f0], buf: [0; 64], buf_len: 0, total: 0 }
}
fn compress(state: &mut [u32; 5], block: &[u8]) {
let mut w = [0u32; 80];
for i in 0..16 {
w[i] = u32::from_be_bytes([block[4 * i], block[4 * i + 1], block[4 * i + 2], block[4 * i + 3]]);
}
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] = *state;
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 t = 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 = t;
}
for (s, v) in state.iter_mut().zip([a, b, c, d, e]) {
*s = s.wrapping_add(v);
}
}
pub fn update(&mut self, mut data: &[u8]) {
self.total = self.total.wrapping_add(data.len() as u64);
if self.buf_len > 0 {
let n = (64 - self.buf_len).min(data.len());
self.buf[self.buf_len..self.buf_len + n].copy_from_slice(&data[..n]);
self.buf_len += n;
data = &data[n..];
if self.buf_len == 64 {
let b = self.buf;
Self::compress(&mut self.state, &b);
self.buf_len = 0;
}
}
while data.len() >= 64 {
Self::compress(&mut self.state, &data[..64]);
data = &data[64..];
}
if !data.is_empty() {
self.buf[..data.len()].copy_from_slice(data);
self.buf_len = data.len();
}
}
pub fn finish(mut self) -> [u8; 20] {
let bits = self.total.wrapping_mul(8);
let mut pad = [0u8; 72];
pad[0] = 0x80;
let pad_len = if self.buf_len < 56 { 56 - self.buf_len } else { 120 - self.buf_len };
self.update(&pad[..pad_len]);
self.update(&bits.to_be_bytes());
debug_assert_eq!(self.buf_len, 0);
let mut out = [0u8; 20];
for (o, w) in out.chunks_mut(4).zip(self.state) {
o.copy_from_slice(&w.to_be_bytes());
}
out
}
pub fn digest(data: &[u8]) -> [u8; 20] {
let mut h = Sha1::new();
h.update(data);
h.finish()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::util::hex;
#[test]
fn fips_180_vectors() {
assert_eq!(hex(&Sha1::digest(b"")), "da39a3ee5e6b4b0d3255bfef95601890afd80709");
assert_eq!(hex(&Sha1::digest(b"abc")), "a9993e364706816aba3e25717850c26c9cd0d89d");
assert_eq!(
hex(&Sha1::digest(b"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq")),
"84983e441c3bd26ebaae4aa1f95129e5e54670f1"
);
let mut h = Sha1::new();
for _ in 0..1000 {
h.update(&[b'a'; 1000]);
}
assert_eq!(hex(&h.finish()), "34aa973cd4c4daa4f61eeb2bdbad27316534016f");
let msg: Vec<u8> = (0..130u8).collect();
let whole = Sha1::digest(&msg);
for cut in 0..msg.len() {
let mut h = Sha1::new();
h.update(&msg[..cut]);
h.update(&msg[cut..]);
assert_eq!(h.finish(), whole);
}
}
}