You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

462 lines
12 KiB

/*==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 <http://www.gnu.org/licenses/>.
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 <openssl/md5.h>
#include <openssl/sha.h>
#include <openssl/rc4.h>
/*****************************************************************************
*
* 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 <pshpack1.h>
struct {
dword clientChallenge;
dword serverChallenge;
ShaDigest namePassHash;
} buffer;
#include <poppack.h>
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);
}
}