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.
		
		
		
		
		
			
		
			
				
					384 lines
				
				14 KiB
			
		
		
			
		
	
	
					384 lines
				
				14 KiB
			| 
								 
											3 years ago
										 
									 | 
							
								"""passlib.handlers.scrypt -- scrypt password hash"""
							 | 
						||
| 
								 | 
							
								#=============================================================================
							 | 
						||
| 
								 | 
							
								# imports
							 | 
						||
| 
								 | 
							
								#=============================================================================
							 | 
						||
| 
								 | 
							
								from __future__ import with_statement, absolute_import
							 | 
						||
| 
								 | 
							
								# core
							 | 
						||
| 
								 | 
							
								import logging; log = logging.getLogger(__name__)
							 | 
						||
| 
								 | 
							
								# site
							 | 
						||
| 
								 | 
							
								# pkg
							 | 
						||
| 
								 | 
							
								from passlib.crypto import scrypt as _scrypt
							 | 
						||
| 
								 | 
							
								from passlib.utils import h64, to_bytes
							 | 
						||
| 
								 | 
							
								from passlib.utils.binary import h64, b64s_decode, b64s_encode
							 | 
						||
| 
								 | 
							
								from passlib.utils.compat import u, bascii_to_str, suppress_cause
							 | 
						||
| 
								 | 
							
								from passlib.utils.decor import classproperty
							 | 
						||
| 
								 | 
							
								import passlib.utils.handlers as uh
							 | 
						||
| 
								 | 
							
								# local
							 | 
						||
| 
								 | 
							
								__all__ = [
							 | 
						||
| 
								 | 
							
								    "scrypt",
							 | 
						||
| 
								 | 
							
								]
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#=============================================================================
							 | 
						||
| 
								 | 
							
								# scrypt format identifiers
							 | 
						||
| 
								 | 
							
								#=============================================================================
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								IDENT_SCRYPT = u("$scrypt$")  # identifier used by passlib
							 | 
						||
| 
								 | 
							
								IDENT_7 = u("$7$")  # used by official scrypt spec
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								_UDOLLAR = u("$")
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#=============================================================================
							 | 
						||
| 
								 | 
							
								# handler
							 | 
						||
| 
								 | 
							
								#=============================================================================
							 | 
						||
| 
								 | 
							
								class scrypt(uh.ParallelismMixin, uh.HasRounds, uh.HasRawSalt, uh.HasRawChecksum, uh.HasManyIdents,
							 | 
						||
| 
								 | 
							
								             uh.GenericHandler):
							 | 
						||
