Question

I'm using C# and BCrypt.Net to hash my passwords.

For example:

string salt = BCrypt.Net.BCrypt.GenerateSalt(6);
var hashedPassword = BCrypt.Net.BCrypt.HashPassword("password", salt);

//This evaluates to True. How? I'm not telling it the salt anywhere, nor
//is it a member of a BCrypt instance because there IS NO BCRYPT INSTANCE.
Console.WriteLine(BCrypt.Net.BCrypt.Verify("password", hashedPassword));
Console.WriteLine(hashedPassword);

How is BCrypt verifying the password with the hash if it's not saving the salt anywhere. The only idea I have is that it's somehow appending the salt at the end of the hash.

Is this a correct assumption?

Was it helpful?

Solution

How is BCrypt verifying the password with the hash if it's not saving the salt anywhere?

Clearly it is not doing any such thing. The salt has to be saved somewhere.

Let's look up password encryption schemes on Wikipedia. From http://en.wikipedia.org/wiki/Crypt_(Unix) :

The output of the function is not merely the hash: it is a text string which also encodes the salt and identifies the hash algorithm used.

Alternatively, an answer to your previous question on this subject included a link to the source code. Rather than asking the internet to read the source code for you, you could always choose to read it yourself. That would probably get your answer faster. The relevant section of the source code is:

    StringBuilder rs = new StringBuilder();
    rs.Append("$2");
    if (minor >= 'a') {
        rs.Append(minor);
    }
    rs.Append('$');
    if (rounds < 10) {
        rs.Append('0');
    }
    rs.Append(rounds);
    rs.Append('$');
    rs.Append(EncodeBase64(saltBytes, saltBytes.Length));
    rs.Append(EncodeBase64(hashed,(bf_crypt_ciphertext.Length * 4) - 1));
    return rs.ToString();

Clearly the returned string is version information, followed by the number of rounds used, followed by the salt encoded as base64, followed by the hash encoded as base64.

OTHER TIPS

A BCrypt hash string looks like:

$2a$10$Ro0CUfOqk6cXEKf3dyaM7OhSCvnwM9s4wIX9JeLapehKK5YdLxKcm
$==$==$======================-------------------------------

Where

  • 2a: Algorithm Identifier (BCrypt, UTF8 encoded password, null terminated)
  • 10: Cost Factor (210 = 1,024 rounds)
  • Ro0CUfOqk6cXEKf3dyaM7O: OpenBSD-Base64 encoded salt (22 characters, 16 bytes)
  • hSCvnwM9s4wIX9JeLapehKK5YdLxKcm: OpenBSD-Base64 encoded hash (31 characters, 24 bytes)

Edit: i just noticed these words fit exactly. i had to share:

$2a$10$TwentytwocharactersaltThirtyonecharacterspasswordhash
$==$==$======================-------------------------------

BCrypt does create a 24-byte binary hash, using 16-byte salt. You're free to store the binary hash and the salt however you like; nothing says you have to base-64 encode it into a string.

But BCrypt was created by guys who were working on OpenBSD. OpenBSD already defines a format for their password file:

$[HashAlgorithmIdentifier]$[AlgorithmSpecificData]

This means that the "bcrypt specification" is inexorably linked to the OpenBSD password file format. And whenever anyone creates a "bcrypt hash" they always convert it to an ISO-8859-1 string of the format:

$2a$[Cost]$[Base64Salt][Base64Hash]

A few important points:

  • 2a is the alogithm identifier
    • 1: MD5
    • 2: early bcrypt, which had confusion over which encoding passwords are in (obsolete)
    • 2a: current bcrypt, which specifies passwords as UTF-8 encoded
  • Cost is a cost factor used when computing the hash. The "current" value is 10, meaning the internal key setup goes through 1,024 rounds
    • 10: 210 = 1,024 iterations
    • 11: 211 = 2,048 iterations
    • 12: 212 = 4,096 iterations
  • the base64 algorithm used by the OpenBSD password file is not the same Base64 encoding that everybody else uses; they have their own:

    Regular Base64 Alphabet: ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/
        BSD Base64 Alphabet: ./ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789
    

    So any implementations of bcrypt cannot use any built-in, or standard, base64 library


Armed with this knowledge, you can now verify a password correctbatteryhorsestapler against the saved hash:

$2a$12$mACnM5lzNigHMaf7O1py1O3vlf6.BA8k8x3IoJ.Tq3IB/2e7g61Km
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top