Skip to content

Commit

Permalink
Add support for SHA-3
Browse files Browse the repository at this point in the history
This adds API to interact with the SHA-3 algorithm to the framework.

Like all other cryptographic algorithms in .NET, this only functions on OSes where the underlying system exposes the algorithm.
  • Loading branch information
vcsjones authored Jun 1, 2023
1 parent ac5febd commit 125b216
Show file tree
Hide file tree
Showing 103 changed files with 6,462 additions and 450 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -47,12 +47,33 @@ internal static IntPtr EvpSha512() =>

internal static IntPtr HashAlgorithmToEvp(string hashAlgorithmId) => hashAlgorithmId switch
{
nameof(HashAlgorithmName.SHA1) => EvpSha1(),
nameof(HashAlgorithmName.SHA256) => EvpSha256(),
nameof(HashAlgorithmName.SHA384) => EvpSha384(),
nameof(HashAlgorithmName.SHA512) => EvpSha512(),
nameof(HashAlgorithmName.MD5) => EvpMd5(),
HashAlgorithmNames.SHA1 => EvpSha1(),
HashAlgorithmNames.SHA256 => EvpSha256(),
HashAlgorithmNames.SHA384 => EvpSha384(),
HashAlgorithmNames.SHA512 => EvpSha512(),
HashAlgorithmNames.MD5 => EvpMd5(),
HashAlgorithmNames.SHA3_256 or HashAlgorithmNames.SHA3_384 or HashAlgorithmNames.SHA3_512 =>
throw new PlatformNotSupportedException(),
_ => throw new CryptographicException(SR.Format(SR.Cryptography_UnknownHashAlgorithm, hashAlgorithmId))
};

