As explained in my previous question, I want to develop a scheme which would be able to increase the password hash strength without the user password.
According to Thomas Pornin it should be safe to chain BCrypt hashes to generate a new, more expensive hash. I disregarded the handling for the salt in the previous question so he warned to be careful about that.
To reiterate, I basically want to do the following:
bcrypt_hash_0 = bcrypt(pw, salt_0, work_factor_0); bcrypt_hash_1 = bcrypt(bcrypt_hash_0, salt_1, work_factor_1); ... bcrypt_hash_n = bcrypt(bcrypt_hash_n-1, salt_n, work_factor_n);
Possible Handling for the Salt
Let's assume I have a BCrypt function, where I can manually choose the salt.
salt_0 is the salt for the initial hash,
salt_1 the used salt for the chained hash which produces
bcrypt_hash_1 and so on.
Option 0: Reuse the Same Salt
The most simple approach is just to reuse the same salt. This makes it quite easy to handle but it is unclear if this approach introduces severe security issues (is there a research on that?)
Option 1: Create a New Salt
The naive fix to Option 0 would be to create a new random 16 byte salt for each iteration. Unfortunately, this increases the hash output message since in addition to
salt_0, the new
salt_1 must be appended (etc.). This is probably fine for a single chain, but using it to chain multiple hashes, the message will get quite impractical. From the security side, it should be safe, as it uses an unrelated new salt (?).
Option 2: Derive a New Salt from the Old One (CTR)
Assume the scheme allows for chaining up to 255 hashes. We already introduced HKDF to the scheme and could use the following simple derivation:
salt_n = hkdf_expand(salt_n-1, n, 16)
n is a counter; the n-th Bcrypt chained hash; by using
n as the info parameter we create a new salt for each iteration.
n be up to 255, do we "use up" some entropy? Would it be sound to say we lose 1 byte of the 16-byte source salt (aka
Option 2.1: Derive a New Salt from the Old One (CTR+Hash)
As an enhancement of the previous scheme, which would allow deriving all future salts from the source salt, we could incorporate the previous chained hash:
salt_n = hkdf_expand(salt_n-1, n | bcrypt_hash_n-1, 16)
I'm not sure this has some actual security benefits because a salt can be public. It would make it as expensive as calculating the hash to get the salt of the specific round basically.
Which of these schemes would be safe to use? The goal is to keep the output Bcrypt hash as small as possible, without compromising the security of BCrypt (in reasonable ways)