You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					245 lines
				
				6.7 KiB
			
		
		
			
		
	
	
					245 lines
				
				6.7 KiB
			| 
								 
											3 years ago
										 
									 | 
							
								"""
							 | 
						||
| 
								 | 
							
								passlib.crypto._md4 -- fallback implementation of MD4
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Helper implementing insecure and obsolete md4 algorithm.
							 | 
						||
| 
								 | 
							
								used for NTHASH format, which is also insecure and broken,
							 | 
						||
| 
								 | 
							
								since it's just md4(password).
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Implementated based on rfc at http://www.faqs.org/rfcs/rfc1320.html
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								.. note::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    This shouldn't be imported directly, it's merely used conditionally
							 | 
						||
| 
								 | 
							
								    by ``passlib.crypto.lookup_hash()`` when a native implementation can't be found.
							 | 
						||
| 
								 | 
							
								"""
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#=============================================================================
							 | 
						||
| 
								 | 
							
								# imports
							 | 
						||
| 
								 | 
							
								#=============================================================================
							 | 
						||
| 
								 | 
							
								# core
							 | 
						||
| 
								 | 
							
								from binascii import hexlify
							 | 
						||
| 
								 | 
							
								import struct
							 | 
						||
| 
								 | 
							
								# site
							 | 
						||
| 
								 | 
							
								from passlib.utils.compat import bascii_to_str, irange, PY3
							 | 
						||
| 
								 | 
							
								# local
							 | 
						||
| 
								 | 
							
								__all__ = ["md4"]
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#=============================================================================
							 | 
						||
| 
								 | 
							
								# utils
							 | 
						||
| 
								 | 
							
								#=============================================================================
							 | 
						||
| 
								 | 
							
								def F(x,y,z):
							 | 
						||
| 
								 | 
							
								    return (x&y) | ((~x) & z)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								def G(x,y,z):
							 | 
						||
| 
								 | 
							
								    return (x&y) | (x&z) | (y&z)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								##def H(x,y,z):
							 | 
						||
| 
								 | 
							
								##    return x ^ y ^ z
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								MASK_32 = 2**32-1
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#=============================================================================
							 | 
						||
| 
								 | 
							
								# main class
							 | 
						||
| 
								 | 
							
								#=============================================================================
							 | 
						||
| 
								 | 
							
								class md4(object):
							 | 
						||
| 
								 | 
							
								    """pep-247 compatible implementation of MD4 hash algorithm
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    .. attribute:: digest_size
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        size of md4 digest in bytes (16 bytes)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    .. method:: update
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        update digest by appending additional content
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    .. method:: copy
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        create clone of digest object, including current state
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    .. method:: digest
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        return bytes representing md4 digest of current content
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    .. method:: hexdigest
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        return hexadecimal version of digest
							 | 
						||
| 
								 | 
							
								    """
							 | 
						||
| 
								 | 
							
								    # FIXME: make this follow hash object PEP better.
							 | 
						||
| 
								 | 
							
								    # FIXME: this isn't threadsafe
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    name = "md4"
							 | 
						||
| 
								 | 
							
								    digest_size = digestsize = 16
							 | 
						||
| 
								 | 
							
								    block_size = 64
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    _count = 0 # number of 64-byte blocks processed so far (not including _buf)
							 | 
						||
| 
								 | 
							
								    _state = None # list of [a,b,c,d] 32 bit ints used as internal register
							 | 
						||
| 
								 | 
							
								    _buf = None # data processed in 64 byte blocks, this holds leftover from last update
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def __init__(self, content=None):
							 | 
						||
| 
								 | 
							
								        self._count = 0
							 | 
						||
| 
								 | 
							
								        self._state = [0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476]
							 | 
						||
| 
								 | 
							
								        self._buf = b''
							 | 
						||
| 
								 | 
							
								        if content:
							 | 
						||
| 
								 | 
							
								            self.update(content)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    # round 1 table - [abcd k s]
							 | 
						||
| 
								 | 
							
								    _round1 = [
							 | 
						||
| 
								 | 
							
								        [0,1,2,3, 0,3],
							 | 
						||
| 
								 | 
							
								        [3,0,1,2, 1,7],
							 | 
						||
| 
								 | 
							
								        [2,3,0,1, 2,11],
							 | 
						||
| 
								 | 
							
								        [1,2,3,0, 3,19],
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        [0,1,2,3, 4,3],
							 | 
						||
| 
								 | 
							
								        [3,0,1,2, 5,7],
							 | 
						||
| 
								 | 
							
								        [2,3,0,1, 6,11],
							 | 
						||
| 
								 | 
							
								        [1,2,3,0, 7,19],
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        [0,1,2,3, 8,3],
							 | 
						||
| 
								 | 
							
								        [3,0,1,2, 9,7],
							 | 
						||
| 
								 | 
							
								        [2,3,0,1, 10,11],
							 | 
						||
| 
								 | 
							
								        [1,2,3,0, 11,19],
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        [0,1,2,3, 12,3],
							 | 
						||
| 
								 | 
							
								        [3,0,1,2, 13,7],
							 | 
						||
| 
								 | 
							
								        [2,3,0,1, 14,11],
							 | 
						||
| 
								 | 
							
								        [1,2,3,0, 15,19],
							 | 
						||
| 
								 | 
							
								    ]
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    # round 2 table - [abcd k s]
							 | 
						||
