Picture a state of the art implementation of a website registration and login system.

I'm interested in analyzing what a defender gains and loses by feeding the user password to a key-stretching KDF function (e.g. argon2).

Let's start from the basis.

Given a hash function H and a password Pwd, performing h0 = H(Pwd) and sending h0 to the server is pretty useless. However, if Ks is a per-site unique value (some use the hostname but a random unique value is more portable) then h1 = H(Ks || Pwd) turns a possible shared secret (Pwd) into a per-site unique secret (h1).

An attacker knowing h1 from site A can still impersonate the user on the specific site but it cannot use h1 on, say, site B. Even if both sites use the same client-side processing of Pwd.

As standing the scheme is weak, H is prone to many attacks, a quick analysis turn H into full-blown KDF with key-stretching with the server sending all the necessary entropy (or just set minimum requirements for the UAs).

So, by sending h2 = KDF(Ks, Pwd, costs_params, salt) to the server, we make sure the effective password used to register/login on this website is unique and resistant to usual attacks on stored passwords. An unrelated but useful reminder: server-side h2 is still "hashed" (i.e. KDF with KS) to prevent turning a leak into an easy impersonation of the users.

I've not seen this scheme implemented in any framework or even raccomended as far as I know. Why? Should I start implementing/requiring it?

I tried to write down a list of cons and pros.


  1. The server cannot enforce a password policy. My opinion is that most password policies are harmful but the server should at least do a few checks. In particular, check the length and if the password is in the n-most-used-passwords list. However, the length check can be done client-side (at the cost of possibly letting "smart" users bypass the check).
  2. This extra processing prevents credential stuffing only in the case the attack can solely intercept TLS traffic passively. If the attacker can control the user device, or the server, or tamper with TLS traffic this is all worthless. It's worth noting that if an attacker can read the TLS traffic they already have access to the user's sensitive information (including authentication tokens). This reduces the usefulness of this client-side processing.
  3. It consumes (variable) client-side power. JavaScript implementations are sufficiently fast but they have great variability and the user may not be happy with the increased power consumed. It's not much about time as much as about battery life nowadays.
  4. It's an extra moving wheel. I have the feeling (under discussion) that all this processing is doing is adding an extra "protection" on top of TLS but without adding too much (see point 2). Less moving parts are better. We do something similar server-side though, but in that case, the weak link is the server, which is a single-person business, while in this case, the weak link is TLS which is all people business. Plus unique password generation can be handled by encouraging the use of password managers. All this gives me a feeling of "reinventing the wheel".


  1. It prevents credential stuffing even if somebody is sniffing TLS. This is the only bullet but it is very appealing.

There already is a very similar question: Secure authentication: partial client-side key stretching... please review/criticize my idea but the answer focuses only on con #3 which, 8 years later, don't really hold anymore.

Personally, I have the feeling that theoretically it is useful but in practice the attack surface removed is very small and if one considers TLS compromised then all bets are off (just as if the standard crypto is broken).
Yet maybe I'm just old and not receptive as I should be.

Reiterating the question: Should we start requiring client-side key-stretching KDF processing of the user password as we require server-side?

  • "Kp is a per-site unique value (i.e. a pepper, some use the hostname but a random unique value is more portable)" - a pepper is a secret. The hostname is not a secret, thus it does not fit the description of a pepper. Even a random unique value is likely not secret because it must be always the same for a site and it must be known before logging in - thus also known to the attacker. Aug 4, 2022 at 19:21
  • @SteffenUllrich My bad, you are right (although not very helpful considering the word "pepper" appeared only once in the question and was kind of irrelevant). I corrected the question, thank you for pointing this out. Aug 5, 2022 at 8:08
  • Similar question: security.stackexchange.com/questions/254820/…
    – mti2935
    Aug 5, 2022 at 9:26
  • @ThoriumBR Unfortunately it doesn't. The answers to that questions are naive and don't address any of my points. Aug 5, 2022 at 10:30
  • @MargaretBloom, wrt So, by sending h2 = KDF(Ks, Pwd, costs_params, salt) to the server - where would the salt be stored? Presumably, on the server. If so, then you would have to work out some protocol for the client first obtaining the salt from the server, then performing Argon2. At that point, this begins to look very much like PAKE/SRP, so why not use PAKE/SRP, which offers many other benefits (including mutual authentication between client and server, and more, see security.stackexchange.com/questions/242811/….
    – mti2935
    Aug 5, 2022 at 10:42


Browse other questions tagged .