Can Someone Explain How Bcrypt Verifies a Hash

Can someone explain how BCrypt verifies a hash?

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. 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.

Verifying a bcrypt hash?

The answer is that it's my fault. i gave you the wrong hash in the question for correctbatteryhorsestapler.

i picked a real hash; but not wanting to give away the password i changed it to a reference to XKCD.

i should have taken the extra moments to generate an actual hash for correctbatteryhorsestapler:

$2a$12$mACnM5lzNigHMaf7O1py1O3vlf6.BA8k8x3IoJ.Tq3IB/2e7g61Km

My fault for the confusion. In the original question i meant it as now you are ready to go out into the world, it wasn't meant to be an actual test vector.


You might want to look at some actual known test vectors:

('',                                   '$2a$06$DCq7YPn5Rq63x1Lad4cll.',    '$2a$06$DCq7YPn5Rq63x1Lad4cll.TV4S6ytwfsfvkgY8jIucDrjc8deX1s.'),
('', '$2a$08$HqWuK6/Ng6sg9gQzbLrgb.', '$2a$08$HqWuK6/Ng6sg9gQzbLrgb.Tl.ZHfXLhvt/SgVyWhQqgqcZ7ZuUtye'),
('', '$2a$10$k1wbIrmNyFAPwPVPSVa/ze', '$2a$10$k1wbIrmNyFAPwPVPSVa/zecw2BCEnBwVS2GbrmgzxFUOqW9dk4TCW'),
('', '$2a$12$k42ZFHFWqBp3vWli.nIn8u', '$2a$12$k42ZFHFWqBp3vWli.nIn8uYyIkbvYRvodzbfbK18SSsY.CsIQPlxO'),
('a', '$2a$06$m0CrhHm10qJ3lXRY.5zDGO', '$2a$06$m0CrhHm10qJ3lXRY.5zDGO3rS2KdeeWLuGmsfGlMfOxih58VYVfxe'),
('a', '$2a$08$cfcvVd2aQ8CMvoMpP2EBfe', '$2a$08$cfcvVd2aQ8CMvoMpP2EBfeodLEkkFJ9umNEfPD18.hUF62qqlC/V.'),
('a', '$2a$10$k87L/MF28Q673VKh8/cPi.', '$2a$10$k87L/MF28Q673VKh8/cPi.SUl7MU/rWuSiIDDFayrKk/1tBsSQu4u'),
('a', '$2a$12$8NJH3LsPrANStV6XtBakCe', '$2a$12$8NJH3LsPrANStV6XtBakCez0cKHXVxmvxIlcz785vxAIZrihHZpeS'),
('abc', '$2a$06$If6bvum7DFjUnE9p2uDeDu', '$2a$06$If6bvum7DFjUnE9p2uDeDu0YHzrHM6tf.iqN8.yx.jNN1ILEf7h0i'),
('abc', '$2a$08$Ro0CUfOqk6cXEKf3dyaM7O', '$2a$08$Ro0CUfOqk6cXEKf3dyaM7OhSCvnwM9s4wIX9JeLapehKK5YdLxKcm'),
('abc', '$2a$10$WvvTPHKwdBJ3uk0Z37EMR.', '$2a$10$WvvTPHKwdBJ3uk0Z37EMR.hLA2W6N9AEBhEgrAOljy2Ae5MtaSIUi'),
('abc', '$2a$12$EXRkfkdmXn2gzds2SSitu.', '$2a$12$EXRkfkdmXn2gzds2SSitu.MW9.gAVqa9eLS1//RYtYCmB1eLHg.9q'),
('abcdefghijklmnopqrstuvwxyz', '$2a$06$.rCVZVOThsIa97pEDOxvGu', '$2a$06$.rCVZVOThsIa97pEDOxvGuRRgzG64bvtJ0938xuqzv18d3ZpQhstC'),
('abcdefghijklmnopqrstuvwxyz', '$2a$08$aTsUwsyowQuzRrDqFflhge', '$2a$08$aTsUwsyowQuzRrDqFflhgekJ8d9/7Z3GV3UcgvzQW3J5zMyrTvlz.'),
('abcdefghijklmnopqrstuvwxyz', '$2a$10$fVH8e28OQRj9tqiDXs1e1u', '$2a$10$fVH8e28OQRj9tqiDXs1e1uxpsjN0c7II7YPKXua2NAKYvM6iQk7dq'),
('abcdefghijklmnopqrstuvwxyz', '$2a$12$D4G5f18o7aMMfwasBL7Gpu', '$2a$12$D4G5f18o7aMMfwasBL7GpuQWuP3pkrZrOAnqP.bmezbMng.QwJ/pG'),
('~!@#$%^&*() ~!@#$%^&*()PNBFRD', '$2a$06$fPIsBO8qRqkjj273rfaOI.', '$2a$06$fPIsBO8qRqkjj273rfaOI.HtSV9jLDpTbZn782DC6/t7qT67P6FfO'),
('~!@#$%^&*() ~!@#$%^&*()PNBFRD', '$2a$08$Eq2r4G/76Wv39MzSX262hu', '$2a$08$Eq2r4G/76Wv39MzSX262huzPz612MZiYHVUJe/OcOql2jo4.9UxTW'),
('~!@#$%^&*() ~!@#$%^&*()PNBFRD', '$2a$10$LgfYWkbzEvQ4JakH7rOvHe', '$2a$10$LgfYWkbzEvQ4JakH7rOvHe0y8pHKF9OaFgwUZ2q7W2FFZmZzJYlfS'),
('~!@#$%^&*() ~!@#$%^&*()PNBFRD', '$2a$12$WApznUOJfkEGSmYRfnkrPO', '$2a$12$WApznUOJfkEGSmYRfnkrPOr466oFDCaj4b6HY3EXGvfxm43seyhgC')

