Ejemplo n.º 1
0
class SaltedHash(uh.HasSalt, uh.GenericHandler):
    """test algorithm with a salt"""
    name = "salted_test_hash"
    setting_kwds = ("salt",)

    min_salt_size = 2
    max_salt_size = 4
    checksum_size = 40
    salt_chars = checksum_chars = uh.LOWER_HEX_CHARS

    _hash_regex = re.compile(u("^@salt[0-9a-f]{42,44}$"))

    @classmethod
    def from_string(cls, hash):
        if not cls.identify(hash):
            raise uh.exc.InvalidHashError(cls)
        if isinstance(hash, bytes):
            hash = hash.decode("ascii")
        return cls(salt=hash[5:-40], checksum=hash[-40:])

    _stub_checksum = u('0') * 40

    def to_string(self):
        hash = u("@salt%s%s") % (self.salt, self.checksum or self._stub_checksum)
        return uascii_to_str(hash)

    def _calc_checksum(self, secret):
        if isinstance(secret, unicode):
            secret = secret.encode("utf-8")
        data = self.salt.encode("ascii") + secret + self.salt.encode("ascii")
        return str_to_uascii(hashlib.sha1(data).hexdigest())
Ejemplo n.º 2
0
    def test_to_bytes(self):
        """test to_bytes()"""
        from lib.passlib.utils import to_bytes

        # check unicode inputs
        self.assertEqual(to_bytes(u('abc')), b('abc'))
        self.assertEqual(to_bytes(u('\x00\xff')), b('\x00\xc3\xbf'))

        # check unicode w/ encodings
        self.assertEqual(to_bytes(u('\x00\xff'), 'latin-1'), b('\x00\xff'))
        self.assertRaises(ValueError, to_bytes, u('\x00\xff'), 'ascii')

        # check bytes inputs
        self.assertEqual(to_bytes(b('abc')), b('abc'))
        self.assertEqual(to_bytes(b('\x00\xff')), b('\x00\xff'))
        self.assertEqual(to_bytes(b('\x00\xc3\xbf')), b('\x00\xc3\xbf'))

        # check byte inputs ignores enocding
        self.assertEqual(to_bytes(b('\x00\xc3\xbf'), "latin-1"),
                         b('\x00\xc3\xbf'))

        # check bytes transcoding
        self.assertEqual(to_bytes(b('\x00\xc3\xbf'), "latin-1", "", "utf-8"),
                         b('\x00\xff'))

        # check other
        self.assertRaises(AssertionError, to_bytes, 'abc', None)
        self.assertRaises(TypeError, to_bytes, None)
Ejemplo n.º 3
0
 def test_is_ascii_safe(self):
     """test is_ascii_safe()"""
     from lib.passlib.utils import is_ascii_safe
     self.assertTrue(is_ascii_safe(b("\x00abc\x7f")))
     self.assertTrue(is_ascii_safe(u("\x00abc\x7f")))
     self.assertFalse(is_ascii_safe(b("\x00abc\x80")))
     self.assertFalse(is_ascii_safe(u("\x00abc\x80")))
Ejemplo n.º 4
0
    def test_10_identify(self):
        """test GenericHandler.identify()"""
        class d1(uh.GenericHandler):
            @classmethod
            def from_string(cls, hash):
                if isinstance(hash, bytes):
                    hash = hash.decode("ascii")
                if hash == u('a'):
                    return cls(checksum=hash)
                else:
                    raise ValueError

        # check fallback
        self.assertRaises(TypeError, d1.identify, None)
        self.assertRaises(TypeError, d1.identify, 1)
        self.assertFalse(d1.identify(''))
        self.assertTrue(d1.identify('a'))
        self.assertFalse(d1.identify('b'))

        # check regexp
        d1._hash_regex = re.compile(u('@.'))
        self.assertRaises(TypeError, d1.identify, None)
        self.assertRaises(TypeError, d1.identify, 1)
        self.assertTrue(d1.identify('@a'))
        self.assertFalse(d1.identify('a'))
        del d1._hash_regex

        # check ident-based
        d1.ident = u('!')
        self.assertRaises(TypeError, d1.identify, None)
        self.assertRaises(TypeError, d1.identify, 1)
        self.assertTrue(d1.identify('!a'))
        self.assertFalse(d1.identify('a'))
        del d1.ident
Ejemplo n.º 5
0
    def test_to_native_str(self):
        """test to_native_str()"""
        from lib.passlib.utils import to_native_str

        # test plain ascii
        self.assertEqual(to_native_str(u('abc'), 'ascii'), 'abc')
        self.assertEqual(to_native_str(b('abc'), 'ascii'), 'abc')

        # test invalid ascii
        if PY3:
            self.assertEqual(to_native_str(u('\xE0'), 'ascii'), '\xE0')
            self.assertRaises(UnicodeDecodeError, to_native_str, b('\xC3\xA0'),
                              'ascii')
        else:
            self.assertRaises(UnicodeEncodeError, to_native_str, u('\xE0'),
                              'ascii')
            self.assertEqual(to_native_str(b('\xC3\xA0'), 'ascii'), '\xC3\xA0')

        # test latin-1
        self.assertEqual(to_native_str(u('\xE0'), 'latin-1'), '\xE0')
        self.assertEqual(to_native_str(b('\xE0'), 'latin-1'), '\xE0')

        # test utf-8
        self.assertEqual(to_native_str(u('\xE0'), 'utf-8'),
                         '\xE0' if PY3 else '\xC3\xA0')
        self.assertEqual(to_native_str(b('\xC3\xA0'), 'utf-8'),
                         '\xE0' if PY3 else '\xC3\xA0')

        # other types rejected
        self.assertRaises(TypeError, to_native_str, None, 'ascii')
