Return two booleans. The first is whether the raw password matches the three part encoded digest, and the second whether to regenerate the password.
(password, encoded, preferred="default")
| 37 | |
| 38 | |
| 39 | def verify_password(password, encoded, preferred="default"): |
| 40 | """ |
| 41 | Return two booleans. The first is whether the raw password matches the |
| 42 | three part encoded digest, and the second whether to regenerate the |
| 43 | password. |
| 44 | """ |
| 45 | fake_runtime = password is None or not is_password_usable(encoded) |
| 46 | |
| 47 | preferred = get_hasher(preferred) |
| 48 | try: |
| 49 | hasher = identify_hasher(encoded) |
| 50 | except ValueError: |
| 51 | # encoded is gibberish or uses a hasher that's no longer installed. |
| 52 | fake_runtime = True |
| 53 | |
| 54 | if fake_runtime: |
| 55 | # Run the default password hasher once to reduce the timing difference |
| 56 | # between an existing user with an unusable password and a nonexistent |
| 57 | # user or missing hasher (similar to #20760). |
| 58 | make_password(get_random_string(UNUSABLE_PASSWORD_SUFFIX_LENGTH)) |
| 59 | return False, False |
| 60 | |
| 61 | hasher_changed = hasher.algorithm != preferred.algorithm |
| 62 | must_update = hasher_changed or preferred.must_update(encoded) |
| 63 | is_correct = hasher.verify(password, encoded) |
| 64 | |
| 65 | # If the hasher didn't change (we don't protect against enumeration if it |
| 66 | # does) and the password should get updated, try to close the timing gap |
| 67 | # between the work factor of the current encoded password and the default |
| 68 | # work factor. |
| 69 | if not is_correct and not hasher_changed and must_update: |
| 70 | hasher.harden_runtime(password, encoded) |
| 71 | |
| 72 | return is_correct, must_update |
| 73 | |
| 74 | |
| 75 | def check_password(password, encoded, setter=None, preferred="default"): |
no test coverage detected