#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.Text; using CSharpTest.Net.Bases; using CSharpTest.Net.Collections; using System.Security.Cryptography; using System.IO; namespace CSharpTest.Net.Crypto { /// Represents a comparable, sortable, hash code public sealed class Hash : Comparable { private static readonly byte[] Empty = new byte[0]; private static Hash Create(byte[] data) where T : HashAlgorithm, new() { using(T algo = new T()) return new Hash(algo.ComputeHash(data == null ? Empty : data)); } private static Hash Create(Stream data) where T : HashAlgorithm, new() { using (data) using (T algo = new T()) return new Hash(algo.ComputeHash(data == null ? Stream.Null : data)); } /// Computes an MD5 hash public static Hash MD5(byte[] bytes) { return Create(bytes); } /// Computes an MD5 hash public static Hash MD5(Stream bytes) { return Create(bytes); } /// Computes an SHA1 hash public static Hash SHA1(byte[] bytes) { return Create(bytes); } /// Computes an SHA1 hash public static Hash SHA1(Stream bytes) { return Create(bytes); } /// Computes an SHA256 hash public static Hash SHA256(byte[] bytes) { return Create(bytes); } /// Computes an SHA256 hash public static Hash SHA256(Stream bytes) { return Create(bytes); } /// Computes an SHA384 hash public static Hash SHA384(byte[] bytes) { return Create(bytes); } /// Computes an SHA384 hash public static Hash SHA384(Stream bytes) { return Create(bytes); } /// Computes an SHA512 hash public static Hash SHA512(byte[] bytes) { return Create(bytes); } /// Computes an SHA512 hash public static Hash SHA512(Stream bytes) { return Create(bytes); } /// Creates a comparable Hash object from the given hashcode bytes public static Hash FromBytes(byte[] bytes) { return new Hash((byte[])bytes.Clone()); } /// Creates a comparable Hash object from the base-64 encoded hashcode bytes public static Hash FromString(string encodedBytes) { return new Hash(Convert.FromBase64String(encodedBytes)); } private readonly byte[] _hashCode; private Hash(byte[] hashCode) { int sz = Check.NotNull(hashCode).Length; Check.Assert(sz == 16 || sz == 20 || sz == 32 || sz == 48 || sz == 64); _hashCode = hashCode; } /// /// Creates the hash algorithm associated with this length of hash /// public HashAlgorithm CreateAlgorithm() { return Check.IsAssignable(CryptoConfig.CreateFromName(AlgorithmName)); } /// /// If the hash provided is the same size as this hash both hash codes are feed back into /// the hash algorithm associated with this length of hash to produce the result value. /// If the hash provided is a different length, it is first hashed with this algorithm /// before the two values are combined. /// public Hash Combine(Hash other) { if (Length != other.Length) return Combine(other._hashCode); //We have two hash values of equal lenth, we can now easily combine them. HashAlgorithm alg = CreateAlgorithm(); alg.TransformBlock(_hashCode, 0, _hashCode.Length, _hashCode, 0); alg.TransformFinalBlock(other._hashCode, 0, _hashCode.Length); return FromBytes(alg.Hash); } /// /// Combines the bytes provided by first computing a like sized hash of those bytes and /// then combining the two equal hash values with the same hash algorithm. /// public Hash Combine(byte[] bytes) { return Combine(FromBytes(CreateAlgorithm().ComputeHash(Check.NotNull(bytes)))); } /// Returns the OID of the hash algorithm public string AlgorithmOID { get { return CryptoConfig.MapNameToOID(AlgorithmName); } } /// Returns the name of the hash algorithm public string AlgorithmName { get { switch (_hashCode.Length) { case 16: return ("MD5"); case 20: return ("SHA1"); case 32: return ("SHA256"); case 48: return ("SHA384"); case 64: return ("SHA512"); default: throw new ArgumentOutOfRangeException(); } } } /// Returns the length in bytes of the hash code public int Length { get { return _hashCode.Length; } } /// Returns a copy of the hash code bytes public byte[] ToArray() { return (byte[])_hashCode.Clone(); } /// Returns the hash code as a base-64 encoded string public override string ToString() { return Convert.ToBase64String(_hashCode); } /// Compares the hash codes and returns the result public override int CompareTo(Hash other) { return other == null ? 1 : BinaryComparer.Compare(_hashCode, other._hashCode); } /// Returns a hash of the hash code :) protected override int HashCode { get { return BinaryComparer.GetHashCode(_hashCode); } } } }