1

I'm hashing my password with bcrypt (actually with password_compat since I run php 5.3.10)
I wanted to split the result string of the function into two parts: the salt used and the hash itself. (I know to use password_verify() to verify, well, the password. But I need the hash to use it as a key to encrypt a private key in a more broader security system.)

For a given password (abcdef) this is the result:

 $2y$10$ult68Ti4/zEWX4VQ       ....           YCOWjL6

I've altered the function a little bit, to spit out the concat, salt, hash and hash_format.

 ... from the password_compat ...
 $salt = substr($salt, 0, $required_salt_len);
 $hash = $hash_format . $salt;
 $ret = crypt($password, $hash);
 if (!is_string($ret) || strlen($ret) <= 13) {
        return false;
 }

 return array( 'concat'=>$ret, 
               'salt'=>$salt, 
               'format'=>$hash_format,
               'hash_format'=>$hash);

I thought the result-hash was a concat of the $hash_format, $salt and the hash... but the last character is different...

                                               _
[concat] =>        $2y$10$oWfFYcNqlcUeGwJM0AFUguSJ5t  .....  SvWG
[salt] =>                 oWfFYcNqlcUeGwJM0AFUgw
[hash_format] =>   $2y$10$oWfFYcNqlcUeGwJM0AFUgw
[format] =>        $2y$10$
                                               ^

As you can see the last character is different in the salt before the crypt function and after the function.

How is this possible?

stUrb
  • 6,612
  • 8
  • 43
  • 71
  • Why are you abusing a password hashing library? – PeeHaa Jan 04 '14 at 20:32
  • Perhaps a better suited library is https://github.com/ircmaxell/RandomLib – PeeHaa Jan 04 '14 at 20:34
  • 1
    Well I'm not abusing it... But using it in a broader password-derived encryption sheme: http://security.stackexchange.com/a/48085/36643 – stUrb Jan 04 '14 at 20:42
  • @stUrb: then why not use a Key Derivation Function (KDF) like PBKDF2 instead? An algorithm that's specifically designed for generating encryption keys from passwords... BCrypt is designed for password storage. PBKDF2+SHA256|512 would be an excellent choice for this... – ircmaxell Jan 06 '14 at 02:10

1 Answers1

14

The reason why the salt passed to crypt() can differ from the salt returned in the resulting hash-value is, that BCrypt internally only uses 126 bits of the salt, but the salt passed to the function always contains 128 bits. Since the crypt function expects the salt kind of base64 encoded, you cannot pass 126 bits directly.

A more detailed answer you can find here: Why does crypt/blowfish generate the same hash with two different salts?

I understand that you want to use the real hash as a key to encrypt another key, you could extract it from the resulting hash-value. This string is always of a certain format with $ to separate the parts, with BCrypt the real hash are the last 31 characters

$2y$10$nOUIs5kJ7naTuTFkBy1veuK0kSxUFXfuaOKdOKf9xYT0KKIGSJwFa
 |  |  |                     |
 |  |  |                     hash-value = K0kSxUFXfuaOKdOKf9xYT0KKIGSJwFa
 |  |  |
 |  |  salt = nOUIs5kJ7naTuTFkBy1veu (22 characters)
 |  |
 |  cost-factor = 10 = 2^10 iterations
 |
 hash-algorithm = 2y = BCrypt

Another possibility is to calculate a hash of the whole string (all 60 characters). You could choose an algorithm that returns the required length, e.g. sha256 to get a 256 bit key for MCRYPT_TWOFISH.

Community
  • 1
  • 1
martinstoeckli
  • 23,430
  • 6
  • 56
  • 87
  • Thanks for your answer! So that's why the salts differ 126 instead of 128 bits. BTW I can't use the whole string to generate the key as I then have to store the bcrypt string (and so the resulting key) to verify the login on the server; and that's what I'm trying to avoid. – stUrb Jan 04 '14 at 22:22
  • @stUrb - You would only have to store the sha256 of the bcrypt string, i don't understand why that should be more difficult than using the hash-part from the bcrypt string. Keep in mind that the bcrypt string is salted, so you cannot recreate the hash-part with only the original password (without storing the salt). – martinstoeckli Jan 04 '14 at 22:43
  • Ah I misunderstood your first comment, you're right. But If I have to verify the hash, then I would need to store the salt somewhere. And as its created automatically i would have to split the string anyway. This is the system I'm implementing... security.stackexchange.com/a/48085/36643 – stUrb Jan 04 '14 at 22:53
  • @stUrb - Indeed that's a very interesting question, i will study it for sure. For now, good luck! – martinstoeckli Jan 04 '14 at 23:05
  • 1
    Generally, you're right on the money. However, a minor (or not so) point: BCrypt uses a 128 bit salt. The 22nd character of the salt only provides 2 bits of salt (and 4 bits of hash). So you can't cleanly split it into "salt" and "hash". Practically as you have it here it will work, but be aware that you're mixing... – ircmaxell Jan 06 '14 at 02:19
  • @ircmaxell - Thank you for your clarification, it's really appreciated. I tried to confirm this in a small test, but had some difficulties. If character 22 really contains 4bits of the hash, then this character would change with different passwords, wouldn't it? Whatever i tried, i wasn't able to change this character, here is a small [test script](http://www.martinstoeckli.ch/salt_test.html) i wrote. – martinstoeckli Jan 06 '14 at 10:06