Use the stdlib's PBKDF2 implementation when available.

This is a bit faster than ours, which is good, because it lets you increase
the iteration counts.

This will be used on Python 3.4+, and, pending the acceptance of PEP466, on
newer Python 2.7s.
This commit is contained in:
Alex Gaynor 2014-04-17 11:02:42 -07:00
parent 47927eb786
commit cb68eb3e6d
2 changed files with 63 additions and 45 deletions

View File

@ -15,9 +15,10 @@ class ExpressionNode(tree.Node):
MUL = '*'
DIV = '/'
POW = '^'
MOD = '%%' # This is a quoted % operator - it is quoted
# because it can be used in strings that also
# have parameter substitution.
# This is a quoted % operator - it is quoted
# because it can be used in strings that also
# have parameter substitution.
MOD = '%%'
# Bitwise operators - note that these are generated by .bitand()
# and .bitor(), the '&' and '|' are reserved for boolean operator

View File

@ -117,51 +117,68 @@ def _long_to_bin(x, hex_format_string):
return binascii.unhexlify((hex_format_string % x).encode('ascii'))
def pbkdf2(password, salt, iterations, dklen=0, digest=None):
"""
Implements PBKDF2 as defined in RFC 2898, section 5.2
if hasattr(hashlib, "pbkdf2_hmac"):
def pbkdf2(password, salt, iterations, dklen=0, digest=None):
"""
Implements PBDF2 with the same API as Django's existing implementation,
using the stdlib.
HMAC+SHA256 is used as the default pseudo random function.
This is used in Python 3.4 and up.
"""
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
As of 2011, 10,000 iterations was the recommended default which
took 100ms on a 2.2Ghz Core 2 Duo. 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 only
four times slower than openssl's implementation. Look in
django.contrib.auth.hashers for the present default.
"""
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
HMAC+SHA256 is used as the default pseudo random function.
hex_format_string = "%%0%ix" % (hlen * 2)
As of 2011, 10,000 iterations was the recommended default which
took 100ms on a 2.2Ghz Core 2 Duo. 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 only
four times slower than openssl's implementation. Look in
django.contrib.auth.hashers for the present default.
"""
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
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))
hex_format_string = "%%0%ix" % (hlen * 2)
def F(i):
u = salt + struct.pack(b'>I', i)
result = 0
for j in xrange(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)
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))
T = [F(x) for x in range(1, l)]
return b''.join(T) + F(l)[:r]
def F(i):
u = salt + struct.pack(b'>I', i)
result = 0
for j in xrange(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]