Commit 37aea82b authored by Florian Apolloner's avatar Florian Apolloner
Browse files

[1.6.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 15a6667a
Loading
Loading
Loading
Loading
+9 −20
Original line number Diff line number Diff line
@@ -116,22 +116,6 @@ def _long_to_bin(x, hex_format_string):
    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(hmac.trans_36))
    dig1.update(msg)
    dig2.update(key.translate(hmac.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
@@ -160,16 +144,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(b'>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)