def _load_settings(self): """ Update settings from django """ from django.conf import settings # TODO: would like to add support for inheriting config from a preset # (or from existing hasher state) and letting PASSLIB_CONFIG # be an update, not a replacement. # TODO: wrap and import any custom hashers as passlib handlers, # so they could be used in the passlib config. # load config from settings _UNSET = object() config = getattr(settings, "PASSLIB_CONFIG", _UNSET) if config is _UNSET: # XXX: should probably deprecate this alias config = getattr(settings, "PASSLIB_CONTEXT", _UNSET) if config is _UNSET: config = "passlib-default" if config is None: warn( "setting PASSLIB_CONFIG=None is deprecated, " "and support will be removed in Passlib 1.8, " "use PASSLIB_CONFIG='disabled' instead.", DeprecationWarning, ) config = "disabled" elif not isinstance(config, (unicode, bytes, dict)): raise exc.ExpectedTypeError(config, "str or dict", "PASSLIB_CONFIG") # load custom category func (if any) get_category = getattr(settings, "PASSLIB_GET_CATEGORY", None) if get_category and not callable(get_category): raise exc.ExpectedTypeError(get_category, "callable", "PASSLIB_GET_CATEGORY") # check if we've been disabled if config == "disabled": self.enabled = False return else: self.__dict__.pop("enabled", None) # resolve any preset aliases if isinstance(config, str) and "\n" not in config: config = get_preset_config(config) # setup category func if get_category: self.get_user_category = get_category else: self.__dict__.pop("get_category", None) # setup context self.context.load(config) self.reset_hashers()
def pbkdf1(digest, secret, salt, rounds, keylen=None): """pkcs#5 password-based key derivation v1.5 :arg digest: digest name or constructor. :arg secret: secret to use when generating the key. may be :class:`!bytes` or :class:`unicode` (encoded using UTF-8). :arg salt: salt string to use when generating key. may be :class:`!bytes` or :class:`unicode` (encoded using UTF-8). :param rounds: number of rounds to use to generate key. :arg keylen: number of bytes to generate (if omitted / ``None``, uses digest's native size) :returns: raw :class:`bytes` of generated key .. note:: This algorithm has been deprecated, new code should use PBKDF2. Among other limitations, ``keylen`` cannot be larger than the digest size of the specified hash. """ # resolve digest const, digest_size, block_size = lookup_hash(digest) # validate secret & salt secret = to_bytes(secret, param="secret") salt = to_bytes(salt, param="salt") # validate rounds if not isinstance(rounds, int_types): raise exc.ExpectedTypeError(rounds, "int", "rounds") if rounds < 1: raise ValueError("rounds must be at least 1") # validate keylen if keylen is None: keylen = digest_size elif not isinstance(keylen, int_types): raise exc.ExpectedTypeError(keylen, "int or None", "keylen") elif keylen < 0: raise ValueError("keylen must be at least 0") elif keylen > digest_size: raise ValueError("keylength too large for digest: %r > %r" % (keylen, digest_size)) # main pbkdf1 loop block = secret + salt for _ in irange(rounds): block = const(block).digest() return block[:keylen]
def _resolve(hasher, param="value"): """ internal helper to resolve argument to hasher object """ if is_crypt_handler(hasher): return hasher elif isinstance(hasher, unicode_or_str): return get_crypt_handler(hasher) else: raise exc.ExpectedTypeError(hasher, unicode_or_str, param)
def __call__(self, returns=None): """ frontend used by genword() / genphrase() to create passwords """ if returns is None: return next(self) elif isinstance(returns, int_types): return [next(self) for _ in irange(returns)] elif returns is iter: return self else: raise exc.ExpectedTypeError(returns, "<None>, int, or <iter>", "returns")
def pbkdf2_hmac(digest, secret, salt, rounds, keylen=None): """pkcs#5 password-based key derivation v2.0 using HMAC + arbitrary digest. :arg digest: digest name or constructor. :arg secret: passphrase to use to generate key. may be :class:`!bytes` or :class:`unicode` (encoded using UTF-8). :arg salt: salt string to use when generating key. may be :class:`!bytes` or :class:`unicode` (encoded using UTF-8). :param rounds: number of rounds to use to generate key. :arg keylen: number of bytes to generate. if omitted / ``None``, will use digest's native output size. :returns: raw bytes of generated key .. versionchanged:: 1.7 This function will use the first available of the following backends: * `fastpbk2 <https://pypi.python.org/pypi/fastpbkdf2>`_ * :func:`hashlib.pbkdf2_hmac` (only available in py2 >= 2.7.8, and py3 >= 3.4) * builtin pure-python backend See :data:`passlib.crypto.digest.PBKDF2_BACKENDS` to determine which backend(s) are in use. """ # validate secret & salt secret = to_bytes(secret, param="secret") salt = to_bytes(salt, param="salt") # resolve digest digest_info = lookup_hash(digest) digest_size = digest_info.digest_size # validate rounds if not isinstance(rounds, int_types): raise exc.ExpectedTypeError(rounds, "int", "rounds") if rounds < 1: raise ValueError("rounds must be at least 1") # validate keylen if keylen is None: keylen = digest_size elif not isinstance(keylen, int_types): raise exc.ExpectedTypeError(keylen, "int or None", "keylen") elif keylen < 1: # XXX: could allow keylen=0, but want to be compat w/ stdlib raise ValueError("keylen must be at least 1") # find smallest block count s.t. keylen <= block_count * digest_size; # make sure block count won't overflow (per pbkdf2 spec) # this corresponds to throwing error if keylen > digest_size * MAX_UINT32 # NOTE: stdlib will throw error at lower bound (keylen > MAX_SINT32) # NOTE: have do this before other backends checked, since fastpbkdf2 raises wrong error # (InvocationError, not OverflowError) block_count = (keylen + digest_size - 1) // digest_size if block_count > MAX_UINT32: raise OverflowError("keylen too long for digest") # # check for various high-speed backends # # ~3x faster than pure-python backend # NOTE: have to do this after above guards since fastpbkdf2 lacks bounds checks. if digest_info.supported_by_fastpbkdf2: return _fast_pbkdf2_hmac(digest_info.name, secret, salt, rounds, keylen) # ~1.4x faster than pure-python backend # NOTE: have to do this after fastpbkdf2 since hashlib-ssl is slower, # will support larger number of hashes. if digest_info.supported_by_hashlib_pbkdf2: return _stdlib_pbkdf2_hmac(digest_info.name, secret, salt, rounds, keylen) # # otherwise use our own implementation # # generated keyed hmac keyed_hmac = compile_hmac(digest, secret) # get helper to calculate pbkdf2 inner loop efficiently calc_block = _get_pbkdf2_looper(digest_size) # assemble & return result return join_bytes( calc_block(keyed_hmac, keyed_hmac(salt + _pack_uint32(i)), rounds) for i in irange(1, block_count + 1))[:keylen]
def lookup_hash( digest, # *, return_unknown=False, required=True): """ Returns a :class:`HashInfo` record containing information about a given hash function. Can be used to look up a hash constructor by name, normalize hash name representation, etc. :arg digest: This can be any of: * A string containing a :mod:`!hashlib` digest name (e.g. ``"sha256"``), * A string containing an IANA-assigned hash name, * A digest constructor function (e.g. ``hashlib.sha256``). Case is ignored, underscores are converted to hyphens, and various other cleanups are made. :param required: By default (True), this function will throw an :exc:`~passlib.exc.UnknownHashError` if no hash constructor can be found, or if the hash is not actually available. If this flag is False, it will instead return a dummy :class:`!HashInfo` record which will defer throwing the error until it's constructor function is called. This is mainly used by :func:`norm_hash_name`. :param return_unknown: .. deprecated:: 1.7.3 deprecated, and will be removed in passlib 2.0. this acts like inverse of **required**. :returns HashInfo: :class:`HashInfo` instance containing information about specified digest. Multiple calls resolving to the same hash should always return the same :class:`!HashInfo` instance. """ # check for cached entry cache = _hash_info_cache try: return cache[digest] except (KeyError, TypeError): # NOTE: TypeError is to catch 'TypeError: unhashable type' (e.g. HashInfo) pass # legacy alias if return_unknown: required = False # resolve ``digest`` to ``const`` & ``name_record`` cache_by_name = True if isinstance(digest, unicode_or_bytes_types): # normalize name name_list = _get_hash_aliases(digest) name = name_list[0] assert name # if name wasn't normalized to hashlib format, # get info for normalized name and reuse it. if name != digest: info = lookup_hash(name, required=required) cache[digest] = info return info # else look up constructor # NOTE: may return None, which is handled by HashInfo constructor const = _get_hash_const(name) # if mock fips mode is enabled, replace with dummy constructor # (to replicate how it would behave on a real fips system). if const and mock_fips_mode and name not in _fips_algorithms: def const(source=b""): raise ValueError( "%r disabled for fips by passlib set_mock_fips_mode()" % name) elif isinstance(digest, HashInfo): # handle border case where HashInfo is passed in. return digest elif callable(digest): # try to lookup digest based on it's self-reported name # (which we trust to be the canonical "hashlib" name) const = digest name_list = _get_hash_aliases(const().name) name = name_list[0] other_const = _get_hash_const(name) if other_const is None: # this is probably a third-party digest we don't know about, # so just pass it on through, and register reverse lookup for it's name. pass elif other_const is const: # if we got back same constructor, this is just a known stdlib constructor, # which was passed in before we had cached it by name. proceed normally. pass else: # if we got back different object, then ``const`` is something else # (such as a mock object), in which case we want to skip caching it by name, # as that would conflict with real hash. cache_by_name = False else: raise exc.ExpectedTypeError(digest, "digest name or constructor", "digest") # create new instance info = HashInfo(const=const, names=name_list, required=required) # populate cache if const is not None: cache[const] = info if cache_by_name: for name in name_list: if name: # (skips iana name if it's empty) assert cache.get(name) in [None, info], "%r already in cache" % name cache[name] = info return info
def lookup_hash(digest, return_unknown=False): """ Returns a :class:`HashInfo` record containing information about a given hash function. Can be used to look up a hash constructor by name, normalize hash name representation, etc. :arg digest: This can be any of: * A string containing a :mod:`!hashlib` digest name (e.g. ``"sha256"``), * A string containing an IANA-assigned hash name, * A digest constructor function (e.g. ``hashlib.sha256``). Case is ignored, underscores are converted to hyphens, and various other cleanups are made. :param return_unknown: By default, this function will throw an :exc:`~passlib.exc.UnknownHashError` if no hash constructor can be found. However, if this flag is False, it will instead return a dummy record without a constructor function. This is mainly used by :func:`norm_hash_name`. :returns HashInfo: :class:`HashInfo` instance containing information about specified digest. Multiple calls resolving to the same hash should always return the same :class:`!HashInfo` instance. """ # check for cached entry cache = _hash_info_cache try: return cache[digest] except (KeyError, TypeError): # NOTE: TypeError is to catch 'TypeError: unhashable type' (e.g. HashInfo) pass # resolve ``digest`` to ``const`` & ``name_record`` cache_by_name = True if isinstance(digest, unicode_or_bytes_types): # normalize name name_list = _get_hash_aliases(digest) name = name_list[0] assert name # if name wasn't normalized to hashlib format, # get info for normalized name and reuse it. if name != digest: info = lookup_hash(name, return_unknown=return_unknown) if info.const is None: # pass through dummy record assert return_unknown return info cache[digest] = info return info # else look up constructor const = _get_hash_const(name) if const is None: if return_unknown: # return a dummy record (but don't cache it, so normal lookup still returns error) return HashInfo(None, name_list) else: raise exc.UnknownHashError(name) elif isinstance(digest, HashInfo): # handle border case where HashInfo is passed in. return digest elif callable(digest): # try to lookup digest based on it's self-reported name # (which we trust to be the canonical "hashlib" name) const = digest name_list = _get_hash_aliases(const().name) name = name_list[0] other_const = _get_hash_const(name) if other_const is None: # this is probably a third-party digest we don't know about, # so just pass it on through, and register reverse lookup for it's name. pass elif other_const is const: # if we got back same constructor, this is just a known stdlib constructor, # which was passed in before we had cached it by name. proceed normally. pass else: # if we got back different object, then ``const`` is something else # (such as a mock object), in which case we want to skip caching it by name, # as that would conflict with real hash. cache_by_name = False else: raise exc.ExpectedTypeError(digest, "digest name or constructor", "digest") # create new instance info = HashInfo(const, name_list) # populate cache cache[const] = info if cache_by_name: for name in name_list: if name: # (skips iana name if it's empty) assert cache.get(name) in [None, info], "%r already in cache" % name cache[name] = info return info