Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow overriding _handle_long_passwords #90

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 12 additions & 4 deletions flask_bcrypt.py
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ def _unicode_to_bytes(self, unicode_string):
bytes_object = unicode_string
return bytes_object

def generate_password_hash(self, password, rounds=None, prefix=None):
def generate_password_hash(self, password, rounds=None, prefix=None, handle_long_passwords=None):
'''Generates a password hash using bcrypt. Specifying `rounds`
sets the log_rounds parameter of `bcrypt.gensalt()` which determines
the complexity of the salt. 12 is the default value. Specifying `prefix`
Expand All @@ -178,6 +178,7 @@ def generate_password_hash(self, password, rounds=None, prefix=None):
:param password: The password to be hashed.
:param rounds: The optional number of rounds.
:param prefix: The algorithm version to use.
:param handle_long_passwords: Enable prehashing with SHA256
'''

if not password:
Expand All @@ -192,14 +193,17 @@ def generate_password_hash(self, password, rounds=None, prefix=None):
password = self._unicode_to_bytes(password)
prefix = self._unicode_to_bytes(prefix)

if self._handle_long_passwords:
if handle_long_passwords is None:
handle_long_passwords = self._handle_long_passwords

if handle_long_passwords:
password = hashlib.sha256(password).hexdigest()
password = self._unicode_to_bytes(password)

salt = bcrypt.gensalt(rounds=rounds, prefix=prefix)
return bcrypt.hashpw(password, salt)

def check_password_hash(self, pw_hash, password):
def check_password_hash(self, pw_hash, password, handle_long_passwords=None):
'''Tests a password hash against a candidate password. The candidate
password is first hashed and then subsequently compared in constant
time to the existing hash. This will either return `True` or `False`.
Expand All @@ -212,13 +216,17 @@ def check_password_hash(self, pw_hash, password):

:param pw_hash: The hash to be compared against.
:param password: The password to compare.
:param handle_long_passwords: Enable prehashing with SHA256
'''

# Python 3 unicode strings must be encoded as bytes before hashing.
pw_hash = self._unicode_to_bytes(pw_hash)
password = self._unicode_to_bytes(password)

if self._handle_long_passwords:
if handle_long_passwords is None:
handle_long_passwords = self._handle_long_passwords

if handle_long_passwords:
password = hashlib.sha256(password).hexdigest()
password = self._unicode_to_bytes(password)

Expand Down
20 changes: 19 additions & 1 deletion test_bcrypt.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,15 @@ def test_long_password(self):
# Ensure that a longer password yields the same hash
self.assertTrue(self.bcrypt.check_password_hash(pw_hash, 'A' * 80))

def test_custom_long_password(self):
"""Test the work around bcrypt maximum password length when set explicitly to True."""

# Create a password with a 72 bytes length
password = 'A' * 72
pw_hash = self.bcrypt.generate_password_hash(password, handle_long_passwords=True)
# Ensure that a longer password *does not* yield the same hash
self.assertFalse(self.bcrypt.check_password_hash(pw_hash, 'A' * 80, handle_long_passwords=True))


class LongPasswordsTestCase(BasicTestCase):

Expand All @@ -80,9 +89,18 @@ def test_long_password(self):
# Create a password with a 72 bytes length
password = 'A' * 72
pw_hash = self.bcrypt.generate_password_hash(password)
# Ensure that a longer password **do not** yield the same hash
# Ensure that a longer password **does not** yield the same hash
self.assertFalse(self.bcrypt.check_password_hash(pw_hash, 'A' * 80))

def test_custom_long_password(self):
"""Test the work around bcrypt maximum password length when set explicitly to False."""

# Create a password with a 72 bytes length
password = 'A' * 72
pw_hash = self.bcrypt.generate_password_hash(password, handle_long_passwords=False)
# Ensure that a longer password yields the same hash
self.assertTrue(self.bcrypt.check_password_hash(pw_hash, 'A' * 80, handle_long_passwords=False))


if __name__ == '__main__':
unittest.main()