1 """
2 helper implementing insecure and obsolete md4 algorithm.
3 used for NTHASH format, which is also insecure and broken,
4 since it's just md4(password)
5
6 implementated based on rfc at http://www.faqs.org/rfcs/rfc1320.html
7
8 """
9
10
11
12
13
14
15
16 from binascii import hexlify
17 import struct
18 from warnings import warn
19
20 from compat import b, bytes, bascii_to_str, irange, PY3
21
22 __all__ = [ "md4" ]
23
24
25
27 return (x&y) | ((~x) & z)
28
30 return (x&y) | (x&z) | (y&z)
31
32
33
34
35 MASK_32 = 2**32-1
36
37
38
39
41 """pep-247 compatible implementation of MD4 hash algorithm
42
43 .. attribute:: digest_size
44
45 size of md4 digest in bytes (16 bytes)
46
47 .. method:: update
48
49 update digest by appending additional content
50
51 .. method:: copy
52
53 create clone of digest object, including current state
54
55 .. method:: digest
56
57 return bytes representing md4 digest of current content
58
59 .. method:: hexdigest
60
61 return hexdecimal version of digest
62 """
63
64
65
66
67 name = "md4"
68 digest_size = digestsize = 16
69
70 _count = 0
71 _state = None
72 _buf = None
73
75 self._count = 0
76 self._state = [0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476]
77 self._buf = b('')
78 if content:
79 self.update(content)
80
81
82 _round1 = [
83 [0,1,2,3, 0,3],
84 [3,0,1,2, 1,7],
85 [2,3,0,1, 2,11],
86 [1,2,3,0, 3,19],
87
88 [0,1,2,3, 4,3],
89 [3,0,1,2, 5,7],
90 [2,3,0,1, 6,11],
91 [1,2,3,0, 7,19],
92
93 [0,1,2,3, 8,3],
94 [3,0,1,2, 9,7],
95 [2,3,0,1, 10,11],
96 [1,2,3,0, 11,19],
97
98 [0,1,2,3, 12,3],
99 [3,0,1,2, 13,7],
100 [2,3,0,1, 14,11],
101 [1,2,3,0, 15,19],
102 ]
103
104
105 _round2 = [
106 [0,1,2,3, 0,3],
107 [3,0,1,2, 4,5],
108 [2,3,0,1, 8,9],
109 [1,2,3,0, 12,13],
110
111 [0,1,2,3, 1,3],
112 [3,0,1,2, 5,5],
113 [2,3,0,1, 9,9],
114 [1,2,3,0, 13,13],
115
116 [0,1,2,3, 2,3],
117 [3,0,1,2, 6,5],
118 [2,3,0,1, 10,9],
119 [1,2,3,0, 14,13],
120
121 [0,1,2,3, 3,3],
122 [3,0,1,2, 7,5],
123 [2,3,0,1, 11,9],
124 [1,2,3,0, 15,13],
125 ]
126
127
128 _round3 = [
129 [0,1,2,3, 0,3],
130 [3,0,1,2, 8,9],
131 [2,3,0,1, 4,11],
132 [1,2,3,0, 12,15],
133
134 [0,1,2,3, 2,3],
135 [3,0,1,2, 10,9],
136 [2,3,0,1, 6,11],
137 [1,2,3,0, 14,15],
138
139 [0,1,2,3, 1,3],
140 [3,0,1,2, 9,9],
141 [2,3,0,1, 5,11],
142 [1,2,3,0, 13,15],
143
144 [0,1,2,3, 3,3],
145 [3,0,1,2, 11,9],
146 [2,3,0,1, 7,11],
147 [1,2,3,0, 15,15],
148 ]
149
151 "process 64 byte block"
152
153 X = struct.unpack("<16I", block)
154
155
156 orig = self._state
157 state = list(orig)
158
159
160 for a,b,c,d,k,s in self._round1:
161 t = (state[a] + F(state[b],state[c],state[d]) + X[k]) & MASK_32
162 state[a] = ((t<<s) & MASK_32) + (t>>(32-s))
163
164
165 for a,b,c,d,k,s in self._round2:
166 t = (state[a] + G(state[b],state[c],state[d]) + X[k] + 0x5a827999) & MASK_32
167 state[a] = ((t<<s) & MASK_32) + (t>>(32-s))
168
169
170 for a,b,c,d,k,s in self._round3:
171 t = (state[a] + (state[b] ^ state[c] ^ state[d]) + X[k] + 0x6ed9eba1) & MASK_32
172 state[a] = ((t<<s) & MASK_32) + (t>>(32-s))
173
174
175 for i in irange(4):
176 orig[i] = (orig[i]+state[i]) & MASK_32
177
179 if not isinstance(content, bytes):
180 raise TypeError("expected bytes")
181 buf = self._buf
182 if buf:
183 content = buf + content
184 idx = 0
185 end = len(content)
186 while True:
187 next = idx + 64
188 if next <= end:
189 self._process(content[idx:next])
190 self._count += 1
191 idx = next
192 else:
193 self._buf = content[idx:]
194 return
195
202
204
205
206 orig = list(self._state)
207
208
209
210
211 buf = self._buf
212 msglen = self._count*512 + len(buf)*8
213 block = buf + b('\x80') + b('\x00') * ((119-len(buf)) % 64) + \
214 struct.pack("<2I", msglen & MASK_32, (msglen>>32) & MASK_32)
215 if len(block) == 128:
216 self._process(block[:64])
217 self._process(block[64:])
218 else:
219 assert len(block) == 64
220 self._process(block)
221
222
223 out = struct.pack("<4I", *self._state)
224 self._state = orig
225 return out
226
229
230
231
232
233
234
235 _builtin_md4 = md4
236
237
238
239
240 import hashlib
241 from compat import PYPY
242
244 try:
245 h = hashlib.new("md4")
246 except ValueError:
247
248 return False
249 result = h.hexdigest()
250 if result == '31d6cfe0d16ae931b73c59d7e0c089c0':
251 return True
252 if PYPY and result == '':
253
254 return False
255
256 from passlib.exc import PasslibRuntimeWarning
257 warn("native md4 support disabled, sanity check failed!", PasslibRuntimeWarning)
258 return False
259
260 if _has_native_md4():
261
262 - def md4(content=None):
263 "wrapper for hashlib.new('md4')"
264 return hashlib.new('md4', content or b(''))
265
266
267
268
269 new = md4
270
271
272
273
274