Skip to content

Commit

Permalink
Reduce allocations and P/Invokes in one-shot hashes
Browse files Browse the repository at this point in the history
  • Loading branch information
vcsjones authored Jul 16, 2020
1 parent 8285abc commit 6ae5b38
Show file tree
Hide file tree
Showing 4 changed files with 119 additions and 8 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.Runtime.InteropServices;

using Microsoft.Win32.SafeHandles;

internal partial class Interop
{
internal partial class BCrypt
{
[DllImport(Libraries.BCrypt, CharSet = CharSet.Unicode)]
internal static unsafe extern NTSTATUS BCryptHash(SafeBCryptAlgorithmHandle hAlgorithm, byte* pbSecret, int cbSecret, byte* pbInput, int cbInput, byte* pbOutput, int cbOutput);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,12 @@ namespace Internal.Cryptography
{
internal static partial class HashProviderDispenser
{
private static volatile IntPtr s_evpMd5;
private static volatile IntPtr s_evpSha1;
private static volatile IntPtr s_evpSha256;
private static volatile IntPtr s_evpSha384;
private static volatile IntPtr s_evpSha512;

public static HashProvider CreateHashProvider(string hashAlgorithmId)
{
IntPtr evpType = HashAlgorithmToEvp(hashAlgorithmId);
Expand All @@ -24,11 +30,11 @@ public static HashProvider CreateMacProvider(string hashAlgorithmId, ReadOnlySpa
}

private static IntPtr HashAlgorithmToEvp(string hashAlgorithmId) => hashAlgorithmId switch {
HashAlgorithmNames.SHA1 => Interop.Crypto.EvpSha1(),
HashAlgorithmNames.SHA256 => Interop.Crypto.EvpSha256(),
HashAlgorithmNames.SHA384 => Interop.Crypto.EvpSha384(),
HashAlgorithmNames.SHA512 => Interop.Crypto.EvpSha512(),
HashAlgorithmNames.MD5 => Interop.Crypto.EvpMd5(),
HashAlgorithmNames.SHA1 => s_evpSha1 == IntPtr.Zero ? (s_evpSha1 = Interop.Crypto.EvpSha1()) : s_evpSha1,
HashAlgorithmNames.SHA256 => s_evpSha256 == IntPtr.Zero ? (s_evpSha256 = Interop.Crypto.EvpSha256()) : s_evpSha256,
HashAlgorithmNames.SHA384 => s_evpSha384 == IntPtr.Zero ? (s_evpSha384 = Interop.Crypto.EvpSha384()) : s_evpSha384,
HashAlgorithmNames.SHA512 => s_evpSha512 == IntPtr.Zero ? (s_evpSha512 = Interop.Crypto.EvpSha512()) : s_evpSha512,
HashAlgorithmNames.MD5 => s_evpMd5 == IntPtr.Zero ? (s_evpMd5 = Interop.Crypto.EvpMd5()) : s_evpMd5,
_ => throw new CryptographicException(SR.Format(SR.Cryptography_UnknownHashAlgorithm, hashAlgorithmId))
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@
using System;
using System.Diagnostics;
using System.Security.Cryptography;
using Microsoft.Win32.SafeHandles;
using NTSTATUS = Interop.BCrypt.NTSTATUS;
using BCryptOpenAlgorithmProviderFlags = Interop.BCrypt.BCryptOpenAlgorithmProviderFlags;
using BCryptCreateHashFlags = Interop.BCrypt.BCryptCreateHashFlags;
using BCryptAlgorithmCache = Interop.BCrypt.BCryptAlgorithmCache;

namespace Internal.Cryptography
{
Expand All @@ -24,12 +29,94 @@ public static HashProvider CreateMacProvider(string hashAlgorithmId, ReadOnlySpa

public static class OneShotHashProvider
{
private static volatile bool s_useCompatOneShot;

public static unsafe int HashData(string hashAlgorithmId, ReadOnlySpan<byte> source, Span<byte> destination)
{
using (HashProviderCng hashProvider = new HashProviderCng(hashAlgorithmId, null))
// Shared handle, no using or dispose.
SafeBCryptAlgorithmHandle cachedAlgorithmHandle = BCryptAlgorithmCache.GetCachedBCryptAlgorithmHandle(
hashAlgorithmId,
BCryptOpenAlgorithmProviderFlags.None);

int hashSize;

NTSTATUS ntStatus = Interop.BCrypt.BCryptGetProperty(
cachedAlgorithmHandle,
Interop.BCrypt.BCryptPropertyStrings.BCRYPT_HASH_LENGTH,
&hashSize,
sizeof(int),
out _,
0);

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

if (destination.Length < hashSize)
{
hashProvider.AppendHashData(source);
return hashProvider.FinalizeHashAndReset(destination);
throw new CryptographicException();
}

if (!s_useCompatOneShot)
{
try
{
fixed (byte* pSource = source)
fixed (byte* pDestination = destination)
{
ntStatus = Interop.BCrypt.BCryptHash(cachedAlgorithmHandle, null, 0, pSource, source.Length, pDestination, hashSize);

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

return hashSize;
}
catch (EntryPointNotFoundException)
{
s_useCompatOneShot = true;
}
}

Debug.Assert(s_useCompatOneShot);
HashUpdateAndFinish(cachedAlgorithmHandle, hashSize, source, destination);

return hashSize;
}

private static void HashUpdateAndFinish(
SafeBCryptAlgorithmHandle algHandle,
int hashSize,
ReadOnlySpan<byte> source,
Span<byte> destination)
{
NTSTATUS ntStatus = Interop.BCrypt.BCryptCreateHash(
algHandle,
out SafeBCryptHashHandle hHash,
IntPtr.Zero,
0,
default,
0,
BCryptCreateHashFlags.None);

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

using (hHash)
{
ntStatus = Interop.BCrypt.BCryptHashData(hHash, source, source.Length, 0);

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

Interop.BCrypt.BCryptFinishHash(hHash, destination, hashSize, 0);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -323,6 +323,8 @@
Link="Common\Interop\Windows\BCrypt\Interop.BCryptDestroyHash.cs" />
<Compile Include="$(CommonPath)Interop\Windows\BCrypt\Interop.BCryptDuplicateHash.cs"
Link="Common\Interop\Windows\BCrypt\Interop.BCryptDuplicateHash.cs" />
<Compile Include="$(CommonPath)Interop\Windows\BCrypt\Interop.BCryptHash.cs"
Link="Common\Interop\Windows\BCrypt\Interop.BCryptHash.cs" />
<Compile Include="$(CommonPath)Interop\Windows\BCrypt\Interop.BCryptHashData.cs"
Link="Common\Interop\Windows\BCrypt\Interop.BCryptHashData.cs" />
<Compile Include="$(CommonPath)Interop\Windows\BCrypt\Interop.BCryptFinishHash.cs"
Expand Down

0 comments on commit 6ae5b38

Please sign in to comment.