Skip to content

Commit

Permalink
Use constant time string comparisons for auth.
Browse files Browse the repository at this point in the history
Fix bug 942644.

Use constant time string comparisons when doing authentication to help
guard against timing attacks.

Change-Id: I5fa5c8f07e57201e129903f71b3dea19071cac5e
  • Loading branch information
russellb committed Feb 29, 2012
1 parent 95413dc commit 1c24191
Show file tree
Hide file tree
Showing 5 changed files with 30 additions and 3 deletions.
1 change: 1 addition & 0 deletions AUTHORS
Expand Up @@ -76,6 +76,7 @@ Ramana Juvvadi <rrjuvvadi@gmail.com>
Robin Norwood <robin.norwood@gmail.com>
root <root@bsirish.(none)>
root <root@newapps.(none)>
Russell Bryant <rbryant@redhat.com>
saikrishna1511@gmail.com <psaikrishna@ubudesk1004.(none)>
Sai Krishna <saikrishna1511@gmail.com>
Salvatore Orlando <salvatore.orlando@eu.citrix.com>
Expand Down
20 changes: 20 additions & 0 deletions keystone/common/utils.py
Expand Up @@ -241,3 +241,23 @@ def unixtime(dt_obj):
"""
return time.mktime(dt_obj.utctimetuple())


def auth_str_equal(s1, s2):
"""Constant-time string comparison.
:params s1: the first string
:params s2: the second string
:return: True if the strings are equal.
This function takes two strings and compares them. It is intended to be
used when doing a comparison for authentication purposes to help guard
against timing attacks.
"""
if len(s1) != len(s2):
return False
result = 0
for (a, b) in zip(s1, s2):
result |= ord(a) ^ ord(b)
return result == 0
4 changes: 2 additions & 2 deletions keystone/contrib/ec2/core.py
Expand Up @@ -105,15 +105,15 @@ def __init__(self):
def check_signature(self, creds_ref, credentials):
signer = utils.Ec2Signer(creds_ref['secret'])
signature = signer.generate(credentials)
if signature == credentials['signature']:
if utils.auth_str_equal(signature, credentials['signature']):
return
# NOTE(vish): Some libraries don't use the port when signing
# requests, so try again without port.
elif ':' in credentials['signature']:
hostname, _port = credentials['host'].split(':')
credentials['host'] = hostname
signature = signer.generate(credentials)
if signature != credentials.signature:
if not utils.auth_str_equal(signature, credentials.signature):
# TODO(termie): proper exception
msg = 'Invalid signature'
raise webob.exc.HTTPUnauthorized(explanation=msg)
Expand Down
3 changes: 2 additions & 1 deletion keystone/contrib/s3/core.py
Expand Up @@ -25,6 +25,7 @@
from hashlib import sha1

from keystone import config
from keystone.common import utils
from keystone.common import wsgi
from keystone.contrib import ec2

Expand All @@ -47,5 +48,5 @@ def check_signature(self, creds_ref, credentials):
key = str(creds_ref['secret'])
signed = base64.encodestring(hmac.new(key, msg, sha1).digest()).strip()

if credentials['signature'] != signed:
if not utils.auth_str_equal(credentials['signature'], signed):
raise Exception('Not Authorized')
5 changes: 5 additions & 0 deletions tests/test_utils.py
Expand Up @@ -61,3 +61,8 @@ def test_isotime(self):
output = utils.isotime(dt)
expected = '1987-10-13T01:02:03Z'
self.assertEqual(output, expected)

def test_auth_str_equal(self):
self.assertTrue(utils.auth_str_equal('abc123', 'abc123'))
self.assertFalse(utils.auth_str_equal('a', 'aaaaa'))
self.assertFalse(utils.auth_str_equal('ABC123', 'abc123'))

0 comments on commit 1c24191

Please sign in to comment.