<gh_stars>100-1000
"""passlib.ifc - abstract interfaces used by Passlib"""
#=============================================================================
# imports
#=============================================================================
# core
import logging; log = logging.getLogger(__name__)
import sys
# site
# pkg
from passlib.utils.decor import deprecated_method
# local
__all__ = [
    "PasswordHash",
]

#=============================================================================
# 2/3 compatibility helpers
#=============================================================================
def recreate_with_metaclass(meta):
    """class decorator that re-creates class using metaclass"""
    def builder(cls):
        if meta is type(cls):
            return cls
        return meta(cls.__name__, cls.__bases__, cls.__dict__.copy())
    return builder

#=============================================================================
# PasswordHash interface
#=============================================================================
from abc import ABCMeta, abstractmethod, abstractproperty

# TODO: make this actually use abstractproperty(),
#       now that we dropped py25, 'abc' is always available.

# XXX: rename to PasswordHasher?

@recreate_with_metaclass(ABCMeta)
class PasswordHash(object):
    """This class describes an abstract interface which all password hashes
    in Passlib adhere to. Under Python 2.6 and up, this is an actual
    Abstract Base Class built using the :mod:`!abc` module.

    See the Passlib docs for full documentation.
    """
    #===================================================================
    # class attributes
    #===================================================================

    #---------------------------------------------------------------
    # general information
    #---------------------------------------------------------------
    ##name
    ##setting_kwds
    ##context_kwds

    #: flag which indicates this hasher matches a "disabled" hash
    #: (e.g. unix_disabled, or django_disabled); and doesn't actually
    #: depend on the provided password.
    is_disabled = False

    #: Should be None, or a positive integer indicating hash
    #: doesn't support secrets larger than this value.
    #: Whether hash throws error or silently truncates secret
    #: depends on .truncate_error and .truncate_verify_reject flags below.
    #: NOTE: calls may treat as boolean, since value will never be 0.
    #: .. versionadded:: 1.7
    #: .. TODO: passlib 1.8: deprecate/rename this attr to "max_secret_size"?
    truncate_size = None

    # NOTE: these next two default to the optimistic "ideal",
    #       most hashes in passlib have to default to False
    #       for backward compat and/or expected behavior with existing hashes.

    #: If True, .hash() should throw a :exc:`~passlib.exc.PasswordSizeError` for
    #: any secrets larger than .truncate_size.  Many hashers default to False
    #: for historical / compatibility purposes, indicating they will silently
    #: truncate instead.  All such hashers SHOULD support changing
    #: the policy via ``.using(truncate_error=True)``.
    #: .. versionadded:: 1.7
    #: .. TODO: passlib 1.8: deprecate/rename this attr to "truncate_hash_error"?
    truncate_error = True

    #: If True, .verify() should reject secrets larger than max_password_size.
    #: Many hashers default to False for historical / compatibility purposes,
    #: indicating they will match on the truncated portion instead.
    #: .. versionadded:: 1.7.1
    truncate_verify_reject = True

    #---------------------------------------------------------------
    # salt information -- if 'salt' in setting_kwds
    #---------------------------------------------------------------
    ##min_salt_size
    ##max_salt_size
    ##default_salt_size
    ##salt_chars
    ##default_salt_chars

    #---------------------------------------------------------------
    # rounds information -- if 'rounds' in setting_kwds
    #---------------------------------------------------------------
    ##min_rounds
    ##max_rounds
    ##default_rounds
    ##rounds_cost

    #---------------------------------------------------------------
    # encoding info -- if 'encoding' in context_kwds
    #---------------------------------------------------------------
    ##default_encoding

    #===================================================================
    # primary methods
    #===================================================================
    @classmethod
    @abstractmethod
    def hash(cls, secret,  # *
             **setting_and_context_kwds):  # pragma: no cover -- abstract method
        r"""
        Hash secret, returning result.
        Should handle generating salt, etc, and should return string
        containing identifier, salt & other configuration, as well as digest.

        :param \*\*settings_kwds:

            Pass in settings to customize configuration of resulting hash.

            .. deprecated:: 1.7

                Starting with Passlib 1.7, callers should no longer pass settings keywords
                (e.g. ``rounds`` or ``salt`` directly to :meth:`!hash`); should use
                ``.using(**settings).hash(secret)`` construction instead.

                Support will be removed in Passlib 2.0.

        :param \*\*context_kwds:

            Specific algorithms may require context-specific information (such as the user login).
        """
        # FIXME:  need stub for classes that define .encrypt() instead ...
        #         this should call .encrypt(), and check for recursion back to here.
        raise NotImplementedError("must be implemented by subclass")

    @deprecated_method(deprecated="1.7", removed="2.0", replacement=".hash()")
    @classmethod
    def encrypt(cls, *args, **kwds):
        """
        Legacy alias for :meth:`hash`.

        .. deprecated:: 1.7
            This method was renamed to :meth:`!hash` in version 1.7.
            This alias will be removed in version 2.0, and should only
            be used for compatibility with Passlib 1.3 - 1.6.
        """
        return cls.hash(*args, **kwds)

    # XXX: could provide default implementation which hands value to
    #      hash(), and then does constant-time comparision on the result
    #      (after making both are same string type)
    @classmethod
    @abstractmethod
    def verify(cls, secret, hash, **context_kwds): # pragma: no cover -- abstract method
        """verify secret against hash, returns True/False"""
        raise NotImplementedError("must be implemented by subclass")

    #===================================================================
    # configuration
    #===================================================================
    @classmethod
    @abstractmethod
    def using(cls, relaxed=False, **kwds):
        """
        Return another hasher object (typically a subclass of the current one),
        which integrates the configuration options specified by ``kwds``.
        This should *always* return a new object, even if no configuration options are changed.

        .. todo::

            document which options are accepted.

        :returns:
            typically returns a subclass for most hasher implementations.

        .. todo::

            add this method to main documentation.
        """
        raise NotImplementedError("must be implemented by subclass")

    #===================================================================
    # migration
    #===================================================================
    @classmethod
    def needs_update(cls, hash, secret=None):
        """
        check if hash's configuration is outside desired bounds,
        or contains some other internal option which requires
        updating the password hash.

        :param hash:
            hash string to examine

        :param secret:
            optional secret known to have verified against the provided hash.
            (this is used by some hashes to detect legacy algorithm mistakes).

        :return:
            whether secret needs re-hashing.

        .. versionadded:: 1.7
        """
        # by default, always report that we don't need update
        return False

    #===================================================================
    # additional methods
    #===================================================================
    @classmethod
    @abstractmethod
    def identify(cls, hash): # pragma: no cover -- abstract method
        """check if hash belongs to this scheme, returns True/False"""
        raise NotImplementedError("must be implemented by subclass")

    @deprecated_method(deprecated="1.7", removed="2.0")
    @classmethod
    def genconfig(cls, **setting_kwds): # pragma: no cover -- abstract method
        """
        compile settings into a configuration string for genhash()

        .. deprecated:: 1.7

            As of 1.7, this method is deprecated, and slated for complete removal in Passlib 2.0.

            For all known real-world uses, hashing a constant string
            should provide equivalent functionality.

            This deprecation may be reversed if a use-case presents itself in the mean time.
        """
        # NOTE: this fallback runs full hash alg, w/ whatever cost param is passed along.
        #       implementations (esp ones w/ variable cost) will want to subclass this
        #       with a constant-time implementation that just renders a config string.
        if cls.context_kwds:
            raise NotImplementedError("must be implemented by subclass")
        return cls.using(**setting_kwds).hash("")

    @deprecated_method(deprecated="1.7", removed="2.0")
    @classmethod
    def genhash(cls, secret, config, **context):
        """
        generated hash for secret, using settings from config/hash string

        .. deprecated:: 1.7

            As of 1.7, this method is deprecated, and slated for complete removal in Passlib 2.0.

            This deprecation may be reversed if a use-case presents itself in the mean time.
        """
        # XXX: if hashes reliably offered a .parse() method, could make a fallback for this.
        raise NotImplementedError("must be implemented by subclass")

    #===================================================================
    # undocumented methods / attributes
    #===================================================================
    # the following entry points are used internally by passlib,
    # and aren't documented as part of the exposed interface.
    # they are subject to change between releases,
    # but are documented here so there's a list of them *somewhere*.

    #---------------------------------------------------------------
    # extra metdata
    #---------------------------------------------------------------

    #: this attribute shouldn't be used by hashers themselves,
    #: it's reserved for the CryptContext to track which hashers are deprecated.
    #: Note the context will only set this on objects it owns (and generated by .using()),
    #: and WONT set it on global objects.
    #: [added in 1.7]
    #: TODO: document this, or at least the use of testing for
    #:       'CryptContext().handler().deprecated'
    deprecated = False

    #: optionally present if hasher corresponds to format built into Django.
    #: this attribute (if not None) should be the Django 'algorithm' name.
    #: also indicates to passlib.ext.django that (when installed in django),
    #: django's native hasher should be used in preference to this one.
    ## django_name

    #---------------------------------------------------------------
    # checksum information - defined for many hashes
    #---------------------------------------------------------------
    ## checksum_chars
    ## checksum_size

    #---------------------------------------------------------------
    # experimental methods
    #---------------------------------------------------------------

    ##@classmethod
    ##def normhash(cls, hash):
    ##    """helper to clean up non-canonic instances of hash.
    ##    currently only provided by bcrypt() to fix an historical passlib issue.
    ##    """

    # experimental helper to parse hash into components.
    ##@classmethod
    ##def parsehash(cls, hash, checksum=True, sanitize=False):
    ##    """helper to parse hash into components, returns dict"""

    # experiment helper to estimate bitsize of different hashes,
    # implement for GenericHandler, but may be currently be off for some hashes.
    # want to expand this into a way to programmatically compare
    # "strengths" of different hashes and hash algorithms.
    # still needs to have some factor for estimate relative cost per round,
    # ala in the style of the scrypt whitepaper.
    ##@classmethod
    ##def bitsize(cls, **kwds):
    ##    """returns dict mapping component -> bits contributed.
    ##    components currently include checksum, salt, rounds.
    ##    """

    #===================================================================
    # eoc
    #===================================================================

