示例#1
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")))
示例#2
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')
示例#3
0
    def test_05_load(self):
        """test load()"""
        # setup empty file
        path = self.mktemp()
        set_file(path, "")
        backdate_file_mtime(path, 5)
        ha = apache.HtpasswdFile(path, default_scheme="plaintext")
        self.assertEqual(ha.to_string(), b(""))

        # make changes, check load_if_changed() does nothing
        ha.set_password("user1", "pass1")
        ha.load_if_changed()
        self.assertEqual(ha.to_string(), b("user1:pass1\n"))

        # change file
        set_file(path, self.sample_01)
        ha.load_if_changed()
        self.assertEqual(ha.to_string(), self.sample_01)

        # make changes, check load() overwrites them
        ha.set_password("user5", "pass5")
        ha.load()
        self.assertEqual(ha.to_string(), self.sample_01)

        # test load w/ no path
        hb = apache.HtpasswdFile()
        self.assertRaises(RuntimeError, hb.load)
        self.assertRaises(RuntimeError, hb.load_if_changed)

        # test load w/ dups and explicit path
        set_file(path, self.sample_dup)
        hc = apache.HtpasswdFile()
        hc.load(path)
        self.assertTrue(hc.check_password('user1','pass1'))
示例#4
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)
示例#5
0
    def test_00_constructor_autoload(self):
        """test constructor autoload"""
        # check with existing file
        path = self.mktemp()
        set_file(path, self.sample_01)
        ht = apache.HtpasswdFile(path)
        self.assertEqual(ht.to_string(), self.sample_01)
        self.assertEqual(ht.path, path)
        self.assertTrue(ht.mtime)

        # check changing path
        ht.path = path + "x"
        self.assertEqual(ht.path, path + "x")
        self.assertFalse(ht.mtime)

        # check new=True
        ht = apache.HtpasswdFile(path, new=True)
        self.assertEqual(ht.to_string(), b(""))
        self.assertEqual(ht.path, path)
        self.assertFalse(ht.mtime)

        # check autoload=False (deprecated alias for new=True)
        with self.assertWarningList("``autoload=False`` is deprecated"):
            ht = apache.HtpasswdFile(path, autoload=False)
        self.assertEqual(ht.to_string(), b(""))
        self.assertEqual(ht.path, path)
        self.assertFalse(ht.mtime)

        # check missing file
        os.remove(path)
        self.assertRaises(IOError, apache.HtpasswdFile, path)
示例#6
0
def get_prf(name):
    """lookup pseudo-random family (prf) by name.

    :arg name:
        this must be the name of a recognized prf.
        currently this only recognizes names with the format
        :samp:`hmac-{digest}`, where :samp:`{digest}`
        is the name of a hash function such as
        ``md5``, ``sha256``, etc.

        this can also be a callable with the signature
        ``prf(secret, message) -> digest``,
        in which case it will be returned unchanged.

    :raises ValueError: if the name is not known
    :raises TypeError: if the name is not a callable or string

    :returns:
        a tuple of :samp:`({func}, {digest_size})`.

        * :samp:`{func}` is a function implementing
          the specified prf, and has the signature
          ``func(secret, message) -> digest``.

        * :samp:`{digest_size}` is an integer indicating
          the number of bytes the function returns.

    usage example::

        >>> from lib.passlib.utils.pbkdf2 import get_prf
        >>> hmac_sha256, dsize = get_prf("hmac-sha256")
        >>> hmac_sha256
        <function hmac_sha256 at 0x1e37c80>
        >>> dsize
        32
        >>> digest = hmac_sha256('password', 'message')

    this function will attempt to return the fastest implementation
    it can find; if M2Crypto is present, and supports the specified prf,
    :func:`M2Crypto.EVP.hmac` will be used behind the scenes.
    """
    global _prf_cache
    if name in _prf_cache:
        return _prf_cache[name]
    if isinstance(name, str):
        if name.startswith("hmac-") or name.startswith("hmac_"):
            retval = _get_hmac_prf(name[5:])
        else:
            raise ValueError("unknown prf algorithm: %r" % (name,))
    elif callable(name):
        # assume it's a callable, use it directly
        digest_size = len(name(b('x'),b('y')))
        retval = (name, digest_size)
    else:
        raise ExpectedTypeError(name, "str or callable", "prf name")
    _prf_cache[name] = retval
    return retval