| 
								 | 
							
								    """This class implements an SCrypt-based password [#scrypt-home]_ hash, and follows the :ref:`password-hash-api`.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    It supports a variable-length salt, a variable number of rounds,
							 | 
						||
| 
								 | 
							
								    as well as some custom tuning parameters unique to scrypt (see below).
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    The :meth:`~passlib.ifc.PasswordHash.using` method accepts the following optional keywords:
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    :type salt: str
							 | 
						||
| 
								 | 
							
								    :param salt:
							 | 
						||
| 
								 | 
							
								        Optional salt string.
							 | 
						||
| 
								 | 
							
								        If specified, the length must be between 0-1024 bytes.
							 | 
						||
| 
								 | 
							
								        If not specified, one will be auto-generated (this is recommended).
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    :type salt_size: int
							 | 
						||
| 
								 | 
							
								    :param salt_size:
							 | 
						||
| 
								 | 
							
								        Optional number of bytes to use when autogenerating new salts.
							 | 
						||
| 
								 | 
							
								        Defaults to 16 bytes, but can be any value between 0 and 1024.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    :type rounds: int
							 | 
						||
| 
								 | 
							
								    :param rounds:
							 | 
						||
| 
								 | 
							
								        Optional number of rounds to use.
							 | 
						||
| 
								 | 
							
								        Defaults to 16, but must be within ``range(1,32)``.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        .. warning::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            Unlike many hash algorithms, increasing the rounds value
							 | 
						||
| 
								 | 
							
								            will increase both the time *and memory* required to hash a password.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    :type block_size: int
							 | 
						||
| 
								 | 
							
								    :param block_size:
							 | 
						||
| 
								 | 
							
								        Optional block size to pass to scrypt hash function (the ``r`` parameter).
							 | 
						||
| 
								 | 
							
								        Useful for tuning scrypt to optimal performance for your CPU architecture.
							 | 
						||
| 
								 | 
							
								        Defaults to 8.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    :type parallelism: int
							 | 
						||
| 
								 | 
							
								    :param parallelism:
							 | 
						||
| 
								 | 
							
								        Optional parallelism to pass to scrypt hash function (the ``p`` parameter).
							 | 
						||
| 
								 | 
							
								        Defaults to 1.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    :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.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    .. note::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        The underlying scrypt hash function has a number of limitations
							 | 
						||
| 
								 | 
							
								        on it's parameter values, which forbids certain combinations of settings.
							 | 
						||
| 
								 | 
							
								        The requirements are:
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        * ``linear_rounds = 2**<some positive integer>``
							 | 
						||
| 
								 | 
							
								        * ``linear_rounds < 2**(16 * block_size)``
							 | 
						||
| 
								 | 
							
								        * ``block_size * parallelism <= 2**30-1``
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    .. todo::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        This class currently does not support configuring default values
							 | 
						||
| 
								 | 
							
								        for ``block_size`` or ``parallelism`` via a :class:`~passlib.context.CryptContext`
							 | 
						||
| 
								 | 
							
								        configuration.
							 | 
						||
| 
								 | 
							
								    """
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    #===================================================================
							 | 
						||
| 
								 | 
							
								    # class attrs
							 | 
						||
| 
								 | 
							
								    #===================================================================
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    #------------------------
							 | 
						||
| 
								 | 
							
								    # PasswordHash
							 | 
						||
| 
								 | 
							
								    #------------------------
							 | 
						||
| 
								 | 
							
								    name = "scrypt"
							 | 
						||
| 
								 | 
							
								    setting_kwds = ("ident", "salt", "salt_size", "rounds", "block_size", "parallelism")
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    #------------------------
							 | 
						||
| 
								 | 
							
								    # GenericHandler
							 | 
						||
| 
								 | 
							
								    #------------------------
							 | 
						||
| 
								 | 
							
								    # NOTE: scrypt supports arbitrary output sizes. since it's output runs through
							 | 
						||
| 
								 | 
							
								    #       pbkdf2-hmac-sha256 before returning, and this could be raised eventually...
							 | 
						||
| 
								 | 
							
								    #       but a 256-bit digest is more than sufficient for password hashing.
							 | 
						||
| 
								 | 
							
								    # XXX: make checksum size configurable? could merge w/ argon2 code that does this.
							 | 
						||
| 
								 | 
							
								    checksum_size = 32
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    #------------------------
							 | 
						||
| 
								 | 
							
								    # HasManyIdents
							 | 
						||
| 
								 | 
							
								    #------------------------
							 | 
						||
| 
								 | 
							
								    default_ident = IDENT_SCRYPT
							 | 
						||
| 
								 | 
							
								    ident_values = (IDENT_SCRYPT, IDENT_7)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    #------------------------
							 | 
						||
| 
								 | 
							
								    # HasRawSalt
							 | 
						||
| 
								 | 
							
								    #------------------------
							 | 
						||
| 
								 | 
							
								    default_salt_size = 16
							 | 
						||
| 
								 | 
							
								    max_salt_size = 1024
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    #------------------------
							 | 
						||
| 
								 | 
							
								    # HasRounds
							 | 
						||
| 
								 | 
							
								    #------------------------
							 | 
						||
| 
								 | 
							
								    # TODO: would like to dynamically pick this based on system
							 | 
						||
| 
								 | 
							
								    default_rounds = 16
							 | 
						||
| 
								 | 
							
								    min_rounds = 1
							 | 
						||
| 
								 | 
							
								    max_rounds = 31  # limited by scrypt alg
							 | 
						||
