use crate::compat;
use crate::encoding::binary::{ByteOrder, BIG_ENDIAN};
use crate::hash::{self, Hash};
use crate::math::bits::rotate_left32;
pub const SIZE: usize = 20;
pub const BLOCK_SIZE: usize = 64;
const CHUNK: usize = 64;
const INIT0: u32 = 0x67452301;
const INIT1: u32 = 0xEFCDAB89;
const INIT2: u32 = 0x98BADCFE;
const INIT3: u32 = 0x10325476;
const INIT4: u32 = 0xC3D2E1F0;
#[derive(Copy, Clone)]
pub struct Digest {
h: [u32; 5],
x: [u8; CHUNK],
nx: usize,
len: u64,
}
impl Default for Digest {
fn default() -> Self {
Self::new()
}
}
impl Digest {
pub fn new() -> Self {
let mut d = Self {
h: [0; 5],
x: [0; CHUNK],
nx: 0,
len: 0,
};
d.reset();
d
}
pub fn write(&mut self, p: &[u8]) {
let mut p = p;
self.len += p.len() as u64;
if self.nx > 0 {
let n = compat::copy(&mut self.x[self.nx..], p);
self.nx += n;
if self.nx == CHUNK {
block_generic(&mut self.h, &self.x[..]);
self.nx = 0
}
p = &p[n..];
}
if p.len() >= CHUNK {
let n = p.len() & !(CHUNK - 1);
block_generic(&mut self.h, &p[..n]);
p = &p[n..];
}
if !p.is_empty() {
self.nx = compat::copy(&mut self.x, p);
}
}
fn checksum(&mut self) -> [u8; SIZE] {
let len = self.len;
let mut tmp = [0; 64 + 8]; tmp[0] = 0x80_u8;
let t: u64 = if len % 64 < 56 {
56 - len % 64
} else {
64 + 56 - len % 64
};
let len = len << 3;
let padlen = &mut tmp[0..(t + 8) as usize];
BIG_ENDIAN.put_uint64(&mut padlen[(t as usize)..], len);
self.write(padlen);
if self.nx != 0 {
panic!("self.nx != 0");
}
let mut digest = [0; SIZE];
BIG_ENDIAN.put_uint32(&mut digest[0..], self.h[0]);
BIG_ENDIAN.put_uint32(&mut digest[4..], self.h[1]);
BIG_ENDIAN.put_uint32(&mut digest[8..], self.h[2]);
BIG_ENDIAN.put_uint32(&mut digest[12..], self.h[3]);
BIG_ENDIAN.put_uint32(&mut digest[16..], self.h[4]);
digest
}
}
impl hash::Hash for Digest {
fn reset(&mut self) {
self.h[0] = INIT0;
self.h[1] = INIT1;
self.h[2] = INIT2;
self.h[3] = INIT3;
self.h[4] = INIT4;
self.nx = 0;
self.len = 0;
}
fn size(&self) -> usize {
SIZE
}
fn block_size(&self) -> usize {
BLOCK_SIZE
}
fn sum(&self, b: &[u8]) -> Vec<u8> {
let mut d0 = *self;
let hash = d0.checksum();
let mut res = b.to_vec();
res.extend_from_slice(&hash);
res
}
}
impl std::io::Write for Digest {
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
Digest::write(self, buf);
Ok(buf.len())
}
fn flush(&mut self) -> std::io::Result<()> {
Ok(())
}
}
pub fn sum(data: &[u8]) -> [u8; SIZE] {
let mut d = Digest::new();
d.write(data);
d.checksum()
}
const _K0: u32 = 0x5A827999;
const _K1: u32 = 0x6ED9EBA1;
const _K2: u32 = 0x8F1BBCDC;
const _K3: u32 = 0xCA62C1D6;
fn block_generic(h: &mut [u32; 5], p: &[u8]) {
let mut p = p;
let mut w = [0_u32; 16];
let (mut h0, mut h1, mut h2, mut h3, mut h4) = (h[0], h[1], h[2], h[3], h[4]);
while p.len() >= CHUNK {
#[allow(clippy::needless_range_loop)]
for i in 0..16 {
let j = i * 4;
w[i] = (p[j] as u32) << 24
| (p[j + 1] as u32) << 16
| (p[j + 2] as u32) << 8
| (p[j + 3] as u32)
}
let (mut a, mut b, mut c, mut d, mut e) = (h0, h1, h2, h3, h4);
for i in 0_usize..16 {
let f = (b & c) | ((!b) & d);
let t = rotate_left32(a, 5)
.wrapping_add(f)
.wrapping_add(e)
.wrapping_add(w[i & 0xf])
.wrapping_add(_K0);
(a, b, c, d, e) = (t, a, rotate_left32(b, 30), c, d);
}
for i in 16_usize..20 {
let tmp = w[(i - 3) & 0xf] ^ w[(i - 8) & 0xf] ^ w[(i - 14) & 0xf] ^ w[(i) & 0xf];
w[i & 0xf] = (tmp << 1) | (tmp >> (32 - 1));
let f = (b & c) | ((!b) & d);
let t = rotate_left32(a, 5)
.wrapping_add(f)
.wrapping_add(e)
.wrapping_add(w[i & 0xf])
.wrapping_add(_K0);
(a, b, c, d, e) = (t, a, rotate_left32(b, 30), c, d);
}
for i in 20_usize..40 {
let tmp = w[(i - 3) & 0xf] ^ w[(i - 8) & 0xf] ^ w[(i - 14) & 0xf] ^ w[(i) & 0xf];
w[i & 0xf] = (tmp << 1) | (tmp >> (32 - 1));
let f = b ^ c ^ d;
let t = rotate_left32(a, 5)
.wrapping_add(f)
.wrapping_add(e)
.wrapping_add(w[i & 0xf])
.wrapping_add(_K1);
(a, b, c, d, e) = (t, a, rotate_left32(b, 30), c, d);
}
for i in 40_usize..60 {
let tmp = w[(i - 3) & 0xf] ^ w[(i - 8) & 0xf] ^ w[(i - 14) & 0xf] ^ w[(i) & 0xf];
w[i & 0xf] = (tmp << 1) | (tmp >> (32 - 1));
let f = ((b | c) & d) | (b & c);
let t = rotate_left32(a, 5)
.wrapping_add(f)
.wrapping_add(e)
.wrapping_add(w[i & 0xf])
.wrapping_add(_K2);
(a, b, c, d, e) = (t, a, rotate_left32(b, 30), c, d);
}
for i in 60_usize..80 {
let tmp = w[(i - 3) & 0xf] ^ w[(i - 8) & 0xf] ^ w[(i - 14) & 0xf] ^ w[(i) & 0xf];
w[i & 0xf] = (tmp << 1) | (tmp >> (32 - 1));
let f = b ^ c ^ d;
let t = rotate_left32(a, 5)
.wrapping_add(f)
.wrapping_add(e)
.wrapping_add(w[i & 0xf])
.wrapping_add(_K3);
(a, b, c, d, e) = (t, a, rotate_left32(b, 30), c, d);
}
h0 = h0.wrapping_add(a);
h1 = h1.wrapping_add(b);
h2 = h2.wrapping_add(c);
h3 = h3.wrapping_add(d);
h4 = h4.wrapping_add(e);
p = &p[CHUNK..];
}
(h[0], h[1], h[2], h[3], h[4]) = (h0, h1, h2, h3, h4);
}