buup/transformers/
sha1_hash.rs1use crate::{Transform, TransformError, TransformerCategory};
2
3const H0: u32 = 0x67452301;
5const H1: u32 = 0xEFCDAB89;
6const H2: u32 = 0x98BADCFE;
7const H3: u32 = 0x10325476;
8const H4: u32 = 0xC3D2E1F0;
9
10#[derive(Debug, Clone, Copy, PartialEq, Eq)]
12pub struct Sha1Hash;
13
14impl Sha1Hash {
18 fn pad_message(message: &[u8]) -> Vec<u8> {
20 let message_len_bits = (message.len() as u64) * 8;
21 let mut padded = message.to_vec();
22 padded.push(0x80); while padded.len() % 64 != 56 {
26 padded.push(0x00);
27 }
28
29 padded.extend_from_slice(&message_len_bits.to_be_bytes());
31
32 padded
33 }
34
35 fn process_block(h: &mut [u32; 5], block: &[u8]) {
37 assert_eq!(block.len(), 64);
38
39 let mut w = [0u32; 80];
40 for (i, chunk) in block.chunks_exact(4).enumerate() {
41 w[i] = u32::from_be_bytes(chunk.try_into().unwrap());
42 }
43
44 for i in 16..80 {
45 w[i] = (w[i - 3] ^ w[i - 8] ^ w[i - 14] ^ w[i - 16]).rotate_left(1);
46 }
47
48 let mut a = h[0];
49 let mut b = h[1];
50 let mut c = h[2];
51 let mut d = h[3];
52 let mut e = h[4];
53
54 for (i, w_i) in w.iter().enumerate() {
55 let (f, k) = match i {
56 0..=19 => (((b & c) | (!b & d)), 0x5A827999),
57 20..=39 => ((b ^ c ^ d), 0x6ED9EBA1),
58 40..=59 => (((b & c) | (b & d) | (c & d)), 0x8F1BBCDC),
59 60..=79 => ((b ^ c ^ d), 0xCA62C1D6),
60 _ => unreachable!(), };
62
63 let temp = a
64 .rotate_left(5)
65 .wrapping_add(f)
66 .wrapping_add(e)
67 .wrapping_add(k)
68 .wrapping_add(*w_i);
69
70 e = d;
71 d = c;
72 c = b.rotate_left(30);
73 b = a;
74 a = temp;
75 }
76
77 h[0] = h[0].wrapping_add(a);
78 h[1] = h[1].wrapping_add(b);
79 h[2] = h[2].wrapping_add(c);
80 h[3] = h[3].wrapping_add(d);
81 h[4] = h[4].wrapping_add(e);
82 }
83}
84
85impl Transform for Sha1Hash {
86 fn name(&self) -> &'static str {
87 "SHA-1 Hash"
88 }
89
90 fn id(&self) -> &'static str {
91 "sha1hash"
92 }
93
94 fn description(&self) -> &'static str {
95 "Computes the SHA-1 hash of the input text (Warning: SHA-1 is cryptographically weak)"
96 }
97
98 fn category(&self) -> TransformerCategory {
99 TransformerCategory::Crypto
100 }
101
102 fn transform(&self, input: &str) -> Result<String, TransformError> {
103 let message = input.as_bytes();
104 let padded_message = Self::pad_message(message);
105
106 let mut h = [H0, H1, H2, H3, H4]; for block in padded_message.chunks_exact(64) {
109 Self::process_block(&mut h, block);
110 }
111
112 let mut result = String::with_capacity(40); for val in h.iter() {
115 result.push_str(&format!("{:08x}", val));
116 }
117
118 Ok(result)
119 }
120
121 fn default_test_input(&self) -> &'static str {
122 "buup"
123 }
124}
125
126#[cfg(test)]
127mod tests {
128 use super::*;
129
130 #[test]
131 fn test_sha1_empty_string() {
132 let transformer = Sha1Hash;
133 let input = "";
134 let expected = "da39a3ee5e6b4b0d3255bfef95601890afd80709";
135 assert_eq!(transformer.transform(input).unwrap(), expected);
136 }
137
138 #[test]
139 fn test_sha1_simple_string() {
140 let transformer = Sha1Hash;
141 let input = transformer.default_test_input();
142 let expected = "fb68687a3bc7428da3ddeecabc907bea236ae70b";
143 assert_eq!(transformer.transform(input).unwrap(), expected);
144
145 let input_hw = "hello world";
146 let expected_hw = "2aae6c35c94fcfb415dbe95f408b9ce91ee846ed";
147 assert_eq!(transformer.transform(input_hw).unwrap(), expected_hw);
148 }
149
150 #[test]
151 fn test_sha1_rfc3174_test_case_1() {
152 let transformer = Sha1Hash;
154 let input = "abc";
155 let expected = "a9993e364706816aba3e25717850c26c9cd0d89d";
156 assert_eq!(transformer.transform(input).unwrap(), expected);
157 }
158
159 #[test]
160 fn test_sha1_rfc3174_test_case_2() {
161 let transformer = Sha1Hash;
163 let input = "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq";
164 let expected = "84983e441c3bd26ebaae4aa1f95129e5e54670f1";
165 assert_eq!(transformer.transform(input).unwrap(), expected);
166 }
167
168 #[test]
169 fn test_sha1_million_a_chars() {
170 let transformer = Sha1Hash;
173 let input = String::from("a").repeat(1_000_000);
174 let expected = "34aa973cd4c4daa4f61eeb2bdbad27316534016f";
175 assert_eq!(transformer.transform(&input).unwrap(), expected);
176 }
177
178 #[test]
179 fn test_sha1_long_string_multiple_blocks() {
180 let transformer = Sha1Hash;
182 let input = "The quick brown fox jumps over the lazy dog.";
183 let expected = "408d94384216f890ff7a0c3528e8bed1e0b01621";
184 assert_eq!(transformer.transform(input).unwrap(), expected);
185 }
186}