mirror of
https://github.com/Tautulli/Tautulli.git
synced 2025-01-04 10:10:00 -08:00
040972bcba
OWASP Cheat Sheet recommends 600,000 iterations. https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html#pbkdf2
57 lines
1.9 KiB
Python
57 lines
1.9 KiB
Python
# coding: utf8
|
|
"""
|
|
|
|
Securely hash and check passwords using PBKDF2.
|
|
|
|
Use random salts to protect againt rainbow tables, many iterations against
|
|
brute-force, and constant-time comparaison againt timing attacks.
|
|
|
|
Keep parameters to the algorithm together with the hash so that we can
|
|
change the parameters and keep older hashes working.
|
|
|
|
See more details at http://exyr.org/2011/hashing-passwords/
|
|
|
|
Author: Simon Sapin
|
|
License: BSD
|
|
|
|
"""
|
|
|
|
from os import urandom
|
|
from base64 import b64encode, b64decode
|
|
from hashlib import pbkdf2_hmac
|
|
from hmac import compare_digest
|
|
|
|
|
|
# Parameters to PBKDF2. Only affect new passwords.
|
|
SALT_LENGTH = 16
|
|
KEY_LENGTH = 24
|
|
HASH_FUNCTION = 'sha256' # Must be in hashlib.
|
|
# Linear to the hashing time. Adjust to be high but take a reasonable
|
|
# amount of time on your server. Measure with:
|
|
# python -m timeit -s 'import passwords as p' 'p.make_hash("something")'
|
|
COST_FACTOR = 600000
|
|
|
|
|
|
def make_hash(password):
|
|
"""Generate a random salt and return a new hash for the password."""
|
|
if isinstance(password, str):
|
|
password = password.encode('utf-8')
|
|
salt = b64encode(urandom(SALT_LENGTH))
|
|
return 'PBKDF2${}${}${}${}'.format(
|
|
HASH_FUNCTION,
|
|
COST_FACTOR,
|
|
salt.decode('utf-8'),
|
|
b64encode(pbkdf2_hmac(HASH_FUNCTION, password, salt, COST_FACTOR, KEY_LENGTH)).decode('utf-8'))
|
|
|
|
|
|
def check_hash(password, hash_):
|
|
"""Check a password against an existing hash."""
|
|
if isinstance(password, str):
|
|
password = password.encode('utf-8')
|
|
algorithm, hash_function, cost_factor, salt, hash_a = hash_.split('$')
|
|
assert algorithm == 'PBKDF2'
|
|
hash_a = b64decode(hash_a.encode('utf-8'))
|
|
hash_b = pbkdf2_hmac(hash_function, password, salt.encode('utf-8'), int(cost_factor), len(hash_a))
|
|
assert len(hash_a) == len(hash_b) # we requested this from pbkdf2_bin()
|
|
return compare_digest(hash_a, hash_b)
|