class DisabledHash(PasswordHash):
    """
    extended disabled-hash methods; only need be present if .disabled = True
    """

    is_disabled = True

    @classmethod
    def disable(cls, hash=None):
        """
        return string representing a 'disabled' hash;
        optionally including previously enabled hash
        (this is up to the individual scheme).
        """
        # default behavior: ignore original hash, return standalone marker
        return cls.hash("")

    @classmethod
    def enable(cls, hash):
        """
        given a disabled-hash string,
        extract previously-enabled hash if one is present,
        otherwise raises ValueError
        """
        # default behavior: no way to restore original hash
        raise ValueError("cannot restore original hash")

#=============================================================================
# eof
#=============================================================================

"""passlib.ifc - abstract interfaces used by Passlib"""
#=============================================================================
# imports
#=============================================================================
# core
import logging; log = logging.getLogger(__name__)
import sys
# site
# pkg
from passlib.utils.decor import deprecated_method
# local
__all__ = [
    "PasswordHash",
]

#=============================================================================
# 2/3 compatibility helpers
#=============================================================================
def recreate_with_metaclass(meta):
    """class decorator that re-creates class using metaclass"""
    def builder(cls):
        if meta is type(cls):
            return cls
        return meta(cls.__name__, cls.__bases__, cls.__dict__.copy())
    return builder

