// dnlib: See LICENSE.txt for more info
using System;
using System.IO;
using System.Security.Cryptography;
namespace dnlib.DotNet {
///
/// Hashes some data according to a
///
readonly struct AssemblyHash : IDisposable {
readonly HashAlgorithm hasher;
///
/// Constructor
///
/// If is an unsupported hash algorithm, then
/// will be used as the hash algorithm.
/// The algorithm to use
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(),
};
///
public void Dispose() {
if (hasher is not null)
((IDisposable)hasher).Dispose();
}
///
/// Hash data
///
/// If is an unsupported hash algorithm, then
/// will be used as the hash algorithm.
/// The data
/// The algorithm to use
/// Hashed data or null if was null
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();
}
}
///
/// Hash data
///
/// Data
public void Hash(byte[] data) => Hash(data, 0, data.Length);
///
/// Hash data
///
/// Data
/// Offset
/// Length
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");
}
///
/// Hash stream data
///
/// Stream
/// Number of bytes to hash
/// Temp buffer
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;
}
}
///
/// Computes the hash
///
public byte[] ComputeHash() {
hasher.TransformFinalBlock(Array2.Empty(), 0, 0);
return hasher.Hash;
}
///
/// Creates a public key token from the hash of some
///
/// A public key is hashed, and the last 8 bytes of the hash, in reverse
/// order, is used as the public key token
/// The data
/// A new instance
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);
}
}
}