mirror of
				https://github.com/PowerShell/Win32-OpenSSH.git
				synced 2025-10-31 19:53:55 +01:00 
			
		
		
		
	KeyObject for cipher symmetric key was being allocated improperly due to an error in getting the key object size. Also added code to free keyobject in the event of a key creation failure.
		
			
				
	
	
		
			333 lines
		
	
	
		
			7.1 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			333 lines
		
	
	
		
			7.1 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|  * Author: Microsoft Corp.
 | |
|  *
 | |
|  * Copyright (c) 2015 Microsoft Corp.
 | |
|  * All rights reserved
 | |
|  *
 | |
|  * Microsoft openssh win32 port
 | |
|  *
 | |
|  * Redistribution and use in source and binary forms, with or without
 | |
|  * modification, are permitted provided that the following conditions
 | |
|  * are met:
 | |
|  *
 | |
|  * 1. Redistributions of source code must retain the above copyright
 | |
|  * notice, this list of conditions and the following disclaimer.
 | |
|  * 2. 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 AUTHOR ``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 AUTHOR 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.
 | |
|  */
 | |
| /* cng_cipher.c
 | |
|  *
 | |
|  * Openssh ciphers implemented using Microsoft Crypto Next Generation (CNG).
 | |
|  *
 | |
|  */
 | |
| 
 | |
| #include <Windows.h>
 | |
| #include <bcrypt.h>
 | |
| 
 | |
| #include "cng_cipher.h"
 | |
| 
 | |
| #ifdef USE_MSCNG
 | |
| 
 | |
| 
 | |
| #define AES_BLOCK_SIZE  16
 | |
| 
 | |
| 
 | |
| /*
 | |
| * increment the aes counter (iv)
 | |
| */
 | |
| static void aesctr_inc(unsigned char *ctr, unsigned int len)
 | |
| {
 | |
| 	size_t i;
 | |
| 
 | |
| #ifndef CONSTANT_TIME_INCREMENT
 | |
| 	for (i = len - 1; i >= 0; i--)
 | |
| 		if (++ctr[i])	/* continue on overflow */
 | |
| 			return;
 | |
| #else
 | |
| 	u8 x, add = 1;
 | |
| 
 | |
| 	for (i = len - 1; i >= 0; i--) {
 | |
| 		ctr[i] += add;
 | |
| 		/* constant time for: x = ctr[i] ? 1 : 0 */
 | |
| 		x = ctr[i];
 | |
| 		x = (x | (x >> 4)) & 0xf;
 | |
| 		x = (x | (x >> 2)) & 0x3;
 | |
| 		x = (x | (x >> 1)) & 0x1;
 | |
| 		add *= (x ^ 1);
 | |
| 	}
 | |
| #endif
 | |
| }
 | |
| 
 | |
| 
 | |
| /*
 | |
| * Routine to encrypt a counter for ctr encryption.  This requries
 | |
| * us to use an IV that is reset for each call to avoid cng attempting 
 | |
| * to chain encryptions.  
 | |
| */
 | |
| DWORD cng_counter_encrypt(const unsigned char *in, unsigned char *out, BCRYPT_KEY_HANDLE key, unsigned int blocklen)
 | |
| {
 | |
| 	HRESULT status = S_OK;
 | |
| 	DWORD cbResult = 0;
 | |
| 
 | |
| 	unsigned char iv[AES_BLOCK_SIZE] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
 | |
| 
 | |
| 	status = BCryptEncrypt(
 | |
| 		key,
 | |
| 		(PUCHAR)in,
 | |
| 		blocklen,
 | |
| 		NULL,
 | |
| 		iv,
 | |
| 		blocklen,
 | |
| 		out,
 | |
| 		blocklen,
 | |
| 		&cbResult,
 | |
| 		0);
 | |
| 
 | |
| 	return cbResult;
 | |
| }
 | |
| 
 | |
| /*
 | |
| *	Encrypt/Decrypt data using a CTR mode.  
 | |
| *   In this mode, we can't call CNG encryption/decription directly.  The mode requires
 | |
| *   the use of the iv as a counter that is incremented and encrypted.  The
 | |
| *   encrypted counter is then XORd with the data to produce the cipher text.
 | |
| */
 | |
| int cng_aesctr_encrypt_bytes(PSSH_CNG_CIPHER_CTX x, const unsigned char *m, unsigned char *c, unsigned int bytes)
 | |
