Kaydet (Commit) cb68eb3e authored tarafından Alex Gaynor's avatar Alex Gaynor

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.
üst 47927eb7
...@@ -15,9 +15,10 @@ class ExpressionNode(tree.Node): ...@@ -15,9 +15,10 @@ class ExpressionNode(tree.Node):
MUL = '*' MUL = '*'
DIV = '/' DIV = '/'
POW = '^' POW = '^'
MOD = '%%' # This is a quoted % operator - it is quoted # This is a quoted % operator - it is quoted
# because it can be used in strings that also # because it can be used in strings that also
# have parameter substitution. # have parameter substitution.
MOD = '%%'
# Bitwise operators - note that these are generated by .bitand() # Bitwise operators - note that these are generated by .bitand()
# and .bitor(), the '&' and '|' are reserved for boolean operator # and .bitor(), the '&' and '|' are reserved for boolean operator
......
...@@ -117,51 +117,68 @@ def _long_to_bin(x, hex_format_string): ...@@ -117,51 +117,68 @@ def _long_to_bin(x, hex_format_string):
return binascii.unhexlify((hex_format_string % x).encode('ascii')) return binascii.unhexlify((hex_format_string % x).encode('ascii'))
def pbkdf2(password, salt, iterations, dklen=0, digest=None): if hasattr(hashlib, "pbkdf2_hmac"):
""" def pbkdf2(password, salt, iterations, dklen=0, digest=None):
Implements PBKDF2 as defined in RFC 2898, section 5.2 """
Implements PBDF2 with the same API as Django's existing implementation,
HMAC+SHA256 is used as the default pseudo random function. using the stdlib.
As of 2011, 10,000 iterations was the recommended default which This is used in Python 3.4 and up.
took 100ms on a 2.2Ghz Core 2 Duo. This is probably the bare """
minimum for security given 1000 iterations was recommended in if digest is None:
2001. This code is very well optimized for CPython and is only digest = hashlib.sha256
four times slower than openssl's implementation. Look in if not dklen:
django.contrib.auth.hashers for the present default. dklen = None
""" password = force_bytes(password)
assert iterations > 0 salt = force_bytes(salt)
if not digest: return hashlib.pbkdf2_hmac(
digest = hashlib.sha256 digest().name, password, salt, iterations, dklen)
password = force_bytes(password) else:
salt = force_bytes(salt) def pbkdf2(password, salt, iterations, dklen=0, digest=None):
hlen = digest().digest_size """
if not dklen: Implements PBKDF2 as defined in RFC 2898, section 5.2
dklen = hlen
if dklen > (2 ** 32 - 1) * hlen: HMAC+SHA256 is used as the default pseudo random function.
raise OverflowError('dklen too big')
l = -(-dklen // hlen) As of 2011, 10,000 iterations was the recommended default which
r = dklen - (l - 1) * hlen took 100ms on a 2.2Ghz Core 2 Duo. This is probably the bare
minimum for security given 1000 iterations was recommended in
hex_format_string = "%%0%ix" % (hlen * 2) 2001. This code is very well optimized for CPython and is only
four times slower than openssl's implementation. Look in
inner, outer = digest(), digest() django.contrib.auth.hashers for the present default.
if len(password) > inner.block_size: """
password = digest(password).digest() assert iterations > 0
password += b'\x00' * (inner.block_size - len(password)) if not digest:
inner.update(password.translate(hmac.trans_36)) digest = hashlib.sha256
outer.update(password.translate(hmac.trans_5C)) password = force_bytes(password)
salt = force_bytes(salt)
def F(i): hlen = digest().digest_size
u = salt + struct.pack(b'>I', i) if not dklen:
result = 0 dklen = hlen
for j in xrange(int(iterations)): if dklen > (2 ** 32 - 1) * hlen:
dig1, dig2 = inner.copy(), outer.copy() raise OverflowError('dklen too big')
dig1.update(u) l = -(-dklen // hlen)
dig2.update(dig1.digest()) r = dklen - (l - 1) * hlen
u = dig2.digest()
result ^= _bin_to_long(u) hex_format_string = "%%0%ix" % (hlen * 2)
return _long_to_bin(result, hex_format_string)
inner, outer = digest(), digest()
T = [F(x) for x in range(1, l)] if len(password) > inner.block_size:
return b''.join(T) + F(l)[:r] 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 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]
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment