You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

136 line
4.7 KiB

  1. """passlib.handlers.phpass - PHPass Portable Crypt
  2. phppass located - http://www.openwall.com/phpass/
  3. algorithm described - http://www.openwall.com/articles/PHP-Users-Passwords
  4. phpass context - blowfish, bsdi_crypt, phpass
  5. """
  6. #=============================================================================
  7. # imports
  8. #=============================================================================
  9. # core
  10. from hashlib import md5
  11. import logging; log = logging.getLogger(__name__)
  12. # site
  13. # pkg
  14. from passlib.utils.binary import h64
  15. from passlib.utils.compat import u, uascii_to_str, unicode
  16. import passlib.utils.handlers as uh
  17. # local
  18. __all__ = [
  19. "phpass",
  20. ]
  21. #=============================================================================
  22. # phpass
  23. #=============================================================================
  24. class phpass(uh.HasManyIdents, uh.HasRounds, uh.HasSalt, uh.GenericHandler):
  25. """This class implements the PHPass Portable Hash, and follows the :ref:`password-hash-api`.
  26. It supports a fixed-length salt, and a variable number of rounds.
  27. The :meth:`~passlib.ifc.PasswordHash.using` method accepts the following optional keywords:
  28. :type salt: str
  29. :param salt:
  30. Optional salt string.
  31. If not specified, one will be autogenerated (this is recommended).
  32. If specified, it must be 8 characters, drawn from the regexp range ``[./0-9A-Za-z]``.
  33. :type rounds: int
  34. :param rounds:
  35. Optional number of rounds to use.
  36. Defaults to 19, must be between 7 and 30, inclusive.
  37. This value is logarithmic, the actual number of iterations used will be :samp:`2**{rounds}`.
  38. :type ident: str
  39. :param ident:
  40. phpBB3 uses ``H`` instead of ``P`` for its identifier,
  41. this may be set to ``H`` in order to generate phpBB3 compatible hashes.
  42. it defaults to ``P``.
  43. :type relaxed: bool
  44. :param relaxed:
  45. By default, providing an invalid value for one of the other
  46. keywords will result in a :exc:`ValueError`. If ``relaxed=True``,
  47. and the error can be corrected, a :exc:`~passlib.exc.PasslibHashWarning`
  48. will be issued instead. Correctable errors include ``rounds``
  49. that are too small or too large, and ``salt`` strings that are too long.
  50. .. versionadded:: 1.6
  51. """
  52. #===================================================================
  53. # class attrs
  54. #===================================================================
  55. #--GenericHandler--
  56. name = "phpass"
  57. setting_kwds = ("salt", "rounds", "ident")
  58. checksum_chars = uh.HASH64_CHARS
  59. #--HasSalt--
  60. min_salt_size = max_salt_size = 8
  61. salt_chars = uh.HASH64_CHARS
  62. #--HasRounds--
  63. default_rounds = 19
  64. min_rounds = 7
  65. max_rounds = 30
  66. rounds_cost = "log2"
  67. #--HasManyIdents--
  68. default_ident = u("$P$")
  69. ident_values = (u("$P$"), u("$H$"))
  70. ident_aliases = {u("P"):u("$P$"), u("H"):u("$H$")}
  71. #===================================================================
  72. # formatting
  73. #===================================================================
  74. #$P$9IQRaTwmfeRo7ud9Fh4E2PdI0S3r.L0
  75. # $P$
  76. # 9
  77. # IQRaTwmf
  78. # eRo7ud9Fh4E2PdI0S3r.L0
  79. @classmethod
  80. def from_string(cls, hash):
  81. ident, data = cls._parse_ident(hash)
  82. rounds, salt, chk = data[0], data[1:9], data[9:]
  83. return cls(
  84. ident=ident,
  85. rounds=h64.decode_int6(rounds.encode("ascii")),
  86. salt=salt,
  87. checksum=chk or None,
  88. )
  89. def to_string(self):
  90. hash = u("%s%s%s%s") % (self.ident,
  91. h64.encode_int6(self.rounds).decode("ascii"),
  92. self.salt,
  93. self.checksum or u(''))
  94. return uascii_to_str(hash)
  95. #===================================================================
  96. # backend
  97. #===================================================================
  98. def _calc_checksum(self, secret):
  99. # FIXME: can't find definitive policy on how phpass handles non-ascii.
  100. if isinstance(secret, unicode):
  101. secret = secret.encode("utf-8")
  102. real_rounds = 1<<self.rounds
  103. result = md5(self.salt.encode("ascii") + secret).digest()
  104. r = 0
  105. while r < real_rounds:
  106. result = md5(result + secret).digest()
  107. r += 1
  108. return h64.encode_bytes(result).decode("ascii")
  109. #===================================================================
  110. # eoc
  111. #===================================================================
  112. #=============================================================================
  113. # eof
  114. #=============================================================================