Ejemplo n.º 6
0
    def test_02_handler_wrapper(self):
        """test Hasher-compatible handler wrappers"""
        if not has_django14:
            raise self.skipTest("Django >= 1.4 not installed")
        from lib.passlib.ext.django.utils import get_passlib_hasher
        from django.contrib.auth import hashers

        # should return native django hasher if available
        hasher = get_passlib_hasher("hex_md5")
        self.assertIsInstance(hasher, hashers.UnsaltedMD5PasswordHasher)

        hasher = get_passlib_hasher("django_bcrypt")
        self.assertIsInstance(hasher, hashers.BCryptPasswordHasher)

        # otherwise should return wrapper
        from lib.passlib.hash import sha256_crypt
        hasher = get_passlib_hasher("sha256_crypt")
        self.assertEqual(hasher.algorithm, "passlib_sha256_crypt")

        # and wrapper should return correct hash
        encoded = hasher.encode("stub")
        self.assertTrue(sha256_crypt.verify("stub", encoded))
        self.assertTrue(hasher.verify("stub", encoded))
        self.assertFalse(hasher.verify("xxxx", encoded))

        # test wrapper accepts options
        encoded = hasher.encode("stub", "abcd"*4, iterations=1234)
        self.assertEqual(encoded, "$5$rounds=1234$abcdabcdabcdabcd$"
                                  "v2RWkZQzctPdejyRqmmTDQpZN6wTh7.RUy9zF2LftT6")
        self.assertEqual(hasher.safe_summary(encoded),
            {'algorithm': 'sha256_crypt',
             'salt': u('abcdab**********'),
             'iterations': 1234,
             'hash': u('v2RWkZ*************************************'),
             })
Ejemplo n.º 7
0
 def to_string(self):
     if self.rounds == 5000 and self.implicit_rounds:
         hash = u("%s%s$%s") % (self.ident, self.salt, self.checksum
                                or u(''))
     else:
         hash = u("%srounds=%d$%s$%s") % (self.ident, self.rounds,
                                          self.salt, self.checksum or u(''))
     return uascii_to_str(hash)
Ejemplo n.º 8
0
 def to_string(self):
     if self.rounds == 5000 and self.implicit_rounds:
         hash = u("%s%s$%s") % (self.ident, self.salt,
                                self.checksum or u(''))
     else:
         hash = u("%srounds=%d$%s$%s") % (self.ident, self.rounds,
                                          self.salt, self.checksum or u(''))
     return uascii_to_str(hash)
Ejemplo n.º 9
0
 def test_lmhash(self):
     from lib.passlib.win32 import raw_lmhash
     for secret, hash in [
         ("OLDPASSWORD", u("c9b81d939d6fd80cd408e6b105741864")),
         ("NEWPASSWORD", u('09eeab5aa415d6e4d408e6b105741864')),
         ("welcome", u("c23413a8a1e7665faad3b435b51404ee")),
     ]:
         result = raw_lmhash(secret, hex=True)
         self.assertEqual(result, hash)
Ejemplo n.º 10
0
class ldap_md5(_Base64DigestHelper):
    """This class stores passwords using LDAP's plain MD5 format, and follows the :ref:`password-hash-api`.

    The :meth:`~passlib.ifc.PasswordHash.hash` and :meth:`~passlib.ifc.PasswordHash.genconfig` methods have no optional keywords.
    """
    name = "ldap_md5"
    ident = u("{MD5}")
    _hash_func = md5
    _hash_regex = re.compile(u(r"^\{MD5\}(?P<chk>[+/a-zA-Z0-9]{22}==)$"))
Ejemplo n.º 11
0
class ldap_sha1(_Base64DigestHelper):
    """This class stores passwords using LDAP's plain SHA1 format, and follows the :ref:`password-hash-api`.

    The :meth:`~passlib.ifc.PasswordHash.encrypt` and :meth:`~passlib.ifc.PasswordHash.genconfig` methods have no optional keywords.
    """
    name = "ldap_sha1"
    ident = u("{SHA}")
    _hash_func = sha1
    _hash_regex = re.compile(u(r"^\{SHA\}(?P<chk>[+/a-zA-Z0-9]{27}=)$"))
Ejemplo n.º 12
0
 def test_lmhash(self):
     from lib.passlib.win32 import raw_lmhash
     for secret, hash in [
         ("OLDPASSWORD", u("c9b81d939d6fd80cd408e6b105741864")),
         ("NEWPASSWORD", u('09eeab5aa415d6e4d408e6b105741864')),
         ("welcome", u("c23413a8a1e7665faad3b435b51404ee")),
         ]:
         result = raw_lmhash(secret, hex=True)
         self.assertEqual(result, hash)
Ejemplo n.º 13
0
 def test_nthash(self):
     warnings.filterwarnings("ignore",
                             r"nthash\.raw_nthash\(\) is deprecated")
     from lib.passlib.win32 import raw_nthash
     for secret, hash in [
         ("OLDPASSWORD", u("6677b2c394311355b54f25eec5bfacf5")),
         ("NEWPASSWORD", u("256781a62031289d3c2c98c14f1efc8c")),
         ]:
         result = raw_nthash(secret, hex=True)
         self.assertEqual(result, hash)
Ejemplo n.º 14
0
 def test_nthash(self):
     warnings.filterwarnings("ignore",
                             r"nthash\.raw_nthash\(\) is deprecated")
     from lib.passlib.win32 import raw_nthash
     for secret, hash in [
         ("OLDPASSWORD", u("6677b2c394311355b54f25eec5bfacf5")),
         ("NEWPASSWORD", u("256781a62031289d3c2c98c14f1efc8c")),
     ]:
         result = raw_nthash(secret, hex=True)
         self.assertEqual(result, hash)
