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