Exemple #1
0
    def test_to_native_str(self):
        "test to_native_str()"
        from 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')
Exemple #2
0
    def test_to_native_str(self):
        "test to_native_str()"
        from 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')
Exemple #3
0
 def genhash(cls, secret, config, marker=None):
     uh.validate_secret(secret)
     if config is not None and not cls.identify(config): # handles typecheck
         raise uh.exc.InvalidHashError(cls)
     if config:
         # we want to preserve the existing str,
         # since it might contain a disabled password hash ("!" + hash)
         return to_native_str(config, param="config")
     # if None or empty string, replace with marker
     if marker:
         if not cls.identify(marker):
             raise ValueError("invalid marker: %r" % marker)
     else:
         marker = cls.default_marker
         assert marker and cls.identify(marker)
     return to_native_str(marker, param="marker")
Exemple #4
0
 def verify(cls, secret, hash, encoding=None):
     if not encoding:
         encoding = cls.default_encoding
     hash = to_native_str(hash, encoding, "hash")
     if not cls.identify(hash):
         raise uh.exc.InvalidHashError(cls)
     return consteq(cls.encrypt(secret, encoding), hash)
Exemple #5
0
 def hash(cls, secret, **kwds):
     if kwds:
         uh.warn_hash_settings_deprecation(cls, kwds)
         return cls.using(**kwds).hash(secret)
     uh.validate_secret(secret)
     marker = cls.default_marker
     assert marker and cls.identify(marker)
     return to_native_str(marker, param="marker")
Exemple #6
0
 def hash(cls, secret, **kwds):
     if kwds:
         uh.warn_hash_settings_deprecation(cls, kwds)
         return cls.using(**kwds).hash(secret)
     uh.validate_secret(secret)
     marker = cls.default_marker
     assert marker and cls.identify(marker)
     return to_native_str(marker, param="marker")
 def _norm_hash(cls, hash):
     "normalize hash to native string, and validate it"
     hash = to_native_str(hash, param="hash")
     if len(hash) != 32:
         raise uh.exc.MalformedHashError(cls, "wrong size")
     for char in hash:
         if char not in uh.LC_HEX_CHARS:
             raise uh.exc.MalformedHashError(cls, "invalid chars in hash")
     return hash
Exemple #8
0
 def _norm_hash(cls, hash):
     """normalize hash to native string, and validate it"""
     hash = to_native_str(hash, param="hash")
     if len(hash) != 32:
         raise uh.exc.MalformedHashError(cls, "wrong size")
     for char in hash:
         if char not in uh.LC_HEX_CHARS:
             raise uh.exc.MalformedHashError(cls, "invalid chars in hash")
     return hash
Exemple #9
0
 def check_pybcrypt(secret, hash):
     "pybcrypt"
     secret = to_native_str(secret, self.fuzz_password_encoding)
     if hash.startswith(IDENT_2Y):
         hash = IDENT_2A + hash[4:]
     try:
         return bcrypt.hashpw(secret, hash) == hash
     except ValueError:
         raise ValueError("py-bcrypt rejected hash: %r" % (hash,))
 def check_pybcrypt(secret, hash):
     "pybcrypt"
     secret = to_native_str(secret, self.fuzz_password_encoding)
     if hash.startswith(IDENT_2Y):
         hash = IDENT_2A + hash[4:]
     try:
         return bcrypt.hashpw(secret, hash) == hash
     except ValueError:
         raise ValueError("py-bcrypt rejected hash: %r" % (hash, ))
Exemple #11
0
 def disable(cls, hash=None):
     out = cls.hash("")
     if hash is not None:
         hash = to_native_str(hash, param="hash")
         if cls.identify(hash):
             # extract original hash, so that we normalize marker
             hash = cls.enable(hash)
         if hash:
             out += hash
     return out
Exemple #12
0
 def disable(cls, hash=None):
     out = cls.hash("")
     if hash is not None:
         hash = to_native_str(hash, param="hash")
         if cls.identify(hash):
             # extract original hash, so that we normalize marker
             hash = cls.enable(hash)
         if hash:
             out += hash
     return out
