Use compare_digest or an equivalent when available

This avoids the same issue that caused a prior exploit
in nova, we might as well avoid this by doing it correctly
in the first place.

See: https://bugs.launchpad.net/bugs/cve/2014-3517

Change-Id: Iea4b3bcded268de926f32f956383d03fd9f15d59
This commit is contained in:
Joshua Harlow 2014-07-15 12:41:48 -07:00 committed by Joshua Harlow
parent 803be8cc8f
commit 034987f083

View File

@ -21,6 +21,30 @@ import os
import six import six
try:
# Only in python 2.7.7+ (and python 3.3+)
# https://docs.python.org/2/library/hmac.html#hmac.compare_digest
from hmac import compare_digest # noqa
except (AttributeError, ImportError):
# Taken/slightly modified from:
# https://mail.python.org/pipermail/python-checkins/2012-June/114532.html
def compare_digest(a, b):
"""Returns the equivalent of 'a == b', but avoids content based short
circuiting to reduce the vulnerability to timing attacks.
"""
# We assume the length of the expected digest is public knowledge,
# thus this early return isn't leaking anything an attacker wouldn't
# already know
if len(a) != len(b):
return False
# We assume that integers in the bytes range are all cached,
# thus timing shouldn't vary much due to integer object creation
result = 0
for x, y in zip(a, b):
result |= ord(x) ^ ord(y)
return result == 0
def binary_encode(text, encoding='utf-8'): def binary_encode(text, encoding='utf-8'):
"""Converts a string of into a binary type using given encoding. """Converts a string of into a binary type using given encoding.
@ -84,10 +108,11 @@ def signed_unpack(data, hmac_data, hmac_key):
if not (hmac_key and hmac_data): if not (hmac_key and hmac_data):
return None return None
hmac_data = hmac_data.strip()
try: try:
if generate_hmac(data, hmac_key) != hmac_data.strip(): user_hmac_data = generate_hmac(data, hmac_key)
if not compare_digest(hmac_data, user_hmac_data):
return None return None
return json.loads(binary_decode(base64.urlsafe_b64decode(data))) return json.loads(binary_decode(base64.urlsafe_b64decode(data)))
except Exception: except Exception:
return None return None