Ejemplo n.º 15
0
    def test_91_parsehash(self):
        """test parsehash()"""
        # NOTE: this just tests some existing GenericHandler classes
        from lib.passlib import hash

        #
        # parsehash()
        #

        # simple hash w/ salt
        result = hash.des_crypt.parsehash("OgAwTx2l6NADI")
        self.assertEqual(result, {'checksum': u('AwTx2l6NADI'), 'salt': u('Og')})

        # parse rounds and extra implicit_rounds flag
        h = '$5$LKO/Ute40T3FNF95$U0prpBQd4PloSGU0pnpM4z9wKn4vZ1.jsrzQfPqxph9'
        s = u('LKO/Ute40T3FNF95')
        c = u('U0prpBQd4PloSGU0pnpM4z9wKn4vZ1.jsrzQfPqxph9')
        result = hash.sha256_crypt.parsehash(h)
        self.assertEqual(result, dict(salt=s, rounds=5000,
                                      implicit_rounds=True, checksum=c))

        # omit checksum
        result = hash.sha256_crypt.parsehash(h, checksum=False)
        self.assertEqual(result, dict(salt=s, rounds=5000, implicit_rounds=True))

        # sanitize
        result = hash.sha256_crypt.parsehash(h, sanitize=True)
        self.assertEqual(result, dict(rounds=5000, implicit_rounds=True,
            salt=u('LK**************'),
             checksum=u('U0pr***************************************')))

        # parse w/o implicit rounds flag
        result = hash.sha256_crypt.parsehash('$5$rounds=10428$uy/jIAhCetNCTtb0$YWvUOXbkqlqhyoPMpN8BMe.ZGsGx2aBvxTvDFI613c3')
        self.assertEqual(result, dict(
            checksum=u('YWvUOXbkqlqhyoPMpN8BMe.ZGsGx2aBvxTvDFI613c3'),
            salt=u('uy/jIAhCetNCTtb0'),
            rounds=10428,
        ))

        # parsing of raw checksums & salts
        h1 = '$pbkdf2$60000$DoEwpvQeA8B4T.k951yLUQ$O26Y3/NJEiLCVaOVPxGXshyjW8k'
        result = hash.pbkdf2_sha1.parsehash(h1)
        self.assertEqual(result, dict(
            checksum=b(';n\x98\xdf\xf3I\x12"\xc2U\xa3\x95?\x11\x97\xb2\x1c\xa3[\xc9'),
            rounds=60000,
            salt=b('\x0e\x810\xa6\xf4\x1e\x03\xc0xO\xe9=\xe7\\\x8bQ'),
        ))

        # sanitizing of raw checksums & salts
        result = hash.pbkdf2_sha1.parsehash(h1, sanitize=True)
        self.assertEqual(result, dict(
            checksum=u('O26************************'),
            rounds=60000,
            salt=u('Do********************'),
        ))
Ejemplo n.º 16
0
    def test_11_norm_checksum(self):
        """test GenericHandler checksum handling"""
        # setup helpers
        class d1(uh.GenericHandler):
            name = 'd1'
            checksum_size = 4
            checksum_chars = u('xz')
            _stub_checksum = u('z')*4

        def norm_checksum(*a, **k):
            return d1(*a, **k).checksum

        # too small
        self.assertRaises(ValueError, norm_checksum, u('xxx'))

        # right size
        self.assertEqual(norm_checksum(u('xxxx')), u('xxxx'))
        self.assertEqual(norm_checksum(u('xzxz')), u('xzxz'))

        # too large
        self.assertRaises(ValueError, norm_checksum, u('xxxxx'))

        # wrong chars
        self.assertRaises(ValueError, norm_checksum, u('xxyx'))

        # wrong type
        self.assertRaises(TypeError, norm_checksum, b('xxyx'))

        # relaxed
        with self.assertWarningList("checksum should be unicode"):
            self.assertEqual(norm_checksum(b('xxzx'), relaxed=True), u('xxzx'))
        self.assertRaises(TypeError, norm_checksum, 1, relaxed=True)

        # test _stub_checksum behavior
        self.assertIs(norm_checksum(u('zzzz')), None)
Ejemplo n.º 17
0
 def to_string(self, _withchk=True):
     ss = u('') if self.bare_salt else u('$')
     rounds = self.rounds
     if rounds > 0:
         hash = u("$md5,rounds=%d$%s%s") % (rounds, self.salt, ss)
     else:
         hash = u("$md5$%s%s") % (self.salt, ss)
     if _withchk:
         chk = self.checksum
         hash = u("%s$%s") % (hash, chk)
     return uascii_to_str(hash)
Ejemplo n.º 18
0
 def to_string(self, withchk=True):
     ss = u('') if self.bare_salt else u('$')
     rounds = self.rounds
     if rounds > 0:
         hash = u("$md5,rounds=%d$%s%s") % (rounds, self.salt, ss)
     else:
         hash = u("$md5$%s%s") % (self.salt, ss)
     if withchk:
         chk = self.checksum
         if chk:
             hash = u("%s$%s") % (hash, chk)
     return uascii_to_str(hash)
Ejemplo n.º 19
0
        class d1(uh.StaticHandler):
            name = "d1"
            context_kwds = ("flag",)
            _hash_prefix = u("_")
            checksum_chars = u("ab")
            checksum_size = 1

            def __init__(self, flag=False, **kwds):
                super(d1, self).__init__(**kwds)
                self.flag = flag

            def _calc_checksum(self, secret):
                return u('b') if self.flag else u('a')
Ejemplo n.º 20
0
 def _calc_checksum(self, secret):
     if self.checksum:
         # NOTE: hash will generally be "!", but we want to preserve
         # it in case it's something else, like "*".
         return self.checksum
     else:
         return u("!")
Ejemplo n.º 21
0
 def from_string(cls, hash):
     if isinstance(hash, bytes):
         hash = hash.decode("ascii")
     if hash == u('a'):
         return cls(checksum=hash)
     else:
         raise ValueError
