123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146 |
- """passlib.handlers.digests - plain hash digests
- """
- #=============================================================================
- # imports
- #=============================================================================
- # core
- import hashlib
- import logging; log = logging.getLogger(__name__)
- # site
- # pkg
- from passlib.utils import to_native_str, to_bytes, render_bytes, consteq
- from passlib.utils.compat import unicode, str_to_uascii
- import passlib.utils.handlers as uh
- from passlib.crypto.digest import lookup_hash
- # local
- __all__ = [
- "create_hex_hash",
- "hex_md4",
- "hex_md5",
- "hex_sha1",
- "hex_sha256",
- "hex_sha512",
- ]
- #=============================================================================
- # helpers for hexadecimal hashes
- #=============================================================================
- class HexDigestHash(uh.StaticHandler):
- """this provides a template for supporting passwords stored as plain hexadecimal hashes"""
- #===================================================================
- # class attrs
- #===================================================================
- _hash_func = None # hash function to use - filled in by create_hex_hash()
- checksum_size = None # filled in by create_hex_hash()
- checksum_chars = uh.HEX_CHARS
- #===================================================================
- # methods
- #===================================================================
- @classmethod
- def _norm_hash(cls, hash):
- return hash.lower()
- def _calc_checksum(self, secret):
- if isinstance(secret, unicode):
- secret = secret.encode("utf-8")
- return str_to_uascii(self._hash_func(secret).hexdigest())
- #===================================================================
- # eoc
- #===================================================================
- def create_hex_hash(digest, module=__name__):
- # NOTE: could set digest_name=hash.name for cpython, but not for some other platforms.
- info = lookup_hash(digest)
- name = "hex_" + info.name
- return type(name, (HexDigestHash,), dict(
- name=name,
- __module__=module, # so ABCMeta won't clobber it
- _hash_func=staticmethod(info.const), # sometimes it's a function, sometimes not. so wrap it.
- checksum_size=info.digest_size*2,
- __doc__="""This class implements a plain hexadecimal %s hash, and follows the :ref:`password-hash-api`.
- It supports no optional or contextual keywords.
- """ % (info.name,)
- ))
- #=============================================================================
- # predefined handlers
- #=============================================================================
- hex_md4 = create_hex_hash("md4")
- hex_md5 = create_hex_hash("md5")
- hex_md5.django_name = "unsalted_md5"
- hex_sha1 = create_hex_hash("sha1")
- hex_sha256 = create_hex_hash("sha256")
- hex_sha512 = create_hex_hash("sha512")
- #=============================================================================
- # htdigest
- #=============================================================================
- class htdigest(uh.MinimalHandler):
- """htdigest hash function.
- .. todo::
- document this hash
- """
- name = "htdigest"
- setting_kwds = ()
- context_kwds = ("user", "realm", "encoding")
- default_encoding = "utf-8"
- @classmethod
- def hash(cls, secret, user, realm, encoding=None):
- # NOTE: this was deliberately written so that raw bytes are passed through
- # unchanged, the encoding kwd is only used to handle unicode values.
- if not encoding:
- encoding = cls.default_encoding
- uh.validate_secret(secret)
- if isinstance(secret, unicode):
- secret = secret.encode(encoding)
- user = to_bytes(user, encoding, "user")
- realm = to_bytes(realm, encoding, "realm")
- data = render_bytes("%s:%s:%s", user, realm, secret)
- return hashlib.md5(data).hexdigest()
- @classmethod
- 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
- @classmethod
- def verify(cls, secret, hash, user, realm, encoding="utf-8"):
- hash = cls._norm_hash(hash)
- other = cls.hash(secret, user, realm, encoding)
- return consteq(hash, other)
- @classmethod
- def identify(cls, hash):
- try:
- cls._norm_hash(hash)
- except ValueError:
- return False
- return True
- @uh.deprecated_method(deprecated="1.7", removed="2.0")
- @classmethod
- def genconfig(cls):
- return cls.hash("", "", "")
- @uh.deprecated_method(deprecated="1.7", removed="2.0")
- @classmethod
- def genhash(cls, secret, config, user, realm, encoding=None):
- # NOTE: 'config' is ignored, as this hash has no salting / other configuration.
- # just have to make sure it's valid.
- cls._norm_hash(config)
- return cls.hash(secret, user, realm, encoding)
- #=============================================================================
- # eof
- #=============================================================================
|