1pub fn sha1(data: &[u8]) -> [u8; 20] {
34 let mut h: [u32; 5] = [0x6745_2301, 0xEFCD_AB89, 0x98BA_DCFE, 0x1032_5476, 0xC3D2_E1F0];
35
36 let bit_len: u64 = (data.len() as u64) * 8;
39 let mut buf: Vec<u8> = Vec::with_capacity(data.len() + 72);
40 buf.extend_from_slice(data);
41 buf.push(0x80);
42 while buf.len() % 64 != 56 {
43 buf.push(0);
44 }
45 buf.extend_from_slice(&bit_len.to_be_bytes());
46 debug_assert_eq!(buf.len() % 64, 0);
47
48 for chunk in buf.as_chunks::<64>().0 {
49 compress(&mut h, chunk);
50 }
51
52 let mut out = [0u8; 20];
53 for (i, &word) in h.iter().enumerate() {
54 out[i * 4..i * 4 + 4].copy_from_slice(&word.to_be_bytes());
55 }
56 out
57}
58
59fn compress(h: &mut [u32; 5], chunk: &[u8]) {
61 let mut w = [0u32; 80];
62 for (i, word) in chunk.as_chunks::<4>().0.iter().enumerate() {
63 w[i] = u32::from_be_bytes([word[0], word[1], word[2], word[3]]);
64 }
65 for i in 16..80 {
66 w[i] = (w[i - 3] ^ w[i - 8] ^ w[i - 14] ^ w[i - 16]).rotate_left(1);
67 }
68 let mut a = h[0];
69 let mut b = h[1];
70 let mut c = h[2];
71 let mut d = h[3];
72 let mut e = h[4];
73 for (i, &wi) in w.iter().enumerate() {
74 let (f, k) = match i {
75 0..=19 => ((b & c) | ((!b) & d), 0x5A82_7999),
76 20..=39 => (b ^ c ^ d, 0x6ED9_EBA1),
77 40..=59 => ((b & c) | (b & d) | (c & d), 0x8F1B_BCDC),
78 _ => (b ^ c ^ d, 0xCA62_C1D6),
79 };
80 let t = a.rotate_left(5).wrapping_add(f).wrapping_add(e).wrapping_add(k).wrapping_add(wi);
81 e = d;
82 d = c;
83 c = b.rotate_left(30);
84 b = a;
85 a = t;
86 }
87 h[0] = h[0].wrapping_add(a);
88 h[1] = h[1].wrapping_add(b);
89 h[2] = h[2].wrapping_add(c);
90 h[3] = h[3].wrapping_add(d);
91 h[4] = h[4].wrapping_add(e);
92}
93
94pub fn hex(digest: &[u8; 20]) -> [u8; 40] {
102 const HEX: &[u8; 16] = b"0123456789abcdef";
103 let mut out = [0u8; 40];
104 for (i, &byte) in digest.iter().enumerate() {
105 out[i * 2] = HEX[(byte >> 4) as usize];
106 out[i * 2 + 1] = HEX[(byte & 0x0f) as usize];
107 }
108 out
109}
110
111pub fn parse_hex(hex_str: &[u8]) -> Option<[u8; 20]> {
122 if hex_str.len() != 40 {
123 return None;
124 }
125 let mut out = [0u8; 20];
126 for (i, pair) in hex_str.as_chunks::<2>().0.iter().enumerate() {
127 let hi = hex_nibble(pair[0])?;
128 let lo = hex_nibble(pair[1])?;
129 out[i] = (hi << 4) | lo;
130 }
131 Some(out)
132}
133
134fn hex_nibble(b: u8) -> Option<u8> {
135 match b {
136 b'0'..=b'9' => Some(b - b'0'),
137 b'a'..=b'f' => Some(b - b'a' + 10),
138 b'A'..=b'F' => Some(b - b'A' + 10),
139 _ => None,
140 }
141}
142
143#[cfg(test)]
144mod tests {
145 use super::*;
146
147 #[test]
149 fn empty_string() {
150 let d = sha1(b"");
152 assert_eq!(hex(&d), *b"da39a3ee5e6b4b0d3255bfef95601890afd80709");
153 }
154
155 #[test]
156 fn abc() {
157 let d = sha1(b"abc");
159 assert_eq!(hex(&d), *b"a9993e364706816aba3e25717850c26c9cd0d89d");
160 }
161
162 #[test]
163 fn quick_brown_fox() {
164 let d = sha1(b"The quick brown fox jumps over the lazy dog");
167 assert_eq!(hex(&d), *b"2fd4e1c67a2d28fced849ee1bb76e7391b93eb12");
168 }
169
170 #[test]
171 fn quick_brown_fox_dot() {
172 let d = sha1(b"The quick brown fox jumps over the lazy cog");
175 assert_eq!(hex(&d), *b"de9f2c7fd25e1b3afad3e85a0bd17d9b100db4b3");
176 }
177
178 #[test]
179 fn fips180_56_byte_msg() {
180 let d = sha1(b"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq");
183 assert_eq!(hex(&d), *b"84983e441c3bd26ebaae4aa1f95129e5e54670f1");
184 }
185
186 #[test]
187 fn eight_byte_return_1() {
188 let d = sha1(b"return 1");
190 assert_eq!(hex(&d), *b"e0e1f9fabfc9d4800c877a703b823ac0578ff8db");
191 }
192
193 #[test]
194 fn four_byte_input() {
195 let d = sha1(b"1234");
197 assert_eq!(hex(&d), *b"7110eda4d09e062aa5e4a390b0a572ac0d2c0220");
198 }
199
200 #[test]
201 fn fips180_one_million_a() {
202 let data = vec![b'a'; 1_000_000];
204 let d = sha1(&data);
205 assert_eq!(hex(&d), *b"34aa973cd4c4daa4f61eeb2bdbad27316534016f");
206 }
207
208 #[test]
209 fn hex_round_trips_through_parse_hex() {
210 let d1 = sha1(b"kevy");
211 let h = hex(&d1);
212 let d2 = parse_hex(&h).expect("valid hex");
213 assert_eq!(d1, d2);
214 }
215
216 #[test]
217 fn parse_hex_rejects_wrong_length() {
218 assert!(parse_hex(b"too short").is_none());
219 assert!(parse_hex(&[b'a'; 41]).is_none());
220 }
221
222 #[test]
223 fn parse_hex_rejects_non_hex_chars() {
224 assert!(parse_hex(b"zzz3e364706816aba3e25717850c26c9cd0d89d").is_none());
225 }
226
227 #[test]
228 fn parse_hex_accepts_uppercase() {
229 let d1 = sha1(b"abc");
230 let lower = hex(&d1);
231 let upper: Vec<u8> = lower.iter().map(|&b| b.to_ascii_uppercase()).collect();
232 let d2 = parse_hex(&upper).expect("upper-hex valid");
233 assert_eq!(d1, d2);
234 }
235}