Nelze vybrat více než 25 témat Téma musí začínat písmenem nebo číslem, může obsahovat pomlčky („-“) a může být dlouhé až 35 znaků.
 
 
 
 

476 řádky
19 KiB

  1. """passlib.handlers.pbkdf - PBKDF2 based hashes"""
  2. #=============================================================================
  3. # imports
  4. #=============================================================================
  5. # core
  6. from binascii import hexlify, unhexlify
  7. from base64 import b64encode, b64decode
  8. import logging; log = logging.getLogger(__name__)
  9. # site
  10. # pkg
  11. from passlib.utils import to_unicode
  12. from passlib.utils.binary import ab64_decode, ab64_encode
  13. from passlib.utils.compat import str_to_bascii, u, uascii_to_str, unicode
  14. from passlib.crypto.digest import pbkdf2_hmac
  15. import passlib.utils.handlers as uh
  16. # local
  17. __all__ = [
  18. "pbkdf2_sha1",
  19. "pbkdf2_sha256",
  20. "pbkdf2_sha512",
  21. "cta_pbkdf2_sha1",
  22. "dlitz_pbkdf2_sha1",
  23. "grub_pbkdf2_sha512",
  24. ]
  25. #=============================================================================
  26. #
  27. #=============================================================================
  28. class Pbkdf2DigestHandler(uh.HasRounds, uh.HasRawSalt, uh.HasRawChecksum, uh.GenericHandler):
  29. """base class for various pbkdf2_{digest} algorithms"""
  30. #===================================================================
  31. # class attrs
  32. #===================================================================
  33. #--GenericHandler--
  34. setting_kwds = ("salt", "salt_size", "rounds")
  35. checksum_chars = uh.HASH64_CHARS
  36. #--HasSalt--
  37. default_salt_size = 16
  38. max_salt_size = 1024
  39. #--HasRounds--
  40. default_rounds = None # set by subclass
  41. min_rounds = 1
  42. max_rounds = 0xffffffff # setting at 32-bit limit for now
  43. rounds_cost = "linear"
  44. #--this class--
  45. _digest = None # name of subclass-specified hash
  46. # NOTE: max_salt_size and max_rounds are arbitrarily chosen to provide sanity check.
  47. # the underlying pbkdf2 specifies no bounds for either.
  48. # NOTE: defaults chosen to be at least as large as pbkdf2 rfc recommends...
  49. # >8 bytes of entropy in salt, >1000 rounds
  50. # increased due to time since rfc established
  51. #===================================================================
  52. # methods
  53. #===================================================================
  54. @classmethod
  55. def from_string(cls, hash):
  56. rounds, salt, chk = uh.parse_mc3(hash, cls.ident, handler=cls)
  57. salt = ab64_decode(salt.encode("ascii"))
  58. if chk:
  59. chk = ab64_decode(chk.encode("ascii"))
  60. return cls(rounds=rounds, salt=salt, checksum=chk)
  61. def to_string(self):
  62. salt = ab64_encode(self.salt).decode("ascii")
  63. chk = ab64_encode(self.checksum).decode("ascii")
  64. return uh.render_mc3(self.ident, self.rounds, salt, chk)
  65. def _calc_checksum(self, secret):
  66. # NOTE: pbkdf2_hmac() will encode secret & salt using UTF8
  67. return pbkdf2_hmac(self._digest, secret, self.salt, self.rounds, self.checksum_size)
  68. def create_pbkdf2_hash(hash_name, digest_size, rounds=12000, ident=None, module=__name__):
  69. """create new Pbkdf2DigestHandler subclass for a specific hash"""
  70. name = 'pbkdf2_' + hash_name
  71. if ident is None:
  72. ident = u("$pbkdf2-%s$") % (hash_name,)
  73. base = Pbkdf2DigestHandler
  74. return type(name, (base,), dict(
  75. __module__=module, # so ABCMeta won't clobber it.
  76. name=name,
  77. ident=ident,
  78. _digest = hash_name,
  79. default_rounds=rounds,
  80. checksum_size=digest_size,
  81. encoded_checksum_size=(digest_size*4+2)//3,
  82. __doc__="""This class implements a generic ``PBKDF2-HMAC-%(digest)s``-based password hash, and follows the :ref:`password-hash-api`.
  83. It supports a variable-length salt, and a variable number of rounds.
  84. The :meth:`~passlib.ifc.PasswordHash.using` method accepts the following optional keywords:
  85. :type salt: bytes
  86. :param salt:
  87. Optional salt bytes.
  88. If specified, the length must be between 0-1024 bytes.
  89. If not specified, a %(dsc)d byte salt will be autogenerated (this is recommended).
  90. :type salt_size: int
  91. :param salt_size:
  92. Optional number of bytes to use when autogenerating new salts.
  93. Defaults to %(dsc)d bytes, but can be any value between 0 and 1024.
  94. :type rounds: int
  95. :param rounds:
  96. Optional number of rounds to use.
  97. Defaults to %(dr)d, but must be within ``range(1,1<<32)``.
  98. :type relaxed: bool
  99. :param relaxed:
  100. By default, providing an invalid value for one of the other
  101. keywords will result in a :exc:`ValueError`. If ``relaxed=True``,
  102. and the error can be corrected, a :exc:`~passlib.exc.PasslibHashWarning`
  103. will be issued instead. Correctable errors include ``rounds``
  104. that are too small or too large, and ``salt`` strings that are too long.
  105. .. versionadded:: 1.6
  106. """ % dict(digest=hash_name.upper(), dsc=base.default_salt_size, dr=rounds)
  107. ))
  108. #------------------------------------------------------------------------
  109. # derived handlers
  110. #------------------------------------------------------------------------
  111. pbkdf2_sha1 = create_pbkdf2_hash("sha1", 20, 131000, ident=u("$pbkdf2$"))
  112. pbkdf2_sha256 = create_pbkdf2_hash("sha256", 32, 29000)
  113. pbkdf2_sha512 = create_pbkdf2_hash("sha512", 64, 25000)
  114. ldap_pbkdf2_sha1 = uh.PrefixWrapper("ldap_pbkdf2_sha1", pbkdf2_sha1, "{PBKDF2}", "$pbkdf2$", ident=True)
  115. ldap_pbkdf2_sha256 = uh.PrefixWrapper("ldap_pbkdf2_sha256", pbkdf2_sha256, "{PBKDF2-SHA256}", "$pbkdf2-sha256$", ident=True)
  116. ldap_pbkdf2_sha512 = uh.PrefixWrapper("ldap_pbkdf2_sha512", pbkdf2_sha512, "{PBKDF2-SHA512}", "$pbkdf2-sha512$", ident=True)
  117. #=============================================================================
  118. # cryptacular's pbkdf2 hash
  119. #=============================================================================
  120. # bytes used by cta hash for base64 values 63 & 64
  121. CTA_ALTCHARS = b"-_"
  122. class cta_pbkdf2_sha1(uh.HasRounds, uh.HasRawSalt, uh.HasRawChecksum, uh.GenericHandler):
  123. """This class implements Cryptacular's PBKDF2-based crypt algorithm, and follows the :ref:`password-hash-api`.
  124. It supports a variable-length salt, and a variable number of rounds.
  125. The :meth:`~passlib.ifc.PasswordHash.using` method accepts the following optional keywords:
  126. :type salt: bytes
  127. :param salt:
  128. Optional salt bytes.
  129. If specified, it may be any length.
  130. If not specified, a one will be autogenerated (this is recommended).
  131. :type salt_size: int
  132. :param salt_size:
  133. Optional number of bytes to use when autogenerating new salts.
  134. Defaults to 16 bytes, but can be any value between 0 and 1024.
  135. :type rounds: int
  136. :param rounds:
  137. Optional number of rounds to use.
  138. Defaults to 60000, must be within ``range(1,1<<32)``.
  139. :type relaxed: bool
  140. :param relaxed:
  141. By default, providing an invalid value for one of the other
  142. keywords will result in a :exc:`ValueError`. If ``relaxed=True``,
  143. and the error can be corrected, a :exc:`~passlib.exc.PasslibHashWarning`
  144. will be issued instead. Correctable errors include ``rounds``
  145. that are too small or too large, and ``salt`` strings that are too long.
  146. .. versionadded:: 1.6
  147. """
  148. #===================================================================
  149. # class attrs
  150. #===================================================================
  151. #--GenericHandler--
  152. name = "cta_pbkdf2_sha1"
  153. setting_kwds = ("salt", "salt_size", "rounds")
  154. ident = u("$p5k2$")
  155. checksum_size = 20
  156. # NOTE: max_salt_size and max_rounds are arbitrarily chosen to provide a
  157. # sanity check. underlying algorithm (and reference implementation)
  158. # allows effectively unbounded values for both of these parameters.
  159. #--HasSalt--
  160. default_salt_size = 16
  161. max_salt_size = 1024
  162. #--HasRounds--
  163. default_rounds = pbkdf2_sha1.default_rounds
  164. min_rounds = 1
  165. max_rounds = 0xffffffff # setting at 32-bit limit for now
  166. rounds_cost = "linear"
  167. #===================================================================
  168. # formatting
  169. #===================================================================
  170. # hash $p5k2$1000$ZxK4ZBJCfQg=$jJZVscWtO--p1-xIZl6jhO2LKR0=
  171. # ident $p5k2$
  172. # rounds 1000
  173. # salt ZxK4ZBJCfQg=
  174. # chk jJZVscWtO--p1-xIZl6jhO2LKR0=
  175. # NOTE: rounds in hex
  176. @classmethod
  177. def from_string(cls, hash):
  178. # NOTE: passlib deviation - forbidding zero-padded rounds
  179. rounds, salt, chk = uh.parse_mc3(hash, cls.ident, rounds_base=16, handler=cls)
  180. salt = b64decode(salt.encode("ascii"), CTA_ALTCHARS)
  181. if chk:
  182. chk = b64decode(chk.encode("ascii"), CTA_ALTCHARS)
  183. return cls(rounds=rounds, salt=salt, checksum=chk)
  184. def to_string(self):
  185. salt = b64encode(self.salt, CTA_ALTCHARS).decode("ascii")
  186. chk = b64encode(self.checksum, CTA_ALTCHARS).decode("ascii")
  187. return uh.render_mc3(self.ident, self.rounds, salt, chk, rounds_base=16)
  188. #===================================================================
  189. # backend
  190. #===================================================================
  191. def _calc_checksum(self, secret):
  192. # NOTE: pbkdf2_hmac() will encode secret & salt using utf-8
  193. return pbkdf2_hmac("sha1", secret, self.salt, self.rounds, 20)
  194. #===================================================================
  195. # eoc
  196. #===================================================================
  197. #=============================================================================
  198. # dlitz's pbkdf2 hash
  199. #=============================================================================
  200. class dlitz_pbkdf2_sha1(uh.HasRounds, uh.HasSalt, uh.GenericHandler):
  201. """This class implements Dwayne Litzenberger's PBKDF2-based crypt algorithm, and follows the :ref:`password-hash-api`.
  202. It supports a variable-length salt, and a variable number of rounds.
  203. The :meth:`~passlib.ifc.PasswordHash.using` method accepts the following optional keywords:
  204. :type salt: str
  205. :param salt:
  206. Optional salt string.
  207. If specified, it may be any length, but must use the characters in the regexp range ``[./0-9A-Za-z]``.
  208. If not specified, a 16 character salt will be autogenerated (this is recommended).
  209. :type salt_size: int
  210. :param salt_size:
  211. Optional number of bytes to use when autogenerating new salts.
  212. Defaults to 16 bytes, but can be any value between 0 and 1024.
  213. :type rounds: int
  214. :param rounds:
  215. Optional number of rounds to use.
  216. Defaults to 60000, must be within ``range(1,1<<32)``.
  217. :type relaxed: bool
  218. :param relaxed:
  219. By default, providing an invalid value for one of the other
  220. keywords will result in a :exc:`ValueError`. If ``relaxed=True``,
  221. and the error can be corrected, a :exc:`~passlib.exc.PasslibHashWarning`
  222. will be issued instead. Correctable errors include ``rounds``
  223. that are too small or too large, and ``salt`` strings that are too long.
  224. .. versionadded:: 1.6
  225. """
  226. #===================================================================
  227. # class attrs
  228. #===================================================================
  229. #--GenericHandler--
  230. name = "dlitz_pbkdf2_sha1"
  231. setting_kwds = ("salt", "salt_size", "rounds")
  232. ident = u("$p5k2$")
  233. _stub_checksum = u("0" * 48 + "=")
  234. # NOTE: max_salt_size and max_rounds are arbitrarily chosen to provide a
  235. # sanity check. underlying algorithm (and reference implementation)
  236. # allows effectively unbounded values for both of these parameters.
  237. #--HasSalt--
  238. default_salt_size = 16
  239. max_salt_size = 1024
  240. salt_chars = uh.HASH64_CHARS
  241. #--HasRounds--
  242. # NOTE: for security, the default here is set to match pbkdf2_sha1,
  243. # even though this hash's extra block makes it twice as slow.
  244. default_rounds = pbkdf2_sha1.default_rounds
  245. min_rounds = 1
  246. max_rounds = 0xffffffff # setting at 32-bit limit for now
  247. rounds_cost = "linear"
  248. #===================================================================
  249. # formatting
  250. #===================================================================
  251. # hash $p5k2$c$u9HvcT4d$Sd1gwSVCLZYAuqZ25piRnbBEoAesaa/g
  252. # ident $p5k2$
  253. # rounds c
  254. # salt u9HvcT4d
  255. # chk Sd1gwSVCLZYAuqZ25piRnbBEoAesaa/g
  256. # rounds in lowercase hex, no zero padding
  257. @classmethod
  258. def from_string(cls, hash):
  259. rounds, salt, chk = uh.parse_mc3(hash, cls.ident, rounds_base=16,
  260. default_rounds=400, handler=cls)
  261. return cls(rounds=rounds, salt=salt, checksum=chk)
  262. def to_string(self):
  263. rounds = self.rounds
  264. if rounds == 400:
  265. rounds = None # omit rounds measurement if == 400
  266. return uh.render_mc3(self.ident, rounds, self.salt, self.checksum, rounds_base=16)
  267. def _get_config(self):
  268. rounds = self.rounds
  269. if rounds == 400:
  270. rounds = None # omit rounds measurement if == 400
  271. return uh.render_mc3(self.ident, rounds, self.salt, None, rounds_base=16)
  272. #===================================================================
  273. # backend
  274. #===================================================================
  275. def _calc_checksum(self, secret):
  276. # NOTE: pbkdf2_hmac() will encode secret & salt using utf-8
  277. salt = self._get_config()
  278. result = pbkdf2_hmac("sha1", secret, salt, self.rounds, 24)
  279. return ab64_encode(result).decode("ascii")
  280. #===================================================================
  281. # eoc
  282. #===================================================================
  283. #=============================================================================
  284. # crowd
  285. #=============================================================================
  286. class atlassian_pbkdf2_sha1(uh.HasRawSalt, uh.HasRawChecksum, uh.GenericHandler):
  287. """This class implements the PBKDF2 hash used by Atlassian.
  288. It supports a fixed-length salt, and a fixed number of rounds.
  289. The :meth:`~passlib.ifc.PasswordHash.using` method accepts the following optional keywords:
  290. :type salt: bytes
  291. :param salt:
  292. Optional salt bytes.
  293. If specified, the length must be exactly 16 bytes.
  294. If not specified, a salt will be autogenerated (this is recommended).
  295. :type relaxed: bool
  296. :param relaxed:
  297. By default, providing an invalid value for one of the other
  298. keywords will result in a :exc:`ValueError`. If ``relaxed=True``,
  299. and the error can be corrected, a :exc:`~passlib.exc.PasslibHashWarning`
  300. will be issued instead. Correctable errors include
  301. ``salt`` strings that are too long.
  302. .. versionadded:: 1.6
  303. """
  304. #--GenericHandler--
  305. name = "atlassian_pbkdf2_sha1"
  306. setting_kwds =("salt",)
  307. ident = u("{PKCS5S2}")
  308. checksum_size = 32
  309. #--HasRawSalt--
  310. min_salt_size = max_salt_size = 16
  311. @classmethod
  312. def from_string(cls, hash):
  313. hash = to_unicode(hash, "ascii", "hash")
  314. ident = cls.ident
  315. if not hash.startswith(ident):
  316. raise uh.exc.InvalidHashError(cls)
  317. data = b64decode(hash[len(ident):].encode("ascii"))
  318. salt, chk = data[:16], data[16:]
  319. return cls(salt=salt, checksum=chk)
  320. def to_string(self):
  321. data = self.salt + self.checksum
  322. hash = self.ident + b64encode(data).decode("ascii")
  323. return uascii_to_str(hash)
  324. def _calc_checksum(self, secret):
  325. # TODO: find out what crowd's policy is re: unicode
  326. # crowd seems to use a fixed number of rounds.
  327. # NOTE: pbkdf2_hmac() will encode secret & salt using utf-8
  328. return pbkdf2_hmac("sha1", secret, self.salt, 10000, 32)
  329. #=============================================================================
  330. # grub
  331. #=============================================================================
  332. class grub_pbkdf2_sha512(uh.HasRounds, uh.HasRawSalt, uh.HasRawChecksum, uh.GenericHandler):
  333. """This class implements Grub's pbkdf2-hmac-sha512 hash, and follows the :ref:`password-hash-api`.
  334. It supports a variable-length salt, and a variable number of rounds.
  335. The :meth:`~passlib.ifc.PasswordHash.using` method accepts the following optional keywords:
  336. :type salt: bytes
  337. :param salt:
  338. Optional salt bytes.
  339. If specified, the length must be between 0-1024 bytes.
  340. If not specified, a 64 byte salt will be autogenerated (this is recommended).
  341. :type salt_size: int
  342. :param salt_size:
  343. Optional number of bytes to use when autogenerating new salts.
  344. Defaults to 64 bytes, but can be any value between 0 and 1024.
  345. :type rounds: int
  346. :param rounds:
  347. Optional number of rounds to use.
  348. Defaults to 19000, but must be within ``range(1,1<<32)``.
  349. :type relaxed: bool
  350. :param relaxed:
  351. By default, providing an invalid value for one of the other
  352. keywords will result in a :exc:`ValueError`. If ``relaxed=True``,
  353. and the error can be corrected, a :exc:`~passlib.exc.PasslibHashWarning`
  354. will be issued instead. Correctable errors include ``rounds``
  355. that are too small or too large, and ``salt`` strings that are too long.
  356. .. versionadded:: 1.6
  357. """
  358. name = "grub_pbkdf2_sha512"
  359. setting_kwds = ("salt", "salt_size", "rounds")
  360. ident = u("grub.pbkdf2.sha512.")
  361. checksum_size = 64
  362. # NOTE: max_salt_size and max_rounds are arbitrarily chosen to provide a
  363. # sanity check. the underlying pbkdf2 specifies no bounds for either,
  364. # and it's not clear what grub specifies.
  365. default_salt_size = 64
  366. max_salt_size = 1024
  367. default_rounds = pbkdf2_sha512.default_rounds
  368. min_rounds = 1
  369. max_rounds = 0xffffffff # setting at 32-bit limit for now
  370. rounds_cost = "linear"
  371. @classmethod
  372. def from_string(cls, hash):
  373. rounds, salt, chk = uh.parse_mc3(hash, cls.ident, sep=u("."),
  374. handler=cls)
  375. salt = unhexlify(salt.encode("ascii"))
  376. if chk:
  377. chk = unhexlify(chk.encode("ascii"))
  378. return cls(rounds=rounds, salt=salt, checksum=chk)
  379. def to_string(self):
  380. salt = hexlify(self.salt).decode("ascii").upper()
  381. chk = hexlify(self.checksum).decode("ascii").upper()
  382. return uh.render_mc3(self.ident, self.rounds, salt, chk, sep=u("."))
  383. def _calc_checksum(self, secret):
  384. # TODO: find out what grub's policy is re: unicode
  385. # NOTE: pbkdf2_hmac() will encode secret & salt using utf-8
  386. return pbkdf2_hmac("sha512", secret, self.salt, self.rounds, 64)
  387. #=============================================================================
  388. # eof
  389. #=============================================================================