Refs #23919 -- Removed obsolete compare_digest() and pbkdf2() implementations.
This commit is contained in:
parent
9695b14982
commit
d4bb37593e
|
@ -1,11 +1,9 @@
|
|||
"""
|
||||
Django's standard crypto functions and utilities.
|
||||
"""
|
||||
import binascii
|
||||
import hashlib
|
||||
import hmac
|
||||
import random
|
||||
import struct
|
||||
import time
|
||||
|
||||
from django.conf import settings
|
||||
|
@ -73,115 +71,17 @@ def get_random_string(length=12,
|
|||
return ''.join(random.choice(allowed_chars) for i in range(length))
|
||||
|
||||
|
||||
if hasattr(hmac, "compare_digest"):
|
||||
# Prefer the stdlib implementation, when available.
|
||||
def constant_time_compare(val1, val2):
|
||||
return hmac.compare_digest(force_bytes(val1), force_bytes(val2))
|
||||
else:
|
||||
def constant_time_compare(val1, val2):
|
||||
"""
|
||||
Returns True if the two strings are equal, False otherwise.
|
||||
|
||||
The time taken is independent of the number of characters that match.
|
||||
|
||||
For the sake of simplicity, this function executes in constant time only
|
||||
when the two strings have the same length. It short-circuits when they
|
||||
have different lengths. Since Django only uses it to compare hashes of
|
||||
known expected length, this is acceptable.
|
||||
"""
|
||||
if len(val1) != len(val2):
|
||||
return False
|
||||
result = 0
|
||||
if isinstance(val1, bytes) and isinstance(val2, bytes):
|
||||
for x, y in zip(val1, val2):
|
||||
result |= x ^ y
|
||||
else:
|
||||
for x, y in zip(val1, val2):
|
||||
result |= ord(x) ^ ord(y)
|
||||
return result == 0
|
||||
def constant_time_compare(val1, val2):
|
||||
"""Return True if the two strings are equal, False otherwise."""
|
||||
return hmac.compare_digest(force_bytes(val1), force_bytes(val2))
|
||||
|
||||
|
||||
def _bin_to_long(x):
|
||||
"""
|
||||
Convert a binary string into a long integer
|
||||
|
||||
This is a clever optimization for fast xor vector math
|
||||
"""
|
||||
return int(binascii.hexlify(x), 16)
|
||||
|
||||
|
||||
def _long_to_bin(x, hex_format_string):
|
||||
"""
|
||||
Convert a long integer into a binary string.
|
||||
hex_format_string is like "%020x" for padding 10 characters.
|
||||
"""
|
||||
return binascii.unhexlify((hex_format_string % x).encode('ascii'))
|
||||
|
||||
|
||||
if hasattr(hashlib, "pbkdf2_hmac"):
|
||||
def pbkdf2(password, salt, iterations, dklen=0, digest=None):
|
||||
"""
|
||||
Implements PBKDF2 with the same API as Django's existing
|
||||
implementation, using the stdlib.
|
||||
|
||||
This is used in Python 2.7.8+ and 3.4+.
|
||||
"""
|
||||
if digest is None:
|
||||
digest = hashlib.sha256
|
||||
if not dklen:
|
||||
dklen = None
|
||||
password = force_bytes(password)
|
||||
salt = force_bytes(salt)
|
||||
return hashlib.pbkdf2_hmac(
|
||||
digest().name, password, salt, iterations, dklen)
|
||||
else:
|
||||
def pbkdf2(password, salt, iterations, dklen=0, digest=None):
|
||||
"""
|
||||
Implements PBKDF2 as defined in RFC 2898, section 5.2
|
||||
|
||||
HMAC+SHA256 is used as the default pseudo random function.
|
||||
|
||||
As of 2014, 100,000 iterations was the recommended default which took
|
||||
100ms on a 2.7Ghz Intel i7 with an optimized implementation. This is
|
||||
probably the bare minimum for security given 1000 iterations was
|
||||
recommended in 2001. This code is very well optimized for CPython and
|
||||
is about five times slower than OpenSSL's implementation. Look in
|
||||
django.contrib.auth.hashers for the present default, it is lower than
|
||||
the recommended 100,000 because of the performance difference between
|
||||
this and an optimized implementation.
|
||||
"""
|
||||
assert iterations > 0
|
||||
if not digest:
|
||||
digest = hashlib.sha256
|
||||
password = force_bytes(password)
|
||||
salt = force_bytes(salt)
|
||||
hlen = digest().digest_size
|
||||
if not dklen:
|
||||
dklen = hlen
|
||||
if dklen > (2 ** 32 - 1) * hlen:
|
||||
raise OverflowError('dklen too big')
|
||||
L = -(-dklen // hlen)
|
||||
r = dklen - (L - 1) * hlen
|
||||
|
||||
hex_format_string = "%%0%ix" % (hlen * 2)
|
||||
|
||||
inner, outer = digest(), digest()
|
||||
if len(password) > inner.block_size:
|
||||
password = digest(password).digest()
|
||||
password += b'\x00' * (inner.block_size - len(password))
|
||||
inner.update(password.translate(hmac.trans_36))
|
||||
outer.update(password.translate(hmac.trans_5C))
|
||||
|
||||
def F(i):
|
||||
u = salt + struct.pack(b'>I', i)
|
||||
result = 0
|
||||
for j in range(int(iterations)):
|
||||
dig1, dig2 = inner.copy(), outer.copy()
|
||||
dig1.update(u)
|
||||
dig2.update(dig1.digest())
|
||||
u = dig2.digest()
|
||||
result ^= _bin_to_long(u)
|
||||
return _long_to_bin(result, hex_format_string)
|
||||
|
||||
T = [F(x) for x in range(1, L)]
|
||||
return b''.join(T) + F(L)[:r]
|
||||
def pbkdf2(password, salt, iterations, dklen=0, digest=None):
|
||||
"""Return the hash of password using pbkdf2."""
|
||||
if digest is None:
|
||||
digest = hashlib.sha256
|
||||
if not dklen:
|
||||
dklen = None
|
||||
password = force_bytes(password)
|
||||
salt = force_bytes(salt)
|
||||
return hashlib.pbkdf2_hmac(digest().name, password, salt, iterations, dklen)
|
||||
|
|
Loading…
Reference in New Issue