I am trying to add a small proof of work function to protect a password from brute force attacks. The solution I have so far is based on the security iPhones have on their lock screen passcodes. I'm repeatedly hashing the password to achieve this.
import CryptoJS from 'crypto-js';
function sha256_digest(content) {
return CryptoJS.SHA256(content).toString(CryptoJS.enc.Hex);
}
function hash_n_times(content,n) {
let hash = content;
for (let _ = 0; _ < n; _++) { hash = sha256_digest(content); }
return hash;
}
let password = hash_n_times('Password12345',100000);
However, for iPhones, as I understand it, the way the password is stored is device specific and hardware dependent. So they only need to ensure that their work function would take time (like 80ms I heard) to compute on that particular hardware. On the other hand, my application is going to be used on the web so, I have no way to tie this to hardware, so I would like my work function to hold up (at least to some degree) to attacks from other hardware.
The issue is there seems to be a huge discrepancy between the hashing power of a mobile phone or PC compared to crypto mining hardware on the market. With my Mac on Chrome, I can get like 350K hashes in a second. Meanwhile the latest Antminers are doing over 100TH/s...
Assuming a password input that's 8 characters, case sensitive, and alphanumeric, to brute force every permutation it would take 62^8 attempts. Let's say I use 100K hashes so slow mobiles don't freeze up, a single Antminers could brute force it in 62^8 * 10^5 / 10^13
seconds or approx 25 days.
Users are pretty bad an generating random passwords, so that's really worst case, probably most passwords would get broken much faster. So I need a solution that is ideally a few orders of magnitude stronger.
Unfortunately due to hardware limitations, I can't really hash that much more on mobiles. Maybe there's a better operation than hashing or something to be done in addition to hashing? I can't really make users use longer and safer passwords because in practice they will forget them, and making a friendly password recovery system will reduce the effective security as a whole to that of the recovery system.
I also have a salt (and even pepper), but I'm not really sure it helps in this case against brute force on the password itself since the salt is usually stored in the database anyways, so if the attacker has the password hash, he also has the salt. For pepper, I'm looking at worst case where everything is compromised, so while I implement these, I would like to have a more robust foundation for my work function to be harder to intrinsically break.
How do I do this more effectively?