2025-04-27 07:49:33 -04:00

580 lines
14 KiB
C++

// CoCrypt.cpp: implementation of the CCoCrypt class.
//
//////////////////////////////////////////////////////////////////////
#include "stdafx.h"
#include "CoCrypt.h"
#include "hmac.h"
#include "BstrDebug.h"
#include <winsock2.h> // ntohl, htonl
#include <time.h>
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
CBinHex CCoCrypt::m_binhex;
CCoCrypt::CCoCrypt() : m_ok(FALSE), m_ivecCtr(0)
{
//Set seed
srand((unsigned)time(NULL));
// randomize the init state
// Total 62 characters: 0 - 9, a - z, A - Z
for (int iii=0; iii < 8; iii++)
{
int idx = rand() % 62;
if (idx <= 9)
m_nextIvec[iii] = '0' + idx;
else if (idx <= 35)
m_nextIvec[iii] = 'a' + idx - 10;
else
m_nextIvec[iii] = 'A' + idx - 36;
}
}
CCoCrypt::~CCoCrypt()
{
}
// Decrypte an encrypted key value without base64 encoding
BOOL CCoCrypt::DecryptKey(BYTE *rawData, UINT cbData, BSTR *pDecrypted)
{
// we are only dealing with partner key here
// must be kv + ivec + bh(hmac) long at LEAST
if (cbData < 25 || cbData > 2048)
{
if (pDecrypted) *pDecrypted = NULL;
return FALSE;
}
return decrypt2048((LPWSTR)(&rawData[1]), cbData, pDecrypted, FALSE);
}
BOOL CCoCrypt::Decrypt(LPWSTR rawData, UINT dataSize,
BSTR *pUnencrypted)
{
// So it goes like this see:
// 1. De binhex
// 2. Decrypt
// 3. Check hmac
// 4. return data
if (dataSize < 23) // must be kv + ivec + bh(hmac) long at LEAST
{
if (pUnencrypted) *pUnencrypted = NULL;
return FALSE;
}
if (dataSize > 2048)
return decryptDynamic(rawData+1, dataSize-sizeof(WCHAR), pUnencrypted);
else
return decrypt2048(rawData+1, dataSize-sizeof(WCHAR), pUnencrypted);
}
BOOL CCoCrypt::decrypt2048(LPWSTR rawData, UINT dataSize,
BSTR *pUnencrypted, BOOL bEncoded)
{
unsigned char buf[2048];
unsigned char ivec[9];
ULONG lSize = 2048, i;
unsigned char pad;
// XXXXXXXX THIS CODE APPEARS BELOW IN DYNAMIC VERSION, CHANGE BOTH!!!
// FOR SPEED, WE DIDN'T DO FN CALLS OR ALWAYS DO DYNAMIC ALLOC
if(bEncoded)
m_binhex.PartFromWideBase64(rawData+9, buf, &lSize);
else
{
lSize = dataSize - 10;
memcpy(buf, (BYTE*)rawData+9, lSize);
}
if(bEncoded)
{
for (i = 0; i < 9; i++)
ivec[i] = (unsigned char) rawData[i];
}
else
{
memcpy(ivec, rawData, 9);
}
pad = ivec[8];
// Now lsize holds the # of bytes outputted, which after hmac should be %8=0
if ((lSize-10) % 8 || lSize<=10)
{
if (pUnencrypted) *pUnencrypted = NULL;
return FALSE;
}
#ifdef UNIX
des_ede3_cbc_encrypt((C_Block*)(buf+10),(C_Block*)(buf+10), lSize,
ks1, ks2, ks3, (C_Block*)rawData, DES_DECRYPT);
#else
for (i = 0; i < lSize-10; i+=8)
{
CBC(tripledes, 8, buf+10+i,buf+10+i, &ks, DECRYPT, (BYTE*)ivec);
}
#endif
// Padding must be >0 and <8
//if (rawData[8]-65 > 7 || rawData[8] < 65)
if((pad - 65) > 7 || pad < 65)
{
if (pUnencrypted) *pUnencrypted = NULL;
return FALSE;
}
// Now check hmac
unsigned char hmac[10];
hmac_sha(m_keyMaterial, 24, buf+10, lSize-10, hmac, 10);
if (memcmp(hmac, buf, 10) != 0)
{
if (pUnencrypted) *pUnencrypted = NULL;
return FALSE;
}
// ok, now make our BSTR blob
//*pUnencrypted = ALLOC_AND_GIVEAWAY_BSTR_BYTE_LEN((char*) (buf+10),lSize-10-(rawData[8]-65));
*pUnencrypted = ALLOC_AND_GIVEAWAY_BSTR_BYTE_LEN((char*) (buf+10),lSize-10-(pad-65));
return TRUE;
}
BOOL CCoCrypt::decryptDynamic(LPWSTR rawData, UINT dataSize,
BSTR *pUnencrypted)
{
unsigned char *buf = new unsigned char[dataSize];
if (!buf)
return FALSE;
// XXXXXXXX THIS CODE APPEARS ABOVE. MODIFY BOTH!
// FOR SPEED, WE DIDN'T DO FN CALLS OR ALWAYS DO DYNAMIC ALLOC
unsigned char ivec[8];
ULONG lSize = dataSize, i;
m_binhex.PartFromWideBase64(rawData+9, buf, &lSize);
for (i = 0; i < 8; i++)
ivec[i] = (unsigned char) rawData[i];
// Now lsize holds the # of bytes outputted, which after hmac should be %8=0
if ((lSize-10) % 8 || lSize <= 10)
{
if (pUnencrypted) *pUnencrypted = NULL;
delete[] buf;
return FALSE;
}
#ifdef UNIX
des_ede3_cbc_encrypt((C_Block*)(buf+10),(C_Block*)(buf+10), lSize,
ks1, ks2, ks3, (C_Block*)rawData, DES_DECRYPT);
#else
for (i = 0; i < lSize-10; i+=8)
{
CBC(tripledes, 8, buf+10+i,buf+10+i, &ks, DECRYPT, (BYTE*)ivec);
}
#endif
// Padding must be >0 and <8
if (rawData[8]-65 > 7 || rawData[8] < 65)
{
if (pUnencrypted) *pUnencrypted = NULL;
delete[] buf;
return FALSE;
}
// Now check hmac
unsigned char hmac[10];
hmac_sha(m_keyMaterial, 24, buf+10, lSize-10, hmac, 10);
if (memcmp(hmac, buf, 10) != 0)
{
if (pUnencrypted) *pUnencrypted = NULL;
delete[] buf;
return FALSE;
}
// ok, now make our BSTR blob
*pUnencrypted = ALLOC_AND_GIVEAWAY_BSTR_BYTE_LEN((char*) (buf+10),lSize-10-(rawData[8]-65));
// XXX END COPY
delete[] buf;
return TRUE;
}
// Encrypte a key value without base64 encoding
// bstrIn should be pack with SysAllocStringByteLen()
void CCoCrypt::EncryptKey(int nKeyVer, BSTR bstrIn, BYTE *pEncrypted, UINT &cbOut)
{
// Find out how big the encrypted blob needs to be:
// The final pack is:
// <KeyVersion><IVEC><PADCOUNT>BINHEX(HMAC+3DES(DATA+PAD))
//
// So, we're concerned with the size of HMAC+3DES(DATA+PAD)
BSTR bstrOut;
int nSize = ::SysStringByteLen(bstrIn);
int outLen = nSize; // DATA
if (outLen % 8)
{
outLen += (8 - (outLen%8)); // + PAD, if necessary
}
// construct the prepended buffer
char ivec[9];
// Select an IVEC, and leave a byte for padding count
memcpy(ivec, m_nextIvec, 8);
// Fix for the next ivec
char lc = m_nextIvec[m_ivecCtr];
short ic = m_ivecCtr;
if (lc == '9')
{
m_ivecCtr = (ic+1)%8;
m_nextIvec[ic] = 'A';
}
else
{
if (lc == 'Z')
m_nextIvec[ic] = 'a';
else if (lc == 'z')
m_nextIvec[ic] = '0';
else
m_nextIvec[ic] = lc+1;
}
if (outLen > 2038) // 10 for HMAC
{
cbOut = 0;
*pEncrypted = NULL;
}
encrypt2048(ivec, outLen, nKeyVer, (LPSTR)bstrIn, nSize, &bstrOut, FALSE);
memcpy(pEncrypted, &bstrOut[0], cbOut = ::SysStringByteLen(bstrOut));
::SysFreeString(bstrOut);
}
void CCoCrypt::Encrypt(int keyVersion,
LPSTR rawData, UINT dataSize,
BSTR *pEncrypted)
{
// Find out how big the encrypted blob needs to be:
// The final pack is:
// <KeyVersion><IVEC><PADCOUNT>BINHEX(HMAC+3DES(DATA+PAD))
//
// So, we're concerned with the size of HMAC+3DES(DATA+PAD)
// because BinHex will handle the rest
int outLen = dataSize; // DATA
if (outLen % 8)
{
outLen += (8 - (outLen%8)); // + PAD, if necessary
}
// construct the prepended buffer
char ivec[9];
// Select an IVEC, and leave a byte for padding count
memcpy(ivec, m_nextIvec, 8);
// Fix for the next ivec
char lc = m_nextIvec[m_ivecCtr];
short ic = m_ivecCtr;
if (lc == '9')
{
m_ivecCtr = (ic+1)%8;
m_nextIvec[ic] = 'A';
}
else
{
if (lc == 'Z')
m_nextIvec[ic] = 'a';
else if (lc == 'z')
m_nextIvec[ic] = '0';
else
m_nextIvec[ic] = lc+1;
}
if (outLen > 2038) // 10 for HMAC
encryptDynamic(ivec, outLen, keyVersion, rawData, dataSize, pEncrypted);
else
encrypt2048(ivec, outLen, keyVersion, rawData, dataSize, pEncrypted);
}
void CCoCrypt::encrypt2048(char ivec[9], int blockSize, int keyVersion,
LPSTR rawData, UINT dataSize,
BSTR *pEncrypted, BOOL bEncode)
{
unsigned char buf[2048];
// XXXXXXXX THIS CODE APPEARS BELOW IN DYNAMIC VERSION, CHANGE BOTH!!!
// FOR SPEED, WE DIDN'T DO FN CALLS OR ALWAYS DO DYNAMIC ALLOC
// Compute HMAC+3DES(DATA+PAD)
int padding = blockSize - dataSize;
ivec[8] = (char) padding+65;
char ivec2[8];
memcpy(ivec2,ivec,8);
memset(buf, 0, 10); // HMAC
memcpy(buf+10, rawData, dataSize); // data
//randomize padding
for (int iii=0; iii < padding; iii++)
{
*(buf+10+dataSize+iii) = (unsigned char) rand();
}
// Compute HMAC
hmac_sha(m_keyMaterial, 24, buf+10, dataSize+padding, buf, 10);
#ifdef UNIX
des_ede3_cbc_encrypt((C_Block*)(buf+10),(C_Block*)(buf+10), blockSize,
ks1, ks2, ks3, (C_Block*)ivec2, DES_ENCRYPT);
#else
for (int i = 0; i < blockSize; i+=8)
{
CBC(tripledes, 8, buf+10+i, buf+10+i, &ks, ENCRYPT, (BYTE*)ivec2);
}
#endif
// Now we've got a buffer of blockSize ready to be binhexed, and have the key
// version prepended
keyVersion = keyVersion % 36; // 0 - 9 & A - Z
char v = (char) ((keyVersion > 9) ? (55+keyVersion) : (48+keyVersion));
if(bEncode)
{
m_binhex.ToBase64(buf, blockSize+10, v, ivec, pEncrypted);
}
else
{
BYTE tb[2048];
tb[0] = v;
memcpy(&tb[1], ivec, 9);
memcpy(&tb[10], buf, blockSize + 10);
*pEncrypted = ::SysAllocStringByteLen((LPCSTR)tb, blockSize + 20);
}
}
void CCoCrypt::encryptDynamic(char ivec[9], int blockSize, int keyVersion,
LPSTR rawData, UINT dataSize,
BSTR *pEncrypted)
{
unsigned char *buf = new unsigned char[blockSize+18];
if (!buf)
{
*pEncrypted= NULL;
return;
}
// XXXXXXXX THIS CODE IS COPIED FROM ABOVE, CHANGE BOTH!!!
// FOR SPEED, WE DIDN'T DO FN CALLS OR ALWAYS DO DYNAMIC ALLOC
// Compute HMAC+3DES(DATA+PAD)
int padding = blockSize - dataSize;
ivec[8] = (char) padding+65;
char ivec2[8];
memcpy(ivec2,ivec,8);
memset(buf, 0, 10); // HMAC
memcpy(buf+10, rawData, dataSize); // data
//randomize padding
for (int iii=0; iii < padding; iii++)
{
*(buf+10+dataSize+iii) = (unsigned char) rand();
}
// Compute HMAC
hmac_sha(m_keyMaterial, 24, buf+10, dataSize+padding, buf, 10);
#ifdef UNIX
des_ede3_cbc_encrypt((C_Block*)(buf+10),(C_Block*)(buf+10), blockSize,
ks1, ks2, ks3, (C_Block*)ivec2, DES_ENCRYPT);
#else
for (int i = 0; i < blockSize; i+=8)
{
CBC(tripledes, 8, buf+10+i, buf+10+i, &ks, ENCRYPT, (BYTE*)ivec2);
}
#endif
// Now we've got a buffer of blockSize ready to be binhexed, and have the key
// version prepended
keyVersion = keyVersion % 36; // 0 - 9 & A - Z
char v = (char) ((keyVersion > 9) ?
(55+keyVersion) : (48+keyVersion));
m_binhex.ToBase64(buf, blockSize+10, v, ivec, pEncrypted);
// XXX END COPY
delete[] buf;
}
void CCoCrypt::setKeyMaterial(BSTR newVal)
{
if (SysStringByteLen(newVal) != 24)
{
m_ok = FALSE;
return;
}
memcpy(m_keyMaterial, (LPSTR)newVal, 24);
#ifdef UNIX
int ok;
ok = des_key_sched((C_Block*)(m_keyMaterial), ks1) ||
des_key_sched((C_Block*)(m_keyMaterial+8), ks2) ||
des_key_sched((C_Block*)(m_keyMaterial+16), ks3);
m_ok = (ok == 0);
#else
tripledes3key(&ks, (BYTE*) m_keyMaterial);
m_ok = TRUE;
#endif
}
unsigned char *CCoCrypt::getKeyMaterial(DWORD *pdwLen)
{
if (pdwLen)
*pdwLen = 24;
return m_keyMaterial;
}
int CCoCrypt::getKeyVersion(BSTR encrypted)
{
char c = (char) encrypted[0];
if (isdigit(c))
return (c-48);
if(isalpha(c)) // Key version can be 0 - 9 & A - Z (36)
return (toupper(c)-65+10);
return -1;
}
int CCoCrypt::getKeyVersion(BYTE *encrypted)
{
char c = (char) encrypted[0];
if (isdigit(c))
return (c-48);
if(isalpha(c)) // Key version can be 0 - 9 & A - Z (36)
return (toupper(c)-65+10);
return -1;
}
void CCoCrypt::CryptMemberID(BOOL encrypt, long &memberId, long &domainId, long puidScope)
{
unsigned char buf[256];
int blockSize = 8;
char ivec2[8];
unsigned char hmac[24];
int operation;
long nlPUIDScope = htonl(puidScope); // Make sure our hash is system independent
#ifdef UNIX
des_key_schedule ksHMAC1, ksHMAC2, ksHMAC3;
#else
DES3TABLE ksHMAC;
#endif
// Initialize HMAC buffer
memset(hmac, 0, 24);
// Create the HMAC of the partner ID using the master key passed in
hmac_sha( m_keyMaterial, 24, (unsigned char *)&nlPUIDScope, 4, hmac, 20);
// Create the Triple DES table from the HMAC
#ifdef UNIX
des_key_sched((C_Block*)(hmac), ksHMAC1);
des_key_sched((C_Block*)(hmac+8), ksHMAC2);
des_key_sched((C_Block*)(hmac+16), ksHMAC3);
#else
tripledes3key(&ksHMAC, (BYTE*) hmac);
#endif
// Init the initial vector to zero
memset(ivec2,0,8);
// Make sure member ID is system independent for encryption
if (encrypt)
{
memberId = htonl(memberId);
domainId = htonl(domainId);
}
// copy ID's into a buffer for crypting
memcpy(buf, &memberId, 4);
memcpy(buf + 4, &domainId, 4);
// Crypt the ID buffer with the Triple DES table
#ifdef UNIX
if (encrypt) {
operation = DES_ENCRYPT;
} else {
operation = DES_DECRYPT;
}
des_ede3_cbc_encrypt((C_Block*)(buf),(C_Block*)(buf), blockSize,
ksHMAC1, ksHMAC2, ksHMAC3, (C_Block*)ivec2, operation);
#else
if (encrypt) {
operation = ENCRYPT;
} else {
operation = DECRYPT;
}
for (int i = 0; i < blockSize; i+=8)
{
CBC(tripledes, 8, buf+i, buf+i, &ksHMAC, operation, (BYTE*)ivec2);
}
#endif
// Copy crypted data from buffer back into the ID variables
memcpy(&memberId, buf, 4);
memcpy(&domainId, buf+4, 4);
// If decrypting - transform back to host specific number
if (!encrypt)
{
memberId = ntohl(memberId);
domainId = ntohl(domainId);
}
}
void CCoCrypt::setWideMaterial(BSTR kvalue)
{
m_bstrWideMaterial = kvalue;
}
BSTR CCoCrypt::getWideMaterial()
{
return m_bstrWideMaterial;
}