Commit 1e4f53a6 authored by Florian Apolloner's avatar Florian Apolloner
Browse files

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.
parent 1e39982b
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)