Ejemplo n.º 22
0
class ldap_plaintext(plaintext):
    """This class stores passwords in plaintext, and follows the :ref:`password-hash-api`.

    This class acts much like the generic :class:`!passlib.hash.plaintext` handler,
    except that it will identify a hash only if it does NOT begin with the ``{XXX}`` identifier prefix
    used by RFC2307 passwords.

    The :meth:`~passlib.ifc.PasswordHash.encrypt`, :meth:`~passlib.ifc.PasswordHash.genhash`, and :meth:`~passlib.ifc.PasswordHash.verify` methods all require the
    following additional contextual keyword:

    :type encoding: str
    :param encoding:
        This controls the character encoding to use (defaults to ``utf-8``).

        This encoding will be used to encode :class:`!unicode` passwords
        under Python 2, and decode :class:`!bytes` hashes under Python 3.

    .. versionchanged:: 1.6
        The ``encoding`` keyword was added.
    """
    # NOTE: this subclasses plaintext, since all it does differently
    # is override identify()

    name = "ldap_plaintext"
    _2307_pat = re.compile(u(r"^\{\w+\}.*$"))

    @classmethod
    def identify(cls, hash):
        # NOTE: identifies all strings EXCEPT those with {XXX} prefix
        hash = uh.to_unicode_for_identify(hash)
        return bool(hash) and cls._2307_pat.match(hash) is None
Ejemplo n.º 23
0
 def to_string(self, withchk=True):
     salt = hexlify(self.salt).decode("ascii").upper()
     if withchk and self.checksum:
         chk = hexlify(self.checksum).decode("ascii").upper()
     else:
         chk = None
     return uh.render_mc3(self.ident, self.rounds, salt, chk, sep=u("."))
Ejemplo n.º 24
0
class mysql41(uh.StaticHandler):
    """This class implements the MySQL 4.1 password hash, and follows the :ref:`password-hash-api`.

    It has no salt and a single fixed round.

    The :meth:`~passlib.ifc.PasswordHash.hash` and :meth:`~passlib.ifc.PasswordHash.genconfig` methods accept no optional keywords.
    """
    #===================================================================
    # class attrs
    #===================================================================
    name = "mysql41"
    _hash_prefix = u("*")
    checksum_chars = uh.HEX_CHARS
    checksum_size = 40

    #===================================================================
    # methods
    #===================================================================
    @classmethod
    def _norm_hash(cls, hash):
        return hash.upper()

    def _calc_checksum(self, secret):
        # FIXME: no idea if mysql has a policy about handling unicode passwords
        if isinstance(secret, unicode):
            secret = secret.encode("utf-8")
        return str_to_uascii(sha1(sha1(secret).digest()).hexdigest()).upper()
Ejemplo n.º 25
0
 def from_string(cls, hash):
     rounds, salt, chk = uh.parse_mc3(hash, cls.ident, sep=u("."),
                                      handler=cls)
     salt = unhexlify(salt.encode("ascii"))
     if chk:
         chk = unhexlify(chk.encode("ascii"))
     return cls(rounds=rounds, salt=salt, checksum=chk)
Ejemplo n.º 26
0
    def test_norm_hash_name(self):
        """test norm_hash_name()"""
        from itertools import chain
        from lib.passlib.utils.pbkdf2 import norm_hash_name, _nhn_hash_names

        # test formats
        for format in self.ndn_formats:
            norm_hash_name("md4", format)
        self.assertRaises(ValueError, norm_hash_name, "md4", None)
        self.assertRaises(ValueError, norm_hash_name, "md4", "fake")

        # test types
        self.assertEqual(norm_hash_name(u("MD4")), "md4")
        self.assertEqual(norm_hash_name(b("MD4")), "md4")
        self.assertRaises(TypeError, norm_hash_name, None)

        # test selected results
        with catch_warnings():
            warnings.filterwarnings("ignore", '.*unknown hash')
            for row in chain(_nhn_hash_names, self.ndn_values):
                for idx, format in enumerate(self.ndn_formats):
                    correct = row[idx]
                    for value in row:
                        result = norm_hash_name(value, format)
                        self.assertEqual(
                            result, correct,
                            "name=%r, format=%r:" % (value, format))
Ejemplo n.º 27
0
class postgres_md5(uh.HasUserContext, uh.StaticHandler):
    """This class implements the Postgres MD5 Password hash, and follows the :ref:`password-hash-api`.

    It does a single round of hashing, and relies on the username as the salt.

    The :meth:`~passlib.ifc.PasswordHash.encrypt`, :meth:`~passlib.ifc.PasswordHash.genhash`, and :meth:`~passlib.ifc.PasswordHash.verify` methods all require the
    following additional contextual keywords:

    :type user: str
    :param user: name of postgres user account this password is associated with.
    """
    #===================================================================
    # algorithm information
    #===================================================================
    name = "postgres_md5"
    _hash_prefix = u("md5")
    checksum_chars = uh.HEX_CHARS
    checksum_size = 32

    #===================================================================
    # primary interface
    #===================================================================
    def _calc_checksum(self, secret):
        if isinstance(secret, unicode):
            secret = secret.encode("utf-8")
        user = to_bytes(self.user, "utf-8", param="user")
        return str_to_uascii(md5(secret + user).hexdigest())
Ejemplo n.º 28
0
class apr_md5_crypt(_MD5_Common):
    """This class implements the Apr-MD5-Crypt password hash, and follows the :ref:`password-hash-api`.

    It supports a variable-length salt.

    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 0-8 characters, drawn from the regexp range ``[./0-9A-Za-z]``.

    :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
        ``salt`` strings that are too long.

        .. versionadded:: 1.6
    """
    #===================================================================
    # class attrs
    #===================================================================
    name = "apr_md5_crypt"
    ident = u("$apr1$")

    #===================================================================
    # methods
    #===================================================================
    def _calc_checksum(self, secret):
        return _raw_md5_crypt(secret, self.salt, use_apr=True)
