1#![no_std]
9
10const PI_SUB: [u8; 256] = [
11 41, 46, 67, 201, 162, 216, 124, 1, 61, 54, 84, 161, 236, 240, 6,
12 19, 98, 167, 5, 243, 192, 199, 115, 140, 152, 147, 43, 217, 188,
13 76, 130, 202, 30, 155, 87, 60, 253, 212, 224, 22, 103, 66, 111, 24,
14 138, 23, 229, 18, 190, 78, 196, 214, 218, 158, 222, 73, 160, 251,
15 245, 142, 187, 47, 238, 122, 169, 104, 121, 145, 21, 178, 7, 63,
16 148, 194, 16, 137, 11, 34, 95, 33, 128, 127, 93, 154, 90, 144, 50,
17 39, 53, 62, 204, 231, 191, 247, 151, 3, 255, 25, 48, 179, 72, 165,
18 181, 209, 215, 94, 146, 42, 172, 86, 170, 198, 79, 184, 56, 210,
19 150, 164, 125, 182, 118, 252, 107, 226, 156, 116, 4, 241, 69, 157,
20 112, 89, 100, 113, 135, 32, 134, 91, 207, 101, 230, 45, 168, 2, 27,
21 96, 37, 173, 174, 176, 185, 246, 28, 70, 97, 105, 52, 64, 126, 15,
22 85, 71, 163, 35, 221, 81, 175, 58, 195, 92, 249, 206, 186, 197,
23 234, 38, 44, 83, 13, 110, 133, 40, 132, 9, 211, 223, 205, 244, 65,
24 129, 77, 82, 106, 220, 55, 200, 108, 193, 171, 250, 36, 225, 123,
25 8, 12, 189, 177, 74, 120, 136, 149, 139, 227, 99, 232, 109, 233,
26 203, 213, 254, 59, 0, 29, 57, 242, 239, 183, 14, 102, 88, 208, 228,
27 166, 119, 114, 248, 235, 117, 75, 10, 49, 68, 80, 180, 143, 237,
28 31, 26, 219, 153, 141, 51, 159, 17, 131, 20
29];
30
31#[derive(Clone, Copy)]
32enum Padding { Zero, One, Two,
36 Three,
37 Four,
38 Five,
39 Six,
40 Seven,
41 Eight,
42 Nine,
43 Ten,
44 Eleven,
45 Twelve,
46 Thirteen,
47 Fourteen,
48 Fifteen,
49}
50
51impl Padding {
52 #[inline]
53 const fn new(input: usize) -> Self {
54 let input = 16 - if input > 0 && input < 16 {input} else {input % 16};
55 match input {
56 0 | 16 => Self::Zero, 1 => Self::One,
58 2 => Self::Two,
59 3 => Self::Three,
60 4 => Self::Four,
61 5 => Self::Five,
62 6 => Self::Six,
63 7 => Self::Seven,
64 8 => Self::Eight,
65 9 => Self::Nine,
66 10 => Self::Ten,
67 11 => Self::Eleven,
68 12 => Self::Twelve,
69 13 => Self::Thirteen,
70 14 => Self::Fourteen,
71 15 => Self::Fifteen,
72 _ => unreachable!(),
73 }
74 }
75 #[inline]
76 const fn len(self) -> usize { match self { Self::Zero => 16, Self::One => 1, Self::Two => 2, Self::Three => 3, Self::Four => 4, Self::Five => 5, Self::Six => 6, Self::Seven => 7,
86 Self::Eight => 8,
87 Self::Nine => 9,
88 Self::Ten => 10,
89 Self::Eleven => 11,
90 Self::Twelve => 12,
91 Self::Thirteen => 13,
92 Self::Fourteen => 14,
93 Self::Fifteen => 15,
94 }
95 }
96}
97
98#[derive(Clone, Copy)]
99struct MsgWithPadding<'msg> {
100 msg: &'msg [u8],
101 padding: Padding,
102}
103
104impl<'msg> MsgWithPadding<'msg> {
105 #[inline]
106 const fn new(msg: &'msg [u8], padding: Padding) -> Self {
107 Self {msg, padding}
108 }
109 #[inline]
110 const fn len(&self) -> usize {
111 self.msg.len() + self.padding.len()
112 }
113 #[inline]
114 #[allow(clippy::cast_possible_truncation)]
115 const fn get_inner(&self, index: usize) -> u8 {
116 if index >= self.msg.len() {self.padding.len() as u8} else {self.msg[index]}
118 }
119}
120
121pub struct MD2Digest([u8; 16]);
123
124impl MD2Digest {
125 #[must_use]
127 pub const fn new(input: &[u8]) -> Self {
128 Self (make_md2(input))
129 }
130 #[must_use]
132 pub const fn bytes(&self) -> [u8; 16] {
133 self.0
134 }
135}
136
137impl core::fmt::Display for MD2Digest {
138 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
139 write!(f, "{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}",
140 self.0[0], self.0[1], self.0[2], self.0[3], self.0[4], self.0[5], self.0[6], self.0[7],
141 self.0[8], self.0[9], self.0[10], self.0[11], self.0[12], self.0[13], self.0[14], self.0[15])
142 }
143}
144
145#[allow(clippy::cast_possible_truncation)]
146#[inline]
147const fn make_md2(input: &[u8]) -> [u8; 16] {
148 let msg_with_padding = MsgWithPadding::new(input, Padding::new(input.len()));
149 let checksum = checksum(msg_with_padding);
150 let msg = MsgWithChecksum::new(msg_with_padding, checksum);
151 let mut buffer = [0; 48];
152 let mut outer_index = 0;
153 while outer_index < (msg.len() / 16) {
154 let mut inner_index = 0;
155 while inner_index < 16 {
156 buffer[16 + inner_index] = msg.get_inner((outer_index * 16) + inner_index);
157 buffer[32 + inner_index] = buffer[16 + inner_index] ^ buffer[inner_index];
158 inner_index += 1;
159 }
160 let mut t = 0;
161 inner_index = 0;
162 while inner_index < 18 {
163 let mut innermost_index = 0;
164 while innermost_index < 48 {
165 t = (buffer[innermost_index]) ^ (PI_SUB[t as usize]);
166 buffer[innermost_index] = t;
167 innermost_index += 1;
168 }
169 t = t.wrapping_add(inner_index as u8); inner_index += 1;
171 }
172 outer_index += 1;
173 }
174 let mut digest = [0; 16];
175 outer_index = 0;
176 while outer_index < 16 {
177 digest[outer_index] = buffer[outer_index];
178 outer_index += 1;
179 }
180 digest
181}
182
183#[inline]
184const fn checksum(msg: MsgWithPadding) -> [u8; 16] { let mut checksum = [0; 16]; let mut l = 0; let mut outer_index = 0; while outer_index < (msg.len() / 16) { let mut inner_index = 0; while inner_index < 16 { let c = msg.get_inner((outer_index * 16) + inner_index); checksum[inner_index] ^= PI_SUB[(c ^ l) as usize]; l = checksum[inner_index]; inner_index += 1; } outer_index += 1; } checksum
199}
200
201struct MsgWithChecksum<'msg> {
202 msg_with_padding: MsgWithPadding<'msg>,
203 checksum: [u8; 16],
204}
205
206impl<'msg> MsgWithChecksum<'msg> {
207 #[inline]
208 const fn new(msg_with_padding: MsgWithPadding<'msg>, checksum: [u8; 16]) -> Self {
209 Self {msg_with_padding, checksum}
210 }
211 #[inline]
212 const fn len(&self) -> usize {
213 self.msg_with_padding.len() + self.checksum.len()
214 }
215 #[inline]
216 const fn get_inner(&self, index: usize) -> u8 {
217 if index < self.msg_with_padding.len() {self.msg_with_padding.get_inner(index)} else {
218 self.checksum[index - self.msg_with_padding.len()]
219 }
220 }
221}
222
223#[cfg(test)]
224mod tests {
225 use crate::{make_md2, checksum, MsgWithPadding};
226 const fn is_identical(first: &[u8], second: &[u8]) -> bool {
227 let mut index = 0;
228 while index != first.len() {
229 if first[index] != second[index] {return false;}
230 index += 1;
231 }
232 true
233 }
234 #[test]
235 const fn empty_md2_checksum() {
236 assert!(is_identical(
237 &checksum(MsgWithPadding::new(b"", crate::Padding::Zero)),
238 &[98, 56, 103, 182, 175, 82, 121, 94, 95, 33, 78, 151, 32, 190, 234, 141]
239 ));
240 assert!(is_identical(
241 &[98, 56, 103, 182, 175, 82, 121, 94, 95, 33, 78, 151, 32, 190, 234, 141],
242 &[0x62, 0x38, 0x67, 0xb6, 0xaf, 0x52, 0x79, 0x5e, 0x5f, 0x21, 0x4e, 0x97, 0x20, 0xbe, 0xea, 0x8d]
243 ));
244 }
245 #[test]
246 const fn empty_md2() { assert!(is_identical(
248 &make_md2(b""),
249 &[131, 80, 229, 163, 226, 76, 21, 61, 242, 39, 92, 159, 128, 105, 39, 115]
250 ));
251 assert!(is_identical(
252 &[131, 80, 229, 163, 226, 76, 21, 61, 242, 39, 92, 159, 128, 105, 39, 115],
253 &[0x83, 0x50, 0xe5, 0xa3, 0xe2, 0x4c, 0x15, 0x3d, 0xf2, 0x27, 0x5c, 0x9f, 0x80, 0x69, 0x27, 0x73]
254 ));
255 }
256 #[test]
257 const fn a_md2_checksum() {
258 assert!(is_identical(
259 &checksum(MsgWithPadding::new(b"a", crate::Padding::Fifteen)),
260 &[25, 115, 156, 173, 163, 186, 40, 22, 147, 52, 142, 157, 37, 111, 255, 49]
261 ));
262 assert!(is_identical(
263 &[25, 115, 156, 173, 163, 186, 40, 22, 147, 52, 142, 157, 37, 111, 255, 49],
264 &[0x19, 0x73, 0x9c, 0xad, 0xa3, 0xba, 0x28, 0x16, 0x93, 0x34, 0x8e, 0x9d, 0x25, 0x6f, 0xff, 0x31]
265 ));
266 }
267 #[test]
268 const fn a_md2() { assert!(is_identical(
270 &make_md2(b"a"),
271 &[50, 236, 1, 236, 74, 109, 172, 114, 192, 171, 150, 251, 52, 192, 181, 209]
272 ));
273 assert!(is_identical(
274 &[50, 236, 1, 236, 74, 109, 172, 114, 192, 171, 150, 251, 52, 192, 181, 209],
275 &[0x32, 0xec, 0x01, 0xec, 0x4a, 0x6d, 0xac, 0x72, 0xc0, 0xab, 0x96, 0xfb, 0x34, 0xc0, 0xb5, 0xd1]
276 ));
277 }
278 #[test]
279 const fn abc_md2_checksum() {
280 assert!(is_identical(
281 &checksum(MsgWithPadding::new(b"abc", crate::Padding::Thirteen)),
282 &[25, 226, 157, 27, 115, 4, 54, 142, 89, 90, 39, 111, 48, 47, 87, 204]
283 ));
284 assert!(is_identical(
285 &[25, 226, 157, 27, 115, 4, 54, 142, 89, 90, 39, 111, 48, 47, 87, 204],
286 &[0x19, 0xe2, 0x9d, 0x1b, 0x73, 0x04, 0x36, 0x8e, 0x59, 0x5a, 0x27, 0x6f, 0x30, 0x2f, 0x57, 0xcc]
287 ));
288 }
289 #[test]
290 const fn abc_md2() { assert!(is_identical(
292 &make_md2(b"abc"),
293 &[218, 133, 59, 13, 63, 136, 217, 155, 48, 40, 58, 105, 230, 222, 214, 187]
294 ));
295 assert!(is_identical(
296 &[218, 133, 59, 13, 63, 136, 217, 155, 48, 40, 58, 105, 230, 222, 214, 187],
297 &[0xda, 0x85, 0x3b, 0x0d, 0x3f, 0x88, 0xd9, 0x9b, 0x30, 0x28, 0x3a, 0x69, 0xe6, 0xde, 0xd6, 0xbb]
298 ));
299 }
300 #[test]
301 const fn message_digest_md2_checksum() {
302 assert!(is_identical(
303 &checksum(MsgWithPadding::new(b"message digest", crate::Padding::Two)),
304 &[86, 214, 81, 87, 222, 223, 205, 117, 167, 177, 232, 45, 151, 14, 236, 75]
305 ));
306 assert!(is_identical(
307 &[86, 214, 81, 87, 222, 223, 205, 117, 167, 177, 232, 45, 151, 14, 236, 75],
308 &[0x56, 0xd6, 0x51, 0x57, 0xde, 0xdf, 0xcd, 0x75, 0xa7, 0xb1, 0xe8, 0x2d, 0x97, 0x0e, 0xec, 0x4b]
309 ));
310 }
311 #[test]
312 const fn message_digest_md2() { assert!(is_identical(
314 &make_md2(b"message digest"),
315 &[171, 79, 73, 107, 251, 42, 83, 11, 33, 159, 243, 48, 49, 254, 6, 176]
316 ));
317 assert!(is_identical(
318 &[171, 79, 73, 107, 251, 42, 83, 11, 33, 159, 243, 48, 49, 254, 6, 176],
319 &[0xab, 0x4f, 0x49, 0x6b, 0xfb, 0x2a, 0x53, 0x0b, 0x21, 0x9f, 0xf3, 0x30, 0x31, 0xfe, 0x06, 0xb0]
320 ));
321 }
322 #[test]
323 const fn alphabet_md2_checksum() {
324 assert!(is_identical(
325 &checksum(MsgWithPadding::new(b"abcdefghijklmnopqrstuvwxyz", crate::Padding::Six)),
326 &[74, 66, 211, 163, 119, 183, 233, 152, 143, 185, 40, 150, 153, 228, 211, 163]
327 ));
328 assert!(is_identical(
329 &[74, 66, 211, 163, 119, 183, 233, 152, 143, 185, 40, 150, 153, 228, 211, 163],
330 &[0x4a, 0x42, 0xd3, 0xa3, 0x77, 0xb7, 0xe9, 0x98, 0x8f, 0xb9, 0x28, 0x96, 0x99, 0xe4, 0xd3, 0xa3]
331 ));
332 }
333 #[test]
334 const fn alphabet_md2() { assert!(is_identical(
336 &make_md2(b"abcdefghijklmnopqrstuvwxyz"),
337 &[78, 141, 223, 243, 101, 2, 146, 171, 90, 65, 8, 195, 170, 71, 148, 11]
338 ));
339 assert!(is_identical(
340 &[78, 141, 223, 243, 101, 2, 146, 171, 90, 65, 8, 195, 170, 71, 148, 11],
341 &[0x4e, 0x8d, 0xdf, 0xf3, 0x65, 0x02, 0x92, 0xab, 0x5a, 0x41, 0x08, 0xc3, 0xaa, 0x47, 0x94, 0x0b]
342 ));
343 }
344 #[test]
345 const fn double_alphabet_numbers_checksum() {
346 assert!(is_identical(
347 &checksum(MsgWithPadding::new(b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789",
348 crate::Padding::Two)),
349 &[195, 219, 117, 146, 238, 29, 217, 184, 69, 5, 207, 180, 226, 249, 167, 101]
350 ));
351 assert!(is_identical(
352 &[195, 219, 117, 146, 238, 29, 217, 184, 69, 5, 207, 180, 226, 249, 167, 101],
353 &[0xc3, 0xdb, 0x75, 0x92, 0xee, 0x1d, 0xd9, 0xb8, 0x45, 0x05, 0xcf, 0xb4, 0xe2, 0xf9, 0xa7, 0x65]
354 ));
355 }
356 #[test]
357 const fn double_alphabet_numbers() { assert!(is_identical(
359 &make_md2(b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"),
360 &[218, 51, 222, 242, 164, 45, 241, 57, 117, 53, 40, 70, 195, 3, 56, 205]
361 ));
362 assert!(is_identical(
363 &[218, 51, 222, 242, 164, 45, 241, 57, 117, 53, 40, 70, 195, 3, 56, 205],
364 &[0xda, 0x33, 0xde, 0xf2, 0xa4, 0x2d, 0xf1, 0x39, 0x75, 0x35, 0x28, 0x46, 0xc3, 0x03, 0x38, 0xcd]
365 ));
366 }
367 #[test]
368 const fn numbers_times_eight_md2_checksum() {
369 assert!(is_identical(
370 &checksum(MsgWithPadding::new(b"12345678901234567890123456789012345678901234567890123456789012345678901234567890",
371 crate::Padding::Zero)),
372 &[5, 156, 165, 103, 60, 143, 147, 27, 196, 18, 20, 245, 107, 92, 108, 1]
373 ));
374 assert!(is_identical(
375 &[5, 156, 165, 103, 60, 143, 147, 27, 196, 18, 20, 245, 107, 92, 108, 1],
376 &[0x05, 0x9c, 0xa5, 0x67, 0x3c, 0x8f, 0x93, 0x1b, 0xc4, 0x12, 0x14, 0xf5, 0x6b, 0x5c, 0x6c, 0x01]
377 ));
378 }
379 #[test]
380 const fn numbers_times_eight_md2() { assert!(is_identical(
382 &make_md2(b"12345678901234567890123456789012345678901234567890123456789012345678901234567890"),
383 &[213, 151, 111, 121, 216, 61, 58, 13, 201, 128, 108, 60, 102, 243, 239, 216]
384 ));
385 assert!(is_identical(
386 &[213, 151, 111, 121, 216, 61, 58, 13, 201, 128, 108, 60, 102, 243, 239, 216],
387 &[0xd5, 0x97, 0x6f, 0x79, 0xd8, 0x3d, 0x3a, 0x0d, 0xc9, 0x80, 0x6c, 0x3c, 0x66, 0xf3, 0xef, 0xd8]
388 ));
389 }
390}