示例#7
0
    def test_08_get_hash(self):
        """test get_hash()"""
        ht = apache.HtpasswdFile.from_string(self.sample_01)
        self.assertEqual(ht.get_hash("user3"), b("{SHA}3ipNV1GrBtxPmHFC21fCbVCSXIo="))
        self.assertEqual(ht.get_hash("user4"), b("pass4"))
        self.assertEqual(ht.get_hash("user5"), None)

        with self.assertWarningList("find\(\) is deprecated"):
            self.assertEqual(ht.find("user4"), b("pass4"))
示例#8
0
    def test_03_encrypt_bytes(self):
        """test des_encrypt_block()"""
        from lib.passlib.utils.des import (des_encrypt_block, shrink_des_key,
                                           _pack64, _unpack64)

        # run through test vectors
        for key, plaintext, correct in self.des_test_vectors:
            # convert to bytes
            key = _pack64(key)
            plaintext = _pack64(plaintext)
            correct = _pack64(correct)

            # test 64-bit key
            result = des_encrypt_block(key, plaintext)
            self.assertEqual(result, correct,
                             "key=%r plaintext=%r:" % (key, plaintext))

            # test 56-bit version
            key2 = shrink_des_key(key)
            result = des_encrypt_block(key2, plaintext)
            self.assertEqual(
                result, correct,
                "key=%r shrink(key)=%r plaintext=%r:" % (key, key2, plaintext))

            # test with random parity bits
            for _ in range(20):
                key3 = _pack64(self._random_parity(_unpack64(key)))
                result = des_encrypt_block(key3, plaintext)
                self.assertEqual(
                    result, correct, "key=%r rndparity(key)=%r plaintext=%r:" %
                    (key, key3, plaintext))

        # check invalid keys
        stub = b('\x00') * 8
        self.assertRaises(TypeError, des_encrypt_block, 0, stub)
        self.assertRaises(ValueError, des_encrypt_block, b('\x00') * 6, stub)

        # check invalid input
        self.assertRaises(TypeError, des_encrypt_block, stub, 0)
        self.assertRaises(ValueError, des_encrypt_block, stub, b('\x00') * 7)

        # check invalid salts
        self.assertRaises(ValueError, des_encrypt_block, stub, stub, salt=-1)
        self.assertRaises(ValueError,
                          des_encrypt_block,
                          stub,
                          stub,
                          salt=1 << 24)

        # check invalid rounds
        self.assertRaises(ValueError,
                          des_encrypt_block,
                          stub,
                          stub,
                          0,
                          rounds=0)
示例#9
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
示例#10
0
    def test_custom_prf(self):
        """test custom prf function"""
        from lib.passlib.utils.pbkdf2 import pbkdf2

        def prf(key, msg):
            return hashlib.md5(key + msg + b('fooey')).digest()

        result = pbkdf2(b('secret'), b('salt'), 1000, 20, prf)
        self.assertEqual(result,
                         hb('5fe7ce9f7e379d3f65cbc66ba8aa6440474a6849'))
示例#11
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********************'),
        ))
示例#12
0
    def test_md4_copy(self):
        """test md4 copy()"""
        from lib.passlib.utils.md4 import md4
        h = md4(b('abc'))

        h2 = h.copy()
        h2.update(b('def'))
        self.assertEqual(h2.hexdigest(), '804e7f1c2586e50b49ac65db5b645131')

        h.update(b('ghi'))
        self.assertEqual(h.hexdigest(), 'c5225580bfe176f6deeee33dee98732c')
示例#13
0
    def test_01_delete_autosave(self):
        path = self.mktemp()
        sample = b('user1:pass1\nuser2:pass2\n')
        set_file(path, sample)

        ht = apache.HtpasswdFile(path)
        ht.delete("user1")
        self.assertEqual(get_file(path), sample)

        ht = apache.HtpasswdFile(path, autosave=True)
        ht.delete("user1")
        self.assertEqual(get_file(path), b("user2:pass2\n"))
