4

Recently I got into passwords hashing (SHA256, HMAC-SHA1, PBKDF2, etc.) and one "statement" I've been reading over and over again, stuck in my mind and I can not find a satisfying answer.

"When we say “Guid” above, we mean “the 16-byte array representing a Guid” – not “Guid string form”." from (http://securitydriven.net/)

What is the difference behind using GUID as a salt as the 16 bytes array it is (.NET) and concating a GUID in string format (which in UTF8 is 36 bytes) to the plain-text password and initialize the hashing process afterwards? And why is the first mentioned considered more secure?

4

Simply put, you want to maximize the amount of entropy for a given length of data, when doing most cryptographic tasks.

You have (at most) 16 bytes of entropy in your GUID - probably less (some of the bits are deterministic), but we can ignore that detail for the current discussion. When using the GUID's byte[] representation as a salt, you have 16 bytes of data. That's at most 16 bytes of entropy / 16 bytes of data = 1 byte of entropy per byte of data, assuming a completely random GUID. Now assume that you use the string representation. That's 36 bytes of data, but still at most 16 bytes of entropy. That results in less than half the entropy per byte of data (about a 0.44 entropy/data ratio compared to the compact byte[]), which is problematic for some hashing algorithms.

When might this come up?

To take a contrived example (just off the top of my head - I would not actually recommend doing this), consider bcrypt. bcrypt is a password hashing algorithm which, for technical reasons, limits the password to 72 bytes. It does include its own salt, but let's suppose for the sake of argument that you don't trust its salt (again, this is not advice, just an example).

So, you decide to prefix the user's password with a GUID, before feeding it into bcrypt. If you've used the byte[] version, that shortens the effective upper limit on password length by 16 bytes. So, the user can supply a 56 byte password, at most. That's fine - that's still more than 50 ASCII characters, which is a common length people use when they've got a password manager and are a bit paranoid.

Now, let's suppose you had used the string version instead. You've still got the same amount of entropy, but now, you eat up 36 bytes that the user could have been using to pad out their password. They're now stuck with a 36 byte password at most.

And that's just an example off the top of my head; I'm sure there are other cases where using the string version would hurt you.

  • I'm not disputing your conclusion (I agree with it, actually) but you really need to explain how you come up with "That's at most 16 / 16 = 1 byte of entropy per byte of data, assuming a completely random GUID". If the GUID was completely random, it would be 8 bit of entropy per byte so, you'r 1/16 ratio needs to be explained somehow (clearly, each byte of a Windows GUID does not have only two possible random values so I'm wondering where you got your number from) – Stephane Jul 21 '15 at 7:23
  • Ah, sorry - I meant that it was "16 bytes of entropy / 16 bytes of data". I'll clarify that in the answer. – Ethan Kaminski Jul 21 '15 at 9:15
2

Salt is not really about entropy. What salts must achieve is uniqueness: as much as possible, you must try never to reuse a salt value (even if changing the password for the same user). GUID are nice for that. Randomness is a nice method to achieve that kind of uniqueness with overwhelming probability (it is "nice" because it does not need any kind of worldwide directory).

For uniqueness, GUID-encoded-as-bytes and GUID-encoded-as-string are completely equivalent since conversion between the two encodings works both ways: two GUID that are encoded as distinct sequences of bytes will also be encoded as distinct strings, and vice versa. It does not matter that one encoding has "more entropy per character" or any such notion, because entropy is irrelevant for salts. (Or, said otherwise, when entropy concentration matters for a salt, then it is not a salt, but something else.)

However, existing password hashing algorithms and file formats can be picky about salts. For instance, the article that specifies bcrypt(*) says that the salt has length exactly 128 bits, so, presumably, if you try to feed a bcrypt implementation with a 36-character strings, bad things may happen (e.g. the implementation only uses the first 16 characters, ignoring the rest). The uniqueness of GUID is ensured only when the complete GUID is used, in the encoding that you chose.

On a more practical note, since both encodings achieve the same uniqueness, you may as well prefer to use the shorter of the two, to save some space in your database. Be wary, though, of any poor handling of binary data (e.g. using bytes as if they were a string which ends at the first byte of value 0). Conversely, since most password hashing algorithms expect bytes for the salt, you must take care to specify an unambiguous encoding if you want to store salts as character strings.

(*) While the article specifies bcrypt, the bcrypt authors tend to consider that the real bcrypt specification is the reference implementation; in that sense, the real acceptable salt length for bcrypt is "whatever the reference implementation accepts to use".

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.