1#![allow(clippy::indexing_slicing)]
11
12use crate::buf::{Buffer, FixedBuf, RoundBuffer};
13use crate::hash::{HashAlgorithm, HashDigest};
14use core::ops::{BitAnd, BitOr, BitXor, Not};
15use irox_bits::{Bits, Error, MutBits};
16
17pub const BLOCK_SIZE: usize = 64;
18pub const OUTPUT_SIZE: usize = 20;
19
20#[derive(Clone)]
26pub struct SHA1 {
27 written_length: u64,
28 buf: RoundBuffer<BLOCK_SIZE, u8>,
29
30 h0: u32,
31 h1: u32,
32 h2: u32,
33 h3: u32,
34 h4: u32,
35}
36
37impl Default for SHA1 {
38 fn default() -> Self {
39 Self {
40 h0: 0x67452301,
41 h1: 0xEFCDAB89,
42 h2: 0x98BADCFE,
43 h3: 0x10325476,
44 h4: 0xC3D2E1F0,
45 written_length: 0,
46 buf: RoundBuffer::default(),
47 }
48 }
49}
50
51impl SHA1 {
52 #[must_use]
53 pub fn new() -> Self {
54 Self::default()
55 }
56 fn try_chomp(&mut self) {
57 if self.buf.len() < BLOCK_SIZE {
58 return;
59 }
60 let mut words: FixedBuf<80, u32> = FixedBuf::default();
61 for i in 0..16 {
62 words[i] = self.buf.read_be_u32().unwrap_or_default();
63 }
64 for i in 16..=79 {
65 let w1 = words.get(i - 3).copied().unwrap_or_default();
66 let w2 = words.get(i - 8).copied().unwrap_or_default();
67 let w3 = words.get(i - 14).copied().unwrap_or_default();
68 let w4 = words.get(i - 16).copied().unwrap_or_default();
69 words[i] = w1.bitxor(w2).bitxor(w3).bitxor(w4).rotate_left(1);
70 }
71
72 let mut a = self.h0;
73 let mut b = self.h1;
74 let mut c = self.h2;
75 let mut d = self.h3;
76 let mut e = self.h4;
77
78 for i in 0..=79 {
79 let mut f: u32 = 0;
80 let mut k: u32 = 0;
81 match i {
82 0..=19 => {
83 f = b.bitand(c).bitxor(b.not().bitand(d));
84 k = 0x5A827999;
85 }
86 20..=39 => {
87 f = b.bitxor(c).bitxor(d);
88 k = 0x6ED9EBA1;
89 }
90 40..=59 => {
91 f = b.bitand(c).bitor(b.bitand(d)).bitor(c.bitand(d));
92 k = 0x8F1BBCDC;
93 }
94 60..=79 => {
95 f = b.bitxor(c).bitxor(d);
96 k = 0xCA62C1D6;
97 }
98 _ => {
99 }
101 }
102 let w = words.get(i as usize).copied().unwrap_or_default();
103 let temp = a
104 .rotate_left(5)
105 .wrapping_add(f)
106 .wrapping_add(e)
107 .wrapping_add(k)
108 .wrapping_add(w);
109 e = d;
110 d = c;
111 c = b.rotate_left(30);
112 b = a;
113 a = temp;
114 }
115 self.h0 = self.h0.wrapping_add(a);
116 self.h1 = self.h1.wrapping_add(b);
117 self.h2 = self.h2.wrapping_add(c);
118 self.h3 = self.h3.wrapping_add(d);
119 self.h4 = self.h4.wrapping_add(e);
120 }
121 pub fn finish(mut self) -> [u8; OUTPUT_SIZE] {
124 let mut modlen64 = (self.written_length & 0x3F) as usize;
125 let mut pad: usize = 0;
126 if modlen64 >= 56 {
127 pad += BLOCK_SIZE - modlen64;
129 modlen64 = 0;
130 }
131 pad += 56 - modlen64;
132 let _ = self.buf.push_back(0x80);
133 pad -= 1;
134 for _ in 0..pad {
135 self.try_chomp();
136 let _ = self.buf.push_back(0);
137 }
138 let _ = self.buf.write_be_u64(self.written_length << 3);
139 self.try_chomp();
140 let mut out: [u8; OUTPUT_SIZE] = [0; OUTPUT_SIZE];
141 let mut v = out.as_mut_slice();
142 let _ = v.write_be_u32(self.h0);
143 let _ = v.write_be_u32(self.h1);
144 let _ = v.write_be_u32(self.h2);
145 let _ = v.write_be_u32(self.h3);
146 let _ = v.write_be_u32(self.h4);
147 out
148 }
149
150 pub fn write(&mut self, bytes: &[u8]) {
153 for b in bytes {
154 let _ = self.buf.push_back(*b);
155 self.written_length += 1;
156 self.try_chomp();
157 }
158 }
159
160 pub fn hash(mut self, bytes: &[u8]) -> [u8; OUTPUT_SIZE] {
163 self.write(bytes);
164 self.finish()
165 }
166}
167
168impl MutBits for SHA1 {
169 fn write_u8(&mut self, val: u8) -> Result<(), Error> {
170 self.write(&[val]);
171 Ok(())
172 }
173}
174
175impl HashDigest<BLOCK_SIZE, OUTPUT_SIZE> for SHA1 {
176 fn write(&mut self, bytes: &[u8]) {
177 SHA1::write(self, bytes)
178 }
179
180 fn hash(self, bytes: &[u8]) -> [u8; OUTPUT_SIZE] {
181 SHA1::hash(self, bytes)
182 }
183
184 fn finish(self) -> [u8; OUTPUT_SIZE] {
185 SHA1::finish(self)
186 }
187
188 fn algorithm() -> HashAlgorithm {
189 HashAlgorithm::SHA1
190 }
191}
192
193#[cfg(test)]
194mod test {
195 use crate::hash::sha1::SHA1;
196 use crate::hex::from_hex_into;
197 use irox_bits::Error;
198
199 #[test]
200 pub fn test_sha1() -> Result<(), Error> {
201 let tests = [
202 ("DA39A3EE 5E6B4B0D 3255BFEF 95601890 AFD80709", ""),
203 ("86F7E437 FAA5A7FC E15D1DDC B9EAEAEA 377667B8", "a"),
204 ("A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D", "abc"),
205 (
206 "C12252CE DA8BE899 4D5FA029 0A47231C 1D16AAE3",
207 "message digest",
208 ),
209 (
210 "32D10C7B 8CF96570 CA04CE37 F2A19D84 240D3A89",
211 "abcdefghijklmnopqrstuvwxyz",
212 ),
213 (
214 "761C457B F73B14D2 7E9E9265 C46F4B4D DA11F940",
215 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789",
216 ),
217 (
218 "CF0800F7 644ACE3C B4C3FA33 388D3BA0 EA3C8B6E",
219 "0123456789012345678901234567890123456789012345678901234567890123",
220 ),
221 (
222 "50ABF570 6A150990 A08B2C5E A40FA0E5 85554732",
223 "12345678901234567890123456789012345678901234567890123456789012345678901234567890",
224 ),
225 (
226 "84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1",
227 "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq",
228 ),
229 (
230 "2FD4E1C6 7A2D28FC ED849EE1 BB76E739 1B93EB12",
231 "The quick brown fox jumps over the lazy dog",
232 ),
233 (
234 "DE9F2C7F D25E1B3A FAD3E85A 0BD17D9B 100DB4B3",
235 "The quick brown fox jumps over the lazy cog",
236 ),
237 (
238 "408D9438 4216F890 FF7A0C35 28E8BED1 E0B01621",
239 "The quick brown fox jumps over the lazy dog.",
240 ),
241 ];
242 for (hash, st) in tests {
243 let mut bbuf: [u8; 1024] = [0; 1024];
244 let mut buf = bbuf.as_mut_slice();
245 let wrote = from_hex_into(hash, &mut buf)?;
246 let hash = SHA1::default().hash(st.as_bytes());
247 assert_eq!(&bbuf[0..wrote], &hash);
248 }
249
250 Ok(())
251 }
252}