Merge pull request #2600 from alex/builtin-constant-time-compare

Use the stdlib's compare_digest for constant time comparisons when available
This commit is contained in:
Donald Stufft 2014-04-22 17:53:08 -04:00
commit 03401701f3
1 changed files with 23 additions and 19 deletions

View File

@ -77,27 +77,31 @@ 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))
def constant_time_compare(val1, val2): if hasattr(hmac, "compare_digest"):
""" # Prefer the stdlib implementation, when available.
Returns True if the two strings are equal, False otherwise. constant_time_compare = hmac.compare_digest
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. The time taken is independent of the number of characters that match.
For the sake of simplicity, this function executes in constant time only 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 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 have different lengths. Since Django only uses it to compare hashes of
known expected length, this is acceptable. known expected length, this is acceptable.
""" """
if len(val1) != len(val2): if len(val1) != len(val2):
return False return False
result = 0 result = 0
if six.PY3 and isinstance(val1, bytes) and isinstance(val2, bytes): if six.PY3 and isinstance(val1, bytes) and isinstance(val2, bytes):
for x, y in zip(val1, val2): for x, y in zip(val1, val2):
result |= x ^ y result |= x ^ y
else: else:
for x, y in zip(val1, val2): for x, y in zip(val1, val2):
result |= ord(x) ^ ord(y) result |= ord(x) ^ ord(y)
return result == 0 return result == 0
def _bin_to_long(x): def _bin_to_long(x):