There are also tests that make sure that it doesn't fail on Unicode characters. There was a bug in an implementation somewhere that crashed when given UTF-8:

  • Password: ππππππππ Greek Small Letter Pi (U+03C0), eight repetitions

For which an actual hash is:

$2a$10$.TtQJ4Jr6isd4Hp.mVfZeuh6Gws4rOQ/vdBczhDx.19NFK0Y84Dle

BCrypt Verify stored password hash

Especially when it comes to encryption, you ought to have some general idea of the principles and concepts involved. Salted Password Hashing explains common pitfalls and makes a number of recommendations (one is BCrypt, so you may be on the right path).

It looks like you are not reading the stored hash from the DB before you verify. You don't show how it is saved, but that is important in order for it to verify.

 ' cuts down on dot operators
Imports BCryptor = BCrypt.Net.BCrypt

Create New Logon

' new user save
Dim sql = "INSERT INTO userlogin (email, username, pwhash) VALUES (@email, @n, @pw)"
' prep:
Dim salt = BCryptor.GenerateSalt(12) ' == 2^12
Dim hash = BCryptor.HashPassword(tbPass)

' to do: Try/Catch for an email that already exists
Using dbCon As New MySqlConnection(MySQLConnStr),
cmd As New MySqlCommand(sql, dbCon)

cmd.Parameters.Add("@email", MySqlDbType.Text).Value = tbEmail
cmd.Parameters.Add("@n", MySqlDbType.Text).Value = tbUserName
cmd.Parameters.Add("@pw", MySqlDbType.Text).Value = hash
dbCon.Open()
cmd.ExecuteNonQuery()

End Using

Verify an Attempt

Dim bRet As Boolean = False

' login user
Dim sql = "SELECT pwhash FROM userlogin WHERE email = @email"

Using dbCon As New MySqlConnection(MySQLConnStr),
cmd As New MySqlCommand(sql, dbCon)

' data for the where clause
cmd.Parameters.Add("@email", MySqlDbType.Text).Value = tbEmail

dbCon.Open()
Using rdr = cmd.ExecuteReader()
' read from the reader to load data
If rdr.Read() Then
' get the saved hash
Dim savedHash = rdr.GetString(0)
bRet = BCryptor.Verify(tbPass, savedHash)
Else
bRet = False
End If
End Using
' return whether the hash verified
Return ret
End Using

Notes

  • DbConnection, DbCommand and DbDataReader all implement Dispose which means they may very well allocate resources which need to be released. The code uses each of them in a Using block. This creates them at the start and disposes of them at the end of the Block.
  • This uses an email for the unique identifier because there are a lot of Steves out there. This means the SQL will return one record at most.
  • After loading the hashed pw from the DB, use it to verify the password attempt entered. Your code seems to be creating a new hash (and doesnt actually load anything from the DB).

The random salt originally generated when the account was created becomes part of the hash (as well as the work factor you used) as shown here:

Dim pw = "My!Weak#Pa$$word"
Dim salt = BCryptor.GenerateSalt(12)
Dim hash = BCryptor.HashPassword(pw, salt)

Console.WriteLine(salt)
Console.WriteLine(hash)

Output:

$2a$12$XPC20niJIhZPxaKvJkSUfO

