use crate::error::{Error, Result};
use crate::hash::{Hash, HashAlgorithm, HashFunction};
use crate::types::Digest;
use byteorder::{BigEndian, ByteOrder};
use zeroize::Zeroize;
#[cfg(not(feature = "std"))]
use alloc::vec::Vec;
const SHA1_BLOCK_SIZE: usize = 64;
const SHA1_OUTPUT_SIZE: usize = 20;
const H0: [u32; 5] = [0x67452301, 0xEFCDAB89, 0x98BADCFE, 0x10325476, 0xC3D2E1F0];
pub enum Sha1Algorithm {}
impl HashAlgorithm for Sha1Algorithm {
const OUTPUT_SIZE: usize = SHA1_OUTPUT_SIZE;
const BLOCK_SIZE: usize = SHA1_BLOCK_SIZE;
const ALGORITHM_ID: &'static str = "SHA-1";
}
#[derive(Clone, Zeroize)]
pub struct Sha1 {
h: [u32; 5],
buffer: [u8; SHA1_BLOCK_SIZE],
buffer_len: usize,
total_len: u64,
}
impl Sha1 {
pub fn new() -> Self {
Self {
h: H0,
buffer: [0u8; SHA1_BLOCK_SIZE],
buffer_len: 0,
total_len: 0,
}
}
fn process_block(&mut self, block: &[u8; SHA1_BLOCK_SIZE]) {
let mut w = [0u32; 80];
for i in 0..16 {
w[i] = BigEndian::read_u32(&block[i * 4..]);
}
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 = self.h[0];
let mut b = self.h[1];
let mut c = self.h[2];
let mut d = self.h[3];
let mut e = self.h[4];
for (i, &word) in w.iter().enumerate().take(80) {
let (f, k) = if i < 20 {
((b & c) | ((!b) & d), 0x5A827999)
} else if i < 40 {
(b ^ c ^ d, 0x6ED9EBA1)
} else if i < 60 {
((b & c) | (b & d) | (c & d), 0x8F1BBCDC)
} else {
(b ^ c ^ d, 0xCA62C1D6)
};
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;
}
self.h[0] = self.h[0].wrapping_add(a);
self.h[1] = self.h[1].wrapping_add(b);
self.h[2] = self.h[2].wrapping_add(c);
self.h[3] = self.h[3].wrapping_add(d);
self.h[4] = self.h[4].wrapping_add(e);
}
fn update_internal(&mut self, data: &[u8]) -> Result<()> {
let mut data_idx = 0;
let new_bits = (data.len() as u64).wrapping_mul(8);
self.total_len = self
.total_len
.checked_add(new_bits)
.ok_or(Error::Processing {
operation: "SHA-1",
details: "Message length overflow",
})?;
if self.buffer_len > 0 {
let copy_len = core::cmp::min(SHA1_BLOCK_SIZE - self.buffer_len, data.len());
self.buffer[self.buffer_len..self.buffer_len + copy_len]
.copy_from_slice(&data[..copy_len]);
self.buffer_len += copy_len;
data_idx += copy_len;
if self.buffer_len == SHA1_BLOCK_SIZE {
let mut block = [0u8; SHA1_BLOCK_SIZE];
block.copy_from_slice(&self.buffer);
self.process_block(&block);
self.buffer_len = 0;
}
}
while data_idx + SHA1_BLOCK_SIZE <= data.len() {
let mut block = [0u8; SHA1_BLOCK_SIZE];
block.copy_from_slice(&data[data_idx..data_idx + SHA1_BLOCK_SIZE]);
self.process_block(&block);
data_idx += SHA1_BLOCK_SIZE;
}
if data_idx < data.len() {
let remaining = data.len() - data_idx;
self.buffer[..remaining].copy_from_slice(&data[data_idx..]);
self.buffer_len = remaining;
}
Ok(())
}
fn finalize_internal(&mut self) -> Result<Hash> {
let mut buffer = [0u8; SHA1_BLOCK_SIZE];
let mut buffer_idx = self.buffer_len;
buffer[..self.buffer_len].copy_from_slice(&self.buffer[..self.buffer_len]);
buffer[buffer_idx] = 0x80;
buffer_idx += 1;
if buffer_idx > SHA1_BLOCK_SIZE - 8 {
for byte in &mut buffer[buffer_idx..] {
*byte = 0;
}
self.process_block(&buffer);
buffer_idx = 0;
}
for byte in &mut buffer[buffer_idx..SHA1_BLOCK_SIZE - 8] {
*byte = 0;
}
BigEndian::write_u64(&mut buffer[SHA1_BLOCK_SIZE - 8..], self.total_len);
self.process_block(&buffer);
let mut result = Vec::with_capacity(SHA1_OUTPUT_SIZE);
for &word in &self.h {
result.extend_from_slice(&word.to_be_bytes());
}
Ok(result)
}
}
impl Default for Sha1 {
fn default() -> Self {
Self::new()
}
}
impl HashFunction for Sha1 {
type Algorithm = Sha1Algorithm;
type Output = Digest<SHA1_OUTPUT_SIZE>;
fn new() -> Self {
Sha1::new()
}
fn update(&mut self, data: &[u8]) -> Result<&mut Self> {
self.update_internal(data)?;
Ok(self)
}
fn finalize(&mut self) -> Result<Self::Output> {
let hash = self.finalize_internal()?;
let mut digest = [0u8; SHA1_OUTPUT_SIZE];
digest.copy_from_slice(&hash);
Ok(Digest::new(digest))
}
fn output_size() -> usize {
Self::Algorithm::OUTPUT_SIZE
}
fn block_size() -> usize {
Self::Algorithm::BLOCK_SIZE
}
fn name() -> String {
Self::Algorithm::ALGORITHM_ID.to_string()
}
}
#[cfg(test)]
mod tests;