| 
								 | 
							
								    rounds_cost = "log2"
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    # TODO: make default block size configurable via using(), and deprecatable via .needs_update()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    #===================================================================
							 | 
						||
| 
								 | 
							
								    # instance attrs
							 | 
						||
| 
								 | 
							
								    #===================================================================
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    #: default parallelism setting (min=1 currently hardcoded in mixin)
							 | 
						||
| 
								 | 
							
								    parallelism = 1
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    #: default block size setting
							 | 
						||
| 
								 | 
							
								    block_size = 8
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    #===================================================================
							 | 
						||
| 
								 | 
							
								    # variant constructor
							 | 
						||
| 
								 | 
							
								    #===================================================================
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    @classmethod
							 | 
						||
| 
								 | 
							
								    def using(cls, block_size=None, **kwds):
							 | 
						||
| 
								 | 
							
								        subcls = super(scrypt, cls).using(**kwds)
							 | 
						||
| 
								 | 
							
								        if block_size is not None:
							 | 
						||
| 
								 | 
							
								            if isinstance(block_size, uh.native_string_types):
							 | 
						||
| 
								 | 
							
								                block_size = int(block_size)
							 | 
						||
| 
								 | 
							
								            subcls.block_size = subcls._norm_block_size(block_size, relaxed=kwds.get("relaxed"))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # make sure param combination is valid for scrypt()
							 | 
						||
| 
								 | 
							
								        try:
							 | 
						||
| 
								 | 
							
								            _scrypt.validate(1 << cls.default_rounds, cls.block_size, cls.parallelism)
							 | 
						||
| 
								 | 
							
								        except ValueError as err:
							 | 
						||
| 
								 | 
							
								            raise suppress_cause(ValueError("scrypt: invalid settings combination: " + str(err)))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        return subcls
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    #===================================================================
							 | 
						||
| 
								 | 
							
								    # parsing
							 | 
						||
| 
								 | 
							
								    #===================================================================
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    @classmethod
							 | 
						||
| 
								 | 
							
								    def from_string(cls, hash):
							 | 
						||
| 
								 | 
							
								        return cls(**cls.parse(hash))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    @classmethod
							 | 
						||
| 
								 | 
							
								    def parse(cls, hash):
							 | 
						||
| 
								 | 
							
								        ident, suffix = cls._parse_ident(hash)
							 | 
						||
| 
								 | 
							
								        func = getattr(cls, "_parse_%s_string" % ident.strip(_UDOLLAR), None)
							 | 
						||
| 
								 | 
							
								        if func:
							 | 
						||
| 
								 | 
							
								            return func(suffix)
							 | 
						||
| 
								 | 
							
								        else:
							 | 
						||
| 
								 | 
							
								            raise uh.exc.InvalidHashError(cls)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    #
							 | 
						||
| 
								 | 
							
								    # passlib's format:
							 | 
						||
| 
								 | 
							
								    #   $scrypt$ln=<logN>,r=<r>,p=<p>$<salt>[$<digest>]
							 | 
						||
| 
								 | 
							
								    # where:
							 | 
						||
| 
								 | 
							
								    #   logN, r, p -- decimal-encoded positive integer, no zero-padding
							 | 
						||
| 
								 | 
							
								    #   logN -- log cost setting
							 | 
						||
| 
								 | 
							
								    #   r -- block size setting (usually 8)
							 | 
						||
| 
								 | 
							
								    #   p -- parallelism setting (usually 1)
							 | 
						||
| 
								 | 
							
								    #   salt, digest -- b64-nopad encoded bytes
							 | 
						||
| 
								 | 
							
								    #
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    @classmethod
							 | 
						||
| 
								 | 
							
								    def _parse_scrypt_string(cls, suffix):
							 | 
						||
| 
								 | 
							
								        # break params, salt, and digest sections
							 | 
						||
| 
								 | 
							
								        parts = suffix.split("$")
							 | 
						||
| 
								 | 
							
								        if len(parts) == 3:
							 | 
						||
