HashPassword changes existing salt
YOUR1 opened this issue · 1 comments
Hi,
I've encountered a bug where I'm using a predefined salt. See attached code:
public const string INPUT_PASSWORD = "abcdefg";
public const string INPUT_SALT = "1sHdZFnB7gGdMG0gTRO/kg==";
public static void Main() {
// Set salt with options
string saltWithOptions = String.Format("$2a$08${0}", INPUT_SALT);
// Generate hash
string BCryptHash = BCrypt.Net.BCrypt.HashPassword(INPUT_PASSWORD, saltWithOptions, false, HashType.None);
// Run regex
Regex re = new Regex("[$](?<version>2[abxy]?)[$](?<strength>(?<cost>(0[4-9]|[12][0-9]|3[01])))[$](?<password>((?<salt>[./0-9a-zA-Z]{22})(?<hash>[./0-9a-zA-Z]{31})))");
GroupCollection matches = re.Match(BCryptHash).Groups;
// Validate hash
if (matches.Count != 9)
throw new ApplicationException("Invalid BCrypt hash generated");
string hashVersion = matches["version"].Value;
string hashCost = matches["cost"].Value;
string hashSalt = matches["salt"].Value;
string hashHash = matches["hash"].Value;
byte[] hashBytes = encoding.GetBytes(hashHash);
Console.WriteLine("Got hash: " + BCryptHash);
Console.WriteLine(String.Format(" - Version: {0}", hashVersion));
Console.WriteLine(String.Format(" - Strength: {0}", hashCost));
Console.WriteLine(String.Format(" - Salt: {0}", hashSalt));
Console.WriteLine(String.Format(" - Hash: {0} ({1} bytes)", Base64UrlEncode(hashBytes), hashBytes.Count()));
Console.WriteLine("Salt matches original salt: " + (INPUT_SALT == hashSalt).ToString());
}
Console output:
Got hash: $2a$08$1sHdZFnB7gGdMG0gTRO/ke3gj9C9vhaGDE7bysgSivGBzWe5t3pPi
- Version: 2a
- Strength: 08
- Salt: 1sHdZFnB7gGdMG0gTRO/ke
- Hash: M2dqOUM5dmhhR0RFN2J5c2dTaXZHQnpXZTV0M3BQaQ (31 bytes)
Salt matches original salt: False
I expect to be the salt exactly the same as the input. But the last character of the input salt has changed from g to e.
Can somebody explain me why this is happening, and what's the best way to deal with this?
The salt1sHdZFnB7gGdMG0gTRO/kg==
is 24 characters; salts are only 22, so its truncated.
So the salt used becomes 1sHdZFnB7gGdMG0gTRO/kg
BCrypt the decodes the 22 char salt into a 16-byte value salt using BCrypt's base64 scheme decoder.
Byte16 - 222,226,95,108,122,67,246,34,31,56,141,162,85,52,1,154
The decoders equal, the encoder converts this back as 1sHdZFnB7gGdMG0gTRO/ke
This is the same true in all implementations.
If you have python; pip install bcrypt --user
import bcrypt
bcrypt.hashpw("abcdefg","$2a$08$1sHdZFnB7gGdMG0gTRO/kg==")
#OUTPUT: '$2a$08$1sHdZFnB7gGdMG0gTRO/ke3gj9C9vhaGDE7bysgSivGBzWe5t3pPi'
We could throw if the salt is too large to point out that what you're passing is being truncated but its not standard behaviour across other libraries; generating salts yourself is generally discouraged though across all major implementations.
| Name | Value in .net b64 representation | Type
-- | -- | -- | --
| Full "1sHdZFnB7gGdMG0gTRO/kg==" | "3uJfbHpD9iIfOI2iVTQBmg==" | string
| Truncated"1sHdZFnB7gGdMG0gTRO/kg" | "3uJfbHpD9iIfOI2iVTQBmg==" | string