diff --git a/nova/api/metadata/handler.py b/nova/api/metadata/handler.py index ba711b210739..29c0443b29e5 100644 --- a/nova/api/metadata/handler.py +++ b/nova/api/metadata/handler.py @@ -30,6 +30,7 @@ from nova import exception from nova.openstack.common.gettextutils import _ from nova.openstack.common import log as logging from nova.openstack.common import memorycache +from nova import utils from nova import wsgi CACHE_EXPIRATION = 15 # in seconds @@ -175,7 +176,7 @@ class MetadataRequestHandler(wsgi.Application): instance_id, hashlib.sha256).hexdigest() - if expected_signature != signature: + if not utils.constant_time_compare(expected_signature, signature): if instance_id: LOG.warn(_('X-Instance-ID-Signature: %(signature)s does not ' 'match the expected value: %(expected_signature)s ' diff --git a/nova/tests/test_utils.py b/nova/tests/test_utils.py index d83c086a4a89..c3e96fd9662a 100644 --- a/nova/tests/test_utils.py +++ b/nova/tests/test_utils.py @@ -962,3 +962,10 @@ class VersionTestCase(test.NoDBTestCase): def test_convert_version_to_tuple(self): self.assertEqual(utils.convert_version_to_tuple('6.7.0'), (6, 7, 0)) + + +class ConstantTimeCompareTestCase(test.NoDBTestCase): + def test_constant_time_compare(self): + self.assertTrue(utils.constant_time_compare("abcd1234", "abcd1234")) + self.assertFalse(utils.constant_time_compare("abcd1234", "a")) + self.assertFalse(utils.constant_time_compare("abcd1234", "ABCD234")) diff --git a/nova/utils.py b/nova/utils.py index bf5a522926aa..a02ba078925f 100644 --- a/nova/utils.py +++ b/nova/utils.py @@ -21,6 +21,7 @@ import contextlib import datetime import functools import hashlib +import hmac import inspect import os import pyclbr @@ -1144,3 +1145,20 @@ def get_image_from_system_metadata(system_meta): def get_hash_str(base_str): """returns string that represents hash of base_str (in hex format).""" return hashlib.md5(base_str).hexdigest() + +if hasattr(hmac, 'compare_digest'): + constant_time_compare = hmac.compare_digest +else: + def constant_time_compare(first, second): + """Returns True if both string inputs are equal, otherwise False. + + This function should take a constant amount of time regardless of + how many characters in the strings match. + + """ + if len(first) != len(second): + return False + result = 0 + for x, y in zip(first, second): + result |= ord(x) ^ ord(y) + return result == 0