// CoCrypt.cpp: implementation of the CCoCrypt class. // ////////////////////////////////////////////////////////////////////// #include "stdafx.h" #include "CoCrypt.h" #include "hmac.h" #include "BstrDebug.h" #include // ntohl, htonl #include ////////////////////////////////////////////////////////////////////// // 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: // 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: // 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; }