From 9b0b414e3eb915c89c9786abeb1307ba734f5901 Mon Sep 17 00:00:00 2001 From: Dmitriy Rabotyagov Date: Thu, 10 Aug 2023 11:55:57 +0200 Subject: [PATCH] Add support for bcrypt_sha256 hasher This patch adds new hashing alhorythm bcrypt_sha256, which is based on the bcrypt but does not have limitations on the leght of the passwords, since passwords are passed through HMAC-SHA2-256 first. At accepts exactly same parameters as bcrypt does. However, it prefix the hash using `prefix` attribute rather then `indent_values` which are same as for bcrypt. Change-Id: I5430ebf5a20142c1a9caab960ced9b3ee2e782c1 --- keystone/common/password_hashing.py | 23 +++++++++++++++---- keystone/conf/identity.py | 2 +- keystone/tests/unit/common/test_utils.py | 11 +++++++++ ...d_bcrypt_sha256_algo-d6b146a59df9373c.yaml | 6 +++++ 4 files changed, 37 insertions(+), 5 deletions(-) create mode 100644 releasenotes/notes/add_bcrypt_sha256_algo-d6b146a59df9373c.yaml diff --git a/keystone/common/password_hashing.py b/keystone/common/password_hashing.py index 7c3c617e4e..ddc4094f17 100644 --- a/keystone/common/password_hashing.py +++ b/keystone/common/password_hashing.py @@ -27,6 +27,7 @@ CONF = keystone.conf.CONF LOG = log.getLogger(__name__) SUPPORTED_HASHERS = frozenset([passlib.hash.bcrypt, + passlib.hash.bcrypt_sha256, passlib.hash.scrypt, passlib.hash.pbkdf2_sha512, passlib.hash.sha512_crypt]) @@ -39,12 +40,26 @@ _HASHER_NAME_MAP = {hasher.name: hasher for hasher in SUPPORTED_HASHERS} # '$$$') so we can do a fast-lookup on the hasher to # use. If has hasher has multiple ident options it is encoded in the # .ident_values attribute whereas hashers that have a single option -# (sha512_crypt) only has the .ident attribute. +# ( ) only has the .ident attribute. +# NOTE(noonedeadpunk): Though bcrypt_sha256 does define as part of +# the metadata, actual indent is represented with a instead. +def _get_hash_ident(hashers): + for hasher in hashers: + if hasattr(hasher, 'prefix'): + ident = (getattr(hasher, 'prefix'),) + elif hasattr(hasher, 'ident_values'): + ident = getattr(hasher, 'ident_values') + else: + ident = (getattr(hasher, 'ident'),) + yield (hasher, ident) + + _HASHER_IDENT_MAP = { prefix: module for module, prefix in itertools.chain( - *[zip([mod] * len(getattr(mod, 'ident_values', (mod.ident,))), - getattr(mod, 'ident_values', (mod.ident,))) - for mod in SUPPORTED_HASHERS])} + *[zip([mod] * len(ident), ident) + for mod, ident in _get_hash_ident(SUPPORTED_HASHERS)] + ) +} def _get_hasher_from_ident(hashed): diff --git a/keystone/conf/identity.py b/keystone/conf/identity.py index d2cb34da17..e50468e0cf 100644 --- a/keystone/conf/identity.py +++ b/keystone/conf/identity.py @@ -114,7 +114,7 @@ Maximum number of entities that will be returned in an identity collection. password_hash_algorithm = cfg.StrOpt( 'password_hash_algorithm', - choices=['bcrypt', 'scrypt', 'pbkdf2_sha512'], + choices=['bcrypt', 'bcrypt_sha256', 'scrypt', 'pbkdf2_sha512'], default='bcrypt', help=utils.fmt(""" The password hashing algorithm to use for passwords stored within keystone. diff --git a/keystone/tests/unit/common/test_utils.py b/keystone/tests/unit/common/test_utils.py index 15ebc273ed..163eb02458 100644 --- a/keystone/tests/unit/common/test_utils.py +++ b/keystone/tests/unit/common/test_utils.py @@ -145,6 +145,17 @@ class UtilsTestCase(unit.BaseTestCase): common_utils.hash_password, invalid_length_password) + def test_bcrypt_sha256_not_truncate_password(self): + self.config_fixture.config(strict_password_check=True) + self.config_fixture.config(group='identity', + password_hash_algorithm='bcrypt_sha256') + password = '0' * 128 + password_verified = \ + common_utils.verify_length_and_trunc_password(password) + hashed = common_utils.hash_password(password) + self.assertTrue(common_utils.check_password(password, hashed)) + self.assertEqual(password.encode('utf-8'), password_verified) + def _create_test_user(self, password=OPTIONAL): user = {"name": "hthtest"} if password is not self.OPTIONAL: diff --git a/releasenotes/notes/add_bcrypt_sha256_algo-d6b146a59df9373c.yaml b/releasenotes/notes/add_bcrypt_sha256_algo-d6b146a59df9373c.yaml new file mode 100644 index 0000000000..eb55df00f8 --- /dev/null +++ b/releasenotes/notes/add_bcrypt_sha256_algo-d6b146a59df9373c.yaml @@ -0,0 +1,6 @@ +--- +features: + - | + Added support for the ``bcrypt_sha256`` password hashing algorythm, which + does workaround limitation on a password length BCrypt have by running the + password through HMAC-SHA2-256 first.