3

From what I read here: How to securely hash passwords?, you're supposed to iterate the hashing. However in the implementation of BCrypt for .Net uses the workfactor (2^wf iterations) in the 'GenerateSalt' function. Since the salt isn't supposed to be secret, what's the point in spending cpu power into generating some random string?

Did I missunderstand the concept of a salt? A random string that you append to the password you want to hash, to make the hash for the same password with different salts different?

FalcoGer
  • 402
  • 3
  • 9
  • I remember having the same question and having figured it out... but I don't remember the answer :(. You're completely right: the iterations should happen in the password hashing step and not in the salt generation step. It could be that they store only the initial salt, so the checking function is like this: `$salt=read_db(); repeat(9001){$salt=hash($salt);} return hash($salt+$password)`. In that case, an attacker can compute the salt once and then check passwords really fast... so a slow salt is pointless. (So it probably doesn't do that.) Either it's insecure, or the documentation is wrong. – Luc Feb 01 '19 at 10:32
  • BCrypt does not work like you'd expect a hash function to work and does rather iterations on encryption. Thus, the salt does have function that slightly differs from a salt in a hash function. – Tobi Nary Feb 01 '19 at 11:22
  • What would you recommend a good workfactor to be? 500ms on the server? 5 seconds? We get a lot of logins (dozens) in the morning. At the one end if you assume the attacker takes as long as the server you get 10 password tries per user per second with 100ms. that seems very slow. is that enough? on the other hand users can wait 100ms for the login no problem. – FalcoGer Feb 01 '19 at 16:05
  • Note that it does make sense to look for a very fast implementation in your case. Preferably something using native code. What's acceptable depends on your situation and how much CPU power you can throw at it (bcrypt itself cannot be parallelized but multiple calls to bcrypt certainly can). – Maarten Bodewes Feb 01 '19 at 16:21

2 Answers2

2

The 'Salt' that the generateSalt-function generates seems to be a combination of different parameters chained together with '$' followed by the encoded salt.

For example $2a$14$uJFVYDcsSqo.eofbk/xmAu Where the 2a seems to be the version, and the 14 seems to be the number of iterations. Then in the next step when generating the hash it uses the argument from the string that the generateSalt-function creates as the iteration count in the hash function.

I find it a bit odd to pass arguments like that in the salt string argument of the hash function.

Ref: https://github.com/BcryptNet/bcrypt.net/issues/26

FalcoGer
  • 402
  • 3
  • 9
  • It doesn't make any kind of sense to me. The salt is just that, 16 bytes. It has been clearly defined for bcrypt. Calling it anything else is ignoring the definition of bcrypt, so it is wrong, period. The finished hash output does **not** contain 29B of salt. The salt does not consist of *characters* / delimiters. And the *encoding* of the hash is not the same thing as the hash itself - this is an important distinction that you should learn, otherwise it will bite you. You've explained what you see, but the answer still shows a lack of understanding of the underlying concepts, unfortunately. – Maarten Bodewes Feb 02 '19 at 11:30
  • 1
    My bad. That's just what the generate salt function spits at me. It's not a byte array, but a string, it is 29B long and it contains the version number and the number of iterations, which to my limited knowledge is not part of the salt. I am aware that it's encoded and would probably contain nonprintable control characters that would likely mess with or complicate matters with whatever datastorage you decide to you. I'm a programer and I was confused about why the generate salt function takes the workfactor, not the actual hash function. It's a nice blackbox, 3 functions: Salt, hash, verify. – FalcoGer Feb 04 '19 at 09:07
  • OK, let's leave the answer as it does nicely explain what the code does. As long as it is clear that the API doesn't follow the standard & conventions then it may become handy for any other developer looking for an explanation. – Maarten Bodewes Feb 04 '19 at 11:27
1

From what I read here: How to securely hash passwords?, you're supposed to iterate the hashing.

You didn't understand that correctly. The password hashing requires a work factor to add some security to otherwise insecure passwords, as passwords commonly do not contain enough randomness to be be secure.

bcrypt internally uses a block cipher. But it repeats the key setup rather than the block encrypt itself to implement a work factor. Without it you cannot encrypt the bcrypt specific string, so the work needs to be performed before you can compare the password hash. As bcrypt internally doesn't use a (cryptographic) hash at all, it would be kind-of hard to repeat it.

In other words, bcrypt is a password hash, it doesn't use a hash - it uses a block cipher called Blowfish.

Other password hashes (or Password Based Key Derivation Functions) such as PBKDF(1) and PBKDF2 do use a repeated hash, or rather they repeat a hash based MAC (HMAC). scrypt in turn is build upon PBKDF2, so it also depends on hashing.

However in the implementation of BCrypt for .Net uses the workfactor (2^wf iterations) in the 'GenerateSalt' function.

That would be weird. Generating the salt is just getting 16 bytes from a (secure) random number generator. It is the key setup that is repeated, and that also requires the password as input.

It is quite common for developers to mess up their design or method naming, so I'm not saying that you're wrong stating this.

Did I missunderstand the concept of a salt? A random string that you append to the password you want to hash, to make the hash for the same password with different salts different?

You understood the function of the salt just fine. The salt is also used to avoid rainbow table attacks though, so just avoiding the same password to create the same password hash in the normal password hash table or tables is just one function of it.

However, how the salt is used within the password hash depends on the scheme used. For bcrypt the salt is encrypted into the state of the key setup, so it is never put in front of the password. To put it in front of the password would be a good idea if a hash was used internally by bcrypt, but we've already established that that isn't the case.


The salt is just a binary string of 16 random bytes, e.g. from Wikipedia:

salt: array of Bytes (16 bytes)

you're confusing the hash string with the salt itself; the salt is just a 22-character portion of the hash string, with the salt bytes encoded using radix 64, e.g. the Wikipedia example:

 $2a$10$N9qo8uLOickgx2ZMRZoMyeIjZAgcfl7p92ldGxad68LJZdL17lhWy
        ^    salt encoding   ^^    password hash encoding   ^

This encodes the 16 byte salt in a password hash radix 64 encoding. The password hash itself is another radix 64 string simply stuck onto the end of the salt string. The salt is decoded back to bytes during verification; the characters are just a textual representation of the 16 random bytes.

If the random bytes of the salt were directly put into a text file, the file and / or the salt would become corrupted (as control characters such as End-Of-File - EOF - are completely valid for the salt). So the radix 64 encoding is used to represent the bytes as text; this is also called an ASCII armor as it protects the byte values against processes handling the text file.

Maarten Bodewes
  • 4,602
  • 15
  • 29
  • I see. The generate salt function returns the $2a$10$ portion as well, which isn't actually part of the salt as is. Thank you for your clear answer. – FalcoGer Feb 01 '19 at 16:03
  • Yep, you are getting it :) – Maarten Bodewes Feb 01 '19 at 16:08
  • But why would the salt need to be from a cryptographically secure random generator? It's public anyway, or at least not secret. Couldn't any old garbage of byte data do the trick? I mean why random at all? couldn't you just start at 0x000000...01 and count your way up as you generate more salts? the only thing that matters is that it's different for every user, right? – FalcoGer Feb 01 '19 at 16:11
  • That would only work if there was one generator which keeps state (e.g. a counter). Using a large enough fully random salt makes sure you won't repeat the salt on any server, without keeping state. People should use unique passwords especially for different services, but they don't. – Maarten Bodewes Feb 01 '19 at 16:13