| 
								 | 
							
								    _round2 = [
							 | 
						||
| 
								 | 
							
								        [0,1,2,3, 0,3],
							 | 
						||
| 
								 | 
							
								        [3,0,1,2, 4,5],
							 | 
						||
| 
								 | 
							
								        [2,3,0,1, 8,9],
							 | 
						||
| 
								 | 
							
								        [1,2,3,0, 12,13],
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        [0,1,2,3, 1,3],
							 | 
						||
| 
								 | 
							
								        [3,0,1,2, 5,5],
							 | 
						||
| 
								 | 
							
								        [2,3,0,1, 9,9],
							 | 
						||
| 
								 | 
							
								        [1,2,3,0, 13,13],
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        [0,1,2,3, 2,3],
							 | 
						||
| 
								 | 
							
								        [3,0,1,2, 6,5],
							 | 
						||
| 
								 | 
							
								        [2,3,0,1, 10,9],
							 | 
						||
| 
								 | 
							
								        [1,2,3,0, 14,13],
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        [0,1,2,3, 3,3],
							 | 
						||
| 
								 | 
							
								        [3,0,1,2, 7,5],
							 | 
						||
| 
								 | 
							
								        [2,3,0,1, 11,9],
							 | 
						||
| 
								 | 
							
								        [1,2,3,0, 15,13],
							 | 
						||
| 
								 | 
							
								    ]
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    # round 3 table - [abcd k s]
							 | 
						||
| 
								 | 
							
								    _round3 = [
							 | 
						||
| 
								 | 
							
								        [0,1,2,3, 0,3],
							 | 
						||
| 
								 | 
							
								        [3,0,1,2, 8,9],
							 | 
						||
| 
								 | 
							
								        [2,3,0,1, 4,11],
							 | 
						||
| 
								 | 
							
								        [1,2,3,0, 12,15],
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        [0,1,2,3, 2,3],
							 | 
						||
| 
								 | 
							
								        [3,0,1,2, 10,9],
							 | 
						||
| 
								 | 
							
								        [2,3,0,1, 6,11],
							 | 
						||
| 
								 | 
							
								        [1,2,3,0, 14,15],
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        [0,1,2,3, 1,3],
							 | 
						||
| 
								 | 
							
								        [3,0,1,2, 9,9],
							 | 
						||
| 
								 | 
							
								        [2,3,0,1, 5,11],
							 | 
						||
| 
								 | 
							
								        [1,2,3,0, 13,15],
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        [0,1,2,3, 3,3],
							 | 
						||
| 
								 | 
							
								        [3,0,1,2, 11,9],
							 | 
						||
| 
								 | 
							
								        [2,3,0,1, 7,11],
							 | 
						||
| 
								 | 
							
								        [1,2,3,0, 15,15],
							 | 
						||
| 
								 | 
							
								    ]
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def _process(self, block):
							 | 
						||
| 
								 | 
							
								        """process 64 byte block"""
							 | 
						||
| 
								 | 
							
								        # unpack block into 16 32-bit ints
							 | 
						||
| 
								 | 
							
								        X = struct.unpack("<16I", block)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # clone state
							 | 
						||
| 
								 | 
							
								        orig = self._state
							 | 
						||
| 
								 | 
							
								        state = list(orig)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # round 1 - F function - (x&y)|(~x & z)
							 | 
						||
| 
								 | 
							
								        for a,b,c,d,k,s in self._round1:
							 | 
						||
| 
								 | 
							
								            t = (state[a] + F(state[b],state[c],state[d]) + X[k]) & MASK_32
							 | 
						||
| 
								 | 
							
								            state[a] = ((t<<s) & MASK_32) + (t>>(32-s))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # round 2 - G function
							 | 
						||
| 
								 | 
							
								        for a,b,c,d,k,s in self._round2:
							 | 
						||
| 
								 | 
							
								            t = (state[a] + G(state[b],state[c],state[d]) + X[k] + 0x5a827999) & MASK_32
							 | 
						||
