#region Copyright 2010-2014 by Roger Knapp, Licensed under the Apache License, Version 2.0
/* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#endregion
using System;
using System.Collections.Generic;
using System.Security.Cryptography;
using System.IO;
using CSharpTest.Net.IO;
using System.Text;
using System.Collections;
using CSharpTest.Net.Bases;
using System.Security;
namespace CSharpTest.Net.Crypto
{
/// Creates a salted hash
public sealed class PasswordHash : Comparable, IDisposable
{
readonly SaltedData _hash;
/// Defines the derived hash iteration count used
public const int StandardIterations = 1024;
/// recreates a hash from a base-64 encoded string
public static PasswordHash FromString(string hash)
{ return FromBytes(Convert.FromBase64String(hash)); }
/// recreates a hash from the bytes returned by ToArray()
public static PasswordHash FromBytes(byte[] hash)
{ return new PasswordHash(new SaltedData(hash)); }
/// Recreates a hash
private PasswordHash(SaltedData hash)
{
_hash = hash;
}
/// Creates a salted hash from the given bytes and salt
public PasswordHash(Stream bytes, Salt salt)
{
using (bytes)
using (HashDerivedBytes hashBytes = new HashDerivedBytes(bytes, salt, StandardIterations))
_hash = new SaltedData(salt, hashBytes.GetBytes(32));
}
#region CTor overloads
/// Creates a salted hash from the given password
public PasswordHash(Password bytes)
: this(bytes.ReadBytes(), new Salt())
{ }
/// Creates a salted hash from the given password and salt
public PasswordHash(Password bytes, Salt salt)
: this(bytes.ReadBytes(), salt)
{ }
/// Creates the hash from the given bytes and salt
public PasswordHash(bool clear, byte[] bytes, Salt salt)
: this(new MemoryStream(bytes, 0, bytes.Length, false, false), salt)
{ if (clear) Array.Clear(bytes, 0, bytes.Length); }
/// Creates the hash from the given bytes
public PasswordHash(bool clear, byte[] bytes)
: this(clear, bytes, new Salt())
{ }
/// Creates the hash from the given data and salt
public PasswordHash(string data, Salt salt)
: this(true, Password.Encoding.GetBytes(data), salt)
{ }
/// Creates the hash from the given data and salt
public PasswordHash(string data)
: this(true, Password.Encoding.GetBytes(data), new Salt())
{ }
/// Creates the hash from the given data and salt
public PasswordHash(SecureString data, Salt salt)
: this(true, SecureStringUtils.ToByteArray(data, Password.Encoding), salt)
{ }
/// Creates the hash from the given data and salt
public PasswordHash(SecureString data)
: this(true, SecureStringUtils.ToByteArray(data, Password.Encoding), new Salt())
{ }
#endregion
/// Disposes of hash bytes
public void Dispose()
{ _hash.Dispose(); }
/// Gets the hash
protected override int HashCode { get { return BinaryComparer.GetHashCode(_hash.ToArray()); } }
/// Compares the hash
public override int CompareTo(PasswordHash other)
{
if (((object)other) == null)
return 1;
return BinaryComparer.Compare(_hash.ToArray(), other._hash.ToArray());
}
/// returns the salted-hash length in bytes
public int Length { get { return _hash.Length; } }
/// returns the salt used to create this hash
public Salt Salt { get { return _hash.Salt; } }
/// returns the salted-hash bytes
public byte[] ToArray() { return _hash.ToArray(); }
#region bool VerifyPassword(Password password)
/// Returns true if the provided password matches this hash
public bool VerifyPassword(Password password)
{
PasswordHash hash = password.CreateHash(Salt);
return this.Equals(hash);
}
/// Returns true if the provided password matches this hash
public bool VerifyPassword(Stream password)
{
PasswordHash hash = new PasswordHash(password, this.Salt);
return this.Equals(hash);
}
/// Returns true if the provided password matches this hash
public bool VerifyPassword(byte[] password)
{
using (Stream pb = new MemoryStream(password, 0, password.Length, false, false))
return VerifyPassword(pb);
}
/// Returns true if the provided password matches this hash
public bool VerifyPassword(string password)
{
byte[] tmp = Password.Encoding.GetBytes(password);
try { return VerifyPassword(tmp); }
finally { Array.Clear(tmp, 0, tmp.Length); }
}
#endregion
#region Object overrides
/// Returns the hash as a base-64 encoded string
public override string ToString()
{ return Convert.ToBase64String(_hash.ToArray()); }
#endregion
}
}