libmhash/paranoid_hash/
md2.rs

1use crate::{
2    paranoid_hash::{hash_helper::slice_as_chunks, Hasher},
3    Error, Result,
4};
5
6#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
7pub struct MD2 {
8    checksum: [u8; 16],
9    buffer: [u8; 48],
10    count: usize,
11    is_done: bool,
12}
13
14impl Hasher for MD2 {
15    fn update(&mut self, data: &[u8]) -> Result<()> {
16        if self.is_done {
17            return Err(Error::UpdatingAfterFinished);
18        }
19
20        if data.len() % Self::BLOCK_SIZE != 0 {
21            return Err(Error::DataLengthMismatched(data.len(), Self::BLOCK_SIZE));
22        }
23
24        self.count = self.count.wrapping_add(data.len());
25
26        let block_chunks: &[[u8; Self::BLOCK_SIZE]] = slice_as_chunks(data);
27
28        for block_chunk in block_chunks {
29            self.update_block(block_chunk);
30        }
31
32        Ok(())
33    }
34
35    fn update_last(&mut self, data: &[u8]) -> Result<()> {
36        if self.is_done {
37            return Err(Error::UpdatingAfterFinished);
38        }
39
40        if data.len() > Self::BLOCK_SIZE {
41            return Err(Error::DataTooLarge(data.len(), Self::BLOCK_SIZE));
42        }
43
44        self.count = self.count.wrapping_add(data.len());
45        let mut padding_num = (16 - (self.count % 16)) as u8;
46        if padding_num == 0 {
47            padding_num = 16;
48        }
49
50        let mut padding = [padding_num; 32];
51        padding[0..data.len()].clone_from_slice(data);
52        let padding_slice = if data.len() < 16 {
53            &padding[0..16]
54        } else {
55            &padding[..]
56        };
57
58        self.update(padding_slice)?;
59        let checksum = self.checksum;
60        self.update_block(&checksum);
61
62        self.is_done = true;
63
64        Ok(())
65    }
66
67    fn digest(&self) -> Result<&[u8]> {
68        if !self.is_done {
69            return Err(Error::NotFinished);
70        }
71
72        Ok(&self.buffer[..Self::DIGEST_SIZE])
73    }
74
75    fn reset(&mut self) {
76        *self = Self::new();
77    }
78
79    fn block_size(&self) -> usize {
80        Self::BLOCK_SIZE
81    }
82
83    fn digest_size(&self) -> usize {
84        Self::DIGEST_SIZE
85    }
86}
87
88impl MD2 {
89    pub const BLOCK_SIZE: usize = 16;
90    pub const DIGEST_SIZE: usize = 16;
91
92    pub const fn new() -> Self {
93        Self {
94            buffer: [0; 48],
95            checksum: [0; 16],
96            count: 0,
97            is_done: false,
98        }
99    }
100
101    #[inline]
102    fn update_block(&mut self, block: &[u8; Self::BLOCK_SIZE]) {
103        let mut t = *self.checksum.last().unwrap();
104        for i in 0..16 {
105            self.checksum[i] ^= S_TABLE[(block[i] ^ t) as usize];
106            t = self.checksum[i];
107        }
108
109        let x = &mut self.buffer;
110        for i in 0..16 {
111            x[i + 16] = block[i];
112            x[i + 32] = x[i] ^ block[i];
113        }
114
115        let mut t = 0u8;
116        for i in 0..18 {
117            for x in x.iter_mut() {
118                *x ^= S_TABLE[t as usize];
119                t = *x;
120            }
121            t = t.wrapping_add(i);
122        }
123    }
124}
125
126impl Default for MD2 {
127    fn default() -> Self {
128        Self::new()
129    }
130}
131
132const S_TABLE: [u8; 256] = [
133    0x29, 0x2E, 0x43, 0xC9, 0xA2, 0xD8, 0x7C, 0x01, 0x3D, 0x36, 0x54, 0xA1, 0xEC, 0xF0, 0x06, 0x13,
134    0x62, 0xA7, 0x05, 0xF3, 0xC0, 0xC7, 0x73, 0x8C, 0x98, 0x93, 0x2B, 0xD9, 0xBC, 0x4C, 0x82, 0xCA,
135    0x1E, 0x9B, 0x57, 0x3C, 0xFD, 0xD4, 0xE0, 0x16, 0x67, 0x42, 0x6F, 0x18, 0x8A, 0x17, 0xE5, 0x12,
136    0xBE, 0x4E, 0xC4, 0xD6, 0xDA, 0x9E, 0xDE, 0x49, 0xA0, 0xFB, 0xF5, 0x8E, 0xBB, 0x2F, 0xEE, 0x7A,
137    0xA9, 0x68, 0x79, 0x91, 0x15, 0xB2, 0x07, 0x3F, 0x94, 0xC2, 0x10, 0x89, 0x0B, 0x22, 0x5F, 0x21,
138    0x80, 0x7F, 0x5D, 0x9A, 0x5A, 0x90, 0x32, 0x27, 0x35, 0x3E, 0xCC, 0xE7, 0xBF, 0xF7, 0x97, 0x03,
139    0xFF, 0x19, 0x30, 0xB3, 0x48, 0xA5, 0xB5, 0xD1, 0xD7, 0x5E, 0x92, 0x2A, 0xAC, 0x56, 0xAA, 0xC6,
140    0x4F, 0xB8, 0x38, 0xD2, 0x96, 0xA4, 0x7D, 0xB6, 0x76, 0xFC, 0x6B, 0xE2, 0x9C, 0x74, 0x04, 0xF1,
141    0x45, 0x9D, 0x70, 0x59, 0x64, 0x71, 0x87, 0x20, 0x86, 0x5B, 0xCF, 0x65, 0xE6, 0x2D, 0xA8, 0x02,
142    0x1B, 0x60, 0x25, 0xAD, 0xAE, 0xB0, 0xB9, 0xF6, 0x1C, 0x46, 0x61, 0x69, 0x34, 0x40, 0x7E, 0x0F,
143    0x55, 0x47, 0xA3, 0x23, 0xDD, 0x51, 0xAF, 0x3A, 0xC3, 0x5C, 0xF9, 0xCE, 0xBA, 0xC5, 0xEA, 0x26,
144    0x2C, 0x53, 0x0D, 0x6E, 0x85, 0x28, 0x84, 0x09, 0xD3, 0xDF, 0xCD, 0xF4, 0x41, 0x81, 0x4D, 0x52,
145    0x6A, 0xDC, 0x37, 0xC8, 0x6C, 0xC1, 0xAB, 0xFA, 0x24, 0xE1, 0x7B, 0x08, 0x0C, 0xBD, 0xB1, 0x4A,
146    0x78, 0x88, 0x95, 0x8B, 0xE3, 0x63, 0xE8, 0x6D, 0xE9, 0xCB, 0xD5, 0xFE, 0x3B, 0x00, 0x1D, 0x39,
147    0xF2, 0xEF, 0xB7, 0x0E, 0x66, 0x58, 0xD0, 0xE4, 0xA6, 0x77, 0x72, 0xF8, 0xEB, 0x75, 0x4B, 0x0A,
148    0x31, 0x44, 0x50, 0xB4, 0x8F, 0xED, 0x1F, 0x1A, 0xDB, 0x99, 0x8D, 0x33, 0x9F, 0x11, 0x83, 0x14,
149];
150
151#[cfg(test)]
152mod tests {
153    use crate::paranoid_hash::{
154        tester::{HasherTestWrapper, TestData},
155        Hasher,
156    };
157
158    use super::MD2;
159
160    const TESTS: &[TestData] = &[
161        TestData {
162            data: "".as_bytes(),
163            repeat: 1,
164            result: "8350e5a3e24c153df2275c9f80692773",
165        },
166        TestData {
167            data: "a".as_bytes(),
168            repeat: 1,
169            result: "32ec01ec4a6dac72c0ab96fb34c0b5d1",
170        },
171        TestData {
172            data: "abc".as_bytes(),
173            repeat: 1,
174            result: "da853b0d3f88d99b30283a69e6ded6bb",
175        },
176        TestData {
177            data: "message digest".as_bytes(),
178            repeat: 1,
179            result: "ab4f496bfb2a530b219ff33031fe06b0",
180        },
181        TestData {
182            data: "abcdefghijklmnopqrstuvwxyz".as_bytes(),
183            repeat: 1,
184            result: "4e8ddff3650292ab5a4108c3aa47940b",
185        },
186        TestData {
187            data: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789".as_bytes(),
188            repeat: 1,
189            result: "da33def2a42df13975352846c30338cd",
190        },
191        TestData {
192            data:
193                "12345678901234567890123456789012345678901234567890123456789012345678901234567890"
194                    .as_bytes(),
195            repeat: 1,
196            result: "d5976f79d83d3a0dc9806c3c66f3efd8",
197        },
198    ];
199
200    #[test]
201    fn tests_from_rfc() {
202        HasherTestWrapper::new(MD2::new()).run_tests(TESTS);
203    }
204
205    #[test]
206    #[should_panic]
207    fn panic_test1() {
208        let mut hasher = MD2::new();
209        hasher
210            .update("Not multiple of block size".as_bytes())
211            .unwrap();
212    }
213
214    #[test]
215    #[should_panic]
216    fn panic_test2() {
217        let mut hasher = MD2::new();
218        hasher
219            .update_last("Not less or equal to block size".as_bytes())
220            .unwrap();
221    }
222}