Team Ai
Datasetpublic

codekingpro/portable-devtools

sourceHugging Faceupdated 5mo agoView on Hugging Face
1likes14kdownloads
fshp.py215 linesDownload Raw Back to handlers
1"""passlib.handlers.fshp2"""3 4#=============================================================================5# imports6#=============================================================================7# core8from base64 import b64encode, b64decode9import re10import logging; log = logging.getLogger(__name__)11# site12# pkg13from passlib.utils import to_unicode14import passlib.utils.handlers as uh15from passlib.utils.compat import bascii_to_str, iteritems, u,\16                                 unicode17from passlib.crypto.digest import pbkdf118# local19__all__ = [20    'fshp',21]22#=============================================================================23# sha1-crypt24#=============================================================================25class fshp(uh.HasRounds, uh.HasRawSalt, uh.HasRawChecksum, uh.GenericHandler):26    """This class implements the FSHP password hash, and follows the :ref:`password-hash-api`.27 28    It supports a variable-length salt, and a variable number of rounds.29 30    The :meth:`~passlib.ifc.PasswordHash.using` method accepts the following optional keywords:31 32    :param salt:33        Optional raw salt string.34        If not specified, one will be autogenerated (this is recommended).35 36    :param salt_size:37        Optional number of bytes to use when autogenerating new salts.38        Defaults to 16 bytes, but can be any non-negative value.39 40    :param rounds:41        Optional number of rounds to use.42        Defaults to 480000, must be between 1 and 4294967295, inclusive.43 44    :param variant:45        Optionally specifies variant of FSHP to use.46 47        * ``0`` - uses SHA-1 digest (deprecated).48        * ``1`` - uses SHA-2/256 digest (default).49        * ``2`` - uses SHA-2/384 digest.50        * ``3`` - uses SHA-2/512 digest.51 52    :type relaxed: bool53    :param relaxed:54        By default, providing an invalid value for one of the other55        keywords will result in a :exc:`ValueError`. If ``relaxed=True``,56        and the error can be corrected, a :exc:`~passlib.exc.PasslibHashWarning`57        will be issued instead. Correctable errors include ``rounds``58        that are too small or too large, and ``salt`` strings that are too long.59 60        .. versionadded:: 1.661    """62 63    #===================================================================64    # class attrs65    #===================================================================66    #--GenericHandler--67    name = "fshp"68    setting_kwds = ("salt", "salt_size", "rounds", "variant")69    checksum_chars = uh.PADDED_BASE64_CHARS70    ident = u("{FSHP")71    # checksum_size is property() that depends on variant72 73    #--HasRawSalt--74    default_salt_size = 16 # current passlib default, FSHP uses 875    max_salt_size = None76 77    #--HasRounds--78    # FIXME: should probably use different default rounds79    # based on the variant. setting for default variant (sha256) for now.80    default_rounds = 480000 # current passlib default, FSHP uses 409681    min_rounds = 1 # set by FSHP82    max_rounds = 4294967295 # 32-bit integer limit - not set by FSHP83    rounds_cost = "linear"84 85    #--variants--86    default_variant = 187    _variant_info = {88        # variant: (hash name, digest size)89        0: ("sha1",     20),90        1: ("sha256",   32),91        2: ("sha384",   48),92        3: ("sha512",   64),93        }94    _variant_aliases = dict(95        [(unicode(k),k) for k in _variant_info] +96        [(v[0],k) for k,v in iteritems(_variant_info)]97        )98 99    #===================================================================100    # configuration101    #===================================================================102    @classmethod103    def using(cls, variant=None, **kwds):104        subcls = super(fshp, cls).using(**kwds)105        if variant is not None:106            subcls.default_variant = cls._norm_variant(variant)107        return subcls108 109    #===================================================================110    # instance attrs111    #===================================================================112    variant = None113 114    #===================================================================115    # init116    #===================================================================117    def __init__(self, variant=None, **kwds):118        # NOTE: variant must be set first, since it controls checksum size, etc.119        self.use_defaults = kwds.get("use_defaults") # load this early120        if variant is not None:121            variant = self._norm_variant(variant)122        elif self.use_defaults:123            variant = self.default_variant124            assert self._norm_variant(variant) == variant, "invalid default variant: %r" % (variant,)125        else:126            raise TypeError("no variant specified")127        self.variant = variant128        super(fshp, self).__init__(**kwds)129 130    @classmethod131    def _norm_variant(cls, variant):132        if isinstance(variant, bytes):133            variant = variant.decode("ascii")134        if isinstance(variant, unicode):135            try:136                variant = cls._variant_aliases[variant]137            except KeyError:138                raise ValueError("invalid fshp variant")139        if not isinstance(variant, int):140            raise TypeError("fshp variant must be int or known alias")141        if variant not in cls._variant_info:142            raise ValueError("invalid fshp variant")143        return variant144 145    @property146    def checksum_alg(self):147        return self._variant_info[self.variant][0]148 149    @property150    def checksum_size(self):151        return self._variant_info[self.variant][1]152 153    #===================================================================154    # formatting155    #===================================================================156 157    _hash_regex = re.compile(u(r"""158            ^159            \{FSHP160            (\d+)\| # variant161            (\d+)\| # salt size162            (\d+)\} # rounds163            ([a-zA-Z0-9+/]+={0,3}) # digest164            $"""), re.X)165 166    @classmethod167    def from_string(cls, hash):168        hash = to_unicode(hash, "ascii", "hash")169        m = cls._hash_regex.match(hash)170        if not m:171            raise uh.exc.InvalidHashError(cls)172        variant, salt_size, rounds, data = m.group(1,2,3,4)173        variant = int(variant)174        salt_size = int(salt_size)175        rounds = int(rounds)176        try:177            data = b64decode(data.encode("ascii"))178        except TypeError:179            raise uh.exc.MalformedHashError(cls)180        salt = data[:salt_size]181        chk = data[salt_size:]182        return cls(salt=salt, checksum=chk, rounds=rounds, variant=variant)183 184    def to_string(self):185        chk = self.checksum186        salt = self.salt187        data = bascii_to_str(b64encode(salt+chk))188        return "{FSHP%d|%d|%d}%s" % (self.variant, len(salt), self.rounds, data)189 190    #===================================================================191    # backend192    #===================================================================193 194    def _calc_checksum(self, secret):195        if isinstance(secret, unicode):196            secret = secret.encode("utf-8")197        # NOTE: for some reason, FSHP uses pbkdf1 with password & salt reversed.198        #       this has only a minimal impact on security,199        #       but it is worth noting this deviation.200        return pbkdf1(201            digest=self.checksum_alg,202            secret=self.salt,203            salt=secret,204            rounds=self.rounds,205            keylen=self.checksum_size,206            )207 208    #===================================================================209    # eoc210    #===================================================================211 212#=============================================================================213# eof214#=============================================================================215 
codekingpro/portable-devtools · Team Ai