libmhash/paranoid_hash/
md4.rs

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}