示例#14
0
def _get_hmac_prf(digest):
    """helper to return HMAC prf for specific digest"""
    def tag_wrapper(prf):
        prf.__name__ = "hmac_" + digest
        prf.__doc__ = ("hmac_%s(key, msg) -> digest;"
                       " generated by passlib.utils.pbkdf2.get_prf()" %
                       digest)

    if _EVP and digest == "sha1":
        # use m2crypto function directly for sha1, since that's its default digest
        try:
            result = _EVP.hmac(b('x'),b('y'))
        except ValueError: # pragma: no cover
            pass
        else:
            if result == _XY_DIGEST:
                return _EVP.hmac, 20
        # don't expect to ever get here, but will fall back to pure-python if we do.
        warn("M2Crypto.EVP.HMAC() returned unexpected result " # pragma: no cover -- sanity check
             "during Passlib self-test!", PasslibRuntimeWarning)
    elif _EVP:
        # use m2crypto if it's present and supports requested digest
        try:
            result = _EVP.hmac(b('x'), b('y'), digest)
        except ValueError:
            pass
        else:
            # it does. so use M2Crypto's hmac & digest code
            hmac_const = _EVP.hmac
            def prf(key, msg):
                return hmac_const(key, msg, digest)
            digest_size = len(result)
            tag_wrapper(prf)
            return prf, digest_size

    # fall back to hashlib-based implementation
    digest_const = getattr(hashlib, digest, None)
    if not digest_const:
        raise ValueError("unknown hash algorithm: %r" % (digest,))
    tmp = digest_const()
    block_size = tmp.block_size
    assert block_size >= 16, "unacceptably low block size"
    digest_size = tmp.digest_size
    del tmp
    def prf(key, msg):
        # simplified version of stdlib's hmac module
        if len(key) > block_size:
            key = digest_const(key).digest()
        key += _BNULL * (block_size - len(key))
        tmp = digest_const(key.translate(_trans_36) + msg).digest()
        return digest_const(key.translate(_trans_5C) + tmp).digest()
    tag_wrapper(prf)
    return prf, digest_size
示例#15
0
    def test_02_set_password_autosave(self):
        path = self.mktemp()
        sample = b('user1:pass1\n')
        set_file(path, sample)

        ht = apache.HtpasswdFile(path)
        ht.set_password("user1", "pass2")
        self.assertEqual(get_file(path), sample)

        ht = apache.HtpasswdFile(path, default_scheme="plaintext", autosave=True)
        ht.set_password("user1", "pass2")
        self.assertEqual(get_file(path), b("user1:pass2\n"))
示例#16
0
    def test_bytes(self):
        """test b() helper, bytes and native str type"""
        if PY3:
            import builtins
            self.assertIs(bytes, builtins.bytes)
        else:
            import __builtin__ as builtins
            self.assertIs(bytes, builtins.str)

        self.assertIsInstance(b(''), bytes)
        self.assertIsInstance(b('\x00\xff'), bytes)
        if PY3:
            self.assertEqual(b('\x00\xff').decode("latin-1"), "\x00\xff")
        else:
            self.assertEqual(b('\x00\xff'), "\x00\xff")
示例#17
0
    def test_decode_bytes_padding(self):
        """test decode_bytes() ignores padding bits"""
        bchr = (lambda v: bytes([v])) if PY3 else chr
        engine = self.engine
        m = self.m
        decode = engine.decode_bytes
        BNULL = b("\x00")

        # length == 2 mod 4: 4 bits of padding
        self.assertEqual(decode(m(0, 0)), BNULL)
        for i in range(0, 6):
            if engine.big:  # 4 lsb padding
                correct = BNULL if i < 4 else bchr(1 << (i - 4))
            else:  # 4 msb padding
                correct = bchr(1 << (i + 6)) if i < 2 else BNULL
            self.assertEqual(decode(m(0, 1 << i)), correct, "%d/4 bits:" % i)

        # length == 3 mod 4: 2 bits of padding
        self.assertEqual(decode(m(0, 0, 0)), BNULL * 2)
        for i in range(0, 6):
            if engine.big:  # 2 lsb are padding
                correct = BNULL if i < 2 else bchr(1 << (i - 2))
            else:  # 2 msg are padding
                correct = bchr(1 << (i + 4)) if i < 4 else BNULL
            self.assertEqual(decode(m(0, 0, 1 << i)), BNULL + correct,
                             "%d/2 bits:" % i)
