Deriving a key from a pseudo-random string I would like to know if deriving a key from a pseudo-random string with a single iteration is secure.

Concretely, I am designing a system where a secret key is derived in the client side, and then sent to the server for authentication. The steps are as follows:


User enters email and password
Derive k0 &lt;- pbkdf2(password: password, salt: email, alg: 'sha256', iteration: 10000)
Derive k1 &lt;- pbkdf2(password: k0, salt: password, alg: 'sha256', iteration: 1)
Send to server: (email, k1)
Server derives pbkdf2(password: k1, salt: somerandomstring, alg: 'sha256', iteration: 20000) and compares it with the hash in the database


k0 is the 256-bit secret key. The client uses it to encrypt things using a block cipher. The client needs to send something to the server for authentication purposes. But the server should not know the client's secret key. My idea is that step 3 prevents the server from knowing the client's secret key.

But I am wondering if 1 iteration of pbkdf2 is enough. I think it is sufficient because k0 is pseudo-random, and the attacker shouldn't be able to figure it out from k1. (Exhaustive search takes 2^256 steps). Is this correct?



EDIT

For clarification, here is my design so far:

## Register

Client
* User enters email and password
* Derive k0 &lt;- pbkdf2(password: password, salt: email, alg: 'sha256', iteration: 10000)
* Derive k1 &lt;- pbkdf2(password: k0, salt: password, alg: 'sha256', iteration: 1)
* POST to '/register': (email, k1, kdf, kdf_iteration)

Server
* Generate salt &lt;- 256bit random string
* Derive k2 &lt;- pbkdf2(password: k1, salt: salt, alg: 'sha256', iteration: 20000)
* Create a user with (hashed_password, salt) = (k2, salt)
* Login the user and reply with a session key

## Login

Client
* User enters email and password
* GET '/prelogin': (kdf, kdf_iteration)
* Derive k0 &lt;- pbkdf2(password: password, salt: email, alg: 'sha256', iteration: 10000)
* Derive k1 &lt;- pbkdf2(password: k0, salt: password, alg: 'sha256', iteration: 1)
* POST to '/login': (email, k1)

Server
* Look up user (hashed_password, salt)
* Derive k2 &lt;- pbkdf2(password: k1, salt: salt, alg: 'sha256', iteration: 20000)
* Check k2 == hashed_password, and reply with a session token


EDIT2

Clarification on what this is for:

This is to add end-to-end encryption for an open source note taking app (https://github.com/dnote/cli).

Basically, the client can sync the data with the server. Before leaving the client, all data is encrypted using k0 which server does not know.

The server should have no idea about how to decrypt the data, so I decided to derive k1 from k0 to send to the server rather than sending k0 directly to the server.
The problem with this scheme is that $k_1$ can be calculated from $k_0$. That may not be a huge issue, but it can easily be avoided.

You can make sure that $k_0$ and $k_1$ are derived from the original master $k$ which is derived from the password. So you would have k = PBKDF2(password, mail, iteration_count) and k0 = KDF(k, "Enc") and k0 = KDF(k, "Auth").

If you've just PBKDF2 with SHA-256 you could define the second KDF(k, label) as PBKDF2(password: k, salt: label, alg: 'sha256', iteration: 1).

Other options are given in my other answer on the followup question.