| 
								 | 
							
								            params, salt, digest = parts
							 | 
						||
| 
								 | 
							
								        elif len(parts) == 2:
							 | 
						||
| 
								 | 
							
								            params, salt = parts
							 | 
						||
| 
								 | 
							
								            digest = None
							 | 
						||
| 
								 | 
							
								        else:
							 | 
						||
| 
								 | 
							
								            raise uh.exc.MalformedHashError(cls, "malformed hash")
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # break params apart
							 | 
						||
| 
								 | 
							
								        parts = params.split(",")
							 | 
						||
| 
								 | 
							
								        if len(parts) == 3:
							 | 
						||
| 
								 | 
							
								            nstr, bstr, pstr = parts
							 | 
						||
| 
								 | 
							
								            assert nstr.startswith("ln=")
							 | 
						||
| 
								 | 
							
								            assert bstr.startswith("r=")
							 | 
						||
| 
								 | 
							
								            assert pstr.startswith("p=")
							 | 
						||
| 
								 | 
							
								        else:
							 | 
						||
| 
								 | 
							
								            raise uh.exc.MalformedHashError(cls, "malformed settings field")
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        return dict(
							 | 
						||
| 
								 | 
							
								            ident=IDENT_SCRYPT,
							 | 
						||
| 
								 | 
							
								            rounds=int(nstr[3:]),
							 | 
						||
| 
								 | 
							
								            block_size=int(bstr[2:]),
							 | 
						||
| 
								 | 
							
								            parallelism=int(pstr[2:]),
							 | 
						||
| 
								 | 
							
								            salt=b64s_decode(salt.encode("ascii")),
							 | 
						||
| 
								 | 
							
								            checksum=b64s_decode(digest.encode("ascii")) if digest else None,
							 | 
						||
| 
								 | 
							
								            )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    #
							 | 
						||
| 
								 | 
							
								    # official format specification defined at
							 | 
						||
| 
								 | 
							
								    #   https://gitlab.com/jas/scrypt-unix-crypt/blob/master/unix-scrypt.txt
							 | 
						||
| 
								 | 
							
								    # format:
							 | 
						||
| 
								 | 
							
								    #   $7$<N><rrrrr><ppppp><salt...>[$<digest>]
							 | 
						||
| 
								 | 
							
								    #       0  12345  67890  1
							 | 
						||
| 
								 | 
							
								    # where:
							 | 
						||
| 
								 | 
							
								    #   All bytes use h64-little-endian encoding
							 | 
						||
| 
								 | 
							
								    #   N: 6-bit log cost setting
							 | 
						||
| 
								 | 
							
								    #   r: 30-bit block size setting
							 | 
						||
| 
								 | 
							
								    #   p: 30-bit parallelism setting
							 | 
						||
| 
								 | 
							
								    #   salt: variable length salt bytes
							 | 
						||
| 
								 | 
							
								    #   digest: fixed 32-byte digest
							 | 
						||
| 
								 | 
							
								    #
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    @classmethod
							 | 
						||
| 
								 | 
							
								    def _parse_7_string(cls, suffix):
							 | 
						||
| 
								 | 
							
								        # XXX: annoyingly, official spec embeds salt *raw*, yet doesn't specify a hash encoding.
							 | 
						||
| 
								 | 
							
								        #      so assuming only h64 chars are valid for salt, and are ASCII encoded.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # split into params & digest
							 | 
						||
| 
								 | 
							
								        parts = suffix.encode("ascii").split(b"$")
							 | 
						||
| 
								 | 
							
								        if len(parts) == 2:
							 | 
						||
| 
								 | 
							
								            params, digest = parts
							 | 
						||
| 
								 | 
							
								        elif len(parts) == 1:
							 | 
						||
| 
								 | 
							
								            params, = parts
							 | 
						||
| 
								 | 
							
								            digest = None
							 | 
						||
| 
								 | 
							
								        else:
							 | 
						||
| 
								 | 
							
								            raise uh.exc.MalformedHashError()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # parse params & return
							 | 
						||
| 
								 | 
							
								        if len(params) < 11:
							 | 
						||
