//+---------------------------------------------------------------------------- // // 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; }