using System.Buffers; using System.IO.Hashing; using System.Security.Cryptography; using System.Text; using SPTarkov.DI.Annotations; namespace SPTarkov.Server.Core.Utils; [Injectable(InjectionType.Singleton)] public class HashUtil(RandomUtil _randomUtil) { public uint GenerateCrc32ForData(string data) { return Crc32.HashToUInt32(new ArraySegment(Encoding.UTF8.GetBytes(data))); } public uint GenerateCrc32ForData(ReadOnlySpan data) { return Crc32.HashToUInt32(data); } /// /// Generates a CRC32 hash for a file, reading in chunks using a pooled buffer to reduce allocations. /// /// The path to the file /// The CRC32 hash as a uint public async Task GenerateCrc32ForFileAsync(string filePath) { var crc = new Crc32(); await using var stream = File.OpenRead(filePath); // Rent from pool to avoid repeated allocations for each file read var buffer = ArrayPool.Shared.Rent(81920); try { int bytesRead; while ((bytesRead = await stream.ReadAsync(buffer)) > 0) { crc.Append(buffer.AsSpan(0, bytesRead)); } } finally { ArrayPool.Shared.Return(buffer); } return crc.GetCurrentHashAsUInt32(); } /// /// Create a hash for the data parameter /// /// algorithm to use to hash /// data to be hashed /// hash value /// thrown if the provided algorithm is not implemented /// > public string GenerateHashForData(HashingAlgorithm algorithm, string data) { switch (algorithm) { case HashingAlgorithm.MD5: var md5HashData = MD5.HashData(Encoding.UTF8.GetBytes(data)); return Convert.ToHexString(md5HashData).Replace("-", string.Empty); case HashingAlgorithm.SHA1: var sha1HashData = SHA1.HashData(Encoding.UTF8.GetBytes(data)); return Convert.ToHexString(sha1HashData).Replace("-", string.Empty); } throw new NotImplementedException($"Provided hash algorithm: {algorithm} is not supported."); } /// /// Create a hash for the data parameter asynchronously /// /// algorithm to use to hash /// data to be hashed /// A task which contains the hash value /// thrown if the provided algorithm is not implemented /// > public async Task GenerateHashForDataAsync(HashingAlgorithm algorithm, string data) { switch (algorithm) { case HashingAlgorithm.MD5: { await using var ms = new MemoryStream(Encoding.UTF8.GetBytes(data)); var md5HashData = await MD5.HashDataAsync(ms); return Convert.ToHexString(md5HashData).Replace("-", string.Empty); } case HashingAlgorithm.SHA1: { await using var ms = new MemoryStream(Encoding.UTF8.GetBytes(data)); var sha1HashData = await SHA1.HashDataAsync(ms); return Convert.ToHexString(sha1HashData).Replace("-", string.Empty); } } throw new NotImplementedException($"Provided hash algorithm: {algorithm} is not supported."); } /// /// Generates an account ID for a profile /// /// Generated account ID public int GenerateAccountId() { const int min = 1000000; const int max = 1999999; return _randomUtil.Random.Next(min, max + 1); } } public enum HashingAlgorithm { MD5, SHA1, }