From ee2706aa19f0cb33f90c2b3f4ae7f283d35f7033 Mon Sep 17 00:00:00 2001 From: Ivan Tikhonov Date: Tue, 11 Aug 2026 13:35:07 +0300 Subject: [PATCH 1/4] undo IHashAlgorithm --- IT.Hashing.Benchmarks/Hash.cs | 4 +- IT.Hashing.Gost.Native/HashAlgorithms.cs | 28 ---------- .../IT.Hashing.Gost.Native.csproj | 2 +- .../Internal/SafeHashHandleImpl.cs | 25 --------- IT.Hashing.Gost/Gost3411_2012_256.cs | 30 +---------- IT.Hashing.Gost/Gost3411_2012_512.cs | 27 +--------- IT.Hashing.Gost/IHashAlgorithm.cs | 4 -- IT.Hashing.Gost/IT.Hashing.Gost.csproj | 2 +- IT.Hashing.Tests/Gost.cs | 53 +++++++++---------- 9 files changed, 31 insertions(+), 144 deletions(-) diff --git a/IT.Hashing.Benchmarks/Hash.cs b/IT.Hashing.Benchmarks/Hash.cs index 993e4ef..6e7b250 100644 --- a/IT.Hashing.Benchmarks/Hash.cs +++ b/IT.Hashing.Benchmarks/Hash.cs @@ -151,7 +151,7 @@ public byte[] IT_Bytes_XXH64() [Benchmark] public byte[] CryptoHives_GOST_256() => _streebog256.ComputeHash(_bytes); - [Benchmark] + //[Benchmark] public byte[] Open_GOST_256() => _streebog256o.ComputeHash(_bytes); [Benchmark] @@ -163,7 +163,7 @@ public byte[] IT_Bytes_XXH64() [Benchmark] public byte[] CryptoHives_GOST_512() => _streebog512.ComputeHash(_bytes); - [Benchmark] + //[Benchmark] public byte[] Open_GOST_512() => _streebog512o.ComputeHash(_bytes); //[Benchmark] diff --git a/IT.Hashing.Gost.Native/HashAlgorithms.cs b/IT.Hashing.Gost.Native/HashAlgorithms.cs index d63b6ff..9e89cd3 100644 --- a/IT.Hashing.Gost.Native/HashAlgorithms.cs +++ b/IT.Hashing.Gost.Native/HashAlgorithms.cs @@ -1,8 +1,5 @@ using IT.Hashing.Gost.Native.Internal; using System; -using System.Buffers; -using System.Buffers.Text; -using System.Diagnostics; namespace IT.Hashing.Gost.Native; @@ -59,8 +56,6 @@ private abstract class Resetable_Gost3411 : IHashAlgorithm public abstract int Size { get; } - public abstract int SizeInBase64 { get; } - protected Resetable_Gost3411() { _handle = CreateHandle(); @@ -87,23 +82,6 @@ public void Reset() public bool TryGetHash(Span hash, out int length) => _handle.TryGetHash(hash, out length); - public bool TryGetHashInBase64(Span destination, out int length) - { - length = SizeInBase64; - if (destination.Length < length) - return false; - - var isTrue = _handle.TryGetHash(destination, out var written); - Debug.Assert(isTrue); - Debug.Assert(written == Size); - - var status = Base64.EncodeToUtf8InPlace(destination, written, out written); - Debug.Assert(status == OperationStatus.Done); - Debug.Assert(written == length); - - return true; - } - protected abstract SafeHashHandleImpl CreateHandle(); } @@ -111,8 +89,6 @@ private class Resetable_Gost3411_94 : Resetable_Gost3411 { public override int Size => 32; - public override int SizeInBase64 => 44; - protected override SafeHashHandleImpl CreateHandle() => CryptoApiHelper.CreateHash_3411_94(_provider!); } @@ -121,8 +97,6 @@ private class Resetable_Gost3411_2012_256 : Resetable_Gost3411 { public override int Size => 32; - public override int SizeInBase64 => 44; - protected override SafeHashHandleImpl CreateHandle() => CryptoApiHelper.CreateHash_3411_2012_256(_provider!); } @@ -131,8 +105,6 @@ private class Resetable_Gost3411_2012_512 : Resetable_Gost3411 { public override int Size => 64; - public override int SizeInBase64 => 88; - protected override SafeHashHandleImpl CreateHandle() => CryptoApiHelper.CreateHash_3411_2012_512(_provider!); } diff --git a/IT.Hashing.Gost.Native/IT.Hashing.Gost.Native.csproj b/IT.Hashing.Gost.Native/IT.Hashing.Gost.Native.csproj index d8b39e6..9342fd0 100644 --- a/IT.Hashing.Gost.Native/IT.Hashing.Gost.Native.csproj +++ b/IT.Hashing.Gost.Native/IT.Hashing.Gost.Native.csproj @@ -13,7 +13,7 @@ true Ivan Tikhonov Ivan Tikhonov © 2026 - 2.0.3 + 2.0.4 Gost Hashing Native CryptoPro VipNet Readme.md Icon.png diff --git a/IT.Hashing.Gost.Native/Internal/SafeHashHandleImpl.cs b/IT.Hashing.Gost.Native/Internal/SafeHashHandleImpl.cs index 5b980d5..eb2e18d 100644 --- a/IT.Hashing.Gost.Native/Internal/SafeHashHandleImpl.cs +++ b/IT.Hashing.Gost.Native/Internal/SafeHashHandleImpl.cs @@ -1,6 +1,5 @@ using Microsoft.Win32.SafeHandles; using System; -using System.Buffers; using System.Buffers.Text; using System.Security; @@ -67,30 +66,6 @@ public bool TryGetHash(Span hash, out int length) return CryptoApiHelper.TryGetEndHashData(this, hash, out length); } - [SecurityCritical] - public bool TryGetHashInBase64(Span hash, out int length) - { - if (!CryptoApiHelper.TryGetEndHashData(this, hash, out length)) - { - length = Base64.GetMaxEncodedToUtf8Length(length); - return false; - } - - var status = Base64.EncodeToUtf8InPlace(hash, length, out var written); - if (status != OperationStatus.Done) - { - if (status == OperationStatus.DestinationTooSmall) - { - hash.Slice(0, length).Clear(); - length = Base64.GetMaxEncodedToUtf8Length(length); - return false; - } - throw new InvalidOperationException($"Status is {status}"); - } - length = written; - return true; - } - public void Reset() { throw new NotImplementedException(); diff --git a/IT.Hashing.Gost/Gost3411_2012_256.cs b/IT.Hashing.Gost/Gost3411_2012_256.cs index 5530edb..8481c13 100644 --- a/IT.Hashing.Gost/Gost3411_2012_256.cs +++ b/IT.Hashing.Gost/Gost3411_2012_256.cs @@ -1,8 +1,5 @@ using IT.Hashing.Gost.Internal; using System; -using System.Buffers; -using System.Buffers.Text; -using System.Diagnostics; using System.Runtime.CompilerServices; namespace IT.Hashing.Gost; @@ -13,8 +10,6 @@ public class Gost3411_2012_256 : Gost3411_2012_512 public override int Size => 32; - public override int SizeInBase64 => 44; - /// Thrown when the instance has been disposed. [MethodImpl(MethodImplOptionsEx.OptimizedLoop)] public override bool TryGetHash(Span destination, out int length) @@ -23,30 +18,7 @@ public override bool TryGetHash(Span destination, out int length) if (destination.Length < length) return false; - Span hash = stackalloc ulong[BlockSizeWords]; - HashFinal(hash); - - BinarySpans.WriteUInt64LittleEndian(hash.Slice(HalfBlockSizeWords, HalfBlockSizeWords), destination); - - return true; - } - - /// Thrown when the instance has been disposed. - [MethodImpl(MethodImplOptionsEx.OptimizedLoop)] - public override bool TryGetHashInBase64(Span destination, out int length) - { - length = 44; - if (destination.Length < length) - return false; - - Span hash = stackalloc ulong[BlockSizeWords]; - HashFinal(hash); - - BinarySpans.WriteUInt64LittleEndian(hash.Slice(HalfBlockSizeWords, HalfBlockSizeWords), destination); - - var status = Base64.EncodeToUtf8InPlace(destination, 32, out var written); - Debug.Assert(status == OperationStatus.Done); - Debug.Assert(written == length); + BinarySpans.WriteUInt64LittleEndian(HashFinal().AsSpan(HalfBlockSizeWords, HalfBlockSizeWords), destination); return true; } diff --git a/IT.Hashing.Gost/Gost3411_2012_512.cs b/IT.Hashing.Gost/Gost3411_2012_512.cs index 95c733f..332076e 100644 --- a/IT.Hashing.Gost/Gost3411_2012_512.cs +++ b/IT.Hashing.Gost/Gost3411_2012_512.cs @@ -199,8 +199,6 @@ static Gost3411_2012_512() public virtual int Size => 64; - public virtual int SizeInBase64 => 88; - public Gost3411_2012_512() { _h = new ulong[BlockSizeWords]; @@ -275,30 +273,7 @@ public virtual bool TryGetHash(Span destination, out int length) if (destination.Length < length) return false; - Span hash = stackalloc ulong[BlockSizeWords]; - HashFinal(hash); - BinarySpans.WriteUInt64LittleEndian(hash, destination); - - return true; - } - - /// Thrown when the instance has been disposed. - [MethodImpl(MethodImplOptionsEx.OptimizedLoop)] - public virtual bool TryGetHashInBase64(Span destination, out int length) - { - if (_disposed) throw new ObjectDisposedException(GetType().FullName); - - length = 88; - if (destination.Length < length) - return false; - - Span hash = stackalloc ulong[BlockSizeWords]; - HashFinal(hash); - BinarySpans.WriteUInt64LittleEndian(hash, destination); - - var status = Base64.EncodeToUtf8InPlace(destination, 64, out var written); - Debug.Assert(status == OperationStatus.Done); - Debug.Assert(written == length); + BinarySpans.WriteUInt64LittleEndian(HashFinal(), destination); return true; } diff --git a/IT.Hashing.Gost/IHashAlgorithm.cs b/IT.Hashing.Gost/IHashAlgorithm.cs index fc9f983..a05b6cc 100644 --- a/IT.Hashing.Gost/IHashAlgorithm.cs +++ b/IT.Hashing.Gost/IHashAlgorithm.cs @@ -6,8 +6,6 @@ public interface IHashAlgorithm : IDisposable { int Size { get; } - int SizeInBase64 { get; } - void Append(byte value); void Append(ReadOnlySpan span); @@ -16,7 +14,5 @@ public interface IHashAlgorithm : IDisposable bool TryGetHash(Span hash, out int length); - bool TryGetHashInBase64(Span hash, out int length); - void Reset(); } \ No newline at end of file diff --git a/IT.Hashing.Gost/IT.Hashing.Gost.csproj b/IT.Hashing.Gost/IT.Hashing.Gost.csproj index 695b2fc..070348f 100644 --- a/IT.Hashing.Gost/IT.Hashing.Gost.csproj +++ b/IT.Hashing.Gost/IT.Hashing.Gost.csproj @@ -13,7 +13,7 @@ true Ivan Tikhonov Ivan Tikhonov © 2026 - 2.0.3 + 2.0.4 Gost Hashing Managed Readme.md Icon.png diff --git a/IT.Hashing.Tests/Gost.cs b/IT.Hashing.Tests/Gost.cs index d16acaa..67fb7ff 100644 --- a/IT.Hashing.Tests/Gost.cs +++ b/IT.Hashing.Tests/Gost.cs @@ -1,7 +1,6 @@ using IT.Hashing.Gost; using IT.Hashing.Gost.Native; using Org.BouncyCastle.Security; -using System.Text; namespace IT.Hashing.Tests; @@ -21,13 +20,14 @@ public void Gost94() { _random.NextBytes(bytes); - nativeAlg.Append(bytes); + if (i > 0) + { + nativeAlg.Reset(); + } - var hashBase64 = new byte[nativeAlg.SizeInBase64]; - nativeAlg.TryGetHashInBase64(hashBase64, out _); + nativeAlg.Append(bytes); - var hash = ToHashAndReset(nativeAlg); - Assert.That(Encoding.UTF8.GetString(hashBase64), Is.EqualTo(Convert.ToBase64String(hash))); + var hash = GetHash(nativeAlg); var hash1 = gostNative.ComputeHash(bytes); @@ -51,21 +51,23 @@ public void Gost512() { _random.NextBytes(bytes); + if (i > 0) + { + nativeAlg.Reset(); + gostManaged.Reset(); + } + nativeAlg.Append(bytes); gostManaged.Append(bytes); - var hashBase64 = new byte[nativeAlg.SizeInBase64]; - nativeAlg.TryGetHashInBase64(hashBase64, out _); - - var hash = ToHashAndReset(nativeAlg); - Assert.That(Encoding.UTF8.GetString(hashBase64), Is.EqualTo(Convert.ToBase64String(hash))); + var hash = GetHash(nativeAlg); var hash1 = gostNative.ComputeHash(bytes); - + var hash2 = DigestUtilities.CalculateDigest("GOST3411_2012_512", bytes); - var hash3 = ToHashAndReset(gostManaged); - + var hash3 = GetHash(gostManaged); + Assert.That(hash.SequenceEqual(hash1), Is.True); Assert.That(hash.SequenceEqual(hash2), Is.True); Assert.That(hash.SequenceEqual(hash3), Is.True); @@ -85,20 +87,22 @@ public void Gost256() { _random.NextBytes(bytes); + if (i > 0) + { + nativeAlg.Reset(); + gostManaged.Reset(); + } + nativeAlg.Append(bytes); gostManaged.Append(bytes); - var hashBase64 = new byte[nativeAlg.SizeInBase64]; - nativeAlg.TryGetHashInBase64(hashBase64, out _); - - var hash = ToHashAndReset(nativeAlg); - Assert.That(Encoding.UTF8.GetString(hashBase64), Is.EqualTo(Convert.ToBase64String(hash))); + var hash = GetHash(nativeAlg); var hash1 = gostNative.ComputeHash(bytes); var hash2 = DigestUtilities.CalculateDigest("GOST3411_2012_256", bytes); - var hash3 = ToHashAndReset(gostManaged); + var hash3 = GetHash(gostManaged); Assert.That(hash.SequenceEqual(hash1), Is.True); Assert.That(hash.SequenceEqual(hash2), Is.True); @@ -106,18 +110,11 @@ public void Gost256() } } - private static byte[] ToHashAndReset(IHashAlgorithm alg) + private static byte[] GetHash(IHashAlgorithm alg) { var hash = new byte[alg.Size]; alg.TryGetHash(hash, out _); - - var hash2 = new byte[alg.Size]; - alg.TryGetHash(hash2, out _); - - Assert.That(hash.SequenceEqual(hash2), Is.True); - alg.Reset(); - return hash; } } \ No newline at end of file From 44cdd8b796c6328a7c4fc35ea0ee8959227bb907 Mon Sep 17 00:00:00 2001 From: Ivan Tikhonov Date: Tue, 11 Aug 2026 13:36:14 +0300 Subject: [PATCH 2/4] ns --- IT.Hashing.Gost/Gost3411_2012_512.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/IT.Hashing.Gost/Gost3411_2012_512.cs b/IT.Hashing.Gost/Gost3411_2012_512.cs index 332076e..5355fc6 100644 --- a/IT.Hashing.Gost/Gost3411_2012_512.cs +++ b/IT.Hashing.Gost/Gost3411_2012_512.cs @@ -2,10 +2,7 @@ using IT.Hashing.Gost.Internal; using System; -using System.Buffers; using System.Buffers.Binary; -using System.Diagnostics; -using System.Buffers.Text; using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; From e21630aa70d7547a03e27094bfa7619ff8358568 Mon Sep 17 00:00:00 2001 From: Ivan Tikhonov Date: Tue, 11 Aug 2026 13:37:09 +0300 Subject: [PATCH 3/4] fix --- IT.Hashing.Gost.Native/Internal/SafeHashHandleImpl.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/IT.Hashing.Gost.Native/Internal/SafeHashHandleImpl.cs b/IT.Hashing.Gost.Native/Internal/SafeHashHandleImpl.cs index eb2e18d..222edad 100644 --- a/IT.Hashing.Gost.Native/Internal/SafeHashHandleImpl.cs +++ b/IT.Hashing.Gost.Native/Internal/SafeHashHandleImpl.cs @@ -1,6 +1,5 @@ using Microsoft.Win32.SafeHandles; using System; -using System.Buffers.Text; using System.Security; namespace IT.Hashing.Gost.Native.Internal; @@ -15,8 +14,6 @@ internal class SafeHashHandleImpl : SafeHandleZeroOrMinusOneIsInvalid, IHashAlgo public int Size => CryptoApiHelper.GetEndHashDataLength(this); - public int SizeInBase64 => Base64.GetMaxEncodedToUtf8Length(Size); - public SafeHashHandleImpl() : base(true) { } From 6724871357e506431884be55ef6dbdce93659626 Mon Sep 17 00:00:00 2001 From: Ivan Tikhonov Date: Tue, 11 Aug 2026 13:42:24 +0300 Subject: [PATCH 4/4] test resetable: false --- IT.Hashing.Gost/Gost3411_2012_512.cs | 2 +- IT.Hashing.Tests/Gost.cs | 21 +++++++++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/IT.Hashing.Gost/Gost3411_2012_512.cs b/IT.Hashing.Gost/Gost3411_2012_512.cs index 5355fc6..2d81c5f 100644 --- a/IT.Hashing.Gost/Gost3411_2012_512.cs +++ b/IT.Hashing.Gost/Gost3411_2012_512.cs @@ -302,7 +302,7 @@ protected void HashFinal(Span h) Span p = stackalloc ulong[BlockSizeWords]; BinarySpans.ReadUInt64LittleEndian(paddedBlock, p); - Debug.Assert(h.Length == _h.Length); + System.Diagnostics.Debug.Assert(h.Length == _h.Length); _h.CopyTo(h); Span n = stackalloc ulong[BlockSizeWords]; diff --git a/IT.Hashing.Tests/Gost.cs b/IT.Hashing.Tests/Gost.cs index 67fb7ff..d0dbf3d 100644 --- a/IT.Hashing.Tests/Gost.cs +++ b/IT.Hashing.Tests/Gost.cs @@ -14,6 +14,7 @@ public void Gost94() var bytes = new byte[1024]; using var nativeAlg = HashAlgorithms.CreateNativeGost3411_94(); + using var nativeAlgFirst = HashAlgorithms.CreateNativeGost3411_94(resetable: false); using var gostNative = new Gost_R3411_94_HashAlgorithm(); for (int i = 0; i < 100; i++) @@ -35,6 +36,12 @@ public void Gost94() Assert.That(hash.SequenceEqual(hash1), Is.True); Assert.That(hash.SequenceEqual(hash2), Is.True); + + if (i == 0) + { + nativeAlgFirst.Append(bytes); + Assert.That(hash.SequenceEqual(GetHash(nativeAlgFirst)), Is.True); + } } } @@ -44,6 +51,7 @@ public void Gost512() var bytes = new byte[1024]; using var nativeAlg = HashAlgorithms.CreateNativeGost3411_2012_512(); + using var nativeAlgFirst = HashAlgorithms.CreateNativeGost3411_2012_512(resetable: false); using var gostNative = new Gost_R3411_2012_512_HashAlgorithm(); var gostManaged = new Gost3411_2012_512(); @@ -71,6 +79,12 @@ public void Gost512() Assert.That(hash.SequenceEqual(hash1), Is.True); Assert.That(hash.SequenceEqual(hash2), Is.True); Assert.That(hash.SequenceEqual(hash3), Is.True); + + if (i == 0) + { + nativeAlgFirst.Append(bytes); + Assert.That(hash.SequenceEqual(GetHash(nativeAlgFirst)), Is.True); + } } } @@ -80,6 +94,7 @@ public void Gost256() var bytes = new byte[1024]; using var nativeAlg = HashAlgorithms.CreateNativeGost3411_2012_256(); + using var nativeAlgFirst = HashAlgorithms.CreateNativeGost3411_2012_256(resetable: false); using var gostNative = new Gost_R3411_2012_256_HashAlgorithm(); var gostManaged = new Gost3411_2012_256(); @@ -107,6 +122,12 @@ public void Gost256() Assert.That(hash.SequenceEqual(hash1), Is.True); Assert.That(hash.SequenceEqual(hash2), Is.True); Assert.That(hash.SequenceEqual(hash3), Is.True); + + if (i == 0) + { + nativeAlgFirst.Append(bytes); + Assert.That(hash.SequenceEqual(GetHash(nativeAlgFirst)), Is.True); + } } }