Exemple #13
0
 def enable(cls, hash):
     hash = to_native_str(hash, param="hash")
     for prefix in cls._disable_prefixes:
         if hash.startswith(prefix):
             orig = hash[len(prefix):]
             if orig:
                 return orig
             else:
                 raise ValueError("cannot restore original hash")
     raise uh.exc.InvalidHashError(cls)
Exemple #14
0
 def enable(cls, hash):
     hash = to_native_str(hash, param="hash")
     for prefix in cls._disable_prefixes:
         if hash.startswith(prefix):
             orig = hash[len(prefix):]
             if orig:
                 return orig
             else:
                 raise ValueError("cannot restore original hash")
     raise uh.exc.InvalidHashError(cls)
 def check_pybcrypt(secret, hash):
     """pybcrypt"""
     secret = to_native_str(secret, self.fuzz_password_encoding)
     if len(secret) > 200:  # vulnerable to wraparound bug
         secret = secret[:200]
     if hash.startswith((IDENT_2B, IDENT_2Y)):
         hash = IDENT_2A + hash[4:]
     try:
         return bcrypt.hashpw(secret, hash) == hash
     except ValueError:
         raise ValueError("py-bcrypt rejected hash: %r" % (hash,))
Exemple #16
0
 def genhash(cls, secret, config, marker=None):
     if not cls.identify(config):
         raise uh.exc.InvalidHashError(cls)
     elif config:
         # preserve the existing str,since it might contain a disabled password hash ("!" + hash)
         uh.validate_secret(secret)
         return to_native_str(config, param="config")
     else:
         if marker is not None:
             cls = cls.using(marker=marker)
         return cls.hash(secret)
Exemple #17
0
 def genhash(cls, secret, config, marker=None):
     if not cls.identify(config):
         raise uh.exc.InvalidHashError(cls)
     elif config:
         # preserve the existing str,since it might contain a disabled password hash ("!" + hash)
         uh.validate_secret(secret)
         return to_native_str(config, param="config")
     else:
         if marker is not None:
             cls = cls.using(marker=marker)
         return cls.hash(secret)
 def check_bcryptor(secret, hash):
     "bcryptor"
     secret = to_native_str(secret, self.fuzz_password_encoding)
     if hash.startswith(IDENT_2Y):
         hash = IDENT_2A + hash[4:]
     elif hash.startswith(IDENT_2):
         # bcryptor doesn't support $2$ hashes; but we can fake it
         # using the $2a$ algorithm, by repeating the password until
         # it's 72 chars in length.
         hash = IDENT_2A + hash[3:]
         if secret:
             secret = repeat_string(secret, 72)
     return Engine(False).hash_key(secret, hash) == hash
 def check_bcryptor(secret, hash):
     """bcryptor"""
     secret = to_native_str(secret, self.FuzzHashGenerator.password_encoding)
     if hash.startswith((IDENT_2B, IDENT_2Y)):
         hash = IDENT_2A + hash[4:]
     elif hash.startswith(IDENT_2):
         # bcryptor doesn't support $2$ hashes; but we can fake it
         # using the $2a$ algorithm, by repeating the password until
         # it's 72 chars in length.
         hash = IDENT_2A + hash[3:]
         if secret:
             secret = repeat_string(secret, 72)
     return Engine(False).hash_key(secret, hash) == hash
Exemple #20
0
 def check_pybcrypt(secret, hash):
     """pybcrypt"""
     secret = to_native_str(secret,
                            self.FuzzHashGenerator.password_encoding)
     if len(secret) > 200:  # vulnerable to wraparound bug
         secret = secret[:200]
     if hash.startswith((IDENT_2B, IDENT_2Y)):
         hash = IDENT_2A + hash[4:]
     try:
         if lock:
             with lock:
                 return bcrypt_mod.hashpw(secret, hash) == hash
         else:
             return bcrypt_mod.hashpw(secret, hash) == hash
     except ValueError:
         raise ValueError("py-bcrypt rejected hash: %r" % (hash, ))
