mirror of
https://github.com/SabreTools/SabreTools.Hashing.git
synced 2026-07-20 07:45:13 +00:00
206 lines
5.7 KiB
C#
206 lines
5.7 KiB
C#
using System;
|
|
#if NET462_OR_GREATER || NETCOREAPP
|
|
using System.IO.Hashing;
|
|
#endif
|
|
#if NET35_OR_GREATER || NETCOREAPP
|
|
using System.Linq;
|
|
#endif
|
|
using System.Security.Cryptography;
|
|
using Aaru.Checksums;
|
|
using Aaru.CommonTypes.Interfaces;
|
|
|
|
namespace SabreTools.Hashing
|
|
{
|
|
/// <summary>
|
|
/// Wrapper for a single hashing algorithm
|
|
/// </summary>
|
|
public class HashWrapper : IDisposable
|
|
{
|
|
#region Properties
|
|
|
|
/// <summary>
|
|
/// Hash type associated with the current state
|
|
/// </summary>
|
|
#if NETFRAMEWORK || NETCOREAPP3_1
|
|
public HashType HashType { get; private set; }
|
|
#else
|
|
public HashType HashType { get; init; }
|
|
#endif
|
|
|
|
/// <summary>
|
|
/// Current hash in bytes
|
|
/// </summary>
|
|
public byte[]? CurrentHashBytes
|
|
{
|
|
get
|
|
{
|
|
return _hasher switch
|
|
{
|
|
HashAlgorithm ha => ha.Hash,
|
|
IChecksum ic => ic.Final(),
|
|
#if NET462_OR_GREATER || NETCOREAPP
|
|
NonCryptographicHashAlgorithm ncha => ncha.GetCurrentHash().Reverse().ToArray(),
|
|
#endif
|
|
_ => null,
|
|
};
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Current hash as a string
|
|
/// </summary>
|
|
public string? CurrentHashString
|
|
{
|
|
get
|
|
{
|
|
return _hasher switch
|
|
{
|
|
IChecksum ic => ic.End(),
|
|
_ => ByteArrayToString(CurrentHashBytes),
|
|
};
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Private Fields
|
|
|
|
/// <summary>
|
|
/// Internal hasher being used for processing
|
|
/// </summary>
|
|
/// <remarks>May be either a HashAlgorithm or NonCryptographicHashAlgorithm</remarks>
|
|
private object? _hasher;
|
|
|
|
#endregion
|
|
|
|
#region Constructors
|
|
|
|
/// <summary>
|
|
/// Constructor
|
|
/// </summary>
|
|
/// <param name="hashType">Hash type to instantiate</param>
|
|
public HashWrapper(HashType hashType)
|
|
{
|
|
this.HashType = hashType;
|
|
GetHasher();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Generate the correct hashing class based on the hash type
|
|
/// </summary>
|
|
private void GetHasher()
|
|
{
|
|
_hasher = HashType switch
|
|
{
|
|
#if NET462_OR_GREATER || NETCOREAPP
|
|
HashType.CRC32 => new Crc32(),
|
|
HashType.CRC64 => new Crc64(),
|
|
#else
|
|
HashType.CRC32 => new OptimizedCRC.OptimizedCRC(),
|
|
#endif
|
|
HashType.MD5 => MD5.Create(),
|
|
HashType.SHA1 => SHA1.Create(),
|
|
HashType.SHA256 => SHA256.Create(),
|
|
HashType.SHA384 => SHA384.Create(),
|
|
HashType.SHA512 => SHA512.Create(),
|
|
HashType.SpamSum => new SpamSumContext(),
|
|
#if NET462_OR_GREATER || NETCOREAPP
|
|
HashType.XxHash32 => new XxHash32(),
|
|
HashType.XxHash64 => new XxHash64(),
|
|
HashType.XxHash3 => new XxHash3(),
|
|
HashType.XxHash128 => new XxHash128(),
|
|
#endif
|
|
_ => null,
|
|
};
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
public void Dispose()
|
|
{
|
|
if (_hasher is IDisposable disposable)
|
|
disposable.Dispose();
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Hashing
|
|
|
|
/// <summary>
|
|
/// Process a buffer of some length with the internal hash algorithm
|
|
/// </summary>
|
|
public void Process(byte[] buffer, int size)
|
|
{
|
|
switch (_hasher)
|
|
{
|
|
case HashAlgorithm ha:
|
|
ha.TransformBlock(buffer, 0, size, null, 0);
|
|
break;
|
|
|
|
case IChecksum ic:
|
|
ic.Update(buffer);
|
|
break;
|
|
|
|
#if NET462_OR_GREATER || NETCOREAPP
|
|
case NonCryptographicHashAlgorithm ncha:
|
|
var bufferSpan = new ReadOnlySpan<byte>(buffer, 0, size);
|
|
ncha.Append(bufferSpan);
|
|
break;
|
|
#else
|
|
case OptimizedCRC.OptimizedCRC oc:
|
|
oc.Update(buffer, 0, size);
|
|
break;
|
|
#endif
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Finalize the internal hash algorigthm
|
|
/// </summary>
|
|
/// <remarks>NonCryptographicHashAlgorithm implementations do not need finalization</remarks>
|
|
public void Terminate()
|
|
{
|
|
byte[] emptyBuffer = [];
|
|
switch (_hasher)
|
|
{
|
|
case HashAlgorithm ha:
|
|
ha.TransformFinalBlock(emptyBuffer, 0, 0);
|
|
break;
|
|
|
|
#if NET20_OR_GREATER || NETCOREAPP3_1 || NET5_0
|
|
case OptimizedCRC.OptimizedCRC oc:
|
|
oc.Update([], 0, 0);
|
|
break;
|
|
#endif
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Helpers
|
|
|
|
/// <summary>
|
|
/// Convert a byte array to a hex string
|
|
/// </summary>
|
|
/// <param name="bytes">Byte array to convert</param>
|
|
/// <returns>Hex string representing the byte array</returns>
|
|
/// <link>http://stackoverflow.com/questions/311165/how-do-you-convert-byte-array-to-hexadecimal-string-and-vice-versa</link>
|
|
private static string? ByteArrayToString(byte[]? bytes)
|
|
{
|
|
// If we get null in, we send null out
|
|
if (bytes == null)
|
|
return null;
|
|
|
|
try
|
|
{
|
|
string hex = BitConverter.ToString(bytes);
|
|
return hex.Replace("-", string.Empty).ToLowerInvariant();
|
|
}
|
|
catch
|
|
{
|
|
return null;
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
} |