示例#18
0
    def test_md4_update(self):
        """test md4 update"""
        from lib.passlib.utils.md4 import md4
        h = md4(b(''))
        self.assertEqual(h.hexdigest(), "31d6cfe0d16ae931b73c59d7e0c089c0")

        # NOTE: under py2, hashlib methods try to encode to ascii,
        #       though shouldn't rely on that.
        if PY3 or self._disable_native:
            self.assertRaises(TypeError, h.update, u('x'))

        h.update(b('a'))
        self.assertEqual(h.hexdigest(), "bde52cb31de33e46245e05fbdbd6fb24")

        h.update(b('bcdefghijklmnopqrstuvwxyz'))
        self.assertEqual(h.hexdigest(), "d79e1c308aa5bbcdeea8ed63df412da9")
示例#19
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))
示例#20
0
 def genhash(cls, secret, hash):
     if secret is None:
         raise TypeError("no secret provided")
     if isinstance(secret, unicode):
         secret = secret.encode("utf-8")
     if hash is not None and not cls.identify(hash):
         raise ValueError("invalid hash")
     return hashlib.sha1(b("xyz") + secret).hexdigest()
示例#21
0
class atlassian_pbkdf2_sha1(uh.HasRawSalt, uh.HasRawChecksum,
                            uh.GenericHandler):
    """This class implements the PBKDF2 hash used by Atlassian.

    It supports a fixed-length salt, and a fixed number of rounds.

    The :meth:`~passlib.ifc.PasswordHash.encrypt` and :meth:`~passlib.ifc.PasswordHash.genconfig` methods accept the following optional keyword:

    :type salt: bytes
    :param salt:
        Optional salt bytes.
        If specified, the length must be exactly 16 bytes.
        If not specified, a salt will be autogenerated (this is recommended).

    :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
    """
    #--GenericHandler--
    name = "atlassian_pbkdf2_sha1"
    setting_kwds = ("salt", )
    ident = u("{PKCS5S2}")
    checksum_size = 32

    _stub_checksum = b("\x00") * 32

    #--HasRawSalt--
    min_salt_size = max_salt_size = 16

    @classmethod
    def from_string(cls, hash):
        hash = to_unicode(hash, "ascii", "hash")
        ident = cls.ident
        if not hash.startswith(ident):
            raise uh.exc.InvalidHashError(cls)
        data = b64decode(hash[len(ident):].encode("ascii"))
        salt, chk = data[:16], data[16:]
        return cls(salt=salt, checksum=chk)

    def to_string(self):
        data = self.salt + (self.checksum or self._stub_checksum)
        hash = self.ident + b64encode(data).decode("ascii")
        return uascii_to_str(hash)

    def _calc_checksum(self, secret):
        # TODO: find out what crowd's policy is re: unicode
        if isinstance(secret, unicode):
            secret = secret.encode("utf-8")
        # crowd seems to use a fixed number of rounds.
        return pbkdf2(secret, self.salt, 10000, 32, "hmac-sha1")
示例#22
0
    def test_10_to_string(self):
        """test to_string()"""

        # check sample
        ht = apache.HtdigestFile.from_string(self.sample_01)
        self.assertEqual(ht.to_string(), self.sample_01)

        # check blank
        ht = apache.HtdigestFile()
        self.assertEqual(ht.to_string(), b(""))
示例#23
0
    def test_09_to_string(self):
        """test to_string"""

        # check with known sample
        ht = apache.HtpasswdFile.from_string(self.sample_01)
        self.assertEqual(ht.to_string(), self.sample_01)

        # test blank
        ht = apache.HtpasswdFile()
        self.assertEqual(ht.to_string(), b(""))
示例#24
0
    def test_07_realms(self):
        """test realms() & delete_realm()"""
        ht = apache.HtdigestFile.from_string(self.sample_01)

        self.assertEqual(ht.delete_realm("x"), 0)
        self.assertEqual(ht.realms(), ['realm'])

        self.assertEqual(ht.delete_realm("realm"), 4)
        self.assertEqual(ht.realms(), [])
        self.assertEqual(ht.to_string(), b(""))