| 
								 | 
							
								            state[a] = ((t<<s) & MASK_32) + (t>>(32-s))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # round 3 - H function - x ^ y ^ z
							 | 
						||
| 
								 | 
							
								        for a,b,c,d,k,s in self._round3:
							 | 
						||
| 
								 | 
							
								            t = (state[a] + (state[b] ^ state[c] ^ state[d]) + X[k] + 0x6ed9eba1) & MASK_32
							 | 
						||
| 
								 | 
							
								            state[a] = ((t<<s) & MASK_32) + (t>>(32-s))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # add back into original state
							 | 
						||
| 
								 | 
							
								        for i in irange(4):
							 | 
						||
| 
								 | 
							
								            orig[i] = (orig[i]+state[i]) & MASK_32
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def update(self, content):
							 | 
						||
| 
								 | 
							
								        if not isinstance(content, bytes):
							 | 
						||
| 
								 | 
							
								            if PY3:
							 | 
						||
| 
								 | 
							
								                raise TypeError("expected bytes")
							 | 
						||
| 
								 | 
							
								            else:
							 | 
						||
| 
								 | 
							
								                # replicate behavior of hashlib under py2
							 | 
						||
| 
								 | 
							
								                content = content.encode("ascii")
							 | 
						||
| 
								 | 
							
								        buf = self._buf
							 | 
						||
| 
								 | 
							
								        if buf:
							 | 
						||
| 
								 | 
							
								            content = buf + content
							 | 
						||
| 
								 | 
							
								        idx = 0
							 | 
						||
| 
								 | 
							
								        end = len(content)
							 | 
						||
| 
								 | 
							
								        while True:
							 | 
						||
| 
								 | 
							
								            next = idx + 64
							 | 
						||
| 
								 | 
							
								            if next <= end:
							 | 
						||
| 
								 | 
							
								                self._process(content[idx:next])
							 | 
						||
| 
								 | 
							
								                self._count += 1
							 | 
						||
| 
								 | 
							
								                idx = next
							 | 
						||
| 
								 | 
							
								            else:
							 | 
						||
| 
								 | 
							
								                self._buf = content[idx:]
							 | 
						||
| 
								 | 
							
								                return
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def copy(self):
							 | 
						||
| 
								 | 
							
								        other = md4()
							 | 
						||
| 
								 | 
							
								        other._count = self._count
							 | 
						||
| 
								 | 
							
								        other._state = list(self._state)
							 | 
						||
| 
								 | 
							
								        other._buf = self._buf
							 | 
						||
| 
								 | 
							
								        return other
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def digest(self):
							 | 
						||
| 
								 | 
							
								        # NOTE: backing up state so we can restore it after _process is called,
							 | 
						||
| 
								 | 
							
								        #       in case object is updated again (this is only attr altered by this method)
							 | 
						||
| 
								 | 
							
								        orig = list(self._state)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # final block: buf + 0x80,
							 | 
						||
| 
								 | 
							
								        # then 0x00 padding until congruent w/ 56 mod 64 bytes
							 | 
						||
| 
								 | 
							
								        # then last 8 bytes = msg length in bits
							 | 
						||
| 
								 | 
							
								        buf = self._buf
							 | 
						||
| 
								 | 
							
								        msglen = self._count*512 + len(buf)*8
							 | 
						||
| 
								 | 
							
								        block = buf + b'\x80' + b'\x00' * ((119-len(buf)) % 64) + \
							 | 
						||
| 
								 | 
							
								            struct.pack("<2I", msglen & MASK_32, (msglen>>32) & MASK_32)
							 | 
						||
| 
								 | 
							
								        if len(block) == 128:
							 | 
						||
| 
								 | 
							
								            self._process(block[:64])
							 | 
						||
| 
								 | 
							
								            self._process(block[64:])
							 | 
						||
| 
								 | 
							
								        else:
							 | 
						||
| 
								 | 
							
								            assert len(block) == 64
							 | 
						||
| 
								 | 
							
								            self._process(block)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # render digest & restore un-finalized state
							 | 
						||
| 
								 | 
							
								        out = struct.pack("<4I", *self._state)
							 | 
						||
| 
								 | 
							
								        self._state = orig
							 | 
						||
| 
								 | 
							
								        return out
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def hexdigest(self):
							 | 
						||
| 
								 | 
							
								        return bascii_to_str(hexlify(self.digest()))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    #===================================================================
							 | 
						||
| 
								 | 
							
								    # eoc
							 | 
						||
| 
								 | 
							
								    #===================================================================
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#=============================================================================
							 | 
						||
| 
								 | 
							
								# eof
							 | 
						||
| 
								 | 
							
								#=============================================================================
							 |