$2a$12$XPC20niJIhZPxaKvJkSUfO/rwIetoScCze.tOcVS/aJzowvjpCPlq

The 12 after "$2a$" is the work factor.

Hashing token using bcrypt and verify it by hashing the secret string again

bcrypt returns different hashes every time you make a call. This is how bcrypt was designed, This help makes the hash secured.

please use the compare or use a different hashing algorithm.

Why is the hash generated by BCrypt non-deterministic

Is it salting the values internally

Yep. bcrypt is more than a raw hash function, it includes the salt and a few other bits to allow the hash to be validated without extra input:

$2a$12$q6r.MpvzPrUszrWLgaRdlOs04kPcjk0syCDelrzES9O8.UNlHON.u
^^ ^^ ^^^^^^^^^^^^^^^^^^^^^^
| | \- salt
| \---- work factor
\------- format

The API you're using doesn't expose it as you don't generally need to manipulate the salt, but it's there and you don't need to add your own.

Is BCrypt a good hashing algorithm to use in C#? Where can I find it?

First, some terms that are important:

Hashing - The act of taking a string and producing a sequence of characters that cannot be reverted to the original string.

Symmetric Encryption - (Usually just referred to as 'encryption') - The act of taking a string and producing a sequence of characters that can be decrypted to the original string through the use of the same encryption key that encrypted it.

Rainbow Table - a lookup table that contains all variations of characters hashed in a specific hashing algorithm.

Salt - a known random string appended to the original string before it is hashed.

For the .NET Framework, Bcrypt does not yet have a verified reference implementation. This is important because there's no way to know if there are serious flaws in an existing implementation. You can get an implementation of BCrypt for .NET here. I don't know enough about cryptography to say whether it's a good or bad implementation. Cryptography is a very deep field. Do not attempt to build your own encryption algorithm. Seriously.

If you are going to implement your own password security (sigh), then you need to do several things:

  1. Use a relatively secure hash algorithm.
  2. Salt each password before it's hashed.
  3. Use a unique and long salt for each password, and store the salt with the password.
  4. Require strong passwords.

Unfortunately, even if you do all this, a determined hacker still could potentially figure out the passwords, it would just take him a really long time. That's your chief enemy: Time.

The bcrypt algorithm works because it takes five orders of magnitude longer to hash a password than MD5; (and still much longer than AES or SHA-512). It forces the hacker to spend a lot more time to create a rainbow table to lookup your passwords, making it far less likely that your passwords will be in jeopardy of being hacked.

If you're salting and hashing your passwords, and each salt is different, then a potential hacker would have to create a rainbow table for each variation of salt, just to have a rainbow table for one salted+hashed password. That means if you have 1 million users, a hacker has to generate 1 million rainbow tables. If you're using the same salt for every user, then the hacker only has to generate 1 rainbow table to successfully hack your system.

If you're not salting your passwords, then all an attacker has to do is to pull up an existing Rainbow table for every implementation out there (AES, SHA-512, MD5) and just see if one matches the hash. This has already been done, an attacker does not need to calculate these Rainbow tables themselves.

Even with all this, you've got to be using good security practices. If they can successfully use another attack vector (XSS, SQL Injection, CSRF, et. al.) on your site, good password security doesn't matter. That sounds like a controversial statement, but think about it: If I can get all your user information through a SQL injection attack, or I can get your users to give me their cookies through XSS, then it doesn't matter how good your password security is.

Other resources:

  1. Jeff Atwood: .NET Encryption Simplified (great for an overview of hashing)
  2. Jeff Atwood: I just logged in as you
  3. Jeff Atwood: You're probably storing passwords incorrectly
  4. Jeff Atwood: Speed Hashing

Note: Please recommend other good resources. I've must have read a dozen articles by dozens of authors, but few write as plainly on the subject as Jeff does. Please edit in articles as you find them.

Zend\Crypt\Password\BCrypt verify method

Bcrypt hash has well documented structure, for example this hash:

$2y$10$aPk2mEEIkGonq6/JGr0OKOhYOdgomu61ARBjDLgb0UmHM4L8f7Hxe

String $2y$ is prefix, 10 is cost, aPk2mEEIkGonq6/JGr0OKO is salt (128-bit, base64 encoded 22 characters) and hYOdgomu61ARBjDLgb0UmHM4L8f7Hxe is resulting hash.

crypt function recognizes this format and use appropriate part of it as a salt, so there is no problem to pass whole hash as second parameter.



Related Topics



Leave a reply



Submit