diff options
author | Stephen Toub <stoub@microsoft.com> | 2017-08-10 16:05:48 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-08-10 16:05:48 +0300 |
commit | 154c2a35d5480d359729959f3e3e7d8578a18612 (patch) | |
tree | 715709bd61943affc601b7a785738d1e45191bd7 /src/System.Security.Cryptography.Algorithms | |
parent | 6c8411036d4b196c48433ac5e0118b2de525c01c (diff) |
Add Span-based overloads to HashAlgorithm and IncrementalHash (#23076)
* Add Span-based overloads to HashAlgorithm and IncrementalHash
- Added IncrementalHash.AppendData/TryGetHashAndReset span-based methods
- Added HashAlgorithm.TryComputeHash/HashCore/TryHashFinal span-based methods
- Overrode methods on MD5, SHA1, SHA1Managed, SHA256, SHA256Managed, SHA384, SHA384Managed, SHA512, SHA512Managed, HMACMD5, HMACSHA1, HMACSHA256, HMACSHA384, HMACSHA512, MD5CryptoServiceProvider, SHA1CryptoServiceProvider, SHA256CryptoServiceProvider, SHA512CryptoServiceProvider
- Added tests
- Some minor formatting cleanup along the way to make things more consistent with the new code being added
* Address PR feedback
- Rewrite HashProviderDispenser (OSX) FinalizeHashAndReset in terms of TryFinalizeHashAndReset
- Reorder _running/SetKey check in HashProviderDispenser
- Add bigger-than-needed-span test for HashAlgorithm
- Fix incorrect const usage in original and copied test
- Change HashAlgorithm.Hash to avoid throwing if HashValue is null
- Make TryComputeHash null out HashValue on success
- Cache HashSizeValue/8 into a local in TryHashFinal rather than recomputing
Diffstat (limited to 'src/System.Security.Cryptography.Algorithms')
24 files changed, 589 insertions, 303 deletions
diff --git a/src/System.Security.Cryptography.Algorithms/ref/System.Security.Cryptography.Algorithms.netcoreapp.cs b/src/System.Security.Cryptography.Algorithms/ref/System.Security.Cryptography.Algorithms.netcoreapp.cs index b2d67d0e63..c96d9ce3ca 100644 --- a/src/System.Security.Cryptography.Algorithms/ref/System.Security.Cryptography.Algorithms.netcoreapp.cs +++ b/src/System.Security.Cryptography.Algorithms/ref/System.Security.Cryptography.Algorithms.netcoreapp.cs @@ -8,6 +8,12 @@ namespace System.Security.Cryptography { + public sealed partial class IncrementalHash : System.IDisposable + { + public void AppendData(ReadOnlySpan<byte> data) { } + public bool TryGetHashAndReset(Span<byte> destination, out int bytesWritten) { throw null; } + } + public abstract partial class RandomNumberGenerator : System.IDisposable { public virtual void GetBytes(Span<byte> data) { } diff --git a/src/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/HMACCommon.cs b/src/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/HMACCommon.cs index a5f5aa6b54..8a6d4006bd 100644 --- a/src/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/HMACCommon.cs +++ b/src/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/HMACCommon.cs @@ -4,9 +4,6 @@ using System; using System.Diagnostics; -using System.Security.Cryptography; - -using Internal.Cryptography; namespace Internal.Cryptography { @@ -30,13 +27,7 @@ namespace Internal.Cryptography ChangeKey(key); } - public int HashSizeInBits - { - get - { - return _hMacProvider.HashSizeInBytes * 8; - } - } + public int HashSizeInBits => _hMacProvider.HashSizeInBytes * 8; public void ChangeKey(byte[] key) { @@ -46,15 +37,16 @@ namespace Internal.Cryptography { // Perform RFC 2104, section 2 key adjustment. if (_lazyHashProvider == null) + { _lazyHashProvider = HashProviderDispenser.CreateHashProvider(_hashAlgorithmId); + } _lazyHashProvider.AppendHashData(key, 0, key.Length); key = _lazyHashProvider.FinalizeHashAndReset(); } HashProvider oldHashProvider = _hMacProvider; _hMacProvider = null; - if (oldHashProvider != null) - oldHashProvider.Dispose(true); + oldHashProvider?.Dispose(true); _hMacProvider = HashProviderDispenser.CreateMacProvider(_hashAlgorithmId, key); ActualKey = key; @@ -65,31 +57,31 @@ namespace Internal.Cryptography public byte[] ActualKey { get; private set; } // Adds new data to be hashed. This can be called repeatedly in order to hash data from noncontiguous sources. - public void AppendHashData(byte[] data, int offset, int count) - { + public void AppendHashData(byte[] data, int offset, int count) => _hMacProvider.AppendHashData(data, offset, count); - } + + public void AppendHashData(ReadOnlySpan<byte> source) => + _hMacProvider.AppendHashData(source); // Compute the hash based on the appended data and resets the HashProvider for more hashing. - public byte[] FinalizeHashAndReset() - { - return _hMacProvider.FinalizeHashAndReset(); - } + public byte[] FinalizeHashAndReset() => + _hMacProvider.FinalizeHashAndReset(); + + public bool TryFinalizeHashAndReset(Span<byte> destination, out int bytesWritten) => + _hMacProvider.TryFinalizeHashAndReset(destination, out bytesWritten); public void Dispose(bool disposing) { - if (disposing) + if (disposing && _hMacProvider != null) { - if (_hMacProvider != null) - _hMacProvider.Dispose(true); + _hMacProvider.Dispose(true); _hMacProvider = null; } } - private readonly String _hashAlgorithmId; + private readonly string _hashAlgorithmId; private HashProvider _hMacProvider; private volatile HashProvider _lazyHashProvider; - private readonly int _blockSize; } } diff --git a/src/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/HashProviderDispenser.OSX.cs b/src/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/HashProviderDispenser.OSX.cs index ea9fbaa465..b663864fd8 100644 --- a/src/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/HashProviderDispenser.OSX.cs +++ b/src/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/HashProviderDispenser.OSX.cs @@ -86,43 +86,22 @@ namespace Internal.Cryptography HashSizeInBytes = hashSizeInBytes; } - public override unsafe void AppendHashDataCore(byte[] data, int offset, int count) + public override void AppendHashData(ReadOnlySpan<byte> data) { - Debug.Assert(data != null); - Debug.Assert(offset >= 0); - Debug.Assert(offset < data.Length); - Debug.Assert(count >= 0); - Debug.Assert(data.Length - offset > count); - if (!_running) { SetKey(); } - int ret; - - fixed (byte* pData = data) - { - byte* pbData = pData + offset; - ret = Interop.AppleCrypto.HmacUpdate(_ctx, pbData, count); - } - - if (ret != 1) + if (Interop.AppleCrypto.HmacUpdate(_ctx, data, data.Length) != 1) { throw new CryptographicException(); } } - private unsafe void SetKey() + private void SetKey() { - int ret; - - fixed (byte* pbKey = _key) - { - ret = Interop.AppleCrypto.HmacInit(_ctx, pbKey, _key.Length); - } - - if (ret != 1) + if (Interop.AppleCrypto.HmacInit(_ctx, _key, _key.Length) != 1) { throw new CryptographicException(); } @@ -132,26 +111,34 @@ namespace Internal.Cryptography public override unsafe byte[] FinalizeHashAndReset() { - if (!_running) + var output = new byte[HashSizeInBytes]; + bool success = TryFinalizeHashAndReset(output, out int bytesWritten); + Debug.Assert(success); + Debug.Assert(bytesWritten == output.Length); + return output; + } + + public override bool TryFinalizeHashAndReset(Span<byte> destination, out int bytesWritten) + { + if (destination.Length < HashSizeInBytes) { - SetKey(); + bytesWritten = 0; + return false; } - byte[] output = new byte[HashSizeInBytes]; - int ret; - - fixed (byte* pbOutput = output) + if (!_running) { - ret = Interop.AppleCrypto.HmacFinal(_ctx, pbOutput, output.Length); + SetKey(); } - if (ret != 1) + if (Interop.AppleCrypto.HmacFinal(_ctx, destination, destination.Length) != 1) { throw new CryptographicException(); } + bytesWritten = HashSizeInBytes; _running = false; - return output; + return true; } public override void Dispose(bool disposing) @@ -193,22 +180,9 @@ namespace Internal.Cryptography HashSizeInBytes = hashSizeInBytes; } - public override unsafe void AppendHashDataCore(byte[] data, int offset, int count) + public override void AppendHashData(ReadOnlySpan<byte> data) { - Debug.Assert(data != null); - Debug.Assert(offset >= 0); - Debug.Assert(offset < data.Length); - Debug.Assert(count >= 0); - Debug.Assert(data.Length - offset > count); - - int ret; - - fixed (byte* pData = data) - { - byte* pbData = pData + offset; - ret = Interop.AppleCrypto.DigestUpdate(_ctx, pbData, count); - } - + int ret = Interop.AppleCrypto.DigestUpdate(_ctx, data, data.Length); if (ret != 1) { Debug.Assert(ret == 0, $"DigestUpdate return value {ret} was not 0 or 1"); @@ -216,23 +190,32 @@ namespace Internal.Cryptography } } - public override unsafe byte[] FinalizeHashAndReset() + public override byte[] FinalizeHashAndReset() { - byte[] hash = new byte[HashSizeInBytes]; - int ret; + var hash = new byte[HashSizeInBytes]; + bool success = TryFinalizeHashAndReset(hash, out int bytesWritten); + Debug.Assert(success); + Debug.Assert(bytesWritten == hash.Length); + return hash; + } - fixed (byte* pHash = hash) + public override bool TryFinalizeHashAndReset(Span<byte> destination, out int bytesWritten) + { + if (destination.Length < HashSizeInBytes) { - ret = Interop.AppleCrypto.DigestFinal(_ctx, pHash, hash.Length); + bytesWritten = 0; + return false; } + int ret = Interop.AppleCrypto.DigestFinal(_ctx, destination, destination.Length); if (ret != 1) { Debug.Assert(ret == 0, $"DigestFinal return value {ret} was not 0 or 1"); throw new CryptographicException(); } - return hash; + bytesWritten = HashSizeInBytes; + return true; } public override void Dispose(bool disposing) diff --git a/src/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/HashProviderDispenser.Unix.cs b/src/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/HashProviderDispenser.Unix.cs index 24473ee480..c838da7bfc 100644 --- a/src/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/HashProviderDispenser.Unix.cs +++ b/src/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/HashProviderDispenser.Unix.cs @@ -74,35 +74,43 @@ namespace Internal.Cryptography Interop.Crypto.CheckValidOpenSslHandle(_ctx); } - public sealed override unsafe void AppendHashDataCore(byte[] data, int offset, int count) + public override void AppendHashData(ReadOnlySpan<byte> data) => + Check(Interop.Crypto.EvpDigestUpdate(_ctx, data, data.Length)); + + public override byte[] FinalizeHashAndReset() { - fixed (byte* md = data) - { - Check(Interop.Crypto.EvpDigestUpdate(_ctx, md + offset, count)); - } + var result = new byte[_hashSize]; + bool success = TryFinalizeHashAndReset(result, out int bytesWritten); + Debug.Assert(success); + Debug.Assert(result.Length == bytesWritten); + return result; } - public sealed override unsafe byte[] FinalizeHashAndReset() + public override unsafe bool TryFinalizeHashAndReset(Span<byte> destination, out int bytesWritten) { - byte* md = stackalloc byte[Interop.Crypto.EVP_MAX_MD_SIZE]; - uint length = (uint)Interop.Crypto.EVP_MAX_MD_SIZE; - Check(Interop.Crypto.EvpDigestFinalEx(_ctx, md, ref length)); - Debug.Assert(length == _hashSize); + if (destination.Length < _hashSize) + { + bytesWritten = 0; + return false; + } + + fixed (byte* ptrDest = &destination.DangerousGetPinnableReference()) + { + uint length = (uint)destination.Length; + Check(Interop.Crypto.EvpDigestFinalEx(_ctx, ptrDest, ref length)); + Debug.Assert(length == _hashSize); + bytesWritten = (int)length; + } // Reset the algorithm provider. Check(Interop.Crypto.EvpDigestReset(_ctx, _algorithmEvp)); - byte[] result = new byte[(int)length]; - Marshal.Copy((IntPtr)md, result, 0, (int)length); - return result; + return true; } - public sealed override int HashSizeInBytes - { - get { return _hashSize; } - } + public override int HashSizeInBytes => _hashSize; - public sealed override void Dispose(bool disposing) + public override void Dispose(bool disposing) { if (disposing) { @@ -134,42 +142,46 @@ namespace Internal.Cryptography } } - public sealed override unsafe void AppendHashDataCore(byte[] data, int offset, int count) + public override void AppendHashData(ReadOnlySpan<byte> data) => + Check(Interop.Crypto.HmacUpdate(_hmacCtx, data, data.Length)); + + public override byte[] FinalizeHashAndReset() { - fixed (byte* md = data) - { - Check(Interop.Crypto.HmacUpdate(_hmacCtx, md + offset, count)); - } + var hash = new byte[_hashSize]; + bool success = TryFinalizeHashAndReset(hash, out int bytesWritten); + Debug.Assert(success); + Debug.Assert(hash.Length == bytesWritten); + return hash; } - public sealed override unsafe byte[] FinalizeHashAndReset() + public override unsafe bool TryFinalizeHashAndReset(Span<byte> destination, out int bytesWritten) { - byte* md = stackalloc byte[Interop.Crypto.EVP_MAX_MD_SIZE]; - int length = Interop.Crypto.EVP_MAX_MD_SIZE; - Check(Interop.Crypto.HmacFinal(_hmacCtx, md, ref length)); - Debug.Assert(length == _hashSize); + if (destination.Length < _hashSize) + { + bytesWritten = 0; + return false; + } - Check(Interop.Crypto.HmacReset(_hmacCtx)); + fixed (byte* ptrDest = &destination.DangerousGetPinnableReference()) + { + int length = destination.Length; + Check(Interop.Crypto.HmacFinal(_hmacCtx, ptrDest, ref length)); + Debug.Assert(length == _hashSize); + bytesWritten = length; + } - byte[] result = new byte[length]; - Marshal.Copy((IntPtr)md, result, 0, length); - return result; + Check(Interop.Crypto.HmacReset(_hmacCtx)); + return true; } - public sealed override int HashSizeInBytes - { - get { return _hashSize; } - } + public override int HashSizeInBytes => _hashSize; - public sealed override void Dispose(bool disposing) + public override void Dispose(bool disposing) { - if (disposing) + if (disposing && _hmacCtx != null) { - if (_hmacCtx != null) - { - _hmacCtx.Dispose(); - _hmacCtx = null; - } + _hmacCtx.Dispose(); + _hmacCtx = null; } } } diff --git a/src/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/HMACMD5.cs b/src/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/HMACMD5.cs index 6e9e633d09..c59156cad9 100644 --- a/src/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/HMACMD5.cs +++ b/src/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/HMACMD5.cs @@ -2,10 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System; -using System.Diagnostics; -using System.Security.Cryptography; - using Internal.Cryptography; namespace System.Security.Cryptography @@ -46,21 +42,22 @@ namespace System.Security.Cryptography } } - protected override void HashCore(byte[] rgb, int ib, int cb) - { + protected override void HashCore(byte[] rgb, int ib, int cb) => _hMacCommon.AppendHashData(rgb, ib, cb); - } - protected override byte[] HashFinal() - { - return _hMacCommon.FinalizeHashAndReset(); - } + protected override void HashCore(ReadOnlySpan<byte> source) => + _hMacCommon.AppendHashData(source); + + protected override byte[] HashFinal() => + _hMacCommon.FinalizeHashAndReset(); + + protected override bool TryHashFinal(Span<byte> destination, out int bytesWritten) => + _hMacCommon.TryFinalizeHashAndReset(destination, out bytesWritten); public override void Initialize() { // Nothing to do here. We expect HashAlgorithm to invoke HashFinal() and Initialize() as a pair. This reflects the // reality that our native crypto providers (e.g. CNG) expose hash finalization and object reinitialization as an atomic operation. - return; } protected override void Dispose(bool disposing) @@ -68,9 +65,11 @@ namespace System.Security.Cryptography if (disposing) { HMACCommon hMacCommon = _hMacCommon; - _hMacCommon = null; if (hMacCommon != null) + { + _hMacCommon = null; hMacCommon.Dispose(disposing); + } } base.Dispose(disposing); } diff --git a/src/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/HMACSHA1.cs b/src/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/HMACSHA1.cs index 3277039ad0..d885c2c2d2 100644 --- a/src/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/HMACSHA1.cs +++ b/src/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/HMACSHA1.cs @@ -49,21 +49,22 @@ namespace System.Security.Cryptography } } - protected override void HashCore(byte[] rgb, int ib, int cb) - { + protected override void HashCore(byte[] rgb, int ib, int cb) => _hMacCommon.AppendHashData(rgb, ib, cb); - } - protected override byte[] HashFinal() - { - return _hMacCommon.FinalizeHashAndReset(); - } + protected override void HashCore(ReadOnlySpan<byte> source) => + _hMacCommon.AppendHashData(source); + + protected override byte[] HashFinal() => + _hMacCommon.FinalizeHashAndReset(); + + protected override bool TryHashFinal(Span<byte> destination, out int bytesWritten) => + _hMacCommon.TryFinalizeHashAndReset(destination, out bytesWritten); public override void Initialize() { // Nothing to do here. We expect HashAlgorithm to invoke HashFinal() and Initialize() as a pair. This reflects the // reality that our native crypto providers (e.g. CNG) expose hash finalization and object reinitialization as an atomic operation. - return; } protected override void Dispose(bool disposing) @@ -71,9 +72,11 @@ namespace System.Security.Cryptography if (disposing) { HMACCommon hMacCommon = _hMacCommon; - _hMacCommon = null; if (hMacCommon != null) + { + _hMacCommon = null; hMacCommon.Dispose(disposing); + } } base.Dispose(disposing); } diff --git a/src/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/HMACSHA256.cs b/src/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/HMACSHA256.cs index 268d4b79f1..59f893c7ab 100644 --- a/src/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/HMACSHA256.cs +++ b/src/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/HMACSHA256.cs @@ -46,21 +46,22 @@ namespace System.Security.Cryptography } } - protected override void HashCore(byte[] rgb, int ib, int cb) - { + protected override void HashCore(byte[] rgb, int ib, int cb) => _hMacCommon.AppendHashData(rgb, ib, cb); - } - protected override byte[] HashFinal() - { - return _hMacCommon.FinalizeHashAndReset(); - } + protected override void HashCore(ReadOnlySpan<byte> source) => + _hMacCommon.AppendHashData(source); + + protected override byte[] HashFinal() => + _hMacCommon.FinalizeHashAndReset(); + + protected override bool TryHashFinal(Span<byte> destination, out int bytesWritten) => + _hMacCommon.TryFinalizeHashAndReset(destination, out bytesWritten); public override void Initialize() { // Nothing to do here. We expect HashAlgorithm to invoke HashFinal() and Initialize() as a pair. This reflects the // reality that our native crypto providers (e.g. CNG) expose hash finalization and object reinitialization as an atomic operation. - return; } protected override void Dispose(bool disposing) @@ -68,9 +69,11 @@ namespace System.Security.Cryptography if (disposing) { HMACCommon hMacCommon = _hMacCommon; - _hMacCommon = null; if (hMacCommon != null) + { + _hMacCommon = null; hMacCommon.Dispose(disposing); + } } base.Dispose(disposing); } diff --git a/src/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/HMACSHA384.cs b/src/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/HMACSHA384.cs index 64f73980b7..1f9ef0a24a 100644 --- a/src/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/HMACSHA384.cs +++ b/src/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/HMACSHA384.cs @@ -62,21 +62,22 @@ namespace System.Security.Cryptography } } - protected override void HashCore(byte[] rgb, int ib, int cb) - { + protected override void HashCore(byte[] rgb, int ib, int cb) => _hMacCommon.AppendHashData(rgb, ib, cb); - } - protected override byte[] HashFinal() - { - return _hMacCommon.FinalizeHashAndReset(); - } + protected override void HashCore(ReadOnlySpan<byte> source) => + _hMacCommon.AppendHashData(source); + + protected override byte[] HashFinal() => + _hMacCommon.FinalizeHashAndReset(); + + protected override bool TryHashFinal(Span<byte> destination, out int bytesWritten) => + _hMacCommon.TryFinalizeHashAndReset(destination, out bytesWritten); public override void Initialize() { // Nothing to do here. We expect HashAlgorithm to invoke HashFinal() and Initialize() as a pair. This reflects the // reality that our native crypto providers (e.g. CNG) expose hash finalization and object reinitialization as an atomic operation. - return; } protected override void Dispose(bool disposing) @@ -84,9 +85,11 @@ namespace System.Security.Cryptography if (disposing) { HMACCommon hMacCommon = _hMacCommon; - _hMacCommon = null; if (hMacCommon != null) + { + _hMacCommon = null; hMacCommon.Dispose(disposing); + } } base.Dispose(disposing); } diff --git a/src/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/HMACSHA512.cs b/src/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/HMACSHA512.cs index f902b5c71e..0ab29f04ca 100644 --- a/src/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/HMACSHA512.cs +++ b/src/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/HMACSHA512.cs @@ -60,21 +60,22 @@ namespace System.Security.Cryptography } } - protected override void HashCore(byte[] rgb, int ib, int cb) - { + protected override void HashCore(byte[] rgb, int ib, int cb) => _hMacCommon.AppendHashData(rgb, ib, cb); - } - protected override byte[] HashFinal() - { - return _hMacCommon.FinalizeHashAndReset(); - } + protected override void HashCore(ReadOnlySpan<byte> source) => + _hMacCommon.AppendHashData(source); + + protected override byte[] HashFinal() => + _hMacCommon.FinalizeHashAndReset(); + + protected override bool TryHashFinal(Span<byte> destination, out int bytesWritten) => + _hMacCommon.TryFinalizeHashAndReset(destination, out bytesWritten); public override void Initialize() { // Nothing to do here. We expect HashAlgorithm to invoke HashFinal() and Initialize() as a pair. This reflects the // reality that our native crypto providers (e.g. CNG) expose hash finalization and object reinitialization as an atomic operation. - return; } protected override void Dispose(bool disposing) @@ -82,9 +83,11 @@ namespace System.Security.Cryptography if (disposing) { HMACCommon hMacCommon = _hMacCommon; - _hMacCommon = null; if (hMacCommon != null) + { + _hMacCommon = null; hMacCommon.Dispose(disposing); + } } base.Dispose(disposing); } diff --git a/src/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/IncrementalHash.cs b/src/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/IncrementalHash.cs index eb46eea65a..13c13280ad 100644 --- a/src/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/IncrementalHash.cs +++ b/src/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/IncrementalHash.cs @@ -40,10 +40,7 @@ namespace System.Security.Cryptography /// <summary> /// Get the name of the algorithm being performed. /// </summary> - public HashAlgorithmName AlgorithmName - { - get { return _algorithmName; } - } + public HashAlgorithmName AlgorithmName => _algorithmName; /// <summary> /// Append the entire contents of <paramref name="data"/> to the data already processed in the hash or HMAC. @@ -92,14 +89,24 @@ namespace System.Security.Cryptography if (_disposed) throw new ObjectDisposedException(typeof(IncrementalHash).Name); + AppendData(new ReadOnlySpan<byte>(data, offset, count)); + } + + public void AppendData(ReadOnlySpan<byte> data) + { + if (_disposed) + { + throw new ObjectDisposedException(typeof(IncrementalHash).Name); + } + + Debug.Assert((_hash != null) ^ (_hmac != null)); if (_hash != null) { - _hash.AppendHashDataCore(data, offset, count); + _hash.AppendHashData(data); } else { - Debug.Assert(_hmac != null, "Both _hash and _hmac were null"); - _hmac.AppendHashData(data, offset, count); + _hmac.AppendHashData(data); } } @@ -113,21 +120,27 @@ namespace System.Security.Cryptography public byte[] GetHashAndReset() { if (_disposed) + { throw new ObjectDisposedException(typeof(IncrementalHash).Name); + } - byte[] hashValue; + Debug.Assert((_hash != null) ^ (_hmac != null)); + return _hash != null ? + _hash.FinalizeHashAndReset() : + _hmac.FinalizeHashAndReset(); + } - if (_hash != null) - { - hashValue = _hash.FinalizeHashAndReset(); - } - else + public bool TryGetHashAndReset(Span<byte> destination, out int bytesWritten) + { + if (_disposed) { - Debug.Assert(_hmac != null, "Both _hash and _hmac were null"); - hashValue = _hmac.FinalizeHashAndReset(); + throw new ObjectDisposedException(typeof(IncrementalHash).Name); } - return hashValue; + Debug.Assert((_hash != null) ^ (_hmac != null)); + return _hash != null ? + _hash.TryFinalizeHashAndReset(destination, out bytesWritten) : + _hmac.TryFinalizeHashAndReset(destination, out bytesWritten); } /// <summary> diff --git a/src/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/MD5.cs b/src/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/MD5.cs index 444ec604c3..0049613885 100644 --- a/src/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/MD5.cs +++ b/src/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/MD5.cs @@ -16,15 +16,9 @@ namespace System.Security.Cryptography { protected MD5() { } - public static new MD5 Create() - { - return new Implementation(); - } + public static new MD5 Create() => new Implementation(); - public static new MD5 Create(String algName) - { - return (MD5)CryptoConfig.CreateFromName(algName); - } + public static new MD5 Create(String algName) => (MD5)CryptoConfig.CreateFromName(algName); private sealed class Implementation : MD5 { @@ -36,21 +30,22 @@ namespace System.Security.Cryptography HashSizeValue = _hashProvider.HashSizeInBytes * 8; } - protected sealed override void HashCore(byte[] array, int ibStart, int cbSize) - { + protected sealed override void HashCore(byte[] array, int ibStart, int cbSize) => _hashProvider.AppendHashData(array, ibStart, cbSize); - } - protected sealed override byte[] HashFinal() - { - return _hashProvider.FinalizeHashAndReset(); - } + protected sealed override void HashCore(ReadOnlySpan<byte> source) => + _hashProvider.AppendHashData(source); + + protected sealed override byte[] HashFinal() => + _hashProvider.FinalizeHashAndReset(); + + protected sealed override bool TryHashFinal(Span<byte> destination, out int bytesWritten) => + _hashProvider.TryFinalizeHashAndReset(destination, out bytesWritten); public sealed override void Initialize() { // Nothing to do here. We expect HashAlgorithm to invoke HashFinal() and Initialize() as a pair. This reflects the // reality that our native crypto providers (e.g. CNG) expose hash finalization and object reinitialization as an atomic operation. - return; } protected sealed override void Dispose(bool disposing) diff --git a/src/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/SHA1.cs b/src/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/SHA1.cs index 1d075f9d88..d9ad3a8b59 100644 --- a/src/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/SHA1.cs +++ b/src/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/SHA1.cs @@ -17,15 +17,9 @@ namespace System.Security.Cryptography protected SHA1() { } [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA5350", Justification = "This is the implementaton of SHA1")] - public static new SHA1 Create() - { - return new Implementation(); - } + public static new SHA1 Create() => new Implementation(); - public static new SHA1 Create(string hashName) - { - return (SHA1)CryptoConfig.CreateFromName(hashName); - } + public static new SHA1 Create(string hashName) => (SHA1)CryptoConfig.CreateFromName(hashName); private sealed class Implementation : SHA1 { @@ -37,21 +31,22 @@ namespace System.Security.Cryptography HashSizeValue = _hashProvider.HashSizeInBytes * 8; } - protected sealed override void HashCore(byte[] array, int ibStart, int cbSize) - { + protected sealed override void HashCore(byte[] array, int ibStart, int cbSize) => _hashProvider.AppendHashData(array, ibStart, cbSize); - } - protected sealed override byte[] HashFinal() - { - return _hashProvider.FinalizeHashAndReset(); - } + protected sealed override void HashCore(ReadOnlySpan<byte> source) => + _hashProvider.AppendHashData(source); + + protected sealed override byte[] HashFinal() => + _hashProvider.FinalizeHashAndReset(); + + protected sealed override bool TryHashFinal(Span<byte> destination, out int bytesWritten) => + _hashProvider.TryFinalizeHashAndReset(destination, out bytesWritten); public sealed override void Initialize() { // Nothing to do here. We expect HashAlgorithm to invoke HashFinal() and Initialize() as a pair. This reflects the // reality that our native crypto providers (e.g. CNG) expose hash finalization and object reinitialization as an atomic operation. - return; } protected sealed override void Dispose(bool disposing) diff --git a/src/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/SHA1Managed.cs b/src/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/SHA1Managed.cs index 1bd1c7867b..fab9ff8252 100644 --- a/src/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/SHA1Managed.cs +++ b/src/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/SHA1Managed.cs @@ -19,21 +19,22 @@ namespace System.Security.Cryptography HashSizeValue = _hashProvider.HashSizeInBytes * 8; } - protected sealed override void HashCore(byte[] array, int ibStart, int cbSize) - { + protected sealed override void HashCore(byte[] array, int ibStart, int cbSize) => _hashProvider.AppendHashData(array, ibStart, cbSize); - } - protected sealed override byte[] HashFinal() - { - return _hashProvider.FinalizeHashAndReset(); - } + protected sealed override void HashCore(ReadOnlySpan<byte> source) => + _hashProvider.AppendHashData(source); + + protected sealed override byte[] HashFinal() => + _hashProvider.FinalizeHashAndReset(); + + protected sealed override bool TryHashFinal(Span<byte> destination, out int bytesWritten) => + _hashProvider.TryFinalizeHashAndReset(destination, out bytesWritten); public sealed override void Initialize() { // Nothing to do here. We expect HashAlgorithm to invoke HashFinal() and Initialize() as a pair. This reflects the // reality that our native crypto providers (e.g. CNG) expose hash finalization and object reinitialization as an atomic operation. - return; } protected sealed override void Dispose(bool disposing) diff --git a/src/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/SHA256.cs b/src/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/SHA256.cs index d8695c40c3..9fe667b55a 100644 --- a/src/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/SHA256.cs +++ b/src/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/SHA256.cs @@ -16,15 +16,9 @@ namespace System.Security.Cryptography { protected SHA256() { } - public static new SHA256 Create() - { - return new Implementation(); - } + public static new SHA256 Create() => new Implementation(); - public static new SHA256 Create(string hashName) - { - return (SHA256)CryptoConfig.CreateFromName(hashName); - } + public static new SHA256 Create(string hashName) => (SHA256)CryptoConfig.CreateFromName(hashName); private sealed class Implementation : SHA256 { @@ -36,21 +30,22 @@ namespace System.Security.Cryptography HashSizeValue = _hashProvider.HashSizeInBytes * 8; } - protected sealed override void HashCore(byte[] array, int ibStart, int cbSize) - { + protected sealed override void HashCore(byte[] array, int ibStart, int cbSize) => _hashProvider.AppendHashData(array, ibStart, cbSize); - } - protected sealed override byte[] HashFinal() - { - return _hashProvider.FinalizeHashAndReset(); - } + protected sealed override void HashCore(ReadOnlySpan<byte> source) => + _hashProvider.AppendHashData(source); + + protected sealed override byte[] HashFinal() => + _hashProvider.FinalizeHashAndReset(); + + protected sealed override bool TryHashFinal(Span<byte> destination, out int bytesWritten) => + _hashProvider.TryFinalizeHashAndReset(destination, out bytesWritten); public sealed override void Initialize() { // Nothing to do here. We expect HashAlgorithm to invoke HashFinal() and Initialize() as a pair. This reflects the // reality that our native crypto providers (e.g. CNG) expose hash finalization and object reinitialization as an atomic operation. - return; } protected sealed override void Dispose(bool disposing) diff --git a/src/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/SHA256Managed.cs b/src/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/SHA256Managed.cs index 7ea4f77873..77496bed44 100644 --- a/src/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/SHA256Managed.cs +++ b/src/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/SHA256Managed.cs @@ -19,21 +19,22 @@ namespace System.Security.Cryptography HashSizeValue = _hashProvider.HashSizeInBytes * 8; } - protected sealed override void HashCore(byte[] array, int ibStart, int cbSize) - { + protected sealed override void HashCore(byte[] array, int ibStart, int cbSize) => _hashProvider.AppendHashData(array, ibStart, cbSize); - } - protected sealed override byte[] HashFinal() - { - return _hashProvider.FinalizeHashAndReset(); - } + protected sealed override void HashCore(ReadOnlySpan<byte> source) => + _hashProvider.AppendHashData(source); + + protected sealed override byte[] HashFinal() => + _hashProvider.FinalizeHashAndReset(); + + protected sealed override bool TryHashFinal(Span<byte> destination, out int bytesWritten) => + _hashProvider.TryFinalizeHashAndReset(destination, out bytesWritten); public sealed override void Initialize() { // Nothing to do here. We expect HashAlgorithm to invoke HashFinal() and Initialize() as a pair. This reflects the // reality that our native crypto providers (e.g. CNG) expose hash finalization and object reinitialization as an atomic operation. - return; } protected sealed override void Dispose(bool disposing) diff --git a/src/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/SHA384.cs b/src/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/SHA384.cs index 2311d7b25e..f5e6b80c52 100644 --- a/src/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/SHA384.cs +++ b/src/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/SHA384.cs @@ -16,15 +16,9 @@ namespace System.Security.Cryptography { protected SHA384() { } - public static new SHA384 Create() - { - return new Implementation(); - } + public static new SHA384 Create() => new Implementation(); - public static new SHA384 Create(string hashName) - { - return (SHA384)CryptoConfig.CreateFromName(hashName); - } + public static new SHA384 Create(string hashName) => (SHA384)CryptoConfig.CreateFromName(hashName); private sealed class Implementation : SHA384 { @@ -36,21 +30,22 @@ namespace System.Security.Cryptography HashSizeValue = _hashProvider.HashSizeInBytes * 8; } - protected sealed override void HashCore(byte[] array, int ibStart, int cbSize) - { + protected sealed override void HashCore(byte[] array, int ibStart, int cbSize) => _hashProvider.AppendHashData(array, ibStart, cbSize); - } - protected sealed override byte[] HashFinal() - { - return _hashProvider.FinalizeHashAndReset(); - } + protected sealed override void HashCore(ReadOnlySpan<byte> source) => + _hashProvider.AppendHashData(source); + + protected sealed override byte[] HashFinal() => + _hashProvider.FinalizeHashAndReset(); + + protected sealed override bool TryHashFinal(Span<byte> destination, out int bytesWritten) => + _hashProvider.TryFinalizeHashAndReset(destination, out bytesWritten); public sealed override void Initialize() { // Nothing to do here. We expect HashAlgorithm to invoke HashFinal() and Initialize() as a pair. This reflects the // reality that our native crypto providers (e.g. CNG) expose hash finalization and object reinitialization as an atomic operation. - return; } protected sealed override void Dispose(bool disposing) diff --git a/src/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/SHA384Managed.cs b/src/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/SHA384Managed.cs index d9c1aff058..6c7780fce8 100644 --- a/src/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/SHA384Managed.cs +++ b/src/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/SHA384Managed.cs @@ -19,21 +19,22 @@ namespace System.Security.Cryptography HashSizeValue = _hashProvider.HashSizeInBytes * 8; } - protected sealed override void HashCore(byte[] array, int ibStart, int cbSize) - { + protected sealed override void HashCore(byte[] array, int ibStart, int cbSize) => _hashProvider.AppendHashData(array, ibStart, cbSize); - } - protected sealed override byte[] HashFinal() - { - return _hashProvider.FinalizeHashAndReset(); - } + protected sealed override void HashCore(ReadOnlySpan<byte> source) => + _hashProvider.AppendHashData(source); + + protected sealed override byte[] HashFinal() => + _hashProvider.FinalizeHashAndReset(); + + protected sealed override bool TryHashFinal(Span<byte> destination, out int bytesWritten) => + _hashProvider.TryFinalizeHashAndReset(destination, out bytesWritten); public sealed override void Initialize() { // Nothing to do here. We expect HashAlgorithm to invoke HashFinal() and Initialize() as a pair. This reflects the // reality that our native crypto providers (e.g. CNG) expose hash finalization and object reinitialization as an atomic operation. - return; } protected sealed override void Dispose(bool disposing) diff --git a/src/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/SHA512.cs b/src/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/SHA512.cs index 584dd0553c..1c62d97cc5 100644 --- a/src/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/SHA512.cs +++ b/src/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/SHA512.cs @@ -16,15 +16,9 @@ namespace System.Security.Cryptography { protected SHA512() { } - public static new SHA512 Create() - { - return new Implementation(); - } + public static new SHA512 Create() => new Implementation(); - public static new SHA512 Create(string hashName) - { - return (SHA512)CryptoConfig.CreateFromName(hashName); - } + public static new SHA512 Create(string hashName) => (SHA512)CryptoConfig.CreateFromName(hashName); private sealed class Implementation : SHA512 { @@ -36,21 +30,22 @@ namespace System.Security.Cryptography HashSizeValue = _hashProvider.HashSizeInBytes * 8; } - protected sealed override void HashCore(byte[] array, int ibStart, int cbSize) - { + protected sealed override void HashCore(byte[] array, int ibStart, int cbSize) => _hashProvider.AppendHashData(array, ibStart, cbSize); - } - protected sealed override byte[] HashFinal() - { - return _hashProvider.FinalizeHashAndReset(); - } + protected sealed override void HashCore(ReadOnlySpan<byte> source) => + _hashProvider.AppendHashData(source); + + protected sealed override byte[] HashFinal() => + _hashProvider.FinalizeHashAndReset(); + + protected sealed override bool TryHashFinal(Span<byte> destination, out int bytesWritten) => + _hashProvider.TryFinalizeHashAndReset(destination, out bytesWritten); public sealed override void Initialize() { // Nothing to do here. We expect HashAlgorithm to invoke HashFinal() and Initialize() as a pair. This reflects the // reality that our native crypto providers (e.g. CNG) expose hash finalization and object reinitialization as an atomic operation. - return; } protected sealed override void Dispose(bool disposing) diff --git a/src/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/SHA512Managed.cs b/src/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/SHA512Managed.cs index cd88bd422e..95435ebe90 100644 --- a/src/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/SHA512Managed.cs +++ b/src/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/SHA512Managed.cs @@ -19,21 +19,22 @@ namespace System.Security.Cryptography HashSizeValue = _hashProvider.HashSizeInBytes * 8; } - protected sealed override void HashCore(byte[] array, int ibStart, int cbSize) - { + protected sealed override void HashCore(byte[] array, int ibStart, int cbSize) => _hashProvider.AppendHashData(array, ibStart, cbSize); - } - protected sealed override byte[] HashFinal() - { - return _hashProvider.FinalizeHashAndReset(); - } + protected sealed override void HashCore(ReadOnlySpan<byte> source) => + _hashProvider.AppendHashData(source); + + protected sealed override byte[] HashFinal() => + _hashProvider.FinalizeHashAndReset(); + + protected sealed override bool TryHashFinal(Span<byte> destination, out int bytesWritten) => + _hashProvider.TryFinalizeHashAndReset(destination, out bytesWritten); public sealed override void Initialize() { // Nothing to do here. We expect HashAlgorithm to invoke HashFinal() and Initialize() as a pair. This reflects the // reality that our native crypto providers (e.g. CNG) expose hash finalization and object reinitialization as an atomic operation. - return; } protected sealed override void Dispose(bool disposing) diff --git a/src/System.Security.Cryptography.Algorithms/tests/HashAlgorithmTest.cs b/src/System.Security.Cryptography.Algorithms/tests/HashAlgorithmTest.cs index e626bd6340..249e6b6c56 100644 --- a/src/System.Security.Cryptography.Algorithms/tests/HashAlgorithmTest.cs +++ b/src/System.Security.Cryptography.Algorithms/tests/HashAlgorithmTest.cs @@ -9,7 +9,7 @@ using Xunit; namespace System.Security.Cryptography.Hashing.Algorithms.Tests { - public abstract class HashAlgorithmTest + public abstract partial class HashAlgorithmTest { protected abstract HashAlgorithm Create(); @@ -208,6 +208,12 @@ namespace System.Security.Cryptography.Hashing.Algorithms.Tests protected void Verify(byte[] input, string output) { + Verify_Array(input, output); + Verify_Span(input, output); + } + + private void Verify_Array(byte[] input, string output) + { byte[] expected = ByteUtils.HexToByteArray(output); byte[] actual; @@ -223,6 +229,8 @@ namespace System.Security.Cryptography.Hashing.Algorithms.Tests } } + partial void Verify_Span(byte[] input, string output); + protected void VerifyRepeating(string input, int repeatCount, string output) { using (Stream stream = new DataRepeatingStream(input, repeatCount)) diff --git a/src/System.Security.Cryptography.Algorithms/tests/HashAlgorithmTest.netcoreapp.cs b/src/System.Security.Cryptography.Algorithms/tests/HashAlgorithmTest.netcoreapp.cs new file mode 100644 index 0000000000..c76951d8dc --- /dev/null +++ b/src/System.Security.Cryptography.Algorithms/tests/HashAlgorithmTest.netcoreapp.cs @@ -0,0 +1,70 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Linq; +using Test.Cryptography; +using Xunit; + +namespace System.Security.Cryptography.Hashing.Algorithms.Tests +{ + public abstract partial class HashAlgorithmTest + { + partial void Verify_Span(byte[] input, string output) + { + byte[] expected = ByteUtils.HexToByteArray(output); + byte[] actual; + int bytesWritten; + + using (HashAlgorithm hash = Create()) + { + // Too small + actual = new byte[expected.Length - 1]; + Assert.False(hash.TryComputeHash(input, actual, out bytesWritten)); + Assert.Equal(0, bytesWritten); + + // Just right + actual = new byte[expected.Length]; + Assert.True(hash.TryComputeHash(input, actual, out bytesWritten)); + Assert.Equal(expected.Length, bytesWritten); + Assert.Equal(expected, actual); + + // Bigger than needed + actual = new byte[expected.Length + 1]; + actual[actual.Length - 1] = 42; + Assert.True(hash.TryComputeHash(input, actual, out bytesWritten)); + Assert.Equal(expected.Length, bytesWritten); + Assert.Equal(expected, actual.AsSpan().Slice(0, expected.Length).ToArray()); + Assert.Equal(42, actual[actual.Length - 1]); + } + } + + [Fact] + public void ComputeHash_TryComputeHash_HashSetExplicitlyByBoth() + { + using (HashAlgorithm hash = Create()) + { + byte[] input = Enumerable.Range(0, 100).Select(i => (byte)i).ToArray(); + + byte[] computeHashResult = hash.ComputeHash(input); + Assert.NotNull(computeHashResult); + Assert.NotNull(hash.Hash); + Assert.NotSame(computeHashResult, hash.Hash); + Assert.Equal(computeHashResult, hash.Hash); + + Assert.True(hash.TryComputeHash(input, computeHashResult, out int bytesWritten)); + Assert.Equal(computeHashResult.Length, bytesWritten); + Assert.Null(hash.Hash); + } + } + + [Fact] + public void Dispose_TryComputeHash_ThrowsException() + { + HashAlgorithm hash = Create(); + hash.Dispose(); + Assert.Throws<ObjectDisposedException>(() => hash.ComputeHash(new byte[1])); + Assert.Throws<ObjectDisposedException>(() => hash.TryComputeHash(new byte[1], new byte[1], out int bytesWritten)); + } + } +} diff --git a/src/System.Security.Cryptography.Algorithms/tests/IncrementalHashTests.cs b/src/System.Security.Cryptography.Algorithms/tests/IncrementalHashTests.cs index e9926220e3..ee0e6cd5d0 100644 --- a/src/System.Security.Cryptography.Algorithms/tests/IncrementalHashTests.cs +++ b/src/System.Security.Cryptography.Algorithms/tests/IncrementalHashTests.cs @@ -8,7 +8,7 @@ using Xunit; namespace System.Security.Cryptography.Algorithms.Tests { - public class IncrementalHashTests + public partial class IncrementalHashTests { // Some arbitrarily chosen OID segments private static readonly byte[] s_hmacKey = { 2, 5, 29, 54, 1, 2, 84, 113, 54, 91, 1, 1, 2, 5, 29, 10, }; @@ -38,6 +38,31 @@ namespace System.Security.Cryptography.Algorithms.Tests }; } + [Fact] + public static void InvalidArguments_Throw() + { + AssertExtensions.Throws<ArgumentException>("hashAlgorithm", () => IncrementalHash.CreateHash(new HashAlgorithmName(null))); + AssertExtensions.Throws<ArgumentException>("hashAlgorithm", () => IncrementalHash.CreateHash(new HashAlgorithmName(""))); + + AssertExtensions.Throws<ArgumentException>("hashAlgorithm", () => IncrementalHash.CreateHMAC(new HashAlgorithmName(null), new byte[1])); + AssertExtensions.Throws<ArgumentException>("hashAlgorithm", () => IncrementalHash.CreateHMAC(new HashAlgorithmName(""), new byte[1])); + + AssertExtensions.Throws<ArgumentNullException>("key", () => IncrementalHash.CreateHMAC(HashAlgorithmName.SHA512, null)); + + using (IncrementalHash incrementalHash = IncrementalHash.CreateHash(HashAlgorithmName.SHA512)) + { + AssertExtensions.Throws<ArgumentNullException>("data", () => incrementalHash.AppendData(null)); + AssertExtensions.Throws<ArgumentNullException>("data", () => incrementalHash.AppendData(null, 0, 0)); + + AssertExtensions.Throws<ArgumentOutOfRangeException>("offset", () => incrementalHash.AppendData(new byte[1], -1, 1)); + + AssertExtensions.Throws<ArgumentOutOfRangeException>("count", () => incrementalHash.AppendData(new byte[1], 0, -1)); + AssertExtensions.Throws<ArgumentOutOfRangeException>("count", () => incrementalHash.AppendData(new byte[1], 0, 2)); + + Assert.Throws<ArgumentException>(() => incrementalHash.AppendData(new byte[2], 1, 2)); + } + } + [Theory] [MemberData(nameof(GetHashAlgorithms))] public static void VerifyIncrementalHash(HashAlgorithm referenceAlgorithm, HashAlgorithmName hashAlgorithm) @@ -45,6 +70,7 @@ namespace System.Security.Cryptography.Algorithms.Tests using (referenceAlgorithm) using (IncrementalHash incrementalHash = IncrementalHash.CreateHash(hashAlgorithm)) { + Assert.Equal(hashAlgorithm, incrementalHash.AlgorithmName); VerifyIncrementalResult(referenceAlgorithm, incrementalHash); } } @@ -86,8 +112,8 @@ namespace System.Security.Cryptography.Algorithms.Tests while (position < s_inputBytes.Length - StepB) { - incrementalHash.AppendData(s_inputBytes, position, StepA); - position += StepA; + incrementalHash.AppendData(s_inputBytes, position, StepB); + position += StepB; } incrementalHash.AppendData(s_inputBytes, position, s_inputBytes.Length - position); diff --git a/src/System.Security.Cryptography.Algorithms/tests/IncrementalHashTests.netcoreapp.cs b/src/System.Security.Cryptography.Algorithms/tests/IncrementalHashTests.netcoreapp.cs new file mode 100644 index 0000000000..a4372f0846 --- /dev/null +++ b/src/System.Security.Cryptography.Algorithms/tests/IncrementalHashTests.netcoreapp.cs @@ -0,0 +1,184 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections.Generic; +using Test.Cryptography; +using Xunit; + +namespace System.Security.Cryptography.Algorithms.Tests +{ + public partial class IncrementalHashTests + { + [Theory] + [MemberData(nameof(GetHashAlgorithms))] + public static void VerifyIncrementalHash_Span(HashAlgorithm referenceAlgorithm, HashAlgorithmName hashAlgorithm) + { + using (referenceAlgorithm) + using (IncrementalHash incrementalHash = IncrementalHash.CreateHash(hashAlgorithm)) + { + VerifyIncrementalResult_Span(referenceAlgorithm, incrementalHash); + } + } + + [Theory] + [MemberData(nameof(GetHMACs))] + public static void VerifyIncrementalHMAC_Span(HMAC referenceAlgorithm, HashAlgorithmName hashAlgorithm) + { + using (referenceAlgorithm) + using (IncrementalHash incrementalHash = IncrementalHash.CreateHMAC(hashAlgorithm, s_hmacKey)) + { + referenceAlgorithm.Key = s_hmacKey; + VerifyIncrementalResult_Span(referenceAlgorithm, incrementalHash); + } + } + + private static void VerifyIncrementalResult_Span(HashAlgorithm referenceAlgorithm, IncrementalHash incrementalHash) + { + int referenceHashLength; + byte[] referenceHash = new byte[1]; + while (!referenceAlgorithm.TryComputeHash(s_inputBytes, referenceHash, out referenceHashLength)) + { + referenceHash = new byte[referenceHash.Length * 2]; + } + + const int StepA = 13; + const int StepB = 7; + int position = 0; + + while (position < s_inputBytes.Length - StepA) + { + incrementalHash.AppendData(new ReadOnlySpan<byte>(s_inputBytes, position, StepA)); + position += StepA; + } + + incrementalHash.AppendData(new ReadOnlySpan<byte>(s_inputBytes, position, s_inputBytes.Length - position)); + + byte[] incrementalA = new byte[referenceHashLength]; + int bytesWritten; + Assert.True(incrementalHash.TryGetHashAndReset(incrementalA, out bytesWritten)); + Assert.Equal(referenceHashLength, bytesWritten); + Assert.Equal<byte>(new Span<byte>(referenceHash, 0, referenceHashLength).ToArray(), new Span<byte>(incrementalA).Slice(0, bytesWritten).ToArray()); + + // Now try again, verifying both immune to step size behaviors, and that GetHashAndReset resets. + position = 0; + + while (position < s_inputBytes.Length - StepB) + { + incrementalHash.AppendData(new ReadOnlySpan<byte>(s_inputBytes, position, StepB)); + position += StepB; + } + + incrementalHash.AppendData(new ReadOnlySpan<byte>(s_inputBytes, position, s_inputBytes.Length - position)); + + byte[] incrementalB = new byte[referenceHashLength]; + Assert.True(incrementalHash.TryGetHashAndReset(incrementalB, out bytesWritten)); + Assert.Equal(referenceHashLength, bytesWritten); + Assert.Equal<byte>(new Span<byte>(referenceHash, 0, referenceHashLength).ToArray(), incrementalB); + } + + [Theory] + [MemberData(nameof(GetHashAlgorithms))] + public static void VerifyEmptyHash_Span(HashAlgorithm referenceAlgorithm, HashAlgorithmName hashAlgorithm) + { + using (referenceAlgorithm) + using (IncrementalHash incrementalHash = IncrementalHash.CreateHash(hashAlgorithm)) + { + for (int i = 0; i < 10; i++) + { + incrementalHash.AppendData(ReadOnlySpan<byte>.Empty); + } + + byte[] referenceHash = referenceAlgorithm.ComputeHash(Array.Empty<byte>()); + byte[] incrementalResult = new byte[referenceHash.Length]; + Assert.True(incrementalHash.TryGetHashAndReset(incrementalResult, out int bytesWritten)); + Assert.Equal(referenceHash.Length, bytesWritten); + Assert.Equal(referenceHash, incrementalResult); + } + } + + [Theory] + [MemberData(nameof(GetHMACs))] + public static void VerifyEmptyHMAC_Span(HMAC referenceAlgorithm, HashAlgorithmName hashAlgorithm) + { + using (referenceAlgorithm) + using (IncrementalHash incrementalHash = IncrementalHash.CreateHMAC(hashAlgorithm, s_hmacKey)) + { + referenceAlgorithm.Key = s_hmacKey; + + for (int i = 0; i < 10; i++) + { + incrementalHash.AppendData(ReadOnlySpan<byte>.Empty); + } + + byte[] referenceHash = referenceAlgorithm.ComputeHash(Array.Empty<byte>()); + byte[] incrementalResult = new byte[referenceHash.Length]; + Assert.True(incrementalHash.TryGetHashAndReset(incrementalResult, out int bytesWritten)); + Assert.Equal(referenceHash.Length, bytesWritten); + Assert.Equal(referenceHash, incrementalResult); + } + } + + [Theory] + [MemberData(nameof(GetHashAlgorithms))] + public static void VerifyTrivialHash_Span(HashAlgorithm referenceAlgorithm, HashAlgorithmName hashAlgorithm) + { + using (referenceAlgorithm) + using (IncrementalHash incrementalHash = IncrementalHash.CreateHash(hashAlgorithm)) + { + byte[] referenceHash = referenceAlgorithm.ComputeHash(Array.Empty<byte>()); + byte[] incrementalResult = new byte[referenceHash.Length]; + Assert.True(incrementalHash.TryGetHashAndReset(incrementalResult, out int bytesWritten)); + Assert.Equal(referenceHash.Length, bytesWritten); + Assert.Equal(referenceHash, incrementalResult); + } + } + + [Theory] + [MemberData(nameof(GetHMACs))] + public static void VerifyTrivialHMAC_Span(HMAC referenceAlgorithm, HashAlgorithmName hashAlgorithm) + { + using (referenceAlgorithm) + using (IncrementalHash incrementalHash = IncrementalHash.CreateHMAC(hashAlgorithm, s_hmacKey)) + { + referenceAlgorithm.Key = s_hmacKey; + + byte[] referenceHash = referenceAlgorithm.ComputeHash(Array.Empty<byte>()); + byte[] incrementalResult = new byte[referenceHash.Length]; + Assert.True(incrementalHash.TryGetHashAndReset(incrementalResult, out int bytesWritten)); + Assert.Equal(referenceHash.Length, bytesWritten); + Assert.Equal(referenceHash, incrementalResult); + } + } + + [Theory] + [MemberData(nameof(GetHashAlgorithms))] + public static void Dispose_HashAlgorithm_ThrowsException(HashAlgorithm referenceAlgorithm, HashAlgorithmName hashAlgorithm) + { + referenceAlgorithm.Dispose(); + var incrementalHash = IncrementalHash.CreateHash(hashAlgorithm); + incrementalHash.Dispose(); + + Assert.Throws<ObjectDisposedException>(() => incrementalHash.AppendData(new byte[1])); + Assert.Throws<ObjectDisposedException>(() => incrementalHash.AppendData(new ReadOnlySpan<byte>(new byte[1]))); + + Assert.Throws<ObjectDisposedException>(() => incrementalHash.GetHashAndReset()); + Assert.Throws<ObjectDisposedException>(() => incrementalHash.TryGetHashAndReset(new byte[1], out int _)); + } + + [Theory] + [MemberData(nameof(GetHMACs))] + public static void Dispose_HMAC_ThrowsException(HMAC referenceAlgorithm, HashAlgorithmName hashAlgorithm) + { + referenceAlgorithm.Dispose(); + var incrementalHash = IncrementalHash.CreateHMAC(hashAlgorithm, s_hmacKey); + incrementalHash.Dispose(); + + Assert.Throws<ObjectDisposedException>(() => incrementalHash.AppendData(new byte[1])); + Assert.Throws<ObjectDisposedException>(() => incrementalHash.AppendData(new ReadOnlySpan<byte>(new byte[1]))); + + Assert.Throws<ObjectDisposedException>(() => incrementalHash.GetHashAndReset()); + Assert.Throws<ObjectDisposedException>(() => incrementalHash.TryGetHashAndReset(new byte[1], out int _)); + } + } +} diff --git a/src/System.Security.Cryptography.Algorithms/tests/System.Security.Cryptography.Algorithms.Tests.csproj b/src/System.Security.Cryptography.Algorithms/tests/System.Security.Cryptography.Algorithms.Tests.csproj index ee62be88a2..8aaea4acbe 100644 --- a/src/System.Security.Cryptography.Algorithms/tests/System.Security.Cryptography.Algorithms.Tests.csproj +++ b/src/System.Security.Cryptography.Algorithms/tests/System.Security.Cryptography.Algorithms.Tests.csproj @@ -149,6 +149,8 @@ <Compile Include="DSACreateTests.cs" /> <Compile Include="DSASignatureFormatterTests.cs" /> <Compile Include="ECDiffieHellmanPublicKeyTests.cs" /> + <Compile Include="HashAlgorithmTest.netcoreapp.cs" /> + <Compile Include="IncrementalHashTests.netcoreapp.cs" /> <Compile Include="PaddingModeTests.cs" /> <Compile Include="PKCS1MaskGenerationMethodTest.cs" /> <Compile Include="RandomNumberGeneratorTests.netcoreapp.cs" /> |