How does bcrypt work?

1

I do not quite understand how salt of bcrypt works, in a javascript (node) code:

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 it to create the hash of the password, then I check the password using the compare function, but in it is not passed the salt created previously, as then bcrypt makes this comparison, since the value of the salt will interfere with the final hash of the password?

    
asked by anonymous 30.05.2018 / 02:30

1 answer

1

% w / w includes the salt used. I practically answered this in another question , but since the question was not specifically about it, then I think I can answer here also.

The same way that "works on Node" is the same that 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 any existing KDF. BCrypt has three parameters:

  • Computational cost.
  • Salt.
  • Password.
  

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

As a result you will have:

  • "Hash".

However, the first two values are public, in the case of BCrypt. Therefore, BCrypt itself uses a format similar to the 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 of:

$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 hashPassword 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.hash , bcrypt.compare does the same as crypto.timingSafeEqual , however it is safe against timing-attack. That's basically what === does, when you include bcrypt.compare it already has the required data. :)

    
30.05.2018 / 03:12