summaryrefslogtreecommitdiff
path: root/lib/python2.7/site-packages/django/utils/crypto.py
diff options
context:
space:
mode:
Diffstat (limited to 'lib/python2.7/site-packages/django/utils/crypto.py')
-rw-r--r--lib/python2.7/site-packages/django/utils/crypto.py166
1 files changed, 166 insertions, 0 deletions
diff --git a/lib/python2.7/site-packages/django/utils/crypto.py b/lib/python2.7/site-packages/django/utils/crypto.py
new file mode 100644
index 0000000..6463709
--- /dev/null
+++ b/lib/python2.7/site-packages/django/utils/crypto.py
@@ -0,0 +1,166 @@
+"""
+Django's standard crypto functions and utilities.
+"""
+from __future__ import unicode_literals
+
+import hmac
+import struct
+import hashlib
+import binascii
+import operator
+import time
+from functools import reduce
+
+# Use the system PRNG if possible
+import random
+try:
+ random = random.SystemRandom()
+ using_sysrandom = True
+except NotImplementedError:
+ import warnings
+ warnings.warn('A secure pseudo-random number generator is not available '
+ 'on your system. Falling back to Mersenne Twister.')
+ using_sysrandom = False
+
+from django.conf import settings
+from django.utils.encoding import force_bytes
+from django.utils import six
+from django.utils.six.moves import xrange
+
+
+def salted_hmac(key_salt, value, secret=None):
+ """
+ Returns the HMAC-SHA1 of 'value', using a key generated from key_salt and a
+ secret (which defaults to settings.SECRET_KEY).
+
+ A different key_salt should be passed in for every application of HMAC.
+ """
+ if secret is None:
+ secret = settings.SECRET_KEY
+
+ # We need to generate a derived key from our base key. We can do this by
+ # passing the key_salt and our base key through a pseudo-random function and
+ # SHA1 works nicely.
+ key = hashlib.sha1((key_salt + secret).encode('utf-8')).digest()
+
+ # If len(key_salt + secret) > sha_constructor().block_size, the above
+ # line is redundant and could be replaced by key = key_salt + secret, since
+ # the hmac module does the same thing for keys longer than the block size.
+ # However, we need to ensure that we *always* do this.
+ return hmac.new(key, msg=force_bytes(value), digestmod=hashlib.sha1)
+
+
+def get_random_string(length=12,
+ allowed_chars='abcdefghijklmnopqrstuvwxyz'
+ 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'):
+ """
+ Returns a securely generated random string.
+
+ The default length of 12 with the a-z, A-Z, 0-9 character set returns
+ a 71-bit value. log_2((26+26+10)^12) =~ 71 bits
+ """
+ if not using_sysrandom:
+ # This is ugly, and a hack, but it makes things better than
+ # the alternative of predictability. This re-seeds the PRNG
+ # using a value that is hard for an attacker to predict, every
+ # time a random string is required. This may change the
+ # properties of the chosen random sequence slightly, but this
+ # is better than absolute predictability.
+ random.seed(
+ hashlib.sha256(
+ ("%s%s%s" % (
+ random.getstate(),
+ time.time(),
+ settings.SECRET_KEY)).encode('utf-8')
+ ).digest())
+ return ''.join([random.choice(allowed_chars) for i in range(length)])
+
+
+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 six.PY3 and 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):
+ """
+ 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'))
+
+
+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 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)
+
+ 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):
+ def U():
+ u = salt + struct.pack(b'>I', i)
+ for j in xrange(int(iterations)):
+ dig1, dig2 = inner.copy(), outer.copy()
+ dig1.update(u)
+ dig2.update(dig1.digest())
+ u = dig2.digest()
+ yield _bin_to_long(u)
+ return _long_to_bin(reduce(operator.xor, U()), hex_format_string)
+
+ T = [F(x) for x in range(1, l + 1)]
+ return b''.join(T[:-1]) + T[-1][:r]