| {
 | |
| 	int			 ret = 0;
 | |
| 	unsigned int n = 0;
 | |
| 	unsigned char buf[AES_BLOCK_SIZE];
 | |
| 
 | |
| 	while ((bytes--) > 0) {
 | |
| 		if (n == 0) {
 | |
| 			if (!cng_counter_encrypt(x->pbIV, buf, x->hKey, AES_BLOCK_SIZE))
 | |
| 			{
 | |
| 				ret = -1;
 | |
| 				break;
 | |
| 			}
 | |
| 			aesctr_inc(x->pbIV, AES_BLOCK_SIZE);
 | |
| 		}
 | |
| 		*(c++) = *(m++) ^ buf[n];
 | |
| 		n = (n + 1) % AES_BLOCK_SIZE;
 | |
| 	}
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| 
 | |
| /*
 | |
| *	Encrypt data using a provided cipher context
 | |
| */
 | |
| unsigned int cng_cipher_encrypt(PSSH_CNG_CIPHER_CTX x, unsigned char *dest, unsigned int dest_len, const unsigned char *src, unsigned int len)
 | |
| {
 | |
| 	DWORD cbResult = 0;
 | |
| 	HRESULT status = S_OK;
 | |
| 
 | |
| 	if (x->flags & _CNG_MODE_CTR)
 | |
| 	{
 | |
| 		if (-1 == cng_aesctr_encrypt_bytes(x, src, dest, len))
 | |
| 		{
 | |
| 			status = GetLastError();
 | |
| 		}
 | |
| 		cbResult = len;
 | |
| 	}
 | |
| 	else
 | |
| 	{
 | |
| 
 | |
| 		status = BCryptEncrypt(
 | |
| 			x->hKey,
 | |
| 			(PUCHAR)src,
 | |
| 			len,
 | |
| 			NULL,
 | |
| 			x->pbIV,
 | |
| 			x->cbBlockSize,
 | |
| 			dest,
 | |
| 			dest_len,
 | |
| 			&cbResult,
 | |
| 			0);
 | |
| 		if (S_OK != status)
 | |
| 		{
 | |
| 			cbResult = 0;
 | |
| 			SetLastError(status);
 | |
| 		}
 | |
| 	}
 | |
| 	return cbResult;
 | |
| }
 | |
| 
 | |
| /*
 | |
| *	Decrypt encrypted data using a provided cipher context
 | |
| */
 | |
| unsigned int cng_cipher_decrypt(PSSH_CNG_CIPHER_CTX x, unsigned char *dest, unsigned int dest_len, const unsigned char *src, unsigned int len)
 | |
| {
 | |
| 	DWORD cbResult = 0;
 | |
| 	HRESULT status = S_OK;
 | |
| 
 | |
| 	if (x->flags & _CNG_MODE_CTR)
 | |
| 	{
 | |
| 		// ctr mode is just an XOR so encrypt=decrypt
 | |
| 		if (-1 == cng_aesctr_encrypt_bytes(x, src, dest, len))
 | |
| 		{
 | |
| 			status = GetLastError();
 | |
| 		}
 | |
| 		cbResult = len;
 | |
| 	}
 | |
| 	else
 | |
| 	{
 | |
| 
 | |
| 		status = BCryptDecrypt(
 | |
| 			x->hKey,
 | |
| 			(PUCHAR)src,
 | |
| 			len,
 | |
| 			NULL,
 | |
| 			x->pbIV,
 | |
| 			x->cbBlockSize,
 | |
| 			dest,
 | |
| 			dest_len,
 | |
| 			&cbResult,
 | |
| 			0);
 | |
| 		if (S_OK != status)
 | |
| 		{
 | |
| 			cbResult = 0;
 | |
| 			SetLastError(status);
 | |
| 		}
 | |
| 	}
 | |
| 	return cbResult;
 | |
| }
 | |
| 
 | |
| 
 | |
| /*
 | |
| *	Initialize cipher context 
 | |
| */
 | |
| unsigned int cng_cipher_init(PSSH_CNG_CIPHER_CTX x, const unsigned char *key, unsigned int keylen, const unsigned char *iv, size_t ivlen, unsigned int flags)
 | |
| {
 | |
| 	HRESULT					status = S_OK;
 | |
| 	BCRYPT_ALG_HANDLE       hAlg = NULL;
 | |
| 	DWORD					cbData = 0;
 | |
| 	LPCWSTR					pAlg = NULL;
 | |
| 	DWORD					cbBlockLen = 0;
 | |
| 	DWORD					cbKeyObject = 0;
 | |
| 
 | |
| 	if ((0 == (flags & _CNG_CIPHER_AES)) || (0 == (flags & (_CNG_MODE_CBC | _CNG_MODE_CTR))))
 | |
| 		return STATUS_INVALID_PARAMETER;
 | |
| 
 | |
| 	
 | |
| 
 | |
| 	// wipe out old context
 | |
| 	memset(x, 0, sizeof(SSH_CNG_CIPHER_CTX));
 | |
| 
 | |
| 
 | |
| 	// initialize simple context fields
 | |
| 	x->flags = flags;
 | |
| 
 | |
| 	// only one cipher supported right now
 | |
| 	if (flags & _CNG_CIPHER_AES)
 | |
| 		pAlg = BCRYPT_AES_ALGORITHM;
 | |
| 
 | |
| 
 | |
| 	// Generate BCrypt Key and set mode if applicable
 | |
| 	if (NT_SUCCESS(status = BCryptOpenAlgorithmProvider(
 | |
| 		&hAlg,
 | |
| 		pAlg,
 | |
| 		NULL,
 | |
| 		0)))
 | |
| 	{
 | |
| 
 | |
| 		if (NT_SUCCESS(status = BCryptGetProperty(
 | |
| 			hAlg,
 | |
| 			BCRYPT_BLOCK_LENGTH,
 | |
| 			(PBYTE)&cbBlockLen,
 | |
| 			sizeof(DWORD),
 | |
| 			&cbData,
 | |
| 			0)))
 | |
| 		{
 | |
| 			x->cbBlockSize = cbBlockLen;
 | |
| 			if (cbBlockLen != ivlen)
 | |
| 			{
 | |
| 				status = STATUS_INVALID_PARAMETER;
 | |
| 			}
 | |
| 			else
 | |
| 			{
 | |
| 				x->pbIV = (PBYTE)HeapAlloc(GetProcessHeap(), 0, ivlen);
 | |
| 				if (NULL == x->pbIV)
 | |
| 				{
 | |
| 					status = GetLastError();
 | |
| 				}
 | |
| 				else
 | |
| 				{
 | |
| 					memcpy(x->pbIV, iv, ivlen);
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 
 | |
| 		if (status == S_OK && flags & _CNG_MODE_CBC)
 | |
| 		{
 | |
| 			status = BCryptSetProperty(
 | |
| 				hAlg,
 | |
| 				BCRYPT_CHAINING_MODE,
 | |
| 				(PBYTE)BCRYPT_CHAIN_MODE_CBC,
 | |
| 				sizeof(BCRYPT_CHAIN_MODE_CBC),
 | |
| 				0);
 | |
| 		}
 | |
| 
 | |
| 		if (status == S_OK)
 | |
| 		{
 | |
| 			status = BCryptGetProperty(
 | |
| 				hAlg,
 | |
| 				BCRYPT_OBJECT_LENGTH,
 | |
| 				(PBYTE)&cbKeyObject,
 | |
| 				sizeof(DWORD),
 | |
| 				&cbData,
 | |
| 				0);
 | |
| 		}
 | |
| 
 | |
| 		if ((status == S_OK) && (x->pKeyObject = (PBYTE)HeapAlloc(GetProcessHeap(),0,cbKeyObject)))
 | |
| 		{
 | |
| 			status = BCryptGenerateSymmetricKey(
 | |
| 				hAlg,
 | |
| 				&(x->hKey),
 | |
| 				x->pKeyObject,
 | |
| 				cbKeyObject,
 | |
| 				(PBYTE)key,
 | |
| 				keylen,
 | |
| 				0);
 | |
| 		}
 | |
| 		BCryptCloseAlgorithmProvider(hAlg, 0);
 | |
| 
 | |
| 		// if we got an error along the way, free up the iv and key object
 | |
| 		if (status != S_OK && x->pbIV)
 | |
| 		{
 | |
| 			HeapFree(GetProcessHeap(), 0, x->pbIV);
 | |
| 		}
 | |
| 		if (status != S_OK && x->pKeyObject)
 | |
| 		{
 | |
| 			HeapFree(GetProcessHeap(), 0, x->pKeyObject);
 | |
| 		}
 | |
| 	}
 | |
| 	return status;
 | |
| }
 | |
| /*
 | |
| *  Cleanup cipher context fields
 | |
| */
 | |
| void cng_cipher_cleanup(PSSH_CNG_CIPHER_CTX x)
 | |
| {
 | |
| 	if (x->pbIV)
 | |
| 		HeapFree(GetProcessHeap(), 0, x->pbIV);
 | |
| 	if (x->hKey)
 | |
| 		BCryptDestroyKey(x->hKey);
 | |
| 	if (x->pKeyObject)
 | |
| 		HeapFree(GetProcessHeap(), 0, x->pKeyObject);
 | |
| }
 | |
| 
 | |
| #endif |