diff --git a/src/Blake3.Tests/Blake3StreamTests.cs b/src/Blake3.Tests/Blake3StreamTests.cs index 14563c4..6f8d437 100644 --- a/src/Blake3.Tests/Blake3StreamTests.cs +++ b/src/Blake3.Tests/Blake3StreamTests.cs @@ -1,4 +1,6 @@ +using System; using System.IO; +using System.Threading.Tasks; using NUnit.Framework; namespace Blake3.Tests @@ -56,5 +58,37 @@ public void TestHashWrite() blake3Stream.Write(HasherTests.BigData); AssertTextAreEqual(HasherTests.BigExpected, blake3Stream.ComputeHash().ToString()); } + + [Test] + public void TestHashReadSpan_PartialRead() + { + var data = HasherTests.SimpleData; + var expected = HasherTests.SimpleExpected; + + var stream = new MemoryStream(data); + using var blake3Stream = new Blake3Stream(stream); + + var oversizedBuffer = new byte[data.Length + 1024]; + var bytesRead = blake3Stream.Read(oversizedBuffer.AsSpan()); + + Assert.AreEqual(data.Length, bytesRead); + AssertTextAreEqual(expected, blake3Stream.ComputeHash().ToString()); + } + + [Test] + public async Task TestHashReadAsyncMemory_PartialRead() + { + var data = HasherTests.SimpleData; + var expected = HasherTests.SimpleExpected; + + var stream = new MemoryStream(data); + await using var blake3Stream = new Blake3Stream(stream); + + var oversizedBuffer = new byte[data.Length + 1024]; + var bytesRead = await blake3Stream.ReadAsync(oversizedBuffer.AsMemory()); + + Assert.AreEqual(data.Length, bytesRead); + AssertTextAreEqual(expected, blake3Stream.ComputeHash().ToString()); + } } } diff --git a/src/Blake3.Tests/HasherTests.cs b/src/Blake3.Tests/HasherTests.cs index 5f712a0..149d512 100644 --- a/src/Blake3.Tests/HasherTests.cs +++ b/src/Blake3.Tests/HasherTests.cs @@ -110,5 +110,25 @@ public void TestFinalizeWithOffset() Assert.True(bigHash.SequenceEqual(reconstructedHash.ToArray())); } + + [Test] + public void TestUpdateWithJoinEmptySpan() + { + using var hasher = Hasher.New(); + hasher.UpdateWithJoin(ReadOnlySpan.Empty); + var hash = hasher.Finalize(); + using var hasher2 = Hasher.New(); + var expected = hasher2.Finalize(); + Assert.AreEqual(expected, hash); + } + + [Test] + public void TestFinalizeWithNegativeOffset() + { + using var hasher = Hasher.New(); + hasher.Update(SimpleData); + var output = new byte[32]; + Assert.Throws(() => hasher.Finalize(-1L, output)); + } } } \ No newline at end of file diff --git a/src/Blake3/Blake3Stream.cs b/src/Blake3/Blake3Stream.cs index cc0285a..eef3a3b 100644 --- a/src/Blake3/Blake3Stream.cs +++ b/src/Blake3/Blake3Stream.cs @@ -101,7 +101,7 @@ public override async Task ReadAsync(byte[] buffer, int offset, int count, var length = await _stream.ReadAsync(buffer, cancellationToken); if (length > 0) { - _hasher.Update(buffer.Span); + _hasher.Update(buffer.Span.Slice(0, length)); } return length; } @@ -111,7 +111,7 @@ public override int Read(Span buffer) var length = _stream.Read(buffer); if (length > 0) { - _hasher.Update(buffer); + _hasher.Update(buffer.Slice(0, length)); } return length; } diff --git a/src/Blake3/Hash.cs b/src/Blake3/Hash.cs index db8a8b0..92de9db 100644 --- a/src/Blake3/Hash.cs +++ b/src/Blake3/Hash.cs @@ -6,6 +6,7 @@ using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +using System.Security.Cryptography; namespace Blake3; @@ -85,7 +86,7 @@ public static Hash FromBytes(ReadOnlySpan data) public bool Equals(Hash other) { - return this.AsSpan().SequenceCompareTo(other.AsSpan()) == 0; + return CryptographicOperations.FixedTimeEquals(AsSpan(), other.AsSpan()); } public override bool Equals(object obj) diff --git a/src/Blake3/Hasher.cs b/src/Blake3/Hasher.cs index 1aebdf0..d2ab5d2 100644 --- a/src/Blake3/Hasher.cs +++ b/src/Blake3/Hasher.cs @@ -183,7 +183,6 @@ public void Update(ReadOnlySpan data) where T : unmanaged /// public void UpdateWithJoin(ReadOnlySpan data) { - if (data == null) ThrowArgumentNullException(); if (_hasher == null) ThrowNullReferenceException(); fixed (void* ptr = data) { @@ -283,6 +282,7 @@ public void Finalize(ulong offset, Span hash) /// public void Finalize(long offset, Span hash) { + ArgumentOutOfRangeException.ThrowIfLessThan(offset, 0); Finalize((ulong)offset, hash); }