示例#25
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)
示例#26
0
    def test_to_unicode(self):
        """test to_unicode()"""
        from lib.passlib.utils import to_unicode

        # check unicode inputs
        self.assertEqual(to_unicode(u('abc')), u('abc'))
        self.assertEqual(to_unicode(u('\x00\xff')), u('\x00\xff'))

        # check unicode input ignores encoding
        self.assertEqual(to_unicode(u('\x00\xff'), "ascii"), u('\x00\xff'))

        # check bytes input
        self.assertEqual(to_unicode(b('abc')), u('abc'))
        self.assertEqual(to_unicode(b('\x00\xc3\xbf')), u('\x00\xff'))
        self.assertEqual(to_unicode(b('\x00\xff'), 'latin-1'), u('\x00\xff'))
        self.assertRaises(ValueError, to_unicode, b('\x00\xff'))

        # check other
        self.assertRaises(AssertionError, to_unicode, 'abc', None)
        self.assertRaises(TypeError, to_unicode, None)
示例#27
0
def raw_lmhash(secret, encoding="ascii", hex=False):
    """encode password using des-based LMHASH algorithm; returns string of raw bytes, or unicode hex"""
    # NOTE: various references say LMHASH uses the OEM codepage of the host
    #       for its encoding. until a clear reference is found,
    #       as well as a path for getting the encoding,
    #       letting this default to "ascii" to prevent incorrect hashes
    #       from being made w/o user explicitly choosing an encoding.
    if isinstance(secret, unicode):
        secret = secret.encode(encoding)
    ns = secret.upper()[:14] + b("\x00") * (14-len(secret))
    out = des_encrypt_block(ns[:7], LM_MAGIC) + des_encrypt_block(ns[7:], LM_MAGIC)
    return hexlify(out).decode("ascii") if hex else out
示例#28
0
    def test_05_load(self):
        """test load()"""
        # setup empty file
        path = self.mktemp()
        set_file(path, "")
        backdate_file_mtime(path, 5)
        ha = apache.HtdigestFile(path)
        self.assertEqual(ha.to_string(), b(""))

        # make changes, check load_if_changed() does nothing
        ha.set_password("user1", "realm", "pass1")
        ha.load_if_changed()
        self.assertEqual(ha.to_string(), b('user1:realm:2a6cf53e7d8f8cf39d946dc880b14128\n'))

        # change file
        set_file(path, self.sample_01)
        ha.load_if_changed()
        self.assertEqual(ha.to_string(), self.sample_01)

        # make changes, check load_if_changed overwrites them
        ha.set_password("user5", "realm", "pass5")
        ha.load()
        self.assertEqual(ha.to_string(), self.sample_01)

        # test load w/ no path
        hb = apache.HtdigestFile()
        self.assertRaises(RuntimeError, hb.load)
        self.assertRaises(RuntimeError, hb.load_if_changed)

        # test load w/ explicit path
        hc = apache.HtdigestFile()
        hc.load(path)
        self.assertEqual(hc.to_string(), self.sample_01)

        # change file, test deprecated force=False kwd
        ensure_mtime_changed(path)
        set_file(path, "")
        with self.assertWarningList(r"load\(force=False\) is deprecated"):
            ha.load(force=False)
        self.assertEqual(ha.to_string(), b(""))
示例#29
0
def des_cbc_encrypt(key, value, iv=b('\x00') * 8, pad=b('\x00')):
    """performs des-cbc encryption, returns only last block.

    this performs a specific DES-CBC encryption implementation
    as needed by the Oracle10 hash. it probably won't be useful for
    other purposes as-is.

    input value is null-padded to multiple of 8 bytes.

    :arg key: des key as bytes
    :arg value: value to encrypt, as bytes.
    :param iv: optional IV
    :param pad: optional pad byte

    :returns: last block of DES-CBC encryption of all ``value``'s byte blocks.
    """
    value += pad * (-len(value) % 8)  # null pad to multiple of 8
    hash = iv  # start things off
    for offset in irange(0, len(value), 8):
        chunk = xor_bytes(hash, value[offset:offset + 8])
        hash = des_encrypt_block(key, chunk)
    return hash
