diff --git a/src/libraries/System.Security.Cryptography/src/Resources/Strings.resx b/src/libraries/System.Security.Cryptography/src/Resources/Strings.resx
index a0a9e1e4afcf72..b121f8fd75c2d8 100644
--- a/src/libraries/System.Security.Cryptography/src/Resources/Strings.resx
+++ b/src/libraries/System.Security.Cryptography/src/Resources/Strings.resx
@@ -273,6 +273,9 @@
The specified CipherMode '{0}' is not supported.
+
+ Concurrent operations from multiple threads on this type are not supported.
+
This key is for algorithm '{0}'. Expected '{1}'.
diff --git a/src/libraries/System.Security.Cryptography/src/System.Security.Cryptography.csproj b/src/libraries/System.Security.Cryptography/src/System.Security.Cryptography.csproj
index 7e24c20a7fc418..ae5affcbb971a0 100644
--- a/src/libraries/System.Security.Cryptography/src/System.Security.Cryptography.csproj
+++ b/src/libraries/System.Security.Cryptography/src/System.Security.Cryptography.csproj
@@ -371,6 +371,8 @@
+
+
diff --git a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/ConcurrencyBlock.cs b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/ConcurrencyBlock.cs
new file mode 100644
index 00000000000000..c0eafe849da906
--- /dev/null
+++ b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/ConcurrencyBlock.cs
@@ -0,0 +1,40 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Threading;
+
+namespace System.Security.Cryptography
+{
+ internal struct ConcurrencyBlock
+ {
+ private int _count;
+
+ internal static Scope Enter(ref ConcurrencyBlock block)
+ {
+ int count = Interlocked.Increment(ref block._count);
+
+ if (count != 1)
+ {
+ Interlocked.Decrement(ref block._count);
+ throw new CryptographicException(SR.Cryptography_ConcurrentUseNotSupported);
+ }
+
+ return new Scope(ref block._count);
+ }
+
+ internal ref struct Scope
+ {
+ private ref int _parentCount;
+
+ internal Scope(ref int parentCount)
+ {
+ _parentCount = ref parentCount;
+ }
+
+ internal void Dispose()
+ {
+ Interlocked.Decrement(ref _parentCount);
+ }
+ }
+ }
+}
diff --git a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/ConcurrentSafeKmac.cs b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/ConcurrentSafeKmac.cs
new file mode 100644
index 00000000000000..ca41668473fa6d
--- /dev/null
+++ b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/ConcurrentSafeKmac.cs
@@ -0,0 +1,52 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+namespace System.Security.Cryptography
+{
+ internal struct ConcurrentSafeKmac
+ {
+ private readonly LiteKmac _liteKmac;
+ private ConcurrencyBlock _block;
+
+ public int HashSizeInBytes => _liteKmac.HashSizeInBytes;
+
+ internal ConcurrentSafeKmac(string algorithmId, ReadOnlySpan key, ReadOnlySpan customizationString, bool xof)
+ {
+ _liteKmac = LiteHashProvider.CreateKmac(algorithmId, key, customizationString, xof);
+ }
+
+ public void Append(ReadOnlySpan data)
+ {
+ using (ConcurrencyBlock.Enter(ref _block))
+ {
+ _liteKmac.Append(data);
+ }
+ }
+
+ public int Current(Span destination)
+ {
+ using (ConcurrencyBlock.Enter(ref _block))
+ {
+ return _liteKmac.Current(destination);
+ }
+ }
+
+ public int Finalize(Span destination)
+ {
+ using (ConcurrencyBlock.Enter(ref _block))
+ {
+ return _liteKmac.Finalize(destination);
+ }
+ }
+
+ public void Reset()
+ {
+ using (ConcurrencyBlock.Enter(ref _block))
+ {
+ _liteKmac.Reset();
+ }
+ }
+
+ public void Dispose() => _liteKmac.Dispose();
+ }
+}
diff --git a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/HashProviderCng.cs b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/HashProviderCng.cs
index 36f4767e9939e6..7e83210727f049 100644
--- a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/HashProviderCng.cs
+++ b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/HashProviderCng.cs
@@ -3,6 +3,7 @@
using System;
using System.Diagnostics;
+using System.Threading;
using Microsoft.Win32.SafeHandles;
using BCryptCreateHashFlags = Interop.BCrypt.BCryptCreateHashFlags;
using BCryptOpenAlgorithmProviderFlags = Interop.BCrypt.BCryptOpenAlgorithmProviderFlags;
@@ -63,47 +64,57 @@ internal HashProviderCng(string hashAlgId, ReadOnlySpan key, bool isHmac)
public sealed override unsafe void AppendHashData(ReadOnlySpan source)
{
Debug.Assert(_hHash != null);
- NTSTATUS ntStatus = Interop.BCrypt.BCryptHashData(_hHash, source, source.Length, 0);
- if (ntStatus != NTSTATUS.STATUS_SUCCESS)
+
+ using (ConcurrencyBlock.Enter(ref _block))
{
- throw Interop.BCrypt.CreateCryptographicException(ntStatus);
- }
+ NTSTATUS ntStatus = Interop.BCrypt.BCryptHashData(_hHash, source, source.Length, 0);
+ if (ntStatus != NTSTATUS.STATUS_SUCCESS)
+ {
+ throw Interop.BCrypt.CreateCryptographicException(ntStatus);
+ }
- _running = true;
+ _running = true;
+ }
}
public override int FinalizeHashAndReset(Span destination)
{
Debug.Assert(destination.Length >= _hashSize);
-
Debug.Assert(_hHash != null);
- NTSTATUS ntStatus = Interop.BCrypt.BCryptFinishHash(_hHash, destination, _hashSize, 0);
- if (ntStatus != NTSTATUS.STATUS_SUCCESS)
+
+ using (ConcurrencyBlock.Enter(ref _block))
{
- throw Interop.BCrypt.CreateCryptographicException(ntStatus);
- }
+ NTSTATUS ntStatus = Interop.BCrypt.BCryptFinishHash(_hHash, destination, _hashSize, 0);
+
+ if (ntStatus != NTSTATUS.STATUS_SUCCESS)
+ {
+ throw Interop.BCrypt.CreateCryptographicException(ntStatus);
+ }
- _running = false;
- Reset();
- return _hashSize;
+ _running = false;
+ Reset();
+ return _hashSize;
+ }
}
public override int GetCurrentHash(Span destination)
{
Debug.Assert(destination.Length >= _hashSize);
-
Debug.Assert(_hHash != null);
- using (SafeBCryptHashHandle tmpHash = Interop.BCrypt.BCryptDuplicateHash(_hHash))
+ using (ConcurrencyBlock.Enter(ref _block))
{
- NTSTATUS ntStatus = Interop.BCrypt.BCryptFinishHash(tmpHash, destination, _hashSize, 0);
-
- if (ntStatus != NTSTATUS.STATUS_SUCCESS)
+ using (SafeBCryptHashHandle tmpHash = Interop.BCrypt.BCryptDuplicateHash(_hHash))
{
- throw Interop.BCrypt.CreateCryptographicException(ntStatus);
- }
+ NTSTATUS ntStatus = Interop.BCrypt.BCryptFinishHash(tmpHash, destination, _hashSize, 0);
- return _hashSize;
+ if (ntStatus != NTSTATUS.STATUS_SUCCESS)
+ {
+ throw Interop.BCrypt.CreateCryptographicException(ntStatus);
+ }
+
+ return _hashSize;
+ }
}
}
@@ -125,21 +136,27 @@ public sealed override void Dispose(bool disposing)
public override void Reset()
{
+ // Reset does not need to use ConcurrencyBlock. It either no-ops, or creates an entirely new handle, exchanges
+ // them, and disposes of the old handle. We don't need to block concurrency on the Dispose because SafeHandle
+ // does that.
if (_reusable && !_running)
return;
- DestroyHash();
-
BCryptCreateHashFlags flags = _reusable ?
BCryptCreateHashFlags.BCRYPT_HASH_REUSABLE_FLAG :
BCryptCreateHashFlags.None;
SafeBCryptHashHandle hHash;
NTSTATUS ntStatus = Interop.BCrypt.BCryptCreateHash(_hAlgorithm, out hHash, IntPtr.Zero, 0, _key, _key == null ? 0 : _key.Length, flags);
+
if (ntStatus != NTSTATUS.STATUS_SUCCESS)
+ {
+ hHash.Dispose();
throw Interop.BCrypt.CreateCryptographicException(ntStatus);
+ }
- _hHash = hHash;
+ SafeBCryptHashHandle? previousHash = Interlocked.Exchange(ref _hHash, hHash);
+ previousHash?.Dispose();
}
private void DestroyHash()
@@ -161,5 +178,6 @@ private void DestroyHash()
private readonly int _hashSize;
private bool _running;
+ private ConcurrencyBlock _block;
}
}
diff --git a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/HashProviderDispenser.Apple.cs b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/HashProviderDispenser.Apple.cs
index 58e63629300a71..fdd414d59ff235 100644
--- a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/HashProviderDispenser.Apple.cs
+++ b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/HashProviderDispenser.Apple.cs
@@ -141,6 +141,7 @@ private sealed class AppleDigestProvider : HashProvider
{
private readonly LiteHash _liteHash;
private bool _running;
+ private ConcurrencyBlock _block;
public AppleDigestProvider(string hashAlgorithmId)
{
@@ -149,21 +150,30 @@ public AppleDigestProvider(string hashAlgorithmId)
public override void AppendHashData(ReadOnlySpan data)
{
- _liteHash.Append(data);
- _running = true;
+ using (ConcurrencyBlock.Enter(ref _block))
+ {
+ _liteHash.Append(data);
+ _running = true;
+ }
}
public override int FinalizeHashAndReset(Span destination)
{
- int written = _liteHash.Finalize(destination);
- // Apple's DigestFinal self-resets, so don't bother calling reset.
- _running = false;
- return written;
+ using (ConcurrencyBlock.Enter(ref _block))
+ {
+ int written = _liteHash.Finalize(destination);
+ // Apple's DigestFinal self-resets, so don't bother calling reset.
+ _running = false;
+ return written;
+ }
}
public override int GetCurrentHash(Span destination)
{
- return _liteHash.Current(destination);
+ using (ConcurrencyBlock.Enter(ref _block))
+ {
+ return _liteHash.Current(destination);
+ }
}
public override int HashSizeInBytes => _liteHash.HashSizeInBytes;
@@ -178,10 +188,13 @@ public override void Dispose(bool disposing)
public override void Reset()
{
- if (_running)
+ using (ConcurrencyBlock.Enter(ref _block))
{
- _liteHash.Reset();
- _running = false;
+ if (_running)
+ {
+ _liteHash.Reset();
+ _running = false;
+ }
}
}
}
@@ -191,6 +204,7 @@ private sealed class AppleHmacProvider : HashProvider
private readonly LiteHmac _liteHmac;
private readonly byte[] _key;
private bool _running;
+ private ConcurrencyBlock _block;
public AppleHmacProvider(string hashAlgorithmId, ReadOnlySpan key)
{
@@ -201,36 +215,45 @@ public AppleHmacProvider(string hashAlgorithmId, ReadOnlySpan key)
public override void AppendHashData(ReadOnlySpan data)
{
- if (!_running)
+ using (ConcurrencyBlock.Enter(ref _block))
{
- _liteHmac.Reset(_key);
- }
+ if (!_running)
+ {
+ _liteHmac.Reset(_key);
+ }
- _liteHmac.Append(data);
- _running = true;
+ _liteHmac.Append(data);
+ _running = true;
+ }
}
public override int FinalizeHashAndReset(Span destination)
{
- if (!_running)
+ using (ConcurrencyBlock.Enter(ref _block))
{
+ if (!_running)
+ {
+ _liteHmac.Reset(_key);
+ }
+
+ int written = _liteHmac.Finalize(destination);
_liteHmac.Reset(_key);
+ _running = false;
+ return written;
}
-
- int written = _liteHmac.Finalize(destination);
- _liteHmac.Reset(_key);
- _running = false;
- return written;
}
public override int GetCurrentHash(Span destination)
{
- if (!_running)
+ using (ConcurrencyBlock.Enter(ref _block))
{
- _liteHmac.Reset(_key);
- }
+ if (!_running)
+ {
+ _liteHmac.Reset(_key);
+ }
- return _liteHmac.Current(destination);
+ return _liteHmac.Current(destination);
+ }
}
public override int HashSizeInBytes => _liteHmac.HashSizeInBytes;
diff --git a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/HashProviderDispenser.Unix.cs b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/HashProviderDispenser.Unix.cs
index d62505e88873d3..18e8659d0cbfc8 100644
--- a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/HashProviderDispenser.Unix.cs
+++ b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/HashProviderDispenser.Unix.cs
@@ -87,6 +87,7 @@ private sealed class EvpHashProvider : HashProvider
{
private readonly LiteHash _liteHash;
private bool _running;
+ private ConcurrencyBlock _block;
public EvpHashProvider(string hashAlgorithmId)
{
@@ -95,21 +96,30 @@ public EvpHashProvider(string hashAlgorithmId)
public override void AppendHashData(ReadOnlySpan data)
{
- _liteHash.Append(data);
- _running = true;
+ using (ConcurrencyBlock.Enter(ref _block))
+ {
+ _liteHash.Append(data);
+ _running = true;
+ }
}
public override int FinalizeHashAndReset(Span destination)
{
- int written = _liteHash.Finalize(destination);
- _liteHash.Reset();
- _running = false;
- return written;
+ using (ConcurrencyBlock.Enter(ref _block))
+ {
+ int written = _liteHash.Finalize(destination);
+ _liteHash.Reset();
+ _running = false;
+ return written;
+ }
}
public override int GetCurrentHash(Span destination)
{
- return _liteHash.Current(destination);
+ using (ConcurrencyBlock.Enter(ref _block))
+ {
+ return _liteHash.Current(destination);
+ }
}
public override int HashSizeInBytes => _liteHash.HashSizeInBytes;
@@ -124,10 +134,13 @@ public override void Dispose(bool disposing)
public override void Reset()
{
- if (_running)
+ using (ConcurrencyBlock.Enter(ref _block))
{
- _liteHash.Reset();
- _running = false;
+ if (_running)
+ {
+ _liteHash.Reset();
+ _running = false;
+ }
}
}
}
@@ -136,6 +149,7 @@ private sealed class HmacHashProvider : HashProvider
{
private readonly LiteHmac _liteHmac;
private bool _running;
+ private ConcurrencyBlock _block;
public HmacHashProvider(string hashAlgorithmId, ReadOnlySpan key)
{
@@ -144,21 +158,30 @@ public HmacHashProvider(string hashAlgorithmId, ReadOnlySpan key)
public override void AppendHashData(ReadOnlySpan data)
{
- _liteHmac.Append(data);
- _running = true;
+ using (ConcurrencyBlock.Enter(ref _block))
+ {
+ _liteHmac.Append(data);
+ _running = true;
+ }
}
public override int FinalizeHashAndReset(Span destination)
{
- int written = _liteHmac.Finalize(destination);
- _liteHmac.Reset();
- _running = false;
- return written;
+ using (ConcurrencyBlock.Enter(ref _block))
+ {
+ int written = _liteHmac.Finalize(destination);
+ _liteHmac.Reset();
+ _running = false;
+ return written;
+ }
}
public override int GetCurrentHash(Span destination)
{
- return _liteHmac.Current(destination);
+ using (ConcurrencyBlock.Enter(ref _block))
+ {
+ return _liteHmac.Current(destination);
+ }
}
public override int HashSizeInBytes => _liteHmac.HashSizeInBytes;
@@ -173,10 +196,13 @@ public override void Dispose(bool disposing)
public override void Reset()
{
- if (_running)
+ using (ConcurrencyBlock.Enter(ref _block))
{
- _liteHmac.Reset();
- _running = false;
+ if (_running)
+ {
+ _liteHmac.Reset();
+ _running = false;
+ }
}
}
}
diff --git a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/Kmac128.cs b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/Kmac128.cs
index c850977ef9b8bf..a90c987a8278d2 100644
--- a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/Kmac128.cs
+++ b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/Kmac128.cs
@@ -19,7 +19,7 @@ namespace System.Security.Cryptography
///
public sealed class Kmac128 : IDisposable
{
- private readonly LiteKmac _kmacProvider;
+ private ConcurrentSafeKmac _kmacProvider;
private bool _disposed;
///
@@ -53,7 +53,7 @@ public Kmac128(byte[] key, byte[]? customizationString = null)
public Kmac128(ReadOnlySpan key, ReadOnlySpan customizationString = default)
{
CheckPlatformSupport();
- _kmacProvider = LiteHashProvider.CreateKmac(HashAlgorithmNames.KMAC128, key, customizationString, xof: false);
+ _kmacProvider = new ConcurrentSafeKmac(HashAlgorithmNames.KMAC128, key, customizationString, xof: false);
}
///
diff --git a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/Kmac256.cs b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/Kmac256.cs
index 3ee2ff818191c1..d5c3d013e31e73 100644
--- a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/Kmac256.cs
+++ b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/Kmac256.cs
@@ -19,7 +19,7 @@ namespace System.Security.Cryptography
///
public sealed class Kmac256 : IDisposable
{
- private readonly LiteKmac _kmacProvider;
+ private ConcurrentSafeKmac _kmacProvider;
private bool _disposed;
///
@@ -53,7 +53,7 @@ public Kmac256(byte[] key, byte[]? customizationString = null)
public Kmac256(ReadOnlySpan key, ReadOnlySpan customizationString = default)
{
CheckPlatformSupport();
- _kmacProvider = LiteHashProvider.CreateKmac(HashAlgorithmNames.KMAC256, key, customizationString, xof: false);
+ _kmacProvider = new ConcurrentSafeKmac(HashAlgorithmNames.KMAC256, key, customizationString, xof: false);
}
///
diff --git a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/KmacXof128.cs b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/KmacXof128.cs
index 3514fbaaa0c9b4..7275d822b136cc 100644
--- a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/KmacXof128.cs
+++ b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/KmacXof128.cs
@@ -19,7 +19,7 @@ namespace System.Security.Cryptography
///
public sealed class KmacXof128 : IDisposable
{
- private readonly LiteKmac _kmacProvider;
+ private ConcurrentSafeKmac _kmacProvider;
private bool _disposed;
///
@@ -53,7 +53,7 @@ public KmacXof128(byte[] key, byte[]? customizationString = null)
public KmacXof128(ReadOnlySpan key, ReadOnlySpan customizationString = default)
{
CheckPlatformSupport();
- _kmacProvider = LiteHashProvider.CreateKmac(HashAlgorithmNames.KMAC128, key, customizationString, xof: true);
+ _kmacProvider = new ConcurrentSafeKmac(HashAlgorithmNames.KMAC128, key, customizationString, xof: true);
}
///
diff --git a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/KmacXof256.cs b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/KmacXof256.cs
index 040fe7a37ec017..fe08a87d494fdc 100644
--- a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/KmacXof256.cs
+++ b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/KmacXof256.cs
@@ -19,7 +19,7 @@ namespace System.Security.Cryptography
///
public sealed class KmacXof256 : IDisposable
{
- private readonly LiteKmac _kmacProvider;
+ private ConcurrentSafeKmac _kmacProvider;
private bool _disposed;
///
@@ -53,7 +53,7 @@ public KmacXof256(byte[] key, byte[]? customizationString = null)
public KmacXof256(ReadOnlySpan key, ReadOnlySpan customizationString = default)
{
CheckPlatformSupport();
- _kmacProvider = LiteHashProvider.CreateKmac(HashAlgorithmNames.KMAC256, key, customizationString, xof: true);
+ _kmacProvider = new ConcurrentSafeKmac(HashAlgorithmNames.KMAC256, key, customizationString, xof: true);
}
///
diff --git a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/Shake128.cs b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/Shake128.cs
index a8b5e5f3257f22..14dc8e0eac407c 100644
--- a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/Shake128.cs
+++ b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/Shake128.cs
@@ -22,6 +22,7 @@ public sealed partial class Shake128 : IDisposable
// Some platforms have a mutable struct for LiteXof, do not mark this field as readonly.
private LiteXof _hashProvider;
private bool _disposed;
+ private ConcurrencyBlock _block;
///
/// Initializes a new instance of the class.
@@ -68,7 +69,10 @@ public void AppendData(ReadOnlySpan data)
{
CheckDisposed();
- _hashProvider.Append(data);
+ using (ConcurrencyBlock.Enter(ref _block))
+ {
+ _hashProvider.Append(data);
+ }
}
///
@@ -87,10 +91,13 @@ public byte[] GetHashAndReset(int outputLength)
ArgumentOutOfRangeException.ThrowIfNegative(outputLength);
CheckDisposed();
- byte[] output = new byte[outputLength];
- _hashProvider.Finalize(output);
- _hashProvider.Reset();
- return output;
+ using (ConcurrencyBlock.Enter(ref _block))
+ {
+ byte[] output = new byte[outputLength];
+ _hashProvider.Finalize(output);
+ _hashProvider.Reset();
+ return output;
+ }
}
///
@@ -104,8 +111,11 @@ public void GetHashAndReset(Span destination)
{
CheckDisposed();
- _hashProvider.Finalize(destination);
- _hashProvider.Reset();
+ using (ConcurrencyBlock.Enter(ref _block))
+ {
+ _hashProvider.Finalize(destination);
+ _hashProvider.Reset();
+ }
}
///
@@ -124,9 +134,12 @@ public byte[] GetCurrentHash(int outputLength)
ArgumentOutOfRangeException.ThrowIfNegative(outputLength);
CheckDisposed();
- byte[] output = new byte[outputLength];
- _hashProvider.Current(output);
- return output;
+ using (ConcurrencyBlock.Enter(ref _block))
+ {
+ byte[] output = new byte[outputLength];
+ _hashProvider.Current(output);
+ return output;
+ }
}
///
@@ -139,7 +152,11 @@ public byte[] GetCurrentHash(int outputLength)
public void GetCurrentHash(Span destination)
{
CheckDisposed();
- _hashProvider.Current(destination);
+
+ using (ConcurrencyBlock.Enter(ref _block))
+ {
+ _hashProvider.Current(destination);
+ }
}
///
diff --git a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/Shake256.cs b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/Shake256.cs
index cafe8d3036334b..c801adde9395ca 100644
--- a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/Shake256.cs
+++ b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/Shake256.cs
@@ -22,6 +22,7 @@ public sealed partial class Shake256 : IDisposable
// Some platforms have a mutable struct for LiteXof, do not mark this field as readonly.
private LiteXof _hashProvider;
private bool _disposed;
+ private ConcurrencyBlock _block;
///
/// Initializes a new instance of the class.
@@ -68,7 +69,10 @@ public void AppendData(ReadOnlySpan data)
{
CheckDisposed();
- _hashProvider.Append(data);
+ using (ConcurrencyBlock.Enter(ref _block))
+ {
+ _hashProvider.Append(data);
+ }
}
///
@@ -87,10 +91,13 @@ public byte[] GetHashAndReset(int outputLength)
ArgumentOutOfRangeException.ThrowIfNegative(outputLength);
CheckDisposed();
- byte[] output = new byte[outputLength];
- _hashProvider.Finalize(output);
- _hashProvider.Reset();
- return output;
+ using (ConcurrencyBlock.Enter(ref _block))
+ {
+ byte[] output = new byte[outputLength];
+ _hashProvider.Finalize(output);
+ _hashProvider.Reset();
+ return output;
+ }
}
///
@@ -104,8 +111,11 @@ public void GetHashAndReset(Span destination)
{
CheckDisposed();
- _hashProvider.Finalize(destination);
- _hashProvider.Reset();
+ using (ConcurrencyBlock.Enter(ref _block))
+ {
+ _hashProvider.Finalize(destination);
+ _hashProvider.Reset();
+ }
}
///
@@ -124,9 +134,12 @@ public byte[] GetCurrentHash(int outputLength)
ArgumentOutOfRangeException.ThrowIfNegative(outputLength);
CheckDisposed();
- byte[] output = new byte[outputLength];
- _hashProvider.Current(output);
- return output;
+ using (ConcurrencyBlock.Enter(ref _block))
+ {
+ byte[] output = new byte[outputLength];
+ _hashProvider.Current(output);
+ return output;
+ }
}
///
@@ -139,7 +152,11 @@ public byte[] GetCurrentHash(int outputLength)
public void GetCurrentHash(Span destination)
{
CheckDisposed();
- _hashProvider.Current(destination);
+
+ using (ConcurrencyBlock.Enter(ref _block))
+ {
+ _hashProvider.Current(destination);
+ }
}
///
diff --git a/src/libraries/System.Security.Cryptography/tests/HashAlgorithmTestDriver.cs b/src/libraries/System.Security.Cryptography/tests/HashAlgorithmTestDriver.cs
index 2fa0779a257e47..bbcf23ec0e596e 100644
--- a/src/libraries/System.Security.Cryptography/tests/HashAlgorithmTestDriver.cs
+++ b/src/libraries/System.Security.Cryptography/tests/HashAlgorithmTestDriver.cs
@@ -5,6 +5,8 @@
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
+using Microsoft.DotNet.RemoteExecutor;
+using Microsoft.DotNet.XUnitExtensions;
using Test.Cryptography;
using Xunit;
@@ -14,7 +16,7 @@ public abstract class HashAlgorithmTestDriver where THashTrait : IHa
{
public static bool IsSupported => THashTrait.IsSupported;
public static bool IsNotSupported => !IsSupported;
- protected abstract HashAlgorithm Create();
+ protected HashAlgorithm Create() => THashTrait.Create();
protected abstract bool TryHashData(ReadOnlySpan source, Span destination, out int bytesWritten);
protected abstract byte[] HashData(byte[] source);
protected abstract byte[] HashData(ReadOnlySpan source);
@@ -930,11 +932,235 @@ public void CryptographicOperations_HashData_ArgValidation_UnreadableStream()
Assert.Throws("source", () =>
CryptographicOperations.HashDataAsync(HashAlgorithm, UntouchableStream.Instance, Memory.Empty));
}
+
+ [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))]
+ public void HashAlgorithm_ComputeHash_ConcurrentUseDoesNotCrashProcess()
+ {
+ if (!IsSupported)
+ {
+ throw new SkipTestException("Algorithm is not supported on this platform.");
+ }
+
+ static void Update(object obj)
+ {
+ byte[] data = [1, 2, 3, 4, 5, 6, 7, 8, 9];
+
+ for (int i = 0; i < 10_000; i++)
+ {
+ try
+ {
+ ((HashAlgorithm)obj).ComputeHash(data);
+ }
+ catch
+ {
+ // Ignore all managed exceptions. HashAlgorithm is not thread safe, but we don't want process
+ // crashes.
+ }
+ }
+ }
+
+ RemoteExecutor.Invoke(static () =>
+ {
+ using (HashAlgorithm hash = THashTrait.Create())
+ {
+ Thread thread1 = new(Update);
+ Thread thread2 = new(Update);
+ thread1.Start(hash);
+ thread2.Start(hash);
+ thread1.Join();
+ thread2.Join();
+ }
+
+ return RemoteExecutor.SuccessExitCode;
+ }).Dispose();
+ }
+
+ [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))]
+ public void HashAlgorithm_TransformBlock_ConcurrentUseDoesNotCrashProcess()
+ {
+ if (!IsSupported)
+ {
+ throw new SkipTestException("Algorithm is not supported on this platform.");
+ }
+
+ static void Update(object obj)
+ {
+ byte[] data = [1, 2, 3, 4, 5, 6, 7, 8, 9];
+
+ for (int i = 0; i < 10_000; i++)
+ {
+ try
+ {
+ ((HashAlgorithm)obj).TransformBlock(data, 0, data.Length, null, 0);
+ }
+ catch
+ {
+ // Ignore all managed exceptions. HashAlgorithm is not thread safe, but we don't want process
+ // crashes.
+ }
+ }
+ }
+
+ RemoteExecutor.Invoke(static () =>
+ {
+ using (HashAlgorithm hash = THashTrait.Create())
+ {
+ Thread thread1 = new(Update);
+ Thread thread2 = new(Update);
+ thread1.Start(hash);
+ thread2.Start(hash);
+ thread1.Join();
+ thread2.Join();
+ }
+
+ return RemoteExecutor.SuccessExitCode;
+ }).Dispose();
+ }
+
+ [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))]
+ public void HashAlgorithm_TransformFinalBlock_ConcurrentUseDoesNotCrashProcess()
+ {
+ if (!IsSupported)
+ {
+ throw new SkipTestException("Algorithm is not supported on this platform.");
+ }
+
+ static void Update(object obj)
+ {
+ byte[] data = [1, 2, 3, 4, 5, 6, 7, 8, 9];
+
+ for (int i = 0; i < 10_000; i++)
+ {
+ try
+ {
+ ((HashAlgorithm)obj).TransformFinalBlock(data, 0, data.Length);
+ }
+ catch
+ {
+ // Ignore all managed exceptions. HashAlgorithm is not thread safe, but we don't want process
+ // crashes.
+ }
+ }
+ }
+
+ RemoteExecutor.Invoke(static () =>
+ {
+ using (HashAlgorithm hash = THashTrait.Create())
+ {
+ Thread thread1 = new(Update);
+ Thread thread2 = new(Update);
+ thread1.Start(hash);
+ thread2.Start(hash);
+ thread1.Join();
+ thread2.Join();
+ }
+
+ return RemoteExecutor.SuccessExitCode;
+ }).Dispose();
+ }
+
+ [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))]
+ public void HashAlgorithm_TransformBlockAndInitialize_ConcurrentUseDoesNotCrashProcess()
+ {
+ if (!IsSupported)
+ {
+ throw new SkipTestException("Algorithm is not supported on this platform.");
+ }
+
+ static void Update(object obj)
+ {
+ byte[] data = [1, 2, 3, 4, 5, 6, 7, 8, 9];
+
+ for (int i = 0; i < 10_000; i++)
+ {
+ try
+ {
+ HashAlgorithm hash = ((HashAlgorithm)obj);
+ hash.TransformBlock(data, 0, data.Length, null, 0);
+ hash.Initialize();
+ }
+ catch
+ {
+ // Ignore all managed exceptions. HashAlgorithm is not thread safe, but we don't want process
+ // crashes.
+ }
+ }
+ }
+
+ RemoteExecutor.Invoke(static () =>
+ {
+ using (HashAlgorithm hash = THashTrait.Create())
+ {
+ Thread thread1 = new(Update);
+ Thread thread2 = new(Update);
+ thread1.Start(hash);
+ thread2.Start(hash);
+ thread1.Join();
+ thread2.Join();
+ }
+
+ return RemoteExecutor.SuccessExitCode;
+ }).Dispose();
+ }
+
+ [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))]
+ public void HashAlgorithm_TransformBlockAndDispose_ConcurrentUseDoesNotCrashProcess()
+ {
+ if (!IsSupported)
+ {
+ throw new SkipTestException("Algorithm is not supported on this platform.");
+ }
+
+ static void Update(object obj)
+ {
+ byte[] data = [1, 2, 3, 4, 5, 6, 7, 8, 9];
+
+ for (int i = 0; i < 10_000; i++)
+ {
+ try
+ {
+ HashAlgorithm hash = ((HashAlgorithm)obj);
+ hash.TransformBlock(data, 0, data.Length, null, 0);
+ }
+ catch
+ {
+ // Ignore all managed exceptions. HashAlgorithm is not thread safe, but we don't want process
+ // crashes.
+ }
+ }
+ }
+
+ RemoteExecutor.Invoke(static () =>
+ {
+ using (HashAlgorithm hash = THashTrait.Create())
+ {
+ Thread thread1 = new(Update);
+ Thread thread2 = new(obj =>
+ {
+ Thread.Sleep(10);
+ try
+ {
+ ((HashAlgorithm)obj).Dispose();
+ }
+ catch
+ {
+ }
+ });
+ thread1.Start(hash);
+ thread2.Start(hash);
+ thread1.Join();
+ thread2.Join();
+ }
+
+ return RemoteExecutor.SuccessExitCode;
+ }).Dispose();
+ }
}
public interface IHashTrait
{
static abstract bool IsSupported { get; }
static abstract int HashSizeInBytes { get; }
+ static abstract HashAlgorithm Create();
}
}
diff --git a/src/libraries/System.Security.Cryptography/tests/IncrementalHashTests.cs b/src/libraries/System.Security.Cryptography/tests/IncrementalHashTests.cs
index dc7c33debfa8cd..69788b24023234 100644
--- a/src/libraries/System.Security.Cryptography/tests/IncrementalHashTests.cs
+++ b/src/libraries/System.Security.Cryptography/tests/IncrementalHashTests.cs
@@ -2,6 +2,8 @@
// The .NET Foundation licenses this file to you under the MIT license.
using System.Collections.Generic;
+using System.Threading;
+using Microsoft.DotNet.RemoteExecutor;
using Test.Cryptography;
using Xunit;
@@ -622,6 +624,106 @@ public static void VerifyBounds_GetHashAndReset_Hash(HashAlgorithm referenceAlgo
}
}
+ [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))]
+ public static void Hash_GetHashAndReset_ConcurrentUseDoesNotCrashProcess()
+ {
+ static void ThreadWork(object obj)
+ {
+ try
+ {
+ IncrementalHash hash = (IncrementalHash)obj;
+
+ for (int i = 0; i < 10_000; i++)
+ {
+ hash.AppendData("potatos and carrots make for a fine stew."u8);
+ hash.GetHashAndReset();
+ }
+ }
+ catch
+ {
+ // Ignore all managed exceptions. IncrementalHash is not thread safe, but we don't want process
+ // crashes.
+ }
+ }
+
+ RemoteExecutor.Invoke(static () =>
+ {
+ foreach(object[] items in GetHashAlgorithms())
+ {
+ if (items is [HashAlgorithm referenceAlgorithm, HashAlgorithmName hashAlgorithm])
+ {
+ referenceAlgorithm.Dispose();
+
+ using (IncrementalHash hash = IncrementalHash.CreateHash(hashAlgorithm))
+ {
+ Thread thread1 = new(ThreadWork);
+ Thread thread2 = new(ThreadWork);
+ thread1.Start(hash);
+ thread2.Start(hash);
+ thread1.Join();
+ thread2.Join();
+ }
+ }
+ else
+ {
+ Assert.Fail("Test is not set up correctly.");
+ }
+ }
+
+ return RemoteExecutor.SuccessExitCode;
+ }).Dispose();
+ }
+
+ [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))]
+ public static void HMAC_GetHashAndReset_ConcurrentUseDoesNotCrashProcess()
+ {
+ static void ThreadWork(object obj)
+ {
+ try
+ {
+ IncrementalHash hash = (IncrementalHash)obj;
+
+ for (int i = 0; i < 10_000; i++)
+ {
+ hash.AppendData("potatos and carrots make for a fine stew."u8);
+ hash.GetHashAndReset();
+ }
+ }
+ catch
+ {
+ // Ignore all managed exceptions. IncrementalHash is not thread safe, but we don't want process
+ // crashes.
+ }
+ }
+
+ RemoteExecutor.Invoke(static () =>
+ {
+ foreach(object[] items in GetHMACs())
+ {
+ if (items is [HashAlgorithm referenceAlgorithm, HashAlgorithmName hashAlgorithm])
+ {
+ referenceAlgorithm.Dispose();
+
+ using (IncrementalHash hash = IncrementalHash.CreateHMAC(hashAlgorithm, [1, 2, 3, 4]))
+ {
+ Thread thread1 = new(ThreadWork);
+ Thread thread2 = new(ThreadWork);
+ thread1.Start(hash);
+ thread2.Start(hash);
+ thread1.Join();
+ thread2.Join();
+ }
+ }
+ else
+ {
+ Assert.Fail("Test is not set up correctly.");
+ }
+ }
+
+ return RemoteExecutor.SuccessExitCode;
+ }).Dispose();
+ }
+
private static void VerifyGetCurrentHash(IncrementalHash single, IncrementalHash accumulated)
{
Span buf = stackalloc byte[2048];
diff --git a/src/libraries/System.Security.Cryptography/tests/MD5Tests.cs b/src/libraries/System.Security.Cryptography/tests/MD5Tests.cs
index 8fe91caf0d1b73..6e9f4a724fffc1 100644
--- a/src/libraries/System.Security.Cryptography/tests/MD5Tests.cs
+++ b/src/libraries/System.Security.Cryptography/tests/MD5Tests.cs
@@ -15,9 +15,9 @@ public sealed class Traits : IHashTrait
{
public static bool IsSupported => true;
public static int HashSizeInBytes => MD5.HashSizeInBytes;
+ public static HashAlgorithm Create() => MD5.Create();
}
- protected override HashAlgorithm Create() => MD5.Create();
protected override HashAlgorithmName HashAlgorithm => HashAlgorithmName.MD5;
protected override bool TryHashData(ReadOnlySpan source, Span destination, out int bytesWritten)
diff --git a/src/libraries/System.Security.Cryptography/tests/Sha1ManagedTests.cs b/src/libraries/System.Security.Cryptography/tests/Sha1ManagedTests.cs
index 54e7476f544719..7d9fee4ee137c7 100644
--- a/src/libraries/System.Security.Cryptography/tests/Sha1ManagedTests.cs
+++ b/src/libraries/System.Security.Cryptography/tests/Sha1ManagedTests.cs
@@ -6,11 +6,13 @@ namespace System.Security.Cryptography.Tests
///
/// Sha1Managed has a copy of the same implementation as SHA1
///
- public class Sha1ManagedTests : Sha1Tests
+ public class Sha1ManagedTests : Sha1Tests
{
- protected override HashAlgorithm Create()
+ public sealed class Traits : IHashTrait
{
- return new SHA1Managed();
+ public static bool IsSupported => true;
+ public static int HashSizeInBytes => SHA1.HashSizeInBytes;
+ public static HashAlgorithm Create() => new SHA1Managed();
}
}
}
diff --git a/src/libraries/System.Security.Cryptography/tests/Sha1Tests.cs b/src/libraries/System.Security.Cryptography/tests/Sha1Tests.cs
index 691dd9ea45e149..ede541452f59f2 100644
--- a/src/libraries/System.Security.Cryptography/tests/Sha1Tests.cs
+++ b/src/libraries/System.Security.Cryptography/tests/Sha1Tests.cs
@@ -8,15 +8,18 @@
namespace System.Security.Cryptography.Tests
{
- public class Sha1Tests : HashAlgorithmTestDriver
+ public sealed class FactorySha1Tests : Sha1Tests
{
public sealed class Traits : IHashTrait
{
public static bool IsSupported => true;
public static int HashSizeInBytes => SHA1.HashSizeInBytes;
+ public static HashAlgorithm Create() => SHA1.Create();
}
+ }
- protected override HashAlgorithm Create() => SHA1.Create();
+ public abstract class Sha1Tests : HashAlgorithmTestDriver where THashTrait : IHashTrait
+ {
protected override HashAlgorithmName HashAlgorithm => HashAlgorithmName.SHA1;
protected override bool TryHashData(ReadOnlySpan source, Span destination, out int bytesWritten)
diff --git a/src/libraries/System.Security.Cryptography/tests/Sha256ManagedTests.cs b/src/libraries/System.Security.Cryptography/tests/Sha256ManagedTests.cs
index 2f153da87a60ff..3241fe97d5bd95 100644
--- a/src/libraries/System.Security.Cryptography/tests/Sha256ManagedTests.cs
+++ b/src/libraries/System.Security.Cryptography/tests/Sha256ManagedTests.cs
@@ -6,11 +6,13 @@ namespace System.Security.Cryptography.Tests
///
/// Sha256Managed has a copy of the same implementation as SHA256
///
- public class Sha256ManagedTests : Sha256Tests
+ public class Sha256ManagedTests : Sha256Tests
{
- protected override HashAlgorithm Create()
+ public sealed class Traits : IHashTrait
{
- return new SHA256Managed();
+ public static bool IsSupported => true;
+ public static int HashSizeInBytes => SHA256.HashSizeInBytes;
+ public static HashAlgorithm Create() => new SHA256Managed();
}
}
}
diff --git a/src/libraries/System.Security.Cryptography/tests/Sha256Tests.cs b/src/libraries/System.Security.Cryptography/tests/Sha256Tests.cs
index b10cc017a313e9..c19008e5580ccf 100644
--- a/src/libraries/System.Security.Cryptography/tests/Sha256Tests.cs
+++ b/src/libraries/System.Security.Cryptography/tests/Sha256Tests.cs
@@ -8,15 +8,18 @@
namespace System.Security.Cryptography.Tests
{
- public class Sha256Tests : HashAlgorithmTestDriver
+ public sealed class FactorySha256Tests : Sha256Tests
{
public sealed class Traits : IHashTrait
{
public static bool IsSupported => true;
public static int HashSizeInBytes => SHA256.HashSizeInBytes;
+ public static HashAlgorithm Create() => SHA256.Create();
}
+ }
- protected override HashAlgorithm Create() => SHA256.Create();
+ public abstract class Sha256Tests : HashAlgorithmTestDriver where THashTrait : IHashTrait
+ {
protected override HashAlgorithmName HashAlgorithm => HashAlgorithmName.SHA256;
protected override bool TryHashData(ReadOnlySpan source, Span destination, out int bytesWritten)
diff --git a/src/libraries/System.Security.Cryptography/tests/Sha384ManagedTests.cs b/src/libraries/System.Security.Cryptography/tests/Sha384ManagedTests.cs
index 88166dbf0496a5..4cf7baa1fa3987 100644
--- a/src/libraries/System.Security.Cryptography/tests/Sha384ManagedTests.cs
+++ b/src/libraries/System.Security.Cryptography/tests/Sha384ManagedTests.cs
@@ -6,11 +6,13 @@ namespace System.Security.Cryptography.Tests
///
/// Sha384Managed has a copy of the same implementation as SHA384
///
- public class Sha384ManagedTests : Sha384Tests
+ public class Sha384ManagedTests : Sha384Tests
{
- protected override HashAlgorithm Create()
+ public sealed class Traits : IHashTrait
{
- return new SHA384Managed();
+ public static bool IsSupported => true;
+ public static int HashSizeInBytes => SHA384.HashSizeInBytes;
+ public static HashAlgorithm Create() => new SHA384Managed();
}
}
}
diff --git a/src/libraries/System.Security.Cryptography/tests/Sha384Tests.cs b/src/libraries/System.Security.Cryptography/tests/Sha384Tests.cs
index ae1a720c51eedd..53b557b4219044 100644
--- a/src/libraries/System.Security.Cryptography/tests/Sha384Tests.cs
+++ b/src/libraries/System.Security.Cryptography/tests/Sha384Tests.cs
@@ -8,15 +8,18 @@
namespace System.Security.Cryptography.Tests
{
- public class Sha384Tests : HashAlgorithmTestDriver
+ public sealed class FactorySha384Tests : Sha384Tests
{
public sealed class Traits : IHashTrait
{
public static bool IsSupported => true;
public static int HashSizeInBytes => SHA384.HashSizeInBytes;
+ public static HashAlgorithm Create() => SHA384.Create();
}
+ }
- protected override HashAlgorithm Create() => SHA384.Create();
+ public abstract class Sha384Tests : HashAlgorithmTestDriver where THashTrait : IHashTrait
+ {
protected override HashAlgorithmName HashAlgorithm => HashAlgorithmName.SHA384;
protected override bool TryHashData(ReadOnlySpan source, Span destination, out int bytesWritten)
diff --git a/src/libraries/System.Security.Cryptography/tests/Sha3_256Tests.cs b/src/libraries/System.Security.Cryptography/tests/Sha3_256Tests.cs
index 96fe91e4f30261..f5c002d64e4a9a 100644
--- a/src/libraries/System.Security.Cryptography/tests/Sha3_256Tests.cs
+++ b/src/libraries/System.Security.Cryptography/tests/Sha3_256Tests.cs
@@ -15,9 +15,9 @@ public sealed class Traits : IHashTrait
{
public static bool IsSupported => SHA3_256.IsSupported;
public static int HashSizeInBytes => SHA3_256.HashSizeInBytes;
+ public static HashAlgorithm Create() => SHA3_256.Create();
}
- protected override HashAlgorithm Create() => SHA3_256.Create();
protected override HashAlgorithmName HashAlgorithm => HashAlgorithmName.SHA3_256;
protected override bool TryHashData(ReadOnlySpan source, Span destination, out int bytesWritten)
diff --git a/src/libraries/System.Security.Cryptography/tests/Sha3_384Tests.cs b/src/libraries/System.Security.Cryptography/tests/Sha3_384Tests.cs
index 9e67fb32b7ab24..416e6ae1b6cb87 100644
--- a/src/libraries/System.Security.Cryptography/tests/Sha3_384Tests.cs
+++ b/src/libraries/System.Security.Cryptography/tests/Sha3_384Tests.cs
@@ -15,9 +15,9 @@ public sealed class Traits : IHashTrait
{
public static bool IsSupported => SHA3_384.IsSupported;
public static int HashSizeInBytes => SHA3_384.HashSizeInBytes;
+ public static HashAlgorithm Create() => SHA3_384.Create();
}
- protected override HashAlgorithm Create() => SHA3_384.Create();
protected override HashAlgorithmName HashAlgorithm => HashAlgorithmName.SHA3_384;
protected override bool TryHashData(ReadOnlySpan source, Span destination, out int bytesWritten)
diff --git a/src/libraries/System.Security.Cryptography/tests/Sha3_512Tests.cs b/src/libraries/System.Security.Cryptography/tests/Sha3_512Tests.cs
index a715cd40c5596f..843d87110fc633 100644
--- a/src/libraries/System.Security.Cryptography/tests/Sha3_512Tests.cs
+++ b/src/libraries/System.Security.Cryptography/tests/Sha3_512Tests.cs
@@ -15,9 +15,9 @@ public sealed class Traits : IHashTrait
{
public static bool IsSupported => SHA3_512.IsSupported;
public static int HashSizeInBytes => SHA3_512.HashSizeInBytes;
+ public static HashAlgorithm Create() => SHA3_512.Create();
}
- protected override HashAlgorithm Create() => SHA3_512.Create();
protected override HashAlgorithmName HashAlgorithm => HashAlgorithmName.SHA3_512;
protected override bool TryHashData(ReadOnlySpan source, Span destination, out int bytesWritten)
diff --git a/src/libraries/System.Security.Cryptography/tests/Sha512ManagedTests.cs b/src/libraries/System.Security.Cryptography/tests/Sha512ManagedTests.cs
index e616e4fea13e00..d6299ec4c84f5b 100644
--- a/src/libraries/System.Security.Cryptography/tests/Sha512ManagedTests.cs
+++ b/src/libraries/System.Security.Cryptography/tests/Sha512ManagedTests.cs
@@ -6,11 +6,13 @@ namespace System.Security.Cryptography.Tests
///
/// Sha512Managed has a copy of the same implementation as SHA512
///
- public class Sha512ManagedTests : Sha512Tests
+ public class Sha512ManagedTests : Sha512Tests
{
- protected override HashAlgorithm Create()
+ public sealed class Traits : IHashTrait
{
- return new SHA512Managed();
+ public static bool IsSupported => true;
+ public static int HashSizeInBytes => SHA512.HashSizeInBytes;
+ public static HashAlgorithm Create() => new SHA512Managed();
}
}
}
diff --git a/src/libraries/System.Security.Cryptography/tests/Sha512Tests.cs b/src/libraries/System.Security.Cryptography/tests/Sha512Tests.cs
index dde06d36469657..7abfdb8a546084 100644
--- a/src/libraries/System.Security.Cryptography/tests/Sha512Tests.cs
+++ b/src/libraries/System.Security.Cryptography/tests/Sha512Tests.cs
@@ -8,15 +8,18 @@
namespace System.Security.Cryptography.Tests
{
- public class Sha512Tests : HashAlgorithmTestDriver
+ public sealed class FactorySha512Tests : Sha512Tests
{
public sealed class Traits : IHashTrait
{
public static bool IsSupported => true;
public static int HashSizeInBytes => SHA512.HashSizeInBytes;
+ public static HashAlgorithm Create() => SHA512.Create();
}
+ }
- protected override HashAlgorithm Create() => SHA512.Create();
+ public abstract class Sha512Tests : HashAlgorithmTestDriver where THashTrait : IHashTrait
+ {
protected override HashAlgorithmName HashAlgorithm => HashAlgorithmName.SHA512;
protected override bool TryHashData(ReadOnlySpan source, Span destination, out int bytesWritten)
diff --git a/src/libraries/System.Security.Cryptography/tests/ShakeTestDriver.cs b/src/libraries/System.Security.Cryptography/tests/ShakeTestDriver.cs
index f7d1ee2de07015..d0c25b1722eeaa 100644
--- a/src/libraries/System.Security.Cryptography/tests/ShakeTestDriver.cs
+++ b/src/libraries/System.Security.Cryptography/tests/ShakeTestDriver.cs
@@ -6,6 +6,8 @@
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
+using Microsoft.DotNet.RemoteExecutor;
+using Microsoft.DotNet.XUnitExtensions;
using Xunit;
namespace System.Security.Cryptography.Tests
@@ -536,5 +538,47 @@ public void IsSupported_AgreesWithPlatform()
{
Assert.Equal(TShakeTrait.IsSupported, PlatformDetection.SupportsSha3);
}
+
+ [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))]
+ public void GetHashAndReset_ConcurrentUseDoesNotCrashProcess()
+ {
+ if (!IsSupported)
+ {
+ throw new SkipTestException("Algorithm is not supported on this platform.");
+ }
+
+ RemoteExecutor.Invoke(static () =>
+ {
+ using (TShake shake = TShakeTrait.Create())
+ {
+ Thread thread1 = new(ThreadWork);
+ Thread thread2 = new(ThreadWork);
+ thread1.Start(shake);
+ thread2.Start(shake);
+ thread1.Join();
+ thread2.Join();
+ }
+ }).Dispose();
+
+ static void ThreadWork(object obj)
+ {
+ TShake shake = (TShake)obj;
+
+ try
+ {
+ byte[] input = new byte[128];
+
+ for (int i = 0; i < 10_000; i++)
+ {
+ TShakeTrait.AppendData(shake, input);
+ TShakeTrait.GetHashAndReset(shake, 128);
+ }
+ }
+ catch
+ {
+ // Ignore all managed exceptions. HashAlgorithm is not thread safe, but we don't want process crashes.
+ }
+ }
+ }
}
}