summaryrefslogtreecommitdiffstats
path: root/third_party/heimdal/lib/hcrypto/evp-wincng.c
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/heimdal/lib/hcrypto/evp-wincng.c')
-rw-r--r--third_party/heimdal/lib/hcrypto/evp-wincng.c725
1 files changed, 725 insertions, 0 deletions
diff --git a/third_party/heimdal/lib/hcrypto/evp-wincng.c b/third_party/heimdal/lib/hcrypto/evp-wincng.c
new file mode 100644
index 0000000..34ac3b3
--- /dev/null
+++ b/third_party/heimdal/lib/hcrypto/evp-wincng.c
@@ -0,0 +1,725 @@
+/*
+ * Copyright (c) 2015, Secure Endpoints Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/* Windows CNG provider */
+
+#include <config.h>
+#include <roken.h>
+#include <assert.h>
+#include <versionsupport.h>
+
+#include <evp.h>
+#include <evp-wincng.h>
+
+#include <bcrypt.h>
+
+#ifndef BCRYPT_HASH_REUSABLE_FLAG
+#define BCRYPT_HASH_REUSABLE_FLAG 0x00000020
+#endif
+
+/*
+ * CNG cipher provider
+ */
+
+struct wincng_key {
+ BCRYPT_KEY_HANDLE hKey;
+ UCHAR rgbKeyObject[1];
+};
+
+#define WINCNG_KEY_OBJECT_SIZE(ctx) \
+ ((ctx)->cipher->ctx_size - sizeof(struct wincng_key) + 1)
+
+static int
+wincng_do_cipher(EVP_CIPHER_CTX *ctx,
+ unsigned char *out,
+ const unsigned char *in,
+ unsigned int size)
+{
+ struct wincng_key *cng = ctx->cipher_data;
+ NTSTATUS status;
+ ULONG cbResult;
+
+ assert(EVP_CIPHER_CTX_mode(ctx) == EVP_CIPH_STREAM_CIPHER ||
+ (size % ctx->cipher->block_size) == 0);
+
+ if (ctx->encrypt) {
+ status = BCryptEncrypt(cng->hKey,
+ (PUCHAR)in,
+ size,
+ NULL, /* pPaddingInfo */
+ ctx->cipher->iv_len ? ctx->iv : NULL,
+ ctx->cipher->iv_len,
+ out,
+ size,
+ &cbResult,
+ 0);
+ } else {
+ status = BCryptDecrypt(cng->hKey,
+ (PUCHAR)in,
+ size,
+ NULL, /* pPaddingInfo */
+ ctx->cipher->iv_len ? ctx->iv : NULL,
+ ctx->cipher->iv_len,
+ out,
+ size,
+ &cbResult,
+ 0);
+ }
+
+ return BCRYPT_SUCCESS(status) && cbResult == size;
+}
+
+static int
+wincng_cleanup(EVP_CIPHER_CTX *ctx)
+{
+ struct wincng_key *cng = ctx->cipher_data;
+
+ if (cng->hKey) {
+ BCryptDestroyKey(cng->hKey);
+ cng->hKey = (BCRYPT_KEY_HANDLE)0;
+ }
+ SecureZeroMemory(cng->rgbKeyObject, WINCNG_KEY_OBJECT_SIZE(ctx));
+
+ return 1;
+}
+
+static int
+wincng_cipher_algorithm_init(EVP_CIPHER *cipher,
+ LPWSTR pszAlgId)
+{
+ BCRYPT_ALG_HANDLE hAlgorithm = NULL;
+ NTSTATUS status;
+ LPCWSTR pszChainingMode;
+ ULONG cbKeyObject, cbChainingMode, cbData;
+
+ if (cipher->app_data)
+ return 1;
+
+ status = BCryptOpenAlgorithmProvider(&hAlgorithm,
+ pszAlgId,
+ NULL,
+ 0);
+ if (!BCRYPT_SUCCESS(status))
+ return 0;
+
+ status = BCryptGetProperty(hAlgorithm,
+ BCRYPT_OBJECT_LENGTH,
+ (PUCHAR)&cbKeyObject,
+ sizeof(ULONG),
+ &cbData,
+ 0);
+ if (!BCRYPT_SUCCESS(status)) {
+ BCryptCloseAlgorithmProvider(hAlgorithm, 0);
+ return 0;
+ }
+
+ cipher->ctx_size = sizeof(struct wincng_key) + cbKeyObject - 1;
+
+ switch (cipher->flags & EVP_CIPH_MODE) {
+ case EVP_CIPH_CBC_MODE:
+ pszChainingMode = BCRYPT_CHAIN_MODE_CBC;
+ cbChainingMode = sizeof(BCRYPT_CHAIN_MODE_CBC);
+ break;
+ case EVP_CIPH_CFB8_MODE:
+ pszChainingMode = BCRYPT_CHAIN_MODE_CFB;
+ cbChainingMode = sizeof(BCRYPT_CHAIN_MODE_CFB);
+ break;
+ default:
+ pszChainingMode = NULL;
+ cbChainingMode = 0;
+ break;
+ }
+
+ if (cbChainingMode) {
+ status = BCryptSetProperty(hAlgorithm,
+ BCRYPT_CHAINING_MODE,
+ (PUCHAR)pszChainingMode,
+ cbChainingMode,
+ 0);
+ if (!BCRYPT_SUCCESS(status)) {
+ BCryptCloseAlgorithmProvider(hAlgorithm, 0);
+ return 0;
+ }
+ }
+
+ if (wcscmp(pszAlgId, BCRYPT_RC2_ALGORITHM) == 0) {
+ ULONG cbEffectiveKeyLength = EVP_CIPHER_key_length(cipher) * 8;
+
+ status = BCryptSetProperty(hAlgorithm,
+ BCRYPT_EFFECTIVE_KEY_LENGTH,
+ (PUCHAR)&cbEffectiveKeyLength,
+ sizeof(cbEffectiveKeyLength),
+ 0);
+ if (!BCRYPT_SUCCESS(status)) {
+ BCryptCloseAlgorithmProvider(hAlgorithm, 0);
+ return 0;
+ }
+ }
+
+ InterlockedCompareExchangePointerRelease(&cipher->app_data,
+ hAlgorithm, NULL);
+ return 1;
+}
+
+static int
+wincng_key_init(EVP_CIPHER_CTX *ctx,
+ const unsigned char *key,
+ const unsigned char *iv,
+ int encp)
+{
+ struct wincng_key *cng = ctx->cipher_data;
+ NTSTATUS status;
+
+ assert(cng != NULL);
+ assert(ctx->cipher != NULL);
+
+ if (ctx->cipher->app_data == NULL)
+ return 0;
+
+ wincng_cleanup(ctx);
+
+ /*
+ * Note: ctx->key_len not EVP_CIPHER_CTX_key_length() for
+ * variable length key support.
+ */
+ status = BCryptGenerateSymmetricKey(ctx->cipher->app_data,
+ &cng->hKey,
+ cng->rgbKeyObject,
+ WINCNG_KEY_OBJECT_SIZE(ctx),
+ (PUCHAR)key,
+ ctx->key_len,
+ 0);
+
+ return BCRYPT_SUCCESS(status);
+}
+
+#define WINCNG_CIPHER_ALGORITHM(name, alg_id, block_size, key_len, \
+ iv_len, flags) \
+ \
+ static EVP_CIPHER \
+ wincng_##name = { \
+ 0, \
+ block_size, \
+ key_len, \
+ iv_len, \
+ flags, \
+ wincng_key_init, \
+ wincng_do_cipher, \
+ wincng_cleanup, \
+ 0, \
+ NULL, \
+ NULL, \
+ NULL, \
+ NULL \
+ }; \
+ \
+ const EVP_CIPHER * \
+ hc_EVP_wincng_##name(void) \
+ { \
+ wincng_cipher_algorithm_init(&wincng_##name, alg_id); \
+ return wincng_##name.app_data ? &wincng_##name : NULL; \
+ }
+
+#define WINCNG_CIPHER_ALGORITHM_CLEANUP(name) do { \
+ if (wincng_##name.app_data) { \
+ BCryptCloseAlgorithmProvider(wincng_##name.app_data, 0); \
+ wincng_##name.app_data = NULL; \
+ } \
+ } while (0)
+
+#define WINCNG_CIPHER_ALGORITHM_UNAVAILABLE(name) \
+ \
+ const EVP_CIPHER * \
+ hc_EVP_wincng_##name(void) \
+ { \
+ return NULL; \
+ }
+
+/**
+ * The triple DES cipher type (Windows CNG provider)
+ *
+ * @return the DES-EDE3-CBC EVP_CIPHER pointer.
+ *
+ * @ingroup hcrypto_evp
+ */
+
+WINCNG_CIPHER_ALGORITHM(des_ede3_cbc,
+ BCRYPT_3DES_ALGORITHM,
+ 8,
+ 24,
+ 8,
+ EVP_CIPH_CBC_MODE);
+
+/**
+ * The DES cipher type (Windows CNG provider)
+ *
+ * @return the DES-CBC EVP_CIPHER pointer.
+ *
+ * @ingroup hcrypto_evp
+ */
+
+WINCNG_CIPHER_ALGORITHM(des_cbc,
+ BCRYPT_DES_ALGORITHM,
+ 8,
+ 8,
+ 8,
+ EVP_CIPH_CBC_MODE);
+
+/**
+ * The AES-128 cipher type (Windows CNG provider)
+ *
+ * @return the AES-128-CBC EVP_CIPHER pointer.
+ *
+ * @ingroup hcrypto_evp
+ */
+
+WINCNG_CIPHER_ALGORITHM(aes_128_cbc,
+ BCRYPT_AES_ALGORITHM,
+ 16,
+ 16,
+ 16,
+ EVP_CIPH_CBC_MODE);
+
+/**
+ * The AES-192 cipher type (Windows CNG provider)
+ *
+ * @return the AES-192-CBC EVP_CIPHER pointer.
+ *
+ * @ingroup hcrypto_evp
+ */
+
+WINCNG_CIPHER_ALGORITHM(aes_192_cbc,
+ BCRYPT_AES_ALGORITHM,
+ 16,
+ 24,
+ 16,
+ EVP_CIPH_CBC_MODE);
+
+/**
+ * The AES-256 cipher type (Windows CNG provider)
+ *
+ * @return the AES-256-CBC EVP_CIPHER pointer.
+ *
+ * @ingroup hcrypto_evp
+ */
+
+WINCNG_CIPHER_ALGORITHM(aes_256_cbc,
+ BCRYPT_AES_ALGORITHM,
+ 16,
+ 32,
+ 16,
+ EVP_CIPH_CBC_MODE);
+
+/**
+ * The AES-128 CFB8 cipher type (Windows CNG provider)
+ *
+ * @return the AES-128-CFB8 EVP_CIPHER pointer.
+ *
+ * @ingroup hcrypto_evp
+ */
+
+WINCNG_CIPHER_ALGORITHM(aes_128_cfb8,
+ BCRYPT_AES_ALGORITHM,
+ 16,
+ 16,
+ 16,
+ EVP_CIPH_CFB8_MODE);
+
+/**
+ * The AES-192 CFB8 cipher type (Windows CNG provider)
+ *
+ * @return the AES-192-CFB8 EVP_CIPHER pointer.
+ *
+ * @ingroup hcrypto_evp
+ */
+
+WINCNG_CIPHER_ALGORITHM(aes_192_cfb8,
+ BCRYPT_AES_ALGORITHM,
+ 16,
+ 24,
+ 16,
+ EVP_CIPH_CFB8_MODE);
+
+/**
+ * The AES-256 CFB8 cipher type (Windows CNG provider)
+ *
+ * @return the AES-256-CFB8 EVP_CIPHER pointer.
+ *
+ * @ingroup hcrypto_evp
+ */
+
+WINCNG_CIPHER_ALGORITHM(aes_256_cfb8,
+ BCRYPT_AES_ALGORITHM,
+ 16,
+ 32,
+ 16,
+ EVP_CIPH_CFB8_MODE);
+
+/**
+ * The RC2 cipher type - Windows CNG
+ *
+ * @return the RC2 EVP_CIPHER pointer.
+ *
+ * @ingroup hcrypto_evp
+ */
+
+WINCNG_CIPHER_ALGORITHM(rc2_cbc,
+ BCRYPT_RC2_ALGORITHM,
+ 8,
+ 16,
+ 8,
+ EVP_CIPH_CBC_MODE);
+
+/**
+ * The RC2-40 cipher type - Windows CNG
+ *
+ * @return the RC2-40 EVP_CIPHER pointer.
+ *
+ * @ingroup hcrypto_evp
+ */
+
+WINCNG_CIPHER_ALGORITHM(rc2_40_cbc,
+ BCRYPT_RC2_ALGORITHM,
+ 8,
+ 5,
+ 8,
+ EVP_CIPH_CBC_MODE);
+
+/**
+ * The RC2-64 cipher type - Windows CNG
+ *
+ * @return the RC2-64 EVP_CIPHER pointer.
+ *
+ * @ingroup hcrypto_evp
+ */
+
+WINCNG_CIPHER_ALGORITHM(rc2_64_cbc,
+ BCRYPT_RC2_ALGORITHM,
+ 8,
+ 8,
+ 8,
+ EVP_CIPH_CBC_MODE);
+
+/**
+ * The Camellia-128 cipher type - CommonCrypto
+ *
+ * @return the Camellia-128 EVP_CIPHER pointer.
+ *
+ * @ingroup hcrypto_evp
+ */
+
+WINCNG_CIPHER_ALGORITHM_UNAVAILABLE(camellia_128_cbc);
+
+/**
+ * The Camellia-198 cipher type - CommonCrypto
+ *
+ * @return the Camellia-198 EVP_CIPHER pointer.
+ *
+ * @ingroup hcrypto_evp
+ */
+
+WINCNG_CIPHER_ALGORITHM_UNAVAILABLE(camellia_192_cbc);
+
+/**
+ * The Camellia-256 cipher type - CommonCrypto
+ *
+ * @return the Camellia-256 EVP_CIPHER pointer.
+ *
+ * @ingroup hcrypto_evp
+ */
+
+WINCNG_CIPHER_ALGORITHM_UNAVAILABLE(camellia_256_cbc);
+
+/**
+ * The RC4 cipher type (Windows CNG provider)
+ *
+ * @return the RC4 EVP_CIPHER pointer.
+ *
+ * @ingroup hcrypto_evp
+ */
+
+WINCNG_CIPHER_ALGORITHM(rc4,
+ BCRYPT_RC4_ALGORITHM,
+ 1,
+ 16,
+ 0,
+ EVP_CIPH_STREAM_CIPHER | EVP_CIPH_VARIABLE_LENGTH);
+
+/**
+ * The RC4-40 cipher type (Windows CNG provider)
+ *
+ * @return the RC4 EVP_CIPHER pointer.
+ *
+ * @ingroup hcrypto_evp
+ */
+
+WINCNG_CIPHER_ALGORITHM(rc4_40,
+ BCRYPT_RC4_ALGORITHM,
+ 1,
+ 5,
+ 0,
+ EVP_CIPH_STREAM_CIPHER | EVP_CIPH_VARIABLE_LENGTH);
+
+static void
+wincng_cipher_algorithm_cleanup(void)
+{
+ WINCNG_CIPHER_ALGORITHM_CLEANUP(des_ede3_cbc);
+ WINCNG_CIPHER_ALGORITHM_CLEANUP(des_cbc);
+ WINCNG_CIPHER_ALGORITHM_CLEANUP(aes_128_cbc);
+ WINCNG_CIPHER_ALGORITHM_CLEANUP(aes_192_cbc);
+ WINCNG_CIPHER_ALGORITHM_CLEANUP(aes_256_cbc);
+ WINCNG_CIPHER_ALGORITHM_CLEANUP(aes_128_cfb8);
+ WINCNG_CIPHER_ALGORITHM_CLEANUP(aes_192_cfb8);
+ WINCNG_CIPHER_ALGORITHM_CLEANUP(aes_256_cfb8);
+ WINCNG_CIPHER_ALGORITHM_CLEANUP(rc2_cbc);
+ WINCNG_CIPHER_ALGORITHM_CLEANUP(rc2_40_cbc);
+ WINCNG_CIPHER_ALGORITHM_CLEANUP(rc2_64_cbc);
+ WINCNG_CIPHER_ALGORITHM_CLEANUP(rc4);
+ WINCNG_CIPHER_ALGORITHM_CLEANUP(rc4_40);
+}
+
+/*
+ * CNG digest provider
+ */
+
+struct wincng_md_ctx {
+ BCRYPT_HASH_HANDLE hHash;
+ ULONG cbHashObject;
+ UCHAR rgbHashObject[1];
+};
+
+static BCRYPT_ALG_HANDLE
+wincng_md_algorithm_init(EVP_MD *md,
+ LPCWSTR pszAlgId)
+{
+ BCRYPT_ALG_HANDLE hAlgorithm;
+ NTSTATUS status;
+ ULONG cbHashObject, cbData;
+ ULONG cbHash = 0, cbBlock = 0;
+
+ status = BCryptOpenAlgorithmProvider(&hAlgorithm,
+ pszAlgId,
+ NULL,
+ 0);
+ if (!BCRYPT_SUCCESS(status))
+ return NULL;
+
+ status = BCryptGetProperty(hAlgorithm,
+ BCRYPT_HASH_LENGTH,
+ (PUCHAR)&cbHash,
+ sizeof(ULONG),
+ &cbData,
+ 0);
+ if (!BCRYPT_SUCCESS(status)) {
+ BCryptCloseAlgorithmProvider(hAlgorithm, 0);
+ return NULL;
+ }
+
+ status = BCryptGetProperty(hAlgorithm,
+ BCRYPT_HASH_BLOCK_LENGTH,
+ (PUCHAR)&cbBlock,
+ sizeof(ULONG),
+ &cbData,
+ 0);
+ if (!BCRYPT_SUCCESS(status)) {
+ BCryptCloseAlgorithmProvider(hAlgorithm, 0);
+ return NULL;
+ }
+
+ status = BCryptGetProperty(hAlgorithm,
+ BCRYPT_OBJECT_LENGTH,
+ (PUCHAR)&cbHashObject,
+ sizeof(ULONG),
+ &cbData,
+ 0);
+ if (!BCRYPT_SUCCESS(status)) {
+ BCryptCloseAlgorithmProvider(hAlgorithm, 0);
+ return NULL;
+ }
+
+ md->hash_size = cbHash;
+ md->block_size = cbBlock;
+ md->ctx_size = sizeof(struct wincng_md_ctx) + cbHashObject - 1;
+
+ return hAlgorithm;
+}
+
+static int
+wincng_md_cleanup(EVP_MD_CTX *ctx);
+
+static int
+wincng_md_hash_init(BCRYPT_ALG_HANDLE hAlgorithm,
+ EVP_MD_CTX *ctx)
+{
+ struct wincng_md_ctx *cng = (struct wincng_md_ctx *)ctx;
+ NTSTATUS status;
+ ULONG cbData, dwFlags = 0;
+
+ if (IsWindows8OrGreaterCached()) {
+ if (cng->hHash)
+ return 1;
+ else
+ dwFlags |= BCRYPT_HASH_REUSABLE_FLAG;
+ } else
+ wincng_md_cleanup(ctx);
+
+ status = BCryptGetProperty(hAlgorithm,
+ BCRYPT_OBJECT_LENGTH,
+ (PUCHAR)&cng->cbHashObject,
+ sizeof(ULONG),
+ &cbData,
+ 0);
+ if (!BCRYPT_SUCCESS(status))
+ return 0;
+
+ status = BCryptCreateHash(hAlgorithm,
+ &cng->hHash,
+ cng->rgbHashObject,
+ cng->cbHashObject,
+ NULL,
+ 0,
+ dwFlags);
+
+ return BCRYPT_SUCCESS(status);
+}
+
+static int
+wincng_md_update(EVP_MD_CTX *ctx,
+ const void *data,
+ size_t length)
+{
+ struct wincng_md_ctx *cng = (struct wincng_md_ctx *)ctx;
+ NTSTATUS status;
+
+ status = BCryptHashData(cng->hHash, (PUCHAR)data, length, 0);
+
+ return BCRYPT_SUCCESS(status);
+}
+
+static int
+wincng_md_final(void *digest,
+ EVP_MD_CTX *ctx)
+{
+ struct wincng_md_ctx *cng = (struct wincng_md_ctx *)ctx;
+ NTSTATUS status;
+ ULONG cbHash, cbData;
+
+ status = BCryptGetProperty(cng->hHash,
+ BCRYPT_HASH_LENGTH,
+ (PUCHAR)&cbHash,
+ sizeof(DWORD),
+ &cbData,
+ 0);
+ if (!BCRYPT_SUCCESS(status))
+ return 0;
+
+ status = BCryptFinishHash(cng->hHash,
+ digest,
+ cbHash,
+ 0);
+
+ return BCRYPT_SUCCESS(status);
+}
+
+static int
+wincng_md_cleanup(EVP_MD_CTX *ctx)
+{
+ struct wincng_md_ctx *cng = (struct wincng_md_ctx *)ctx;
+
+ if (cng->hHash) {
+ BCryptDestroyHash(cng->hHash);
+ cng->hHash = (BCRYPT_HASH_HANDLE)0;
+ }
+ SecureZeroMemory(cng->rgbHashObject, cng->cbHashObject);
+
+ return 1;
+}
+
+#define WINCNG_MD_ALGORITHM(name, alg_id) \
+ \
+ static BCRYPT_ALG_HANDLE wincng_hAlgorithm_##name; \
+ \
+ static int wincng_##name##_init(EVP_MD_CTX *ctx) \
+ { \
+ return wincng_md_hash_init(wincng_hAlgorithm_##name, ctx); \
+ } \
+ \
+ const EVP_MD * \
+ hc_EVP_wincng_##name(void) \
+ { \
+ static struct hc_evp_md name = { \
+ 0, \
+ 0, \
+ 0, \
+ wincng_##name##_init, \
+ wincng_md_update, \
+ wincng_md_final, \
+ wincng_md_cleanup \
+ }; \
+ \
+ if (wincng_hAlgorithm_##name == NULL) { \
+ BCRYPT_ALG_HANDLE hAlgorithm = \
+ wincng_md_algorithm_init(&name, alg_id); \
+ InterlockedCompareExchangePointerRelease( \
+ &wincng_hAlgorithm_##name, hAlgorithm, NULL); \
+ } \
+ return wincng_hAlgorithm_##name ? &name : NULL; \
+ }
+
+#define WINCNG_MD_ALGORITHM_CLEANUP(name) do { \
+ if (wincng_hAlgorithm_##name) { \
+ BCryptCloseAlgorithmProvider(wincng_hAlgorithm_##name, 0); \
+ wincng_hAlgorithm_##name = NULL; \
+ } \
+ } while (0)
+
+WINCNG_MD_ALGORITHM(md4, BCRYPT_MD4_ALGORITHM);
+WINCNG_MD_ALGORITHM(md5, BCRYPT_MD5_ALGORITHM);
+WINCNG_MD_ALGORITHM(sha1, BCRYPT_SHA1_ALGORITHM);
+WINCNG_MD_ALGORITHM(sha256, BCRYPT_SHA256_ALGORITHM);
+WINCNG_MD_ALGORITHM(sha384, BCRYPT_SHA384_ALGORITHM);
+WINCNG_MD_ALGORITHM(sha512, BCRYPT_SHA512_ALGORITHM);
+
+static void
+wincng_md_algorithm_cleanup(void)
+{
+ WINCNG_MD_ALGORITHM_CLEANUP(md4);
+ WINCNG_MD_ALGORITHM_CLEANUP(md5);
+ WINCNG_MD_ALGORITHM_CLEANUP(sha1);
+ WINCNG_MD_ALGORITHM_CLEANUP(sha256);
+ WINCNG_MD_ALGORITHM_CLEANUP(sha384);
+ WINCNG_MD_ALGORITHM_CLEANUP(sha512);
+}
+
+void _hc_wincng_cleanup(void)
+{
+ wincng_md_algorithm_cleanup();
+ wincng_cipher_algorithm_cleanup();
+}