726 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			726 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|  * 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();
 | |
| }
 | 
