Hash functions support different hash algorithms

The token hash functions always used MD5. With this change, the
hash function can be passed in to the hash functions.

SecurityImpact
Related-Bug: #1174499

Change-Id: Ia08c2d6252bb034087a244b47d5bcbea7dcfa70b
This commit is contained in:
Brant Knudson 2014-04-08 19:50:09 -05:00
parent 61322a3fee
commit 82359492dc
4 changed files with 65 additions and 4 deletions

View File

@ -261,7 +261,7 @@ def cms_to_token(cms_text):
return signed_text
def cms_hash_token(token_id):
def cms_hash_token(token_id, mode='md5'):
"""Hash PKI tokens.
return: for ans1_token, returns the hash of the passed in token
@ -270,7 +270,7 @@ def cms_hash_token(token_id):
if token_id is None:
return None
if is_ans1_token(token_id):
hasher = hashlib.md5()
hasher = hashlib.new(mode)
if isinstance(token_id, six.text_type):
token_id = token_id.encode('utf-8')
hasher.update(token_id)

View File

@ -15,6 +15,7 @@ import subprocess
import mock
import testresources
from testtools import matchers
from keystoneclient.common import cms
from keystoneclient import exceptions
@ -111,6 +112,32 @@ class CMSTest(utils.TestCase, testresources.ResourcedTestCase):
self.examples.SIGNING_CERT_FILE,
self.examples.SIGNING_CA_FILE))
def test_cms_hash_token_no_token_id(self):
token_id = None
self.assertThat(cms.cms_hash_token(token_id), matchers.Is(None))
def test_cms_hash_token_not_pki(self):
"""If the token_id is not a PKI token then it returns the token_id."""
token = 'something'
self.assertFalse(cms.is_ans1_token(token))
self.assertThat(cms.cms_hash_token(token), matchers.Is(token))
def test_cms_hash_token_default_md5(self):
"""The default hash method is md5."""
token = self.examples.SIGNED_TOKEN_SCOPED
token_id_default = cms.cms_hash_token(token)
token_id_md5 = cms.cms_hash_token(token, mode='md5')
self.assertThat(token_id_default, matchers.Equals(token_id_md5))
# md5 hash is 32 chars.
self.assertThat(token_id_default, matchers.HasLength(32))
def test_cms_hash_token_sha256(self):
"""Can also hash with sha256."""
token = self.examples.SIGNED_TOKEN_SCOPED
token_id = cms.cms_hash_token(token, mode='sha256')
# sha256 hash is 64 chars.
self.assertThat(token_id, matchers.HasLength(64))
def load_tests(loader, tests, pattern):
return testresources.OptimisingTestSuite(tests)

View File

@ -14,8 +14,11 @@ import logging
import sys
import six
import testresources
from testtools import matchers
from keystoneclient import exceptions
from keystoneclient.tests import client_fixtures
from keystoneclient.tests import utils as test_utils
from keystoneclient import utils
@ -204,3 +207,34 @@ class TestPositional(test_utils.TestCase):
def test_normal_method(self):
self.assertEqual((self, 1, 2), self.normal_method(1, b=2))
self.assertRaises(TypeError, self.normal_method, 1, 2)
class HashSignedTokenTestCase(test_utils.TestCase,
testresources.ResourcedTestCase):
"""Unit tests for utils.hash_signed_token()."""
resources = [('examples', client_fixtures.EXAMPLES_RESOURCE)]
def test_default_md5(self):
"""The default hash method is md5."""
token = self.examples.SIGNED_TOKEN_SCOPED
if six.PY3:
token = token.encode('utf-8')
token_id_default = utils.hash_signed_token(token)
token_id_md5 = utils.hash_signed_token(token, mode='md5')
self.assertThat(token_id_default, matchers.Equals(token_id_md5))
# md5 hash is 32 chars.
self.assertThat(token_id_default, matchers.HasLength(32))
def test_sha256(self):
"""Can also hash with sha256."""
token = self.examples.SIGNED_TOKEN_SCOPED
if six.PY3:
token = token.encode('utf-8')
token_id = utils.hash_signed_token(token, mode='sha256')
# sha256 hash is 64 chars.
self.assertThat(token_id, matchers.HasLength(64))
def load_tests(loader, tests, pattern):
return testresources.OptimisingTestSuite(tests)

View File

@ -141,8 +141,8 @@ def isunauthenticated(f):
return getattr(f, 'unauthenticated', False)
def hash_signed_token(signed_text):
hash_ = hashlib.md5()
def hash_signed_token(signed_text, mode='md5'):
hash_ = hashlib.new(mode)
hash_.update(signed_text)
return hash_.hexdigest()