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