I am migrating my legacy ASP.NET webforms application to start using the ASP.NET build-in DefaultMembershipProvider. My old homebrewn authenticationmodule used to store SHA256 hashes of passwords. I figured that 'upgrading' to the DefaultMembershipProvider, which also stores hashes in the SQL DB, would mean that I could migrate these hashes as well and therefore offer painless upgrade for current users (so they wouldn't have to reset their password).
Now, this didn't quite work as planned (obviously!) and I started to look into the source of the DefaultMembershipProvider (to be more specific, right here: https://github.com/wyxy2005/bluceNet/tree/master/System.Web.Providers) and I came accross the following function:
private string EncodePassword(string pass, int passwordFormat, string salt)
{
if (passwordFormat == 0)
{
return pass;
}
byte[] bytes = Encoding.Unicode.GetBytes(pass);
byte[] src = Convert.FromBase64String(salt);
byte[] inArray = null;
if (passwordFormat == 1)
{
HashAlgorithm hashAlgorithm = this.GetHashAlgorithm();
KeyedHashAlgorithm algorithm2 = hashAlgorithm as KeyedHashAlgorithm;
if (algorithm2 != null)
{
if (algorithm2.Key.Length == src.Length)
{
algorithm2.Key = src;
}
else if (algorithm2.Key.Length < src.Length)
{
byte[] dst = new byte[algorithm2.Key.Length];
Buffer.BlockCopy(src, 0, dst, 0, dst.Length);
algorithm2.Key = dst;
}
else
{
int num2;
byte[] buffer5 = new byte[algorithm2.Key.Length];
for (int i = 0; i < buffer5.Length; i += num2)
{
num2 = Math.Min(src.Length, buffer5.Length - i);
Buffer.BlockCopy(src, 0, buffer5, i, num2);
}
algorithm2.Key = buffer5;
}
inArray = algorithm2.ComputeHash(bytes);
}
else
{
byte[] buffer6 = new byte[src.Length + bytes.Length];
Buffer.BlockCopy(src, 0, buffer6, 0, src.Length);
Buffer.BlockCopy(bytes, 0, buffer6, src.Length, bytes.Length);
inArray = hashAlgorithm.ComputeHash(buffer6);
}
}
else
{
byte[] buffer7 = new byte[src.Length + bytes.Length];
Buffer.BlockCopy(src, 0, buffer7, 0, src.Length);
Buffer.BlockCopy(bytes, 0, buffer7, src.Length, bytes.Length);
inArray = this.EncryptPassword(buffer7, this.LegacyPasswordCompatibilityMode);
}
return Convert.ToBase64String(inArray);
}
Now, in my particular scenario I didn't have any password-salt as my old authentication module didn't use it so string salt
is empty, as is byte [] src
. The hashing algorithm that is used by default is HMACSHA256, which is a keyed hashfunction. So we get into this for
-loop where num2
is always 0. As num2
is also used as the incrementer, we enter an infinite loop.
Shouldn't the algorithm test for this particular condition? Shouldn't this have been an easy error, caught by simply UnitTesting the code? Or am I overlooking something?