Commit 823951ec authored by Florian Apolloner's avatar Florian Apolloner
Browse files

[1.6.x] Force update of the password on iteration count changes.

Backport of 7d0d0dbf from master.
parent 37aea82b
Loading
Loading
Loading
Loading
+9 −0
Original line number Diff line number Diff line
@@ -56,6 +56,8 @@ def check_password(password, encoded, setter=None, preferred='default'):
    hasher = identify_hasher(encoded)

    must_update = hasher.algorithm != preferred.algorithm
    if not must_update:
        must_update = hasher.must_update(encoded)
    is_correct = hasher.verify(password, encoded)
    if setter and is_correct and must_update:
        setter(password)
@@ -212,6 +214,9 @@ class BasePasswordHasher(object):
        """
        raise NotImplementedError()

    def must_update(self, encoded):
        return False


class PBKDF2PasswordHasher(BasePasswordHasher):
    """
@@ -250,6 +255,10 @@ class PBKDF2PasswordHasher(BasePasswordHasher):
            (_('hash'), mask_hash(hash)),
        ])

    def must_update(self, encoded):
        algorithm, iterations, salt, hash = encoded.split('$', 3)
        return int(iterations) != self.iterations


class PBKDF2SHA1PasswordHasher(PBKDF2PasswordHasher):
    """
+31 −0
Original line number Diff line number Diff line
@@ -244,6 +244,37 @@ class TestUtilsHashPass(unittest.TestCase):
            self.assertFalse(check_password('WRONG', encoded, setter))
            self.assertFalse(state['upgraded'])

    def test_pbkdf2_upgrade(self):
        self.assertEqual('pbkdf2_sha256', get_hasher('default').algorithm)
        hasher = get_hasher('default')
        self.assertNotEqual(hasher.iterations, 1)

        old_iterations = hasher.iterations
        try:
            # Generate a password with 1 iteration.
            hasher.iterations = 1
            encoded = make_password('letmein')
            algo, iterations, salt, hash = encoded.split('$', 3)
            self.assertEqual(iterations, '1')

            state = {'upgraded': False}
            def setter(password):
                state['upgraded'] = True

            # Check that no upgrade is triggerd
            self.assertTrue(check_password('letmein', encoded, setter))
            self.assertFalse(state['upgraded'])

            # Revert to the old iteration count and ...
            hasher.iterations = old_iterations

            # ... check if the password would get updated to the new iteration count.
            self.assertTrue(check_password('letmein', encoded, setter))
            self.assertTrue(state['upgraded'])
        finally:
            hasher.iterations = old_iterations


    def test_load_library_no_algorithm(self):
        with self.assertRaises(ValueError) as e:
            BasePasswordHasher()._load_library()