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}