| 
								 | 
							
								            raise uh.exc.MalformedHashError(cls, "params field too short")
							 | 
						||
| 
								 | 
							
								        return dict(
							 | 
						||
| 
								 | 
							
								            ident=IDENT_7,
							 | 
						||
| 
								 | 
							
								            rounds=h64.decode_int6(params[:1]),
							 | 
						||
| 
								 | 
							
								            block_size=h64.decode_int30(params[1:6]),
							 | 
						||
| 
								 | 
							
								            parallelism=h64.decode_int30(params[6:11]),
							 | 
						||
| 
								 | 
							
								            salt=params[11:],
							 | 
						||
| 
								 | 
							
								            checksum=h64.decode_bytes(digest) if digest else None,
							 | 
						||
| 
								 | 
							
								        )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    #===================================================================
							 | 
						||
| 
								 | 
							
								    # formatting
							 | 
						||
| 
								 | 
							
								    #===================================================================
							 | 
						||
| 
								 | 
							
								    def to_string(self):
							 | 
						||
| 
								 | 
							
								        ident = self.ident
							 | 
						||
| 
								 | 
							
								        if ident == IDENT_SCRYPT:
							 | 
						||
| 
								 | 
							
								            return "$scrypt$ln=%d,r=%d,p=%d$%s$%s" % (
							 | 
						||
| 
								 | 
							
								                self.rounds,
							 | 
						||
| 
								 | 
							
								                self.block_size,
							 | 
						||
| 
								 | 
							
								                self.parallelism,
							 | 
						||
| 
								 | 
							
								                bascii_to_str(b64s_encode(self.salt)),
							 | 
						||
| 
								 | 
							
								                bascii_to_str(b64s_encode(self.checksum)),
							 | 
						||
| 
								 | 
							
								            )
							 | 
						||
| 
								 | 
							
								        else:
							 | 
						||
| 
								 | 
							
								            assert ident == IDENT_7
							 | 
						||
| 
								 | 
							
								            salt = self.salt
							 | 
						||
| 
								 | 
							
								            try:
							 | 
						||
| 
								 | 
							
								                salt.decode("ascii")
							 | 
						||
| 
								 | 
							
								            except UnicodeDecodeError:
							 | 
						||
| 
								 | 
							
								                raise suppress_cause(NotImplementedError("scrypt $7$ hashes dont support non-ascii salts"))
							 | 
						||
| 
								 | 
							
								            return bascii_to_str(b"".join([
							 | 
						||
| 
								 | 
							
								                b"$7$",
							 | 
						||
| 
								 | 
							
								                h64.encode_int6(self.rounds),
							 | 
						||
| 
								 | 
							
								                h64.encode_int30(self.block_size),
							 | 
						||
| 
								 | 
							
								                h64.encode_int30(self.parallelism),
							 | 
						||
| 
								 | 
							
								                self.salt,
							 | 
						||
| 
								 | 
							
								                b"$",
							 | 
						||
| 
								 | 
							
								                h64.encode_bytes(self.checksum)
							 | 
						||
| 
								 | 
							
								            ]))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    #===================================================================
							 | 
						||
| 
								 | 
							
								    # init
							 | 
						||
| 
								 | 
							
								    #===================================================================
							 | 
						||
| 
								 | 
							
								    def __init__(self, block_size=None, **kwds):
							 | 
						||
| 
								 | 
							
								        super(scrypt, self).__init__(**kwds)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # init block size
							 | 
						||
| 
								 | 
							
								        if block_size is None:
							 | 
						||
| 
								 | 
							
								            assert uh.validate_default_value(self, self.block_size, self._norm_block_size,
							 | 
						||
| 
								 | 
							
								                                             param="block_size")
							 | 
						||
| 
								 | 
							
								        else:
							 | 
						||
| 
								 | 
							
								            self.block_size = self._norm_block_size(block_size)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # NOTE: if hash contains invalid complex constraint, relying on error
							 | 
						||