Ejemplo n.º 29
0
def genseed(value=None):
    """generate prng seed value from system resources"""
    from hashlib import sha512
    if hasattr(value, "getstate") and hasattr(value, "getrandbits"):
        # caller passed in RNG as seed value
        try:
            value = value.getstate()
        except NotImplementedError:
            # this method throws error for e.g. SystemRandom instances,
            # so fall back to extracting 4k of state
            value = value.getrandbits(1 << 15)
    text = u("%s %s %s %.15f %.15f %s") % (
        # if caller specified a seed value, mix it in
        value,

        # add current process id
        # NOTE: not available in some environments, e.g. GAE
        os.getpid() if hasattr(os, "getpid") else None,

        # id of a freshly created object.
        # (at least 1 byte of which should be hard to predict)
        id(object()),

        # the current time, to whatever precision os uses
        time.time(),
        tick(),

        # if urandom available, might as well mix some bytes in.
        os.urandom(32).decode("latin-1") if has_urandom else 0,
        )
    # hash it all up and return it as int/long
    return int(sha512(text.encode("utf-8")).hexdigest(), 16)
Ejemplo n.º 30
0
 def _calc_checksum(self, secret):
     if self.checksum:
         # NOTE: hash will generally be "!", but we want to preserve
         # it in case it's something else, like "*".
         return self.checksum
     else:
         return u("!")
Ejemplo n.º 31
0
    def test_secret_param(self):
        """'secret' parameter"""
        def run_scrypt(secret):
            return hexstr(scrypt_mod.scrypt(secret, "salt", 2, 2, 2, 16))

        # unicode
        TEXT = u("abc\u00defg")
        self.assertEqual(run_scrypt(TEXT), '05717106997bfe0da42cf4779a2f8bd8')

        # utf8 bytes
        TEXT_UTF8 = b'abc\xc3\x9efg'
        self.assertEqual(run_scrypt(TEXT_UTF8),
                         '05717106997bfe0da42cf4779a2f8bd8')

        # latin1 bytes
        TEXT_LATIN1 = b'abc\xdefg'
        self.assertEqual(run_scrypt(TEXT_LATIN1),
                         '770825d10eeaaeaf98e8a3c40f9f441d')

        # accept empty string
        self.assertEqual(run_scrypt(""), 'ca1399e5fae5d3b9578dcd2b1faff6e2')

        # reject other types
        self.assertRaises(TypeError, run_scrypt, None)
        self.assertRaises(TypeError, run_scrypt, 1)
Ejemplo n.º 32
0
 def from_string(cls, hash):
     ident, tail = cls._parse_ident(hash)
     if ident == IDENT_2X:
         raise ValueError("crypt_blowfish's buggy '2x' hashes are not "
                          "currently supported")
     rounds_str, data = tail.split(u("$"))
     rounds = int(rounds_str)
     if rounds_str != u('%02d') % (rounds, ):
         raise uh.exc.MalformedHashError(cls, "malformed cost field")
     salt, chk = data[:22], data[22:]
     return cls(
         rounds=rounds,
         salt=salt,
         checksum=chk or None,
         ident=ident,
     )
Ejemplo n.º 33
0
 def to_string(self, withchk=True):
     salt = hexlify(self.salt).decode("ascii").upper()
     if withchk and self.checksum:
         chk = hexlify(self.checksum).decode("ascii").upper()
     else:
         chk = None
     return uh.render_mc3(self.ident, self.rounds, salt, chk, sep=u("."))
Ejemplo n.º 34
0
 def from_string(cls, hash):
     rounds, salt, chk = uh.parse_mc3(hash, cls.ident, sep=u("."),
                                      handler=cls)
     salt = unhexlify(salt.encode("ascii"))
     if chk:
         chk = unhexlify(chk.encode("ascii"))
     return cls(rounds=rounds, salt=salt, checksum=chk)
Ejemplo n.º 35
0
def _init_ldap_crypt_handlers():
    # NOTE: I don't like to implicitly modify globals() like this,
    #       but don't want to write out all these handlers out either :)
    g = globals()
    for wname in unix_crypt_schemes:
        name = 'ldap_' + wname
        g[name] = uh.PrefixWrapper(name, wname, prefix=u("{CRYPT}"), lazy=True)
    del g
Ejemplo n.º 36
0
    def test_12_ident(self):
        # test ident is proxied
        h = uh.PrefixWrapper("h2", "ldap_md5", "{XXX}")
        self.assertEqual(h.ident, u("{XXX}{MD5}"))
        self.assertIs(h.ident_values, None)

        # test lack of ident means no proxy
        h = uh.PrefixWrapper("h2", "des_crypt", "{XXX}")
        self.assertIs(h.ident, None)
        self.assertIs(h.ident_values, None)

        # test orig_prefix disabled ident proxy
        h = uh.PrefixWrapper("h1", "ldap_md5", "{XXX}", "{MD5}")
        self.assertIs(h.ident, None)
        self.assertIs(h.ident_values, None)

        # test custom ident overrides default
        h = uh.PrefixWrapper("h3", "ldap_md5", "{XXX}", ident="{X")
        self.assertEqual(h.ident, u("{X"))
        self.assertIs(h.ident_values, None)

        # test custom ident must match
        h = uh.PrefixWrapper("h3", "ldap_md5", "{XXX}", ident="{XXX}A")
        self.assertRaises(ValueError, uh.PrefixWrapper, "h3", "ldap_md5",
                          "{XXX}", ident="{XY")
        self.assertRaises(ValueError, uh.PrefixWrapper, "h3", "ldap_md5",
                          "{XXX}", ident="{XXXX")

        # test ident_values is proxied
        h = uh.PrefixWrapper("h4", "phpass", "{XXX}")
        self.assertIs(h.ident, None)
        self.assertEqual(h.ident_values, [ u("{XXX}$P$"), u("{XXX}$H$") ])

        # test ident=True means use prefix even if hash has no ident.
        h = uh.PrefixWrapper("h5", "des_crypt", "{XXX}", ident=True)
        self.assertEqual(h.ident, u("{XXX}"))
        self.assertIs(h.ident_values, None)

        # ... but requires prefix
        self.assertRaises(ValueError, uh.PrefixWrapper, "h6", "des_crypt", ident=True)

        # orig_prefix + HasManyIdent - warning
        with self.assertWarningList("orig_prefix.*may not work correctly"):
            h = uh.PrefixWrapper("h7", "phpass", orig_prefix="$", prefix="?")
        self.assertEqual(h.ident_values, None) # TODO: should output (u("?P$"), u("?H$")))
        self.assertEqual(h.ident, None)
