crypto.py 3.3 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192
  1. """
  2. Django's standard crypto functions and utilities.
  3. """
  4. import hashlib
  5. import hmac
  6. import secrets
  7. import warnings
  8. from django.conf import settings
  9. from django.utils.deprecation import RemovedInDjango70Warning, django_file_prefixes
  10. from django.utils.encoding import force_bytes
  11. class InvalidAlgorithm(ValueError):
  12. """Algorithm is not supported by hashlib."""
  13. pass
  14. # RemovedInDjango70Warning: algorithm="sha256"
  15. def salted_hmac(key_salt, value, secret=None, *, algorithm=None):
  16. """
  17. Return the HMAC of 'value', using a key generated from key_salt and a
  18. secret (which defaults to settings.SECRET_KEY). Default algorithm is SHA1,
  19. but any algorithm name supported by hashlib can be passed.
  20. Removed in Django70Warning: The default algorithm will change to SHA256
  21. in Django 7.0, so provide an explicit algorithm to silence the warning.
  22. A different key_salt should be passed in for every application of HMAC.
  23. """
  24. if algorithm is None:
  25. warnings.warn(
  26. "The default argument for algorithm in salted_hmac() will change "
  27. "from 'sha1' to 'sha256' in Django 7.0. Pass an explicit "
  28. "algorithm to silence this warning.",
  29. category=RemovedInDjango70Warning,
  30. skip_file_prefixes=django_file_prefixes(),
  31. )
  32. algorithm = "sha1"
  33. if secret is None:
  34. secret = settings.SECRET_KEY
  35. key_salt = force_bytes(key_salt)
  36. secret = force_bytes(secret)
  37. try:
  38. hasher = getattr(hashlib, algorithm)
  39. except AttributeError as e:
  40. raise InvalidAlgorithm(
  41. "%r is not an algorithm accepted by the hashlib module." % algorithm
  42. ) from e
  43. # We need to generate a derived key from our base key. We can do this by
  44. # passing the key_salt and our base key through a pseudo-random function.
  45. key = hasher(key_salt + secret).digest()
  46. # If len(key_salt + secret) > block size of the hash algorithm, the above
  47. # line is redundant and could be replaced by key = key_salt + secret, since
  48. # the hmac module does the same thing for keys longer than the block size.
  49. # However, we need to ensure that we *always* do this.
  50. return hmac.new(key, msg=force_bytes(value), digestmod=hasher)
  51. RANDOM_STRING_CHARS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
  52. def get_random_string(length, allowed_chars=RANDOM_STRING_CHARS):
  53. """
  54. Return a securely generated random string.
  55. The bit length of the returned value can be calculated with the formula:
  56. log_2(len(allowed_chars)^length)
  57. For example, with default `allowed_chars` (26+26+10), this gives:
  58. * length: 12, bit length =~ 71 bits
  59. * length: 22, bit length =~ 131 bits
  60. """
  61. return "".join(secrets.choice(allowed_chars) for i in range(length))
  62. def constant_time_compare(val1, val2):
  63. """Return True if the two strings are equal, False otherwise."""
  64. return secrets.compare_digest(force_bytes(val1), force_bytes(val2))
  65. def pbkdf2(password, salt, iterations, dklen=0, digest=None):
  66. """Return the hash of password using pbkdf2."""
  67. if digest is None:
  68. digest = hashlib.sha256
  69. dklen = dklen or None
  70. password = force_bytes(password)
  71. salt = force_bytes(salt)
  72. return hashlib.pbkdf2_hmac(digest().name, password, salt, iterations, dklen)