if time_since_last_attempt < 1 {
sleep(1)
}
key = sha256(password)
check(key)
But an attacker can just replicate the algorithm without the sleep call, since it's doesn't influence the actual process by which you obtain the encryption key from the password.The standard way to solve this is to use an inherently expensive process to turn the password into a key. In this case, it's something like:
tmp = password
for i in 0 to 10000 {
tmp = sha256(tmp)
}
check(tmp)
Barring some breakthrough attack on SHA-256, an attacker must perform 10000 hashes for every attempt. If they don't, they won't derive the right key and they won't be able to decrypt the data.Another approach is to use attack-resistant hardware which won't run the attacker's code and which has access to some hidden data which can be mixed in, and can't (easily) be extracted from the hardware. Then it looks like:
if time_since_last_attempt < 1 {
sleep(1)
}
hash = sha256(password)
key = encrypt(hidden_data, hash)
check(key)
Since the attacker can't obtain hidden_data, they can't run this code on their own hardware. Since the hardware doesn't accept the attacker's code, the attacker can't remove the sleep. This is how the iPhone's Secure Enclave works, and a less sophisticated version of this is why the FBI had so much trouble getting into that iPhone a while back even though it only had a four-digit passcode set.For example, this device, which was priced just 1300 USD tries
https://www.bitmaintech.com/productDetail.htm?pid=0002016091...
11.85T hashes per second, that's 1 with 13 zeroes per second and that shows what today is possible to achieve so cheap. Elcomsoft's software for previous, non-weak algorithm, managed to try 150000 passwords per second using the GPU, that is, this weakening is like giving an attacker some tens of thousands of computers for free, or tens of millions of computers for $1000.
See https://security.stackexchange.com/questions/62800/is-it-pos..., https://rya.nc/asic-cracking.html