codekingpro/portable-devtools
114k
1"""2passlib.crypto._md4 -- fallback implementation of MD43 4Helper implementing insecure and obsolete md4 algorithm.5used for NTHASH format, which is also insecure and broken,6since it's just md4(password).7 8Implementated based on rfc at http://www.faqs.org/rfcs/rfc1320.html9 10.. note::11 12 This shouldn't be imported directly, it's merely used conditionally13 by ``passlib.crypto.lookup_hash()`` when a native implementation can't be found.14"""15 16#=============================================================================17# imports18#=============================================================================19# core20from binascii import hexlify21import struct22# site23from passlib.utils.compat import bascii_to_str, irange, PY324# local25__all__ = ["md4"]26 27#=============================================================================28# utils29#=============================================================================30def F(x,y,z):31 return (x&y) | ((~x) & z)32 33def G(x,y,z):34 return (x&y) | (x&z) | (y&z)35 36##def H(x,y,z):37## return x ^ y ^ z38 39MASK_32 = 2**32-140 41#=============================================================================42# main class43#=============================================================================44class md4(object):45 """pep-247 compatible implementation of MD4 hash algorithm46 47 .. attribute:: digest_size48 49 size of md4 digest in bytes (16 bytes)50 51 .. method:: update52 53 update digest by appending additional content54 55 .. method:: copy56 57 create clone of digest object, including current state58 59 .. method:: digest60 61 return bytes representing md4 digest of current content62 63 .. method:: hexdigest64 65 return hexadecimal version of digest66 """67 # FIXME: make this follow hash object PEP better.68 # FIXME: this isn't threadsafe69 70 name = "md4"71 digest_size = digestsize = 1672 block_size = 6473 74 _count = 0 # number of 64-byte blocks processed so far (not including _buf)75 _state = None # list of [a,b,c,d] 32 bit ints used as internal register76 _buf = None # data processed in 64 byte blocks, this holds leftover from last update77 78 def __init__(self, content=None):79 self._count = 080 self._state = [0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476]81 self._buf = b''82 if content:83 self.update(content)84 85 # round 1 table - [abcd k s]86 _round1 = [87 [0,1,2,3, 0,3],88 [3,0,1,2, 1,7],89 [2,3,0,1, 2,11],90 [1,2,3,0, 3,19],91 92 [0,1,2,3, 4,3],93 [3,0,1,2, 5,7],94 [2,3,0,1, 6,11],95 [1,2,3,0, 7,19],96 97 [0,1,2,3, 8,3],98 [3,0,1,2, 9,7],99 [2,3,0,1, 10,11],100 [1,2,3,0, 11,19],101 102 [0,1,2,3, 12,3],103 [3,0,1,2, 13,7],104 [2,3,0,1, 14,11],105 [1,2,3,0, 15,19],106 ]107 108 # round 2 table - [abcd k s]109 _round2 = [110 [0,1,2,3, 0,3],111 [3,0,1,2, 4,5],112 [2,3,0,1, 8,9],113 [1,2,3,0, 12,13],114 115 [0,1,2,3, 1,3],116 [3,0,1,2, 5,5],117 [2,3,0,1, 9,9],118 [1,2,3,0, 13,13],119 120 [0,1,2,3, 2,3],121 [3,0,1,2, 6,5],122 [2,3,0,1, 10,9],123 [1,2,3,0, 14,13],124 125 [0,1,2,3, 3,3],126 [3,0,1,2, 7,5],127 [2,3,0,1, 11,9],128 [1,2,3,0, 15,13],129 ]130 131 # round 3 table - [abcd k s]132 _round3 = [133 [0,1,2,3, 0,3],134 [3,0,1,2, 8,9],135 [2,3,0,1, 4,11],136 [1,2,3,0, 12,15],137 138 [0,1,2,3, 2,3],139 [3,0,1,2, 10,9],140 [2,3,0,1, 6,11],141 [1,2,3,0, 14,15],142 143 [0,1,2,3, 1,3],144 [3,0,1,2, 9,9],145 [2,3,0,1, 5,11],146 [1,2,3,0, 13,15],147 148 [0,1,2,3, 3,3],149 [3,0,1,2, 11,9],150 [2,3,0,1, 7,11],151 [1,2,3,0, 15,15],152 ]153 154 def _process(self, block):155 """process 64 byte block"""156 # unpack block into 16 32-bit ints157 X = struct.unpack("<16I", block)158 159 # clone state160 orig = self._state161 state = list(orig)162 163 # round 1 - F function - (x&y)|(~x & z)164 for a,b,c,d,k,s in self._round1:165 t = (state[a] + F(state[b],state[c],state[d]) + X[k]) & MASK_32166 state[a] = ((t<<s) & MASK_32) + (t>>(32-s))167 168 # round 2 - G function169 for a,b,c,d,k,s in self._round2:170 t = (state[a] + G(state[b],state[c],state[d]) + X[k] + 0x5a827999) & MASK_32171 state[a] = ((t<<s) & MASK_32) + (t>>(32-s))172 173 # round 3 - H function - x ^ y ^ z174 for a,b,c,d,k,s in self._round3:175 t = (state[a] + (state[b] ^ state[c] ^ state[d]) + X[k] + 0x6ed9eba1) & MASK_32176 state[a] = ((t<<s) & MASK_32) + (t>>(32-s))177 178 # add back into original state179 for i in irange(4):180 orig[i] = (orig[i]+state[i]) & MASK_32181 182 def update(self, content):183 if not isinstance(content, bytes):184 if PY3:185 raise TypeError("expected bytes")186 else:187 # replicate behavior of hashlib under py2188 content = content.encode("ascii")189 buf = self._buf190 if buf:191 content = buf + content192 idx = 0193 end = len(content)194 while True:195 next = idx + 64196 if next <= end:197 self._process(content[idx:next])198 self._count += 1199 idx = next200 else:201 self._buf = content[idx:]202 return203 204 def copy(self):205 other = md4()206 other._count = self._count207 other._state = list(self._state)208 other._buf = self._buf209 return other210 211 def digest(self):212 # NOTE: backing up state so we can restore it after _process is called,213 # in case object is updated again (this is only attr altered by this method)214 orig = list(self._state)215 216 # final block: buf + 0x80,217 # then 0x00 padding until congruent w/ 56 mod 64 bytes218 # then last 8 bytes = msg length in bits219 buf = self._buf220 msglen = self._count*512 + len(buf)*8221 block = buf + b'\x80' + b'\x00' * ((119-len(buf)) % 64) + \222 struct.pack("<2I", msglen & MASK_32, (msglen>>32) & MASK_32)223 if len(block) == 128:224 self._process(block[:64])225 self._process(block[64:])226 else:227 assert len(block) == 64228 self._process(block)229 230 # render digest & restore un-finalized state231 out = struct.pack("<4I", *self._state)232 self._state = orig233 return out234 235 def hexdigest(self):236 return bascii_to_str(hexlify(self.digest()))237 238 #===================================================================239 # eoc240 #===================================================================241 242#=============================================================================243# eof244#=============================================================================245 