/*==LICENSE==* CyanWorlds.com Engine - MMOG client, server and tools Copyright (C) 2011 Cyan Worlds, Inc. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . You can contact Cyan Worlds, Inc. by email legal@cyan.com or by snail mail at: Cyan Worlds, Inc. 14617 N Newport Hwy Mead, WA 99021 *==LICENSE==*/ /***************************************************************************** * * $/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtCrypt.cpp * ***/ #include "../Pch.h" #pragma hdrstop #include #include #include /***************************************************************************** * * Opaque types * ***/ struct CryptKey { ECryptAlgorithm algorithm; void * handle; }; /***************************************************************************** * * Private * ***/ namespace Crypt { ShaDigest s_shaSeed; /***************************************************************************** * * Internal functions * ***/ //============================================================================ void Md5Process ( void * dest, unsigned sourceCount, const unsigned sourceBytes[], const void * sourcePtrs[] ) { // initialize digest MD5_CTX md5; MD5_Init(&md5); // hash data streams for (unsigned index = 0; index < sourceCount; ++index) MD5_Update(&md5, sourcePtrs[index], sourceBytes[index]); // complete hashing MD5_Final((unsigned char *)dest, &md5); } //============================================================================ void ShaProcess ( void * dest, unsigned sourceCount, const unsigned sourceBytes[], const void * sourcePtrs[] ) { // initialize digest SHA_CTX sha; SHA_Init(&sha); // hash data streams for (unsigned index = 0; index < sourceCount; ++index) SHA_Update(&sha, sourcePtrs[index], sourceBytes[index]); // complete hashing SHA_Final((unsigned char *)dest, &sha); } //============================================================================ void Sha1Process ( void * dest, unsigned sourceCount, const unsigned sourceBytes[], const void * sourcePtrs[] ) { // initialize digest SHA_CTX sha; SHA1_Init(&sha); // hash data streams for (unsigned index = 0; index < sourceCount; ++index) SHA1_Update(&sha, sourcePtrs[index], sourceBytes[index]); // complete hashing SHA1_Final((unsigned char *)dest, &sha); } /***************************************************************************** * * RC4 * ***/ //============================================================================ static void Rc4Codec ( CryptKey * key, bool encrypt, ARRAY(byte) * dest, unsigned sourceBytes, const void * sourceData ) { // RC4 uses the same algorithm to both encrypt and decrypt dest->SetCount(sourceBytes); RC4((RC4_KEY *)key->handle, sourceBytes, (const unsigned char *)sourceData, dest->Ptr()); } //============================================================================ static void Rc4Codec ( CryptKey * key, bool encrypt, unsigned bytes, void * data ) { // RC4 uses the same algorithm to both encrypt and decrypt byte * temp = ALLOCA(byte, bytes); RC4((RC4_KEY *)key->handle, bytes, (const unsigned char *)data, temp); MemCopy(data, temp, bytes); } } using namespace Crypt; /***************************************************************************** * * Exports * ***/ //============================================================================ void CryptDigest ( ECryptAlgorithm algorithm, void * dest, // must be sized to the algorithm's digest size const unsigned sourceBytes, const void * sourceData ) { CryptDigest( algorithm, dest, 1, &sourceBytes, &sourceData ); } //============================================================================ void CryptDigest ( ECryptAlgorithm algorithm, void * dest, // must be sized to the algorithm's digest size unsigned sourceCount, const unsigned sourceBytes[], // [sourceCount] const void * sourcePtrs[] // [sourceCount] ) { switch (algorithm) { case kCryptMd5: Md5Process(dest, sourceCount, sourceBytes, sourcePtrs); break; case kCryptSha: ShaProcess(dest, sourceCount, sourceBytes, sourcePtrs); break; case kCryptSha1: Sha1Process(dest, sourceCount, sourceBytes, sourcePtrs); break; DEFAULT_FATAL(algorithm); } } //============================================================================ CryptKey * CryptKeyCreate ( ECryptAlgorithm algorithm, unsigned bytes, const void * data ) { CryptKey * key = nil; switch (algorithm) { case kCryptRc4: { RC4_KEY * rc4 = NEW(RC4_KEY); RC4_set_key(rc4, bytes, (const unsigned char *)data); key = NEW(CryptKey); key->algorithm = kCryptRc4; key->handle = rc4; } break; case kCryptRsa: // Not implemented; fall-thru to FATAL // break; DEFAULT_FATAL(algorithm); } return key; } //============================================================================ void CryptKeyClose ( CryptKey * key ) { if (!key) return; DEL(key->handle); DEL(key); } //============================================================================ unsigned CryptKeyGetBlockSize ( CryptKey * key ) { switch (key->algorithm) { case kCryptRc4: { return 1; } break; case kCryptRsa: // Not implemented; fall-thru to FATAL // return RsaGetBlockSize(key); DEFAULT_FATAL(algorithm); } } //============================================================================ void CryptCreateRandomSeed ( unsigned bytes, byte * data ) { COMPILER_ASSERT(SHA_DIGEST_LENGTH == 20); // Combine seed with input data { unsigned seedIndex = 0; unsigned dataIndex = 0; unsigned cur = 0; unsigned end = max(bytes, sizeof(s_shaSeed)); for (; cur < end; ++cur) { ((byte *) &s_shaSeed)[seedIndex] ^= data[dataIndex]; if (++seedIndex >= sizeof(s_shaSeed)) seedIndex = 0; if (++dataIndex >= bytes) dataIndex = 0; } s_shaSeed.data[2] ^= (dword) &bytes; s_shaSeed.data[3] ^= (dword) bytes; s_shaSeed.data[4] ^= (dword) data; } // Hash seed ShaDigest digest; CryptDigest(kCryptSha, &digest, sizeof(s_shaSeed), &s_shaSeed); // Update output with contents of digest { unsigned src = 0; unsigned dst = 0; unsigned cur = 0; unsigned end = max(bytes, sizeof(digest)); for (; cur < end; ++cur) { data[dst] ^= ((const byte *) &digest)[src]; if (++src >= sizeof(digest)) src = 0; if (++dst >= bytes) dst = 0; } } // Combine seed with digest s_shaSeed.data[0] ^= digest.data[0]; s_shaSeed.data[1] ^= digest.data[1]; s_shaSeed.data[2] ^= digest.data[2]; s_shaSeed.data[3] ^= digest.data[3]; s_shaSeed.data[4] ^= digest.data[4]; } //============================================================================ void CryptHashPassword ( const wchar username[], const wchar password[], ShaDigest * namePassHash ) { unsigned passlen = StrLen(password); unsigned userlen = StrLen(username); wchar * buffer = ALLOCA(wchar, passlen + userlen); StrCopy(buffer, password, passlen); StrCopy(buffer + passlen, username, userlen); StrLower(buffer + passlen); // lowercase the username CryptDigest( kCryptSha, namePassHash, (userlen + passlen) * sizeof(buffer[0]), buffer ); } //============================================================================ void CryptHashPasswordChallenge ( unsigned clientChallenge, unsigned serverChallenge, const ShaDigest & namePassHash, ShaDigest * challengeHash ) { #include struct { dword clientChallenge; dword serverChallenge; ShaDigest namePassHash; } buffer; #include buffer.clientChallenge = clientChallenge; buffer.serverChallenge = serverChallenge; buffer.namePassHash = namePassHash; CryptDigest(kCryptSha, challengeHash, sizeof(buffer), &buffer); } //============================================================================ void CryptCreateFastWeakChallenge ( unsigned * challenge, unsigned val1, unsigned val2 ) { s_shaSeed.data[0] ^= TimeGetMs(); // looping time s_shaSeed.data[0] ^= _rotl(s_shaSeed.data[0], 1); s_shaSeed.data[0] ^= (unsigned) TimeGetTime(); // global time s_shaSeed.data[0] ^= _rotl(s_shaSeed.data[0], 1); s_shaSeed.data[0] ^= *challenge; // unknown s_shaSeed.data[0] ^= _rotl(s_shaSeed.data[0], 1); s_shaSeed.data[0] ^= (unsigned) challenge; // variable address s_shaSeed.data[0] ^= _rotl(s_shaSeed.data[0], 1); s_shaSeed.data[0] ^= val1; s_shaSeed.data[0] ^= _rotl(s_shaSeed.data[0], 1); s_shaSeed.data[0] ^= val2; *challenge = s_shaSeed.data[0]; } //============================================================================ void CryptEncrypt ( CryptKey * key, ARRAY(byte) * dest, unsigned sourceBytes, const void * sourceData ) { switch (key->algorithm) { case kCryptRc4: { Rc4Codec(key, true, dest, sourceBytes, sourceData); } break; case kCryptRsa: // Not implemented; fall-thru to FATAL // RsaCodec(key, true, dest, sourceBytes, sourceData); // break; DEFAULT_FATAL(key->algorithm); } } //============================================================================ void CryptEncrypt ( CryptKey * key, unsigned bytes, void * data ) { ASSERT(1 == CryptKeyGetBlockSize(key)); switch (key->algorithm) { case kCryptRc4: { Rc4Codec(key, true, bytes, data); } break; case kCryptRsa: // Not implemented; fall-thru to FATAL // RsaCodec(key, true, dest, sourceBytes, sourceData); // break; DEFAULT_FATAL(key->algorithm); } } //============================================================================ void CryptDecrypt ( CryptKey * key, ARRAY(byte) * dest, unsigned sourceBytes, const void * sourceData ) { switch (key->algorithm) { case kCryptRc4: { Rc4Codec(key, false, dest, sourceBytes, sourceData); } break; case kCryptRsa: // Not implemented; fall-thru to FATAL // RsaCodec(key, false, dest, sourceBytes, sourceData); // break; DEFAULT_FATAL(key->algorithm); } } //============================================================================ void CryptDecrypt ( CryptKey * key, unsigned bytes, void * data ) { ASSERT(1 == CryptKeyGetBlockSize(key)); switch (key->algorithm) { case kCryptRc4: { Rc4Codec(key, false, bytes, data); } break; case kCryptRsa: // Not implemented; fall-thru to FATAL // RsaCodec(key, false, dest, sourceBytes, sourceData); // break; DEFAULT_FATAL(key->algorithm); } }