internal static bool HashAlgorithmSupported(string hashAlgorithmId)
{
switch (hashAlgorithmId)
{
case HashAlgorithmNames.SHA1:
case HashAlgorithmNames.SHA256:
case HashAlgorithmNames.SHA384:
case HashAlgorithmNames.SHA512:
case HashAlgorithmNames.MD5:
return true;
case HashAlgorithmNames.SHA3_256:
case HashAlgorithmNames.SHA3_384:
case HashAlgorithmNames.SHA3_512:
return false;
default:
throw new CryptographicException(SR.Format(SR.Cryptography_UnknownHashAlgorithm, hashAlgorithmId));
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,45 +14,128 @@ internal static partial class Crypto
private static volatile IntPtr s_evpSha256;
private static volatile IntPtr s_evpSha384;
private static volatile IntPtr s_evpSha512;
private static volatile IntPtr s_evpSha3_256;
private static volatile IntPtr s_evpSha3_384;
private static volatile IntPtr s_evpSha3_512;
private static volatile bool s_evpSha3_256Cached;
private static volatile bool s_evpSha3_384Cached;
private static volatile bool s_evpSha3_512Cached;

[LibraryImport(Libraries.CryptoNative)]
private static partial IntPtr CryptoNative_EvpMd5();

internal static IntPtr EvpMd5() =>
private static IntPtr EvpMd5() =>
s_evpMd5 != IntPtr.Zero ? s_evpMd5 : (s_evpMd5 = CryptoNative_EvpMd5());

[LibraryImport(Libraries.CryptoNative)]
internal static partial IntPtr CryptoNative_EvpSha1();
private static partial IntPtr CryptoNative_EvpSha1();

internal static IntPtr EvpSha1() =>
private static IntPtr EvpSha1() =>
s_evpSha1 != IntPtr.Zero ? s_evpSha1 : (s_evpSha1 = CryptoNative_EvpSha1());

[LibraryImport(Libraries.CryptoNative)]
internal static partial IntPtr CryptoNative_EvpSha256();
private static partial IntPtr CryptoNative_EvpSha256();

internal static IntPtr EvpSha256() =>
private static IntPtr EvpSha256() =>
s_evpSha256 != IntPtr.Zero ? s_evpSha256 : (s_evpSha256 = CryptoNative_EvpSha256());

[LibraryImport(Libraries.CryptoNative)]
internal static partial IntPtr CryptoNative_EvpSha384();
private static partial IntPtr CryptoNative_EvpSha384();

internal static IntPtr EvpSha384() =>
private static IntPtr EvpSha384() =>
s_evpSha384 != IntPtr.Zero ? s_evpSha384 : (s_evpSha384 = CryptoNative_EvpSha384());

[LibraryImport(Libraries.CryptoNative)]
internal static partial IntPtr CryptoNative_EvpSha512();
private static partial IntPtr CryptoNative_EvpSha512();

internal static IntPtr EvpSha512() =>
private static IntPtr EvpSha512() =>
s_evpSha512 != IntPtr.Zero ? s_evpSha512 : (s_evpSha512 = CryptoNative_EvpSha512());

internal static IntPtr HashAlgorithmToEvp(string hashAlgorithmId) => hashAlgorithmId switch
[LibraryImport(Libraries.CryptoNative)]
private static partial IntPtr CryptoNative_EvpSha3_256();

private static IntPtr EvpSha3_256()
{
if (!s_evpSha3_256Cached)
{
s_evpSha3_256 = CryptoNative_EvpSha3_256();
s_evpSha3_256Cached = true;
}

return s_evpSha3_256;
}

[LibraryImport(Libraries.CryptoNative)]
private static partial IntPtr CryptoNative_EvpSha3_384();

private static IntPtr EvpSha3_384()
{
if (!s_evpSha3_384Cached)
{
s_evpSha3_384 = CryptoNative_EvpSha3_384();
s_evpSha3_384Cached = true;
}

return s_evpSha3_384;
}

[LibraryImport(Libraries.CryptoNative)]
private static partial IntPtr CryptoNative_EvpSha3_512();

private static IntPtr EvpSha3_512()
{
if (!s_evpSha3_512Cached)
{
s_evpSha3_512 = CryptoNative_EvpSha3_512();
s_evpSha3_512Cached = true;
}

return s_evpSha3_512;
}


internal static IntPtr HashAlgorithmToEvp(string hashAlgorithmId)
{
switch (hashAlgorithmId)
{
case HashAlgorithmNames.SHA1: return EvpSha1();
case HashAlgorithmNames.SHA256: return EvpSha256();
case HashAlgorithmNames.SHA384: return EvpSha384();
case HashAlgorithmNames.SHA512: return EvpSha512();
case HashAlgorithmNames.SHA3_256:
IntPtr sha3_256 = EvpSha3_256();
return sha3_256 != 0 ? sha3_256 : throw new PlatformNotSupportedException();
case HashAlgorithmNames.SHA3_384:
IntPtr sha3_384 = EvpSha3_384();
return sha3_384 != 0 ? sha3_384 : throw new PlatformNotSupportedException();
case HashAlgorithmNames.SHA3_512:
IntPtr sha3_512 = EvpSha3_512();
return sha3_512 != 0 ? sha3_512 : throw new PlatformNotSupportedException();
case nameof(HashAlgorithmName.MD5): return EvpMd5();
default:
throw new CryptographicException(SR.Format(SR.Cryptography_UnknownHashAlgorithm, hashAlgorithmId));
};
}

internal static bool HashAlgorithmSupported(string hashAlgorithmId)
{
nameof(HashAlgorithmName.SHA1) => EvpSha1(),
nameof(HashAlgorithmName.SHA256) => EvpSha256(),
nameof(HashAlgorithmName.SHA384) => EvpSha384(),
nameof(HashAlgorithmName.SHA512) => EvpSha512(),
nameof(HashAlgorithmName.MD5) => EvpMd5(),
_ => throw new CryptographicException(SR.Format(SR.Cryptography_UnknownHashAlgorithm, hashAlgorithmId))
};
switch (hashAlgorithmId)
{
case HashAlgorithmNames.SHA1:
case HashAlgorithmNames.SHA256:
case HashAlgorithmNames.SHA384:
case HashAlgorithmNames.SHA512:
case HashAlgorithmNames.MD5:
return true;
case HashAlgorithmNames.SHA3_256:
return EvpSha3_256() != 0;
case HashAlgorithmNames.SHA3_384:
return EvpSha3_384() != 0;
case HashAlgorithmNames.SHA3_512:
return EvpSha3_512() != 0;
default:
throw new CryptographicException(SR.Format(SR.Cryptography_UnknownHashAlgorithm, hashAlgorithmId));
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ internal static partial class BCrypt
internal static class BCryptAlgorithmCache
{
private static readonly ConcurrentDictionary<(string HashAlgorithmId, BCryptOpenAlgorithmProviderFlags Flags), (SafeBCryptAlgorithmHandle Handle, int HashSizeInBytes)> s_handles = new();
private static readonly ConcurrentDictionary<(string HashAlgorithmId, BCryptOpenAlgorithmProviderFlags Flags), bool> s_supported = new();

/// <summary>
/// Returns a SafeBCryptAlgorithmHandle of the desired algorithm and flags. This is a shared handle so do not dispose it!
Expand Down Expand Up @@ -43,6 +44,42 @@ public static unsafe SafeBCryptAlgorithmHandle GetCachedBCryptAlgorithmHandle(st
}
}
}

public static unsafe bool IsBCryptAlgorithmSupported(string hashAlgorithmId, BCryptOpenAlgorithmProviderFlags flags)
{
var key = (hashAlgorithmId, flags);

if (s_supported.TryGetValue(key, out bool supported))
{
return supported;
}

NTSTATUS status = BCryptOpenAlgorithmProvider(
out SafeBCryptAlgorithmHandle handle,
key.hashAlgorithmId,
null,
key.flags);

bool isSupported = status == NTSTATUS.STATUS_SUCCESS;

if (s_supported.TryAdd(key, isSupported) && isSupported)
{
// It's a valid algorithm. Let's prime the handle cache while we are here. Presumably it's
// going to get used if we're asking if it's supported.
int hashSize = BCryptGetDWordProperty(handle, BCryptPropertyStrings.BCRYPT_HASH_LENGTH);
Debug.Assert(hashSize > 0);

if (s_handles.TryAdd(key, (handle, hashSize)))
{
// If we added the handle to the cache, don't dispose of it and return our answer.
return isSupported;
}
}

// Either the algorithm isn't supported or we don't need it for priming the cache, so Dispose.
handle.Dispose();
return isSupported;
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,12 @@ public enum BCryptAlgPseudoHandle : uint
BCRYPT_HMAC_SHA384_ALG_HANDLE = 0x000000c1,
BCRYPT_HMAC_SHA512_ALG_HANDLE = 0x000000d1,
BCRYPT_PBKDF2_ALG_HANDLE = 0x00000331,
BCRYPT_SHA3_256_ALG_HANDLE = 0x000003B1,
BCRYPT_SHA3_384_ALG_HANDLE = 0x000003C1,
BCRYPT_SHA3_512_ALG_HANDLE = 0x000003D1,
BCRYPT_HMAC_SHA3_256_ALG_HANDLE = 0x000003E1,
BCRYPT_HMAC_SHA3_384_ALG_HANDLE = 0x000003F1,
BCRYPT_HMAC_SHA3_512_ALG_HANDLE = 0x00000401,
}

internal static bool PseudoHandlesSupported { get; } =
Expand Down
Loading

0 comments on commit 125b216

Please sign in to comment.