#=============================================================================
# PasswordHash interface
#=============================================================================
from abc import ABCMeta, abstractmethod, abstractproperty

# TODO: make this actually use abstractproperty(),
#       now that we dropped py25, 'abc' is always available.

# XXX: rename to PasswordHasher?

@recreate_with_metaclass(ABCMeta)
class PasswordHash(object):
    """This class describes an abstract interface which all password hashes
    in Passlib adhere to. Under Python 2.6 and up, this is an actual
    Abstract Base Class built using the :mod:`!abc` module.

    See the Passlib docs for full documentation.
    """
    #===================================================================
    # class attributes
    #===================================================================

    #---------------------------------------------------------------
    # general information
    #---------------------------------------------------------------
    ##name
    ##setting_kwds
    ##context_kwds

    #: flag which indicates this hasher matches a "disabled" hash
    #: (e.g. unix_disabled, or django_disabled); and doesn't actually
    #: depend on the provided password.
    is_disabled = False

    #: Should be None, or a positive integer indicating hash
    #: doesn't support secrets larger than this value.
    #: Whether hash throws error or silently truncates secret
    #: depends on .truncate_error and .truncate_verify_reject flags below.
    #: NOTE: calls may treat as boolean, since value will never be 0.
    #: .. versionadded:: 1.7
    #: .. TODO: passlib 1.8: deprecate/rename this attr to "max_secret_size"?
    truncate_size = None

    # NOTE: these next two default to the optimistic "ideal",
    #       most hashes in passlib have to default to False
    #       for backward compat and/or expected behavior with existing hashes.

    #: If True, .hash() should throw a :exc:`~passlib.exc.PasswordSizeError` for
    #: any secrets larger than .truncate_size.  Many hashers default to False
    #: for historical / compatibility purposes, indicating they will silently
    #: truncate instead.  All such hashers SHOULD support changing
    #: the policy via ``.using(truncate_error=True)``.
    #: .. versionadded:: 1.7
    #: .. TODO: passlib 1.8: deprecate/rename this attr to "truncate_hash_error"?
    truncate_error = True

    #: If True, .verify() should reject secrets larger than max_password_size.
    #: Many hashers default to False for historical / compatibility purposes,
    #: indicating they will match on the truncated portion instead.
    #: .. versionadded:: 1.7.1
    truncate_verify_reject = True

    #---------------------------------------------------------------
    # salt information -- if 'salt' in setting_kwds
    #---------------------------------------------------------------
    ##min_salt_size
    ##max_salt_size
    ##default_salt_size
    ##salt_chars
    ##default_salt_chars

    #---------------------------------------------------------------
    # rounds information -- if 'rounds' in setting_kwds
    #---------------------------------------------------------------
    ##min_rounds
    ##max_rounds
    ##default_rounds
    ##rounds_cost

    #---------------------------------------------------------------
    # encoding info -- if 'encoding' in context_kwds
    #---------------------------------------------------------------
    ##default_encoding

    #===================================================================
    # primary methods
    #===================================================================
    @classmethod
    @abstractmethod
    def hash(cls, secret,  # *
             **setting_and_context_kwds):  # pragma: no cover -- abstract method
        r"""
        Hash secret, returning result.
        Should handle generating salt, etc, and should return string
        containing identifier, salt & other configuration, as well as digest.

        :param \*\*settings_kwds:

            Pass in settings to customize configuration of resulting hash.

            .. deprecated:: 1.7

                Starting with Passlib 1.7, callers should no longer pass settings keywords
                (e.g. ``rounds`` or ``salt`` directly to :meth:`!hash`); should use
                ``.using(**settings).hash(secret)`` construction instead.

                Support will be removed in Passlib 2.0.

        :param \*\*context_kwds:

            Specific algorithms may require context-specific information (such as the user login).
        """
        # FIXME:  need stub for classes that define .encrypt() instead ...
        #         this should call .encrypt(), and check for recursion back to here.
        raise NotImplementedError("must be implemented by subclass")

    @deprecated_method(deprecated="1.7", removed="2.0", replacement=".hash()")
    @classmethod
    def encrypt(cls, *args, **kwds):
        """
        Legacy alias for :meth:`hash`.

        .. deprecated:: 1.7
            This method was renamed to :meth:`!hash` in version 1.7.
            This alias will be removed in version 2.0, and should only
            be used for compatibility with Passlib 1.3 - 1.6.
        """
        return cls.hash(*args, **kwds)

    # XXX: could provide default implementation which hands value to
    #      hash(), and then does constant-time comparision on the result
    #      (after making both are same string type)
    @classmethod
    @abstractmethod
    def verify(cls, secret, hash, **context_kwds): # pragma: no cover -- abstract method
        """verify secret against hash, returns True/False"""
        raise NotImplementedError("must be implemented by subclass")

    #===================================================================
    # configuration
    #===================================================================
    @classmethod
    @abstractmethod
    def using(cls, relaxed=False, **kwds):
        """
        Return another hasher object (typically a subclass of the current one),
        which integrates the configuration options specified by ``kwds``.
        This should *always* return a new object, even if no configuration options are changed.

        .. todo::

            document which options are accepted.

        :returns:
            typically returns a subclass for most hasher implementations.

        .. todo::

            add this method to main documentation.
        """
        raise NotImplementedError("must be implemented by subclass")

    #===================================================================
    # migration
    #===================================================================
    @classmethod
    def needs_update(cls, hash, secret=None):
        """
        check if hash's configuration is outside desired bounds,
        or contains some other internal option which requires
        updating the password hash.

        :param hash:
            hash string to examine

        :param secret:
            optional secret known to have verified against the provided hash.
            (this is used by some hashes to detect legacy algorithm mistakes).

        :return:
            whether secret needs re-hashing.

        .. versionadded:: 1.7
        """
        # by default, always report that we don't need update
        return False

    #===================================================================
    # additional methods
    #===================================================================
    @classmethod
    @abstractmethod
    def identify(cls, hash): # pragma: no cover -- abstract method
        """check if hash belongs to this scheme, returns True/False"""
        raise NotImplementedError("must be implemented by subclass")

    @deprecated_method(deprecated="1.7", removed="2.0")
    @classmethod
    def genconfig(cls, **setting_kwds): # pragma: no cover -- abstract method
        """
        compile settings into a configuration string for genhash()

        .. deprecated:: 1.7

            As of 1.7, this method is deprecated, and slated for complete removal in Passlib 2.0.

            For all known real-world uses, hashing a constant string
            should provide equivalent functionality.

            This deprecation may be reversed if a use-case presents itself in the mean time.
        """
        # NOTE: this fallback runs full hash alg, w/ whatever cost param is passed along.
        #       implementations (esp ones w/ variable cost) will want to subclass this
        #       with a constant-time implementation that just renders a config string.
        if cls.context_kwds:
            raise NotImplementedError("must be implemented by subclass")
        return cls.using(**setting_kwds).hash("")

    @deprecated_method(deprecated="1.7", removed="2.0")
    @classmethod
    def genhash(cls, secret, config, **context):
        """
        generated hash for secret, using settings from config/hash string

        .. deprecated:: 1.7

            As of 1.7, this method is deprecated, and slated for complete removal in Passlib 2.0.

            This deprecation may be reversed if a use-case presents itself in the mean time.
        """
        # XXX: if hashes reliably offered a .parse() method, could make a fallback for this.
        raise NotImplementedError("must be implemented by subclass")

    #===================================================================
    # undocumented methods / attributes
    #===================================================================
    # the following entry points are used internally by passlib,
    # and aren't documented as part of the exposed interface.
    # they are subject to change between releases,
    # but are documented here so there's a list of them *somewhere*.

    #---------------------------------------------------------------
    # extra metdata
    #---------------------------------------------------------------

    #: this attribute shouldn't be used by hashers themselves,
    #: it's reserved for the CryptContext to track which hashers are deprecated.
    #: Note the context will only set this on objects it owns (and generated by .using()),
    #: and WONT set it on global objects.
    #: [added in 1.7]
    #: TODO: document this, or at least the use of testing for
    #:       'CryptContext().handler().deprecated'
    deprecated = False

    #: optionally present if hasher corresponds to format built into Django.
    #: this attribute (if not None) should be the Django 'algorithm' name.
    #: also indicates to passlib.ext.django that (when installed in django),
    #: django's native hasher should be used in preference to this one.
    ## django_name

    #---------------------------------------------------------------
    # checksum information - defined for many hashes
    #---------------------------------------------------------------
    ## checksum_chars
    ## checksum_size

    #---------------------------------------------------------------
    # experimental methods
    #---------------------------------------------------------------

    ##@classmethod
    ##def normhash(cls, hash):
    ##    """helper to clean up non-canonic instances of hash.
    ##    currently only provided by bcrypt() to fix an historical passlib issue.
    ##    """

    # experimental helper to parse hash into components.
    ##@classmethod
    ##def parsehash(cls, hash, checksum=True, sanitize=False):
    ##    """helper to parse hash into components, returns dict"""

    # experiment helper to estimate bitsize of different hashes,
    # implement for GenericHandler, but may be currently be off for some hashes.
    # want to expand this into a way to programmatically compare
    # "strengths" of different hashes and hash algorithms.
    # still needs to have some factor for estimate relative cost per round,
    # ala in the style of the scrypt whitepaper.
    ##@classmethod
    ##def bitsize(cls, **kwds):
    ##    """returns dict mapping component -> bits contributed.
    ##    components currently include checksum, salt, rounds.
    ##    """

    #===================================================================
    # eoc
    #===================================================================