Exemple #21
0
    def from_string(cls, hash):
        hash = to_native_str(hash, "ascii", "hash")
        if not hash.startswith("$scram$"):
            raise uh.exc.InvalidHashError(cls)
        parts = hash[7:].split("$")
        if len(parts) != 3:
            raise uh.exc.MalformedHashError(cls)
        rounds_str, salt_str, chk_str = parts

        # decode rounds
        rounds = int(rounds_str)
        if rounds_str != str(rounds): # forbid zero padding, etc.
            raise uh.exc.MalformedHashError(cls)

        # decode salt
        try:
            salt = ab64_decode(salt_str.encode("ascii"))
        except TypeError:
            raise uh.exc.MalformedHashError(cls)

        # decode algs/digest list
        if not chk_str:
            # scram hashes MUST have something here.
            raise uh.exc.MalformedHashError(cls)
        elif "=" in chk_str:
            # comma-separated list of 'alg=digest' pairs
            algs = None
            chkmap = {}
            for pair in chk_str.split(","):
                alg, digest = pair.split("=")
                try:
                    chkmap[alg] = ab64_decode(digest.encode("ascii"))
                except TypeError:
                    raise uh.exc.MalformedHashError(cls)
        else:
            # comma-separated list of alg names, no digests
            algs = chk_str
            chkmap = None

        # return new object
        return cls(
            rounds=rounds,
            salt=salt,
            checksum=chkmap,
            algs=algs,
        )
Exemple #22
0
    def from_string(cls, hash):
        hash = to_native_str(hash, "ascii", "hash")
        if not hash.startswith("$scram$"):
            raise uh.exc.InvalidHashError(cls)
        parts = hash[7:].split("$")
        if len(parts) != 3:
            raise uh.exc.MalformedHashError(cls)
        rounds_str, salt_str, chk_str = parts

        # decode rounds
        rounds = int(rounds_str)
        if rounds_str != str(rounds):  # forbid zero padding, etc.
            raise uh.exc.MalformedHashError(cls)

        # decode salt
        try:
            salt = ab64_decode(salt_str.encode("ascii"))
        except TypeError:
            raise uh.exc.MalformedHashError(cls)

        # decode algs/digest list
        if not chk_str:
            # scram hashes MUST have something here.
            raise uh.exc.MalformedHashError(cls)
        elif "=" in chk_str:
            # comma-separated list of 'alg=digest' pairs
            algs = None
            chkmap = {}
            for pair in chk_str.split(","):
                alg, digest = pair.split("=")
                try:
                    chkmap[alg] = ab64_decode(digest.encode("ascii"))
                except TypeError:
                    raise uh.exc.MalformedHashError(cls)
        else:
            # comma-separated list of alg names, no digests
            algs = chk_str
            chkmap = None

        # return new object
        return cls(
            rounds=rounds,
            salt=salt,
            checksum=chkmap,
            algs=algs,
        )
Exemple #23
0
    def test_to_native_str(self):
        "test to_native_str()"

        self.assertEqual(to_native_str(u'abc'), 'abc')
        self.assertEqual(to_native_str(b('abc')), 'abc')
        self.assertRaises(TypeError, to_native_str, None)

        # Py2k #
        self.assertEqual(to_native_str(u'\x00\xff'), b('\x00\xc3\xbf'))
        self.assertEqual(to_native_str(b('\x00\xc3\xbf')), b('\x00\xc3\xbf'))
        self.assertEqual(to_native_str(u'\x00\xff', 'latin-1'), b('\x00\xff'))
        self.assertEqual(to_native_str(b('\x00\xff'), 'latin-1'),
                         b('\x00\xff'))
    def test_to_native_str(self):
        "test to_native_str()"

        self.assertEqual(to_native_str(u'abc'),             'abc')
        self.assertEqual(to_native_str(b('abc')),           'abc')
        self.assertRaises(TypeError, to_native_str, None)

        # Py2k #
        self.assertEqual(to_native_str(u'\x00\xff'),        b('\x00\xc3\xbf'))
        self.assertEqual(to_native_str(b('\x00\xc3\xbf')),  b('\x00\xc3\xbf'))
        self.assertEqual(to_native_str(u'\x00\xff', 'latin-1'),
                                                            b('\x00\xff'))
        self.assertEqual(to_native_str(b('\x00\xff'), 'latin-1'),
                                                            b('\x00\xff'))
 def test_md4_digest(self):
     "test md4 digest()"
     md4 = self.hash
     for input, hex in self.vectors:
         out = md4(input).digest()
         self.assertEqual(to_native_str(hexlify(out)), hex)
