Skip to content

Instantly share code, notes, and snippets.

@mikeobrien
Created June 10, 2011 02:59
Show Gist options
  • Save mikeobrien/1018166 to your computer and use it in GitHub Desktop.
Save mikeobrien/1018166 to your computer and use it in GitHub Desktop.
Managed implementation of SQL Server pwdencrypt function
public class SqlHashedPassword
{
private static readonly Random Random = new Random();
private static readonly SHA1CryptoServiceProvider Hash = new SHA1CryptoServiceProvider();
private static readonly byte[] Header = new byte[] {1, 0};
private readonly byte[] _hash;
private readonly byte[] _salt;
private SqlHashedPassword(byte[] salt, byte[] hash)
{
_hash = hash;
_salt = salt;
}
public static SqlHashedPassword FromHash(byte[] hash)
{
return new SqlHashedPassword(hash.Skip(2).Take(4).ToArray(), hash.Skip(6).Take(20).ToArray());
}
public static SqlHashedPassword Create(string password)
{
var salt = new byte[4];
Random.NextBytes(salt);
return Create(salt, password);
}
private static SqlHashedPassword Create(byte[] salt, string password)
{
return new SqlHashedPassword(salt, Hash.ComputeHash(EncodePassword(salt, password)));
}
public bool MatchesPassword(string password)
{
return Create(_salt, password)._hash.SequenceEqual(_hash);
}
public byte[] ToBytes()
{
return Header.Concat(_salt).Concat(_hash).ToArray();
}
private static byte[] EncodePassword(byte[] salt, string password)
{
return Encoding.ASCII.GetBytes(password).
Concat(password.Length % 2 == 1 ? new byte[] {0} : new byte[] {}).
Concat(salt).ToArray();
}
}
[TestFixture]
public class SqlHashedPasswordTests
{
private const string EvenLengthPassword = "yada";
private static readonly byte[] EvenLengthPasswordHash = new byte[]
{ 1, 0, 168, 79, 223, 13, 117, 114, 181, 0, 146, 247, 174, 27,
122, 120, 152, 194, 46, 147, 121, 128, 114, 241, 53, 90 };
private const string OddLengthPassword = "yada2";
private static readonly byte[] OddLengthPasswordHash = new byte[]
{ 1, 0, 232, 132, 155, 209, 92, 158, 1, 252, 148, 103, 204, 44,
201, 110, 129, 19, 199, 169, 32, 36, 16, 178, 242, 138 };
[Test]
public void Should_Hash_Plain_Text_Password()
{
SqlHashedPassword.Create(EvenLengthPassword).ToBytes().ShouldNotBeEmpty();
}
[Test]
public void Should_Generate_A_Different_Hash_For_The_Same_Even_Length_Password()
{
SqlHashedPassword.Create(EvenLengthPassword).ToBytes().ShouldNotEqual(SqlHashedPassword.Create(EvenLengthPassword).ToBytes());
}
[Test]
public void Should_Open_Hashed_Even_Length_Password()
{
SqlHashedPassword.FromHash(EvenLengthPasswordHash).ToBytes().ShouldEqual(EvenLengthPasswordHash);
}
[Test]
public void Same_Even_Length_Password_Should_Match_Hashed_Password()
{
SqlHashedPassword.FromHash(EvenLengthPasswordHash).MatchesPassword(EvenLengthPassword).ShouldBeTrue();
}
[Test]
public void Different_Even_Length_Password_Should_Not_Match_Hashed_Password()
{
SqlHashedPassword.FromHash(EvenLengthPasswordHash).MatchesPassword("badpassword").ShouldBeFalse();
}
[Test]
public void Should_Generate_A_Different_Hash_For_The_Same_Odd_Length_Password()
{
SqlHashedPassword.Create(OddLengthPassword).ToBytes().ShouldNotEqual(SqlHashedPassword.Create(OddLengthPassword).ToBytes());
}
[Test]
public void Should_Open_Hashed_Odd_Length_Password()
{
SqlHashedPassword.FromHash(OddLengthPasswordHash).ToBytes().ShouldEqual(OddLengthPasswordHash);
}
[Test]
public void Same_Odd_Length_Password_Should_Match_Hashed_Password()
{
SqlHashedPassword.FromHash(OddLengthPasswordHash).MatchesPassword(OddLengthPassword).ShouldBeTrue();
}
[Test]
public void Different_Odd_Length_Password_Should_Not_Match_Hashed_Password()
{
SqlHashedPassword.FromHash(OddLengthPasswordHash).MatchesPassword("badpassword").ShouldBeFalse();
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment