use core::convert::TryFrom;
const K1: u32 = 0x5a827999;
const K2: u32 = 0x6ed9eba1;
const K3: u32 = 0x8f1bbcdc;
const K4: u32 = 0xca62c1d6;
const INITIAL_STATE: [u32; 5] = [ 0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476, 0xc3d2e1f0 ];
pub fn sha1<T: AsRef<[u8]>>(data: T) -> [u8; Sha1::DIGEST_LEN] {
Sha1::oneshot(data)
}
#[derive(Clone)]
pub struct Sha1 {
buffer: [u8; Self::BLOCK_LEN],
state: [u32; 5],
len: u64, offset: usize,
}
impl Sha1 {
pub const BLOCK_LEN: usize = 64;
pub const DIGEST_LEN: usize = 20;
const BLOCK_LEN_BITS: u64 = Self::BLOCK_LEN as u64 * 8;
const MLEN_SIZE: usize = core::mem::size_of::<u64>();
const MLEN_SIZE_BITS: u64 = Self::MLEN_SIZE as u64 * 8;
const MAX_PAD_LEN: usize = Self::BLOCK_LEN + Self::MLEN_SIZE as usize;
pub fn new() -> Self {
Self {
buffer: [0u8; 64],
state: INITIAL_STATE,
len: 0,
offset: 0,
}
}
pub fn update(&mut self, data: &[u8]) {
let mut i = 0usize;
while i < data.len() {
if self.offset < Self::BLOCK_LEN {
self.buffer[self.offset] = data[i];
self.offset += 1;
i += 1;
}
if self.offset == Self::BLOCK_LEN {
transform(&mut self.state, &self.buffer);
self.offset = 0;
self.len += Self::BLOCK_LEN as u64;
}
}
}
pub fn finalize(mut self) -> [u8; Self::DIGEST_LEN] {
let mlen = self.len + self.offset as u64; let mlen_bits = mlen * 8;
let plen_bits = Self::BLOCK_LEN_BITS - (mlen_bits + Self::MLEN_SIZE_BITS + 1) % Self::BLOCK_LEN_BITS + 1;
let plen = plen_bits / 8;
debug_assert_eq!(plen_bits % 8, 0);
debug_assert!(plen > 1);
debug_assert_eq!((mlen + plen + Self::MLEN_SIZE as u64) % Self::BLOCK_LEN as u64, 0);
let plen = usize::try_from(plen).unwrap();
let mut padding: [u8; Self::MAX_PAD_LEN] = [0u8; Self::MAX_PAD_LEN];
padding[0] = 0x80;
let mlen_octets: [u8; Self::MLEN_SIZE] = mlen_bits.to_be_bytes();
padding[plen..plen + Self::MLEN_SIZE].copy_from_slice(&mlen_octets);
let data = &padding[..plen + Self::MLEN_SIZE];
self.update(data);
debug_assert_eq!(self.offset, 0);
let mut output = [0u8; Self::DIGEST_LEN];
output[ 0.. 4].copy_from_slice(&self.state[0].to_be_bytes());
output[ 4.. 8].copy_from_slice(&self.state[1].to_be_bytes());
output[ 8..12].copy_from_slice(&self.state[2].to_be_bytes());
output[12..16].copy_from_slice(&self.state[3].to_be_bytes());
output[16..20].copy_from_slice(&self.state[4].to_be_bytes());
output
}
pub fn oneshot<T: AsRef<[u8]>>(data: T) -> [u8; Self::DIGEST_LEN] {
let mut m = Self::new();
m.update(data.as_ref());
m.finalize()
}
}
#[inline]
fn transform(state: &mut [u32; 5], block: &[u8]) {
debug_assert_eq!(state.len(), 5);
debug_assert_eq!(block.len(), Sha1::BLOCK_LEN);
let mut w = [0u32; 80];
for i in 0..16 {
w[i] = u32::from_be_bytes([
block[i*4 + 0], block[i*4 + 1],
block[i*4 + 2], block[i*4 + 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 = state[0];
let mut b = state[1];
let mut c = state[2];
let mut d = state[3];
let mut e = state[4];
for i in 0..20 {
let t = a.rotate_left(5)
.wrapping_add( (b & c) ^ (!b & d) )
.wrapping_add(e)
.wrapping_add(K1)
.wrapping_add(w[i]);
e = d;
d = c;
c = b.rotate_left(30);
b = a;
a = t;
}
for i in 20..40 {
let t = a.rotate_left(5)
.wrapping_add(b ^ c ^d)
.wrapping_add(e)
.wrapping_add(K2)
.wrapping_add(w[i]);
e = d;
d = c;
c = b.rotate_left(30);
b = a;
a = t;
}
for i in 40..60 {
let t = a.rotate_left(5)
.wrapping_add((b & c) ^ (b & d) ^ (c & d))
.wrapping_add(e)
.wrapping_add(K3)
.wrapping_add(w[i]);
e = d;
d = c;
c = b.rotate_left(30);
b = a;
a = t;
}
for i in 60..80 {
let t = a.rotate_left(5)
.wrapping_add(b ^ c ^ d)
.wrapping_add(e)
.wrapping_add(K4)
.wrapping_add(w[i]);
e = d;
d = c;
c = b.rotate_left(30);
b = a;
a = t;
}
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);
}
#[test]
fn test_sha1_one_block_message() {
let msg = b"abc";
let digest = [169, 153, 62, 54, 71, 6, 129, 106, 186, 62, 37, 113, 120, 80, 194, 108, 156, 208, 216, 157];
assert_eq!(sha1(&msg[..]), digest);
}
#[test]
fn test_sha1_multi_block_message() {
let msg = b"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq";
let digest = [132, 152, 62, 68, 28, 59, 210, 110, 186, 174, 74, 161, 249, 81, 41, 229, 229, 70, 112, 241];
assert_eq!(sha1(&msg[..]), digest);
}
#[test]
fn test_sha1_long_message() {
let msg = vec![b'a'; 1000_000];
let digest = [52, 170, 151, 60, 212, 196, 218, 164, 246, 30, 235, 43, 219, 173, 39, 49, 101, 52, 1, 111];
assert_eq!(sha1(&msg), digest);
}