1# SPDX-License-Identifier: WTFPL
2# This file is part of Scapy
3# See https://scapy.net/ for more information
4# Copyright (C) 2019 James Seo <james@equiv.tech> (github.com/kangtastic).
5
6"""
7MD4 implementation
8
9Modified from:
10https://gist.github.com/kangtastic/c3349fc4f9d659ee362b12d7d8c639b6
11"""
12
13import struct
14
15
16class MD4:
17 """
18 An implementation of the MD4 hash algorithm.
19
20 Modified to provide the same API as hashlib's.
21 """
22 name = 'md4'
23 block_size = 64
24 width = 32
25 mask = 0xFFFFFFFF
26
27 # Unlike, say, SHA-1, MD4 uses little-endian. Fascinating!
28 h = [0x67452301, 0xEFCDAB89, 0x98BADCFE, 0x10325476]
29
30 def __init__(self, msg=b""):
31 self.msg = msg
32
33 def update(self, msg):
34 self.msg += msg
35
36 def digest(self):
37 # Pre-processing: Total length is a multiple of 512 bits.
38 ml = len(self.msg) * 8
39 self.msg += b"\x80"
40 self.msg += b"\x00" * (-(len(self.msg) + 8) % self.block_size)
41 self.msg += struct.pack("<Q", ml)
42
43 # Process the message in successive 512-bit chunks.
44 self._process([self.msg[i: i + self.block_size]
45 for i in range(0, len(self.msg), self.block_size)])
46
47 return struct.pack("<4L", *self.h)
48
49 def _process(self, chunks):
50 for chunk in chunks:
51 X, h = list(struct.unpack("<16I", chunk)), self.h.copy()
52
53 # Round 1.
54 Xi = [3, 7, 11, 19]
55 for n in range(16):
56 i, j, k, l = map(lambda x: x % 4, range(-n, -n + 4))
57 K, S = n, Xi[n % 4]
58 hn = h[i] + MD4.F(h[j], h[k], h[l]) + X[K]
59 h[i] = MD4.lrot(hn & MD4.mask, S)
60
61 # Round 2.
62 Xi = [3, 5, 9, 13]
63 for n in range(16):
64 i, j, k, l = map(lambda x: x % 4, range(-n, -n + 4))
65 K, S = n % 4 * 4 + n // 4, Xi[n % 4]
66 hn = h[i] + MD4.G(h[j], h[k], h[l]) + X[K] + 0x5A827999
67 h[i] = MD4.lrot(hn & MD4.mask, S)
68
69 # Round 3.
70 Xi = [3, 9, 11, 15]
71 Ki = [0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, 15]
72 for n in range(16):
73 i, j, k, l = map(lambda x: x % 4, range(-n, -n + 4))
74 K, S = Ki[n], Xi[n % 4]
75 hn = h[i] + MD4.H(h[j], h[k], h[l]) + X[K] + 0x6ED9EBA1
76 h[i] = MD4.lrot(hn & MD4.mask, S)
77
78 self.h = [((v + n) & MD4.mask) for v, n in zip(self.h, h)]
79
80 @staticmethod
81 def F(x, y, z):
82 return (x & y) | (~x & z)
83
84 @staticmethod
85 def G(x, y, z):
86 return (x & y) | (x & z) | (y & z)
87
88 @staticmethod
89 def H(x, y, z):
90 return x ^ y ^ z
91
92 @staticmethod
93 def lrot(value, n):
94 lbits, rbits = (value << n) & MD4.mask, value >> (MD4.width - n)
95 return lbits | rbits