From 6306469c568536a5599ff66ac63181940cbe0c07 Mon Sep 17 00:00:00 2001 From: dotnet-bot Date: Wed, 13 May 2015 13:16:47 -0700 Subject: [PATCH] Add System.Security.Cryptography.Hashing.Algorithms Source and Test --- .../Interop/Linux/libcrypto/Interop.HMAC.cs | 26 ++ .../src/Interop/OSX/libcrypto/Interop.HMAC.cs | 50 +++ .../Interop.EnsureOpenSslInitialized.cs | 13 + .../src/Interop/Unix/libcrypto/Interop.ERR.cs | 29 ++ .../src/Interop/Unix/libcrypto/Interop.EVP.cs | 51 +++ .../Unix/libcrypto/Interop.Initialization.cs | 28 ++ src/Common/src/Interop/Windows/BCrypt/Cng.cs | 394 ++++++++++++++++++ .../SafeHandles/SafeEvpMdCtxHandle.Unix.cs | 31 ++ ...curity.Cryptography.Hashing.Algorithms.sln | 58 +++ .../src/Internal/Cryptography/HMACCommon.cs | 70 ++++ .../Cryptography/HashAlgorithmNames.cs | 21 + .../src/Internal/Cryptography/HashProvider.cs | 29 ++ .../HashProviderDispenser.Unix.cs | 183 ++++++++ .../HashProviderDispenser.Windows.cs | 139 ++++++ .../src/Internal/Cryptography/Helpers.cs | 28 ++ .../src/Resources/Strings.resx | 120 ++++++ ...ity.Cryptography.Hashing.Algorithms.csproj | 76 ++++ .../System/Security/Cryptography/HMACSHA1.cs | 83 ++++ .../Security/Cryptography/HMACSHA256.cs | 83 ++++ .../Security/Cryptography/HMACSHA384.cs | 83 ++++ .../Security/Cryptography/HMACSHA512.cs | 83 ++++ .../src/System/Security/Cryptography/MD5.cs | 69 +++ .../src/System/Security/Cryptography/SHA1.cs | 69 +++ .../System/Security/Cryptography/SHA256.cs | 69 +++ .../System/Security/Cryptography/SHA384.cs | 69 +++ .../System/Security/Cryptography/SHA512.cs | 69 +++ .../src/project.json | 16 + .../tests/ByteUtils.cs | 48 +++ .../tests/HashAlgorithmTest.cs | 138 ++++++ .../tests/HmacSha1Tests.cs | 57 +++ .../tests/HmacSha256Tests.cs | 58 +++ .../tests/HmacSha384Tests.cs | 58 +++ .../tests/HmacSha512Tests.cs | 58 +++ .../tests/HmacTests.cs | 48 +++ .../tests/MD5Tests.cs | 65 +++ .../tests/Rfc2202HmacTests.cs | 37 ++ .../tests/Rfc4231HmacTests.cs | 37 ++ .../tests/Sha1Tests.cs | 41 ++ .../tests/Sha256Tests.cs | 41 ++ .../tests/Sha384Tests.cs | 32 ++ .../tests/Sha512Tests.cs | 41 ++ ...yptography.Hashing.Algorithms.Tests.csproj | 42 ++ .../tests/project.json | 18 + 43 files changed, 2858 insertions(+) create mode 100644 src/Common/src/Interop/Linux/libcrypto/Interop.HMAC.cs create mode 100644 src/Common/src/Interop/OSX/libcrypto/Interop.HMAC.cs create mode 100644 src/Common/src/Interop/Unix/libcoreclr/Interop.EnsureOpenSslInitialized.cs create mode 100644 src/Common/src/Interop/Unix/libcrypto/Interop.ERR.cs create mode 100644 src/Common/src/Interop/Unix/libcrypto/Interop.EVP.cs create mode 100644 src/Common/src/Interop/Unix/libcrypto/Interop.Initialization.cs create mode 100644 src/Common/src/Interop/Windows/BCrypt/Cng.cs create mode 100644 src/Common/src/Microsoft/Win32/SafeHandles/SafeEvpMdCtxHandle.Unix.cs create mode 100644 src/System.Security.Cryptography.Hashing.Algorithms/System.Security.Cryptography.Hashing.Algorithms.sln create mode 100644 src/System.Security.Cryptography.Hashing.Algorithms/src/Internal/Cryptography/HMACCommon.cs create mode 100644 src/System.Security.Cryptography.Hashing.Algorithms/src/Internal/Cryptography/HashAlgorithmNames.cs create mode 100644 src/System.Security.Cryptography.Hashing.Algorithms/src/Internal/Cryptography/HashProvider.cs create mode 100644 src/System.Security.Cryptography.Hashing.Algorithms/src/Internal/Cryptography/HashProviderDispenser.Unix.cs create mode 100644 src/System.Security.Cryptography.Hashing.Algorithms/src/Internal/Cryptography/HashProviderDispenser.Windows.cs create mode 100644 src/System.Security.Cryptography.Hashing.Algorithms/src/Internal/Cryptography/Helpers.cs create mode 100644 src/System.Security.Cryptography.Hashing.Algorithms/src/Resources/Strings.resx create mode 100644 src/System.Security.Cryptography.Hashing.Algorithms/src/System.Security.Cryptography.Hashing.Algorithms.csproj create mode 100644 src/System.Security.Cryptography.Hashing.Algorithms/src/System/Security/Cryptography/HMACSHA1.cs create mode 100644 src/System.Security.Cryptography.Hashing.Algorithms/src/System/Security/Cryptography/HMACSHA256.cs create mode 100644 src/System.Security.Cryptography.Hashing.Algorithms/src/System/Security/Cryptography/HMACSHA384.cs create mode 100644 src/System.Security.Cryptography.Hashing.Algorithms/src/System/Security/Cryptography/HMACSHA512.cs create mode 100644 src/System.Security.Cryptography.Hashing.Algorithms/src/System/Security/Cryptography/MD5.cs create mode 100644 src/System.Security.Cryptography.Hashing.Algorithms/src/System/Security/Cryptography/SHA1.cs create mode 100644 src/System.Security.Cryptography.Hashing.Algorithms/src/System/Security/Cryptography/SHA256.cs create mode 100644 src/System.Security.Cryptography.Hashing.Algorithms/src/System/Security/Cryptography/SHA384.cs create mode 100644 src/System.Security.Cryptography.Hashing.Algorithms/src/System/Security/Cryptography/SHA512.cs create mode 100644 src/System.Security.Cryptography.Hashing.Algorithms/src/project.json create mode 100644 src/System.Security.Cryptography.Hashing.Algorithms/tests/ByteUtils.cs create mode 100644 src/System.Security.Cryptography.Hashing.Algorithms/tests/HashAlgorithmTest.cs create mode 100644 src/System.Security.Cryptography.Hashing.Algorithms/tests/HmacSha1Tests.cs create mode 100644 src/System.Security.Cryptography.Hashing.Algorithms/tests/HmacSha256Tests.cs create mode 100644 src/System.Security.Cryptography.Hashing.Algorithms/tests/HmacSha384Tests.cs create mode 100644 src/System.Security.Cryptography.Hashing.Algorithms/tests/HmacSha512Tests.cs create mode 100644 src/System.Security.Cryptography.Hashing.Algorithms/tests/HmacTests.cs create mode 100644 src/System.Security.Cryptography.Hashing.Algorithms/tests/MD5Tests.cs create mode 100644 src/System.Security.Cryptography.Hashing.Algorithms/tests/Rfc2202HmacTests.cs create mode 100644 src/System.Security.Cryptography.Hashing.Algorithms/tests/Rfc4231HmacTests.cs create mode 100644 src/System.Security.Cryptography.Hashing.Algorithms/tests/Sha1Tests.cs create mode 100644 src/System.Security.Cryptography.Hashing.Algorithms/tests/Sha256Tests.cs create mode 100644 src/System.Security.Cryptography.Hashing.Algorithms/tests/Sha384Tests.cs create mode 100644 src/System.Security.Cryptography.Hashing.Algorithms/tests/Sha512Tests.cs create mode 100644 src/System.Security.Cryptography.Hashing.Algorithms/tests/System.Security.Cryptography.Hashing.Algorithms.Tests.csproj create mode 100644 src/System.Security.Cryptography.Hashing.Algorithms/tests/project.json diff --git a/src/Common/src/Interop/Linux/libcrypto/Interop.HMAC.cs b/src/Common/src/Interop/Linux/libcrypto/Interop.HMAC.cs new file mode 100644 index 000000000000..e5c53e58ea5d --- /dev/null +++ b/src/Common/src/Interop/Linux/libcrypto/Interop.HMAC.cs @@ -0,0 +1,26 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; +using System.Runtime.InteropServices; + +internal static partial class Interop +{ + internal static partial class libcrypto + { + [DllImport(Libraries.LibCrypto)] + internal extern static unsafe int HMAC_Init(out HMAC_CTX ctx, byte* key, int key_len, IntPtr md); + + [DllImport(Libraries.LibCrypto)] + internal extern static unsafe int HMAC_Update(ref HMAC_CTX ctx, byte* data, int len); + + [DllImport(Libraries.LibCrypto)] + internal extern static unsafe int HMAC_Final(ref HMAC_CTX ctx, byte* md, ref uint len); + + [DllImport(Libraries.LibCrypto)] + internal extern static unsafe void HMAC_CTX_cleanup(ref HMAC_CTX ctx); + + [StructLayout(LayoutKind.Explicit, Size = 512)] + internal struct HMAC_CTX { } + } +} diff --git a/src/Common/src/Interop/OSX/libcrypto/Interop.HMAC.cs b/src/Common/src/Interop/OSX/libcrypto/Interop.HMAC.cs new file mode 100644 index 000000000000..4aab86aa062e --- /dev/null +++ b/src/Common/src/Interop/OSX/libcrypto/Interop.HMAC.cs @@ -0,0 +1,50 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; +using System.Runtime.InteropServices; + +internal static partial class Interop +{ + internal static partial class libcrypto + { + private const int Success = 1; + + // On OSX, these functions are void returning (and the man pages give no indication of what would happen if they + // fail), but upstack code is written against surface area where they return 1 on success and 0 on error. + // These routines call the underlying APIs and then just return success + + internal static unsafe int HMAC_Init(out HMAC_CTX ctx, byte* key, int key_len, IntPtr md) + { + HMAC_InitNative(out ctx, key, key_len, md); + return Success; + } + + internal static unsafe int HMAC_Update(ref HMAC_CTX ctx, byte* data, int len) + { + HMAC_UpdateNative(ref ctx, data, len); + return Success; + } + + internal static unsafe int HMAC_Final(ref HMAC_CTX ctx, byte* md, ref uint len) + { + HMAC_FinalNative(ref ctx, md, ref len); + return Success; + } + + [DllImport(Libraries.LibCrypto, EntryPoint = "HMAC_Init", ExactSpelling = true)] + private extern static unsafe int HMAC_InitNative(out HMAC_CTX ctx, byte* key, int key_len, IntPtr md); + + [DllImport(Libraries.LibCrypto, EntryPoint = "HMAC_Update", ExactSpelling = true)] + private extern static unsafe int HMAC_UpdateNative(ref HMAC_CTX ctx, byte* data, int len); + + [DllImport(Libraries.LibCrypto, EntryPoint = "HMAC_Final", ExactSpelling = true)] + private extern static unsafe int HMAC_FinalNative(ref HMAC_CTX ctx, byte* md, ref uint len); + + [DllImport(Libraries.LibCrypto)] + internal extern static unsafe void HMAC_CTX_cleanup(ref HMAC_CTX ctx); + + [StructLayout(LayoutKind.Explicit, Size = 512)] + internal struct HMAC_CTX { } + } +} diff --git a/src/Common/src/Interop/Unix/libcoreclr/Interop.EnsureOpenSslInitialized.cs b/src/Common/src/Interop/Unix/libcoreclr/Interop.EnsureOpenSslInitialized.cs new file mode 100644 index 000000000000..df97a0efe51f --- /dev/null +++ b/src/Common/src/Interop/Unix/libcoreclr/Interop.EnsureOpenSslInitialized.cs @@ -0,0 +1,13 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System.Runtime.InteropServices; + +internal static partial class Interop +{ + internal static partial class libcoreclr + { + [DllImport(Libraries.LibCoreClr)] + internal static extern int EnsureOpenSslInitialized(); + } +} diff --git a/src/Common/src/Interop/Unix/libcrypto/Interop.ERR.cs b/src/Common/src/Interop/Unix/libcrypto/Interop.ERR.cs new file mode 100644 index 000000000000..c669d509bd76 --- /dev/null +++ b/src/Common/src/Interop/Unix/libcrypto/Interop.ERR.cs @@ -0,0 +1,29 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System.Runtime.InteropServices; +using System.Text; + +internal static partial class Interop +{ + internal static partial class libcrypto + { + [DllImport(Libraries.LibCrypto)] + private static extern void ERR_load_crypto_strings(); + + [DllImport(Libraries.LibCrypto)] + private static extern uint ERR_get_error(); + + [DllImport(Libraries.LibCrypto, CharSet = CharSet.Ansi)] + private static extern void ERR_error_string_n(uint e, StringBuilder buf, int len); + + internal static string GetOpenSslErrorString() + { + uint error = ERR_get_error(); + StringBuilder buf = new StringBuilder(1024); + + ERR_error_string_n(error, buf, buf.Capacity); + return buf.ToString(); + } + } +} diff --git a/src/Common/src/Interop/Unix/libcrypto/Interop.EVP.cs b/src/Common/src/Interop/Unix/libcrypto/Interop.EVP.cs new file mode 100644 index 000000000000..74e565ef59ac --- /dev/null +++ b/src/Common/src/Interop/Unix/libcrypto/Interop.EVP.cs @@ -0,0 +1,51 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; +using System.Runtime.InteropServices; +using Microsoft.Win32.SafeHandles; + +using size_t = System.IntPtr; + +internal static partial class Interop +{ + internal static partial class libcrypto + { + [DllImport(Libraries.LibCrypto)] + internal extern static SafeEvpMdCtxHandle EVP_MD_CTX_create(); + + [DllImport(Libraries.LibCrypto)] + internal extern static int EVP_DigestInit_ex(SafeEvpMdCtxHandle ctx, IntPtr type, IntPtr impl); + + [DllImport(Libraries.LibCrypto)] + internal extern static unsafe int EVP_DigestUpdate(SafeEvpMdCtxHandle ctx, byte* d, size_t cnt); + + [DllImport(Libraries.LibCrypto)] + internal extern static unsafe int EVP_DigestFinal_ex(SafeEvpMdCtxHandle ctx, byte* md, ref uint s); + + [DllImport(Libraries.LibCrypto)] + internal extern static unsafe void EVP_MD_CTX_destroy(IntPtr ctx); + + [DllImport(Libraries.LibCrypto)] + internal extern static unsafe int EVP_MD_size(IntPtr md); + + + [DllImport(Libraries.LibCrypto)] + internal extern static IntPtr EVP_md5(); + + [DllImport(Libraries.LibCrypto)] + internal extern static IntPtr EVP_sha1(); + + [DllImport(Libraries.LibCrypto)] + internal extern static IntPtr EVP_sha256(); + + [DllImport(Libraries.LibCrypto)] + internal extern static IntPtr EVP_sha384(); + + [DllImport(Libraries.LibCrypto)] + internal extern static IntPtr EVP_sha512(); + + + internal const int EVP_MAX_MD_SIZE = 64; + } +} diff --git a/src/Common/src/Interop/Unix/libcrypto/Interop.Initialization.cs b/src/Common/src/Interop/Unix/libcrypto/Interop.Initialization.cs new file mode 100644 index 000000000000..a4945b2dad9d --- /dev/null +++ b/src/Common/src/Interop/Unix/libcrypto/Interop.Initialization.cs @@ -0,0 +1,28 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; +using System.Diagnostics; +using System.Runtime.InteropServices; +using System.Security.Cryptography; +using System.Threading; + +internal static partial class Interop +{ + internal static partial class libcrypto + { + // Initialization of libcrypto threading support is done in a static constructor. + // This enables a project simply to include this file, and any usage of any of + // the libcrypto functions will trigger initialization of the threading support. + static libcrypto() + { + if (Interop.libcoreclr.EnsureOpenSslInitialized() != 0) + { + throw new CryptographicException(); + } + + // Ensure that the error message table is loaded. + Interop.libcrypto.ERR_load_crypto_strings(); + } + } +} diff --git a/src/Common/src/Interop/Windows/BCrypt/Cng.cs b/src/Common/src/Interop/Windows/BCrypt/Cng.cs new file mode 100644 index 000000000000..13c8225cb872 --- /dev/null +++ b/src/Common/src/Interop/Windows/BCrypt/Cng.cs @@ -0,0 +1,394 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; +using System.Text; +using System.Diagnostics; +using System.Diagnostics.Contracts; +using System.Collections.Generic; +using System.Security.Cryptography; +using System.Runtime.InteropServices; + +namespace Internal.NativeCrypto +{ + internal static partial class BCryptNative + { + /// + /// Well known algorithm names + /// + internal static class AlgorithmName + { + public const string ECDHP256 = "ECDH_P256"; // BCRYPT_ECDH_P256_ALGORITHM + public const string ECDHP384 = "ECDH_P384"; // BCRYPT_ECDH_P384_ALGORITHM + public const string ECDHP521 = "ECDH_P521"; // BCRYPT_ECDH_P521_ALGORITHM + public const string ECDsaP256 = "ECDSA_P256"; // BCRYPT_ECDSA_P256_ALGORITHM + public const string ECDsaP384 = "ECDSA_P384"; // BCRYPT_ECDSA_P384_ALGORITHM + public const string ECDsaP521 = "ECDSA_P521"; // BCRYPT_ECDSA_P521_ALGORITHM + public const string MD5 = "MD5"; // BCRYPT_MD5_ALGORITHM + public const string Sha1 = "SHA1"; // BCRYPT_SHA1_ALGORITHM + public const string Sha256 = "SHA256"; // BCRYPT_SHA256_ALGORITHM + public const string Sha384 = "SHA384"; // BCRYPT_SHA384_ALGORITHM + public const string Sha512 = "SHA512"; // BCRYPT_SHA512_ALGORITHM + } + + /// + /// Magic numbers identifying blob types + /// + internal enum KeyBlobMagicNumber { + ECDHPublicP256 = 0x314B4345, // BCRYPT_ECDH_PUBLIC_P256_MAGIC + ECDHPublicP384 = 0x334B4345, // BCRYPT_ECDH_PUBLIC_P384_MAGIC + ECDHPublicP521 = 0x354B4345, // BCRYPT_ECDH_PUBLIC_P521_MAGIC + ECDsaPublicP256 = 0x31534345, // BCRYPT_ECDSA_PUBLIC_P256_MAGIC + ECDsaPublicP384 = 0x33534345, // BCRYPT_ECDSA_PUBLIC_P384_MAGIC + ECDsaPublicP521 = 0x35534345 // BCRYPT_ECDSA_PUBLIC_P521_MAGIC + } + + internal static class KeyDerivationFunction + { + public const string Hash = "HASH"; // BCRYPT_KDF_HASH + public const string Hmac = "HMAC"; // BCRYPT_KDF_HMAC + public const string Tls = "TLS_PRF"; // BCRYPT_KDF_TLS_PRF + } + } + + // + // Interop layer around Windows CNG api. + // + internal static partial class Cng + { + public const String CngDll = "BCrypt.dll"; + public const String Capi2Dll = "Crypt32.dll"; + + [Flags] + public enum OpenAlgorithmProviderFlags : int + { + NONE = 0x00000000, + BCRYPT_ALG_HANDLE_HMAC_FLAG = 0x00000008, + } + + public const String BCRYPT_AES_ALGORITHM = "AES"; + + public static SafeAlgorithmHandle BCryptOpenAlgorithmProvider(String pszAlgId, String pszImplementation, OpenAlgorithmProviderFlags dwFlags) + { + SafeAlgorithmHandle hAlgorithm = null; + NTSTATUS ntStatus = Interop.BCryptOpenAlgorithmProvider(out hAlgorithm, pszAlgId, pszImplementation, (int)dwFlags); + if (ntStatus != NTSTATUS.STATUS_SUCCESS) + throw CreateCryptographicException(ntStatus); + return hAlgorithm; + } + + public static SafeHashHandle BCryptCreateHash(this SafeAlgorithmHandle hAlgorithm, byte[] pbSecret, int dwFlags) + { + SafeHashHandle hHash = null; + NTSTATUS ntStatus = Interop.BCryptCreateHash(hAlgorithm, out hHash, IntPtr.Zero, 0, pbSecret, pbSecret == null ? 0 : pbSecret.Length, dwFlags); + if (ntStatus != NTSTATUS.STATUS_SUCCESS) + throw CreateCryptographicException(ntStatus); + return hHash; + } + + public static SafeHashHandle BCryptTryCreateReusableHash(this SafeAlgorithmHandle hAlgorithm, byte[] pbSecret) + { + const int BCRYPT_HASH_REUSABLE_FLAG = 0x00000020; + + SafeHashHandle hHash = null; + NTSTATUS ntStatus = Interop.BCryptCreateHash(hAlgorithm, out hHash, IntPtr.Zero, 0, pbSecret, pbSecret == null ? 0 : pbSecret.Length, BCRYPT_HASH_REUSABLE_FLAG); + if (ntStatus == NTSTATUS.STATUS_INVALID_PARAMETER) + return null; // Pre-Win8 OS's do not support BCRYPT_HASH_REUSABLE_FLAG. + if (ntStatus != NTSTATUS.STATUS_SUCCESS) + throw CreateCryptographicException(ntStatus); + return hHash; + } + + public static unsafe void BCryptHashData(this SafeHashHandle hHash, byte* pbInput, int cbInput) + { + NTSTATUS ntStatus = Interop.BCryptHashData(hHash, pbInput, cbInput, 0); + if (ntStatus != NTSTATUS.STATUS_SUCCESS) + throw CreateCryptographicException(ntStatus); + return; + } + + public static byte[] BCryptFinishHash(this SafeHashHandle hHash, int cbHashSize) + { + byte[] hash = new byte[cbHashSize]; + NTSTATUS ntStatus = Interop.BCryptFinishHash(hHash, hash, cbHashSize, 0); + if (ntStatus != NTSTATUS.STATUS_SUCCESS) + throw CreateCryptographicException(ntStatus); + return hash; + } + + public static int GetHashSizeInBytes(this SafeHashHandle hHash) + { + unsafe + { + int cbSizeOfHashSize; + int hashSize; + NTSTATUS ntStatus = Interop.BCryptGetProperty(hHash, BCryptGetPropertyStrings.BCRYPT_HASH_LENGTH, (byte*)&hashSize, 4, out cbSizeOfHashSize, 0); + if (ntStatus != NTSTATUS.STATUS_SUCCESS) + throw CreateCryptographicException(ntStatus); + return hashSize; + } + } + + public static void BCryptGenRandom(byte[] buffer) + { + const int BCRYPT_USE_SYSTEM_PREFERRED_RNG = 0x00000002; + NTSTATUS ntStatus = Interop.BCryptGenRandom(IntPtr.Zero, buffer, buffer.Length, BCRYPT_USE_SYSTEM_PREFERRED_RNG); + if (ntStatus != NTSTATUS.STATUS_SUCCESS) + throw CreateCryptographicException(ntStatus); + } + + public static SafeKeyHandle BCryptImportKey(this SafeAlgorithmHandle hAlg, byte[] key) + { + unsafe + { + const String BCRYPT_KEY_DATA_BLOB = "KeyDataBlob"; + int keySize = key.Length; + int blobSize = sizeof(BCRYPT_KEY_DATA_BLOB_HEADER) + keySize; + byte[] blob = new byte[blobSize]; + fixed (byte* pbBlob = blob) + { + BCRYPT_KEY_DATA_BLOB_HEADER* pBlob = (BCRYPT_KEY_DATA_BLOB_HEADER*)pbBlob; + pBlob->dwMagic = BCRYPT_KEY_DATA_BLOB_HEADER.BCRYPT_KEY_DATA_BLOB_MAGIC; + pBlob->dwVersion = BCRYPT_KEY_DATA_BLOB_HEADER.BCRYPT_KEY_DATA_BLOB_VERSION1; + pBlob->cbKeyData = (uint)keySize; + } + Buffer.BlockCopy(key, 0, blob, sizeof(BCRYPT_KEY_DATA_BLOB_HEADER), keySize); + SafeKeyHandle hKey; + NTSTATUS ntStatus = Interop.BCryptImportKey(hAlg, IntPtr.Zero, BCRYPT_KEY_DATA_BLOB, out hKey, IntPtr.Zero, 0, blob, blobSize, 0); + if (ntStatus != NTSTATUS.STATUS_SUCCESS) + throw CreateCryptographicException(ntStatus); + return hKey; + } + } + + [StructLayout(LayoutKind.Sequential)] + private struct BCRYPT_KEY_DATA_BLOB_HEADER + { + public UInt32 dwMagic; + public UInt32 dwVersion; + public UInt32 cbKeyData; + + public const UInt32 BCRYPT_KEY_DATA_BLOB_MAGIC = 0x4d42444b; + public const UInt32 BCRYPT_KEY_DATA_BLOB_VERSION1 = 0x1; + } + + public static void SetCipherMode(this SafeKeyHandle hKey, CipherMode cipherMode) + { + String cipherModePropertyName; + switch (cipherMode) + { + case CipherMode.CBC: + cipherModePropertyName = "ChainingModeCBC"; + break; + case CipherMode.CTS: + throw new NotSupportedException(); + case CipherMode.ECB: + cipherModePropertyName = "ChainingModeECB"; + break; + + default: + throw new NotSupportedException(); + } + NTSTATUS ntStatus = Interop.BCryptSetProperty(hKey, "ChainingMode", cipherModePropertyName, (cipherModePropertyName.Length + 1) * 2, 0); + if (ntStatus != NTSTATUS.STATUS_SUCCESS) + throw CreateCryptographicException(ntStatus); + return; + } + + // Note: input and output are allowed to be the same buffer. BCryptEncrypt will correctly do the encryption in place according to CNG documentation. + public static int BCryptEncrypt(this SafeKeyHandle hKey, byte[] input, int inputOffset, int inputCount, byte[] iv, byte[] output, int outputOffset, int outputCount) + { + Debug.Assert(input != null); + Debug.Assert(inputOffset >= 0); + Debug.Assert(inputCount >= 0); + Debug.Assert(inputCount <= input.Length - inputOffset); + Debug.Assert(output != null); + Debug.Assert(outputOffset >= 0); + Debug.Assert(outputCount >= 0); + Debug.Assert(outputCount <= output.Length - outputOffset); + + unsafe + { + fixed (byte* pbInput = input) + { + fixed (byte* pbOutput = output) + { + int cbResult; + NTSTATUS ntStatus = Interop.BCryptEncrypt(hKey, pbInput + inputOffset, inputCount, IntPtr.Zero, iv, iv == null ? 0 : iv.Length, pbOutput + outputOffset, outputCount, out cbResult, 0); + if (ntStatus != NTSTATUS.STATUS_SUCCESS) + throw CreateCryptographicException(ntStatus); + return cbResult; + } + } + } + } + + // Note: input and output are allowed to be the same buffer. BCryptDecrypt will correctly do the decryption in place according to CNG documentation. + public static int BCryptDecrypt(this SafeKeyHandle hKey, byte[] input, int inputOffset, int inputCount, byte[] iv, byte[] output, int outputOffset, int outputCount) + { + Debug.Assert(input != null); + Debug.Assert(inputOffset >= 0); + Debug.Assert(inputCount >= 0); + Debug.Assert(inputCount <= input.Length - inputOffset); + Debug.Assert(output != null); + Debug.Assert(outputOffset >= 0); + Debug.Assert(outputCount >= 0); + Debug.Assert(outputCount <= output.Length - outputOffset); + + unsafe + { + fixed (byte* pbInput = input) + { + fixed (byte* pbOutput = output) + { + int cbResult; + NTSTATUS ntStatus = Interop.BCryptDecrypt(hKey, pbInput + inputOffset, inputCount, IntPtr.Zero, iv, iv == null ? 0 : iv.Length, pbOutput + outputOffset, outputCount, out cbResult, 0); + if (ntStatus != NTSTATUS.STATUS_SUCCESS) + throw CreateCryptographicException(ntStatus); + return cbResult; + } + } + } + } + + private static class BCryptGetPropertyStrings + { + public const String BCRYPT_HASH_LENGTH = "HashDigestLength"; + } + + public static String CryptFormatObject(String oidValue, byte[] rawData, bool multiLine) + { + const int X509_ASN_ENCODING = 0x00000001; + const int CRYPT_FORMAT_STR_MULTI_LINE = 0x00000001; + + int dwFormatStrType = multiLine ? CRYPT_FORMAT_STR_MULTI_LINE : 0; + + int cbFormat = 0; + if (!Interop.CryptFormatObject(X509_ASN_ENCODING, 0, dwFormatStrType, IntPtr.Zero, oidValue, rawData, rawData.Length, null, ref cbFormat)) + return null; + StringBuilder sb = new StringBuilder((cbFormat + 1) / 2); + if (!Interop.CryptFormatObject(X509_ASN_ENCODING, 0, dwFormatStrType, IntPtr.Zero, oidValue, rawData, rawData.Length, sb, ref cbFormat)) + return null; + return sb.ToString(); + } + + private enum NTSTATUS : uint + { + STATUS_SUCCESS = 0x0, + STATUS_NOT_FOUND = 0xc0000225, + STATUS_INVALID_PARAMETER = 0xc000000d, + STATUS_NO_MEMORY = 0xc0000017, + } + + private static Exception CreateCryptographicException(NTSTATUS ntStatus) + { + int hr = ((int)ntStatus) | 0x01000000; + return new CryptographicException(hr); + } + } + + + internal static partial class Cng + { + private static class Interop + { + [DllImport(CngDll, CharSet = CharSet.Unicode)] + public static extern NTSTATUS BCryptOpenAlgorithmProvider(out SafeAlgorithmHandle phAlgorithm, String pszAlgId, String pszImplementation, int dwFlags); + + [DllImport(CngDll, CharSet = CharSet.Unicode)] + public static extern NTSTATUS BCryptCreateHash(SafeAlgorithmHandle hAlgorithm, out SafeHashHandle phHash, IntPtr pbHashObject, int cbHashObject, [In, Out] byte[] pbSecret, int cbSecret, int dwFlags); + + [DllImport(CngDll, CharSet = CharSet.Unicode)] + public static extern unsafe NTSTATUS BCryptHashData(SafeHashHandle hHash, byte* pbInput, int cbInput, int dwFlags); + + [DllImport(CngDll, CharSet = CharSet.Unicode)] + public static extern NTSTATUS BCryptFinishHash(SafeHashHandle hHash, [Out] byte[] pbOutput, int cbOutput, int dwFlags); + + [DllImport(CngDll, CharSet = CharSet.Unicode)] + public static extern unsafe NTSTATUS BCryptGetProperty(SafeBCryptHandle hObject, String pszProperty, byte* pbOutput, int cbOutput, out int pcbResult, int dwFlags); + + [DllImport(CngDll, CharSet = CharSet.Unicode)] + public static extern unsafe NTSTATUS BCryptSetProperty(SafeBCryptHandle hObject, String pszProperty, String pbInput, int cbInput, int dwFlags); + + [DllImport(CngDll, CharSet = CharSet.Unicode)] + public static extern NTSTATUS BCryptGenRandom(IntPtr hAlgorithm, [In, Out] byte[] pbBuffer, int cbBuffer, int dwFlags); + + [DllImport(CngDll, CharSet = CharSet.Unicode)] + public static extern NTSTATUS BCryptImportKey(SafeAlgorithmHandle hAlgorithm, IntPtr hImportKey, String pszBlobType, out SafeKeyHandle hKey, IntPtr pbKeyObject, int cbKeyObject, byte[] pbInput, int cbInput, int dwFlags); + + [DllImport(CngDll, CharSet = CharSet.Unicode)] + public static extern unsafe NTSTATUS BCryptEncrypt(SafeKeyHandle hKey, byte* pbInput, int cbInput, IntPtr paddingInfo, [In,Out] byte [] pbIV, int cbIV, byte* pbOutput, int cbOutput, out int cbResult, int dwFlags); + + [DllImport(CngDll, CharSet = CharSet.Unicode)] + public static extern unsafe NTSTATUS BCryptDecrypt(SafeKeyHandle hKey, byte* pbInput, int cbInput, IntPtr paddingInfo, [In, Out] byte[] pbIV, int cbIV, byte* pbOutput, int cbOutput, out int cbResult, int dwFlags); + + [DllImport(Capi2Dll, CharSet = CharSet.Ansi, SetLastError = true, BestFitMapping = false)] + public static extern bool CryptFormatObject( + [In] int dwCertEncodingType, // only valid value is X509_ASN_ENCODING + [In] int dwFormatType, // unused - pass 0. + [In] int dwFormatStrType, // select multiline + [In] IntPtr pFormatStruct, // unused - pass IntPtr.Zero + [MarshalAs(UnmanagedType.LPStr)] + [In] String lpszStructType, // OID value + [In] byte[] pbEncoded, // Data to be formatted + [In] int cbEncoded, // Length of data to be formatted + [MarshalAs(UnmanagedType.LPWStr)] + [In, Out] StringBuilder pbFormat, // Receives formatted string. + [In, Out] ref int pcbFormat); // Sends/receives length of formatted String. + } + } + + internal abstract class SafeBCryptHandle : SafeHandle + { + public SafeBCryptHandle() + : base(IntPtr.Zero, true) + { + } + + public sealed override bool IsInvalid + { + get + { + return handle == IntPtr.Zero; + } + } + } + + internal sealed class SafeAlgorithmHandle : SafeBCryptHandle + { + protected sealed override bool ReleaseHandle() + { + uint ntStatus = BCryptCloseAlgorithmProvider(handle, 0); + return ntStatus == 0; + } + + [DllImport(Cng.CngDll)] + private static extern uint BCryptCloseAlgorithmProvider(IntPtr hAlgorithm, int dwFlags); + } + + internal sealed class SafeHashHandle : SafeBCryptHandle + { + protected sealed override bool ReleaseHandle() + { + uint ntStatus = BCryptDestroyHash(handle); + return ntStatus == 0; + } + + [DllImport(Cng.CngDll)] + private static extern uint BCryptDestroyHash(IntPtr hHash); + } + + internal sealed class SafeKeyHandle : SafeBCryptHandle + { + protected sealed override bool ReleaseHandle() + { + uint ntStatus = BCryptDestroyKey(handle); + return ntStatus == 0; + } + + [DllImport(Cng.CngDll)] + private static extern uint BCryptDestroyKey(IntPtr hKey); + } + +} + diff --git a/src/Common/src/Microsoft/Win32/SafeHandles/SafeEvpMdCtxHandle.Unix.cs b/src/Common/src/Microsoft/Win32/SafeHandles/SafeEvpMdCtxHandle.Unix.cs new file mode 100644 index 000000000000..ba06b272a9f3 --- /dev/null +++ b/src/Common/src/Microsoft/Win32/SafeHandles/SafeEvpMdCtxHandle.Unix.cs @@ -0,0 +1,31 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; +using System.Security; +using System.Runtime.InteropServices; + +namespace Microsoft.Win32.SafeHandles +{ + [SecurityCritical] + internal sealed class SafeEvpMdCtxHandle : SafeHandle + { + private SafeEvpMdCtxHandle() : + base(IntPtr.Zero, ownsHandle: true) + { + } + + [SecurityCritical] + protected override bool ReleaseHandle() + { + Interop.libcrypto.EVP_MD_CTX_destroy(handle); + return true; + } + + public override bool IsInvalid + { + [SecurityCritical] + get { return handle == IntPtr.Zero; } + } + } +} diff --git a/src/System.Security.Cryptography.Hashing.Algorithms/System.Security.Cryptography.Hashing.Algorithms.sln b/src/System.Security.Cryptography.Hashing.Algorithms/System.Security.Cryptography.Hashing.Algorithms.sln new file mode 100644 index 000000000000..012f3b1e3270 --- /dev/null +++ b/src/System.Security.Cryptography.Hashing.Algorithms/System.Security.Cryptography.Hashing.Algorithms.sln @@ -0,0 +1,58 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 14 +VisualStudioVersion = 14.0.22911.2 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "System.Security.Cryptography.Hashing.Algorithms", "src\System.Security.Cryptography.Hashing.Algorithms.csproj", "{ACECDE06-FFFD-4E14-B651-11C189CBF4B8}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "System.Security.Cryptography.Hashing.Algorithms.Tests", "tests\System.Security.Cryptography.Hashing.Algorithms.Tests.csproj", "{F94007FF-0FBC-4817-B5F6-C8C8F1550AC6}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Linux_Debug|Any CPU = Linux_Debug|Any CPU + Linux_Release|Any CPU = Linux_Release|Any CPU + OSX_Debug|Any CPU = OSX_Debug|Any CPU + OSX_Release|Any CPU = OSX_Release|Any CPU + Release|Any CPU = Release|Any CPU + Windows_Debug|Any CPU = Windows_Debug|Any CPU + Windows_Release|Any CPU = Windows_Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {ACECDE06-FFFD-4E14-B651-11C189CBF4B8}.Debug|Any CPU.ActiveCfg = Windows_Debug|Any CPU + {ACECDE06-FFFD-4E14-B651-11C189CBF4B8}.Debug|Any CPU.Build.0 = Windows_Debug|Any CPU + {ACECDE06-FFFD-4E14-B651-11C189CBF4B8}.Linux_Debug|Any CPU.ActiveCfg = Linux_Debug|Any CPU + {ACECDE06-FFFD-4E14-B651-11C189CBF4B8}.Linux_Debug|Any CPU.Build.0 = Linux_Debug|Any CPU + {ACECDE06-FFFD-4E14-B651-11C189CBF4B8}.Linux_Release|Any CPU.ActiveCfg = Linux_Release|Any CPU + {ACECDE06-FFFD-4E14-B651-11C189CBF4B8}.Linux_Release|Any CPU.Build.0 = Linux_Release|Any CPU + {ACECDE06-FFFD-4E14-B651-11C189CBF4B8}.OSX_Debug|Any CPU.ActiveCfg = OSX_Debug|Any CPU + {ACECDE06-FFFD-4E14-B651-11C189CBF4B8}.OSX_Debug|Any CPU.Build.0 = OSX_Debug|Any CPU + {ACECDE06-FFFD-4E14-B651-11C189CBF4B8}.OSX_Release|Any CPU.ActiveCfg = OSX_Release|Any CPU + {ACECDE06-FFFD-4E14-B651-11C189CBF4B8}.OSX_Release|Any CPU.Build.0 = OSX_Release|Any CPU + {ACECDE06-FFFD-4E14-B651-11C189CBF4B8}.Release|Any CPU.ActiveCfg = Windows_Release|Any CPU + {ACECDE06-FFFD-4E14-B651-11C189CBF4B8}.Release|Any CPU.Build.0 = Windows_Release|Any CPU + {ACECDE06-FFFD-4E14-B651-11C189CBF4B8}.Windows_Debug|Any CPU.ActiveCfg = Windows_Debug|Any CPU + {ACECDE06-FFFD-4E14-B651-11C189CBF4B8}.Windows_Debug|Any CPU.Build.0 = Windows_Debug|Any CPU + {ACECDE06-FFFD-4E14-B651-11C189CBF4B8}.Windows_Release|Any CPU.ActiveCfg = Windows_Release|Any CPU + {ACECDE06-FFFD-4E14-B651-11C189CBF4B8}.Windows_Release|Any CPU.Build.0 = Windows_Release|Any CPU + {F94007FF-0FBC-4817-B5F6-C8C8F1550AC6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F94007FF-0FBC-4817-B5F6-C8C8F1550AC6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F94007FF-0FBC-4817-B5F6-C8C8F1550AC6}.Linux_Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F94007FF-0FBC-4817-B5F6-C8C8F1550AC6}.Linux_Debug|Any CPU.Build.0 = Debug|Any CPU + {F94007FF-0FBC-4817-B5F6-C8C8F1550AC6}.Linux_Release|Any CPU.ActiveCfg = Release|Any CPU + {F94007FF-0FBC-4817-B5F6-C8C8F1550AC6}.Linux_Release|Any CPU.Build.0 = Release|Any CPU + {F94007FF-0FBC-4817-B5F6-C8C8F1550AC6}.OSX_Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F94007FF-0FBC-4817-B5F6-C8C8F1550AC6}.OSX_Debug|Any CPU.Build.0 = Debug|Any CPU + {F94007FF-0FBC-4817-B5F6-C8C8F1550AC6}.OSX_Release|Any CPU.ActiveCfg = Release|Any CPU + {F94007FF-0FBC-4817-B5F6-C8C8F1550AC6}.OSX_Release|Any CPU.Build.0 = Release|Any CPU + {F94007FF-0FBC-4817-B5F6-C8C8F1550AC6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F94007FF-0FBC-4817-B5F6-C8C8F1550AC6}.Release|Any CPU.Build.0 = Release|Any CPU + {F94007FF-0FBC-4817-B5F6-C8C8F1550AC6}.Windows_Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F94007FF-0FBC-4817-B5F6-C8C8F1550AC6}.Windows_Debug|Any CPU.Build.0 = Debug|Any CPU + {F94007FF-0FBC-4817-B5F6-C8C8F1550AC6}.Windows_Release|Any CPU.ActiveCfg = Release|Any CPU + {F94007FF-0FBC-4817-B5F6-C8C8F1550AC6}.Windows_Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/src/System.Security.Cryptography.Hashing.Algorithms/src/Internal/Cryptography/HMACCommon.cs b/src/System.Security.Cryptography.Hashing.Algorithms/src/Internal/Cryptography/HMACCommon.cs new file mode 100644 index 000000000000..19a3372eae4c --- /dev/null +++ b/src/System.Security.Cryptography.Hashing.Algorithms/src/Internal/Cryptography/HMACCommon.cs @@ -0,0 +1,70 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; +using System.Diagnostics; +using System.Security.Cryptography; + +using Internal.Cryptography; + +namespace Internal.Cryptography +{ + // + // This class provides the common functionality for HMACSHA1, HMACSHA256, HMACMD5, etc. + // Ideally, this would be encapsulated in a common base class but the preexisting contract + // locks these public classes into deriving directly from HMAC so we have to use encapsulation + // and delegation to HMACCommon instead. + // + // This wrapper adds the ability to change the Key on the fly for compat with the desktop. + // + internal sealed class HMACCommon + { + public HMACCommon(String hashAlgorithmId, byte[] key) + { + _hashAlgorithmId = hashAlgorithmId; + ChangeKey(key); + } + + public int HashSizeInBits + { + get + { + return _hashProvider.HashSizeInBytes * 8; + } + } + + public void ChangeKey(byte[] key) + { + HashProvider oldHashProvider = _hashProvider; + _hashProvider = null; + if (oldHashProvider != null) + oldHashProvider.Dispose(true); + _hashProvider = HashProviderDispenser.CreateMacProvider(_hashAlgorithmId, key); + } + + // Adds new data to be hashed. This can be called repeatedly in order to hash data from incontiguous sources. + public void AppendHashData(byte[] data, int offset, int count) + { + _hashProvider.AppendHashData(data, offset, count); + } + + // Compute the hash based on the appended data and resets the HashProvider for more hashing. + public byte[] FinalizeHashAndReset() + { + return _hashProvider.FinalizeHashAndReset(); + } + + public void Dispose(bool disposing) + { + if (disposing) + { + if (_hashProvider != null) + _hashProvider.Dispose(true); + _hashProvider = null; + } + } + + private readonly String _hashAlgorithmId; + private HashProvider _hashProvider; + } +} diff --git a/src/System.Security.Cryptography.Hashing.Algorithms/src/Internal/Cryptography/HashAlgorithmNames.cs b/src/System.Security.Cryptography.Hashing.Algorithms/src/Internal/Cryptography/HashAlgorithmNames.cs new file mode 100644 index 000000000000..ebda34d1532e --- /dev/null +++ b/src/System.Security.Cryptography.Hashing.Algorithms/src/Internal/Cryptography/HashAlgorithmNames.cs @@ -0,0 +1,21 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; +using System.Diagnostics; + +namespace Internal.Cryptography +{ + internal static class HashAlgorithmNames + { + // These are accepted by CNG + public const String MD5 = "MD5"; + public const String SHA1 = "SHA1"; + public const String SHA256 = "SHA256"; + public const String SHA384 = "SHA384"; + public const String SHA512 = "SHA512"; + } +} + + + diff --git a/src/System.Security.Cryptography.Hashing.Algorithms/src/Internal/Cryptography/HashProvider.cs b/src/System.Security.Cryptography.Hashing.Algorithms/src/Internal/Cryptography/HashProvider.cs new file mode 100644 index 000000000000..19ec5317c782 --- /dev/null +++ b/src/System.Security.Cryptography.Hashing.Algorithms/src/Internal/Cryptography/HashProvider.cs @@ -0,0 +1,29 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; +using System.Diagnostics; + +namespace Internal.Cryptography +{ + // + // This abstract class represents a reusable hash object and can wrap a CNG or WinRT hash object. + // + internal abstract class HashProvider + { + // Adds new data to be hashed. This can be called repeatedly in order to hash data from incontiguous sources. + public abstract void AppendHashData(byte[] data, int offset, int count); + + // Compute the hash based on the appended data and resets the HashProvider for more hashing. + public abstract byte[] FinalizeHashAndReset(); + + // Returns the length of the byte array returned by FinalizeHashAndReset. + public abstract int HashSizeInBytes { get; } + + // Releases any native resources and keys used by the HashProvider. + public abstract void Dispose(bool disposing); + } +} + + + diff --git a/src/System.Security.Cryptography.Hashing.Algorithms/src/Internal/Cryptography/HashProviderDispenser.Unix.cs b/src/System.Security.Cryptography.Hashing.Algorithms/src/Internal/Cryptography/HashProviderDispenser.Unix.cs new file mode 100644 index 000000000000..3af8a40c399b --- /dev/null +++ b/src/System.Security.Cryptography.Hashing.Algorithms/src/Internal/Cryptography/HashProviderDispenser.Unix.cs @@ -0,0 +1,183 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; +using System.Diagnostics; +using System.Runtime.InteropServices; +using System.Security.Cryptography; +using Microsoft.Win32.SafeHandles; + +namespace Internal.Cryptography +{ + internal static partial class HashProviderDispenser + { + public static HashProvider CreateHashProvider(string hashAlgorithmId) + { + switch (hashAlgorithmId) + { + case HashAlgorithmNames.SHA1: + return new EvpHashProvider(Interop.libcrypto.EVP_sha1()); + case HashAlgorithmNames.SHA256: + return new EvpHashProvider(Interop.libcrypto.EVP_sha256()); + case HashAlgorithmNames.SHA384: + return new EvpHashProvider(Interop.libcrypto.EVP_sha384()); + case HashAlgorithmNames.SHA512: + return new EvpHashProvider(Interop.libcrypto.EVP_sha512()); + case HashAlgorithmNames.MD5: + return new EvpHashProvider(Interop.libcrypto.EVP_md5()); + } + throw new CryptographicException(); + } + + public static unsafe HashProvider CreateMacProvider(string hashAlgorithmId, byte[] key) + { + switch (hashAlgorithmId) + { + case HashAlgorithmNames.SHA1: + return new HmacHashProvider(Interop.libcrypto.EVP_sha1(), key); + case HashAlgorithmNames.SHA256: + return new HmacHashProvider(Interop.libcrypto.EVP_sha256(), key); + case HashAlgorithmNames.SHA384: + return new HmacHashProvider(Interop.libcrypto.EVP_sha384(), key); + case HashAlgorithmNames.SHA512: + return new HmacHashProvider(Interop.libcrypto.EVP_sha512(), key); + case HashAlgorithmNames.MD5: + return new HmacHashProvider(Interop.libcrypto.EVP_md5(), key); + } + throw new CryptographicException(); + } + + // ----------------------------- + // ---- PAL layer ends here ---- + // ----------------------------- + + private sealed class EvpHashProvider : HashProvider + { + private readonly int _hashSize; + private readonly SafeEvpMdCtxHandle _ctx; + + public EvpHashProvider(IntPtr algorithmEvp) + { + Debug.Assert(algorithmEvp != IntPtr.Zero); + + _hashSize = Interop.libcrypto.EVP_MD_size(algorithmEvp); + if (_hashSize <= 0 || _hashSize > Interop.libcrypto.EVP_MAX_MD_SIZE) + { + throw new CryptographicException(); + } + + _ctx = Interop.libcrypto.EVP_MD_CTX_create(); + if (_ctx.IsInvalid) + { + throw new CryptographicException(); + } + + Check(Interop.libcrypto.EVP_DigestInit_ex(_ctx, algorithmEvp, IntPtr.Zero)); + } + + public sealed override unsafe void AppendHashData(byte[] data, int offset, int count) + { + Debug.Assert(data != null); + Debug.Assert(offset >= 0 && count >= 0); + Debug.Assert((offset + count) >= 0 && (offset + count) <= data.Length); + + fixed (byte* md = data) + { + Check(Interop.libcrypto.EVP_DigestUpdate(_ctx, md + offset, (IntPtr)count)); + } + } + + public sealed override unsafe byte[] FinalizeHashAndReset() + { + byte* md = stackalloc byte[Interop.libcrypto.EVP_MAX_MD_SIZE]; + uint length = Interop.libcrypto.EVP_MAX_MD_SIZE; + Check(Interop.libcrypto.EVP_DigestFinal_ex(_ctx, md, ref length)); + Debug.Assert(length == _hashSize); + + byte[] result = new byte[(int)length]; + Marshal.Copy((IntPtr)md, result, 0, (int)length); + return result; + } + + public sealed override int HashSizeInBytes + { + get { return _hashSize; } + } + + public sealed override void Dispose(bool disposing) + { + if (disposing) + { + _ctx.Dispose(); + } + } + } + + private sealed class HmacHashProvider : HashProvider + { + private readonly int _hashSize; + private Interop.libcrypto.HMAC_CTX _hmacCtx; + + public unsafe HmacHashProvider(IntPtr algorithmEvp, byte[] key) + { + Debug.Assert(algorithmEvp != IntPtr.Zero); + Debug.Assert(key != null); + + _hashSize = Interop.libcrypto.EVP_MD_size(algorithmEvp); + if (_hashSize <= 0 || _hashSize > Interop.libcrypto.EVP_MAX_MD_SIZE) + { + throw new CryptographicException(); + } + + fixed (byte* keyPtr = key) + { + Check(Interop.libcrypto.HMAC_Init(out _hmacCtx, keyPtr, key.Length, algorithmEvp)); + } + } + + public sealed override unsafe void AppendHashData(byte[] data, int offset, int count) + { + Debug.Assert(data != null); + Debug.Assert(offset >= 0 && count >= 0); + Debug.Assert((offset + count) >= 0 && (offset + count) <= data.Length); + + fixed (byte* md = data) + { + Check(Interop.libcrypto.HMAC_Update(ref _hmacCtx, md + offset, count)); + } + } + + public sealed override unsafe byte[] FinalizeHashAndReset() + { + byte* md = stackalloc byte[Interop.libcrypto.EVP_MAX_MD_SIZE]; + uint length = Interop.libcrypto.EVP_MAX_MD_SIZE; + Check(Interop.libcrypto.HMAC_Final(ref _hmacCtx, md, ref length)); + Debug.Assert(length == _hashSize); + + byte[] result = new byte[(int)length]; + Marshal.Copy((IntPtr)md, result, 0, (int)length); + return result; + } + + public sealed override int HashSizeInBytes + { + get { return _hashSize; } + } + + public sealed override void Dispose(bool disposing) + { + Interop.libcrypto.HMAC_CTX_cleanup(ref _hmacCtx); + } + } + + private static void Check(int result) + { + const int Success = 1; + if (result != Success) + { + Debug.Assert(result == 0); + throw new CryptographicException(Interop.libcrypto.GetOpenSslErrorString()); + } + } + } +} diff --git a/src/System.Security.Cryptography.Hashing.Algorithms/src/Internal/Cryptography/HashProviderDispenser.Windows.cs b/src/System.Security.Cryptography.Hashing.Algorithms/src/Internal/Cryptography/HashProviderDispenser.Windows.cs new file mode 100644 index 000000000000..9564a9225f66 --- /dev/null +++ b/src/System.Security.Cryptography.Hashing.Algorithms/src/Internal/Cryptography/HashProviderDispenser.Windows.cs @@ -0,0 +1,139 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; +using System.Diagnostics; +using System.Security.Cryptography; + +using Internal.NativeCrypto; + +namespace Internal.Cryptography +{ + // + // Provides hash services via the native provider (CNG). + // + internal static partial class HashProviderDispenser + { + public static HashProvider CreateHashProvider(String hashAlgorithmId) + { + return new HashProviderCng(hashAlgorithmId, null); + } + + public static HashProvider CreateMacProvider(String hashAlgorithmId, byte[] key) + { + return new HashProviderCng(hashAlgorithmId, key); + } + + // ----------------------------- + // ---- PAL layer ends here ---- + // ----------------------------- + + private sealed class HashProviderCng : HashProvider + { + // + // - "hashAlgId" must be a name recognized by BCryptOpenAlgorithmProvider(). Examples: MD5, SHA1, SHA256. + // + // - "key" activates MAC hashing if present. If null, this HashProvider performs a regular old hash. + // + public HashProviderCng(String hashAlgId, byte[] key) + { + Cng.OpenAlgorithmProviderFlags dwFlags = Cng.OpenAlgorithmProviderFlags.NONE; + if (key != null) + { + _key = key.CloneByteArray(); + dwFlags |= Cng.OpenAlgorithmProviderFlags.BCRYPT_ALG_HANDLE_HMAC_FLAG; + } + _hAlgorithm = Cng.BCryptOpenAlgorithmProvider(hashAlgId, null, dwFlags); + + _hHash = _hAlgorithm.BCryptTryCreateReusableHash(_key); + if (_hHash == null) + { + // If we got here, we're running on a downlevel OS that doesn't support reusable CNG hash objects. Fall back to creating a + // new HASH object each time. + ResetHashObject(); + } + else + { + _reusable = true; + } + + _hashSize = _hHash.GetHashSizeInBytes(); + return; + } + + public sealed override void AppendHashData(byte[] rgb, int ib, int cb) + { + unsafe + { + fixed (byte* pRgb = rgb) + { + _hHash.BCryptHashData(pRgb + ib, cb); + } + } + } + + public sealed override byte[] FinalizeHashAndReset() + { + byte[] hash = _hHash.BCryptFinishHash(_hashSize); + ResetHashObject(); + return hash; + } + + public sealed override void Dispose(bool disposing) + { + if (disposing) + { + DestroyHash(); + SafeAlgorithmHandle hAlgorithm = _hAlgorithm; + _hAlgorithm = null; + if (hAlgorithm != null) + { + hAlgorithm.Dispose(); + } + + if (_key != null) + { + byte[] key = _key; + _key = null; + Array.Clear(key, 0, key.Length); + } + } + } + + public sealed override int HashSizeInBytes + { + get + { + return _hashSize; + } + } + + private void ResetHashObject() + { + if (_reusable) + return; + DestroyHash(); + _hHash = _hAlgorithm.BCryptCreateHash(_key, 0); + } + + private void DestroyHash() + { + SafeHashHandle hHash = _hHash; + _hHash = null; + if (hHash != null) + { + hHash.Dispose(); + } + } + + private SafeAlgorithmHandle _hAlgorithm; + private SafeHashHandle _hHash; + private byte[] _key; + private readonly bool _reusable; + + private readonly int _hashSize; + } + } +} + + diff --git a/src/System.Security.Cryptography.Hashing.Algorithms/src/Internal/Cryptography/Helpers.cs b/src/System.Security.Cryptography.Hashing.Algorithms/src/Internal/Cryptography/Helpers.cs new file mode 100644 index 000000000000..e5c65a3093aa --- /dev/null +++ b/src/System.Security.Cryptography.Hashing.Algorithms/src/Internal/Cryptography/Helpers.cs @@ -0,0 +1,28 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; +using System.Diagnostics; +using System.Security.Cryptography; + +namespace Internal.Cryptography +{ + internal static class Helpers + { + public static byte[] CloneByteArray(this byte[] src) + { + return (byte[])(src.Clone()); + } + + public static byte[] GenerateRandom(int count) + { + byte[] buffer = new byte[count]; + using (RandomNumberGenerator rng = RandomNumberGenerator.Create()) + { + rng.GetBytes(buffer); + } + return buffer; + } + } +} + diff --git a/src/System.Security.Cryptography.Hashing.Algorithms/src/Resources/Strings.resx b/src/System.Security.Cryptography.Hashing.Algorithms/src/Resources/Strings.resx new file mode 100644 index 000000000000..1af7de150c99 --- /dev/null +++ b/src/System.Security.Cryptography.Hashing.Algorithms/src/Resources/Strings.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/src/System.Security.Cryptography.Hashing.Algorithms/src/System.Security.Cryptography.Hashing.Algorithms.csproj b/src/System.Security.Cryptography.Hashing.Algorithms/src/System.Security.Cryptography.Hashing.Algorithms.csproj new file mode 100644 index 000000000000..f6d1f0282bfb --- /dev/null +++ b/src/System.Security.Cryptography.Hashing.Algorithms/src/System.Security.Cryptography.Hashing.Algorithms.csproj @@ -0,0 +1,76 @@ + + + + + Windows_Debug + AnyCPU + {ACECDE06-FFFD-4E14-B651-11C189CBF4B8} + Library + System.Security.Cryptography.Hashing.Algorithms + 4.0.0.0 + true + false + + + + + + + + + + + + + + + + + + + + + + + + + + Common\Interop\Windows\BCrypt\Cng.cs + + + + + + Common\Interop\Unix\Interop.Libraries.cs + + + Common\Interop\Unix\libcoreclr\Interop.EnsureOpenSslInitialized.cs + + + Common\Interop\Unix\libcrypto\Interop.ERR.cs + + + Common\Interop\Unix\libcrypto\Interop.EVP.cs + + + Common\Interop\Unix\libcrypto\Interop.Initialization.cs + + + Common\Microsoft\Win32\SafeHandles\SafeEvpMdCtxHandle.Unix.cs + + + + + Common\Interop\Linux\libcrypto\Interop.HMAC.cs + + + + + Common\Interop\OSX\libcrypto\Interop.HMAC.cs + + + + + + + diff --git a/src/System.Security.Cryptography.Hashing.Algorithms/src/System/Security/Cryptography/HMACSHA1.cs b/src/System.Security.Cryptography.Hashing.Algorithms/src/System/Security/Cryptography/HMACSHA1.cs new file mode 100644 index 000000000000..6d77d211e7b5 --- /dev/null +++ b/src/System.Security.Cryptography.Hashing.Algorithms/src/System/Security/Cryptography/HMACSHA1.cs @@ -0,0 +1,83 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; +using System.Diagnostics; +using System.Security.Cryptography; + +using Internal.Cryptography; + +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 desktop locks all of these into deriving directly from HMAC, it can't be helped. + // + + public class HMACSHA1 : HMAC + { + public HMACSHA1() + : this(Helpers.GenerateRandom(64)) + { + } + + public HMACSHA1(byte[] key) + { + this.HashName = HashAlgorithmNames.SHA1; + _hMacCommon = new HMACCommon(HashAlgorithmNames.SHA1, key); + base.Key = key; + } + + public override int HashSize + { + get + { + return _hMacCommon.HashSizeInBits; + } + } + + public override byte[] Key + { + get + { + return base.Key; + } + set + { + base.Key = value; + _hMacCommon.ChangeKey(value); + } + } + + protected override void HashCore(byte[] rgb, int ib, int cb) + { + _hMacCommon.AppendHashData(rgb, ib, cb); + } + + protected override byte[] HashFinal() + { + return _hMacCommon.FinalizeHashAndReset(); + } + + 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) + { + if (disposing) + { + HMACCommon hMacCommon = _hMacCommon; + _hMacCommon = null; + if (hMacCommon != null) + hMacCommon.Dispose(disposing); + } + base.Dispose(disposing); + } + + private HMACCommon _hMacCommon; + } +} diff --git a/src/System.Security.Cryptography.Hashing.Algorithms/src/System/Security/Cryptography/HMACSHA256.cs b/src/System.Security.Cryptography.Hashing.Algorithms/src/System/Security/Cryptography/HMACSHA256.cs new file mode 100644 index 000000000000..ebac10b44449 --- /dev/null +++ b/src/System.Security.Cryptography.Hashing.Algorithms/src/System/Security/Cryptography/HMACSHA256.cs @@ -0,0 +1,83 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; +using System.Diagnostics; +using System.Security.Cryptography; + +using Internal.Cryptography; + +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 desktop locks all of these into deriving directly from HMAC, it can't be helped. + // + + public class HMACSHA256 : HMAC + { + public HMACSHA256() + : this(Helpers.GenerateRandom(64)) + { + } + + public HMACSHA256(byte[] key) + { + this.HashName = HashAlgorithmNames.SHA256; + _hMacCommon = new HMACCommon(HashAlgorithmNames.SHA256, key); + base.Key = key; + } + + public override int HashSize + { + get + { + return _hMacCommon.HashSizeInBits; + } + } + + public override byte[] Key + { + get + { + return base.Key; + } + set + { + base.Key = value; + _hMacCommon.ChangeKey(value); + } + } + + protected override void HashCore(byte[] rgb, int ib, int cb) + { + _hMacCommon.AppendHashData(rgb, ib, cb); + } + + protected override byte[] HashFinal() + { + return _hMacCommon.FinalizeHashAndReset(); + } + + 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) + { + if (disposing) + { + HMACCommon hMacCommon = _hMacCommon; + _hMacCommon = null; + if (hMacCommon != null) + hMacCommon.Dispose(disposing); + } + base.Dispose(disposing); + } + + private HMACCommon _hMacCommon; + } +} diff --git a/src/System.Security.Cryptography.Hashing.Algorithms/src/System/Security/Cryptography/HMACSHA384.cs b/src/System.Security.Cryptography.Hashing.Algorithms/src/System/Security/Cryptography/HMACSHA384.cs new file mode 100644 index 000000000000..71d982bfaa9e --- /dev/null +++ b/src/System.Security.Cryptography.Hashing.Algorithms/src/System/Security/Cryptography/HMACSHA384.cs @@ -0,0 +1,83 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; +using System.Diagnostics; +using System.Security.Cryptography; + +using Internal.Cryptography; + +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 desktop locks all of these into deriving directly from HMAC, it can't be helped. + // + + public class HMACSHA384 : HMAC + { + public HMACSHA384() + : this(Helpers.GenerateRandom(128)) + { + } + + public HMACSHA384(byte[] key) + { + this.HashName = HashAlgorithmNames.SHA384; + _hMacCommon = new HMACCommon(HashAlgorithmNames.SHA384, key); + base.Key = key; + } + + public override int HashSize + { + get + { + return _hMacCommon.HashSizeInBits; + } + } + + public override byte[] Key + { + get + { + return base.Key; + } + set + { + base.Key = value; + _hMacCommon.ChangeKey(value); + } + } + + protected override void HashCore(byte[] rgb, int ib, int cb) + { + _hMacCommon.AppendHashData(rgb, ib, cb); + } + + protected override byte[] HashFinal() + { + return _hMacCommon.FinalizeHashAndReset(); + } + + 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) + { + if (disposing) + { + HMACCommon hMacCommon = _hMacCommon; + _hMacCommon = null; + if (hMacCommon != null) + hMacCommon.Dispose(disposing); + } + base.Dispose(disposing); + } + + private HMACCommon _hMacCommon; + } +} diff --git a/src/System.Security.Cryptography.Hashing.Algorithms/src/System/Security/Cryptography/HMACSHA512.cs b/src/System.Security.Cryptography.Hashing.Algorithms/src/System/Security/Cryptography/HMACSHA512.cs new file mode 100644 index 000000000000..337a98db4687 --- /dev/null +++ b/src/System.Security.Cryptography.Hashing.Algorithms/src/System/Security/Cryptography/HMACSHA512.cs @@ -0,0 +1,83 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; +using System.Diagnostics; +using System.Security.Cryptography; + +using Internal.Cryptography; + +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 desktop locks all of these into deriving directly from HMAC, it can't be helped. + // + + public class HMACSHA512 : HMAC + { + public HMACSHA512() + : this(Helpers.GenerateRandom(128)) + { + } + + public HMACSHA512(byte[] key) + { + this.HashName = HashAlgorithmNames.SHA512; + _hMacCommon = new HMACCommon(HashAlgorithmNames.SHA512, key); + base.Key = key; + } + + public override int HashSize + { + get + { + return _hMacCommon.HashSizeInBits; + } + } + + public override byte[] Key + { + get + { + return base.Key; + } + set + { + base.Key = value; + _hMacCommon.ChangeKey(value); + } + } + + protected override void HashCore(byte[] rgb, int ib, int cb) + { + _hMacCommon.AppendHashData(rgb, ib, cb); + } + + protected override byte[] HashFinal() + { + return _hMacCommon.FinalizeHashAndReset(); + } + + 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) + { + if (disposing) + { + HMACCommon hMacCommon = _hMacCommon; + _hMacCommon = null; + if (hMacCommon != null) + hMacCommon.Dispose(disposing); + } + base.Dispose(disposing); + } + + private HMACCommon _hMacCommon; + } +} diff --git a/src/System.Security.Cryptography.Hashing.Algorithms/src/System/Security/Cryptography/MD5.cs b/src/System.Security.Cryptography.Hashing.Algorithms/src/System/Security/Cryptography/MD5.cs new file mode 100644 index 000000000000..c9d0db03f755 --- /dev/null +++ b/src/System.Security.Cryptography.Hashing.Algorithms/src/System/Security/Cryptography/MD5.cs @@ -0,0 +1,69 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; +using System.Diagnostics; + +using Internal.Cryptography; + +namespace System.Security.Cryptography +{ + // + // If you change anything in this class, you must make the same change in the other *Provider classes. This is a pain but given that the + // preexisting contract from the desktop locks all of these into deriving directly from the abstract HashAlgorithm class, + // it can't be helped. + // + + public abstract class MD5 : HashAlgorithm + { + protected MD5() + { + } + + public static MD5 Create() + { + return new Implementation(); + } + + private sealed class Implementation : MD5 + { + public Implementation() + { + _hashProvider = HashProviderDispenser.CreateHashProvider(HashAlgorithmNames.MD5); + } + + public sealed override int HashSize + { + get + { + return _hashProvider.HashSizeInBytes * 8; + } + } + + protected sealed override void HashCore(byte[] array, int ibStart, int cbSize) + { + _hashProvider.AppendHashData(array, ibStart, cbSize); + } + + protected sealed override byte[] HashFinal() + { + return _hashProvider.FinalizeHashAndReset(); + } + + 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) + { + _hashProvider.Dispose(disposing); + base.Dispose(disposing); + } + + private readonly HashProvider _hashProvider; + } + } +} diff --git a/src/System.Security.Cryptography.Hashing.Algorithms/src/System/Security/Cryptography/SHA1.cs b/src/System.Security.Cryptography.Hashing.Algorithms/src/System/Security/Cryptography/SHA1.cs new file mode 100644 index 000000000000..7ecd833a2dc6 --- /dev/null +++ b/src/System.Security.Cryptography.Hashing.Algorithms/src/System/Security/Cryptography/SHA1.cs @@ -0,0 +1,69 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; +using System.Diagnostics; + +using Internal.Cryptography; + +namespace System.Security.Cryptography +{ + // + // If you change anything in this class, you must make the same change in the other *Provider classes. This is a pain but given that the + // preexisting contract from the desktop locks all of these into deriving directly from the abstract HashAlgorithm class, + // it can't be helped. + // + + public abstract class SHA1 : HashAlgorithm + { + protected SHA1() + { + } + + public static SHA1 Create() + { + return new Implementation(); + } + + private sealed class Implementation : SHA1 + { + public Implementation() + { + _hashProvider = HashProviderDispenser.CreateHashProvider(HashAlgorithmNames.SHA1); + } + + public sealed override int HashSize + { + get + { + return _hashProvider.HashSizeInBytes * 8; + } + } + + protected sealed override void HashCore(byte[] array, int ibStart, int cbSize) + { + _hashProvider.AppendHashData(array, ibStart, cbSize); + } + + protected sealed override byte[] HashFinal() + { + return _hashProvider.FinalizeHashAndReset(); + } + + 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) + { + _hashProvider.Dispose(disposing); + base.Dispose(disposing); + } + + private readonly HashProvider _hashProvider; + } + } +} diff --git a/src/System.Security.Cryptography.Hashing.Algorithms/src/System/Security/Cryptography/SHA256.cs b/src/System.Security.Cryptography.Hashing.Algorithms/src/System/Security/Cryptography/SHA256.cs new file mode 100644 index 000000000000..e1d00243e20a --- /dev/null +++ b/src/System.Security.Cryptography.Hashing.Algorithms/src/System/Security/Cryptography/SHA256.cs @@ -0,0 +1,69 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; +using System.Diagnostics; + +using Internal.Cryptography; + +namespace System.Security.Cryptography +{ + // + // If you change anything in this class, you must make the same change in the other *Provider classes. This is a pain but given that the + // preexisting contract from the desktop locks all of these into deriving directly from the abstract HashAlgorithm class, + // it can't be helped. + // + + public abstract class SHA256 : HashAlgorithm + { + protected SHA256() + { + } + + public static SHA256 Create() + { + return new Implementation(); + } + + private sealed class Implementation : SHA256 + { + public Implementation() + { + _hashProvider = HashProviderDispenser.CreateHashProvider(HashAlgorithmNames.SHA256); + } + + public sealed override int HashSize + { + get + { + return _hashProvider.HashSizeInBytes * 8; + } + } + + protected sealed override void HashCore(byte[] array, int ibStart, int cbSize) + { + _hashProvider.AppendHashData(array, ibStart, cbSize); + } + + protected sealed override byte[] HashFinal() + { + return _hashProvider.FinalizeHashAndReset(); + } + + 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) + { + _hashProvider.Dispose(disposing); + base.Dispose(disposing); + } + + private readonly HashProvider _hashProvider; + } + } +} diff --git a/src/System.Security.Cryptography.Hashing.Algorithms/src/System/Security/Cryptography/SHA384.cs b/src/System.Security.Cryptography.Hashing.Algorithms/src/System/Security/Cryptography/SHA384.cs new file mode 100644 index 000000000000..b6a5106ae06a --- /dev/null +++ b/src/System.Security.Cryptography.Hashing.Algorithms/src/System/Security/Cryptography/SHA384.cs @@ -0,0 +1,69 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; +using System.Diagnostics; + +using Internal.Cryptography; + +namespace System.Security.Cryptography +{ + // + // If you change anything in this class, you must make the same change in the other *Provider classes. This is a pain but given that the + // preexisting contract from the desktop locks all of these into deriving directly from the abstract HashAlgorithm class, + // it can't be helped. + // + + public abstract class SHA384 : HashAlgorithm + { + protected SHA384() + { + } + + public static SHA384 Create() + { + return new Implementation(); + } + + private sealed class Implementation : SHA384 + { + public Implementation() + { + _hashProvider = HashProviderDispenser.CreateHashProvider(HashAlgorithmNames.SHA384); + } + + public sealed override int HashSize + { + get + { + return _hashProvider.HashSizeInBytes * 8; + } + } + + protected sealed override void HashCore(byte[] array, int ibStart, int cbSize) + { + _hashProvider.AppendHashData(array, ibStart, cbSize); + } + + protected sealed override byte[] HashFinal() + { + return _hashProvider.FinalizeHashAndReset(); + } + + 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) + { + _hashProvider.Dispose(disposing); + base.Dispose(disposing); + } + + private readonly HashProvider _hashProvider; + } + } +} diff --git a/src/System.Security.Cryptography.Hashing.Algorithms/src/System/Security/Cryptography/SHA512.cs b/src/System.Security.Cryptography.Hashing.Algorithms/src/System/Security/Cryptography/SHA512.cs new file mode 100644 index 000000000000..f21b88ad4e74 --- /dev/null +++ b/src/System.Security.Cryptography.Hashing.Algorithms/src/System/Security/Cryptography/SHA512.cs @@ -0,0 +1,69 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; +using System.Diagnostics; + +using Internal.Cryptography; + +namespace System.Security.Cryptography +{ + // + // If you change anything in this class, you must make the same change in the other *Provider classes. This is a pain but given that the + // preexisting contract from the desktop locks all of these into deriving directly from the abstract HashAlgorithm class, + // it can't be helped. + // + + public abstract class SHA512 : HashAlgorithm + { + protected SHA512() + { + } + + public static SHA512 Create() + { + return new Implementation(); + } + + private sealed class Implementation : SHA512 + { + public Implementation() + { + _hashProvider = HashProviderDispenser.CreateHashProvider(HashAlgorithmNames.SHA512); + } + + public sealed override int HashSize + { + get + { + return _hashProvider.HashSizeInBytes * 8; + } + } + + protected sealed override void HashCore(byte[] array, int ibStart, int cbSize) + { + _hashProvider.AppendHashData(array, ibStart, cbSize); + } + + protected sealed override byte[] HashFinal() + { + return _hashProvider.FinalizeHashAndReset(); + } + + 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) + { + _hashProvider.Dispose(disposing); + base.Dispose(disposing); + } + + private readonly HashProvider _hashProvider; + } + } +} diff --git a/src/System.Security.Cryptography.Hashing.Algorithms/src/project.json b/src/System.Security.Cryptography.Hashing.Algorithms/src/project.json new file mode 100644 index 000000000000..bd738bcf850f --- /dev/null +++ b/src/System.Security.Cryptography.Hashing.Algorithms/src/project.json @@ -0,0 +1,16 @@ +{ + "dependencies": { + "System.Diagnostics.Contracts": "4.0.0-beta-22809", + "System.Diagnostics.Debug": "4.0.10-beta-22809", + "System.Resources.ResourceManager": "4.0.0-beta-22809", + "System.Runtime": "4.0.20-beta-22809", + "System.Runtime.InteropServices": "4.0.20-beta-22809", + "System.Security.Cryptography.Encoding": "4.0.0-beta-22809", + "System.Security.Cryptography.Encryption": "4.0.0-beta-22809", + "System.Security.Cryptography.Hashing": "4.0.0-beta-22809", + "System.Security.Cryptography.RandomNumberGenerator": "4.0.0-beta-22809", + }, + "frameworks": { + "dnxcore50": {} + } +} diff --git a/src/System.Security.Cryptography.Hashing.Algorithms/tests/ByteUtils.cs b/src/System.Security.Cryptography.Hashing.Algorithms/tests/ByteUtils.cs new file mode 100644 index 000000000000..8e13a88cd560 --- /dev/null +++ b/src/System.Security.Cryptography.Hashing.Algorithms/tests/ByteUtils.cs @@ -0,0 +1,48 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; +using System.Globalization; + +namespace System.Security.Cryptography.Hashing.Algorithms.Tests +{ + internal static class ByteUtils + { + internal static byte[] AsciiBytes(string s) + { + byte[] bytes = new byte[s.Length]; + + for (int i = 0; i < s.Length; i++) + { + bytes[i] = (byte)s[i]; + } + + return bytes; + } + + internal static byte[] HexToByteArray(string hexString) + { + byte[] bytes = new byte[hexString.Length / 2]; + + for (int i = 0; i < hexString.Length; i += 2) + { + string s = hexString.Substring(i, 2); + bytes[i / 2] = byte.Parse(s, NumberStyles.HexNumber, null); + } + + return bytes; + } + + internal static byte[] RepeatByte(byte b, int count) + { + byte[] value = new byte[count]; + + for (int i = 0; i < count; i++) + { + value[i] = b; + } + + return value; + } + } +} diff --git a/src/System.Security.Cryptography.Hashing.Algorithms/tests/HashAlgorithmTest.cs b/src/System.Security.Cryptography.Hashing.Algorithms/tests/HashAlgorithmTest.cs new file mode 100644 index 000000000000..1948c3559797 --- /dev/null +++ b/src/System.Security.Cryptography.Hashing.Algorithms/tests/HashAlgorithmTest.cs @@ -0,0 +1,138 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System.IO; +using Xunit; + +namespace System.Security.Cryptography.Hashing.Algorithms.Tests +{ + public abstract class HashAlgorithmTest + { + protected abstract HashAlgorithm Create(); + + protected void Verify(string input, string output) + { + Verify(ByteUtils.AsciiBytes(input), output); + } + + protected void Verify(Stream input, string output) + { + byte[] expected = ByteUtils.HexToByteArray(output); + byte[] actual; + + using (HashAlgorithm hash = Create()) + { + actual = hash.ComputeHash(input); + } + + Assert.Equal(expected, actual); + } + + protected void Verify(byte[] input, string output) + { + byte[] expected = ByteUtils.HexToByteArray(output); + byte[] actual; + + using (HashAlgorithm hash = Create()) + { + actual = hash.ComputeHash(input, 0, input.Length); + } + + Assert.Equal(expected, actual); + } + + protected void VerifyRepeating(string input, int repeatCount, string output) + { + using (Stream stream = new DataRepeatingStream(input, repeatCount)) + { + Verify(stream, output); + } + } + + protected class DataRepeatingStream : Stream + { + private int _remaining; + private byte[] _data; + + public DataRepeatingStream(string data, int repeatCount) + { + _remaining = repeatCount; + _data = ByteUtils.AsciiBytes(data); + } + + public override int Read(byte[] buffer, int offset, int count) + { + if (!CanRead) + { + throw new NotSupportedException(); + } + + if (count < _data.Length) + { + throw new InvalidOperationException(); + } + + if (_remaining == 0) + { + return 0; + } + + int multiple = count / _data.Length; + + if (multiple > _remaining) + { + multiple = _remaining; + } + + int localOffset = offset; + + for (int i = 0; i < multiple; i++) + { + Buffer.BlockCopy(_data, 0, buffer, localOffset, _data.Length); + localOffset += _data.Length; + } + + _remaining -= multiple; + return _data.Length * multiple; + } + + protected override void Dispose(bool disposing) + { + if (disposing) + { + _data = null; + } + } + + public override void Flush() + { + } + + public override long Seek(long offset, SeekOrigin origin) + { + throw new NotSupportedException(); + } + + public override void SetLength(long value) + { + throw new NotSupportedException(); + } + + public override void Write(byte[] buffer, int offset, int count) + { + throw new NotSupportedException(); + } + + public override bool CanRead { get { return _data != null; } } + public override bool CanSeek { get { return false; } } + public override bool CanWrite { get { return false; } } + public override long Length { get { throw new NotSupportedException(); } } + + public override long Position + { + get { throw new NotSupportedException(); } + set { throw new NotSupportedException(); } + } + } + } +} diff --git a/src/System.Security.Cryptography.Hashing.Algorithms/tests/HmacSha1Tests.cs b/src/System.Security.Cryptography.Hashing.Algorithms/tests/HmacSha1Tests.cs new file mode 100644 index 000000000000..5134cc218dc6 --- /dev/null +++ b/src/System.Security.Cryptography.Hashing.Algorithms/tests/HmacSha1Tests.cs @@ -0,0 +1,57 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Xunit; + +namespace System.Security.Cryptography.Hashing.Algorithms.Tests +{ + public class HmacSha1Tests : Rfc2202HmacTests + { + protected override HMAC Create() + { + return new HMACSHA1(); + } + + [Fact] + public void HmacSha1_Rfc2202_1() + { + VerifyHmac(1, "b617318655057264e28bc0b6fb378c8ef146be00"); + } + + [Fact] + public void HmacSha1_Rfc2202_2() + { + VerifyHmac(2, "effcdf6ae5eb2fa2d27416d5f184df9c259a7c79"); + } + + [Fact] + public void HmacSha1_Rfc2202_3() + { + VerifyHmac(3, "125d7342b9ac11cd91a39af48aa17b4f63f175d3"); + } + + [Fact] + public void HmacSha1_Rfc2202_4() + { + VerifyHmac(4, "4c9007f4026250c6bc8414f9bf50c86c2d7235da"); + } + + [Fact] + public void HmacSha1_Rfc2202_5() + { + VerifyHmac(5, "4c1a03424b55e07fe7f27be1d58bb9324a9a5a04"); + } + + [Fact] + public void HmacSha1_Rfc2202_6() + { + VerifyHmac(6, "aa4ae5e15272d00e95705637ce8a3b55ed402112"); + } + + [Fact] + public void HmacSha1_Rfc2202_7() + { + VerifyHmac(7, "e8e99d0f45237d786d6bbaa7965c7808bbff1a91"); + } + } +} \ No newline at end of file diff --git a/src/System.Security.Cryptography.Hashing.Algorithms/tests/HmacSha256Tests.cs b/src/System.Security.Cryptography.Hashing.Algorithms/tests/HmacSha256Tests.cs new file mode 100644 index 000000000000..cdee25197a31 --- /dev/null +++ b/src/System.Security.Cryptography.Hashing.Algorithms/tests/HmacSha256Tests.cs @@ -0,0 +1,58 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Xunit; + +namespace System.Security.Cryptography.Hashing.Algorithms.Tests +{ + public class HmacSha256Tests : Rfc4231HmacTests + { + protected override HMAC Create() + { + return new HMACSHA256(); + } + + [Fact] + public void HmacSha256_Rfc4231_1() + { + VerifyHmac(1, "b0344c61d8db38535ca8afceaf0bf12b881dc200c9833da726e9376c2e32cff7"); + } + + [Fact] + public void HmacSha256_Rfc4231_2() + { + VerifyHmac(2, "5bdcc146bf60754e6a042426089575c75a003f089d2739839dec58b964ec3843"); + } + + [Fact] + public void HmacSha256_Rfc4231_3() + { + VerifyHmac(3, "773ea91e36800e46854db8ebd09181a72959098b3ef8c122d9635514ced565fe"); + } + + [Fact] + public void HmacSha256_Rfc4231_4() + { + VerifyHmac(4, "82558a389a443c0ea4cc819899f2083a85f0faa3e578f8077a2e3ff46729665b"); + } + + [Fact] + public void HmacSha256_Rfc4231_5() + { + // RFC 4231 only defines the first 128 bits of this value. + VerifyHmac(5, "a3b6167473100ee06e0c796c2955552b", 128 / 8); + } + + [Fact] + public void HmacSha256_Rfc4231_6() + { + VerifyHmac(6, "60e431591ee0b67f0d8a26aacbf5b77f8e0bc6213728c5140546040f0ee37f54"); + } + + [Fact] + public void HmacSha256_Rfc4231_7() + { + VerifyHmac(7, "9b09ffa71b942fcb27635fbcd5b0e944bfdc63644f0713938a7f51535c3a35e2"); + } + } +} \ No newline at end of file diff --git a/src/System.Security.Cryptography.Hashing.Algorithms/tests/HmacSha384Tests.cs b/src/System.Security.Cryptography.Hashing.Algorithms/tests/HmacSha384Tests.cs new file mode 100644 index 000000000000..296ddf697df4 --- /dev/null +++ b/src/System.Security.Cryptography.Hashing.Algorithms/tests/HmacSha384Tests.cs @@ -0,0 +1,58 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Xunit; + +namespace System.Security.Cryptography.Hashing.Algorithms.Tests +{ + public class HmacSha384Tests : Rfc4231HmacTests + { + protected override HMAC Create() + { + return new HMACSHA384(); + } + + [Fact] + public void HmacSha384_Rfc4231_1() + { + VerifyHmac(1, "afd03944d84895626b0825f4ab46907f15f9dadbe4101ec682aa034c7cebc59cfaea9ea9076ede7f4af152e8b2fa9cb6"); + } + + [Fact] + public void HmacSha384_Rfc4231_2() + { + VerifyHmac(2, "af45d2e376484031617f78d2b58a6b1b9c7ef464f5a01b47e42ec3736322445e8e2240ca5e69e2c78b3239ecfab21649"); + } + + [Fact] + public void HmacSha384_Rfc4231_3() + { + VerifyHmac(3, "88062608d3e6ad8a0aa2ace014c8a86f0aa635d947ac9febe83ef4e55966144b2a5ab39dc13814b94e3ab6e101a34f27"); + } + + [Fact] + public void HmacSha384_Rfc4231_4() + { + VerifyHmac(4, "3e8a69b7783c25851933ab6290af6ca77a9981480850009cc5577c6e1f573b4e6801dd23c4a7d679ccf8a386c674cffb"); + } + + [Fact] + public void HmacSha384_Rfc4231_5() + { + // RFC 4231 only defines the first 128 bits of this value. + VerifyHmac(5, "3abf34c3503b2a23a46efc619baef897", 128 / 8); + } + + [Fact] + public void HmacSha384_Rfc4231_6() + { + VerifyHmac(6, "4ece084485813e9088d2c63a041bc5b44f9ef1012a2b588f3cd11f05033ac4c60c2ef6ab4030fe8296248df163f44952"); + } + + [Fact] + public void HmacSha384_Rfc4231_7() + { + VerifyHmac(7, "6617178e941f020d351e2f254e8fd32c602420feb0b8fb9adccebb82461e99c5a678cc31e799176d3860e6110c46523e"); + } + } +} \ No newline at end of file diff --git a/src/System.Security.Cryptography.Hashing.Algorithms/tests/HmacSha512Tests.cs b/src/System.Security.Cryptography.Hashing.Algorithms/tests/HmacSha512Tests.cs new file mode 100644 index 000000000000..dface361b129 --- /dev/null +++ b/src/System.Security.Cryptography.Hashing.Algorithms/tests/HmacSha512Tests.cs @@ -0,0 +1,58 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Xunit; + +namespace System.Security.Cryptography.Hashing.Algorithms.Tests +{ + public class HmacSha512Tests : Rfc4231HmacTests + { + protected override HMAC Create() + { + return new HMACSHA512(); + } + + [Fact] + public void HmacSha512_Rfc4231_1() + { + VerifyHmac(1, "87aa7cdea5ef619d4ff0b4241a1d6cb02379f4e2ce4ec2787ad0b30545e17cdedaa833b7d6b8a702038b274eaea3f4e4be9d914eeb61f1702e696c203a126854"); + } + + [Fact] + public void HmacSha512_Rfc4231_2() + { + VerifyHmac(2, "164b7a7bfcf819e2e395fbe73b56e0a387bd64222e831fd610270cd7ea2505549758bf75c05a994a6d034f65f8f0e6fdcaeab1a34d4a6b4b636e070a38bce737"); + } + + [Fact] + public void HmacSha512_Rfc4231_3() + { + VerifyHmac(3, "fa73b0089d56a284efb0f0756c890be9b1b5dbdd8ee81a3655f83e33b2279d39bf3e848279a722c806b485a47e67c807b946a337bee8942674278859e13292fb"); + } + + [Fact] + public void HmacSha512_Rfc4231_4() + { + VerifyHmac(4, "b0ba465637458c6990e5a8c5f61d4af7e576d97ff94b872de76f8050361ee3dba91ca5c11aa25eb4d679275cc5788063a5f19741120c4f2de2adebeb10a298dd"); + } + + [Fact] + public void HmacSha512_Rfc4231_5() + { + // RFC 4231 only defines the first 128 bits of this value. + VerifyHmac(5, "415fad6271580a531d4179bc891d87a6", 128 / 8); + } + + [Fact] + public void HmacSha512_Rfc4231_6() + { + VerifyHmac(6, "80b24263c7c1a3ebb71493c1dd7be8b49b46d1f41b4aeec1121b013783f8f3526b56d037e05f2598bd0fd2215d6a1e5295e64f73f63f0aec8b915a985d786598"); + } + + [Fact] + public void HmacSha512_Rfc4231_7() + { + VerifyHmac(7, "e37b6a775dc87dbaa4dfa9f96e5e3ffddebd71f8867289865df5a32d20cdc944b6022cac3c4982b10d5eeb55c3e4de15134676fb6de0446065c97440fa8c6a58"); + } + } +} \ No newline at end of file diff --git a/src/System.Security.Cryptography.Hashing.Algorithms/tests/HmacTests.cs b/src/System.Security.Cryptography.Hashing.Algorithms/tests/HmacTests.cs new file mode 100644 index 000000000000..de2e25da4e8e --- /dev/null +++ b/src/System.Security.Cryptography.Hashing.Algorithms/tests/HmacTests.cs @@ -0,0 +1,48 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Xunit; + +namespace System.Security.Cryptography.Hashing.Algorithms.Tests +{ + public abstract class HmacTests + { + // RFC2202 defines the test vectors for HMACMD5 and HMACSHA1 + // RFC4231 defines the test vectors for HMACSHA{224,256,384,512} + // They share the same datasets for cases 1-5, but cases 6 and 7 differ. + private readonly byte[][] _testKeys; + private readonly byte[][] _testData; + + protected HmacTests(byte[][] testKeys, byte[][] testData) + { + _testKeys = testKeys; + _testData = testData; + } + + protected abstract HMAC Create(); + + protected void VerifyHmac( + int testCaseId, + string digest, + int truncateSize = -1) + { + byte[] digestBytes = ByteUtils.HexToByteArray(digest); + byte[] computedDigest; + + using (HMAC hmac = Create()) + { + hmac.Key = _testKeys[testCaseId]; + computedDigest = hmac.ComputeHash(_testData[testCaseId]); + } + + if (truncateSize != -1) + { + byte[] tmp = new byte[truncateSize]; + Array.Copy(computedDigest, 0, tmp, 0, truncateSize); + computedDigest = tmp; + } + + Assert.Equal(digestBytes, computedDigest); + } + } +} \ No newline at end of file diff --git a/src/System.Security.Cryptography.Hashing.Algorithms/tests/MD5Tests.cs b/src/System.Security.Cryptography.Hashing.Algorithms/tests/MD5Tests.cs new file mode 100644 index 000000000000..a164ebdc3cf8 --- /dev/null +++ b/src/System.Security.Cryptography.Hashing.Algorithms/tests/MD5Tests.cs @@ -0,0 +1,65 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Xunit; + +namespace System.Security.Cryptography.Hashing.Algorithms.Tests +{ + public class MD5Tests : HashAlgorithmTest + { + protected override HashAlgorithm Create() + { + return MD5.Create(); + } + + // Test cases are defined in RFC 1321, section A.5 + + [Fact] + public void MD5_Rfc1321_1() + { + Verify("", "d41d8cd98f00b204e9800998ecf8427e"); + } + + [Fact] + public void MD5_Rfc1321_2() + { + Verify("a", "0cc175b9c0f1b6a831c399e269772661"); + } + + [Fact] + public void MD5_Rfc1321_3() + { + Verify("abc", "900150983cd24fb0d6963f7d28e17f72"); + } + + [Fact] + public void MD5_Rfc1321_4() + { + Verify("message digest", "f96b697d7cb7938d525a2f31aaf161d0"); + } + + [Fact] + public void MD5_Rfc1321_5() + { + Verify("abcdefghijklmnopqrstuvwxyz", "c3fcd3d76192e4007dfb496cca67e13b"); + } + + [Fact] + public void MD5_Rfc1321_6() + { + Verify("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789", "d174ab98d277d9f5a5611c2c9f419d9f"); + } + + [Fact] + public void MD5_Rfc1321_7() + { + Verify("12345678901234567890123456789012345678901234567890123456789012345678901234567890", "57edf4a22be3c955ac49da2e2107b67a"); + } + + [Fact] + public void MD5_Rfc1321_7_AsStream() + { + VerifyRepeating("1234567890", 8, "57edf4a22be3c955ac49da2e2107b67a"); + } + } +} diff --git a/src/System.Security.Cryptography.Hashing.Algorithms/tests/Rfc2202HmacTests.cs b/src/System.Security.Cryptography.Hashing.Algorithms/tests/Rfc2202HmacTests.cs new file mode 100644 index 000000000000..af91f640441e --- /dev/null +++ b/src/System.Security.Cryptography.Hashing.Algorithms/tests/Rfc2202HmacTests.cs @@ -0,0 +1,37 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace System.Security.Cryptography.Hashing.Algorithms.Tests +{ + public abstract class Rfc2202HmacTests : HmacTests + { + private static readonly byte[][] s_testKeys2202 = + { + null, + ByteUtils.RepeatByte(0x0b, 20), + ByteUtils.AsciiBytes("Jefe"), + ByteUtils.RepeatByte(0xaa, 20), + ByteUtils.HexToByteArray("0102030405060708090a0b0c0d0e0f10111213141516171819"), + ByteUtils.HexToByteArray("0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c"), + ByteUtils.RepeatByte(0xaa, 80), + ByteUtils.RepeatByte(0xaa, 80), + }; + + private static readonly byte[][] s_testData2202 = + { + null, + ByteUtils.AsciiBytes("Hi There"), + ByteUtils.AsciiBytes("what do ya want for nothing?"), + ByteUtils.RepeatByte(0xdd, 50), + ByteUtils.RepeatByte(0xcd, 50), + ByteUtils.AsciiBytes("Test With Truncation"), + ByteUtils.AsciiBytes("Test Using Larger Than Block-Size Key - Hash Key First"), + ByteUtils.AsciiBytes("Test Using Larger Than Block-Size Key and Larger Than One Block-Size Data"), + }; + + protected Rfc2202HmacTests() : + base(s_testKeys2202, s_testData2202) + { + } + } +} \ No newline at end of file diff --git a/src/System.Security.Cryptography.Hashing.Algorithms/tests/Rfc4231HmacTests.cs b/src/System.Security.Cryptography.Hashing.Algorithms/tests/Rfc4231HmacTests.cs new file mode 100644 index 000000000000..54971e1c00f2 --- /dev/null +++ b/src/System.Security.Cryptography.Hashing.Algorithms/tests/Rfc4231HmacTests.cs @@ -0,0 +1,37 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace System.Security.Cryptography.Hashing.Algorithms.Tests +{ + public abstract class Rfc4231HmacTests : HmacTests + { + private static readonly byte[][] s_testKeys4231 = + { + null, + ByteUtils.RepeatByte(0x0b, 20), + ByteUtils.AsciiBytes("Jefe"), + ByteUtils.RepeatByte(0xaa, 20), + ByteUtils.HexToByteArray("0102030405060708090a0b0c0d0e0f10111213141516171819"), + ByteUtils.HexToByteArray("0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c"), + ByteUtils.RepeatByte(0xaa, 131), + ByteUtils.RepeatByte(0xaa, 131), + }; + + private static readonly byte[][] s_testData4231 = + { + null, + ByteUtils.AsciiBytes("Hi There"), + ByteUtils.AsciiBytes("what do ya want for nothing?"), + ByteUtils.RepeatByte(0xdd, 50), + ByteUtils.RepeatByte(0xcd, 50), + ByteUtils.AsciiBytes("Test With Truncation"), + ByteUtils.AsciiBytes("Test Using Larger Than Block-Size Key - Hash Key First"), + ByteUtils.AsciiBytes("This is a test using a larger than block-size key and a larger than block-size data. The key needs to be hashed before being used by the HMAC algorithm."), + }; + + protected Rfc4231HmacTests() : + base(s_testKeys4231, s_testData4231) + { + } + } +} \ No newline at end of file diff --git a/src/System.Security.Cryptography.Hashing.Algorithms/tests/Sha1Tests.cs b/src/System.Security.Cryptography.Hashing.Algorithms/tests/Sha1Tests.cs new file mode 100644 index 000000000000..847f58cb0c7e --- /dev/null +++ b/src/System.Security.Cryptography.Hashing.Algorithms/tests/Sha1Tests.cs @@ -0,0 +1,41 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Xunit; + +namespace System.Security.Cryptography.Hashing.Algorithms.Tests +{ + public class Sha1Tests : HashAlgorithmTest + { + protected override HashAlgorithm Create() + { + return SHA1.Create(); + } + + // SHA1 tests are defined somewhat obliquely within RFC 3174, section 7.3 + // The same tests appear in in http://csrc.nist.gov/publications/fips/fips180-2/fips180-2.pdf Appendix A + [Fact] + public void Sha1_Rfc3174_1() + { + Verify("abc", "A9993E364706816ABA3E25717850C26C9CD0D89D"); + } + + [Fact] + public void Sha1_Rfc3174_2() + { + Verify("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", "84983E441C3BD26EBAAE4AA1F95129E5E54670F1"); + } + + [Fact] + public void Sha1_Rfc3174_3() + { + VerifyRepeating("a", 1000000, "34AA973CD4C4DAA4F61EEB2BDBAD27316534016F"); + } + + [Fact] + public void Sha1_Rfc3174_4() + { + VerifyRepeating("0123456701234567012345670123456701234567012345670123456701234567", 10, "DEA356A2CDDD90C7A7ECEDC5EBB563934F460452"); + } + } +} diff --git a/src/System.Security.Cryptography.Hashing.Algorithms/tests/Sha256Tests.cs b/src/System.Security.Cryptography.Hashing.Algorithms/tests/Sha256Tests.cs new file mode 100644 index 000000000000..bd0c3035a6e6 --- /dev/null +++ b/src/System.Security.Cryptography.Hashing.Algorithms/tests/Sha256Tests.cs @@ -0,0 +1,41 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Xunit; + +namespace System.Security.Cryptography.Hashing.Algorithms.Tests +{ + public class Sha256Tests : HashAlgorithmTest + { + protected override HashAlgorithm Create() + { + return SHA256.Create(); + } + + // These test cases are from http://csrc.nist.gov/publications/fips/fips180-2/fips180-2.pdf Appendix B + [Fact] + public void Sha256_Fips180_1() + { + Verify( + "abc", + "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad"); + } + + [Fact] + public void Sha256_Fips180_2() + { + Verify( + "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", + "248d6a61d20638b8e5c026930c3e6039a33ce45964ff2167f6ecedd419db06c1"); + } + + [Fact] + public void Sha256_Fips180_3() + { + VerifyRepeating( + "a", + 1000000, + "cdc76e5c9914fb9281a1c7e284d73e67f1809a48a497200e046d39ccc7112cd0"); + } + } +} diff --git a/src/System.Security.Cryptography.Hashing.Algorithms/tests/Sha384Tests.cs b/src/System.Security.Cryptography.Hashing.Algorithms/tests/Sha384Tests.cs new file mode 100644 index 000000000000..d57f042a2339 --- /dev/null +++ b/src/System.Security.Cryptography.Hashing.Algorithms/tests/Sha384Tests.cs @@ -0,0 +1,32 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Xunit; + +namespace System.Security.Cryptography.Hashing.Algorithms.Tests +{ + public class Sha384Tests : HashAlgorithmTest + { + protected override HashAlgorithm Create() + { + return SHA384.Create(); + } + + // These test cases are from http://csrc.nist.gov/groups/ST/toolkit/documents/Examples/SHA_All.pdf + [Fact] + public void Sha384_NistShaAll_1() + { + Verify( + "abc", + "CB00753F45A35E8BB5A03D699AC65007272C32AB0EDED1631A8B605A43FF5BED8086072BA1E7CC2358BAECA134C825A7"); + } + + [Fact] + public void Sha384_NistShaAll_2() + { + Verify( + "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu", + "09330C33F71147E83D192FC782CD1B4753111B173B3B05D22FA08086E3B0F712FCC7C71A557E2DB966C3E9FA91746039"); + } + } +} diff --git a/src/System.Security.Cryptography.Hashing.Algorithms/tests/Sha512Tests.cs b/src/System.Security.Cryptography.Hashing.Algorithms/tests/Sha512Tests.cs new file mode 100644 index 000000000000..2140cb3d7dc3 --- /dev/null +++ b/src/System.Security.Cryptography.Hashing.Algorithms/tests/Sha512Tests.cs @@ -0,0 +1,41 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Xunit; + +namespace System.Security.Cryptography.Hashing.Algorithms.Tests +{ + public class Sha512Tests : HashAlgorithmTest + { + protected override HashAlgorithm Create() + { + return SHA512.Create(); + } + + // These test cases are from http://csrc.nist.gov/publications/fips/fips180-2/fips180-2.pdf Appendix C + [Fact] + public void Sha512_Fips180_1() + { + Verify( + "abc", + "ddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a2192992a274fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f"); + } + + [Fact] + public void Sha512_Fips180_2() + { + Verify( + "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu", + "8e959b75dae313da8cf4f72814fc143f8f7779c6eb9f7fa17299aeadb6889018501d289e4900f7e4331b99dec4b5433ac7d329eeb6dd26545e96e55b874be909"); + } + + [Fact] + public void Sha512_Fips180_3() + { + VerifyRepeating( + "a", + 1000000, + "e718483d0ce769644e2e42c7bc15b4638e1f98b13b2044285632a803afa973ebde0ff244877ea60a4cb0432ce577c31beb009c5c2c49aa2e4eadb217ad8cc09b"); + } + } +} diff --git a/src/System.Security.Cryptography.Hashing.Algorithms/tests/System.Security.Cryptography.Hashing.Algorithms.Tests.csproj b/src/System.Security.Cryptography.Hashing.Algorithms/tests/System.Security.Cryptography.Hashing.Algorithms.Tests.csproj new file mode 100644 index 000000000000..14de7e9bfc58 --- /dev/null +++ b/src/System.Security.Cryptography.Hashing.Algorithms/tests/System.Security.Cryptography.Hashing.Algorithms.Tests.csproj @@ -0,0 +1,42 @@ + + + + + Debug + AnyCPU + {F94007FF-0FBC-4817-B5F6-C8C8F1550AC6} + Library + System.Security.Cryptography.Hashing.Algorithms.Tests + System.Security.Cryptography.Hashing.Algorithms.Tests + + + + + + + + {ACECDE06-FFFD-4E14-B651-11C189CBF4B8} + System.Security.Cryptography.Hashing.Algorithms + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/System.Security.Cryptography.Hashing.Algorithms/tests/project.json b/src/System.Security.Cryptography.Hashing.Algorithms/tests/project.json new file mode 100644 index 000000000000..e2a6cd1e972d --- /dev/null +++ b/src/System.Security.Cryptography.Hashing.Algorithms/tests/project.json @@ -0,0 +1,18 @@ +{ + "dependencies": { + "System.Console": "4.0.0-beta-22809", + "System.IO": "4.0.10-beta-22809", + "System.Runtime": "4.0.20-beta-22809", + "System.Runtime.Extensions": "4.0.10-beta-22809", + "System.Security.Cryptography.Hashing.Algorithms": "4.0.0-beta-22809", + "xunit": "2.0.0-beta5-build2785", + "xunit.abstractions.netcore": "1.0.0-prerelease", + "xunit.assert": "2.0.0-beta5-build2785", + "xunit.core.netcore": "1.0.1-prerelease", + "xunit.runner.visualstudio": "0.99.9-build1021", + "xunit.netcore.extensions": "1.0.0-prerelease-*" + }, + "frameworks": { + "dnxcore50": {} + } +}