# Copyright (c) 2014-2017, Jan Varho
#
# Permission to use, copy, modify, and/or distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
# copyright notice and this permission notice appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

"""PBKDF2 in pure Python, compatible with Python3.4+ hashlib.pbkdf2_hmac"""


import hashlib
import hmac
import struct
from warnings import warn

from .common import xrange


# If you see this, then you should upgrade your python version.
# The standard libraries of 2.7.8+ and 3.4+ include PBKDF2.
warn('python pbkdf2 is deprecated, please use a newer version of python',
     DeprecationWarning)


def pbkdf2_hmac(name, password, salt, rounds, dklen=None):
    """Returns the result of the Password-Based Key Derivation Function 2"""
    h = hmac.new(key=password, digestmod=lambda d=b'': hashlib.new(name, d))
    hs = h.copy()
    hs.update(salt)

    blocks = bytearray()
    dklen = hs.digest_size if dklen is None else dklen
    block_count, last_size = divmod(dklen, hs.digest_size)
    block_count += last_size > 0

    for block_number in xrange(1, block_count + 1):
        hb = hs.copy()
        hb.update(struct.pack('>L', block_number))
        U = bytearray(hb.digest())

        if rounds > 1:
            Ui = U
            for i in xrange(rounds - 1):
                hi = h.copy()
                hi.update(Ui)
                Ui = bytearray(hi.digest())
                for j in xrange(hs.digest_size):
                    U[j] ^= Ui[j]

        blocks.extend(U)

    if last_size:
        del blocks[dklen:]
    return bytes(blocks)

if __name__ == "__main__":
    import sys
    from . import tests
    tests.run_pbkdf2_suite(sys.modules[__name__])

