mirror of
https://github.com/Tautulli/Tautulli.git
synced 2025-07-11 07:46:07 -07:00
Update hashing_passwords to use hashlib and remove pbkdf2
This commit is contained in:
parent
121dad588e
commit
169f83ac4a
2 changed files with 7 additions and 142 deletions
|
@ -19,10 +19,7 @@
|
||||||
import hashlib
|
import hashlib
|
||||||
from os import urandom
|
from os import urandom
|
||||||
from base64 import b64encode, b64decode
|
from base64 import b64encode, b64decode
|
||||||
|
from hashlib import pbkdf2_hmac
|
||||||
|
|
||||||
# From https://github.com/mitsuhiko/python-pbkdf2
|
|
||||||
from pbkdf2 import pbkdf2_bin
|
|
||||||
|
|
||||||
|
|
||||||
# Parameters to PBKDF2. Only affect new passwords.
|
# Parameters to PBKDF2. Only affect new passwords.
|
||||||
|
@ -43,9 +40,8 @@ def make_hash(password):
|
||||||
return 'PBKDF2${}${}${}${}'.format(
|
return 'PBKDF2${}${}${}${}'.format(
|
||||||
HASH_FUNCTION,
|
HASH_FUNCTION,
|
||||||
COST_FACTOR,
|
COST_FACTOR,
|
||||||
salt,
|
salt.decode('utf-8'),
|
||||||
b64encode(pbkdf2_bin(password, salt, COST_FACTOR, KEY_LENGTH,
|
b64encode(pbkdf2_hmac(HASH_FUNCTION, password, salt, COST_FACTOR, KEY_LENGTH)).decode('utf-8'))
|
||||||
getattr(hashlib, HASH_FUNCTION))))
|
|
||||||
|
|
||||||
|
|
||||||
def check_hash(password, hash_):
|
def check_hash(password, hash_):
|
||||||
|
@ -54,13 +50,12 @@ def check_hash(password, hash_):
|
||||||
password = password.encode('utf-8')
|
password = password.encode('utf-8')
|
||||||
algorithm, hash_function, cost_factor, salt, hash_a = hash_.split('$')
|
algorithm, hash_function, cost_factor, salt, hash_a = hash_.split('$')
|
||||||
assert algorithm == 'PBKDF2'
|
assert algorithm == 'PBKDF2'
|
||||||
hash_a = b64decode(hash_a)
|
hash_a = b64decode(hash_a.encode('utf-8'))
|
||||||
hash_b = pbkdf2_bin(password, salt, int(cost_factor), len(hash_a),
|
hash_b = pbkdf2_hmac(hash_function, password, salt.encode('utf-8'), int(cost_factor), len(hash_a))
|
||||||
getattr(hashlib, hash_function))
|
|
||||||
assert len(hash_a) == len(hash_b) # we requested this from pbkdf2_bin()
|
assert len(hash_a) == len(hash_b) # we requested this from pbkdf2_bin()
|
||||||
# Same as "return hash_a == hash_b" but takes a constant time.
|
# Same as "return hash_a == hash_b" but takes a constant time.
|
||||||
# See http://carlos.bueno.org/2011/10/timing.html
|
# See http://carlos.bueno.org/2011/10/timing.html
|
||||||
diff = 0
|
diff = 0
|
||||||
for char_a, char_b in zip(hash_a, hash_b):
|
for char_a, char_b in zip(hash_a, hash_b):
|
||||||
diff |= ord(char_a) ^ ord(char_b)
|
diff |= char_a ^ char_b
|
||||||
return diff == 0
|
return diff == 0
|
||||||
|
|
130
lib/pbkdf2.py
130
lib/pbkdf2.py
|
@ -1,130 +0,0 @@
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
"""
|
|
||||||
pbkdf2
|
|
||||||
~~~~~~
|
|
||||||
|
|
||||||
This module implements pbkdf2 for Python. It also has some basic
|
|
||||||
tests that ensure that it works. The implementation is straightforward
|
|
||||||
and uses stdlib only stuff and can be easily be copy/pasted into
|
|
||||||
your favourite application.
|
|
||||||
|
|
||||||
Use this as replacement for bcrypt that does not need a c implementation
|
|
||||||
of a modified blowfish crypto algo.
|
|
||||||
|
|
||||||
Example usage:
|
|
||||||
|
|
||||||
>>> pbkdf2_hex('what i want to hash', 'the random salt')
|
|
||||||
'fa7cc8a2b0a932f8e6ea42f9787e9d36e592e0c222ada6a9'
|
|
||||||
|
|
||||||
How to use this:
|
|
||||||
|
|
||||||
1. Use a constant time string compare function to compare the stored hash
|
|
||||||
with the one you're generating::
|
|
||||||
|
|
||||||
def safe_str_cmp(a, b):
|
|
||||||
if len(a) != len(b):
|
|
||||||
return False
|
|
||||||
rv = 0
|
|
||||||
for x, y in izip(a, b):
|
|
||||||
rv |= ord(x) ^ ord(y)
|
|
||||||
return rv == 0
|
|
||||||
|
|
||||||
2. Use `os.urandom` to generate a proper salt of at least 8 byte.
|
|
||||||
Use a unique salt per hashed password.
|
|
||||||
|
|
||||||
3. Store ``algorithm$salt:costfactor$hash`` in the database so that
|
|
||||||
you can upgrade later easily to a different algorithm if you need
|
|
||||||
one. For instance ``PBKDF2-256$thesalt:10000$deadbeef...``.
|
|
||||||
|
|
||||||
|
|
||||||
:copyright: (c) Copyright 2011 by Armin Ronacher.
|
|
||||||
:license: BSD, see LICENSE for more details.
|
|
||||||
"""
|
|
||||||
import hmac
|
|
||||||
import hashlib
|
|
||||||
from struct import Struct
|
|
||||||
from operator import xor
|
|
||||||
from itertools import starmap
|
|
||||||
|
|
||||||
|
|
||||||
_pack_int = Struct('>I').pack
|
|
||||||
|
|
||||||
|
|
||||||
def pbkdf2_hex(data, salt, iterations=1000, keylen=24, hashfunc=None):
|
|
||||||
"""Like :func:`pbkdf2_bin` but returns a hex encoded string."""
|
|
||||||
return pbkdf2_bin(data, salt, iterations, keylen, hashfunc).encode('hex')
|
|
||||||
|
|
||||||
|
|
||||||
def pbkdf2_bin(data, salt, iterations=1000, keylen=24, hashfunc=None):
|
|
||||||
"""Returns a binary digest for the PBKDF2 hash algorithm of `data`
|
|
||||||
with the given `salt`. It iterates `iterations` time and produces a
|
|
||||||
key of `keylen` bytes. By default SHA-1 is used as hash function,
|
|
||||||
a different hashlib `hashfunc` can be provided.
|
|
||||||
"""
|
|
||||||
hashfunc = hashfunc or hashlib.sha1
|
|
||||||
mac = hmac.new(data, None, hashfunc)
|
|
||||||
def _pseudorandom(x, mac=mac):
|
|
||||||
h = mac.copy()
|
|
||||||
h.update(x)
|
|
||||||
return bytearray(h.digest())
|
|
||||||
buf = []
|
|
||||||
for block in range(1, -(-keylen // mac.digest_size) + 1):
|
|
||||||
rv = u = _pseudorandom(salt + _pack_int(block))
|
|
||||||
for i in range(iterations - 1):
|
|
||||||
u = _pseudorandom(''.join(map(chr, u)))
|
|
||||||
rv = list(starmap(xor, zip(rv, u)))
|
|
||||||
buf.extend(rv)
|
|
||||||
return ''.join(map(chr, buf))[:keylen]
|
|
||||||
|
|
||||||
|
|
||||||
def test():
|
|
||||||
failed = []
|
|
||||||
def check(data, salt, iterations, keylen, expected):
|
|
||||||
rv = pbkdf2_hex(data, salt, iterations, keylen)
|
|
||||||
if rv != expected:
|
|
||||||
print('Test failed:')
|
|
||||||
print(' Expected: %s' % expected)
|
|
||||||
print(' Got: %s' % rv)
|
|
||||||
print(' Parameters:')
|
|
||||||
print(' data=%s' % data)
|
|
||||||
print(' salt=%s' % salt)
|
|
||||||
print(' iterations=%d' % iterations)
|
|
||||||
print()
|
|
||||||
failed.append(1)
|
|
||||||
|
|
||||||
# From RFC 6070
|
|
||||||
check('password', 'salt', 1, 20,
|
|
||||||
'0c60c80f961f0e71f3a9b524af6012062fe037a6')
|
|
||||||
check('password', 'salt', 2, 20,
|
|
||||||
'ea6c014dc72d6f8ccd1ed92ace1d41f0d8de8957')
|
|
||||||
check('password', 'salt', 4096, 20,
|
|
||||||
'4b007901b765489abead49d926f721d065a429c1')
|
|
||||||
check('passwordPASSWORDpassword', 'saltSALTsaltSALTsaltSALTsaltSALTsalt',
|
|
||||||
4096, 25, '3d2eec4fe41c849b80c8d83662c0e44a8b291a964cf2f07038')
|
|
||||||
check('pass\x00word', 'sa\x00lt', 4096, 16,
|
|
||||||
'56fa6aa75548099dcc37d7f03425e0c3')
|
|
||||||
# This one is from the RFC but it just takes for ages
|
|
||||||
##check('password', 'salt', 16777216, 20,
|
|
||||||
## 'eefe3d61cd4da4e4e9945b3d6ba2158c2634e984')
|
|
||||||
|
|
||||||
# From Crypt-PBKDF2
|
|
||||||
check('password', 'ATHENA.MIT.EDUraeburn', 1, 16,
|
|
||||||
'cdedb5281bb2f801565a1122b2563515')
|
|
||||||
check('password', 'ATHENA.MIT.EDUraeburn', 1, 32,
|
|
||||||
'cdedb5281bb2f801565a1122b25635150ad1f7a04bb9f3a333ecc0e2e1f70837')
|
|
||||||
check('password', 'ATHENA.MIT.EDUraeburn', 2, 16,
|
|
||||||
'01dbee7f4a9e243e988b62c73cda935d')
|
|
||||||
check('password', 'ATHENA.MIT.EDUraeburn', 2, 32,
|
|
||||||
'01dbee7f4a9e243e988b62c73cda935da05378b93244ec8f48a99e61ad799d86')
|
|
||||||
check('password', 'ATHENA.MIT.EDUraeburn', 1200, 32,
|
|
||||||
'5c08eb61fdf71e4e4ec3cf6ba1f5512ba7e52ddbc5e5142f708a31e2e62b1e13')
|
|
||||||
check('X' * 64, 'pass phrase equals block size', 1200, 32,
|
|
||||||
'139c30c0966bc32ba55fdbf212530ac9c5ec59f1a452f5cc9ad940fea0598ed1')
|
|
||||||
check('X' * 65, 'pass phrase exceeds block size', 1200, 32,
|
|
||||||
'9ccad6d468770cd51b10e6a68721be611a8b4d282601db3b36be9246915ec82a')
|
|
||||||
|
|
||||||
raise SystemExit(bool(failed))
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
test()
|
|
Loading…
Add table
Add a link
Reference in a new issue