"""passlib.handlers.phpass - PHPass Portable Crypt phppass located - http://www.openwall.com/phpass/ algorithm described - http://www.openwall.com/articles/PHP-Users-Passwords phpass context - blowfish, bsdi_crypt, phpass """ #========================================================= #imports #========================================================= #core from hashlib import md5 import re import logging; log = logging.getLogger(__name__) from warnings import warn #site #libs from passlib.utils import h64 from passlib.utils.compat import b, bytes, u, uascii_to_str, unicode import passlib.utils.handlers as uh #pkg #local __all__ = [ "phpass", ] #========================================================= #phpass #========================================================= class phpass(uh.HasManyIdents, uh.HasRounds, uh.HasSalt, uh.GenericHandler): """This class implements the PHPass Portable Hash, and follows the :ref:`password-hash-api`. It supports a fixed-length salt, and a variable number of rounds. The :meth:`~passlib.ifc.PasswordHash.encrypt` and :meth:`~passlib.ifc.PasswordHash.genconfig` methods accept the following optional keywords: :type salt: str :param salt: Optional salt string. If not specified, one will be autogenerated (this is recommended). If specified, it must be 8 characters, drawn from the regexp range ``[./0-9A-Za-z]``. :type rounds: int :param rounds: Optional number of rounds to use. Defaults to 16, must be between 7 and 30, inclusive. This value is logarithmic, the actual number of iterations used will be :samp:`2**{rounds}`. :type ident: str :param ident: phpBB3 uses ``H`` instead of ``P`` for it's identifier, this may be set to ``H`` in order to generate phpBB3 compatible hashes. it defaults to ``P``. :type relaxed: bool :param relaxed: By default, providing an invalid value for one of the other keywords will result in a :exc:`ValueError`. If ``relaxed=True``, and the error can be corrected, a :exc:`~passlib.exc.PasslibHashWarning` will be issued instead. Correctable errors include ``rounds`` that are too small or too large, and ``salt`` strings that are too long. .. versionadded:: 1.6 """ #========================================================= #class attrs #========================================================= #--GenericHandler-- name = "phpass" setting_kwds = ("salt", "rounds", "ident") checksum_chars = uh.HASH64_CHARS #--HasSalt-- min_salt_size = max_salt_size = 8 salt_chars = uh.HASH64_CHARS #--HasRounds-- default_rounds = 16 min_rounds = 7 max_rounds = 30 rounds_cost = "log2" #--HasManyIdents-- default_ident = u("$P$") ident_values = [u("$P$"), u("$H$")] ident_aliases = {u("P"):u("$P$"), u("H"):u("$H$")} #========================================================= #formatting #========================================================= #$P$9IQRaTwmfeRo7ud9Fh4E2PdI0S3r.L0 # $P$ # 9 # IQRaTwmf # eRo7ud9Fh4E2PdI0S3r.L0 @classmethod def from_string(cls, hash): ident, data = cls._parse_ident(hash) rounds, salt, chk = data[0], data[1:9], data[9:] return cls( ident=ident, rounds=h64.decode_int6(rounds.encode("ascii")), salt=salt, checksum=chk or None, ) def to_string(self): hash = u("%s%s%s%s") % (self.ident, h64.encode_int6(self.rounds).decode("ascii"), self.salt, self.checksum or u('')) return uascii_to_str(hash) #========================================================= #backend #========================================================= def _calc_checksum(self, secret): #FIXME: can't find definitive policy on how phpass handles non-ascii. if isinstance(secret, unicode): secret = secret.encode("utf-8") real_rounds = 1<