windows-nt/Source/XPSP1/NT/net/rras/cm/cmsecure/cryptfnc.cpp

659 lines
19 KiB
C++
Raw Permalink Normal View History

2020-09-26 03:20:57 -05:00
//+----------------------------------------------------------------------------
//
// File: cryptfnc.cpp
//
// Module: CMSECURE.LIB
//
// Synopsis: This file implements the cryptfnc class that provides
// easy to use interfaces on the CryptoAPI.
//
// Copyright (c) 1996-1999 Microsoft Corporation
//
// Author: AshishS Created 12/03/96
// henryt modified for CM 5/21/97
//
//+----------------------------------------------------------------------------
#include "cryptfnc.h"
#ifdef UNICODE
#define LoadLibraryExU LoadLibraryExW
#else
#define LoadLibraryExU LoadLibraryExA
#endif
#include "linkdll.h" // LinkToDll and BindLinkage
CCryptFunctions::~CCryptFunctions()
{
// Release provider handle.
if (m_hProv != 0)
{
m_fnCryptReleaseContext(m_hProv, 0);
}
if (m_AdvApiLink.hInstAdvApi32)
{
FreeLibrary(m_AdvApiLink.hInstAdvApi32);
ZeroMemory(&m_AdvApiLink, sizeof(m_AdvApiLink));
}
}
CCryptFunctions::CCryptFunctions()
{
m_hProv = 0;
ZeroMemory(&m_AdvApiLink, sizeof(m_AdvApiLink));
}
BOOL CCryptFunctions::m_fnCryptAcquireContext(HCRYPTPROV *phProv, LPCSTR pszContainer, LPCSTR pszProvider,
DWORD dwProvType, DWORD dwFlags)
{
BOOL bReturn = FALSE;
MYDBGASSERT(m_AdvApiLink.pfnCryptAcquireContext);
if (m_AdvApiLink.pfnCryptAcquireContext)
{
bReturn = m_AdvApiLink.pfnCryptAcquireContext(phProv, pszContainer, pszProvider,
dwProvType, dwFlags);
}
return bReturn;
}
BOOL CCryptFunctions::m_fnCryptCreateHash(HCRYPTPROV hProv, ALG_ID Algid, HCRYPTKEY hKey,
DWORD dwFlags, HCRYPTHASH *phHash)
{
BOOL bReturn = FALSE;
MYDBGASSERT(m_AdvApiLink.pfnCryptCreateHash);
if (m_AdvApiLink.pfnCryptCreateHash)
{
bReturn = m_AdvApiLink.pfnCryptCreateHash(hProv, Algid, hKey, dwFlags, phHash);
}
return bReturn;
}
BOOL CCryptFunctions::m_fnCryptDecrypt(HCRYPTKEY hKey, HCRYPTHASH hHash, BOOL Final, DWORD dwFlags,
BYTE *pbData, DWORD *pdwDataLen)
{
BOOL bReturn = FALSE;
MYDBGASSERT(m_AdvApiLink.pfnCryptDecrypt);
if (m_AdvApiLink.pfnCryptDecrypt)
{
bReturn = m_AdvApiLink.pfnCryptDecrypt(hKey, hHash, Final, dwFlags, pbData, pdwDataLen);
}
return bReturn;
}
BOOL CCryptFunctions::m_fnCryptDeriveKey(HCRYPTPROV hProv, ALG_ID Algid, HCRYPTHASH hBaseData,
DWORD dwFlags, HCRYPTKEY *phKey)
{
BOOL bReturn = FALSE;
MYDBGASSERT(m_AdvApiLink.pfnCryptDeriveKey);
if (m_AdvApiLink.pfnCryptDeriveKey)
{
bReturn = m_AdvApiLink.pfnCryptDeriveKey(hProv, Algid, hBaseData, dwFlags, phKey);
}
return bReturn;
}
BOOL CCryptFunctions::m_fnCryptDestroyHash(HCRYPTHASH hHash)
{
BOOL bReturn = FALSE;
MYDBGASSERT(m_AdvApiLink.pfnCryptDestroyHash);
if (m_AdvApiLink.pfnCryptDestroyHash)
{
bReturn = m_AdvApiLink.pfnCryptDestroyHash(hHash);
}
return bReturn;
}
BOOL CCryptFunctions::m_fnCryptDestroyKey(HCRYPTKEY hKey)
{
BOOL bReturn = FALSE;
MYDBGASSERT(m_AdvApiLink.pfnCryptDestroyKey);
if (m_AdvApiLink.pfnCryptDestroyKey)
{
bReturn = m_AdvApiLink.pfnCryptDestroyKey(hKey);
}
return bReturn;
}
BOOL CCryptFunctions::m_fnCryptEncrypt(HCRYPTKEY hKey, HCRYPTHASH hHash, BOOL Final, DWORD dwFlags,
BYTE *pbData, DWORD *pdwDataLen, DWORD dwBufLen)
{
BOOL bReturn = FALSE;
MYDBGASSERT(m_AdvApiLink.pfnCryptEncrypt);
if (m_AdvApiLink.pfnCryptEncrypt)
{
bReturn = m_AdvApiLink.pfnCryptEncrypt(hKey, hHash, Final, dwFlags, pbData, pdwDataLen, dwBufLen);
}
return bReturn;
}
BOOL CCryptFunctions::m_fnCryptHashData(HCRYPTHASH hHash, CONST BYTE *pbData, DWORD dwDataLen, DWORD dwFlags)
{
BOOL bReturn = FALSE;
MYDBGASSERT(m_AdvApiLink.pfnCryptHashData);
if (m_AdvApiLink.pfnCryptHashData)
{
bReturn = m_AdvApiLink.pfnCryptHashData(hHash, pbData, dwDataLen, dwFlags);
}
return bReturn;
}
BOOL CCryptFunctions::m_fnCryptReleaseContext(HCRYPTPROV hProv, ULONG_PTR dwFlags)
{
BOOL bReturn = FALSE;
MYDBGASSERT(m_AdvApiLink.pfnCryptReleaseContext);
if (m_AdvApiLink.pfnCryptReleaseContext)
{
bReturn = m_AdvApiLink.pfnCryptReleaseContext(hProv, dwFlags);
}
return bReturn;
}
BOOL CCryptFunctions::m_pfnCryptGenRandom(HCRYPTPROV hProv, DWORD dwLen, BYTE* pbBuffer)
{
BOOL bReturn = FALSE;
MYDBGASSERT(m_AdvApiLink.pfnCryptGenRandom);
if (m_AdvApiLink.pfnCryptGenRandom)
{
bReturn = m_AdvApiLink.pfnCryptGenRandom(hProv, dwLen, pbBuffer);
}
return bReturn;
}
//
// Calls m_pfnCryptGenRandom to create a random key
//
BOOL CCryptFunctions::GenerateRandomKey(PBYTE pbData, DWORD cbData)
{
BOOL fReturn = FALSE;
if (pbData)
{
fReturn = m_pfnCryptGenRandom(m_hProv, cbData, pbData);
}
return fReturn;
}
//+----------------------------------------------------------------------------
//
// Func: CCryptFunctions::GenerateSessionKeyFromPassword
//
// Desc: this function Generates a SessionKey using the pszPassword parameter
//
// Args: [phKey] - location to store the session key
// [pszPassword] - password to generate the session key from
// [dwEncKeyLen] - how many bits of encryption
//
// Return: BOOL (FALSE if a fatal error occurred, else TRUE)
//
// Notes:
//
//-----------------------------------------------------------------------------
BOOL CCryptFunctions::GenerateSessionKeyFromPassword(
HCRYPTKEY * phKey,
LPTSTR pszPassword,
DWORD dwEncKeyLen)
{
DWORD dwLength;
HCRYPTHASH hHash = 0;
// Create hash object.
//
if (!m_fnCryptCreateHash(m_hProv, // handle to CSP
CALG_SHA, // use SHA hash algorithm
0, // not keyed hash
0, // flags - always 0
&hHash)) // address where hash object should be created
{
MYDBG(("Error 0x%x during CryptCreateHash", GetLastError()));
goto cleanup;
}
// Hash password string.
//
dwLength = lstrlen(pszPassword) * sizeof(TCHAR);
if (!m_fnCryptHashData(hHash, // handle to hash object
(BYTE *)pszPassword, // address of data to be hashed
dwLength, // length of data
0)) // flags
{
MYDBG(("Error 0x%x during CryptHashData", GetLastError()));
goto cleanup;
}
// Create block cipher session key based on hash of the password.
//
if (!m_fnCryptDeriveKey(m_hProv, //CSP provider
CALG_RC2, // use RC2 block cipher algorithm
hHash, //handle to hash object
(dwEncKeyLen << 16), // just the key length, no flags - we do not need the key to be exportable
phKey)) //address the newly created key should be copied
{
MYDBG(("Error 0x%x during CryptDeriveKey", GetLastError()));
goto cleanup;
}
// Destroy hash object.
m_fnCryptDestroyHash(hHash);
return TRUE;
cleanup:
// Destroy hash object.
if (hHash != 0)
{
m_fnCryptDestroyHash(hHash);
}
return FALSE;
}
// This function must be called before any member functions of the
// class are used.
// Returns FALSE if a Fatal error occured, TRUE otherwise
BOOL CCryptFunctions::InitCrypt()
{
LPCSTR ArrayOfCryptFuncs [] =
{
#ifdef UNICODE
"CryptAcquireContextW", // this has never been tested
#else
"CryptAcquireContextA",
#endif
"CryptCreateHash",
"CryptDecrypt",
"CryptDeriveKey",
"CryptDestroyHash",
"CryptDestroyKey",
"CryptEncrypt",
"CryptHashData",
"CryptReleaseContext",
"CryptGenRandom", // to create a random session key
NULL
};
BOOL bRet = LinkToDll(&(m_AdvApiLink.hInstAdvApi32), TEXT("Advapi32.dll"), ArrayOfCryptFuncs,
m_AdvApiLink.apvPfn);
if (!bRet)
{
goto cleanup;
}
// Get handle to user default provider.
if (! m_fnCryptAcquireContext(&m_hProv, // address to get the handle to CSP
CM_CRYPTO_CONTAINER, // contianer name
MS_DEF_PROV, // provider
PROV_RSA_FULL, // type of provider
0)) // no flags
{
DWORD dwError = GetLastError();
MYDBGTST(dwError, ("Error 0x%x during CryptAcquireContext", dwError));
MYDBG(("Calling CryptAcquireContext again to create keyset"));
if (! m_fnCryptAcquireContext(&m_hProv,// handle to CSP
CM_CRYPTO_CONTAINER,// contianer name
MS_DEF_PROV, // provider
PROV_RSA_FULL, // type of provider
CRYPT_NEWKEYSET) ) // create the keyset
{
MYDBG(("Fatal Error 0x%x during second call to CryptAcquireContext", GetLastError()));
goto cleanup;
}
}
return TRUE;
cleanup:
// Release provider handle.
if (m_hProv != 0)
{
m_fnCryptReleaseContext(m_hProv, 0);
}
return FALSE;
}
// Given a key string, and data to encrypt this function generates a
// session key from the key string. This session key is then used to
// encrypt the data.
// Returns FALSE if a Fatal error occured, TRUE otherwise
BOOL CCryptFunctions::EncryptDataWithKey(
LPTSTR pszKey, // password
PBYTE pbData, // Data to be encrypted
DWORD dwDataLength, // Length of data in bytes
PBYTE *ppbEncryptedData, // Encrypted secret key will be stored here
DWORD *pdwEncryptedBufferLen, // Length of this buffer
PFN_CMSECUREALLOC pfnAlloc,
PFN_CMSECUREFREE pfnFree,
DWORD dwEncKeySize // how many bits of encryption do we want? (0 implies "don't care")
)
{
HCRYPTKEY hKey = 0;
DWORD dwErr;
DWORD dwBufferLen;
BOOL fOk = FALSE;
PBYTE pbBuf = NULL;
//
// Init should have been successfully called before
// if no data to be encrypted, don't do anything
//
if (m_hProv == 0 || !dwDataLength)
{
return FALSE;
}
if (!GenerateSessionKeyFromPassword(&hKey, pszKey, dwEncKeySize))
goto cleanup;
// copy the data into another buffer to encrypt it
*pdwEncryptedBufferLen = dwDataLength;
dwBufferLen = dwDataLength + DEFAULT_CRYPTO_EXTRA_BUFFER_SIZE;
while (1)
{
//
// alloc memory for output buffer
//
if (pfnAlloc)
{
*ppbEncryptedData = (PBYTE)pfnAlloc(dwBufferLen);
}
else
{
*ppbEncryptedData = (PBYTE)HeapAlloc(GetProcessHeap(),
HEAP_ZERO_MEMORY,
dwBufferLen);
}
if (!*ppbEncryptedData)
{
MYDBG(("EncryptDataWithKey: out of memory error"));
goto cleanup;
}
// copy the data into another buffer to encrypt it
memcpy (*ppbEncryptedData, pbData, dwDataLength);
// now encrypt the secret key using the key generated
if ( ! m_fnCryptEncrypt(hKey,
0, // no hash required
TRUE, // Final packet
0, // Flags - always 0
*ppbEncryptedData, // data buffer
pdwEncryptedBufferLen, // length of data
dwBufferLen ) ) // size of buffer
{
MYDBG(("Error 0x%x during CryptEncrypt", GetLastError()));
if (pfnFree)
{
pfnFree(*ppbEncryptedData);
}
else
{
HeapFree(GetProcessHeap(), 0, *ppbEncryptedData);
}
*ppbEncryptedData = NULL;
dwErr = GetLastError();
//
// if the output is too small, realloc it.
//
if (dwErr == ERROR_MORE_DATA || dwErr == NTE_BAD_LEN)
{
dwBufferLen += DEFAULT_CRYPTO_EXTRA_BUFFER_SIZE;
continue;
}
goto cleanup;
}
//
// we now have the data encrypted. we need to uuencode it.
//
if (pfnAlloc)
{
pbBuf = (PBYTE)pfnAlloc(*pdwEncryptedBufferLen);
}
else
{
pbBuf = (PBYTE)HeapAlloc(GetProcessHeap(),
HEAP_ZERO_MEMORY,
*pdwEncryptedBufferLen);
}
if (!pbBuf)
{
MYDBG(("EncryptDataWithKey: out of memory error"));
if (pfnFree)
{
pfnFree(*ppbEncryptedData);
}
else
{
HeapFree(GetProcessHeap(), 0, *ppbEncryptedData);
}
*ppbEncryptedData = NULL;
goto cleanup;
}
memcpy(pbBuf, *ppbEncryptedData, *pdwEncryptedBufferLen);
uuencode(pbBuf, *pdwEncryptedBufferLen, (CHAR*)*ppbEncryptedData, dwBufferLen);
//
// set the encrypted buffer len
//
*pdwEncryptedBufferLen = lstrlen((LPTSTR)*ppbEncryptedData);
if (pfnFree)
{
pfnFree(pbBuf);
}
else
{
HeapFree(GetProcessHeap(), 0, pbBuf);
}
pbBuf = NULL;
break;
}
fOk = TRUE;
cleanup:
// destroy session key
if (hKey != 0)
m_fnCryptDestroyKey(hKey);
return fOk;
}
// Given a key string, and encrypted data using EncryptDataWithPassword,
// this function generates a session key from the key string. This
// session key is then used to decrypt the data.
// returns
// CRYPT_FNC_NO_ERROR no error
// CRYPT_FNC_BAD_PASSWORD password bad try again
// CRYPT_FNC_INSUFFICIENT_BUFFER larger buffer is required
// *pdwEncrytedBufferLen is set to required length
// CRYPT_FNC_INIT_NOT_CALLED InitCrypt not successfully called
// CRYPT_FNC_INTERNAL_ERROR
DWORD CCryptFunctions::DecryptDataWithKey(
LPTSTR pszKey, // password
PBYTE pbEncryptedData, // Encrypted data
DWORD dwEncrytedDataLen, // Length of encrypted data
PBYTE *ppbData, // Decrypted Data will be stored here
DWORD *pdwDataBufferLength,// Length of the above buffer in bytes
PFN_CMSECUREALLOC pfnAlloc,
PFN_CMSECUREFREE pfnFree,
DWORD dwEncKeySize // how many bits of encryption do we want? (0 implies "don't care")
)
{
DWORD dwBufferLen;
DWORD dwUUDecodeBufLen;
HCRYPTKEY hKey = 0;
DWORD dwError;
DWORD dwMaxBufSize = 1024 * 10; // Just some max buffer size (10K) in order to exit the while loop
//
// Init should have been successfully called before
// if no data to be decrypted, then don't do anything
//
if (m_hProv == 0 || !dwEncrytedDataLen)
{
dwError = CRYPT_FNC_INIT_NOT_CALLED;
goto cleanup;
}
if (!GenerateSessionKeyFromPassword(&hKey, pszKey, dwEncKeySize))
{
dwError = CRYPT_FNC_INTERNAL_ERROR;
goto cleanup;
}
// copy the data into another buffer to encrypt it
dwBufferLen = dwEncrytedDataLen + DEFAULT_CRYPTO_EXTRA_BUFFER_SIZE;
// *pdwDataBufferLength = dwEncrytedDataLen;
//
// Loop until we get to dwMaxBufSize. This is a safeguard to get out
// of the infinite loop problem. DBCS passwords used to loop continuously.
//
while(dwBufferLen < dwMaxBufSize)
{
//
// alloc memory for output buffer
//
if (pfnAlloc)
{
*ppbData = (PBYTE)pfnAlloc(dwBufferLen);
}
else
{
*ppbData = (PBYTE)HeapAlloc(GetProcessHeap(),
HEAP_ZERO_MEMORY,
dwBufferLen);
}
if (!*ppbData)
{
dwError = CRYPT_FNC_OUT_OF_MEMORY;
goto cleanup;
}
//
// set uudecode output buf size
//
dwUUDecodeBufLen = dwBufferLen;
uudecode((char*)pbEncryptedData, (CHAR*)*ppbData, &dwUUDecodeBufLen);
*pdwDataBufferLength = dwUUDecodeBufLen;
// now decrypt the secret key using the key generated
if ( ! m_fnCryptDecrypt(hKey,
0, // no hash required
TRUE, // Final packet
0, // Flags - always 0
*ppbData, // data buffer
pdwDataBufferLength )) // length of data
{
DWORD dwCryptError = GetLastError();
MYDBGTST(dwCryptError, ("Error 0x%x during CryptDecrypt", dwCryptError));
if (pfnFree)
{
pfnFree(*ppbData);
}
else
{
HeapFree(GetProcessHeap(), 0, *ppbData);
}
*ppbData = NULL;
//
// if the output is too small, realloc it.
//
if (dwCryptError == NTE_BAD_LEN)
{
dwBufferLen *= 2; // to speed up memory alloc double the size
continue;
}
// CryptDecrypt fails with error NTE_BAD_DATA if the password
// is incorrect. Hence we should check for this error and prompt the
// user again for the password. If the data is garbled in transit, then the secret key
// will still be decrypted into a wrong value and the user will not
// know about it.
if (dwCryptError == NTE_BAD_DATA)
{
dwError = CRYPT_FNC_BAD_KEY;
}
else
{
dwError = CRYPT_FNC_INTERNAL_ERROR;
}
goto cleanup;
}
break;
}
if (dwBufferLen < dwMaxBufSize)
{
dwError = CRYPT_FNC_NO_ERROR;
}
else
{
CMTRACE1(TEXT("DecryptDataWithKey: not enough buffer = %d bytes"), dwBufferLen);
MYDBGASSERT(FALSE);
dwError = NTE_BAD_LEN;
}
cleanup:
// destroy session key
if (hKey != 0)
{
m_fnCryptDestroyKey(hKey);
}
return dwError;
}