diff --git a/src/libraries/Common/src/Internal/Cryptography/HashProvider.cs b/src/libraries/Common/src/Internal/Cryptography/HashProvider.cs index e77355e5a163a1..4f3e8142c4eebf 100644 --- a/src/libraries/Common/src/Internal/Cryptography/HashProvider.cs +++ b/src/libraries/Common/src/Internal/Cryptography/HashProvider.cs @@ -3,6 +3,8 @@ using System; using System.Diagnostics; +using System.Threading; +using System.Threading.Tasks; namespace Internal.Cryptography { @@ -33,9 +35,11 @@ public void AppendHashData(byte[] data, int offset, int count) public abstract void AppendHashData(ReadOnlySpan data); + public abstract Task AppendHashDataAsync(byte[] array, int ibStart, int cbSize, CancellationToken cancellationToken); + // Compute the hash based on the appended data and resets the HashProvider for more hashing. public abstract int FinalizeHashAndReset(Span destination); - + public abstract Task FinalizeHashAndResetAsync(byte[] destination, CancellationToken cancellationToken); public abstract int GetCurrentHash(Span destination); public byte[] FinalizeHashAndReset() @@ -48,6 +52,16 @@ public byte[] FinalizeHashAndReset() return ret; } + public async Task FinalizeHashAndResetAsync(CancellationToken cancellationToken) + { + byte[] ret = new byte[HashSizeInBytes]; + + int written = await FinalizeHashAndResetAsync(ret, cancellationToken).ConfigureAwait(false); + Debug.Assert(written == HashSizeInBytes); + + return ret; + } + public bool TryFinalizeHashAndReset(Span destination, out int bytesWritten) { if (destination.Length < HashSizeInBytes) diff --git a/src/libraries/Common/src/Internal/Cryptography/HashProviderCng.cs b/src/libraries/Common/src/Internal/Cryptography/HashProviderCng.cs index 36f42e1ee99ea9..ba750a185a59bb 100644 --- a/src/libraries/Common/src/Internal/Cryptography/HashProviderCng.cs +++ b/src/libraries/Common/src/Internal/Cryptography/HashProviderCng.cs @@ -8,6 +8,8 @@ using NTSTATUS = Interop.BCrypt.NTSTATUS; using BCryptOpenAlgorithmProviderFlags = Interop.BCrypt.BCryptOpenAlgorithmProviderFlags; using BCryptCreateHashFlags = Interop.BCrypt.BCryptCreateHashFlags; +using System.Threading; +using System.Threading.Tasks; namespace Internal.Cryptography { @@ -68,7 +70,7 @@ public sealed override unsafe void AppendHashData(ReadOnlySpan source) throw Interop.BCrypt.CreateCryptographicException(ntStatus); } } - + public override Task AppendHashDataAsync(byte[] array, int ibStart, int cbSize, CancellationToken cancellationToken) => throw new NotImplementedException(); public override int FinalizeHashAndReset(Span destination) { Debug.Assert(destination.Length >= _hashSize); @@ -84,6 +86,8 @@ public override int FinalizeHashAndReset(Span destination) return _hashSize; } + public override Task FinalizeHashAndResetAsync(byte[] destination, CancellationToken cancellationToken) => throw new NotImplementedException(); + public override int GetCurrentHash(Span destination) { Debug.Assert(destination.Length >= _hashSize); diff --git a/src/libraries/System.Security.Cryptography.Algorithms/ref/System.Security.Cryptography.Algorithms.cs b/src/libraries/System.Security.Cryptography.Algorithms/ref/System.Security.Cryptography.Algorithms.cs index f14fd4a4f704f8..b19ae2cb5b91e0 100644 --- a/src/libraries/System.Security.Cryptography.Algorithms/ref/System.Security.Cryptography.Algorithms.cs +++ b/src/libraries/System.Security.Cryptography.Algorithms/ref/System.Security.Cryptography.Algorithms.cs @@ -427,11 +427,12 @@ public HMACMD5(byte[] key) { } protected override void Dispose(bool disposing) { } protected override void HashCore(byte[] rgb, int ib, int cb) { } protected override void HashCore(System.ReadOnlySpan source) { } + protected override System.Threading.Tasks.Task HashCoreAsync(byte[] rgb, int ib, int cb, System.Threading.CancellationToken cancellationToken) { throw null; } protected override byte[] HashFinal() { throw null; } + protected override System.Threading.Tasks.Task HashFinalAsync(System.Threading.CancellationToken cancellationToken) { throw null; } public override void Initialize() { } protected override bool TryHashFinal(System.Span destination, out int bytesWritten) { throw null; } } - [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("browser")] public partial class HMACSHA1 : System.Security.Cryptography.HMAC { public HMACSHA1() { } @@ -442,11 +443,12 @@ public HMACSHA1(byte[] key, bool useManagedSha1) { } protected override void Dispose(bool disposing) { } protected override void HashCore(byte[] rgb, int ib, int cb) { } protected override void HashCore(System.ReadOnlySpan source) { } + protected override System.Threading.Tasks.Task HashCoreAsync(byte[] rgb, int ib, int cb, System.Threading.CancellationToken cancellationToken) { throw null; } protected override byte[] HashFinal() { throw null; } + protected override System.Threading.Tasks.Task HashFinalAsync(System.Threading.CancellationToken cancellationToken) { throw null; } public override void Initialize() { } protected override bool TryHashFinal(System.Span destination, out int bytesWritten) { throw null; } } - [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("browser")] public partial class HMACSHA256 : System.Security.Cryptography.HMAC { public HMACSHA256() { } @@ -455,11 +457,12 @@ public HMACSHA256(byte[] key) { } protected override void Dispose(bool disposing) { } protected override void HashCore(byte[] rgb, int ib, int cb) { } protected override void HashCore(System.ReadOnlySpan source) { } + protected override System.Threading.Tasks.Task HashCoreAsync(byte[] rgb, int ib, int cb, System.Threading.CancellationToken cancellationToken) { throw null; } protected override byte[] HashFinal() { throw null; } + protected override System.Threading.Tasks.Task HashFinalAsync(System.Threading.CancellationToken cancellationToken) { throw null; } public override void Initialize() { } protected override bool TryHashFinal(System.Span destination, out int bytesWritten) { throw null; } } - [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("browser")] public partial class HMACSHA384 : System.Security.Cryptography.HMAC { public HMACSHA384() { } @@ -469,11 +472,12 @@ public HMACSHA384(byte[] key) { } protected override void Dispose(bool disposing) { } protected override void HashCore(byte[] rgb, int ib, int cb) { } protected override void HashCore(System.ReadOnlySpan source) { } + protected override System.Threading.Tasks.Task HashCoreAsync(byte[] rgb, int ib, int cb, System.Threading.CancellationToken cancellationToken) { throw null; } protected override byte[] HashFinal() { throw null; } + protected override System.Threading.Tasks.Task HashFinalAsync(System.Threading.CancellationToken cancellationToken) { throw null; } public override void Initialize() { } protected override bool TryHashFinal(System.Span destination, out int bytesWritten) { throw null; } } - [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("browser")] public partial class HMACSHA512 : System.Security.Cryptography.HMAC { public HMACSHA512() { } @@ -483,7 +487,9 @@ public HMACSHA512(byte[] key) { } protected override void Dispose(bool disposing) { } protected override void HashCore(byte[] rgb, int ib, int cb) { } protected override void HashCore(System.ReadOnlySpan source) { } + protected override System.Threading.Tasks.Task HashCoreAsync(byte[] rgb, int ib, int cb, System.Threading.CancellationToken cancellationToken) { throw null; } protected override byte[] HashFinal() { throw null; } + protected override System.Threading.Tasks.Task HashFinalAsync(System.Threading.CancellationToken cancellationToken) { throw null; } public override void Initialize() { } protected override bool TryHashFinal(System.Span destination, out int bytesWritten) { throw null; } } @@ -781,9 +787,12 @@ protected SHA1() { } public static new System.Security.Cryptography.SHA1 Create() { throw null; } public static new System.Security.Cryptography.SHA1? Create(string hashName) { throw null; } public static byte[] HashData(byte[] source) { throw null; } + public static System.Threading.Tasks.Task HashDataAsync(byte[] source, System.Threading.CancellationToken cancellationToken = default) { throw null; } + public static System.Threading.Tasks.Task HashDataAsync(byte[] source, byte[] destination, System.Threading.CancellationToken cancellationToken = default) { throw null; } public static byte[] HashData(System.ReadOnlySpan source) { throw null; } public static int HashData(System.ReadOnlySpan source, System.Span destination) { throw null; } public static bool TryHashData(System.ReadOnlySpan source, System.Span destination, out int bytesWritten) { throw null; } + public static System.Threading.Tasks.Task<(bool IsSuccess, int BytesWritten)> TryHashDataAsync(byte[] source, byte[] destination, System.Threading.CancellationToken cancellationToken = default) { throw null; } } [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] public sealed partial class SHA1Managed : System.Security.Cryptography.SHA1 @@ -792,7 +801,9 @@ public SHA1Managed() { } protected sealed override void Dispose(bool disposing) { } protected sealed override void HashCore(byte[] array, int ibStart, int cbSize) { } protected sealed override void HashCore(System.ReadOnlySpan source) { } + protected sealed override System.Threading.Tasks.Task HashCoreAsync(byte[] array, int ibStart, int cbSize, System.Threading.CancellationToken cancellationToken) { throw null; } protected sealed override byte[] HashFinal() { throw null; } + protected sealed override System.Threading.Tasks.Task HashFinalAsync(System.Threading.CancellationToken cancellationToken) { throw null; } public sealed override void Initialize() { } protected sealed override bool TryHashFinal(System.Span destination, out int bytesWritten) { throw null; } } @@ -802,9 +813,12 @@ protected SHA256() { } public static new System.Security.Cryptography.SHA256 Create() { throw null; } public static new System.Security.Cryptography.SHA256? Create(string hashName) { throw null; } public static byte[] HashData(byte[] source) { throw null; } + public static System.Threading.Tasks.Task HashDataAsync(byte[] source, System.Threading.CancellationToken cancellationToken = default) { throw null; } + public static System.Threading.Tasks.Task HashDataAsync(byte[] source, byte[] destination, System.Threading.CancellationToken cancellationToken = default) { throw null; } public static byte[] HashData(System.ReadOnlySpan source) { throw null; } public static int HashData(System.ReadOnlySpan source, System.Span destination) { throw null; } public static bool TryHashData(System.ReadOnlySpan source, System.Span destination, out int bytesWritten) { throw null; } + public static System.Threading.Tasks.Task<(bool IsSuccess, int BytesWritten)> TryHashDataAsync(byte[] source, byte[] destination, System.Threading.CancellationToken cancellationToken = default) { throw null; } } [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] public sealed partial class SHA256Managed : System.Security.Cryptography.SHA256 @@ -813,7 +827,9 @@ public SHA256Managed() { } protected sealed override void Dispose(bool disposing) { } protected sealed override void HashCore(byte[] array, int ibStart, int cbSize) { } protected sealed override void HashCore(System.ReadOnlySpan source) { } + protected sealed override System.Threading.Tasks.Task HashCoreAsync(byte[] array, int ibStart, int cbSize, System.Threading.CancellationToken cancellationToken) { throw null; } protected sealed override byte[] HashFinal() { throw null; } + protected sealed override System.Threading.Tasks.Task HashFinalAsync(System.Threading.CancellationToken cancellationToken) { throw null; } public sealed override void Initialize() { } protected sealed override bool TryHashFinal(System.Span destination, out int bytesWritten) { throw null; } } @@ -823,9 +839,13 @@ protected SHA384() { } public static new System.Security.Cryptography.SHA384 Create() { throw null; } public static new System.Security.Cryptography.SHA384? Create(string hashName) { throw null; } public static byte[] HashData(byte[] source) { throw null; } + public static System.Threading.Tasks.Task HashDataAsync(byte[] source, System.Threading.CancellationToken cancellationToken = default) { throw null; } + public static System.Threading.Tasks.Task HashDataAsync(byte[] source, byte[] destination, System.Threading.CancellationToken cancellationToken = default) { throw null; } public static byte[] HashData(System.ReadOnlySpan source) { throw null; } public static int HashData(System.ReadOnlySpan source, System.Span destination) { throw null; } public static bool TryHashData(System.ReadOnlySpan source, System.Span destination, out int bytesWritten) { throw null; } + public static System.Threading.Tasks.Task<(bool IsSuccess, int BytesWritten)> TryHashDataAsync(byte[] source, byte[] destination, System.Threading.CancellationToken cancellationToken = default) { throw null; } + } [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] public sealed partial class SHA384Managed : System.Security.Cryptography.SHA384 @@ -834,7 +854,9 @@ public SHA384Managed() { } protected sealed override void Dispose(bool disposing) { } protected sealed override void HashCore(byte[] array, int ibStart, int cbSize) { } protected sealed override void HashCore(System.ReadOnlySpan source) { } + protected sealed override System.Threading.Tasks.Task HashCoreAsync(byte[] array, int ibStart, int cbSize, System.Threading.CancellationToken cancellationToken) { throw null; } protected sealed override byte[] HashFinal() { throw null; } + protected sealed override System.Threading.Tasks.Task HashFinalAsync(System.Threading.CancellationToken cancellationToken) { throw null; } public sealed override void Initialize() { } protected sealed override bool TryHashFinal(System.Span destination, out int bytesWritten) { throw null; } } @@ -844,9 +866,13 @@ protected SHA512() { } public static new System.Security.Cryptography.SHA512 Create() { throw null; } public static new System.Security.Cryptography.SHA512? Create(string hashName) { throw null; } public static byte[] HashData(byte[] source) { throw null; } + public static System.Threading.Tasks.Task HashDataAsync(byte[] source, System.Threading.CancellationToken cancellationToken = default) { throw null; } + public static System.Threading.Tasks.Task HashDataAsync(byte[] source, byte[] destination, System.Threading.CancellationToken cancellationToken = default) { throw null; } public static byte[] HashData(System.ReadOnlySpan source) { throw null; } public static int HashData(System.ReadOnlySpan source, System.Span destination) { throw null; } public static bool TryHashData(System.ReadOnlySpan source, System.Span destination, out int bytesWritten) { throw null; } + public static System.Threading.Tasks.Task<(bool IsSuccess, int BytesWritten)> TryHashDataAsync(byte[] source, byte[] destination, System.Threading.CancellationToken cancellationToken = default) { throw null; } + } [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] public sealed partial class SHA512Managed : System.Security.Cryptography.SHA512 @@ -855,7 +881,9 @@ public SHA512Managed() { } protected sealed override void Dispose(bool disposing) { } protected sealed override void HashCore(byte[] array, int ibStart, int cbSize) { } protected sealed override void HashCore(System.ReadOnlySpan source) { } + protected sealed override System.Threading.Tasks.Task HashCoreAsync(byte[] array, int ibStart, int cbSize, System.Threading.CancellationToken cancellationToken) { throw null; } protected sealed override byte[] HashFinal() { throw null; } + protected sealed override System.Threading.Tasks.Task HashFinalAsync(System.Threading.CancellationToken cancellationToken) { throw null; } public sealed override void Initialize() { } protected sealed override bool TryHashFinal(System.Span destination, out int bytesWritten) { throw null; } } diff --git a/src/libraries/System.Security.Cryptography.Algorithms/src/ExcludeApiList.PNSE.Browser.txt b/src/libraries/System.Security.Cryptography.Algorithms/src/ExcludeApiList.PNSE.Browser.txt index 48bb26d7cd2c6c..c5d50c8123380a 100644 --- a/src/libraries/System.Security.Cryptography.Algorithms/src/ExcludeApiList.PNSE.Browser.txt +++ b/src/libraries/System.Security.Cryptography.Algorithms/src/ExcludeApiList.PNSE.Browser.txt @@ -7,4 +7,8 @@ T:System.Security.Cryptography.SHA512 T:System.Security.Cryptography.SHA1Managed T:System.Security.Cryptography.SHA256Managed T:System.Security.Cryptography.SHA384Managed -T:System.Security.Cryptography.SHA512Managed \ No newline at end of file +T:System.Security.Cryptography.SHA512Managed +T:System.Security.Cryptography.HMACSHA1 +T:System.Security.Cryptography.HMACSHA256 +T:System.Security.Cryptography.HMACSHA384 +T:System.Security.Cryptography.HMACSHA512 diff --git a/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/HMACCommon.cs b/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/HMACCommon.cs index 8d844023b37b43..41c7fbc78b3f44 100644 --- a/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/HMACCommon.cs +++ b/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/HMACCommon.cs @@ -4,6 +4,8 @@ using System; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; +using System.Threading; +using System.Threading.Tasks; namespace Internal.Cryptography { @@ -81,6 +83,9 @@ public void ChangeKey(byte[] key) public void AppendHashData(byte[] data, int offset, int count) => _hMacProvider.AppendHashData(data, offset, count); + public Task AppendHashDataAsync(byte[] data, int offset, int count, CancellationToken cancellationToken) => + _hMacProvider.AppendHashDataAsync(data, offset, count, cancellationToken); + public void AppendHashData(ReadOnlySpan source) => _hMacProvider.AppendHashData(source); @@ -91,6 +96,12 @@ public byte[] FinalizeHashAndReset() => public int FinalizeHashAndReset(Span destination) => _hMacProvider.FinalizeHashAndReset(destination); + public Task FinalizeHashAndResetAsync(CancellationToken cancellationToken) => + _hMacProvider.FinalizeHashAndResetAsync(cancellationToken); + + public Task FinalizeHashAndResetAsync(byte[] destination, CancellationToken cancellationToken) => + _hMacProvider.FinalizeHashAndResetAsync(destination, cancellationToken); + public bool TryFinalizeHashAndReset(Span destination, out int bytesWritten) => _hMacProvider.TryFinalizeHashAndReset(destination, out bytesWritten); diff --git a/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/HashProviderDispenser.Android.cs b/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/HashProviderDispenser.Android.cs index 4b579804fbe342..f4d2018a7bbf97 100644 --- a/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/HashProviderDispenser.Android.cs +++ b/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/HashProviderDispenser.Android.cs @@ -6,6 +6,8 @@ using System.Runtime.InteropServices; using System.Security.Cryptography; using Microsoft.Win32.SafeHandles; +using System.Threading; +using System.Threading.Tasks; namespace Internal.Cryptography { @@ -49,10 +51,9 @@ public static unsafe HashProvider CreateMacProvider(string hashAlgorithmId, Read public static class OneShotHashProvider { - public static int HashData(string hashAlgorithmId, ReadOnlySpan source, Span destination) - { - throw new NotImplementedException(); - } + public static int HashData(string hashAlgorithmId, ReadOnlySpan source, Span destination) => throw new NotImplementedException(); + + public static Task HashDataAsync(string hashAlgorithmId, byte[] source, byte[] destination, CancellationToken cancellationToken) => throw new NotImplementedException(); } private sealed class NotImplementedHashProvider : HashProvider @@ -66,11 +67,15 @@ public override void AppendHashData(ReadOnlySpan data) throw new NotImplementedException(); } + public override Task AppendHashDataAsync(byte[] array, int ibStart, int cbSize, CancellationToken cancellationToken) => throw new NotImplementedException(); + public override int FinalizeHashAndReset(Span destination) { throw new NotImplementedException(); } + public override Task FinalizeHashAndResetAsync(byte[] destination, CancellationToken cancellationToken) => throw new NotImplementedException(); + public override int GetCurrentHash(Span destination) { throw new NotImplementedException(); diff --git a/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/HashProviderDispenser.Browser.cs b/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/HashProviderDispenser.Browser.cs index 17d2292f45362f..98325353c8146d 100644 --- a/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/HashProviderDispenser.Browser.cs +++ b/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/HashProviderDispenser.Browser.cs @@ -4,39 +4,306 @@ using System; using System.Diagnostics; using System.Runtime.InteropServices; +using System.Runtime.InteropServices.JavaScript; using System.Security.Cryptography; -using Microsoft.Win32.SafeHandles; +using System.Threading; +using System.Threading.Tasks; namespace Internal.Cryptography { internal static partial class HashProviderDispenser { + private static readonly JSObject? s_crypto = (JSObject)System.Runtime.InteropServices.JavaScript.Runtime.GetGlobalObject("crypto"); + private static readonly JSObject? s_subtle = (JSObject?)s_crypto?.GetObjectProperty("subtle"); + public static HashProvider CreateHashProvider(string hashAlgorithmId) { - switch (hashAlgorithmId) + if (s_subtle == null) { - case HashAlgorithmNames.SHA1: - case HashAlgorithmNames.SHA256: - case HashAlgorithmNames.SHA384: - case HashAlgorithmNames.SHA512: - return new SHAHashProvider(hashAlgorithmId); + Debug.Fail($"WebCrypto can not be found"); + throw new CryptographicException(); } - throw new CryptographicException(SR.Format(SR.Cryptography_UnknownHashAlgorithm, hashAlgorithmId)); + + return new BrowserAsyncDigestProvider(hashAlgorithmId); } - public static class OneShotHashProvider + public static unsafe HashProvider CreateMacProvider(string hashAlgorithmId, ReadOnlySpan key) { - public static int HashData(string hashAlgorithmId, ReadOnlySpan source, Span destination) + if (s_subtle == null) { - HashProvider provider = HashProviderDispenser.CreateHashProvider(hashAlgorithmId); - provider.AppendHashData(source); - return provider.FinalizeHashAndReset(destination); + Debug.Fail($"WebCrypto can not be found"); + throw new CryptographicException(); } + + return new BrowserAsyncHmacProvider(hashAlgorithmId, key); } + private static string HashAlgorithmToPal(string hashAlgorithmId) => hashAlgorithmId switch { + // https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/digest + // Note: MD5 is not supported by WebCrypto API + HashAlgorithmNames.SHA1 => "SHA-1", + HashAlgorithmNames.SHA256 => "SHA-256", + HashAlgorithmNames.SHA384 => "SHA-384", + HashAlgorithmNames.SHA512 => "SHA-512", + _ => throw new CryptographicException(SR.Format(SR.Cryptography_UnknownHashAlgorithm, hashAlgorithmId)) + }; - public static unsafe HashProvider CreateMacProvider(string hashAlgorithmId, ReadOnlySpan key) + internal static class OneShotHashProvider + { + public static unsafe int HashData(string hashAlgorithmId, ReadOnlySpan source, Span destination) => throw new PlatformNotSupportedException(SR.SystemSecurityCryptographyAlgorithms_PlatformNotSupported); + + public static async Task HashDataAsync(string hashAlgorithmId, byte[] source, byte[] destination, CancellationToken cancellationToken) + { + string algorithm = HashAlgorithmToPal(hashAlgorithmId); + + int written = 0; + if (s_subtle?.GetObjectProperty("digest") is Function digest) + { + using Uint8Array taSource = Uint8Array.From(source); + using (digest) + if (digest.Call(s_subtle, algorithm, taSource) is Task hic) + { + using ArrayBuffer? digestValue = await hic.ConfigureAwait(false) as ArrayBuffer; + using Uint8Array hashArray = new Uint8Array(digestValue!); + written = hashArray.Length; + Debug.Assert(written == destination.Length); + System.Array.Copy(hashArray.ToArray(), destination, destination.Length); + } + else + { + Debug.Fail($"WebCrypto digest() error."); + throw new CryptographicException(); + } + } + else + { + Debug.Fail($"WebCrypto API can not be found"); + throw new CryptographicException(); + } + return written; + } + } + + private sealed class BrowserAsyncDigestProvider : HashProvider + { + private Uint8Array? _dataToHash; + private readonly string _hashAlgorithm; + public override int HashSizeInBytes { get; } + + internal BrowserAsyncDigestProvider(string algorithm) + { + int hashSizeInBytes; + switch (algorithm) + { + case HashAlgorithmNames.SHA1: + hashSizeInBytes = 20; + break; + case HashAlgorithmNames.SHA256: + hashSizeInBytes = 32; + break; + case HashAlgorithmNames.SHA384: + hashSizeInBytes = 48; + break; + case HashAlgorithmNames.SHA512: + hashSizeInBytes = 64; + break; + default: + throw new CryptographicException(SR.Format(SR.Cryptography_UnknownHashAlgorithm, algorithm)); + } + + _hashAlgorithm = HashAlgorithmToPal(algorithm); + HashSizeInBytes = hashSizeInBytes; + } + + public override void AppendHashData(ReadOnlySpan data) => throw new PlatformNotSupportedException(); + + public override Task AppendHashDataAsync(byte[] array, int ibStart, int cbSize, CancellationToken cancellationToken) + { + //System.Diagnostics.Debug.WriteLine($"BrowserDigestAsyncProvider:AppendHashDataAsync data length: {array.Length}"); + if (_dataToHash == null) + { + _dataToHash = Uint8Array.From(array.AsSpan(ibStart, cbSize)); + } + else + { + // TypedArrays do not support concatenation + // So we have to resort to creating multiple arrays. + long hashLength = _dataToHash.Length; + byte[] appendage = new byte[_dataToHash.Length + cbSize]; + System.Array.Copy(_dataToHash.ToArray(), appendage, hashLength); + System.Array.Copy(array, ibStart, appendage, hashLength, cbSize); + _dataToHash = Uint8Array.From(appendage.AsSpan()); + } + return Task.CompletedTask; + } + public override int FinalizeHashAndReset(Span destination) => throw new PlatformNotSupportedException(); + + public override async Task FinalizeHashAndResetAsync(byte[] destination, CancellationToken cancellationToken) + { + int written = 0; + if (s_subtle?.GetObjectProperty("digest") is Function digest) + { + using (digest) + using (_dataToHash) + if (digest!.Call(s_subtle, _hashAlgorithm, _dataToHash) is Task hic) + { + using ArrayBuffer? digestValue = await hic.ConfigureAwait(false) as ArrayBuffer; + using Uint8Array hashArray = new Uint8Array(digestValue!); + written = hashArray.Length; + Debug.Assert(written == destination.Length); + System.Array.Copy(hashArray.ToArray(), destination, destination.Length); + } + _dataToHash = null; + } + else + { + Debug.Fail($"WebCrypto API can not be found"); + throw new CryptographicException(); + } + return written; + } + + public override int GetCurrentHash(Span destination) => throw new PlatformNotSupportedException(); + + public override void Dispose(bool disposing) + { + if (disposing) + { + _dataToHash?.Dispose(); + _dataToHash = null; + } + } + } + private sealed class BrowserAsyncHmacProvider : HashProvider { - throw new PlatformNotSupportedException(SR.SystemSecurityCryptographyAlgorithms_PlatformNotSupported); + private Uint8Array? _dataToHash; + + private readonly byte[] _key; + private JSObject? _cryptoKey; + private readonly string _hashAlgorithm; + public override int HashSizeInBytes { get; } + + internal BrowserAsyncHmacProvider(string algorithm, ReadOnlySpan key) + { + _key = key.ToArray(); + int hashSizeInBytes; + switch (algorithm) + { + case HashAlgorithmNames.SHA1: + hashSizeInBytes = 20; + break; + case HashAlgorithmNames.SHA256: + hashSizeInBytes = 32; + break; + case HashAlgorithmNames.SHA384: + hashSizeInBytes = 48; + break; + case HashAlgorithmNames.SHA512: + hashSizeInBytes = 64; + break; + default: + throw new CryptographicException(SR.Format(SR.Cryptography_UnknownHashAlgorithm, algorithm)); + } + + _hashAlgorithm = HashAlgorithmToPal(algorithm); + HashSizeInBytes = hashSizeInBytes; + } + + public override void AppendHashData(ReadOnlySpan data) => throw new PlatformNotSupportedException(); + + public override Task AppendHashDataAsync(byte[] array, int ibStart, int cbSize, CancellationToken cancellationToken) + { + if (_dataToHash == null) + { + _dataToHash = Uint8Array.From(array.AsSpan(ibStart, cbSize)); + } + else + { + // TypedArrays do not support concatenation + // So we have to resort to creating multiple arrays. + long hashLength = _dataToHash.Length; + byte[] appendage = new byte[_dataToHash.Length + cbSize]; + System.Array.Copy(_dataToHash.ToArray(), appendage, hashLength); + System.Array.Copy(array, ibStart, appendage, hashLength, cbSize); + _dataToHash = Uint8Array.From(appendage.AsSpan()); + } + return Task.CompletedTask; + } + + public override unsafe int FinalizeHashAndReset(Span destination) => throw new PlatformNotSupportedException(); + + public override async Task FinalizeHashAndResetAsync(byte[] destination, CancellationToken cancellationToken) + { + int written = 0; + if (s_subtle?.GetObjectProperty("sign") is Function sign) + { + await ImportKey().ConfigureAwait(false); + + using (sign) + using (_dataToHash) + if (sign!.Call(s_subtle, "HMAC", _cryptoKey, _dataToHash) is Task hic) + { + using ArrayBuffer? digestValue = await hic.ConfigureAwait(false) as ArrayBuffer; + using Uint8Array hashArray = new Uint8Array(digestValue!); + written = hashArray.Length; + Debug.Assert(written == destination.Length); + System.Array.Copy(hashArray.ToArray(), destination, destination.Length); + } + _dataToHash = null; + } + else + { + Debug.Fail($"WebCrypto API can not be found"); + throw new CryptographicException(); + } + return written; + } + + private async Task ImportKey() + { + if (_cryptoKey == null) + { + if (s_subtle?.GetObjectProperty("importKey") is Function importKey) + { + using Uint8Array keyData = Uint8Array.From(_key); + using JSObject hmacImportParams = new JSObject(); + hmacImportParams.SetObjectProperty("name", "HMAC"); + + using JSObject hash = new JSObject(); + hash.SetObjectProperty("name", _hashAlgorithm); + + hmacImportParams.SetObjectProperty("hash", hash); + + using System.Runtime.InteropServices.JavaScript.Array keyUsages = new System.Runtime.InteropServices.JavaScript.Array(); + keyUsages.Push("sign"); + keyUsages.Push("verify"); + + using (importKey) + if (importKey!.Call(s_subtle, "raw", keyData, hmacImportParams, false, keyUsages) is Task hic) + { + _cryptoKey = await hic.ConfigureAwait(false) as JSObject; + Debug.Assert(_cryptoKey != null); + } + } + else + { + Debug.Fail($"WebCrypto API can not be found"); + throw new CryptographicException(); + } + } + return; + } + + public override unsafe int GetCurrentHash(Span destination) => throw new PlatformNotSupportedException(); + + public override void Dispose(bool disposing) + { + if (disposing) + { + System.Array.Clear(_key, 0, _key.Length); + _cryptoKey?.Dispose(); + _cryptoKey = null; + } + } } } } diff --git a/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/HashProviderDispenser.OSX.cs b/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/HashProviderDispenser.OSX.cs index c01debbde58c1d..fc07552b4b256c 100644 --- a/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/HashProviderDispenser.OSX.cs +++ b/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/HashProviderDispenser.OSX.cs @@ -5,6 +5,8 @@ using System.Diagnostics; using System.Security.Cryptography; using System.Security.Cryptography.Apple; +using System.Threading; +using System.Threading.Tasks; namespace Internal.Cryptography { @@ -59,6 +61,8 @@ public static unsafe int HashData(string hashAlgorithmId, ReadOnlySpan sou return digestSize; } } + + public static Task HashDataAsync(string hashAlgorithmId, byte[] source, byte[] destination, CancellationToken cancellationToken) => throw new NotImplementedException(); } private sealed class AppleHmacProvider : HashProvider @@ -107,6 +111,7 @@ public override void AppendHashData(ReadOnlySpan data) } } + public override Task AppendHashDataAsync(byte[] array, int ibStart, int cbSize, CancellationToken cancellationToken) => throw new NotImplementedException(); private void SetKey() { if (Interop.AppleCrypto.HmacInit(_ctx, _key, _key.Length) != 1) @@ -135,6 +140,8 @@ public override unsafe int FinalizeHashAndReset(Span destination) return HashSizeInBytes; } + public override Task FinalizeHashAndResetAsync(byte[] destination, CancellationToken cancellationToken) => throw new NotImplementedException(); + public override unsafe int GetCurrentHash(Span destination) { Debug.Assert(destination.Length >= HashSizeInBytes); @@ -202,6 +209,8 @@ public override void AppendHashData(ReadOnlySpan data) } } + public override Task AppendHashDataAsync(byte[] array, int ibStart, int cbSize, CancellationToken cancellationToken) => throw new NotImplementedException(); + public override int FinalizeHashAndReset(Span destination) { Debug.Assert(destination.Length >= HashSizeInBytes); @@ -217,6 +226,8 @@ public override int FinalizeHashAndReset(Span destination) return HashSizeInBytes; } + public override Task FinalizeHashAndResetAsync(byte[] destination, CancellationToken cancellationToken) => throw new NotImplementedException(); + public override int GetCurrentHash(Span destination) { Debug.Assert(destination.Length >= HashSizeInBytes); diff --git a/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/HashProviderDispenser.Unix.cs b/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/HashProviderDispenser.Unix.cs index a67223334986f6..c6313bfa4f965a 100644 --- a/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/HashProviderDispenser.Unix.cs +++ b/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/HashProviderDispenser.Unix.cs @@ -6,6 +6,8 @@ using System.Runtime.InteropServices; using System.Security.Cryptography; using Microsoft.Win32.SafeHandles; +using System.Threading; +using System.Threading.Tasks; namespace Internal.Cryptography { @@ -60,6 +62,8 @@ public static unsafe int HashData(string hashAlgorithmId, ReadOnlySpan sou return hashSize; } + + public static Task HashDataAsync(string hashAlgorithmId, byte[] source, byte[] destination, CancellationToken cancellationToken) => throw new NotImplementedException(); } private sealed class EvpHashProvider : HashProvider @@ -87,6 +91,7 @@ public EvpHashProvider(IntPtr algorithmEvp) public override void AppendHashData(ReadOnlySpan data) => Check(Interop.Crypto.EvpDigestUpdate(_ctx, data, data.Length)); + public override Task AppendHashDataAsync(byte[] array, int ibStart, int cbSize, CancellationToken cancellationToken) => throw new NotImplementedException(); public override int FinalizeHashAndReset(Span destination) { Debug.Assert(destination.Length >= _hashSize); @@ -101,6 +106,8 @@ public override int FinalizeHashAndReset(Span destination) return _hashSize; } + public override Task FinalizeHashAndResetAsync(byte[] destination, CancellationToken cancellationToken) => throw new NotImplementedException(); + public override int GetCurrentHash(Span destination) { Debug.Assert(destination.Length >= _hashSize); @@ -144,6 +151,7 @@ public HmacHashProvider(IntPtr algorithmEvp, ReadOnlySpan key) public override void AppendHashData(ReadOnlySpan data) => Check(Interop.Crypto.HmacUpdate(_hmacCtx, data, data.Length)); + public override Task AppendHashDataAsync(byte[] array, int ibStart, int cbSize, CancellationToken cancellationToken) => throw new NotImplementedException(); public override int FinalizeHashAndReset(Span destination) { @@ -157,6 +165,8 @@ public override int FinalizeHashAndReset(Span destination) return _hashSize; } + public override Task FinalizeHashAndResetAsync(byte[] destination, CancellationToken cancellationToken) => throw new NotImplementedException(); + public override int GetCurrentHash(Span destination) { Debug.Assert(destination.Length >= _hashSize); diff --git a/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/HashProviderDispenser.Windows.cs b/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/HashProviderDispenser.Windows.cs index d7b710be50ccf8..b7092a0ddd0944 100644 --- a/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/HashProviderDispenser.Windows.cs +++ b/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/HashProviderDispenser.Windows.cs @@ -5,6 +5,8 @@ using System.Diagnostics; using System.Runtime.InteropServices; using System.Security.Cryptography; +using System.Threading; +using System.Threading.Tasks; using Microsoft.Win32.SafeHandles; using NTSTATUS = Interop.BCrypt.NTSTATUS; using BCryptOpenAlgorithmProviderFlags = Interop.BCrypt.BCryptOpenAlgorithmProviderFlags; @@ -162,6 +164,8 @@ private static void HashUpdateAndFinish( Interop.BCrypt.BCryptFinishHash(hHash, destination, hashSize, 0); } } + + public static Task HashDataAsync(string hashAlgorithmId, byte[] source, byte[] destination, CancellationToken cancellationToken) => throw new NotImplementedException(); } } } diff --git a/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/SHAHashProvider.Browser.cs b/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/SHAHashProvider.Browser.cs index 9fd4ab1a47661c..0d5ec05c771bb1 100644 --- a/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/SHAHashProvider.Browser.cs +++ b/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/SHAHashProvider.Browser.cs @@ -5,6 +5,8 @@ using System.IO; using System.Diagnostics; using System.Security.Cryptography; +using System.Threading; +using System.Threading.Tasks; namespace Internal.Cryptography { @@ -49,6 +51,7 @@ public override void AppendHashData(ReadOnlySpan data) buffer.Write(data); } + public override Task AppendHashDataAsync(byte[] array, int ibStart, int cbSize, CancellationToken cancellationToken) => throw new NotImplementedException(); public override int FinalizeHashAndReset(Span destination) { GetCurrentHash(destination); @@ -57,6 +60,8 @@ public override int FinalizeHashAndReset(Span destination) return hashSizeInBytes; } + public override Task FinalizeHashAndResetAsync(byte[] destination, CancellationToken cancellationToken) => throw new NotImplementedException(); + public override int GetCurrentHash(Span destination) { Debug.Assert(destination.Length >= hashSizeInBytes); diff --git a/src/libraries/System.Security.Cryptography.Algorithms/src/System.Security.Cryptography.Algorithms.csproj b/src/libraries/System.Security.Cryptography.Algorithms/src/System.Security.Cryptography.Algorithms.csproj index 2c53e1d740319d..765e85129092fb 100644 --- a/src/libraries/System.Security.Cryptography.Algorithms/src/System.Security.Cryptography.Algorithms.csproj +++ b/src/libraries/System.Security.Cryptography.Algorithms/src/System.Security.Cryptography.Algorithms.csproj @@ -592,7 +592,6 @@ - @@ -603,6 +602,13 @@ + + + + + + $(ArtifactsDir)bin/System.Private.Runtime.InteropServices.JavaScript/$(BuildTargetFramework)-$(TargetOS)-$(Configuration)/System.Private.Runtime.InteropServices.JavaScript.dll + diff --git a/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/HMACMD5.cs b/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/HMACMD5.cs index b408c4ae422385..28a4af18bce617 100644 --- a/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/HMACMD5.cs +++ b/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/HMACMD5.cs @@ -3,6 +3,8 @@ using System.Runtime.Versioning; using Internal.Cryptography; +using System.Threading; +using System.Threading.Tasks; namespace System.Security.Cryptography { @@ -59,9 +61,13 @@ protected override void HashCore(byte[] rgb, int ib, int cb) => protected override void HashCore(ReadOnlySpan source) => _hMacCommon.AppendHashData(source); + protected override Task HashCoreAsync(byte[] array, int ibStart, int cbSize, CancellationToken cancellationToken) => throw new PlatformNotSupportedException(); + protected override byte[] HashFinal() => _hMacCommon.FinalizeHashAndReset(); + protected override System.Threading.Tasks.Task HashFinalAsync(System.Threading.CancellationToken cancellationToken) => throw new PlatformNotSupportedException(); + protected override bool TryHashFinal(Span destination, out int bytesWritten) => _hMacCommon.TryFinalizeHashAndReset(destination, out bytesWritten); diff --git a/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/HMACSHA1.cs b/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/HMACSHA1.cs index 5f7b12ea64ebae..f3c96d75affd76 100644 --- a/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/HMACSHA1.cs +++ b/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/HMACSHA1.cs @@ -4,6 +4,8 @@ using Internal.Cryptography; using System.ComponentModel; using System.Runtime.Versioning; +using System.Threading; +using System.Threading.Tasks; namespace System.Security.Cryptography { @@ -11,8 +13,6 @@ namespace System.Security.Cryptography // If you change anything in this class, you must make the same change in the other HMAC* classes. This is a pain but given that the // preexisting contract from the .NET Framework locks all of these into deriving directly from HMAC, it can't be helped. // - - [UnsupportedOSPlatform("browser")] public class HMACSHA1 : HMAC { public HMACSHA1() @@ -66,9 +66,15 @@ protected override void HashCore(byte[] rgb, int ib, int cb) => protected override void HashCore(ReadOnlySpan source) => _hMacCommon.AppendHashData(source); + protected override Task HashCoreAsync(byte[] array, int ibStart, int cbSize, CancellationToken cancellationToken) => + _hMacCommon.AppendHashDataAsync(array, ibStart, cbSize, cancellationToken); + protected override byte[] HashFinal() => _hMacCommon.FinalizeHashAndReset(); + protected override System.Threading.Tasks.Task HashFinalAsync(System.Threading.CancellationToken cancellationToken) => + _hMacCommon.FinalizeHashAndResetAsync(cancellationToken); + protected override bool TryHashFinal(Span destination, out int bytesWritten) => _hMacCommon.TryFinalizeHashAndReset(destination, out bytesWritten); diff --git a/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/HMACSHA256.cs b/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/HMACSHA256.cs index 01212cabc15b08..c944598e6badcf 100644 --- a/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/HMACSHA256.cs +++ b/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/HMACSHA256.cs @@ -6,6 +6,8 @@ using System.Runtime.Versioning; using System.Security.Cryptography; using Internal.Cryptography; +using System.Threading; +using System.Threading.Tasks; namespace System.Security.Cryptography { @@ -14,7 +16,6 @@ namespace System.Security.Cryptography // preexisting contract from the .NET Framework locks all of these into deriving directly from HMAC, it can't be helped. // - [UnsupportedOSPlatform("browser")] public class HMACSHA256 : HMAC { public HMACSHA256() @@ -62,9 +63,15 @@ protected override void HashCore(byte[] rgb, int ib, int cb) => protected override void HashCore(ReadOnlySpan source) => _hMacCommon.AppendHashData(source); + protected override Task HashCoreAsync(byte[] array, int ibStart, int cbSize, CancellationToken cancellationToken) => + _hMacCommon.AppendHashDataAsync(array, ibStart, cbSize, cancellationToken); + protected override byte[] HashFinal() => _hMacCommon.FinalizeHashAndReset(); + protected override System.Threading.Tasks.Task HashFinalAsync(System.Threading.CancellationToken cancellationToken) => + _hMacCommon.FinalizeHashAndResetAsync(cancellationToken); + protected override bool TryHashFinal(Span destination, out int bytesWritten) => _hMacCommon.TryFinalizeHashAndReset(destination, out bytesWritten); diff --git a/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/HMACSHA384.cs b/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/HMACSHA384.cs index 4a3c79ccd5e7fe..00993d49ece9e8 100644 --- a/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/HMACSHA384.cs +++ b/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/HMACSHA384.cs @@ -6,6 +6,8 @@ using System.Runtime.Versioning; using System.Security.Cryptography; using Internal.Cryptography; +using System.Threading; +using System.Threading.Tasks; namespace System.Security.Cryptography { @@ -14,7 +16,6 @@ namespace System.Security.Cryptography // preexisting contract from the .NET Framework locks all of these into deriving directly from HMAC, it can't be helped. // - [UnsupportedOSPlatform("browser")] public class HMACSHA384 : HMAC { public HMACSHA384() @@ -77,10 +78,15 @@ protected override void HashCore(byte[] rgb, int ib, int cb) => protected override void HashCore(ReadOnlySpan source) => _hMacCommon.AppendHashData(source); + protected override Task HashCoreAsync(byte[] array, int ibStart, int cbSize, CancellationToken cancellationToken) => + _hMacCommon.AppendHashDataAsync(array, ibStart, cbSize, cancellationToken); protected override byte[] HashFinal() => _hMacCommon.FinalizeHashAndReset(); + protected override System.Threading.Tasks.Task HashFinalAsync(System.Threading.CancellationToken cancellationToken) => + _hMacCommon.FinalizeHashAndResetAsync(cancellationToken); + protected override bool TryHashFinal(Span destination, out int bytesWritten) => _hMacCommon.TryFinalizeHashAndReset(destination, out bytesWritten); diff --git a/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/HMACSHA512.cs b/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/HMACSHA512.cs index 0e685cd3a23abd..ab3e5fa897093b 100644 --- a/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/HMACSHA512.cs +++ b/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/HMACSHA512.cs @@ -6,6 +6,8 @@ using System.Runtime.Versioning; using System.Security.Cryptography; using Internal.Cryptography; +using System.Threading; +using System.Threading.Tasks; namespace System.Security.Cryptography { @@ -14,7 +16,6 @@ namespace System.Security.Cryptography // preexisting contract from the .NET Framework locks all of these into deriving directly from HMAC, it can't be helped. // - [UnsupportedOSPlatform("browser")] public class HMACSHA512 : HMAC { public HMACSHA512() @@ -76,9 +77,15 @@ protected override void HashCore(byte[] rgb, int ib, int cb) => protected override void HashCore(ReadOnlySpan source) => _hMacCommon.AppendHashData(source); + protected override Task HashCoreAsync(byte[] array, int ibStart, int cbSize, CancellationToken cancellationToken) => + _hMacCommon.AppendHashDataAsync(array, ibStart, cbSize, cancellationToken); + protected override byte[] HashFinal() => _hMacCommon.FinalizeHashAndReset(); + protected override System.Threading.Tasks.Task HashFinalAsync(System.Threading.CancellationToken cancellationToken) => + _hMacCommon.FinalizeHashAndResetAsync(cancellationToken); + protected override bool TryHashFinal(Span destination, out int bytesWritten) => _hMacCommon.TryFinalizeHashAndReset(destination, out bytesWritten); diff --git a/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/MD5.cs b/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/MD5.cs index c9623eb1b4ec43..1d7d19151f458d 100644 --- a/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/MD5.cs +++ b/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/MD5.cs @@ -4,6 +4,8 @@ using System.Diagnostics; using System.Runtime.Versioning; using Internal.Cryptography; +using System.Threading; +using System.Threading.Tasks; namespace System.Security.Cryptography { @@ -118,10 +120,14 @@ protected sealed override void HashCore(byte[] array, int ibStart, int cbSize) = protected sealed override void HashCore(ReadOnlySpan source) => _hashProvider.AppendHashData(source); + protected sealed override Task HashCoreAsync(byte[] array, int ibStart, int cbSize, CancellationToken cancellationToken) => + _hashProvider.AppendHashDataAsync(array, ibStart, cbSize, cancellationToken); protected sealed override byte[] HashFinal() => _hashProvider.FinalizeHashAndReset(); + protected override Task HashFinalAsync(CancellationToken cancellationToken) => throw new PlatformNotSupportedException(); + protected sealed override bool TryHashFinal(Span destination, out int bytesWritten) => _hashProvider.TryFinalizeHashAndReset(destination, out bytesWritten); diff --git a/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/SHA1.cs b/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/SHA1.cs index 6e93067c3493fc..c45f99f5e466c0 100644 --- a/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/SHA1.cs +++ b/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/SHA1.cs @@ -3,6 +3,8 @@ using Internal.Cryptography; using System.Diagnostics; +using System.Threading; +using System.Threading.Tasks; namespace System.Security.Cryptography { @@ -43,6 +45,60 @@ public static byte[] HashData(byte[] source) return HashData(new ReadOnlySpan(source)); } + /// + /// Async Computes the hash of data using the SHA1 algorithm. + /// + /// The data to hash. + /// CancellationToken used to cancel the hash operation. + /// The hash of the data. + /// + /// is . + /// + public static async Task HashDataAsync(byte[] source, CancellationToken cancellationToken = default) + { + if (source is null) + throw new ArgumentNullException(nameof(source)); + + // Note to self: Verify if this will leak or not. + byte[] buffer = GC.AllocateUninitializedArray(HashSizeBytes); + + int written = await HashDataAsync(source, buffer, cancellationToken).ConfigureAwait(false); + + return buffer; + } + + /// + /// Async Computes the hash of data using the SHA1 algorithm. + /// + /// The data to hash. + /// The buffer to receive the hash value. + /// CancellationToken used to cancel the hash operation. + /// The total number of bytes written to . + /// + /// The buffer in is too small to hold the calculated hash + /// size. The SHA1 algorithm always produces a 160-bit hash, or 20 bytes. + /// + /// + /// is . + /// + /// + /// is . + /// + public static async Task HashDataAsync(byte[] source, byte[] destination, CancellationToken cancellationToken = default) + { + if (source is null) + throw new ArgumentNullException(nameof(source)); + + if (destination is null) + throw new ArgumentNullException(nameof(destination)); + + (bool IsSuccess, int BytesWritten) hashResult = await TryHashDataAsync(source, destination, cancellationToken).ConfigureAwait(false); + if (!hashResult.IsSuccess) + throw new ArgumentException(SR.Argument_DestinationTooShort, nameof(destination)); + Debug.Assert(hashResult.BytesWritten == destination.Length); + return hashResult.BytesWritten; + } + /// /// Computes the hash of data using the SHA1 algorithm. /// @@ -102,6 +158,29 @@ public static bool TryHashData(ReadOnlySpan source, Span destination return true; } + /// + /// Attempts to compute the hash of data using the SHA1 algorithm. + /// + /// The data to hash. + /// The buffer to receive the hash value. + /// CancellationToken used to cancel the hash operation. + /// + /// if is too small to hold the + /// calculated hash, otherwise. + /// + public static async Task<(bool IsSuccess, int BytesWritten)> TryHashDataAsync(byte[] source, byte[] destination, CancellationToken cancellationToken = default) + { + if (destination.Length < HashSizeBytes) + { + return (false, 0); + } + + int bytesWritten = await HashProviderDispenser.OneShotHashProvider.HashDataAsync(HashAlgorithmNames.SHA1, source, destination, cancellationToken).ConfigureAwait(false); + Debug.Assert(bytesWritten == HashSizeBytes); + + return (true, bytesWritten); + } + private sealed class Implementation : SHA1 { private readonly HashProvider _hashProvider; @@ -115,12 +194,17 @@ public Implementation() protected sealed override void HashCore(byte[] array, int ibStart, int cbSize) => _hashProvider.AppendHashData(array, ibStart, cbSize); + protected sealed override Task HashCoreAsync(byte[] array, int ibStart, int cbSize, CancellationToken cancellationToken) => + _hashProvider.AppendHashDataAsync(array, ibStart, cbSize, cancellationToken); + protected sealed override void HashCore(ReadOnlySpan source) => _hashProvider.AppendHashData(source); protected sealed override byte[] HashFinal() => _hashProvider.FinalizeHashAndReset(); + protected sealed override Task HashFinalAsync(CancellationToken cancellationToken) => _hashProvider.FinalizeHashAndResetAsync(cancellationToken); + protected sealed override bool TryHashFinal(Span destination, out int bytesWritten) => _hashProvider.TryFinalizeHashAndReset(destination, out bytesWritten); diff --git a/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/SHA1Managed.cs b/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/SHA1Managed.cs index 66cff4dab2805c..4e8356ceb6bd1a 100644 --- a/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/SHA1Managed.cs +++ b/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/SHA1Managed.cs @@ -3,6 +3,8 @@ using Internal.Cryptography; using System.ComponentModel; +using System.Threading; +using System.Threading.Tasks; namespace System.Security.Cryptography { @@ -23,10 +25,13 @@ protected sealed override void HashCore(byte[] array, int ibStart, int cbSize) = protected sealed override void HashCore(ReadOnlySpan source) => _hashProvider.AppendHashData(source); - + protected sealed override Task HashCoreAsync(byte[] array, int ibStart, int cbSize, CancellationToken cancellationToken) => + _hashProvider.AppendHashDataAsync(array, ibStart, cbSize, cancellationToken); protected sealed override byte[] HashFinal() => _hashProvider.FinalizeHashAndReset(); + protected sealed override Task HashFinalAsync(CancellationToken cancellationToken) => throw new PlatformNotSupportedException(); + protected sealed override bool TryHashFinal(Span destination, out int bytesWritten) => _hashProvider.TryFinalizeHashAndReset(destination, out bytesWritten); diff --git a/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/SHA256.cs b/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/SHA256.cs index eae915be4763de..24afe0a7931014 100644 --- a/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/SHA256.cs +++ b/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/SHA256.cs @@ -3,6 +3,8 @@ using Internal.Cryptography; using System.Diagnostics; +using System.Threading; +using System.Threading.Tasks; namespace System.Security.Cryptography { @@ -42,6 +44,60 @@ public static byte[] HashData(byte[] source) return HashData(new ReadOnlySpan(source)); } + /// + /// Async Computes the hash of data using the SHA256 algorithm. + /// + /// The data to hash. + /// CancellationToken used to cancel the hash operation. + /// The hash of the data. + /// + /// is . + /// + public static async Task HashDataAsync(byte[] source, CancellationToken cancellationToken = default) + { + if (source is null) + throw new ArgumentNullException(nameof(source)); + + // Note to self: Verify if this will leak or not. + byte[] buffer = GC.AllocateUninitializedArray(HashSizeBytes); + + int written = await HashDataAsync(source, buffer, cancellationToken).ConfigureAwait(false); + + return buffer; + } + + /// + /// Async Computes the hash of data using the SHA256 algorithm. + /// + /// The data to hash. + /// The buffer to receive the hash value. + /// CancellationToken used to cancel the hash operation. + /// The total number of bytes written to . + /// + /// The buffer in is too small to hold the calculated hash + /// size. The SHA256 algorithm always produces a 256-bit hash, or 32 bytes. + /// + /// + /// is . + /// + /// + /// is . + /// + public static async Task HashDataAsync(byte[] source, byte[] destination, CancellationToken cancellationToken = default) + { + if (source is null) + throw new ArgumentNullException(nameof(source)); + + if (destination is null) + throw new ArgumentNullException(nameof(destination)); + + (bool IsSuccess, int BytesWritten) hashResult = await TryHashDataAsync(source, destination, cancellationToken).ConfigureAwait(false); + if (!hashResult.IsSuccess) + throw new ArgumentException(SR.Argument_DestinationTooShort, nameof(destination)); + Debug.Assert(hashResult.BytesWritten == destination.Length); + return hashResult.BytesWritten; + } + /// /// Computes the hash of data using the SHA256 algorithm. /// @@ -102,6 +158,29 @@ public static bool TryHashData(ReadOnlySpan source, Span destination return true; } + /// + /// Attempts to compute the hash of data using the SHA1 algorithm. + /// + /// The data to hash. + /// The buffer to receive the hash value. + /// CancellationToken used to cancel the hash operation. + /// + /// if is too small to hold the + /// calculated hash, otherwise. + /// + public static async Task<(bool IsSuccess, int BytesWritten)> TryHashDataAsync(byte[] source, byte[] destination, CancellationToken cancellationToken = default) + { + if (destination.Length < HashSizeBytes) + { + return (false, 0); + } + + int bytesWritten = await HashProviderDispenser.OneShotHashProvider.HashDataAsync(HashAlgorithmNames.SHA256, source, destination, cancellationToken).ConfigureAwait(false); + Debug.Assert(bytesWritten == HashSizeBytes); + + return (true, bytesWritten); + } + private sealed class Implementation : SHA256 { private readonly HashProvider _hashProvider; @@ -117,10 +196,13 @@ protected sealed override void HashCore(byte[] array, int ibStart, int cbSize) = protected sealed override void HashCore(ReadOnlySpan source) => _hashProvider.AppendHashData(source); - + protected sealed override Task HashCoreAsync(byte[] array, int ibStart, int cbSize, CancellationToken cancellationToken) => + _hashProvider.AppendHashDataAsync(array, ibStart, cbSize, cancellationToken); protected sealed override byte[] HashFinal() => _hashProvider.FinalizeHashAndReset(); + protected sealed override Task HashFinalAsync(CancellationToken cancellationToken) => _hashProvider.FinalizeHashAndResetAsync(cancellationToken); + protected sealed override bool TryHashFinal(Span destination, out int bytesWritten) => _hashProvider.TryFinalizeHashAndReset(destination, out bytesWritten); diff --git a/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/SHA256Managed.cs b/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/SHA256Managed.cs index 707a0475a0c7ac..365dfebb51a5d3 100644 --- a/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/SHA256Managed.cs +++ b/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/SHA256Managed.cs @@ -3,6 +3,8 @@ using Internal.Cryptography; using System.ComponentModel; +using System.Threading; +using System.Threading.Tasks; namespace System.Security.Cryptography { @@ -23,10 +25,14 @@ protected sealed override void HashCore(byte[] array, int ibStart, int cbSize) = protected sealed override void HashCore(ReadOnlySpan source) => _hashProvider.AppendHashData(source); + protected sealed override Task HashCoreAsync(byte[] array, int ibStart, int cbSize, CancellationToken cancellationToken) => + _hashProvider.AppendHashDataAsync(array, ibStart, cbSize, cancellationToken); protected sealed override byte[] HashFinal() => _hashProvider.FinalizeHashAndReset(); + protected sealed override Task HashFinalAsync(CancellationToken cancellationToken) => throw new PlatformNotSupportedException(); + protected sealed override bool TryHashFinal(Span destination, out int bytesWritten) => _hashProvider.TryFinalizeHashAndReset(destination, out bytesWritten); diff --git a/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/SHA384.cs b/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/SHA384.cs index 903af6823a6377..6c4a35d4721731 100644 --- a/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/SHA384.cs +++ b/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/SHA384.cs @@ -3,6 +3,8 @@ using Internal.Cryptography; using System.Diagnostics; +using System.Threading; +using System.Threading.Tasks; namespace System.Security.Cryptography { @@ -42,6 +44,60 @@ public static byte[] HashData(byte[] source) return HashData(new ReadOnlySpan(source)); } + /// + /// Async Computes the hash of data using the SHA384 algorithm. + /// + /// The data to hash. + /// CancellationToken used to cancel the hash operation. + /// The hash of the data. + /// + /// is . + /// + public static async Task HashDataAsync(byte[] source, CancellationToken cancellationToken = default) + { + if (source is null) + throw new ArgumentNullException(nameof(source)); + + // Note to self: Verify if this will leak or not. + byte[] buffer = GC.AllocateUninitializedArray(HashSizeBytes); + + int written = await HashDataAsync(source, buffer, cancellationToken).ConfigureAwait(false); + + return buffer; + } + + /// + /// Async Computes the hash of data using the SHA384 algorithm. + /// + /// The data to hash. + /// The buffer to receive the hash value. + /// CancellationToken used to cancel the hash operation. + /// The total number of bytes written to . + /// + /// The buffer in is too small to hold the calculated hash + /// size. The SHA384 algorithm always produces a 384-bit hash, or 48 bytes. + /// + /// + /// is . + /// + /// + /// is . + /// + public static async Task HashDataAsync(byte[] source, byte[] destination, CancellationToken cancellationToken = default) + { + if (source is null) + throw new ArgumentNullException(nameof(source)); + + if (destination is null) + throw new ArgumentNullException(nameof(destination)); + + (bool IsSuccess, int BytesWritten) hashResult = await TryHashDataAsync(source, destination, cancellationToken).ConfigureAwait(false); + if (!hashResult.IsSuccess) + throw new ArgumentException(SR.Argument_DestinationTooShort, nameof(destination)); + Debug.Assert(hashResult.BytesWritten == destination.Length); + return hashResult.BytesWritten; + } + /// /// Computes the hash of data using the SHA384 algorithm. /// @@ -101,6 +157,29 @@ public static bool TryHashData(ReadOnlySpan source, Span destination return true; } + /// + /// Attempts to compute the hash of data using the SHA1 algorithm. + /// + /// The data to hash. + /// The buffer to receive the hash value. + /// CancellationToken used to cancel the hash operation. + /// + /// if is too small to hold the + /// calculated hash, otherwise. + /// + public static async Task<(bool IsSuccess, int BytesWritten)> TryHashDataAsync(byte[] source, byte[] destination, CancellationToken cancellationToken = default) + { + if (destination.Length < HashSizeBytes) + { + return (false, 0); + } + + int bytesWritten = await HashProviderDispenser.OneShotHashProvider.HashDataAsync(HashAlgorithmNames.SHA384, source, destination, cancellationToken).ConfigureAwait(false); + Debug.Assert(bytesWritten == HashSizeBytes); + + return (true, bytesWritten); + } + private sealed class Implementation : SHA384 { private readonly HashProvider _hashProvider; @@ -116,10 +195,13 @@ protected sealed override void HashCore(byte[] array, int ibStart, int cbSize) = protected sealed override void HashCore(ReadOnlySpan source) => _hashProvider.AppendHashData(source); - + protected sealed override Task HashCoreAsync(byte[] array, int ibStart, int cbSize, CancellationToken cancellationToken) => + _hashProvider.AppendHashDataAsync(array, ibStart, cbSize, cancellationToken); protected sealed override byte[] HashFinal() => _hashProvider.FinalizeHashAndReset(); + protected sealed override Task HashFinalAsync(CancellationToken cancellationToken) => _hashProvider.FinalizeHashAndResetAsync(cancellationToken); + protected sealed override bool TryHashFinal(Span destination, out int bytesWritten) => _hashProvider.TryFinalizeHashAndReset(destination, out bytesWritten); diff --git a/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/SHA384Managed.cs b/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/SHA384Managed.cs index f8c855dcc11386..f9871dacbb3877 100644 --- a/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/SHA384Managed.cs +++ b/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/SHA384Managed.cs @@ -3,6 +3,8 @@ using Internal.Cryptography; using System.ComponentModel; +using System.Threading; +using System.Threading.Tasks; namespace System.Security.Cryptography { @@ -23,10 +25,13 @@ protected sealed override void HashCore(byte[] array, int ibStart, int cbSize) = protected sealed override void HashCore(ReadOnlySpan source) => _hashProvider.AppendHashData(source); - + protected sealed override Task HashCoreAsync(byte[] array, int ibStart, int cbSize, CancellationToken cancellationToken) => + _hashProvider.AppendHashDataAsync(array, ibStart, cbSize, cancellationToken); protected sealed override byte[] HashFinal() => _hashProvider.FinalizeHashAndReset(); + protected sealed override Task HashFinalAsync(CancellationToken cancellationToken) => throw new PlatformNotSupportedException(); + protected sealed override bool TryHashFinal(Span destination, out int bytesWritten) => _hashProvider.TryFinalizeHashAndReset(destination, out bytesWritten); diff --git a/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/SHA512.cs b/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/SHA512.cs index d435778e5c5901..a590a286c9c446 100644 --- a/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/SHA512.cs +++ b/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/SHA512.cs @@ -3,6 +3,8 @@ using Internal.Cryptography; using System.Diagnostics; +using System.Threading; +using System.Threading.Tasks; namespace System.Security.Cryptography { @@ -42,6 +44,60 @@ public static byte[] HashData(byte[] source) return HashData(new ReadOnlySpan(source)); } + /// + /// Async Computes the hash of data using the SHA512 algorithm. + /// + /// The data to hash. + /// CancellationToken used to cancel the hash operation. + /// The hash of the data. + /// + /// is . + /// + public static async Task HashDataAsync(byte[] source, CancellationToken cancellationToken = default) + { + if (source is null) + throw new ArgumentNullException(nameof(source)); + + // Note to self: Verify if this will leak or not. + byte[] buffer = GC.AllocateUninitializedArray(HashSizeBytes); + + int written = await HashDataAsync(source, buffer, cancellationToken).ConfigureAwait(false); + + return buffer; + } + + /// + /// Async Computes the hash of data using the SHA512 algorithm. + /// + /// The data to hash. + /// The buffer to receive the hash value. + /// CancellationToken used to cancel the hash operation. + /// The total number of bytes written to . + /// + /// The buffer in is too small to hold the calculated hash + /// size. The SHA512 algorithm always produces a 512-bit hash, or 64 bytes. + /// + /// + /// is . + /// + /// + /// is . + /// + public static async Task HashDataAsync(byte[] source, byte[] destination, CancellationToken cancellationToken = default) + { + if (source is null) + throw new ArgumentNullException(nameof(source)); + + if (destination is null) + throw new ArgumentNullException(nameof(destination)); + + (bool IsSuccess, int BytesWritten) hashResult = await TryHashDataAsync(source, destination, cancellationToken).ConfigureAwait(false); + if (!hashResult.IsSuccess) + throw new ArgumentException(SR.Argument_DestinationTooShort, nameof(destination)); + Debug.Assert(hashResult.BytesWritten == destination.Length); + return hashResult.BytesWritten; + } + /// /// Computes the hash of data using the SHA512 algorithm. /// @@ -65,7 +121,7 @@ public static byte[] HashData(ReadOnlySpan source) /// The total number of bytes written to . /// /// The buffer in is too small to hold the calculated hash - /// size. The SHA1 algorithm always produces a 512-bit hash, or 64 bytes. + /// size. The SHA512 algorithm always produces a 512-bit hash, or 64 bytes. /// public static int HashData(ReadOnlySpan source, Span destination) { @@ -101,6 +157,29 @@ public static bool TryHashData(ReadOnlySpan source, Span destination return true; } + /// + /// Attempts to compute the hash of data using the SHA1 algorithm. + /// + /// The data to hash. + /// The buffer to receive the hash value. + /// CancellationToken used to cancel the hash operation. + /// + /// if is too small to hold the + /// calculated hash, otherwise. + /// + public static async Task<(bool IsSuccess, int BytesWritten)> TryHashDataAsync(byte[] source, byte[] destination, CancellationToken cancellationToken = default) + { + if (destination.Length < HashSizeBytes) + { + return (false, 0); + } + + int bytesWritten = await HashProviderDispenser.OneShotHashProvider.HashDataAsync(HashAlgorithmNames.SHA512, source, destination, cancellationToken).ConfigureAwait(false); + Debug.Assert(bytesWritten == HashSizeBytes); + + return (true, bytesWritten); + } + private sealed class Implementation : SHA512 { private readonly HashProvider _hashProvider; @@ -116,10 +195,14 @@ protected sealed override void HashCore(byte[] array, int ibStart, int cbSize) = protected sealed override void HashCore(ReadOnlySpan source) => _hashProvider.AppendHashData(source); + protected sealed override Task HashCoreAsync(byte[] array, int ibStart, int cbSize, CancellationToken cancellationToken) => + _hashProvider.AppendHashDataAsync(array, ibStart, cbSize, cancellationToken); protected sealed override byte[] HashFinal() => _hashProvider.FinalizeHashAndReset(); + protected sealed override Task HashFinalAsync(CancellationToken cancellationToken) => _hashProvider.FinalizeHashAndResetAsync(cancellationToken); + protected sealed override bool TryHashFinal(Span destination, out int bytesWritten) => _hashProvider.TryFinalizeHashAndReset(destination, out bytesWritten); diff --git a/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/SHA512Managed.cs b/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/SHA512Managed.cs index a4374b316e53d3..32e05f264a9255 100644 --- a/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/SHA512Managed.cs +++ b/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/SHA512Managed.cs @@ -3,6 +3,8 @@ using Internal.Cryptography; using System.ComponentModel; +using System.Threading; +using System.Threading.Tasks; namespace System.Security.Cryptography { @@ -23,10 +25,14 @@ protected sealed override void HashCore(byte[] array, int ibStart, int cbSize) = protected sealed override void HashCore(ReadOnlySpan source) => _hashProvider.AppendHashData(source); + protected sealed override Task HashCoreAsync(byte[] array, int ibStart, int cbSize, CancellationToken cancellationToken) => + _hashProvider.AppendHashDataAsync(array, ibStart, cbSize, cancellationToken); protected sealed override byte[] HashFinal() => _hashProvider.FinalizeHashAndReset(); + protected sealed override Task HashFinalAsync(CancellationToken cancellationToken) => throw new PlatformNotSupportedException(); + protected sealed override bool TryHashFinal(Span destination, out int bytesWritten) => _hashProvider.TryFinalizeHashAndReset(destination, out bytesWritten); diff --git a/src/libraries/System.Security.Cryptography.Csp/ref/System.Security.Cryptography.Csp.cs b/src/libraries/System.Security.Cryptography.Csp/ref/System.Security.Cryptography.Csp.cs index ce402e6d386bd8..35dee7497ec6bd 100644 --- a/src/libraries/System.Security.Cryptography.Csp/ref/System.Security.Cryptography.Csp.cs +++ b/src/libraries/System.Security.Cryptography.Csp/ref/System.Security.Cryptography.Csp.cs @@ -135,7 +135,10 @@ public MD5CryptoServiceProvider() { } protected override void Dispose(bool disposing) { } protected override void HashCore(byte[] array, int ibStart, int cbSize) { } protected override void HashCore(System.ReadOnlySpan source) { } + protected override System.Threading.Tasks.Task HashCoreAsync(byte[] array, int ibStart, int cbSize, System.Threading.CancellationToken cancellationToken) { throw null; } protected override byte[] HashFinal() { throw null; } + protected override System.Threading.Tasks.Task HashFinalAsync(System.Threading.CancellationToken cancellationToken) { throw null; } + public override void Initialize() { } protected override bool TryHashFinal(System.Span destination, out int bytesWritten) { throw null; } } @@ -231,7 +234,9 @@ public SHA1CryptoServiceProvider() { } protected override void Dispose(bool disposing) { } protected override void HashCore(byte[] array, int ibStart, int cbSize) { } protected override void HashCore(System.ReadOnlySpan source) { } + protected override System.Threading.Tasks.Task HashCoreAsync(byte[] array, int ibStart, int cbSize, System.Threading.CancellationToken cancellationToken) { throw null; } protected override byte[] HashFinal() { throw null; } + protected override System.Threading.Tasks.Task HashFinalAsync(System.Threading.CancellationToken cancellationToken) { throw null; } public override void Initialize() { } protected override bool TryHashFinal(System.Span destination, out int bytesWritten) { throw null; } } @@ -242,7 +247,9 @@ public SHA256CryptoServiceProvider() { } protected override void Dispose(bool disposing) { } protected override void HashCore(byte[] array, int ibStart, int cbSize) { } protected override void HashCore(System.ReadOnlySpan source) { } + protected override System.Threading.Tasks.Task HashCoreAsync(byte[] array, int ibStart, int cbSize, System.Threading.CancellationToken cancellationToken) { throw null; } protected override byte[] HashFinal() { throw null; } + protected override System.Threading.Tasks.Task HashFinalAsync(System.Threading.CancellationToken cancellationToken) { throw null; } public override void Initialize() { } protected override bool TryHashFinal(System.Span destination, out int bytesWritten) { throw null; } } @@ -253,7 +260,9 @@ public SHA384CryptoServiceProvider() { } protected override void Dispose(bool disposing) { } protected override void HashCore(byte[] array, int ibStart, int cbSize) { } protected override void HashCore(System.ReadOnlySpan source) { } + protected override System.Threading.Tasks.Task HashCoreAsync(byte[] array, int ibStart, int cbSize, System.Threading.CancellationToken cancellationToken) { throw null; } protected override byte[] HashFinal() { throw null; } + protected override System.Threading.Tasks.Task HashFinalAsync(System.Threading.CancellationToken cancellationToken) { throw null; } public override void Initialize() { } protected override bool TryHashFinal(System.Span destination, out int bytesWritten) { throw null; } } @@ -264,7 +273,9 @@ public SHA512CryptoServiceProvider() { } protected override void Dispose(bool disposing) { } protected override void HashCore(byte[] array, int ibStart, int cbSize) { } protected override void HashCore(System.ReadOnlySpan source) { } + protected override System.Threading.Tasks.Task HashCoreAsync(byte[] array, int ibStart, int cbSize, System.Threading.CancellationToken cancellationToken) { throw null; } protected override byte[] HashFinal() { throw null; } + protected override System.Threading.Tasks.Task HashFinalAsync(System.Threading.CancellationToken cancellationToken) { throw null; } public override void Initialize() { } protected override bool TryHashFinal(System.Span destination, out int bytesWritten) { throw null; } } diff --git a/src/libraries/System.Security.Cryptography.Primitives/ref/System.Security.Cryptography.Primitives.cs b/src/libraries/System.Security.Cryptography.Primitives/ref/System.Security.Cryptography.Primitives.cs index 0c7b73074e25f6..28eeb1b444a01f 100644 --- a/src/libraries/System.Security.Cryptography.Primitives/ref/System.Security.Cryptography.Primitives.cs +++ b/src/libraries/System.Security.Cryptography.Primitives/ref/System.Security.Cryptography.Primitives.cs @@ -112,6 +112,8 @@ protected HashAlgorithm() { } public void Clear() { } public byte[] ComputeHash(byte[] buffer) { throw null; } public byte[] ComputeHash(byte[] buffer, int offset, int count) { throw null; } + public System.Threading.Tasks.Task ComputeHashAsync(byte[] buffer, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } + public System.Threading.Tasks.Task ComputeHashAsync(byte[] buffer, int offset, int count, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } public byte[] ComputeHash(System.IO.Stream inputStream) { throw null; } public System.Threading.Tasks.Task ComputeHashAsync(System.IO.Stream inputStream, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } [System.ObsoleteAttribute("The default implementation of this cryptography algorithm is not supported", DiagnosticId = "SYSLIB0007", UrlFormat = "https://aka.ms/dotnet-warnings/{0}")] @@ -120,8 +122,10 @@ public void Clear() { } public void Dispose() { } protected virtual void Dispose(bool disposing) { } protected abstract void HashCore(byte[] array, int ibStart, int cbSize); + protected abstract System.Threading.Tasks.Task HashCoreAsync(byte[] array, int ibStart, int cbSize, System.Threading.CancellationToken cancellationToken); protected virtual void HashCore(System.ReadOnlySpan source) { } protected abstract byte[] HashFinal(); + protected abstract System.Threading.Tasks.Task HashFinalAsync(System.Threading.CancellationToken cancellationToken); public abstract void Initialize(); public int TransformBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[]? outputBuffer, int outputOffset) { throw null; } public byte[] TransformFinalBlock(byte[] inputBuffer, int inputOffset, int inputCount) { throw null; } diff --git a/src/libraries/System.Security.Cryptography.Primitives/src/System.Security.Cryptography.Primitives.csproj b/src/libraries/System.Security.Cryptography.Primitives/src/System.Security.Cryptography.Primitives.csproj index 681bb084de8acf..f7ae73b8212c55 100644 --- a/src/libraries/System.Security.Cryptography.Primitives/src/System.Security.Cryptography.Primitives.csproj +++ b/src/libraries/System.Security.Cryptography.Primitives/src/System.Security.Cryptography.Primitives.csproj @@ -2,7 +2,7 @@ true true - $(NetCoreAppCurrent) + $(NetCoreAppCurrent);$(NetCoreAppCurrent)-Browser enable @@ -14,6 +14,8 @@ + + diff --git a/src/libraries/System.Security.Cryptography.Primitives/src/System/Security/Cryptography/HashAlgorithm.AnyOS.cs b/src/libraries/System.Security.Cryptography.Primitives/src/System/Security/Cryptography/HashAlgorithm.AnyOS.cs new file mode 100644 index 00000000000000..bc512466f6f8e9 --- /dev/null +++ b/src/libraries/System.Security.Cryptography.Primitives/src/System/Security/Cryptography/HashAlgorithm.AnyOS.cs @@ -0,0 +1,38 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Buffers; +using System.IO; +using System.Threading; +using System.Threading.Tasks; + +namespace System.Security.Cryptography +{ + public abstract partial class HashAlgorithm : IDisposable, ICryptoTransform + { + private async Task ComputeHashAsyncCore( + Stream inputStream, + CancellationToken cancellationToken) + { + // Use ArrayPool.Shared instead of CryptoPool because the array is passed out. + byte[] rented = ArrayPool.Shared.Rent(4096); + Memory buffer = rented; + int clearLimit = 0; + int bytesRead; + + while ((bytesRead = await inputStream.ReadAsync(buffer, cancellationToken).ConfigureAwait(false)) > 0) + { + if (bytesRead > clearLimit) + { + clearLimit = bytesRead; + } + + HashCore(rented, 0, bytesRead); + } + + CryptographicOperations.ZeroMemory(rented.AsSpan(0, clearLimit)); + ArrayPool.Shared.Return(rented, clearArray: false); + return CaptureHashCodeAndReinitialize(); + } + } +} diff --git a/src/libraries/System.Security.Cryptography.Primitives/src/System/Security/Cryptography/HashAlgorithm.Browser.cs b/src/libraries/System.Security.Cryptography.Primitives/src/System/Security/Cryptography/HashAlgorithm.Browser.cs new file mode 100644 index 00000000000000..7943cca58f9609 --- /dev/null +++ b/src/libraries/System.Security.Cryptography.Primitives/src/System/Security/Cryptography/HashAlgorithm.Browser.cs @@ -0,0 +1,38 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Buffers; +using System.IO; +using System.Threading; +using System.Threading.Tasks; + +namespace System.Security.Cryptography +{ + public abstract partial class HashAlgorithm : IDisposable, ICryptoTransform + { + private async Task ComputeHashAsyncCore( + Stream inputStream, + CancellationToken cancellationToken) + { + // Use ArrayPool.Shared instead of CryptoPool because the array is passed out. + byte[] rented = ArrayPool.Shared.Rent(4096); + Memory buffer = rented; + int clearLimit = 0; + int bytesRead; + + while ((bytesRead = await inputStream.ReadAsync(buffer, cancellationToken).ConfigureAwait(false)) > 0) + { + if (bytesRead > clearLimit) + { + clearLimit = bytesRead; + } + + await HashCoreAsync(rented, 0, bytesRead, cancellationToken).ConfigureAwait(false); + } + + CryptographicOperations.ZeroMemory(rented.AsSpan(0, clearLimit)); + ArrayPool.Shared.Return(rented, clearArray: false); + return await CaptureHashCodeAndReinitializeAsync(cancellationToken).ConfigureAwait(false); + } + } +} diff --git a/src/libraries/System.Security.Cryptography.Primitives/src/System/Security/Cryptography/HashAlgorithm.cs b/src/libraries/System.Security.Cryptography.Primitives/src/System/Security/Cryptography/HashAlgorithm.cs index 31d64ebd0157f1..62e61bdb3aa037 100644 --- a/src/libraries/System.Security.Cryptography.Primitives/src/System/Security/Cryptography/HashAlgorithm.cs +++ b/src/libraries/System.Security.Cryptography.Primitives/src/System/Security/Cryptography/HashAlgorithm.cs @@ -8,7 +8,7 @@ namespace System.Security.Cryptography { - public abstract class HashAlgorithm : IDisposable, ICryptoTransform + public abstract partial class HashAlgorithm : IDisposable, ICryptoTransform { private bool _disposed; protected int HashSizeValue; @@ -50,6 +50,24 @@ public byte[] ComputeHash(byte[] buffer) return CaptureHashCodeAndReinitialize(); } + public Task ComputeHashAsync(byte[] buffer, CancellationToken cancellationToken = default) + { + if (_disposed) + throw new ObjectDisposedException(null); + if (buffer == null) + throw new ArgumentNullException(nameof(buffer)); + + return ComputeHashAsyncCore(buffer, 0, buffer.Length, cancellationToken); + } + + private async Task ComputeHashAsyncCore( + byte[] array, int ibStart, int cbSize, + CancellationToken cancellationToken) + { + await HashCoreAsync(array, ibStart, cbSize, cancellationToken).ConfigureAwait(false); + return await CaptureHashCodeAndReinitializeAsync(cancellationToken).ConfigureAwait(false); + } + public bool TryComputeHash(ReadOnlySpan source, Span destination, out int bytesWritten) { if (_disposed) @@ -94,6 +112,25 @@ public byte[] ComputeHash(byte[] buffer, int offset, int count) return CaptureHashCodeAndReinitialize(); } + public async Task ComputeHashAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken = default) + { + if (buffer == null) + throw new ArgumentNullException(nameof(buffer)); + if (offset < 0) + throw new ArgumentOutOfRangeException(nameof(offset), SR.ArgumentOutOfRange_NeedNonNegNum); + if (count < 0 || (count > buffer.Length)) + throw new ArgumentException(SR.Argument_InvalidValue); + if ((buffer.Length - count) < offset) + throw new ArgumentException(SR.Argument_InvalidOffLen); + + if (_disposed) + throw new ObjectDisposedException(null); + + await HashCoreAsync(buffer, offset, count, cancellationToken).ConfigureAwait(false); + return await CaptureHashCodeAndReinitializeAsync(cancellationToken).ConfigureAwait(false); + } + + public byte[] ComputeHash(Stream inputStream) { if (_disposed) @@ -132,42 +169,29 @@ public Task ComputeHashAsync( return ComputeHashAsyncCore(inputStream, cancellationToken); } - private async Task ComputeHashAsyncCore( - Stream inputStream, - CancellationToken cancellationToken) + private byte[] CaptureHashCodeAndReinitialize() { - // Use ArrayPool.Shared instead of CryptoPool because the array is passed out. - byte[] rented = ArrayPool.Shared.Rent(4096); - Memory buffer = rented; - int clearLimit = 0; - int bytesRead; - - while ((bytesRead = await inputStream.ReadAsync(buffer, cancellationToken).ConfigureAwait(false)) > 0) - { - if (bytesRead > clearLimit) - { - clearLimit = bytesRead; - } - - HashCore(rented, 0, bytesRead); - } + HashValue = HashFinal(); - CryptographicOperations.ZeroMemory(rented.AsSpan(0, clearLimit)); - ArrayPool.Shared.Return(rented, clearArray: false); - return CaptureHashCodeAndReinitialize(); + // Clone the hash value prior to invoking Initialize in case the user-defined Initialize + // manipulates the array. + byte[] tmp = (byte[])HashValue.Clone(); + Initialize(); + return tmp; } - private byte[] CaptureHashCodeAndReinitialize() + private async Task CaptureHashCodeAndReinitializeAsync(CancellationToken cancellationToken) { - HashValue = HashFinal(); + HashValue = await HashFinalAsync(cancellationToken).ConfigureAwait(false); // Clone the hash value prior to invoking Initialize in case the user-defined Initialize // manipulates the array. byte[] tmp = (byte[])HashValue.Clone(); Initialize(); - return tmp; + return await Task.FromResult(tmp).ConfigureAwait(false); } + public void Dispose() { Dispose(true); @@ -254,7 +278,9 @@ private void ValidateTransformBlock(byte[] inputBuffer, int inputOffset, int inp } protected abstract void HashCore(byte[] array, int ibStart, int cbSize); + protected abstract Task HashCoreAsync(byte[] array, int ibStart, int cbSize, CancellationToken cancellationToken); protected abstract byte[] HashFinal(); + protected abstract Task HashFinalAsync(CancellationToken cancellationToken); public abstract void Initialize(); protected virtual void HashCore(ReadOnlySpan source)