Skip to content
This repository has been archived by the owner on Jan 23, 2023. It is now read-only.
/ corefx Public archive

Commit

Permalink
Add Span-based overloads to HashAlgorithm and IncrementalHash
Browse files Browse the repository at this point in the history
- 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
  • Loading branch information
stephentoub committed Aug 9, 2017
1 parent 58024e5 commit 22c6e4e
Show file tree
Hide file tree
Showing 39 changed files with 824 additions and 378 deletions.
12 changes: 5 additions & 7 deletions src/Common/src/Internal/Cryptography/HashProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
// See the LICENSE file in the project root for more information.

using System;
using System.Diagnostics;

namespace Internal.Cryptography
{
Expand All @@ -29,16 +28,16 @@ public void AppendHashData(byte[] data, int offset, int count)
if (data.Length - offset < count)
throw new ArgumentException(SR.Argument_InvalidOffLen);

AppendHashDataCore(data, offset, count);
AppendHashData(new ReadOnlySpan<byte>(data, offset, count));
}

// Adds new data to be hashed. This can be called repeatedly in order to hash data from noncontiguous sources.
// Argument validation is handled by AppendHashData.
public abstract void AppendHashDataCore(byte[] data, int offset, int count);

public abstract void AppendHashData(ReadOnlySpan<byte> data);

// Compute the hash based on the appended data and resets the HashProvider for more hashing.
public abstract byte[] FinalizeHashAndReset();

public abstract bool TryFinalizeHashAndReset(Span<byte> destination, out int bytesWritten);

// Returns the length of the byte array returned by FinalizeHashAndReset.
public abstract int HashSizeInBytes { get; }

Expand All @@ -53,4 +52,3 @@ public void Dispose()
public abstract void Dispose(bool disposing);
}
}

52 changes: 35 additions & 17 deletions src/Common/src/Internal/Cryptography/HashProviderCng.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@
// See the LICENSE file in the project root for more information.

using System;
using System.Diagnostics;
using System.Security.Cryptography;

using Microsoft.Win32.SafeHandles;
using NTSTATUS = Interop.BCrypt.NTSTATUS;
Expand Down Expand Up @@ -68,16 +66,17 @@ public HashProviderCng(string hashAlgId, byte[] key)
return;
}

public sealed override void AppendHashDataCore(byte[] data, int offset, int count)
public sealed override unsafe void AppendHashData(ReadOnlySpan<byte> source)
{
unsafe
NTSTATUS ntStatus;
fixed (byte* pRgb = &source.DangerousGetPinnableReference())
{
fixed (byte* pRgb = data)
{
NTSTATUS ntStatus = Interop.BCrypt.BCryptHashData(_hHash, pRgb + offset, count, 0);
if (ntStatus != NTSTATUS.STATUS_SUCCESS)
throw Interop.BCrypt.CreateCryptographicException(ntStatus);
}
ntStatus = Interop.BCrypt.BCryptHashData(_hHash, pRgb, source.Length, 0);

}
if (ntStatus != NTSTATUS.STATUS_SUCCESS)
{
throw Interop.BCrypt.CreateCryptographicException(ntStatus);
}
}

Expand All @@ -86,12 +85,37 @@ public sealed override byte[] FinalizeHashAndReset()
byte[] hash = new byte[_hashSize];
NTSTATUS ntStatus = Interop.BCrypt.BCryptFinishHash(_hHash, hash, hash.Length, 0);
if (ntStatus != NTSTATUS.STATUS_SUCCESS)
{
throw Interop.BCrypt.CreateCryptographicException(ntStatus);
}

ResetHashObject();
return hash;
}

public override unsafe bool TryFinalizeHashAndReset(Span<byte> destination, out int bytesWritten)
{
if (destination.Length < _hashSize)
{
bytesWritten = 0;
return false;
}

NTSTATUS ntStatus;
fixed (byte* ptr = &destination.DangerousGetPinnableReference())
{
ntStatus = Interop.BCrypt.BCryptFinishHash(_hHash, ptr, _hashSize, 0);
}
if (ntStatus != NTSTATUS.STATUS_SUCCESS)
{
throw Interop.BCrypt.CreateCryptographicException(ntStatus);
}

bytesWritten = _hashSize;
ResetHashObject();
return true;
}

public sealed override void Dispose(bool disposing)
{
if (disposing)
Expand All @@ -106,13 +130,7 @@ public sealed override void Dispose(bool disposing)
}
}

public sealed override int HashSizeInBytes
{
get
{
return _hashSize;
}
}
public sealed override int HashSizeInBytes => _hashSize;