Ejemplo n.º 37
0
    def test_crypt(self):
        """test crypt.crypt() wrappers"""
        from lib.passlib.utils import has_crypt, safe_crypt, test_crypt

        # test everything is disabled
        if not has_crypt:
            self.assertEqual(safe_crypt("test", "aa"), None)
            self.assertFalse(test_crypt("test", "aaqPiZY5xR5l."))
            raise self.skipTest("crypt.crypt() not available")

        # XXX: this assumes *every* crypt() implementation supports des_crypt.
        #      if this fails for some platform, this test will need modifying.

        # test return type
        self.assertIsInstance(safe_crypt(u("test"), u("aa")), unicode)

        # test ascii password
        h1 = u('aaqPiZY5xR5l.')
        self.assertEqual(safe_crypt(u('test'), u('aa')), h1)
        self.assertEqual(safe_crypt(b('test'), b('aa')), h1)

        # test utf-8 / unicode password
        h2 = u('aahWwbrUsKZk.')
        self.assertEqual(safe_crypt(u('test\u1234'), 'aa'), h2)
        self.assertEqual(safe_crypt(b('test\xe1\x88\xb4'), 'aa'), h2)

        # test latin-1 password
        hash = safe_crypt(b('test\xff'), 'aa')
        if PY3:  # py3 supports utf-8 bytes only.
            self.assertEqual(hash, None)
        else:  # but py2 is fine.
            self.assertEqual(hash, u('aaOx.5nbTU/.M'))

        # test rejects null chars in password
        self.assertRaises(ValueError, safe_crypt, '\x00', 'aa')

        # check test_crypt()
        h1x = h1[:-1] + 'x'
        self.assertTrue(test_crypt("test", h1))
        self.assertFalse(test_crypt("test", h1x))

        # check crypt returning variant error indicators
        # some platforms return None on errors, others empty string,
        # The BSDs in some cases return ":"
        import lib.passlib.utils as mod
        orig = mod._crypt
        try:
            fake = None
            mod._crypt = lambda secret, hash: fake
            for fake in [None, "", ":", ":0", "*0"]:
                self.assertEqual(safe_crypt("test", "aa"), None)
                self.assertFalse(test_crypt("test", h1))
            fake = 'xxx'
            self.assertEqual(safe_crypt("test", "aa"), "xxx")
        finally:
            mod._crypt = orig
Ejemplo n.º 38
0
    def test_12_norm_checksum_raw(self):
        """test GenericHandler + HasRawChecksum mixin"""
        class d1(uh.HasRawChecksum, uh.GenericHandler):
            name = 'd1'
            checksum_size = 4
            _stub_checksum = b('0')*4

        def norm_checksum(*a, **k):
            return d1(*a, **k).checksum

        # test bytes
        self.assertEqual(norm_checksum(b('1234')), b('1234'))

        # test unicode
        self.assertRaises(TypeError, norm_checksum, u('xxyx'))
        self.assertRaises(TypeError, norm_checksum, u('xxyx'), relaxed=True)

        # test _stub_checksum behavior
        self.assertIs(norm_checksum(b('0')*4), None)
Ejemplo n.º 39
0
    def test_00_static_handler(self):
        """test StaticHandler class"""

        class d1(uh.StaticHandler):
            name = "d1"
            context_kwds = ("flag",)
            _hash_prefix = u("_")
            checksum_chars = u("ab")
            checksum_size = 1

            def __init__(self, flag=False, **kwds):
                super(d1, self).__init__(**kwds)
                self.flag = flag

            def _calc_checksum(self, secret):
                return u('b') if self.flag else u('a')

        # check default identify method
        self.assertTrue(d1.identify(u('_a')))
        self.assertTrue(d1.identify(b('_a')))
        self.assertTrue(d1.identify(u('_b')))

        self.assertFalse(d1.identify(u('_c')))
        self.assertFalse(d1.identify(b('_c')))
        self.assertFalse(d1.identify(u('a')))
        self.assertFalse(d1.identify(u('b')))
        self.assertFalse(d1.identify(u('c')))
        self.assertRaises(TypeError, d1.identify, None)
        self.assertRaises(TypeError, d1.identify, 1)

        # check default genconfig method
        self.assertIs(d1.genconfig(), None)

        # check default verify method
        self.assertTrue(d1.verify('s', b('_a')))
        self.assertTrue(d1.verify('s',u('_a')))
        self.assertFalse(d1.verify('s', b('_b')))
        self.assertFalse(d1.verify('s',u('_b')))
        self.assertTrue(d1.verify('s', b('_b'), flag=True))
        self.assertRaises(ValueError, d1.verify, 's', b('_c'))
        self.assertRaises(ValueError, d1.verify, 's', u('_c'))

        # check default encrypt method
        self.assertEqual(d1.encrypt('s'), '_a')
        self.assertEqual(d1.encrypt('s', flag=True), '_b')
