Commit c4f29c91 authored by Florian Apolloner's avatar Florian Apolloner
Browse files

[1.4.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 1e4f53a6 from master.
parent ea04c81d
Loading
Loading
Loading
Loading
+9 −18
Original line number Diff line number Diff line
@@ -106,20 +106,6 @@ def _long_to_bin(x, hex_format_string):
    return binascii.unhexlify(hex_format_string % x)


def _fast_hmac(key, msg, digest):
    """
    A trimmed down version of Python's HMAC implementation
    """
    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):
    """
    Implements PBKDF2 as defined in RFC 2898, section 5.2
@@ -145,16 +131,21 @@ def pbkdf2(password, salt, iterations, dklen=0, digest=None):

    hex_format_string = "%%0%ix" % (hlen * 2)

    inner_digest_size = digest().block_size
    if len(password) > inner_digest_size:
    inner, outer = digest(), digest()
    if len(password) > inner.block_size:
        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 U():
            u = salt + struct.pack('>I', i)
            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)
        return _long_to_bin(reduce(operator.xor, U()), hex_format_string)