[1.5.x] Fixed #21253 -- PBKDF2 with cached HMAC key

This gives a 2x speed increase compared to the existing implementation.

Thanks to Steve Thomas for the initial patch and Tim Graham for finishing it.

Backport of 1e4f53a6eb from master.
This commit is contained in:
Florian Apolloner 2013-09-17 18:14:38 -05:00
parent 045d192222
commit 166be6a77e
1 changed files with 9 additions and 20 deletions

View File

@ -115,22 +115,6 @@ def _long_to_bin(x, hex_format_string):
return binascii.unhexlify((hex_format_string % x).encode('ascii')) return binascii.unhexlify((hex_format_string % x).encode('ascii'))
def _fast_hmac(key, msg, digest):
"""
A trimmed down version of Python's HMAC implementation.
This function operates on bytes.
"""
dig1, dig2 = digest(), digest()
if len(key) != dig1.block_size:
raise ValueError('Key size needs to match the block_size of the digest.')
dig1.update(key.translate(_trans_36))
dig1.update(msg)
dig2.update(key.translate(_trans_5c))
dig2.update(dig1.digest())
return dig2
def pbkdf2(password, salt, iterations, dklen=0, digest=None): def pbkdf2(password, salt, iterations, dklen=0, digest=None):
""" """
Implements PBKDF2 as defined in RFC 2898, section 5.2 Implements PBKDF2 as defined in RFC 2898, section 5.2
@ -158,16 +142,21 @@ def pbkdf2(password, salt, iterations, dklen=0, digest=None):
hex_format_string = "%%0%ix" % (hlen * 2) hex_format_string = "%%0%ix" % (hlen * 2)
inner_digest_size = digest().block_size inner, outer = digest(), digest()
if len(password) > inner_digest_size: if len(password) > inner.block_size:
password = digest(password).digest() password = digest(password).digest()
password += b'\x00' * (inner_digest_size - len(password)) password += b'\x00' * (inner.block_size - len(password))
inner.update(password.translate(hmac.trans_36))
outer.update(password.translate(hmac.trans_5C))
def F(i): def F(i):
def U(): def U():
u = salt + struct.pack(b'>I', i) u = salt + struct.pack(b'>I', i)
for j in xrange(int(iterations)): for j in xrange(int(iterations)):
u = _fast_hmac(password, u, digest).digest() dig1, dig2 = inner.copy(), outer.copy()
dig1.update(u)
dig2.update(dig1.digest())
u = dig2.digest()
yield _bin_to_long(u) yield _bin_to_long(u)
return _long_to_bin(reduce(operator.xor, U()), hex_format_string) return _long_to_bin(reduce(operator.xor, U()), hex_format_string)