Is it safe to split the output of PBKDF2? I would like to know if splitting a key generated by PBKDF2 to derive two keys is a safe practice.

Concretely, in my system, I need to derive two keys. One for symmetric cipher used in the client side, and one for authenticating the user by sending to the server. The first key is never sent to the server so that only the client knows how to decrypt ciphertexts.


User enters email and password;
Derive k &lt;- pbkdf2(password: password, salt: email, alg: 'sha256', iteration: 10000, dkLen: 512);
Split k into (k0, k1) = (k[0:255], k[256:512]). Use k0 as client side key and send k1 to the server.


My idea is that there is no risk in splitting the key in such manner because the key is a pseudo-random string. Any ideas?
It's secure as the bits are unrelated. It is not secure because it removes a bit of security by giving advantage to an attacker.

PBKDF2 has indeed been defined to generate a dynamic amount of key material. The bits of the output are independent of each other, and having any part of the output doesn't give you any information about the rest of the output values. So $k_0$ and $k_1$ are unrelated because PBKDF2 is indeed a pseudo random generator. Great.

However, PBKDF2 has also a design problem: if you require more than the output of the hash function (in this case SHA-256) then it requires you to perform all the iterations again to calculate the next block of output. This is problematic as an adversary may not need to perform those calculations themselves.

For instance, if the attacker just needs to compare against a value encrypted with $k_0$ then the adversary doesn't need to calculate $k_1$: just calculating and validating $k_0$ is enough to guess the password. You, on the other hand, need $k_1$ so you will have to perform all that work without gaining any advantage from it. As the adversary only needs half the work, obviously you lose one bit of secure. And, as your keys are both the exact output size of the hash function, you've inadvertently made it easy for the adversary.

There are a few ways around this. First of all, you can use a more modern function such as Argon2 (check for secure Argon2 configurations first). Secondly, it is possible to use the output of PBKDF2 and derive two keys from it using a key based key derivation function or KBKDF such as HKDF, e.g. k0 = HKDF(k, "Enc") and k1 = HKDF(k, "Auth"). 

Finally, you can "hack" PBKDF2 by using SHA-512 and have it deliver 512 bits, which you can split. That's solution obviously doesn't scale well, but it is useful if the other options are not available to you, and it is very simple to achieve; you just have to change the hash function after all.