Skip to main content

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