//+---------------------------------------------------------------------------- // // File: cmsecure.cpp // // Module: CMSECURE.LIB // // Synopsis: CM Crypto APIs // Three methods are support for decryption: // CBCEncryption CMSECURE_ET_CBC_CIPHER // Simple xor encryption CMSECURE_ET_STREAM_CIPHER // CryptoApi CMSECURE_ET_RC2 // Two methods are supported for encryption // CBCEncryption // CryptoApi // // CBCEncryption algorithm: Cipher Block Chaining Mode with initializing variable // EnCipher: C[i] = E[k](p[i] XOR C[i-1]) // DeCipher: P[i] = C[i-1] XOR D[k](C[i]) // P: Plain text // C: Cipher text // E[k]: Encryption function with key // D[k]: Decryption function with key // // Copyright (c) 1996-1998 Microsoft Corporation // // Author: henryt created 05/21/97 // fengsun changed encryption algorithm 08/21/97 // //+---------------------------------------------------------------------------- #include "cryptfnc.h" #include "userinfo_str.h" ////////////////////////////////////////////////////////////////////////// // defines ////////////////////////////////////////////////////////////////////////// // we want 40-bit encryption exactly for pre-shared key const DWORD c_dwEncryptKeyLength = 40; // // the max len for the const string for session key generation // #define MAX_KEY_STRING_LEN 40 #define EXTRA_UUDECODE_BUF_LEN 10 ////////////////////////////////////////////////////////////////////////// // Globals ////////////////////////////////////////////////////////////////////////// static CCryptFunctions* g_pCryptFnc = NULL; static long g_nRefCount=0; // the reference count, CryptoApi is unloaded when the count is 0 static BOOL g_fFastEncryption; // // the const string for session key generation // static const TCHAR gc_szKeyStr[] = TEXT("Please enter your password"); ////////////////////////////////////////////////////////////////////////// // Func prototypes ////////////////////////////////////////////////////////////////////////// static int CBCEncipherData(const char* pszKey, const BYTE* pbData, int dwDataLength, BYTE* pbOut, int dwOutBufferLength); static int CBCDecipherData(const char* pszKey, const BYTE* pbData, int dwDataLength, BYTE* pbOut, int dwOutBufferLength); inline int CBCDecipherBufferSize(int dataSize); inline int CBCEncipherBufferSize(int dataSize); static BOOL StreamCipherEncryptData( LPTSTR pszKey, // password LPBYTE pbData, // Data to be encrypted DWORD dwDataLength, // Length of data in bytes LPBYTE *ppbEncryptedData, // Encrypted secret key will be stored here DWORD *pdwEncryptedBufferLen, // Length of this buffer PFN_CMSECUREALLOC pfnAlloc, PFN_CMSECUREFREE pfnFree ); static BOOL StreamCipherDecryptData( LPTSTR pszKey, // password LPBYTE pbEncryptedData, // Encrypted data DWORD dwEncrytedDataLen, // Length of encrypted data LPBYTE *ppbData, // Decrypted Data will be stored here DWORD *pdwDataBufferLength,// Length of the above buffer in bytes PFN_CMSECUREALLOC pfnAlloc, PFN_CMSECUREFREE pfnFree, DWORD dwEncryptionType ); static LPTSTR reverse( LPTSTR s ); static void GenerateKeyString( IN OUT LPTSTR pszBuf, IN DWORD dwBufLen ); static BOOL SetKeyString(IN OUT LPTSTR pszBuf, IN DWORD dwBufLen, IN DWORD dwEncryptionType, IN PFN_CMSECUREALLOC pfnAlloc, IN PFN_CMSECUREFREE pfnFree, IN LPSTR pszUserKey, OUT BOOL *pfMoreSecure); static BOOL GetKeyString(IN OUT LPTSTR pszBuf, IN DWORD * pdwBufLen, IN DWORD dwEncryptionType, IN PFN_CMSECUREALLOC pfnAlloc, IN PFN_CMSECUREFREE pfnFree, IN LPSTR pszUserKey, OUT BOOL *pfMoreSecure); static BOOL GetCurrentKey(PTCHAR szTempKeyStr, DWORD dwTempKeyStrMaxLen, IN PFN_CMSECUREALLOC pfnAlloc, IN PFN_CMSECUREFREE pfnFree); ////////////////////////////////////////////////////////////////////////// // Implementations ////////////////////////////////////////////////////////////////////////// //+--------------------------------------------------------------------------- // // Function: InitCryptoApi // // Synopsis: Initialize the CryptoApi. // // Arguments: // // Returns: Pointer to CCryptFunctions, if success // NULL if failure // // History: fengsun Created 8/22/97 // //---------------------------------------------------------------------------- static CCryptFunctions* InitCryptoApi() { CCryptFunctions* pCryptFnc = new CCryptFunctions(); if (pCryptFnc == NULL) return NULL; if (pCryptFnc->InitCrypt()) { return pCryptFnc; } else { delete pCryptFnc; return NULL; } } //+--------------------------------------------------------------------------- // // Function: InitSecure // // Synopsis: Initialize the security/encryption routines. // // Arguments: fFastEncryption : TRUE will use a faster algorithm vs a more secure one // // Returns: TRUE if success, always return TRUE // FALSE if failure // // History: henryt Created 5/20/97 // fengsun modified // //---------------------------------------------------------------------------- BOOL InitSecure( BOOL fFastEncryption ) { MYDBGASSERT(g_nRefCount>=0); InterlockedIncrement(&g_nRefCount); // // If already initialized, increase the RefCount and return // if (g_nRefCount>1) { return TRUE; } MYDBGASSERT(g_pCryptFnc == NULL); // not initialized yet g_fFastEncryption = fFastEncryption; if (!fFastEncryption) { // // CryptoApi is slow on Win95 // If more secure is desired, try the CryptoApi // Ignore return value of InitCrypt() // g_pCryptFnc = InitCryptoApi(); } // // CryptoApi is not available for Win95 Gold // we'll use stream cipher. // return TRUE; } //+--------------------------------------------------------------------------- // // Function: DeInitSecure // // Synopsis: Clean up function for the security/encryption routines. // // Arguments: NONE // // Returns: NONE // // History: henryt Created 5/20/97 // //---------------------------------------------------------------------------- void DeInitSecure( void ) { MYDBGASSERT(g_nRefCount>=1); // // DeInit the CryptoApi if RefCount is down to 0 // if (InterlockedDecrement(&g_nRefCount) <=0) // if ( (--g_nRefCount) <=0 ) { if (g_pCryptFnc) { delete g_pCryptFnc; g_pCryptFnc = NULL; } } } //+--------------------------------------------------------------------------- // // Function: StreamCipherEncryptData // // Synopsis: data encryption using stream cipher algorithm. // // Arguments: NONE // // Returns: NONE // // History: henryt Created 6/9/97 // fengsun modified 8/21/97 // to use Cipher Block Chaning Mode Algorithm // //---------------------------------------------------------------------------- static BOOL StreamCipherEncryptData( LPTSTR pszKey, // password LPBYTE pbData, // Data to be encrypted DWORD dwDataLength, // Length of data in bytes LPBYTE *ppbEncryptedData, // Encrypted secret key will be stored here DWORD *pdwEncryptedBufferLen, // Length of this buffer PFN_CMSECUREALLOC pfnAlloc, PFN_CMSECUREFREE pfnFree ) { LPBYTE pbTmpBuf = NULL; BOOL fOk = FALSE; BOOL fRet; if (!pszKey || !pbData || !dwDataLength || !ppbEncryptedData || !pdwEncryptedBufferLen) { CMASSERTMSG(FALSE, TEXT("StreamCipherEncryptData - invalid input params")); return FALSE; } // // Alloc a buffer to hold enciphered data // DWORD dwEncipherBufferLen = CBCEncipherBufferSize(dwDataLength); if (pfnAlloc) { pbTmpBuf = (LPBYTE)pfnAlloc(dwEncipherBufferLen); } else { pbTmpBuf = (LPBYTE)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dwEncipherBufferLen); } if (!pbTmpBuf) { goto cleanup; } // // encipher the data // dwEncipherBufferLen = CBCEncipherData(pszKey, pbData, dwDataLength, pbTmpBuf, dwEncipherBufferLen); // // we now have the data encrypted. we need to uuencode it. // DWORD cbBuf; cbBuf = 2*dwEncipherBufferLen + EXTRA_UUDECODE_BUF_LEN; // enough for uuencode if (pfnAlloc) { *ppbEncryptedData = (LPBYTE)pfnAlloc(cbBuf); } else { *ppbEncryptedData = (LPBYTE)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, cbBuf); } if (!*ppbEncryptedData) { goto cleanup; } fRet = uuencode(pbTmpBuf, dwEncipherBufferLen, (CHAR*)*ppbEncryptedData, cbBuf); MYDBGASSERT(fRet); if (!fRet) { if (pfnFree) { pfnFree(*ppbEncryptedData); } else { HeapFree(GetProcessHeap(), 0, *ppbEncryptedData); } *ppbEncryptedData = NULL; goto cleanup; } // // set the encrypted buffer len // *pdwEncryptedBufferLen = lstrlen((LPSTR)*ppbEncryptedData); fOk = TRUE; cleanup: if (pbTmpBuf) { if (pfnFree) { pfnFree(pbTmpBuf); } else { HeapFree(GetProcessHeap(), 0, pbTmpBuf); } } return fOk; } //+--------------------------------------------------------------------------- // // Function: StreamCipherDecryptData // // Synopsis: data decryption using stream cipher algorithm. // // Arguments: NONE // // Returns: NONE // // History: henryt Created 6/9/97 // fengsun modified 8/21/97 // to use Cipher Block Chaning Modem Algorithm // //---------------------------------------------------------------------------- static BOOL StreamCipherDecryptData( LPTSTR pszKey, // password LPBYTE pbEncryptedData, // Encrypted data DWORD dwEncryptedDataLen, // Length of encrypted data LPBYTE *ppbData, // Decrypted Data will be stored here DWORD *pdwDataBufferLength,// Length of the above buffer in bytes PFN_CMSECUREALLOC pfnAlloc, PFN_CMSECUREFREE pfnFree, DWORD dwEncryptionType ) { BOOL fRet = FALSE; DWORD dwUUDecodeBufLen; if (!pszKey || !pbEncryptedData || !dwEncryptedDataLen || !pdwDataBufferLength) { CMASSERTMSG(FALSE, TEXT("StreamCipherDecryptData - invalid input params")); return FALSE; } // // set uudecode output buf size // dwUUDecodeBufLen = dwEncryptedDataLen + EXTRA_UUDECODE_BUF_LEN; // // alloc memory for output buffer // if (pfnAlloc) { *ppbData = (LPBYTE)pfnAlloc(dwUUDecodeBufLen); } else { *ppbData = (LPBYTE)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dwUUDecodeBufLen); } if (!*ppbData) { goto cleanup; } // // uudecode it first // fRet = uudecode((char*)pbEncryptedData, (CHAR*)*ppbData, &dwUUDecodeBufLen); MYDBGASSERT(fRet); if (!fRet) { if (pfnFree) { pfnFree(*ppbData); } else { HeapFree(GetProcessHeap(), 0, *ppbData); } *ppbData = NULL; goto cleanup; } switch(dwEncryptionType) { case CMSECURE_ET_STREAM_CIPHER: { // // Simple decipher algorithm used in old version // DWORD dwLen = lstrlen(pszKey); if (dwLen) { for (DWORD dwIdx = 0; dwIdx < dwUUDecodeBufLen; dwIdx++) { *(*ppbData + dwIdx) ^= pszKey[dwIdx % dwLen]; } *pdwDataBufferLength = dwUUDecodeBufLen; fRet = TRUE; } } break; case CMSECURE_ET_CBC_CIPHER: // // Inplace decipher // *pdwDataBufferLength = CBCDecipherData(pszKey, *ppbData, dwUUDecodeBufLen, *ppbData, dwUUDecodeBufLen); fRet = TRUE; break; default: MYDBGASSERT(FALSE); } cleanup: return fRet; } //+--------------------------------------------------------------------------- // // Function: EncryptData // // Synopsis: Encrypt the data buffer. // // Arguments: IN PBYTE pbData, // Data to be encrypted // IN DWORD dwDataLength, // Length of data in bytes // OUT PBYTE pbEncryptedData, // Encrypted secret key will be stored here // OUT DWORD *pdwEncrytedBufferLen // Length of this buffer // IN PCMSECUREALLOC pfnAlloc // memory allocator(if NULL, then the default is used. // // Win32 - HeapAlloc(GetProcessHeap(), ...) // IN PCMSECUREFREE pfnFree // memory deallocator(if NULL, then the default is used. // // Win32 - HeapFree(GetProcessHeap(), ...) // IN LPTSTR pszUserKey // Reg key where to store encrypted key // // Returns: TRUE if success // FALSE if failure // // History: henryt Created 5/20/97 // //---------------------------------------------------------------------------- BOOL EncryptData( IN LPBYTE pbData, // Data to be encrypted IN DWORD dwDataLength, // Length of data in bytes OUT LPBYTE *ppbEncryptedData, // Encrypted secret key will be stored here OUT LPDWORD pdwEncrytedBufferLen, // Length of this buffer OUT LPDWORD pEncryptionType, // type of the encryption used IN PFN_CMSECUREALLOC pfnAlloc, IN PFN_CMSECUREFREE pfnFree, IN LPSTR pszUserKey ) { BOOL fOk = FALSE; TCHAR szKeyStr[MAX_KEY_STRING_LEN + 1]={0}; BOOL fMoreSecure = FALSE; DWORD dwUseKey = *pEncryptionType; // // get a key string for session key generation // SetKeyString(szKeyStr, MAX_KEY_STRING_LEN, dwUseKey, pfnAlloc, pfnFree, pszUserKey, &fMoreSecure); *pEncryptionType = CMSECURE_ET_NOT_ENCRYPTED; // // If user want use CryptoApi and it is available // if (!g_fFastEncryption && g_pCryptFnc) { // // encrypt the data with the key string // if (fOk = g_pCryptFnc->EncryptDataWithKey( szKeyStr, // Key pbData, // Secret key dwDataLength, // Length of secret key ppbEncryptedData, // Encrypted data will be stored here pdwEncrytedBufferLen, // Length of this buffer pfnAlloc, // mem allocator pfnFree, // mem deallocator 0)) // not specifying keylength { *pEncryptionType = CMSECURE_ET_RC2; // // If the key is randomly generated then we want to make sure we // set the random key mask // if (fMoreSecure) { *pEncryptionType |= CMSECURE_ET_RANDOM_KEY_MASK; } } } if (!fOk) { if (fOk = StreamCipherEncryptData( szKeyStr, // Key pbData, // Secret key dwDataLength, // Length of secret key ppbEncryptedData, // Encrypted data will be stored here pdwEncrytedBufferLen, // Length of this buffer pfnAlloc, // mem allocator pfnFree)) // mem deallocator { *pEncryptionType = CMSECURE_ET_CBC_CIPHER; // // If the key is randomly generated then we want to make sure we // set the random key mask // if (fMoreSecure) { *pEncryptionType |= CMSECURE_ET_RANDOM_KEY_MASK; } } } ZeroMemory((LPVOID)szKeyStr, sizeof(szKeyStr)); return fOk; } //+--------------------------------------------------------------------------- // // Function: DecryptData // // Synopsis: Decrypt the data buffer. // // Arguments: IN PBYTE pbEncryptedData, // Encrypted data // IN DWORD dwEncrytedDataLen // Length of encrypted data // OUT PBYTE *ppbData, // Decrypted Data will be stored here // OUT DWORD *pdwDataBufferLength, // Length of the above buffer in bytes // IN PCMSECUREALLOC pfnAlloc // memory allocator(if NULL, then the default is used. // // Win32 - HeapAlloc(GetProcessHeap(), ...) // IN PCMSECUREFREE pfnFree // memory deallocator(if NULL, then the default is used. // // Win32 - HeapFree(GetProcessHeap(), ...) // IN LPTSTR pszUserKey // Reg key where to store encrypted key // // Returns: TRUE if success // FALSE if failure // // History: henryt Created 5/20/97 // //---------------------------------------------------------------------------- BOOL DecryptData( IN LPBYTE pbEncryptedData, // Encrypted data IN DWORD dwEncrytedDataLen, // Length of encrypted data OUT LPBYTE *ppbData, // Decrypted Data will be stored here OUT LPDWORD pdwDataBufferLength, // Length of the above buffer in bytes IN DWORD dwEncryptionType, // encryption type for decryption IN PFN_CMSECUREALLOC pfnAlloc, IN PFN_CMSECUREFREE pfnFree, IN LPSTR pszUserKey ) { TCHAR szKeyStr[MAX_KEY_STRING_LEN + 1]={0}; // Plus NULL DWORD dwRet = 0xf; // some non-zero value BOOL fMoreSecure = FALSE; // // To speed things up we only want to generate a key // in case the data is encrypted // if (CMSECURE_ET_NOT_ENCRYPTED != dwEncryptionType) { // // get a key string for session key generation // // // Here we don't care if the pszUserKey is NULL, the called function will determine // this and set fMoreSecure appropriately. Right now we don't check for the random key mask // in dwEncryptionType, but if it isn't set the blob (encrypted key) will not be in the registry // thus it will try to default to using the hardcoded key. This should probably be made more explicit // in the code at a later time. // DWORD dwMaxSize = MAX_KEY_STRING_LEN; GetKeyString(szKeyStr, &dwMaxSize, dwEncryptionType, pfnAlloc, pfnFree, pszUserKey, &fMoreSecure); // // If the random key bit mask is set, we better have have fMoreSecure flag set to true. // This has to be always true in order for decryption to work // CMASSERTMSG(((dwEncryptionType & CMSECURE_ET_RANDOM_KEY_MASK) && fMoreSecure), TEXT("DecryptData - Trying to use mismatched keys")); } // // Clear the random key mask // dwEncryptionType &= ~CMSECURE_ET_RANDOM_KEY_MASK; dwEncryptionType &= ~CMSECURE_ET_USE_SECOND_RND_KEY; switch (dwEncryptionType) { case CMSECURE_ET_RC2: if (g_fFastEncryption && !g_pCryptFnc) { // // if we want fast encryption initially, // We have to initialize the CryptoApi now // g_pCryptFnc = InitCryptoApi(); } if (g_pCryptFnc) { dwRet = g_pCryptFnc->DecryptDataWithKey( szKeyStr, // Key pbEncryptedData, // Encrypted data dwEncrytedDataLen, // Length of encrypted data ppbData, // decrypted data pdwDataBufferLength, // Length of decrypted data pfnAlloc, // mem allocator pfnFree, // mem deallocator 0); // not specifying keylength } break; case CMSECURE_ET_STREAM_CIPHER: case CMSECURE_ET_CBC_CIPHER: // // Use our own encryption algorithm // dwRet = (DWORD)!StreamCipherDecryptData( szKeyStr, // Key pbEncryptedData, // Encrypted data dwEncrytedDataLen, // Length of encrypted data ppbData, // decrypted data pdwDataBufferLength, // Length of decrypted data pfnAlloc, // mem allocator pfnFree, // mem deallocator dwEncryptionType // Encryption type used ); break; case CMSECURE_ET_NOT_ENCRYPTED: // // Just copy the exact contents into the OUT buffer // if (pbEncryptedData && dwEncrytedDataLen && ppbData && pdwDataBufferLength) { if (pfnAlloc) { *ppbData = (LPBYTE)pfnAlloc(dwEncrytedDataLen); } else { *ppbData = (LPBYTE)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dwEncrytedDataLen); } if (*ppbData) { CopyMemory(*ppbData, pbEncryptedData, dwEncrytedDataLen); *pdwDataBufferLength = dwEncrytedDataLen; dwRet = 0; // the return statement correctly returns TRUE } } break; default: MYDBGASSERT(FALSE); break; } ZeroMemory((LPVOID)szKeyStr, sizeof(szKeyStr)); return (!dwRet); } //+---------------------------------------------------------------------------- // // Func: EncryptString // // Desc: encrypt a given (Ansi) string using RC2 encryption // // Args: pszToEncrypt -- Ansi string to be encrypted // pszUserKey -- Key to use for Encryption // ppbEncryptedData -- Encrypted secret key will be stored here(memory will be allocated) // pdwEncrytedBufferLen -- Length of this buffer // // Return: BOOL (FALSE if a fatal error occurred, else TRUE) // // Notes: The encryption type must be at least RC2, and exactly 40-bit // //----------------------------------------------------------------------------- BOOL EncryptString( IN LPSTR pszToEncrypt, // Ansi string to be encrypted IN LPSTR pszUserKey, // Key to use for Encryption OUT LPBYTE * ppbEncryptedData, // Encrypted secret key will be stored here(memory will be allocated) OUT LPDWORD pdwEncrytedBufferLen, // Length of this buffer IN PFN_CMSECUREALLOC pfnAlloc, IN PFN_CMSECUREFREE pfnFree ) { BOOL fOk = FALSE; CMASSERTMSG(pszToEncrypt, TEXT("EncryptData - first arg must be a valid string")); CMASSERTMSG(pszUserKey, TEXT("EncryptData - second arg must be a valid user key")); DWORD dwDataLength = lstrlen(pszToEncrypt) + 1; // get the NULL in as well. if (!g_fFastEncryption && g_pCryptFnc) { if (fOk = g_pCryptFnc->EncryptDataWithKey( pszUserKey, // Key (LPBYTE)pszToEncrypt, // Secret key dwDataLength, // Length of secret key ppbEncryptedData, // Encrypted data will be stored here pdwEncrytedBufferLen, // Length of this buffer pfnAlloc, pfnFree, c_dwEncryptKeyLength)) { CMTRACE(TEXT("EncryptString - succeeded.")); } } return fOk; } //+---------------------------------------------------------------------------- // // Func: DecryptString // // Desc: encrypt a given (Ansi) string using RC2 encryption // // Args: pszToEncrypt -- Ansi string to be encrypted // pszUserKey -- Key to use for Encryption // ppbEncryptedData -- Encrypted secret key will be stored here(memory will be allocated) // pdwEncrytedBufferLen -- Length of this buffer // // Return: BOOL (FALSE if a fatal error occurred, else TRUE) // // Notes: The encryption type must be at least RC2, and exactly 40-bit // //----------------------------------------------------------------------------- BOOL DecryptString( IN LPBYTE pbEncryptedData, // Encrypted data IN DWORD dwEncrytedDataLen, // Length of encrypted data IN LPSTR pszUserKey, // Registry key to store encrypted key for passwords OUT LPBYTE * ppbData, // Decrypted Data will be stored here OUT LPDWORD pdwDataBufferLength, // Length of the above buffer in bytes IN PFN_CMSECUREALLOC pfnAlloc, IN PFN_CMSECUREFREE pfnFree ) { DWORD dwRet = 0xf; // some non-zero value if (!g_fFastEncryption && g_pCryptFnc) { dwRet = g_pCryptFnc->DecryptDataWithKey( pszUserKey, // Key pbEncryptedData, // Encrypted data dwEncrytedDataLen, // Length of encrypted data ppbData, // decrypted data pdwDataBufferLength, // Length of decrypted data pfnAlloc, pfnFree, c_dwEncryptKeyLength); // 40-bit } return (!dwRet); } //+--------------------------------------------------------------------------- // // Function: CBCEncipherBufferSize // // Synopsis: Get the buffer size needed for Enciphering // // Arguments: dataSize sizeof data to be enciphered // // Returns: Encipher buffer size needed // // History: fengsun Created 8/21/97 // //---------------------------------------------------------------------------- inline int CBCEncipherBufferSize(int dataSize) { MYDBGASSERT(dataSize > 0); // // We need one byte to hold the initializing variable // return dataSize + 1; } //+--------------------------------------------------------------------------- // // Function: CBCDecipherBufferSize // // Synopsis: Get the buffer size needed for Deciphering // // Arguments: dataSize sizeof data to be Deciphered // // Returns: Decipher buffer size needed // // History: fengsun Created 8/21/97 // //---------------------------------------------------------------------------- inline int CBCDecipherBufferSize(int dataSize) { MYDBGASSERT(dataSize > 1); return dataSize - 1; } //+--------------------------------------------------------------------------- // // Function: EncryptByte // // Synopsis: Encrypt a single byte // // Arguments: pszKey key string // byteSource byte to be encrypted // param aditional parameter for encryption // // Returns: byte encrypted // // History: fengsun Created 8/21/97 // //---------------------------------------------------------------------------- inline BYTE EncryptByte(LPCTSTR pszKey, BYTE byteSource, BYTE Param) { if (NULL == pszKey) { return NULL; } DWORD dwLen = lstrlen(pszKey); if (0 == dwLen) { return NULL; } return ((byteSource ^ (BYTE)pszKey[Param % dwLen]) + Param); } //+--------------------------------------------------------------------------- // // Function: DecryptByte // // Synopsis: Decrypt a single byte // // Arguments: pszKey key string // byteSource byte to be Decrypted // param aditional parameter for encryption // // Returns: byte decrypted // // History: fengsun Created 8/21/97 // //---------------------------------------------------------------------------- inline BYTE DecryptByte(LPCTSTR pszKey, BYTE byteSource, BYTE Param) { if (NULL == pszKey) { return NULL; } DWORD dwLen = lstrlen(pszKey); if (0 == dwLen) { return NULL; } return ((byteSource - Param) ^ (BYTE)pszKey[Param % dwLen]); } //+--------------------------------------------------------------------------- // // Function: CBCEncipherData // // Synopsis: Encipher a block of data // // Arguments: pszKey key string // pbData Data to be enciphered // dwDataLength size of pbData // pbOut Buffer for encipher result // dwOutBufferLength size of pbOut, must be >= CBCEncipherBufferSize(dwDataLength) // // Returns: size of encipher byte in pbOut, 0 means failed // // History: fengsun Created 8/21/97 // //---------------------------------------------------------------------------- static int CBCEncipherData(const char* pszKey, const BYTE* pbData, int dwDataLength, BYTE* pbOut, int dwOutBufferLength) { MYDBGASSERT(pszKey != NULL); MYDBGASSERT(pbData != NULL); MYDBGASSERT(pbOut != NULL); MYDBGASSERT(dwDataLength > 0); MYDBGASSERT(pbData != pbOut); if (dwDataLength <= 0) return 0; if (dwOutBufferLength < CBCEncipherBufferSize(dwDataLength)) return 0; dwOutBufferLength = CBCEncipherBufferSize(dwDataLength); // // Add a random number as the initializing variable (first byte) // pbOut[0] = (BYTE)((GetTickCount() >> 4 ) % 256); // // encipher it // // EnCipher: C[i] = E[k](p[i] XOR C[i-1]) // BYTE lastPlainText = pbOut[0]; //first initilizing byte pbOut[0] = EncryptByte(pszKey, pbOut[0], 0); for (int dwIdx=1; dwIdx= CBCDecipherBufferSize(dwDataLength) // // Returns: size of decipher byte in pbOut, 0 means failed // // History: fengsun Created 8/21/97 // //---------------------------------------------------------------------------- static int CBCDecipherData(const char* pszKey, const BYTE* pbData, int dwDataLength, BYTE* pbOut, int dwOutBufferLength) { MYDBGASSERT(pszKey != NULL); MYDBGASSERT(pbData != NULL); MYDBGASSERT(pbOut != NULL); MYDBGASSERT(dwDataLength > 1); if (dwDataLength <= 1) return 0; if (dwOutBufferLength < CBCDecipherBufferSize(dwDataLength)) return 0; dwOutBufferLength = CBCDecipherBufferSize(dwDataLength); // // decipher data // // DeCipher: P[i] = C[i-1] XOR D[k](C[i]), // BYTE lastPlainText = DecryptByte(pszKey, pbData[0], 0); //first initilizing byte for (int dwIdx=0; dwIdxGenerateRandomKey((PBYTE)pszBuf, dwBufLen*sizeof(TCHAR))) { // Now that we have the randomkey, we need to store it in the registry so // we can use it when decrypting. In order to store it, we have // to encrypt it first. // // Generate a machine specific key that's used to encrypt the random number // used for the session key // fFuncRet = GetCurrentKey(szTempKeyStr, MAX_KEY_STRING_LEN, pfnAlloc, pfnFree); if (fFuncRet) { // // If user want use CryptoApi and it is available // if (!g_pCryptFnc) { g_pCryptFnc = InitCryptoApi(); } if (g_pCryptFnc) { // // encrypt the data with the key string // fOk = g_pCryptFnc->EncryptDataWithKey( szTempKeyStr, // Key (PBYTE)pszBuf, // Secret key to encrypt dwBufLen, // Length of secret key &pbEncryptedData, // Encrypted data will be stored here &cbEncryptedData, // Length of this buffer pfnAlloc, // mem allocator pfnFree, // mem deallocator 0); // not specifying keylength } if (fOk) { // // Store the encrypted key in the registry so we can use it later // for decryption // HKEY hKeyCm; // // Try to open the key for writing // LONG lRes = RegOpenKeyEx(HKEY_CURRENT_USER, pszUserKey, 0, KEY_SET_VALUE , &hKeyCm); // // If we can't open it the key may not be there, try to create it. // if (ERROR_SUCCESS != lRes) { DWORD dwDisposition; lRes = RegCreateKeyEx(HKEY_CURRENT_USER, pszUserKey, 0, TEXT(""), REG_OPTION_NON_VOLATILE, KEY_SET_VALUE, NULL, &hKeyCm, &dwDisposition); } // // On success, update the value, then close // if (ERROR_SUCCESS == lRes) { if (dwEncryptionType & CMSECURE_ET_USE_SECOND_RND_KEY) { lRes = RegSetValueEx(hKeyCm, c_pszCmRegKeyEncryptedInternetPasswordKey, NULL, REG_BINARY, pbEncryptedData, cbEncryptedData); } else { lRes = RegSetValueEx(hKeyCm, c_pszCmRegKeyEncryptedPasswordKey, NULL, REG_BINARY, pbEncryptedData, cbEncryptedData); } RegCloseKey(hKeyCm); if (ERROR_SUCCESS == lRes) { fReturn = TRUE; *pfMoreSecure = TRUE; } } } } } } // // Check if we need to default to the old key // if (FALSE == fReturn) { GenerateKeyString(pszBuf, dwBufLen); fReturn = TRUE; } if (pfnFree) { pfnFree(pbEncryptedData); } else { HeapFree(GetProcessHeap(), 0, pbEncryptedData); } ZeroMemory((LPVOID)szTempKeyStr, sizeof(szTempKeyStr)); return fReturn; } //+--------------------------------------------------------------------------- // // Function: GetKeyString // // Synopsis: Gets the key that is used to create a session key for // decrypting the password. Reads the encrypted key from the // registry. Get the key used for decrypting by calling GetCurrentKey. // Decrypts the data and returns the key. If anything fails we // default to using a hardcoded key by calling GenerateKeyString. // // Arguments: pszBuf The buffer // pdwBufLen pointer to the size of the buffer in # of chars. // dwEncryptionType - selects which reg key to use // pfnAlloc memory allocator(if NULL, then the default is used. // Win32 - HeapAlloc(GetProcessHeap(), ...) // pfnFree memory deallocator(if NULL, then the default is used. // Win32 - HeapFree(GetProcessHeap(), ...) // pszUserKey reg key used to store the encrypted key, if not // provided (NULL), it will default to GenerateKeyString // pfMoreSecure TRUE if we are using the randomly generated key // FALSE if using the hardcoded key // // Returns: TRUE if success // FALSE if failure // // History: 03/14/2001 tomkel Created // //---------------------------------------------------------------------------- static BOOL GetKeyString(IN OUT LPTSTR pszBuf, IN DWORD *pdwBufLen, IN DWORD dwEncryptionType, IN PFN_CMSECUREALLOC pfnAlloc, IN PFN_CMSECUREFREE pfnFree, IN LPSTR pszUserKey, OUT BOOL *pfMoreSecure) { BOOL fReturn = FALSE; BOOL fFuncRet = FALSE; HKEY hKeyCm; TCHAR szTempKeyStr[MAX_KEY_STRING_LEN]={0}; DWORD dwRet = 0; DWORD cbEncryptedData = 0; PBYTE pbEncryptedData = NULL; PBYTE pbData = NULL; DWORD dwDataBufferLength = 0; // // Don't Check for pszUserKey here // if (NULL == pszBuf || NULL == pdwBufLen || NULL == pfMoreSecure) { return fReturn; } *pfMoreSecure = FALSE; // // If we have the user key then we are using the randomly generated key. // Try to get it from the reg. // if (pszUserKey) { // // Try to open the key for writing // LONG lRes = RegOpenKeyEx(HKEY_CURRENT_USER, pszUserKey, 0, KEY_READ , &hKeyCm); // // On success, read the value, then close // if (ERROR_SUCCESS == lRes) { DWORD dwType = REG_BINARY; // // Get the size first // if (dwEncryptionType & CMSECURE_ET_USE_SECOND_RND_KEY) { lRes = RegQueryValueEx(hKeyCm, c_pszCmRegKeyEncryptedInternetPasswordKey, NULL, &dwType, NULL, &cbEncryptedData); } else { lRes = RegQueryValueEx(hKeyCm, c_pszCmRegKeyEncryptedPasswordKey, NULL, &dwType, NULL, &cbEncryptedData); } // // Alloc the appropriate sized buffer. Need to add a space for null, // otherwise decrypt doesn't work. // if (pfnAlloc) { pbEncryptedData = (PBYTE)pfnAlloc(cbEncryptedData + sizeof(TCHAR)); } else { pbEncryptedData = (PBYTE)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, cbEncryptedData + sizeof(TCHAR)); } if (pbEncryptedData) { if (dwEncryptionType & CMSECURE_ET_USE_SECOND_RND_KEY) { lRes = RegQueryValueEx(hKeyCm, c_pszCmRegKeyEncryptedInternetPasswordKey, NULL, &dwType, pbEncryptedData, &cbEncryptedData); } else { lRes = RegQueryValueEx(hKeyCm, c_pszCmRegKeyEncryptedPasswordKey, NULL, &dwType, pbEncryptedData, &cbEncryptedData); } } RegCloseKey(hKeyCm); // // If we find the value decrypt it, otherwise we fall through and use the default key // if (ERROR_SUCCESS == lRes && pbEncryptedData) { // // Decrypt it using the machine specific key // fFuncRet = GetCurrentKey(szTempKeyStr, MAX_KEY_STRING_LEN, pfnAlloc, pfnFree); if (fFuncRet) { if (!g_pCryptFnc) { g_pCryptFnc = InitCryptoApi(); } if (g_pCryptFnc) { dwRet = g_pCryptFnc->DecryptDataWithKey( szTempKeyStr, // Key pbEncryptedData, // Encrypted data cbEncryptedData, // Length of encrypted data &pbData, // decrypted data &dwDataBufferLength, // Length of decrypted data pfnAlloc, // mem allocator pfnFree, // mem deallocator 0); // not specifying keylength if (ERROR_SUCCESS == dwRet) { fReturn = TRUE; *pfMoreSecure = TRUE; // Using the random key } } } } } } if (fReturn) { DWORD dwLen = *pdwBufLen; if (dwDataBufferLength < *pdwBufLen) { dwLen = dwDataBufferLength; } // // Copy into out param // CopyMemory((LPVOID)pszBuf, pbData, dwLen); } else { GenerateKeyString(pszBuf, *pdwBufLen); fReturn = TRUE; } if (pfnFree) { pfnFree(pbEncryptedData); pfnFree(pbData); } else { HeapFree(GetProcessHeap(), 0, pbEncryptedData); HeapFree(GetProcessHeap(), 0, pbData); } ZeroMemory((LPVOID)szTempKeyStr, sizeof(szTempKeyStr)); return fReturn; } //+--------------------------------------------------------------------------- // // Function: GetCurrentKey // // Synopsis: Creates a machine dependent key by using the Hard Drive's // serial number. This serial number is then used to fil lup the // output buffer. If the hard drive is replaced then this of course // will not generate the same serial number. // // Arguments: szTempKeyStr The buffer // dwTempKeyStrMaxLen max length of the buffer in # of chars. // pfnAlloc memory allocator(if NULL, then the default is used. // Win32 - HeapAlloc(GetProcessHeap(), ...) // pfnFree memory deallocator(if NULL, then the default is used. // Win32 - HeapFree(GetProcessHeap(), ...) // // Returns: TRUE if success // FALSE if failure // // History: 03/14/2001 tomkel Created // //---------------------------------------------------------------------------- static BOOL GetCurrentKey(PTCHAR szTempKeyStr, DWORD dwTempKeyStrMaxLen, IN PFN_CMSECUREALLOC pfnAlloc, IN PFN_CMSECUREFREE pfnFree) { BOOL fFuncRet = FALSE; DWORD dwVolumeSerialNumber = 0; DWORD dwMaxComponentLen = 0; DWORD dwFileSysFlags = 0; LPTSTR pszSerialNum = NULL; if (NULL == szTempKeyStr) { return fFuncRet; } // // Lets generate the key from the HD serial number. This means that encrypted // passwords and keys will only be valid on this machine. If the user replaces // the drive, then the decryption will fail. // fFuncRet = GetVolumeInformation(NULL, NULL, 0, &dwVolumeSerialNumber, &dwMaxComponentLen, &dwFileSysFlags, NULL, 0); if (fFuncRet) { DWORD dwLen = 0; // // Make sure we have a buffer large enough to hold the value. // Allocate a string based on the number of bits, thus a decimal number // will always fit. Maybe an exaggeration, but the length isn't hardcoded. // if (pfnAlloc) { pszSerialNum = (LPTSTR)pfnAlloc(sizeof(dwVolumeSerialNumber)*8*sizeof(TCHAR)); } else { pszSerialNum = (LPTSTR)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(dwVolumeSerialNumber)*8*sizeof(TCHAR)); } if (pszSerialNum) { DWORD dwSNLen = 0; wsprintf(pszSerialNum, TEXT("%u"), dwVolumeSerialNumber); // // See how many times the serial number string will fit into our buffer // Need to check the length due to PREFIX. If the length is 0, just return. // dwSNLen = lstrlen(pszSerialNum); if (dwSNLen) { dwLen = (dwTempKeyStrMaxLen - 1) / dwSNLen; } else { fFuncRet = FALSE; goto done; } if (0 < dwLen) { DWORD i = 0; lstrcpy(szTempKeyStr, pszSerialNum); // // Fill up the buffer. Start at 1 because we already copied the first // serial number into the buffer // for (i = 1; i