Ejemplo n.º 40
0
 def _calc_checksum_builtin(self, secret):
     if isinstance(secret, unicode):
         secret = secret.encode("utf-8")
     if _BNULL in secret:
         raise uh.exc.NullPasswordError(self)
     rounds = self.rounds
     # NOTE: this seed value is NOT the same as the config string
     result = (u("%s$sha1$%s") % (self.salt, rounds)).encode("ascii")
     # NOTE: this algorithm is essentially PBKDF1, modified to use HMAC.
     r = 0
     while r < rounds:
         result = _hmac_sha1(secret, result)
         r += 1
     return h64.encode_transposed_bytes(result, self._chk_offsets).decode("ascii")
Ejemplo n.º 41
0
def create_pbkdf2_hash(hash_name, digest_size, rounds=12000, ident=None, module=__name__):
    """create new Pbkdf2DigestHandler subclass for a specific hash"""
    name = 'pbkdf2_' + hash_name
    if ident is None:
        ident = u("$pbkdf2-%s$") % (hash_name,)
    prf = "hmac-%s" % (hash_name,)
    base = Pbkdf2DigestHandler
    return type(name, (base,), dict(
        __module__=module, # so ABCMeta won't clobber it.
        name=name,
        ident=ident,
        _prf = prf,
        default_rounds=rounds,
        checksum_size=digest_size,
        encoded_checksum_size=(digest_size*4+2)//3,
        __doc__="""This class implements a generic ``PBKDF2-%(prf)s``-based password hash, and follows the :ref:`password-hash-api`.

    It supports a variable-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: bytes
    :param salt:
        Optional salt bytes.
        If specified, the length must be between 0-1024 bytes.
        If not specified, a %(dsc)d byte salt will be autogenerated (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 %(dr)d, but must be within ``range(1,1<<32)``.

    :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
    """ % dict(prf=prf.upper(), dsc=base.default_salt_size, dr=rounds)
    ))
Ejemplo n.º 42
0
    def _calc_checksum(self, secret):
        # FIXME: no idea if mysql has a policy about handling unicode passwords
        if isinstance(secret, unicode):
            secret = secret.encode("utf-8")

        MASK_32 = 0xffffffff
        MASK_31 = 0x7fffffff
        WHITE = b(' \t')

        nr1 = 0x50305735
        nr2 = 0x12345671
        add = 7
        for c in secret:
            if c in WHITE:
                continue
            tmp = byte_elem_value(c)
            nr1 ^= ((((nr1 & 63)+add)*tmp) + (nr1 << 8)) & MASK_32
            nr2 = (nr2+((nr2 << 8) ^ nr1)) & MASK_32
            add = (add+tmp) & MASK_32
        return u("%08x%08x") % (nr1 & MASK_31, nr2 & MASK_31)
Ejemplo n.º 43
0
    :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
    """ % dict(prf=prf.upper(), dsc=base.default_salt_size, dr=rounds)
    ))

#------------------------------------------------------------------------
# derived handlers
#------------------------------------------------------------------------
pbkdf2_sha1 = create_pbkdf2_hash("sha1", 20, 131000, ident=u("$pbkdf2$"))
pbkdf2_sha256 = create_pbkdf2_hash("sha256", 32, 29000)
pbkdf2_sha512 = create_pbkdf2_hash("sha512", 64, 25000)

ldap_pbkdf2_sha1 = uh.PrefixWrapper("ldap_pbkdf2_sha1", pbkdf2_sha1, "{PBKDF2}", "$pbkdf2$", ident=True)
ldap_pbkdf2_sha256 = uh.PrefixWrapper("ldap_pbkdf2_sha256", pbkdf2_sha256, "{PBKDF2-SHA256}", "$pbkdf2-sha256$", ident=True)
ldap_pbkdf2_sha512 = uh.PrefixWrapper("ldap_pbkdf2_sha512", pbkdf2_sha512, "{PBKDF2-SHA512}", "$pbkdf2-sha512$", ident=True)

#=============================================================================
# cryptacular's pbkdf2 hash
#=============================================================================

# bytes used by cta hash for base64 values 63 & 64
CTA_ALTCHARS = b("-_")

class cta_pbkdf2_sha1(uh.HasRounds, uh.HasRawSalt, uh.HasRawChecksum, uh.GenericHandler):
Ejemplo n.º 44
0
            # it in case it's something else, like "*".
            return self.checksum
        else:
            return u("!")

    @classmethod
    def verify(cls, secret, hash, enable_wildcard=False):
        uh.validate_secret(secret)
        if not isinstance(hash, base_string_types):
            raise uh.exc.ExpectedStringError(hash, "hash")
        elif hash:
            return False
        else:
            return enable_wildcard

_MARKER_CHARS = u("*!")
_MARKER_BYTES = b("*!")

class unix_disabled(uh.PasswordHash):
    """This class provides disabled password behavior for unix shadow files,
    and follows the :ref:`password-hash-api`.

    This class does not implement a hash, but instead matches the "disabled account"
    strings found in ``/etc/shadow`` on most Unix variants. "encrypting" a password
    will simply return the disabled account marker. It will reject all passwords,
    no matter the hash string. The :meth:`~passlib.ifc.PasswordHash.encrypt`
    method supports one optional keyword:

    :type marker: str
    :param marker:
        Optional marker string which overrides the platform default
Ejemplo n.º 45
0
 def to_string(self):
     hash = u("@salt%s%s") % (self.salt, self.checksum or self._stub_checksum)
     return uascii_to_str(hash)
Ejemplo n.º 46
0
"""passlib.handlers.roundup - Roundup issue tracker hashes"""
#=============================================================================
# imports
#=============================================================================
# core
import logging; log = logging.getLogger(__name__)
# site
# pkg
import lib.passlib.utils.handlers as uh
from lib.passlib.utils.compat import u
# local
__all__ = [
    "roundup_plaintext",
    "ldap_hex_md5",
    "ldap_hex_sha1",
]
#=============================================================================
#
#=============================================================================
roundup_plaintext = uh.PrefixWrapper("roundup_plaintext", "plaintext",
                                     prefix=u("{plaintext}"), lazy=True)

# NOTE: these are here because they're currently only known to be used by roundup
ldap_hex_md5 = uh.PrefixWrapper("ldap_hex_md5", "hex_md5", u("{MD5}"), lazy=True)
ldap_hex_sha1 = uh.PrefixWrapper("ldap_hex_sha1", "hex_sha1", u("{SHA}"), lazy=True)

#=============================================================================
# eof
#=============================================================================
Ejemplo n.º 47
0
            dc = hash_const(odd + hash_const(dc + even).digest()).digest()

        # if rounds was odd, do one last round (since we started at 0,
        # last round will be an even-numbered round)
        if tail & 1:
            dc = hash_const(dc + data[pairs][0]).digest()

    #===================================================================
    # encode digest using appropriate transpose map
    #===================================================================
    return h64.encode_transposed_bytes(dc, transpose_map).decode("ascii")

#=============================================================================
# handlers
#=============================================================================
_UROUNDS = u("rounds=")
_UDOLLAR = u("$")
_UZERO = u("0")

class _SHA2_Common(uh.HasManyBackends, uh.HasRounds, uh.HasSalt,
                   uh.GenericHandler):
    """class containing common code shared by sha256_crypt & sha512_crypt"""
    #===================================================================
    # class attrs
    #===================================================================
    # name - set by subclass
    setting_kwds = ("salt", "rounds", "implicit_rounds", "salt_size")
    # ident - set by subclass
    checksum_chars = uh.HASH64_CHARS
    # checksum_size - set by subclass
Ejemplo n.º 48
0
# site
# pkg
from lib.passlib import hash
from lib.passlib.utils import repeat_string
from lib.passlib.utils.compat import irange, PY3, u, get_method_function
from lib.passlib.tests.utils import TestCase, HandlerCase, skipUnless, \
        TEST_MODE, b, catch_warnings, UserHandlerMixin, randintgauss, EncodingHandlerMixin
from lib.passlib.tests.test_handlers import UPASS_WAV, UPASS_USD, UPASS_TABLE
# module

#=============================================================================
# django
#=============================================================================

# standard string django uses
UPASS_LETMEIN = u('l\xe8tmein')

def vstr(version):
    return ".".join(str(e) for e in version)

class _DjangoHelper(object):
    # NOTE: not testing against Django < 1.0 since it doesn't support
    # most of these hash formats.

    # flag that hash wasn't added until specified version
    min_django_version = ()

    def fuzz_verifier_django(self):
        from lib.passlib.tests.test_ext_django import DJANGO_VERSION
        # check_password() not added until 1.0
        min_django_version = max(self.min_django_version, (1,0))
Ejemplo n.º 49
0
        return uascii_to_str(hash)

    def _calc_checksum(self, secret):
        if isinstance(secret, unicode):
            secret = secret.encode("utf-8")
        data = self.salt.encode("ascii") + secret + self.salt.encode("ascii")
        return str_to_uascii(hashlib.sha1(data).hexdigest())

#=============================================================================
# test sample algorithms - really a self-test of HandlerCase
#=============================================================================

# TODO: provide data samples for algorithms
#       (positive knowns, negative knowns, invalid identify)

UPASS_TEMP = u('\u0399\u03c9\u03b1\u03bd\u03bd\u03b7\u03c2')

class UnsaltedHashTest(HandlerCase):
    handler = UnsaltedHash

    known_correct_hashes = [
        ("password", "61cfd32684c47de231f1f982c214e884133762c0"),
        (UPASS_TEMP, '96b329d120b97ff81ada770042e44ba87343ad2b'),
    ]

    def test_bad_kwds(self):
        if not PY_MAX_25:
            # annoyingly, py25's ``super().__init__()`` doesn't throw TypeError
            # when passing unknown keywords to object. just ignoring
            # this issue for now, since it's a minor border case.
            self.assertRaises(TypeError, UnsaltedHash, salt='x')
Ejemplo n.º 50
0
    def test_50_norm_ident(self):
        """test GenericHandler + HasManyIdents"""
        # setup helpers
        class d1(uh.HasManyIdents, uh.GenericHandler):
            name = 'd1'
            setting_kwds = ('ident',)
            default_ident = u("!A")
            ident_values = [ u("!A"), u("!B") ]
            ident_aliases = { u("A"): u("!A")}

        def norm_ident(**k):
            return d1(**k).ident

        # check ident=None
        self.assertRaises(TypeError, norm_ident)
        self.assertRaises(TypeError, norm_ident, ident=None)
        self.assertEqual(norm_ident(use_defaults=True), u('!A'))

        # check valid idents
        self.assertEqual(norm_ident(ident=u('!A')), u('!A'))
        self.assertEqual(norm_ident(ident=u('!B')), u('!B'))
        self.assertRaises(ValueError, norm_ident, ident=u('!C'))

        # check aliases
        self.assertEqual(norm_ident(ident=u('A')), u('!A'))

        # check invalid idents
        self.assertRaises(ValueError, norm_ident, ident=u('B'))

        # check identify is honoring ident system
        self.assertTrue(d1.identify(u("!Axxx")))
        self.assertTrue(d1.identify(u("!Bxxx")))
        self.assertFalse(d1.identify(u("!Cxxx")))
        self.assertFalse(d1.identify(u("A")))
        self.assertFalse(d1.identify(u("")))
        self.assertRaises(TypeError, d1.identify, None)
        self.assertRaises(TypeError, d1.identify, 1)

        # check default_ident missing is detected.
        d1.default_ident = None
        self.assertRaises(AssertionError, norm_ident, use_defaults=True)
Ejemplo n.º 51
0
 def to_string(self):
     hash = u("%s%s") % (self.salt, self.checksum or u(''))
     return uascii_to_str(hash)
Ejemplo n.º 52
0
 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)
Ejemplo n.º 53
0
 def _calc_checksum(self, secret):
     return u('b') if self.flag else u('a')