class DisabledHash(PasswordHash):
    """
    extended disabled-hash methods; only need be present if .disabled = True
    """

    is_disabled = True

    @classmethod
    def disable(cls, hash=None):
        """
        return string representing a 'disabled' hash;
        optionally including previously enabled hash
        (this is up to the individual scheme).
        """
        # default behavior: ignore original hash, return standalone marker
        return cls.hash("")

    @classmethod
    def enable(cls, hash):
        """
        given a disabled-hash string,
        extract previously-enabled hash if one is present,
        otherwise raises ValueError
        """
        # default behavior: no way to restore original hash
        raise ValueError("cannot restore original hash")

#=============================================================================
# eof
#=============================================================================
passlib.ifc - abstract interfaces used by Passlib #============================================================================= # imports #============================================================================= # core # site # pkg # local #============================================================================= # 2/3 compatibility helpers #============================================================================= class decorator that re-creates class using metaclass #============================================================================= # PasswordHash interface #============================================================================= # TODO: make this actually use abstractproperty(), #       now that we dropped py25, 'abc' is always available. # XXX: rename to PasswordHasher? This class describes an abstract interface which all password hashes     in Passlib adhere to. Under Python 2.6 and up, this is an actual     Abstract Base Class built using the :mod:`!abc` module.      See the Passlib docs for full documentation. #=================================================================== # class attributes #=================================================================== #--------------------------------------------------------------- # general information #--------------------------------------------------------------- ##name ##setting_kwds ##context_kwds #: flag which indicates this hasher matches a "disabled" hash #: (e.g. unix_disabled, or django_disabled); and doesn't actually #: depend on the provided password. #: Should be None, or a positive integer indicating hash #: doesn't support secrets larger than this value. #: Whether hash throws error or silently truncates secret #: depends on .truncate_error and .truncate_verify_reject flags below. #: NOTE: calls may treat as boolean, since value will never be 0. #: .. versionadded:: 1.7 #: .. TODO: passlib 1.8: deprecate/rename this attr to "max_secret_size"? # NOTE: these next two default to the optimistic "ideal", #       most hashes in passlib have to default to False #       for backward compat and/or expected behavior with existing hashes. #: If True, .hash() should throw a :exc:`~passlib.exc.PasswordSizeError` for #: any secrets larger than .truncate_size.  Many hashers default to False #: for historical / compatibility purposes, indicating they will silently #: truncate instead.  All such hashers SHOULD support changing #: the policy via ``.using(truncate_error=True)``. #: .. versionadded:: 1.7 #: .. TODO: passlib 1.8: deprecate/rename this attr to "truncate_hash_error"? #: If True, .verify() should reject secrets larger than max_password_size. #: Many hashers default to False for historical / compatibility purposes, #: indicating they will match on the truncated portion instead. #: .. versionadded:: 1.7.1 #--------------------------------------------------------------- # salt information -- if 'salt' in setting_kwds #--------------------------------------------------------------- ##min_salt_size ##max_salt_size ##default_salt_size ##salt_chars ##default_salt_chars #--------------------------------------------------------------- # rounds information -- if 'rounds' in setting_kwds #--------------------------------------------------------------- ##min_rounds ##max_rounds ##default_rounds ##rounds_cost #--------------------------------------------------------------- # encoding info -- if 'encoding' in context_kwds #--------------------------------------------------------------- ##default_encoding #=================================================================== # primary methods #=================================================================== # * # pragma: no cover -- abstract method Hash secret, returning result.         Should handle generating salt, etc, and should return string         containing identifier, salt & other configuration, as well as digest.          :param \*\*settings_kwds:              Pass in settings to customize configuration of resulting hash.              .. deprecated:: 1.7                  Starting with Passlib 1.7, callers should no longer pass settings keywords                 (e.g. ``rounds`` or ``salt`` directly to :meth:`!hash`); should use                 ``.using(**settings).hash(secret)`` construction instead.                  Support will be removed in Passlib 2.0.          :param \*\*context_kwds:              Specific algorithms may require context-specific information (such as the user login). # FIXME:  need stub for classes that define .encrypt() instead ... #         this should call .encrypt(), and check for recursion back to here. Legacy alias for :meth:`hash`.          .. deprecated:: 1.7             This method was renamed to :meth:`!hash` in version 1.7.             This alias will be removed in version 2.0, and should only             be used for compatibility with Passlib 1.3 - 1.6. # XXX: could provide default implementation which hands value to #      hash(), and then does constant-time comparision on the result #      (after making both are same string type) # pragma: no cover -- abstract method verify secret against hash, returns True/False #=================================================================== # configuration #=================================================================== Return another hasher object (typically a subclass of the current one),         which integrates the configuration options specified by ``kwds``.         This should *always* return a new object, even if no configuration options are changed.          .. todo::              document which options are accepted.          :returns:             typically returns a subclass for most hasher implementations.          .. todo::              add this method to main documentation. #=================================================================== # migration #=================================================================== check if hash's configuration is outside desired bounds,         or contains some other internal option which requires         updating the password hash.          :param hash:             hash string to examine          :param secret:             optional secret known to have verified against the provided hash.             (this is used by some hashes to detect legacy algorithm mistakes).          :return:             whether secret needs re-hashing.          .. versionadded:: 1.7 # by default, always report that we don't need update #=================================================================== # additional methods #=================================================================== # pragma: no cover -- abstract method check if hash belongs to this scheme, returns True/False # pragma: no cover -- abstract method compile settings into a configuration string for genhash()          .. deprecated:: 1.7              As of 1.7, this method is deprecated, and slated for complete removal in Passlib 2.0.              For all known real-world uses, hashing a constant string             should provide equivalent functionality.              This deprecation may be reversed if a use-case presents itself in the mean time. # NOTE: this fallback runs full hash alg, w/ whatever cost param is passed along. #       implementations (esp ones w/ variable cost) will want to subclass this #       with a constant-time implementation that just renders a config string. generated hash for secret, using settings from config/hash string          .. deprecated:: 1.7              As of 1.7, this method is deprecated, and slated for complete removal in Passlib 2.0.              This deprecation may be reversed if a use-case presents itself in the mean time. # XXX: if hashes reliably offered a .parse() method, could make a fallback for this. #=================================================================== # undocumented methods / attributes #=================================================================== # the following entry points are used internally by passlib, # and aren't documented as part of the exposed interface. # they are subject to change between releases, # but are documented here so there's a list of them *somewhere*. #--------------------------------------------------------------- # extra metdata #--------------------------------------------------------------- #: this attribute shouldn't be used by hashers themselves, #: it's reserved for the CryptContext to track which hashers are deprecated. #: Note the context will only set this on objects it owns (and generated by .using()), #: and WONT set it on global objects. #: [added in 1.7] #: TODO: document this, or at least the use of testing for #:       'CryptContext().handler().deprecated' #: optionally present if hasher corresponds to format built into Django. #: this attribute (if not None) should be the Django 'algorithm' name. #: also indicates to passlib.ext.django that (when installed in django), #: django's native hasher should be used in preference to this one. ## django_name #--------------------------------------------------------------- # checksum information - defined for many hashes #--------------------------------------------------------------- ## checksum_chars ## checksum_size #--------------------------------------------------------------- # experimental methods #--------------------------------------------------------------- ##@classmethod ##def normhash(cls, hash): ##    """helper to clean up non-canonic instances of hash. ##    currently only provided by bcrypt() to fix an historical passlib issue. ##    """ # experimental helper to parse hash into components. ##@classmethod ##def parsehash(cls, hash, checksum=True, sanitize=False): ##    """helper to parse hash into components, returns dict""" # experiment helper to estimate bitsize of different hashes, # implement for GenericHandler, but may be currently be off for some hashes. # want to expand this into a way to programmatically compare # "strengths" of different hashes and hash algorithms. # still needs to have some factor for estimate relative cost per round, # ala in the style of the scrypt whitepaper. ##@classmethod ##def bitsize(cls, **kwds): ##    """returns dict mapping component -> bits contributed. ##    components currently include checksum, salt, rounds. ##    """ #=================================================================== # eoc #=================================================================== extended disabled-hash methods; only need be present if .disabled = True return string representing a 'disabled' hash;         optionally including previously enabled hash         (this is up to the individual scheme). # default behavior: ignore original hash, return standalone marker given a disabled-hash string,         extract previously-enabled hash if one is present,         otherwise raises ValueError # default behavior: no way to restore original hash #============================================================================= # eof #=============================================================================