As a client-side hashing approach, I think it's a bit overly complicated but probably not fundamentally flawed. I would use a singular hash function to produce the byte stream, instead of introducing a PRNG and seeding it from the hash functoin just to get a byte stream. But the bigger conversation is, what role can client-side hashing play, either as a full-on replacement to server-side hashing, or perhaps just to augment the server-side hashing everyone is doing today?

Stepping back a minute, it's helpful to consider the fundamental reality of almost any password validation scheme. In this case we are storing a "public key" on the server, but it is more aptly described as the "password validator". Almost all designs will have such a thing (a notable exception is PolyPassHash which cleverly avoids a one-to-one mapping of users to validators, and uses this to try to defeat some offline attacks, with some interesting trade-offs).

If you combine public information, the password validator, and the correct password, your 'password validation function' will complete and return 1. Therefore, if you have the necessary public information (i.e. domain name, username, whatever else is stuffed in the hash) and you steal the password validator database, you now have everything you need in order to run the so-called "offline dictionary attack".

In SRP, an obvious inspiration to this CBCrypt design, the password validator database is basically computed entirely by the modexp() work required to generate the pub/priv keys. There is no additional/configurable key stretching which can be applied other than choosing the bitlength of the keys.

Here we have a configurable round of hashing (Scrypt or PBKDF2) which is applied before the key derivation step. This provides additional protection against offline attacks, depending on how hard the work factor is set. History has shown us, a key detail in ensuring secure password storage is actively managing the hashing function and work factors over time, or else watching Moore's law strip away any protection we thought we had.... So the next key question is, how do we actively manage and maintain a sufficient work factor?

When evaluating a hashing function, one key question to ask is can we add to the work-factor of existing passwords offline (without having the password in RAM) or do we need the user's password resident (somewhere, client or server-side) in order to recompute a new hash with the new work factor? In this case, by design it's impossible to offline harden the existing passwords. You would need to first login the user, verify their password, and then recompute a new hash (perhaps transparently, perhaps with some UI component if it is too slow otherwise) and re-derive and update the key server-side, in order to change the work factor.

But really, the primary consideration with any client-side hashing design, is how to support the extremely wide array of devices which will need to register/login to your site, and what's the user experience when they click the Login button?

From CBcrypt.cs, where the proof of concept is written in C#.NET using BouncyCastle's Scrypt library:

/* cost 4096, blockSize 8, parallel 1, cause SCrypt to take ~175-350ms on Core i5-540M, 2.5Ghz
* This is in addition to the approx 100ms-200ms to generate ECDSA keypair.
*/

Now, in reality we are going to be running Scrypt in Javascript on a mobile device. I found what seems to be a highly optimized implementation [1] and plugged it on Dropbox [2] so I could point my browser at it to see how slow just the Scrypt ran. On my laptop it was taking about 800ms and on my iPhone 5 about 2500ms. I would say that means for about 25% of the mobile population this function runs about an order of magnitude too slow for comfort... it will impact user experience, it could impact bounce rate, etc. This doesn't even include the ECDH part of the calculation.

This is definitely the hardest question to answer with client-side hashing, or even simply "sever-relief" hashing where a portion of the overall hashing may optionally be pushed to the client, particularly during DDoS type events. How do you identify which clients are sufficiently powered to reasonably perform the hashing, or what kind of UI needs to be presented if you could be potentially locking up a browser for 5+ seconds while the hashing is running? Users are not well trained to expect logins to be slow -- a stall in the browser is almost always a reason to simply resubmit the form. It would be great if users associated their browser tab locking up, their fans spinning to full speed, and their battery power plummeting with "Whoohoo, this site is ROCKING a awesome work factor!" but in the real world we see that companies want hashing run on powerful CPUs server-side, with the capability to offline-upgrade hashes over time.

The final nail in any client-side hashing approach is when the claims start to cross over to "server never sees the password". I don't see this as a valid argument any time the code to perform the hashing is being sent over the same trusted connection from the server itself. There are corner cases, for example if you are shipping compiled and signed code which is hashing client-side, or if you take special precautions with browser plugins to ensure you are running a specific build of the Javascript, but that simply doesn't apply to 99.99% of use cases, and so I personal dismiss those benefits as not likely to be achieved in the real-world. What we're left with is technically a sub-optimal user experience with additional trade-offs. So in conclusion, I am currently quite bearish on client-side hashing on the web in general.

[1] - https://github.com/tonyg/js-scrypt
[2] - https://dl.dropboxusercontent.com/u/42286273/site/test_scryp...