Exemple #26
0
def _get_hash_aliases(name):
    """
    internal helper used by :func:`lookup_hash` --
    normalize arbitrary hash name to hashlib format.
    if name not recognized, returns dummy record and issues a warning.

    :arg name:
        unnormalized name

    :returns:
        tuple with 2+ elements: ``(hashlib_name, iana_name|None, ... 0+ aliases)``.
    """

    # normalize input
    orig = name
    if not isinstance(name, str):
        name = to_native_str(name, 'utf-8', 'hash name')
    name = re.sub("[_ /]", "-", name.strip().lower())
    if name.startswith("scram-"): # helper for SCRAM protocol (see passlib.handlers.scram)
        name = name[6:]
        if name.endswith("-plus"):
            name = name[:-5]

    # look through standard names and known aliases
    def check_table(name):
        for row in _known_hash_names:
            if name in row:
                return row
    result = check_table(name)
    if result:
        return result

    # try to clean name up some more
    m = re.match(r"(?i)^(?P<name>[a-z]+)-?(?P<rev>\d)?-?(?P<size>\d{3,4})?$", name)
    if m:
        # roughly follows "SHA2-256" style format, normalize representation,
        # and checked table.
        iana_name, rev, size = m.group("name", "rev", "size")
        if rev:
            iana_name += rev
        hashlib_name = iana_name
        if size:
            iana_name += "-" + size
            if rev:
                hashlib_name += "_"
            hashlib_name += size
        result = check_table(iana_name)
        if result:
            return result

        # not found in table, but roughly recognize format. use names we built up as fallback.
        log.info("normalizing unrecognized hash name %r => %r / %r",
                 orig, hashlib_name, iana_name)

    else:
        # just can't make sense of it. return something
        iana_name = name
        hashlib_name = name.replace("-", "_")
        log.warning("normalizing unrecognized hash name and format %r => %r / %r",
                    orig, hashlib_name, iana_name)

    return hashlib_name, iana_name
Exemple #27
0
 def encrypt(cls, secret, encoding=None):
     uh.validate_secret(secret)
     if not encoding:
         encoding = cls.default_encoding
     return to_native_str(secret, encoding, "secret")
Exemple #28
0
 def hexdigest(self):
     return to_native_str(hexlify(self.digest()), "latin-1")
Exemple #29
0
def _get_hash_aliases(name):
    """
    internal helper used by :func:`lookup_hash` --
    normalize arbitrary hash name to hashlib format.
    if name not recognized, returns dummy record and issues a warning.

    :arg name:
        unnormalized name

    :returns:
        tuple with 2+ elements: ``(hashlib_name, iana_name|None, ... 0+ aliases)``.
    """

    # normalize input
    orig = name
    if not isinstance(name, str):
        name = to_native_str(name, 'utf-8', 'hash name')
    name = re.sub("[_ /]", "-", name.strip().lower())
    if name.startswith(
            "scram-"
    ):  # helper for SCRAM protocol (see passlib.handlers.scram)
        name = name[6:]
        if name.endswith("-plus"):
            name = name[:-5]

    # look through standard names and known aliases
    def check_table(name):
        for row in _known_hash_names:
            if name in row:
                return row

    result = check_table(name)
    if result:
        return result

    # try to clean name up some more
    m = re.match(r"(?i)^(?P<name>[a-z]+)-?(?P<rev>\d)?-?(?P<size>\d{3,4})?$",
                 name)
    if m:
        # roughly follows "SHA2-256" style format, normalize representation,
        # and checked table.
        iana_name, rev, size = m.group("name", "rev", "size")
        if rev:
            iana_name += rev
        hashlib_name = iana_name
        if size:
            iana_name += "-" + size
            if rev:
                hashlib_name += "_"
            hashlib_name += size
        result = check_table(iana_name)
        if result:
            return result

        # not found in table, but roughly recognize format. use names we built up as fallback.
        log.info("normalizing unrecognized hash name %r => %r / %r", orig,
                 hashlib_name, iana_name)

    else:
        # just can't make sense of it. return something
        iana_name = name
        hashlib_name = name.replace("-", "_")
        log.warning(
            "normalizing unrecognized hash name and format %r => %r / %r",
            orig, hashlib_name, iana_name)

    return hashlib_name, iana_name
