1use std::mem::size_of;
2
3use crate::{paranoid_hash::Hasher, Error, Result};
4
5#[derive(Clone, Copy, Debug, Default, Hash, PartialEq, Eq, PartialOrd, Ord)]
6pub struct MD4 {
7 state: [u32; 4],
8 count: u64,
9 is_done: bool,
10 digest: [u8; Self::DIGEST_SIZE],
11}
12
13impl Hasher for MD4 {
14 fn update(&mut self, data: &[u8]) -> Result<()> {
15 transmute_update!(self, data, Self::BLOCK_SIZE, u32, u64, "wrapping", "le");
16 }
17
18 fn update_last(&mut self, data: &[u8]) -> Result<()> {
19 transmute_update_last!(self, data, Self::BLOCK_SIZE, u32, u64, "wrapping", "le");
20 }
21
22 fn digest(&self) -> Result<&[u8]> {
23 if !self.is_done {
24 return Err(Error::NotFinished);
25 }
26
27 Ok(&self.digest)
28 }
29
30 fn reset(&mut self) {
31 *self = Self::new();
32 }
33
34 fn block_size(&self) -> usize {
35 Self::BLOCK_SIZE
36 }
37
38 fn digest_size(&self) -> usize {
39 Self::DIGEST_SIZE
40 }
41}
42
43impl MD4 {
44 pub const BLOCK_SIZE: usize = 64;
45 pub const DIGEST_SIZE: usize = 16;
46
47 const U32_BLOCK_SIZE: usize = Self::BLOCK_SIZE / size_of::<u32>();
48
49 pub const fn new() -> Self {
50 Self {
51 state: [0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476],
52 count: 0,
53 is_done: false,
54 digest: [0; Self::DIGEST_SIZE],
55 }
56 }
57
58 #[inline]
59 fn update_block(&mut self, block: &[u32; Self::U32_BLOCK_SIZE]) {
60 let [mut a, mut b, mut c, mut d] = self.state;
61
62 a = round_1(a, b, c, d, block[0], S11);
63 d = round_1(d, a, b, c, block[1], S12);
64 c = round_1(c, d, a, b, block[2], S13);
65 b = round_1(b, c, d, a, block[3], S14);
66 a = round_1(a, b, c, d, block[4], S11);
67 d = round_1(d, a, b, c, block[5], S12);
68 c = round_1(c, d, a, b, block[6], S13);
69 b = round_1(b, c, d, a, block[7], S14);
70 a = round_1(a, b, c, d, block[8], S11);
71 d = round_1(d, a, b, c, block[9], S12);
72 c = round_1(c, d, a, b, block[10], S13);
73 b = round_1(b, c, d, a, block[11], S14);
74 a = round_1(a, b, c, d, block[12], S11);
75 d = round_1(d, a, b, c, block[13], S12);
76 c = round_1(c, d, a, b, block[14], S13);
77 b = round_1(b, c, d, a, block[15], S14);
78
79 a = round_2(a, b, c, d, block[0], S21);
80 d = round_2(d, a, b, c, block[4], S22);
81 c = round_2(c, d, a, b, block[8], S23);
82 b = round_2(b, c, d, a, block[12], S24);
83 a = round_2(a, b, c, d, block[1], S21);
84 d = round_2(d, a, b, c, block[5], S22);
85 c = round_2(c, d, a, b, block[9], S23);
86 b = round_2(b, c, d, a, block[13], S24);
87 a = round_2(a, b, c, d, block[2], S21);
88 d = round_2(d, a, b, c, block[6], S22);
89 c = round_2(c, d, a, b, block[10], S23);
90 b = round_2(b, c, d, a, block[14], S24);
91 a = round_2(a, b, c, d, block[3], S21);
92 d = round_2(d, a, b, c, block[7], S22);
93 c = round_2(c, d, a, b, block[11], S23);
94 b = round_2(b, c, d, a, block[15], S24);
95
96 a = round_3(a, b, c, d, block[0], S31);
97 d = round_3(d, a, b, c, block[8], S32);
98 c = round_3(c, d, a, b, block[4], S33);
99 b = round_3(b, c, d, a, block[12], S34);
100 a = round_3(a, b, c, d, block[2], S31);
101 d = round_3(d, a, b, c, block[10], S32);
102 c = round_3(c, d, a, b, block[6], S33);
103 b = round_3(b, c, d, a, block[14], S34);
104 a = round_3(a, b, c, d, block[1], S31);
105 d = round_3(d, a, b, c, block[9], S32);
106 c = round_3(c, d, a, b, block[5], S33);
107 b = round_3(b, c, d, a, block[13], S34);
108 a = round_3(a, b, c, d, block[3], S31);
109 d = round_3(d, a, b, c, block[11], S32);
110 c = round_3(c, d, a, b, block[7], S33);
111 b = round_3(b, c, d, a, block[15], S34);
112
113 self.state[0] = self.state[0].wrapping_add(a);
114 self.state[1] = self.state[1].wrapping_add(b);
115 self.state[2] = self.state[2].wrapping_add(c);
116 self.state[3] = self.state[3].wrapping_add(d);
117 }
118}
119
120#[inline(always)]
121fn hash_1(x: u32, y: u32, z: u32) -> u32 {
122 x & y | !x & z
123}
124
125#[inline(always)]
126fn hash_2(x: u32, y: u32, z: u32) -> u32 {
127 x & y | x & z | y & z
128}
129
130#[inline(always)]
131fn hash_3(x: u32, y: u32, z: u32) -> u32 {
132 x ^ y ^ z
133}
134
135#[inline(always)]
136fn round_1(a: u32, b: u32, c: u32, d: u32, x: u32, s: u32) -> u32 {
137 hash_1(b, c, d)
138 .wrapping_add(x)
139 .wrapping_add(a)
140 .rotate_left(s)
141}
142
143#[inline(always)]
144fn round_2(a: u32, b: u32, c: u32, d: u32, x: u32, s: u32) -> u32 {
145 hash_2(b, c, d)
146 .wrapping_add(x)
147 .wrapping_add(a)
148 .wrapping_add(0x5a827999)
149 .rotate_left(s)
150}
151
152#[inline(always)]
153fn round_3(a: u32, b: u32, c: u32, d: u32, x: u32, s: u32) -> u32 {
154 hash_3(b, c, d)
155 .wrapping_add(x)
156 .wrapping_add(a)
157 .wrapping_add(0x6ed9eba1)
158 .rotate_left(s)
159}
160
161const S11: u32 = 3;
162const S12: u32 = 7;
163const S13: u32 = 11;
164const S14: u32 = 19;
165const S21: u32 = 3;
166const S22: u32 = 5;
167const S23: u32 = 9;
168const S24: u32 = 13;
169const S31: u32 = 3;
170const S32: u32 = 9;
171const S33: u32 = 11;
172const S34: u32 = 15;
173
174#[cfg(test)]
175mod tests {
176 use crate::paranoid_hash::{
177 tester::{HasherTestWrapper, TestData},
178 Hasher,
179 };
180
181 use super::MD4;
182
183 const TESTS: &[TestData] = &[
184 TestData {
185 data: "".as_bytes(),
186 repeat: 1,
187 result: "31d6cfe0d16ae931b73c59d7e0c089c0",
188 },
189 TestData {
190 data: "a".as_bytes(),
191 repeat: 1,
192 result: "bde52cb31de33e46245e05fbdbd6fb24",
193 },
194 TestData {
195 data: "abc".as_bytes(),
196 repeat: 1,
197 result: "a448017aaf21d8525fc10ae87aa6729d",
198 },
199 TestData {
200 data: "message digest".as_bytes(),
201 repeat: 1,
202 result: "d9130a8164549fe818874806e1c7014b",
203 },
204 TestData {
205 data: "abcdefghijklmnopqrstuvwxyz".as_bytes(),
206 repeat: 1,
207 result: "d79e1c308aa5bbcdeea8ed63df412da9",
208 },
209 TestData {
210 data: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789".as_bytes(),
211 repeat: 1,
212 result: "043f8582f241db351ce627e153e7f0e4",
213 },
214 TestData {
215 data:
216 "12345678901234567890123456789012345678901234567890123456789012345678901234567890"
217 .as_bytes(),
218 repeat: 1,
219 result: "e33b4ddc9c38f2199c3e7b164fcc0536",
220 },
221 ];
222
223 #[test]
224 fn tests_from_rfc() {
225 HasherTestWrapper::new(MD4::new()).run_tests(TESTS);
226 }
227
228 #[test]
229 #[should_panic]
230 fn panic_test1() {
231 let mut hasher = MD4::new();
232 hasher
233 .update("Not multiple of block size".as_bytes())
234 .unwrap();
235 }
236
237 #[test]
238 #[should_panic]
239 fn panic_test2() {
240 let mut hasher = MD4::new();
241 let data = [0u8; MD4::BLOCK_SIZE + 1];
242 hasher.update_last(&data).unwrap();
243 }
244}