How does bcrypt work?

I did not understand very well the operation of salt of bcrypt , in javascript code (node):

const bcrypt = require('bcrypt');
const saltRounds = 10;

async function init(plainPassword) {
    let salt = await bcrypt.genSalt(saltRounds);
    console.log(salt);

    let hashPassword = await bcrypt.hash(plainPassword, salt);
    console.log(hashPassword);

    let check = await bcrypt.compare(plainPassword, hashPassword);
    console.log(check);
}
init('cba');

I create a salt and use to create the password hash, then I check the password using the function compare, but in it is not passed the previously created salt, how then bcrypt makes this comparison, since the value of salt will interfere with the final hash of the password?

Author: Costamilam, 2018-05-30

1 answers

hashPassword includes the salt used. I pretty much answered that in another question, but since the question wasn't specifically about that, so I guess I can answer it here as well.

the same way that "works on Node" is same as it works anywhere, since it implements the same BCrypt.


BCrypt is deterministic. For this reason it will have the same result if it is using the same salt, as well as any existing KDF. In BCrypt has three parameters:

  • computational cost.
  • jump.
  • password.

There are other derivation functions (KDFs) that have other input parameters. Argon2, for example, has individual cost parameters (for memory, iteration and etc), parameters output length parameters. On the other hand, HKDF has no computational cost, because it is not intended for passwords, but has hash length, from the result, flexible.

As a result will have:

  • "Hash".

However, the first two values are public, in the case of BCrypt. Therefore, BCrypt itself uses a format, similar to modular Crypt Format , where:

${Algoritmo}${Custo}${Salt}{Hash}

In this way the hash is included in values that make it possible to compare the same hash, since we know the algorithm, the cost, the salt and the expected result.


A small test, with a result from:

$2b$10$111111111111111111111u1Fg3CCqE4CYDkm0w0C9gxJ.HpXSNHlS

You can get the algorithm, cost, salt and "expected hash", so:

    let resultado = '$2b$10$111111111111111111111u1Fg3CCqE4CYDkm0w0C9gxJ.HpXSNHlS';

    let algo = resultado.substring(1, 3);
    let custo = resultado.substring(4, 6);
    let salt = resultado.substring(7, 29);

Then just do the bcrypt.hash and you can compare the value, usually:

const crypto = require('crypto');
const bcrypt = require('bcrypt');

async function init(plainPassword) {
    let resultado = '$2b$10$111111111111111111111u1Fg3CCqE4CYDkm0w0C9gxJ.HpXSNHlS';

    let algo = resultado.substring(1, 3);
    let custo = resultado.substring(4, 6);
    let salt = resultado.substring(7, 29);

    let hash = await bcrypt.hash(plainPassword, '$' + algo + '$' + custo + '$' + salt);

    console.log(crypto.timingSafeEqual(Buffer.from(hash), Buffer.from(resultado)));
}

init('cba');

See that there is no bcrypt.compare, crypto.timingSafeEqual does the same as ===, but is safe against timing-attack. That's basically what bcrypt.compare does, when you include hashPassword it already has the necessary data. :)

 2
Author: Inkeliz, 2018-05-30 01:12:13