示例#30
0
def raw_lmhash(secret, encoding="ascii", hex=False):
    """encode password using des-based LMHASH algorithm; returns string of raw bytes, or unicode hex"""
    # NOTE: various references say LMHASH uses the OEM codepage of the host
    #       for its encoding. until a clear reference is found,
    #       as well as a path for getting the encoding,
    #       letting this default to "ascii" to prevent incorrect hashes
    #       from being made w/o user explicitly choosing an encoding.
    if isinstance(secret, unicode):
        secret = secret.encode(encoding)
    ns = secret.upper()[:14] + b("\x00") * (14 - len(secret))
    out = des_encrypt_block(ns[:7], LM_MAGIC) + des_encrypt_block(
        ns[7:], LM_MAGIC)
    return hexlify(out).decode("ascii") if hex else out
示例#31
0
    def test_getrandstr(self):
        """test getrandstr()"""
        from lib.passlib.utils import getrandstr, rng

        def f(*a, **k):
            return getrandstr(rng, *a, **k)

        # count 0
        self.assertEqual(f('abc', 0), '')

        # count <0
        self.assertRaises(ValueError, f, 'abc', -1)

        # letters 0
        self.assertRaises(ValueError, f, '', 0)

        # letters 1
        self.assertEqual(f('a', 5), 'aaaaa')

        # letters
        x = f(u('abc'), 16)
        y = f(u('abc'), 16)
        self.assertIsInstance(x, unicode)
        self.assertNotEqual(x, y)
        self.assertEqual(sorted(set(x)), [u('a'), u('b'), u('c')])

        # bytes
        x = f(b('abc'), 16)
        y = f(b('abc'), 16)
        self.assertIsInstance(x, bytes)
        self.assertNotEqual(x, y)
        # NOTE: decoding this due to py3 bytes
        self.assertEqual(sorted(set(x.decode("ascii"))),
                         [u('a'), u('b'), u('c')])

        # generate_password
        from lib.passlib.utils import generate_password
        self.assertEqual(len(generate_password(15)), 15)
示例#32
0
    def digest(self):
        # NOTE: backing up state so we can restore it after _process is called,
        #       in case object is updated again (this is only attr altered by this method)
        orig = list(self._state)

        # final block: buf + 0x80,
        # then 0x00 padding until congruent w/ 56 mod 64 bytes
        # then last 8 bytes = msg length in bits
        buf = self._buf
        msglen = self._count*512 + len(buf)*8
        block = buf + b('\x80') + b('\x00') * ((119-len(buf)) % 64) + \
            struct.pack("<2I", msglen & MASK_32, (msglen>>32) & MASK_32)
        if len(block) == 128:
            self._process(block[:64])
            self._process(block[64:])
        else:
            assert len(block) == 64
            self._process(block)

        # render digest & restore un-finalized state
        out = struct.pack("<4I", *self._state)
        self._state = orig
        return out
示例#33
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)
    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')
示例#35
0
import logging; log = logging.getLogger(__name__)
from warnings import warn
# site
# pkg
from lib.passlib.utils import classproperty, h64, safe_crypt, test_crypt
from lib.passlib.utils.compat import b, bytes, u, uascii_to_str, unicode
from lib.passlib.utils.pbkdf2 import get_prf
import lib.passlib.utils.handlers as uh
# local
__all__ = [
]
#=============================================================================
# sha1-crypt
#=============================================================================
_hmac_sha1 = get_prf("hmac-sha1")[0]
_BNULL = b('\x00')

