summaryrefslogtreecommitdiff
path: root/django/utils/crypto.py
diff options
context:
space:
mode:
authorAlex Gaynor <alex.gaynor@gmail.com>2014-04-17 11:02:42 -0700
committerAlex Gaynor <alex.gaynor@gmail.com>2014-04-17 11:02:42 -0700
commitcb68eb3e6dd740185734ffcbc4e287860fcc118d (patch)
tree2179fe3c5847137add64bbcb6ca1276ab7117201 /django/utils/crypto.py
parent47927eb786f432cb069f0b00fd810c465a78fd71 (diff)
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.
Diffstat (limited to 'django/utils/crypto.py')
-rw-r--r--django/utils/crypto.py101
1 files changed, 59 insertions, 42 deletions
diff --git a/django/utils/crypto.py b/django/utils/crypto.py
index 21395fe8a3..c70f9bbdeb 100644
--- a/django/utils/crypto.py
+++ b/django/utils/crypto.py
@@ -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.
+
+ 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
- hex_format_string = "%%0%ix" % (hlen * 2)
+ 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))
+ 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 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)
+ 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]
+ T = [F(x) for x in range(1, l)]
+ return b''.join(T) + F(l)[:r]