"""passlib.handlers.sha1_crypt """ #============================================================================= # imports #============================================================================= # core from hmac import new as hmac from hashlib import sha1 import re import logging; log = logging.getLogger(__name__) from warnings import warn # site # pkg from passlib.utils import classproperty, h64, safe_crypt, test_crypt from passlib.utils.compat import b, bytes, u, uascii_to_str, unicode from passlib.utils.pbkdf2 import get_prf import passlib.utils.handlers as uh # local __all__ = [ ] #============================================================================= # sha1-crypt #============================================================================= _hmac_sha1 = get_prf("hmac-sha1")[0] _BNULL = b('\x00') class sha1_crypt(uh.HasManyBackends, uh.HasRounds, uh.HasSalt, uh.GenericHandler): """This class implements the SHA1-Crypt password hash, and follows the :ref:`password-hash-api`. It supports a variable-length salt, and a variable number of rounds. The :meth:`~passlib.ifc.PasswordHash.encrypt` and :meth:`~passlib.ifc.PasswordHash.genconfig` methods accept the following optional keywords: :type salt: str :param salt: Optional salt string. If not specified, an 8 character one will be autogenerated (this is recommended). If specified, it must be 0-64 characters, drawn from the regexp range ``[./0-9A-Za-z]``. :type salt_size: int :param salt_size: Optional number of bytes to use when autogenerating new salts. Defaults to 8 bytes, but can be any value between 0 and 64. :type rounds: int :param rounds: Optional number of rounds to use. Defaults to 480000, must be between 1 and 4294967295, inclusive. :type relaxed: bool :param relaxed: By default, providing an invalid value for one of the other keywords will result in a :exc:`ValueError`. If ``relaxed=True``, and the error can be corrected, a :exc:`~passlib.exc.PasslibHashWarning` will be issued instead. Correctable errors include ``rounds`` that are too small or too large, and ``salt`` strings that are too long. .. versionadded:: 1.6 """ #=================================================================== # class attrs #=================================================================== #--GenericHandler-- name = "sha1_crypt" setting_kwds = ("salt", "salt_size", "rounds") ident = u("$sha1$") checksum_size = 28 checksum_chars = uh.HASH64_CHARS #--HasSalt-- default_salt_size = 8 min_salt_size = 0 max_salt_size = 64 salt_chars = uh.HASH64_CHARS #--HasRounds-- default_rounds = 480000 # current passlib default min_rounds = 1 # really, this should be higher. max_rounds = 4294967295 # 32-bit integer limit rounds_cost = "linear" #=================================================================== # formatting #=================================================================== @classmethod def from_string(cls, hash): rounds, salt, chk = uh.parse_mc3(hash, cls.ident, handler=cls) return cls(rounds=rounds, salt=salt, checksum=chk) def to_string(self, config=False): chk = None if config else self.checksum return uh.render_mc3(self.ident, self.rounds, self.salt, chk) #=================================================================== # backend #=================================================================== backends = ("os_crypt", "builtin") _has_backend_builtin = True @classproperty def _has_backend_os_crypt(cls): return test_crypt("test", '$sha1$1$Wq3GL2Vp$C8U25GvfHS8qGHim' 'ExLaiSFlGkAe') def _calc_checksum_builtin(self, secret): if isinstance(secret, unicode): secret = secret.encode("utf-8") if _BNULL in secret: raise uh.exc.NullPasswordError(self) rounds = self.rounds # NOTE: this seed value is NOT the same as the config string result = (u("%s$sha1$%s") % (self.salt, rounds)).encode("ascii") # NOTE: this algorithm is essentially PBKDF1, modified to use HMAC. r = 0 while r < rounds: result = _hmac_sha1(secret, result) r += 1 return h64.encode_transposed_bytes(result, self._chk_offsets).decode("ascii") _chk_offsets = [ 2,1,0, 5,4,3, 8,7,6, 11,10,9, 14,13,12, 17,16,15, 0,19,18, ] def _calc_checksum_os_crypt(self, secret): config = self.to_string(config=True) hash = safe_crypt(secret, config) if hash: assert hash.startswith(config) and len(hash) == len(config) + 29 return hash[-28:] else: return self._calc_checksum_builtin(secret) #=================================================================== # eoc #=================================================================== #============================================================================= # eof #=============================================================================