class sha1_crypt(uh.HasManyBackends, uh.HasRounds, uh.HasSalt, uh.GenericHandler):
    """This class implements the SHA1-Crypt 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: str
    :param salt:
        Optional salt string.
        If not specified, an 8 character one will be autogenerated (this is recommended).
        If specified, it must be 0-64 characters, drawn from the regexp range ``[./0-9A-Za-z]``.

    :type salt_size: int
示例#36
0
            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
        used to indicate a disabled account.
示例#37
0
from warnings import warn
# site
# pkg
from lib.passlib.utils import classproperty, h64, safe_crypt, test_crypt, repeat_string
from lib.passlib.utils.compat import b, bytes, irange, unicode, u
import lib.passlib.utils.handlers as uh
# local
__all__ = [
    "md5_crypt",
    "apr_md5_crypt",
]

#=============================================================================
# pure-python backend
#=============================================================================
_BNULL = b("\x00")
_MD5_MAGIC = b("$1$")
_APR_MAGIC = b("$apr1$")

# pre-calculated offsets used to speed up C digest stage (see notes below).
# sequence generated using the following:
    ##perms_order = "p,pp,ps,psp,sp,spp".split(",")
    ##def offset(i):
    ##    key = (("p" if i % 2 else "") + ("s" if i % 3 else "") +
    ##        ("p" if i % 7 else "") + ("" if i % 2 else "p"))
    ##    return perms_order.index(key)
    ##_c_digest_offsets = [(offset(i), offset(i+1)) for i in range(0,42,2)]
_c_digest_offsets = (
    (0, 3), (5, 1), (5, 3), (1, 2), (5, 1), (5, 3), (1, 3),
    (4, 1), (5, 3), (1, 3), (5, 0), (5, 3), (1, 3), (5, 1),
    (4, 3), (1, 3), (5, 1), (5, 2), (1, 3), (5, 1), (5, 3),
示例#38
0
from lib.passlib.hash import htdigest
from lib.passlib.utils import consteq, render_bytes, to_bytes, deprecated_method, is_ascii_codec
from lib.passlib.utils.compat import b, bytes, join_bytes, str_to_bascii, u, \
                                 unicode, BytesIO, iteritems, imap, PY3
# local
__all__ = [
    'HtpasswdFile',
    'HtdigestFile',
]

#=============================================================================
# constants & support
#=============================================================================
_UNSET = object()

_BCOLON = b(":")

# byte values that aren't allowed in fields.
_INVALID_FIELD_CHARS = b(":\n\r\t\x00")

#=============================================================================
# backport of OrderedDict for PY2.5
#=============================================================================
try:
    from collections import OrderedDict
except ImportError:
    # Python 2.5
    class OrderedDict(dict):
        """hacked OrderedDict replacement.

        NOTE: this doesn't provide a full OrderedDict implementation,
