codekingpro/portable-devtools
114k
1"""passlib.handlers.mssql - MS-SQL Password Hash2 3Notes4=====5MS-SQL has used a number of hash algs over the years,6most of which were exposed through the undocumented7'pwdencrypt' and 'pwdcompare' sql functions.8 9Known formats10-------------116.512 snefru hash, ascii encoded password13 no examples found14 157.016 snefru hash, unicode (what encoding?)17 saw ref that these blobs were 16 bytes in size18 no examples found19 20200021 byte string using displayed as 0x hex, using 0x0100 prefix.22 contains hashes of password and upper-case password.23 24200725 same as 2000, but without the upper-case hash.26 27refs28----------29https://blogs.msdn.com/b/lcris/archive/2007/04/30/sql-server-2005-about-login-password-hashes.aspx?Redirected=true30http://us.generation-nt.com/securing-passwords-hash-help-35429432.html31http://forum.md5decrypter.co.uk/topic230-mysql-and-mssql-get-password-hashes.aspx32http://www.theregister.co.uk/2002/07/08/cracking_ms_sql_server_passwords/33"""34#=============================================================================35# imports36#=============================================================================37# core38from binascii import hexlify, unhexlify39from hashlib import sha140import re41import logging; log = logging.getLogger(__name__)42from warnings import warn43# site44# pkg45from passlib.utils import consteq46from passlib.utils.compat import bascii_to_str, unicode, u47import passlib.utils.handlers as uh48# local49__all__ = [50 "mssql2000",51 "mssql2005",52]53 54#=============================================================================55# mssql 200056#=============================================================================57def _raw_mssql(secret, salt):58 assert isinstance(secret, unicode)59 assert isinstance(salt, bytes)60 return sha1(secret.encode("utf-16-le") + salt).digest()61 62BIDENT = b"0x0100"63##BIDENT2 = b("\x01\x00")64UIDENT = u("0x0100")65 66def _ident_mssql(hash, csize, bsize):67 """common identify for mssql 2000/2005"""68 if isinstance(hash, unicode):69 if len(hash) == csize and hash.startswith(UIDENT):70 return True71 elif isinstance(hash, bytes):72 if len(hash) == csize and hash.startswith(BIDENT):73 return True74 ##elif len(hash) == bsize and hash.startswith(BIDENT2): # raw bytes75 ## return True76 else:77 raise uh.exc.ExpectedStringError(hash, "hash")78 return False79 80def _parse_mssql(hash, csize, bsize, handler):81 """common parser for mssql 2000/2005; returns 4 byte salt + checksum"""82 if isinstance(hash, unicode):83 if len(hash) == csize and hash.startswith(UIDENT):84 try:85 return unhexlify(hash[6:].encode("utf-8"))86 except TypeError: # throw when bad char found87 pass88 elif isinstance(hash, bytes):89 # assumes ascii-compat encoding90 assert isinstance(hash, bytes)91 if len(hash) == csize and hash.startswith(BIDENT):92 try:93 return unhexlify(hash[6:])94 except TypeError: # throw when bad char found95 pass96 ##elif len(hash) == bsize and hash.startswith(BIDENT2): # raw bytes97 ## return hash[2:]98 else:99 raise uh.exc.ExpectedStringError(hash, "hash")100 raise uh.exc.InvalidHashError(handler)101 102class mssql2000(uh.HasRawSalt, uh.HasRawChecksum, uh.GenericHandler):103 """This class implements the password hash used by MS-SQL 2000, and follows the :ref:`password-hash-api`.104 105 It supports a fixed-length salt.106 107 The :meth:`~passlib.ifc.PasswordHash.using` method accepts the following optional keywords:108 109 :type salt: bytes110 :param salt:111 Optional salt string.112 If not specified, one will be autogenerated (this is recommended).113 If specified, it must be 4 bytes in length.114 115 :type relaxed: bool116 :param relaxed:117 By default, providing an invalid value for one of the other118 keywords will result in a :exc:`ValueError`. If ``relaxed=True``,119 and the error can be corrected, a :exc:`~passlib.exc.PasslibHashWarning`120 will be issued instead. Correctable errors include121 ``salt`` strings that are too long.122 """123 #===================================================================124 # algorithm information125 #===================================================================126 name = "mssql2000"127 setting_kwds = ("salt",)128 checksum_size = 40129 min_salt_size = max_salt_size = 4130 131 #===================================================================132 # formatting133 #===================================================================134 135 # 0100 - 2 byte identifier136 # 4 byte salt137 # 20 byte checksum138 # 20 byte checksum139 # = 46 bytes140 # encoded '0x' + 92 chars = 94141 142 @classmethod143 def identify(cls, hash):144 return _ident_mssql(hash, 94, 46)145 146 @classmethod147 def from_string(cls, hash):148 data = _parse_mssql(hash, 94, 46, cls)149 return cls(salt=data[:4], checksum=data[4:])150 151 def to_string(self):152 raw = self.salt + self.checksum153 # raw bytes format - BIDENT2 + raw154 return "0x0100" + bascii_to_str(hexlify(raw).upper())155 156 def _calc_checksum(self, secret):157 if isinstance(secret, bytes):158 secret = secret.decode("utf-8")159 salt = self.salt160 return _raw_mssql(secret, salt) + _raw_mssql(secret.upper(), salt)161 162 @classmethod163 def verify(cls, secret, hash):164 # NOTE: we only compare against the upper-case hash165 # XXX: add 'full' just to verify both checksums?166 uh.validate_secret(secret)167 self = cls.from_string(hash)168 chk = self.checksum169 if chk is None:170 raise uh.exc.MissingDigestError(cls)171 if isinstance(secret, bytes):172 secret = secret.decode("utf-8")173 result = _raw_mssql(secret.upper(), self.salt)174 return consteq(result, chk[20:])175 176#=============================================================================177# handler178#=============================================================================179class mssql2005(uh.HasRawSalt, uh.HasRawChecksum, uh.GenericHandler):180 """This class implements the password hash used by MS-SQL 2005, and follows the :ref:`password-hash-api`.181 182 It supports a fixed-length salt.183 184 The :meth:`~passlib.ifc.PasswordHash.using` method accepts the following optional keywords:185 186 :type salt: bytes187 :param salt:188 Optional salt string.189 If not specified, one will be autogenerated (this is recommended).190 If specified, it must be 4 bytes in length.191 192 :type relaxed: bool193 :param relaxed:194 By default, providing an invalid value for one of the other195 keywords will result in a :exc:`ValueError`. If ``relaxed=True``,196 and the error can be corrected, a :exc:`~passlib.exc.PasslibHashWarning`197 will be issued instead. Correctable errors include198 ``salt`` strings that are too long.199 """200 #===================================================================201 # algorithm information202 #===================================================================203 name = "mssql2005"204 setting_kwds = ("salt",)205 206 checksum_size = 20207 min_salt_size = max_salt_size = 4208 209 #===================================================================210 # formatting211 #===================================================================212 213 # 0x0100 - 2 byte identifier214 # 4 byte salt215 # 20 byte checksum216 # = 26 bytes217 # encoded '0x' + 52 chars = 54218 219 @classmethod220 def identify(cls, hash):221 return _ident_mssql(hash, 54, 26)222 223 @classmethod224 def from_string(cls, hash):225 data = _parse_mssql(hash, 54, 26, cls)226 return cls(salt=data[:4], checksum=data[4:])227 228 def to_string(self):229 raw = self.salt + self.checksum230 # raw bytes format - BIDENT2 + raw231 return "0x0100" + bascii_to_str(hexlify(raw)).upper()232 233 def _calc_checksum(self, secret):234 if isinstance(secret, bytes):235 secret = secret.decode("utf-8")236 return _raw_mssql(secret, self.salt)237 238 #===================================================================239 # eoc240 #===================================================================241 242#=============================================================================243# eof244#=============================================================================245 