obfuz/Plugins/dnlib/DotNet/AssemblyHash.cs

112 lines
3.6 KiB
C#

// dnlib: See LICENSE.txt for more info
using System;
using System.IO;
using System.Security.Cryptography;
namespace dnlib.DotNet {
/// <summary>
/// Hashes some data according to a <see cref="AssemblyHashAlgorithm"/>
/// </summary>
readonly struct AssemblyHash : IDisposable {
readonly HashAlgorithm hasher;
/// <summary>
/// Constructor
/// </summary>
/// <remarks>If <paramref name="hashAlgo"/> is an unsupported hash algorithm, then
/// <see cref="AssemblyHashAlgorithm.SHA1"/> will be used as the hash algorithm.</remarks>
/// <param name="hashAlgo">The algorithm to use</param>
public AssemblyHash(AssemblyHashAlgorithm hashAlgo) =>
hasher = hashAlgo switch {
AssemblyHashAlgorithm.MD5 => MD5.Create(),
AssemblyHashAlgorithm.SHA_256 => SHA256.Create(),
AssemblyHashAlgorithm.SHA_384 => SHA384.Create(),
AssemblyHashAlgorithm.SHA_512 => SHA512.Create(),
_ => SHA1.Create(),
};
/// <inheritdoc/>
public void Dispose() {
if (hasher is not null)
((IDisposable)hasher).Dispose();
}
/// <summary>
/// Hash data
/// </summary>
/// <remarks>If <paramref name="hashAlgo"/> is an unsupported hash algorithm, then
/// <see cref="AssemblyHashAlgorithm.SHA1"/> will be used as the hash algorithm.</remarks>
/// <param name="data">The data</param>
/// <param name="hashAlgo">The algorithm to use</param>
/// <returns>Hashed data or null if <paramref name="data"/> was <c>null</c></returns>
public static byte[] Hash(byte[] data, AssemblyHashAlgorithm hashAlgo) {
if (data is null)
return null;
using (var asmHash = new AssemblyHash(hashAlgo)) {
asmHash.Hash(data);
return asmHash.ComputeHash();
}
}
/// <summary>
/// Hash data
/// </summary>
/// <param name="data">Data</param>
public void Hash(byte[] data) => Hash(data, 0, data.Length);
/// <summary>
/// Hash data
/// </summary>
/// <param name="data">Data</param>
/// <param name="offset">Offset</param>
/// <param name="length">Length</param>
public void Hash(byte[] data, int offset, int length) {
if (hasher.TransformBlock(data, offset, length, data, offset) != length)
throw new IOException("Could not calculate hash");
}
/// <summary>
/// Hash stream data
/// </summary>
/// <param name="stream">Stream</param>
/// <param name="length">Number of bytes to hash</param>
/// <param name="buffer">Temp buffer</param>
public void Hash(Stream stream, uint length, byte[] buffer) {
while (length > 0) {
int len = length > (uint)buffer.Length ? buffer.Length : (int)length;
if (stream.Read(buffer, 0, len) != len)
throw new IOException("Could not read data");
Hash(buffer, 0, len);
length -= (uint)len;
}
}
/// <summary>
/// Computes the hash
/// </summary>
public byte[] ComputeHash() {
hasher.TransformFinalBlock(Array2.Empty<byte>(), 0, 0);
return hasher.Hash;
}
/// <summary>
/// Creates a public key token from the hash of some <paramref name="publicKeyData"/>
/// </summary>
/// <remarks>A public key is hashed, and the last 8 bytes of the hash, in reverse
/// order, is used as the public key token</remarks>
/// <param name="publicKeyData">The data</param>
/// <returns>A new <see cref="PublicKeyToken"/> instance</returns>
public static PublicKeyToken CreatePublicKeyToken(byte[] publicKeyData) {
if (publicKeyData is null)
return new PublicKeyToken();
var hash = Hash(publicKeyData, AssemblyHashAlgorithm.SHA1);
var pkt = new byte[8];
for (int i = 0; i < pkt.Length && i < hash.Length; i++)
pkt[i] = hash[hash.Length - i - 1];
return new PublicKeyToken(pkt);
}
}
}