| 
								 | 
							
								        #       being raised by scrypt call in _calc_checksum()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    @classmethod
							 | 
						||
| 
								 | 
							
								    def _norm_block_size(cls, block_size, relaxed=False):
							 | 
						||
| 
								 | 
							
								        return uh.norm_integer(cls, block_size, min=1, param="block_size", relaxed=relaxed)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def _generate_salt(self):
							 | 
						||
| 
								 | 
							
								        salt = super(scrypt, self)._generate_salt()
							 | 
						||
| 
								 | 
							
								        if self.ident == IDENT_7:
							 | 
						||
| 
								 | 
							
								            # this format doesn't support non-ascii salts.
							 | 
						||
| 
								 | 
							
								            # as workaround, we take raw bytes, encoded to base64
							 | 
						||
| 
								 | 
							
								            salt = b64s_encode(salt)
							 | 
						||
| 
								 | 
							
								        return salt
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    #===================================================================
							 | 
						||
| 
								 | 
							
								    # backend configuration
							 | 
						||
| 
								 | 
							
								    # NOTE: this following HasManyBackends' API, but provides it's own implementation,
							 | 
						||
| 
								 | 
							
								    #       which actually switches the backend that 'passlib.crypto.scrypt.scrypt()' uses.
							 | 
						||
| 
								 | 
							
								    #===================================================================
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    @classproperty
							 | 
						||
| 
								 | 
							
								    def backends(cls):
							 | 
						||
| 
								 | 
							
								        return _scrypt.backend_values
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    @classmethod
							 | 
						||
| 
								 | 
							
								    def get_backend(cls):
							 | 
						||
| 
								 | 
							
								        return _scrypt.backend
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    @classmethod
							 | 
						||
| 
								 | 
							
								    def has_backend(cls, name="any"):
							 | 
						||
| 
								 | 
							
								        try:
							 | 
						||
| 
								 | 
							
								            cls.set_backend(name, dryrun=True)
							 | 
						||
| 
								 | 
							
								            return True
							 | 
						||
| 
								 | 
							
								        except uh.exc.MissingBackendError:
							 | 
						||
| 
								 | 
							
								            return False
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    @classmethod
							 | 
						||
| 
								 | 
							
								    def set_backend(cls, name="any", dryrun=False):
							 | 
						||
| 
								 | 
							
								        _scrypt._set_backend(name, dryrun=dryrun)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    #===================================================================
							 | 
						||
| 
								 | 
							
								    # digest calculation
							 | 
						||
| 
								 | 
							
								    #===================================================================
							 | 
						||
| 
								 | 
							
								    def _calc_checksum(self, secret):
							 | 
						||
| 
								 | 
							
								        secret = to_bytes(secret, param="secret")
							 | 
						||
| 
								 | 
							
								        return _scrypt.scrypt(secret, self.salt, n=(1 << self.rounds), r=self.block_size,
							 | 
						||
| 
								 | 
							
								                              p=self.parallelism, keylen=self.checksum_size)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    #===================================================================
							 | 
						||
| 
								 | 
							
								    # hash migration
							 | 
						||
| 
								 | 
							
								    #===================================================================
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def _calc_needs_update(self, **kwds):
							 | 
						||
| 
								 | 
							
								        """
							 | 
						||
| 
								 | 
							
								        mark hash as needing update if rounds is outside desired bounds.
							 | 
						||
| 
								 | 
							
								        """
							 | 
						||
| 
								 | 
							
								        # XXX: for now, marking all hashes which don't have matching block_size setting
							 | 
						||
| 
								 | 
							
								        if self.block_size != type(self).block_size:
							 | 
						||
| 
								 | 
							
								            return True
							 | 
						||
| 
								 | 
							
								        return super(scrypt, self)._calc_needs_update(**kwds)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    #===================================================================
							 | 
						||
| 
								 | 
							
								    # eoc
							 | 
						||
| 
								 | 
							
								    #===================================================================
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#=============================================================================
							 | 
						||
| 
								 | 
							
								# eof
							 | 
						||
| 
								 | 
							
								#=============================================================================
							 |