private void ResetHashObject()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,23 @@
// 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.Runtime.InteropServices;

using Microsoft.Win32.SafeHandles;

internal partial class Interop
{
internal partial class BCrypt
{
internal static unsafe NTSTATUS BCryptFinishHash(SafeBCryptHashHandle hHash, byte[] pbOutput, int cbOutput, int dwFlags)
{
fixed (byte* ptr = pbOutput)
{
return BCryptFinishHash(hHash, ptr, cbOutput, dwFlags);
}
}

[DllImport(Libraries.BCrypt, CharSet = CharSet.Unicode)]
internal static extern NTSTATUS BCryptFinishHash(SafeBCryptHashHandle hHash, [Out] byte[] pbOutput, int cbOutput, int dwFlags);
internal static unsafe extern NTSTATUS BCryptFinishHash(SafeBCryptHashHandle hHash, byte* pbOutput, int cbOutput, int dwFlags);
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -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) { }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,6 @@

using System;
using System.Diagnostics;
using System.Security.Cryptography;

using Internal.Cryptography;

namespace Internal.Cryptography
{
Expand All @@ -30,13 +27,7 @@ public HMACCommon(string hashAlgorithmId, byte[] key, int blockSize)
ChangeKey(key);
}

public int HashSizeInBits
{
get
{
return _hMacProvider.HashSizeInBytes * 8;
}
}
public int HashSizeInBits => _hMacProvider.HashSizeInBytes * 8;

public void ChangeKey(byte[] key)
{
Expand All @@ -46,15 +37,16 @@ public void ChangeKey(byte[] key)
{
// 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;
Expand All @@ -65,31 +57,31 @@ public void ChangeKey(byte[] key)
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;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -86,25 +86,18 @@ internal AppleHmacProvider(Interop.AppleCrypto.PAL_HashAlgorithm algorithm, byte
HashSizeInBytes = hashSizeInBytes;
}

public override unsafe void AppendHashDataCore(byte[] data, int offset, int count)
public override unsafe 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)
fixed (byte* pData = &data.DangerousGetPinnableReference())
{
byte* pbData = pData + offset;
ret = Interop.AppleCrypto.HmacUpdate(_ctx, pbData, count);
ret = Interop.AppleCrypto.HmacUpdate(_ctx, pData, data.Length);
}

if (ret != 1)
Expand Down Expand Up @@ -154,6 +147,34 @@ public override unsafe byte[] FinalizeHashAndReset()
return output;
}

public override unsafe bool TryFinalizeHashAndReset(Span<byte> destination, out int bytesWritten)
{
if (!_running)
{
SetKey();
}

if (destination.Length < HashSizeInBytes)
{
bytesWritten = 0;
return false;
}

int ret;
fixed (byte* pbOutput = &destination.DangerousGetPinnableReference())
{
ret = Interop.AppleCrypto.HmacFinal(_ctx, pbOutput, destination.Length);
}
if (ret != 1)
{
throw new CryptographicException();
}

bytesWritten = HashSizeInBytes;
_running = false;
return true;
}

public override void Dispose(bool disposing)
{
if (disposing)
Expand Down Expand Up @@ -193,20 +214,13 @@ internal AppleDigestProvider(Interop.AppleCrypto.PAL_HashAlgorithm algorithm)
HashSizeInBytes = hashSizeInBytes;
}

public override unsafe void AppendHashDataCore(byte[] data, int offset, int count)
public override unsafe 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)
fixed (byte* pData = &data.DangerousGetPinnableReference())
{
byte* pbData = pData + offset;
ret = Interop.AppleCrypto.DigestUpdate(_ctx, pbData, count);
ret = Interop.AppleCrypto.DigestUpdate(_ctx, pData, data.Length);
}

if (ret != 1)
Expand Down Expand Up @@ -235,6 +249,29 @@ public override unsafe byte[] FinalizeHashAndReset()
return hash;
}

public override unsafe bool TryFinalizeHashAndReset(Span<byte> destination, out int bytesWritten)
{
if (destination.Length < HashSizeInBytes)
{
bytesWritten = 0;
return false;
}

int ret;
fixed (byte* pHash = &destination.DangerousGetPinnableReference())
{
ret = Interop.AppleCrypto.DigestFinal(_ctx, pHash, destination.Length);
}
if (ret != 1)
{
Debug.Assert(ret == 0, $"DigestFinal return value {ret} was not 0 or 1");
throw new CryptographicException();
}

bytesWritten = HashSizeInBytes;
return true;
}

public override void Dispose(bool disposing)
{
if (disposing)
Expand Down
Loading

0 comments on commit 22c6e4e

Please sign in to comment.