示例#39
0
# 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):
    """This class implements Cryptacular's PBKDF2-based crypt algorithm, 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, it may be any length.
        If not specified, a one will be autogenerated (this is recommended).

    :type salt_size: int
示例#40
0
MAGIC_HAMLET = b(
    "To be, or not to be,--that is the question:--\n"
    "Whether 'tis nobler in the mind to suffer\n"
    "The slings and arrows of outrageous fortune\n"
    "Or to take arms against a sea of troubles,\n"
    "And by opposing end them?--To die,--to sleep,--\n"
    "No more; and by a sleep to say we end\n"
    "The heartache, and the thousand natural shocks\n"
    "That flesh is heir to,--'tis a consummation\n"
    "Devoutly to be wish'd. To die,--to sleep;--\n"
    "To sleep! perchance to dream:--ay, there's the rub;\n"
    "For in that sleep of death what dreams may come,\n"
    "When we have shuffled off this mortal coil,\n"
    "Must give us pause: there's the respect\n"
    "That makes calamity of so long life;\n"
    "For who would bear the whips and scorns of time,\n"
    "The oppressor's wrong, the proud man's contumely,\n"
    "The pangs of despis'd love, the law's delay,\n"
    "The insolence of office, and the spurns\n"
    "That patient merit of the unworthy takes,\n"
    "When he himself might his quietus make\n"
    "With a bare bodkin? who would these fardels bear,\n"
    "To grunt and sweat under a weary life,\n"
    "But that the dread of something after death,--\n"
    "The undiscover'd country, from whose bourn\n"
    "No traveller returns,--puzzles the will,\n"
    "And makes us rather bear those ills we have\n"
    "Than fly to others that we know not of?\n"
    "Thus conscience does make cowards of us all;\n"
    "And thus the native hue of resolution\n"
    "Is sicklied o'er with the pale cast of thought;\n"
    "And enterprises of great pith and moment,\n"
    "With this regard, their currents turn awry,\n"
    "And lose the name of action.--Soft you now!\n"
    "The fair Ophelia!--Nymph, in thy orisons\n"
    "Be all my sins remember'd.\n\x00" #<- apparently null at end of C string is included (test vector won't pass otherwise)
)
 def _calc_checksum(self, secret):
     if isinstance(secret, unicode):
         secret = secret.encode("utf-8")
     data = b("boblious") + secret
     return str_to_uascii(hashlib.sha1(data).hexdigest())
示例#42
0
 def _stub_checksum(self):
     return b('\x00') * self.checksum_size
示例#43
0
from binascii import hexlify
# site
# pkg
from lib.passlib.utils.compat import b, unicode
from lib.passlib.utils.des import des_encrypt_block
from lib.passlib.hash import nthash
# local
__all__ = [
    "nthash",
    "raw_lmhash",
    "raw_nthash",
]
#=============================================================================
# helpers
#=============================================================================
LM_MAGIC = b("KGS!@#$%")

raw_nthash = nthash.raw_nthash

def raw_lmhash(secret, encoding="ascii", hex=False):
    """encode password using des-based LMHASH algorithm; returns string of raw bytes, or unicode hex"""
    # NOTE: various references say LMHASH uses the OEM codepage of the host
    #       for its encoding. until a clear reference is found,
    #       as well as a path for getting the encoding,
    #       letting this default to "ascii" to prevent incorrect hashes
    #       from being made w/o user explicitly choosing an encoding.
    if isinstance(secret, unicode):
        secret = secret.encode(encoding)
    ns = secret.upper()[:14] + b("\x00") * (14-len(secret))
    out = des_encrypt_block(ns[:7], LM_MAGIC) + des_encrypt_block(ns[7:], LM_MAGIC)
    return hexlify(out).decode("ascii") if hex else out
示例#44
0
import lib.passlib.utils.handlers as uh
# local
__all__ = [
    "mssql2000",
    "mssql2005",
]

#=============================================================================
# mssql 2000
#=============================================================================
def _raw_mssql(secret, salt):
    assert isinstance(secret, unicode)
    assert isinstance(salt, bytes)
    return sha1(secret.encode("utf-16-le") + salt).digest()

BIDENT = b("0x0100")
##BIDENT2 = b("\x01\x00")
UIDENT = u("0x0100")

def _ident_mssql(hash, csize, bsize):
    """common identify for mssql 2000/2005"""
    if isinstance(hash, unicode):
        if len(hash) == csize and hash.startswith(UIDENT):
            return True
    elif isinstance(hash, bytes):
        if len(hash) == csize and hash.startswith(BIDENT):
            return True
        ##elif len(hash) == bsize and hash.startswith(BIDENT2): # raw bytes
        ##    return True
    else:
        raise uh.exc.ExpectedStringError(hash, "hash")
示例#45
0
 def md4(content=None):
     """wrapper for hashlib.new('md4')"""
     return hashlib.new('md4', content or b(''))
示例#46
0
 def __init__(self, content=None):
     self._count = 0
     self._state = [0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476]
     self._buf = b('')
     if content:
         self.update(content)
示例#47
0
        result = check_table(name)
        if result:
            return result

    # else we've done what we can
    warn("norm_hash_name(): unknown hash: %r" % (orig,), PasslibRuntimeWarning)
    name2 = name.replace("-", "")
    row = _nhn_cache[orig] = (name2, name)
    return row[idx]

# TODO: get_hash() func which wraps norm_hash_name(), hashlib.<attr>, and hashlib.new

#=============================================================================
# general prf lookup
#=============================================================================
_BNULL = b('\x00')
_XY_DIGEST = b(',\x1cb\xe0H\xa5\x82M\xfb>\xd6\x98\xef\x8e\xf9oQ\x85\xa3i')

_trans_5C = join_byte_values((x ^ 0x5C) for x in irange(256))
_trans_36 = join_byte_values((x ^ 0x36) for x in irange(256))

def _get_hmac_prf(digest):
    """helper to return HMAC prf for specific digest"""
    def tag_wrapper(prf):
        prf.__name__ = "hmac_" + digest
        prf.__doc__ = ("hmac_%s(key, msg) -> digest;"
                       " generated by passlib.utils.pbkdf2.get_prf()" %
                       digest)

    if _EVP and digest == "sha1":
        # use m2crypto function directly for sha1, since that's its default digest