Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.
 
 
 
 

169 строки
6.2 KiB

  1. """passlib.handlers.digests - plain hash digests
  2. """
  3. #=============================================================================
  4. # imports
  5. #=============================================================================
  6. # core
  7. import hashlib
  8. import logging; log = logging.getLogger(__name__)
  9. # site
  10. # pkg
  11. from passlib.utils import to_native_str, to_bytes, render_bytes, consteq
  12. from passlib.utils.compat import unicode, str_to_uascii
  13. import passlib.utils.handlers as uh
  14. from passlib.crypto.digest import lookup_hash
  15. # local
  16. __all__ = [
  17. "create_hex_hash",
  18. "hex_md4",
  19. "hex_md5",
  20. "hex_sha1",
  21. "hex_sha256",
  22. "hex_sha512",
  23. ]
  24. #=============================================================================
  25. # helpers for hexadecimal hashes
  26. #=============================================================================
  27. class HexDigestHash(uh.StaticHandler):
  28. """this provides a template for supporting passwords stored as plain hexadecimal hashes"""
  29. #===================================================================
  30. # class attrs
  31. #===================================================================
  32. _hash_func = None # hash function to use - filled in by create_hex_hash()
  33. checksum_size = None # filled in by create_hex_hash()
  34. checksum_chars = uh.HEX_CHARS
  35. #: special for detecting if _hash_func is just a stub method.
  36. supported = True
  37. #===================================================================
  38. # methods
  39. #===================================================================
  40. @classmethod
  41. def _norm_hash(cls, hash):
  42. return hash.lower()
  43. def _calc_checksum(self, secret):
  44. if isinstance(secret, unicode):
  45. secret = secret.encode("utf-8")
  46. return str_to_uascii(self._hash_func(secret).hexdigest())
  47. #===================================================================
  48. # eoc
  49. #===================================================================
  50. def create_hex_hash(digest, module=__name__, django_name=None, required=True):
  51. """
  52. create hex-encoded unsalted hasher for specified digest algorithm.
  53. .. versionchanged:: 1.7.3
  54. If called with unknown/supported digest, won't throw error immediately,
  55. but instead return a dummy hasher that will throw error when called.
  56. set ``required=True`` to restore old behavior.
  57. """
  58. info = lookup_hash(digest, required=required)
  59. name = "hex_" + info.name
  60. if not info.supported:
  61. info.digest_size = 0
  62. hasher = type(name, (HexDigestHash,), dict(
  63. name=name,
  64. __module__=module, # so ABCMeta won't clobber it
  65. _hash_func=staticmethod(info.const), # sometimes it's a function, sometimes not. so wrap it.
  66. checksum_size=info.digest_size*2,
  67. __doc__="""This class implements a plain hexadecimal %s hash, and follows the :ref:`password-hash-api`.
  68. It supports no optional or contextual keywords.
  69. """ % (info.name,)
  70. ))
  71. if not info.supported:
  72. hasher.supported = False
  73. if django_name:
  74. hasher.django_name = django_name
  75. return hasher
  76. #=============================================================================
  77. # predefined handlers
  78. #=============================================================================
  79. # NOTE: some digests below are marked as "required=False", because these may not be present on
  80. # FIPS systems (see issue 116). if missing, will return stub hasher that throws error
  81. # if an attempt is made to actually use hash/verify with them.
  82. hex_md4 = create_hex_hash("md4", required=False)
  83. hex_md5 = create_hex_hash("md5", django_name="unsalted_md5", required=False)
  84. hex_sha1 = create_hex_hash("sha1", required=False)
  85. hex_sha256 = create_hex_hash("sha256")
  86. hex_sha512 = create_hex_hash("sha512")
  87. #=============================================================================
  88. # htdigest
  89. #=============================================================================
  90. class htdigest(uh.MinimalHandler):
  91. """htdigest hash function.
  92. .. todo::
  93. document this hash
  94. """
  95. name = "htdigest"
  96. setting_kwds = ()
  97. context_kwds = ("user", "realm", "encoding")
  98. default_encoding = "utf-8"
  99. @classmethod
  100. def hash(cls, secret, user, realm, encoding=None):
  101. # NOTE: this was deliberately written so that raw bytes are passed through
  102. # unchanged, the encoding kwd is only used to handle unicode values.
  103. if not encoding:
  104. encoding = cls.default_encoding
  105. uh.validate_secret(secret)
  106. if isinstance(secret, unicode):
  107. secret = secret.encode(encoding)
  108. user = to_bytes(user, encoding, "user")
  109. realm = to_bytes(realm, encoding, "realm")
  110. data = render_bytes("%s:%s:%s", user, realm, secret)
  111. return hashlib.md5(data).hexdigest()
  112. @classmethod
  113. def _norm_hash(cls, hash):
  114. """normalize hash to native string, and validate it"""
  115. hash = to_native_str(hash, param="hash")
  116. if len(hash) != 32:
  117. raise uh.exc.MalformedHashError(cls, "wrong size")
  118. for char in hash:
  119. if char not in uh.LC_HEX_CHARS:
  120. raise uh.exc.MalformedHashError(cls, "invalid chars in hash")
  121. return hash
  122. @classmethod
  123. def verify(cls, secret, hash, user, realm, encoding="utf-8"):
  124. hash = cls._norm_hash(hash)
  125. other = cls.hash(secret, user, realm, encoding)
  126. return consteq(hash, other)
  127. @classmethod
  128. def identify(cls, hash):
  129. try:
  130. cls._norm_hash(hash)
  131. except ValueError:
  132. return False
  133. return True
  134. @uh.deprecated_method(deprecated="1.7", removed="2.0")
  135. @classmethod
  136. def genconfig(cls):
  137. return cls.hash("", "", "")
  138. @uh.deprecated_method(deprecated="1.7", removed="2.0")
  139. @classmethod
  140. def genhash(cls, secret, config, user, realm, encoding=None):
  141. # NOTE: 'config' is ignored, as this hash has no salting / other configuration.
  142. # just have to make sure it's valid.
  143. cls._norm_hash(config)
  144. return cls.hash(secret, user, realm, encoding)
  145. #=============================================================================
  146. # eof
  147. #=============================================================================