__init__.py 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127
  1. # Author:: Donald Stufft (<donald@stufft.io>)
  2. # Copyright:: Copyright (c) 2013 Donald Stufft
  3. # License:: Apache License, Version 2.0
  4. #
  5. # Licensed under the Apache License, Version 2.0 (the "License");
  6. # you may not use this file except in compliance with the License.
  7. # You may obtain a copy of the License at
  8. #
  9. # http://www.apache.org/licenses/LICENSE-2.0
  10. #
  11. # Unless required by applicable law or agreed to in writing, software
  12. # distributed under the License is distributed on an "AS IS" BASIS,
  13. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. # See the License for the specific language governing permissions and
  15. # limitations under the License.
  16. from __future__ import absolute_import
  17. from __future__ import division
  18. import hmac
  19. import os
  20. import warnings
  21. from .__about__ import (
  22. __author__,
  23. __copyright__,
  24. __email__,
  25. __license__,
  26. __summary__,
  27. __title__,
  28. __uri__,
  29. __version__,
  30. )
  31. from . import _bcrypt # noqa: I100
  32. __all__ = [
  33. "__title__",
  34. "__summary__",
  35. "__uri__",
  36. "__version__",
  37. "__author__",
  38. "__email__",
  39. "__license__",
  40. "__copyright__",
  41. "gensalt",
  42. "hashpw",
  43. "kdf",
  44. "checkpw",
  45. ]
  46. def gensalt(rounds: int = 12, prefix: bytes = b"2b") -> bytes:
  47. if prefix not in (b"2a", b"2b"):
  48. raise ValueError("Supported prefixes are b'2a' or b'2b'")
  49. if rounds < 4 or rounds > 31:
  50. raise ValueError("Invalid rounds")
  51. salt = os.urandom(16)
  52. output = _bcrypt.encode_base64(salt)
  53. return (
  54. b"$"
  55. + prefix
  56. + b"$"
  57. + ("%2.2u" % rounds).encode("ascii")
  58. + b"$"
  59. + output
  60. )
  61. def hashpw(password: bytes, salt: bytes) -> bytes:
  62. if isinstance(password, str) or isinstance(salt, str):
  63. raise TypeError("Strings must be encoded before hashing")
  64. # bcrypt originally suffered from a wraparound bug:
  65. # http://www.openwall.com/lists/oss-security/2012/01/02/4
  66. # This bug was corrected in the OpenBSD source by truncating inputs to 72
  67. # bytes on the updated prefix $2b$, but leaving $2a$ unchanged for
  68. # compatibility. However, pyca/bcrypt 2.0.0 *did* correctly truncate inputs
  69. # on $2a$, so we do it here to preserve compatibility with 2.0.0
  70. password = password[:72]
  71. return _bcrypt.hashpass(password, salt)
  72. def checkpw(password: bytes, hashed_password: bytes) -> bool:
  73. if isinstance(password, str) or isinstance(hashed_password, str):
  74. raise TypeError("Strings must be encoded before checking")
  75. ret = hashpw(password, hashed_password)
  76. return hmac.compare_digest(ret, hashed_password)
  77. def kdf(
  78. password: bytes,
  79. salt: bytes,
  80. desired_key_bytes: int,
  81. rounds: int,
  82. ignore_few_rounds: bool = False,
  83. ) -> bytes:
  84. if isinstance(password, str) or isinstance(salt, str):
  85. raise TypeError("Strings must be encoded before hashing")
  86. if len(password) == 0 or len(salt) == 0:
  87. raise ValueError("password and salt must not be empty")
  88. if desired_key_bytes <= 0 or desired_key_bytes > 512:
  89. raise ValueError("desired_key_bytes must be 1-512")
  90. if rounds < 1:
  91. raise ValueError("rounds must be 1 or more")
  92. if rounds < 50 and not ignore_few_rounds:
  93. # They probably think bcrypt.kdf()'s rounds parameter is logarithmic,
  94. # expecting this value to be slow enough (it probably would be if this
  95. # were bcrypt). Emit a warning.
  96. warnings.warn(
  97. (
  98. "Warning: bcrypt.kdf() called with only {0} round(s). "
  99. "This few is not secure: the parameter is linear, like PBKDF2."
  100. ).format(rounds),
  101. UserWarning,
  102. stacklevel=2,
  103. )
  104. return _bcrypt.pbkdf(password, salt, rounds, desired_key_bytes)