Exemple #30
0
 def hexdigest(self):
     return to_native_str(hexlify(self.digest()), "latin-1")
Exemple #31
0
 def test_md4_digest(self):
     "test md4 digest()"
     md4 = self.hash
     for input, hex in self.vectors:
         out = md4(input).digest()
         self.assertEqual(to_native_str(hexlify(out)), hex)
Exemple #32
0
def norm_hash_name(name, format="hashlib"):
    """Normalize hash function name

    :arg name:
        Original hash function name.

        This name can be a Python :mod:`~hashlib` digest name,
        a SCRAM mechanism name, IANA assigned hash name, etc.
        Case is ignored, and underscores are converted to hyphens.

    :param format:
        Naming convention to normalize to.
        Possible values are:

        * ``"hashlib"`` (the default) - normalizes name to be compatible
          with Python's :mod:`!hashlib`.

        * ``"iana"`` - normalizes name to IANA-assigned hash function name.
          for hashes which IANA hasn't assigned a name for, issues a warning,
          and then uses a heuristic to give a "best guess".

    :returns:
        Hash name, returned as native :class:`!str`.
    """
    # check cache
    try:
        idx = _nhn_formats[format]
    except KeyError:
        raise ValueError("unknown format: %r" % (format,))
    try:
        return _nhn_cache[name][idx]
    except KeyError:
        pass
    orig = name

    # normalize input
    if not isinstance(name, str):
        name = to_native_str(name, 'utf-8', 'hash name')
    name = re.sub("[_ /]", "-", name.strip().lower())
    if name.startswith("scram-"):
        name = name[6:]
        if name.endswith("-plus"):
            name = name[:-5]

    # look through standard names and known aliases
    def check_table(name):
        for row in _nhn_hash_names:
            if name in row:
                _nhn_cache[orig] = row
                return row[idx]
    result = check_table(name)
    if result:
        return result

    # try to clean name up, and recheck table
    m = re.match("^(?P<name>[a-z]+)-?(?P<rev>\d)?-?(?P<size>\d{3,4})?$", name)
    if m:
        name, rev, size = m.group("name", "rev", "size")
        if rev:
            name += rev
        if size:
            name += "-" + size
        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]
Exemple #33
0
def norm_hash_name(name, format="hashlib"):
    """Normalize hash function name

    :arg name:
        Original hash function name.

        This name can be a Python :mod:`~hashlib` digest name,
        a SCRAM mechanism name, IANA assigned hash name, etc.
        Case is ignored, and underscores are converted to hyphens.

    :param format:
        Naming convention to normalize to.
        Possible values are:

        * ``"hashlib"`` (the default) - normalizes name to be compatible
          with Python's :mod:`!hashlib`.

        * ``"iana"`` - normalizes name to IANA-assigned hash function name.
          for hashes which IANA hasn't assigned a name for, issues a warning,
          and then uses a heuristic to give a "best guess".

    :returns:
        Hash name, returned as native :class:`!str`.
    """
    # check cache
    try:
        idx = _nhn_formats[format]
    except KeyError:
        raise ValueError("unknown format: %r" % (format,))
    try:
        return _nhn_cache[name][idx]
    except KeyError:
        pass
    orig = name

    # normalize input
    if not isinstance(name, str):
        name = to_native_str(name, 'utf-8', 'hash name')
    name = re.sub("[_ /]", "-", name.strip().lower())
    if name.startswith("scram-"):
        name = name[6:]
        if name.endswith("-plus"):
            name = name[:-5]

    # look through standard names and known aliases
    def check_table(name):
        for row in _nhn_hash_names:
            if name in row:
                _nhn_cache[orig] = row
                return row[idx]
    result = check_table(name)
    if result:
        return result

    # try to clean name up, and recheck table
    m = re.match("^(?P<name>[a-z]+)-?(?P<rev>\d)?-?(?P<size>\d{3,4})?$", name)
    if m:
        name, rev, size = m.group("name", "rev", "size")
        if rev:
            name += rev
        if size:
            name += "-" + size
        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]