- 
                Notifications
    You must be signed in to change notification settings 
- Fork 5.2k
Add cryptographic operation counts to prevent process crashes #100371
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
dcd23f6
              93494c4
              567ee58
              cf5757f
              15f6168
              94009ea
              1ba673f
              a7da72b
              File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | 
|---|---|---|
| @@ -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); | ||
| } | ||
| } | ||
| } | ||
| } | 
| Original file line number | Diff line number | Diff line change | 
|---|---|---|
| @@ -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<byte> key, ReadOnlySpan<byte> customizationString, bool xof) | ||
| { | ||
| _liteKmac = LiteHashProvider.CreateKmac(algorithmId, key, customizationString, xof); | ||
| } | ||
|  | ||
| public void Append(ReadOnlySpan<byte> data) | ||
| { | ||
| using (ConcurrencyBlock.Enter(ref _block)) | ||
| { | ||
| _liteKmac.Append(data); | ||
| } | ||
| } | ||
|  | ||
| public int Current(Span<byte> destination) | ||
| { | ||
| using (ConcurrencyBlock.Enter(ref _block)) | ||
| { | ||
| return _liteKmac.Current(destination); | ||
| } | ||
| } | ||
|  | ||
| public int Finalize(Span<byte> 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(); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change | 
|---|---|---|
|  | @@ -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<byte> key, bool isHmac) | |
| public sealed override unsafe void AppendHashData(ReadOnlySpan<byte> 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<byte> 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<byte> 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); | ||
| There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Assuming this made it past the  
 If another thread attempted to use that hash during 2 or 3, then some asserts would trip because we never expected  Now, we 
 This ensures we don't have a period of time where _hHash can be null and trip asserts during test runs. | ||
| previousHash?.Dispose(); | ||
| } | ||
|  | ||
| private void DestroyHash() | ||
|  | @@ -161,5 +178,6 @@ private void DestroyHash() | |
|  | ||
| private readonly int _hashSize; | ||
| private bool _running; | ||
| private ConcurrencyBlock _block; | ||
| } | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.