using System; using System.Security.Cryptography; namespace Hazel.Crypto { /// /// Streams data into a SHA256 digest /// public class Sha256Stream : IDisposable { /// /// Size of the SHA256 digest in bytes /// public const int DigestSize = 32; private SHA256 hash = SHA256.Create(); private bool isHashFinished = false; struct EmptyArray { public static readonly byte[] Value = new byte[0]; } /// /// Create a new instance of a SHA256 stream /// public Sha256Stream() { } /// /// Release resources associated with the stream /// public void Dispose() { this.hash?.Dispose(); this.hash = null; GC.SuppressFinalize(this); } /// /// Reset the stream to its initial state /// public void Reset() { this.hash?.Dispose(); this.hash = SHA256.Create(); this.isHashFinished = false; } /// /// Add data to the stream /// public void AddData(ByteSpan data) { while (data.Length > 0) { int offset = this.hash.TransformBlock(data.GetUnderlyingArray(), data.Offset, data.Length, null, 0); data = data.Slice(offset); } } /// /// Calculate the final hash of the stream data /// /// /// Target span to which the hash will be written /// public void CopyOrCalculateFinalHash(ByteSpan output) { if (output.Length != DigestSize) { throw new ArgumentException($"Expected a span of {DigestSize} bytes. Got a span of {output.Length} bytes", nameof(output)); } if (this.isHashFinished == false) { this.hash.TransformFinalBlock(EmptyArray.Value, 0, 0); this.isHashFinished = true; } new ByteSpan(this.hash.Hash).CopyTo(output); } } }