dcrypt_algorithms/hash/sha1/
mod.rs

1//! SHA-1 hash function
2//!
3//! This module implements the SHA-1 hash function as specified in FIPS 180-4.
4//! Note: SHA-1 is considered cryptographically broken and should only be used
5//! for compatibility with existing systems.
6
7use crate::error::{Error, Result};
8use crate::hash::{Hash, HashAlgorithm, HashFunction};
9use crate::types::Digest;
10use byteorder::{BigEndian, ByteOrder};
11use zeroize::Zeroize;
12
13#[cfg(not(feature = "std"))]
14use alloc::vec::Vec;
15
16const SHA1_BLOCK_SIZE: usize = 64;
17const SHA1_OUTPUT_SIZE: usize = 20;
18
19/// Initial hash values for SHA-1
20const H0: [u32; 5] = [0x67452301, 0xEFCDAB89, 0x98BADCFE, 0x10325476, 0xC3D2E1F0];
21
22/// SHA-1 algorithm marker type
23pub enum Sha1Algorithm {}
24
25impl HashAlgorithm for Sha1Algorithm {
26    const OUTPUT_SIZE: usize = SHA1_OUTPUT_SIZE;
27    const BLOCK_SIZE: usize = SHA1_BLOCK_SIZE;
28    const ALGORITHM_ID: &'static str = "SHA-1";
29}
30
31/// SHA-1 hash function
32#[derive(Clone, Zeroize)]
33pub struct Sha1 {
34    /// Current hash state
35    h: [u32; 5],
36    /// Message buffer
37    buffer: [u8; SHA1_BLOCK_SIZE],
38    /// Bytes in buffer
39    buffer_len: usize,
40    /// Total message length in bits
41    total_len: u64,
42}
43
44impl Sha1 {
45    /// Creates a new SHA-1 hasher
46    pub fn new() -> Self {
47        Self {
48            h: H0,
49            buffer: [0u8; SHA1_BLOCK_SIZE],
50            buffer_len: 0,
51            total_len: 0,
52        }
53    }
54
55    /// Process a single block
56    fn process_block(&mut self, block: &[u8; SHA1_BLOCK_SIZE]) {
57        let mut w = [0u32; 80];
58        // Prepare the message schedule
59        for i in 0..16 {
60            w[i] = BigEndian::read_u32(&block[i * 4..]);
61        }
62        for i in 16..80 {
63            w[i] = (w[i - 3] ^ w[i - 8] ^ w[i - 14] ^ w[i - 16]).rotate_left(1);
64        }
65        // Initialize working variables
66        let mut a = self.h[0];
67        let mut b = self.h[1];
68        let mut c = self.h[2];
69        let mut d = self.h[3];
70        let mut e = self.h[4];
71        // Main loop
72        for (i, &word) in w.iter().enumerate().take(80) {
73            let (f, k) = if i < 20 {
74                ((b & c) | ((!b) & d), 0x5A827999)
75            } else if i < 40 {
76                (b ^ c ^ d, 0x6ED9EBA1)
77            } else if i < 60 {
78                ((b & c) | (b & d) | (c & d), 0x8F1BBCDC)
79            } else {
80                (b ^ c ^ d, 0xCA62C1D6)
81            };
82            let temp = a
83                .rotate_left(5)
84                .wrapping_add(f)
85                .wrapping_add(e)
86                .wrapping_add(k)
87                .wrapping_add(word);
88            e = d;
89            d = c;
90            c = b.rotate_left(30);
91            b = a;
92            a = temp;
93        }
94        // Update state
95        self.h[0] = self.h[0].wrapping_add(a);
96        self.h[1] = self.h[1].wrapping_add(b);
97        self.h[2] = self.h[2].wrapping_add(c);
98        self.h[3] = self.h[3].wrapping_add(d);
99        self.h[4] = self.h[4].wrapping_add(e);
100    }
101
102    /// Internal update implementation
103    fn update_internal(&mut self, data: &[u8]) -> Result<()> {
104        let mut data_idx = 0;
105
106        // Check for overflow in total_len calculation
107        let new_bits = (data.len() as u64).wrapping_mul(8);
108        self.total_len = self
109            .total_len
110            .checked_add(new_bits)
111            .ok_or(Error::Processing {
112                operation: "SHA-1",
113                details: "Message length overflow",
114            })?;
115
116        if self.buffer_len > 0 {
117            let copy_len = core::cmp::min(SHA1_BLOCK_SIZE - self.buffer_len, data.len());
118            self.buffer[self.buffer_len..self.buffer_len + copy_len]
119                .copy_from_slice(&data[..copy_len]);
120            self.buffer_len += copy_len;
121            data_idx += copy_len;
122
123            if self.buffer_len == SHA1_BLOCK_SIZE {
124                let mut block = [0u8; SHA1_BLOCK_SIZE];
125                block.copy_from_slice(&self.buffer);
126                self.process_block(&block);
127                self.buffer_len = 0;
128            }
129        }
130
131        while data_idx + SHA1_BLOCK_SIZE <= data.len() {
132            let mut block = [0u8; SHA1_BLOCK_SIZE];
133            block.copy_from_slice(&data[data_idx..data_idx + SHA1_BLOCK_SIZE]);
134            self.process_block(&block);
135            data_idx += SHA1_BLOCK_SIZE;
136        }
137
138        if data_idx < data.len() {
139            let remaining = data.len() - data_idx;
140            self.buffer[..remaining].copy_from_slice(&data[data_idx..]);
141            self.buffer_len = remaining;
142        }
143
144        Ok(())
145    }
146
147    /// Internal finalize implementation
148    fn finalize_internal(&mut self) -> Result<Hash> {
149        // Padding
150        let mut buffer = [0u8; SHA1_BLOCK_SIZE];
151        let mut buffer_idx = self.buffer_len;
152
153        buffer[..self.buffer_len].copy_from_slice(&self.buffer[..self.buffer_len]);
154        buffer[buffer_idx] = 0x80;
155        buffer_idx += 1;
156
157        if buffer_idx > SHA1_BLOCK_SIZE - 8 {
158            for byte in &mut buffer[buffer_idx..] {
159                *byte = 0;
160            }
161            self.process_block(&buffer);
162            buffer_idx = 0;
163        }
164
165        for byte in &mut buffer[buffer_idx..SHA1_BLOCK_SIZE - 8] {
166            *byte = 0;
167        }
168
169        BigEndian::write_u64(&mut buffer[SHA1_BLOCK_SIZE - 8..], self.total_len);
170        self.process_block(&buffer);
171
172        let mut result = Vec::with_capacity(SHA1_OUTPUT_SIZE);
173        for &word in &self.h {
174            result.extend_from_slice(&word.to_be_bytes());
175        }
176        Ok(result)
177    }
178}
179
180impl Default for Sha1 {
181    fn default() -> Self {
182        Self::new()
183    }
184}
185
186impl HashFunction for Sha1 {
187    type Algorithm = Sha1Algorithm;
188    type Output = Digest<SHA1_OUTPUT_SIZE>;
189
190    fn new() -> Self {
191        Sha1::new()
192    }
193
194    fn update(&mut self, data: &[u8]) -> Result<&mut Self> {
195        self.update_internal(data)?;
196        Ok(self)
197    }
198
199    fn finalize(&mut self) -> Result<Self::Output> {
200        let hash = self.finalize_internal()?;
201        let mut digest = [0u8; SHA1_OUTPUT_SIZE];
202        digest.copy_from_slice(&hash);
203        Ok(Digest::new(digest))
204    }
205
206    fn output_size() -> usize {
207        Self::Algorithm::OUTPUT_SIZE
208    }
209
210    fn block_size() -> usize {
211        Self::Algorithm::BLOCK_SIZE
212    }
213
214    fn name() -> String {
215        Self::Algorithm::ALGORITHM_ID.to_string()
216    }
217}
218
219#[cfg(test)]
220mod tests;