% 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:
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. :)