Refs #23919 -- Removed obsolete compare_digest() and pbkdf2() implementations.

This commit is contained in:
Tim Graham 2017-01-19 11:55:23 -05:00 committed by GitHub
parent 9695b14982
commit d4bb37593e
1 changed files with 12 additions and 112 deletions

View File

@ -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 if digest is None:
digest = hashlib.sha256
This is a clever optimization for fast xor vector math if not dklen:
""" dklen = None
return int(binascii.hexlify(x), 16) password = force_bytes(password)
salt = force_bytes(salt)
return hashlib.pbkdf2_hmac(digest().name, password, salt, iterations, dklen)
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]