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.
|
Django's standard crypto functions and utilities.
|
||||||
"""
|
"""
|
||||||
import binascii
|
|
||||||
import hashlib
|
import hashlib
|
||||||
import hmac
|
import hmac
|
||||||
import random
|
import random
|
||||||
import struct
|
|
||||||
import time
|
import time
|
||||||
|
|
||||||
from django.conf import settings
|
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))
|
return ''.join(random.choice(allowed_chars) for i in range(length))
|
||||||
|
|
||||||
|
|
||||||
if hasattr(hmac, "compare_digest"):
|
def constant_time_compare(val1, val2):
|
||||||
# Prefer the stdlib implementation, when available.
|
"""Return True if the two strings are equal, False otherwise."""
|
||||||
def constant_time_compare(val1, val2):
|
|
||||||
return hmac.compare_digest(force_bytes(val1), force_bytes(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 _bin_to_long(x):
|
def pbkdf2(password, salt, iterations, dklen=0, digest=None):
|
||||||
"""
|
"""Return the hash of password using pbkdf2."""
|
||||||
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:
|
if digest is None:
|
||||||
digest = hashlib.sha256
|
digest = hashlib.sha256
|
||||||
if not dklen:
|
if not dklen:
|
||||||
dklen = None
|
dklen = None
|
||||||
password = force_bytes(password)
|
password = force_bytes(password)
|
||||||
salt = force_bytes(salt)
|
salt = force_bytes(salt)
|
||||||
return hashlib.pbkdf2_hmac(
|
return hashlib.pbkdf2_hmac(digest().name, password, salt, iterations, dklen)
|
||||||
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]
|
|
||||||
|
|
Loading…
Reference in New Issue