diff --git a/engine/server/CMakeLists.txt b/engine/server/CMakeLists.txt index 09869e54..5440fbac 100644 --- a/engine/server/CMakeLists.txt +++ b/engine/server/CMakeLists.txt @@ -1,2 +1,3 @@ add_subdirectory(application) +add_subdirectory(library) diff --git a/engine/server/library/CMakeLists.txt b/engine/server/library/CMakeLists.txt new file mode 100644 index 00000000..2030f138 --- /dev/null +++ b/engine/server/library/CMakeLists.txt @@ -0,0 +1,2 @@ + +add_subdirectory(serverKeyShare) \ No newline at end of file diff --git a/engine/server/library/serverKeyShare/CMakeLists.txt b/engine/server/library/serverKeyShare/CMakeLists.txt new file mode 100644 index 00000000..d86cef83 --- /dev/null +++ b/engine/server/library/serverKeyShare/CMakeLists.txt @@ -0,0 +1,11 @@ +cmake_minimum_required(VERSION 2.8) + +project(serverKeyShare) + +if(WIN32) + add_definitions(/D_CRT_SECURE_NO_WARNINGS) +endif() + +include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include/public) + +add_subdirectory(src) diff --git a/engine/server/library/serverKeyShare/include/public/serverKeyShare/FirstServerKeyShare.h b/engine/server/library/serverKeyShare/include/public/serverKeyShare/FirstServerKeyShare.h new file mode 100644 index 00000000..ff737e8d --- /dev/null +++ b/engine/server/library/serverKeyShare/include/public/serverKeyShare/FirstServerKeyShare.h @@ -0,0 +1 @@ +#include "../../src/shared/FirstServerKeyShare.h" diff --git a/engine/server/library/serverKeyShare/include/public/serverKeyShare/KeyServer.h b/engine/server/library/serverKeyShare/include/public/serverKeyShare/KeyServer.h new file mode 100644 index 00000000..d92c99f3 --- /dev/null +++ b/engine/server/library/serverKeyShare/include/public/serverKeyShare/KeyServer.h @@ -0,0 +1 @@ +#include "../../src/shared/KeyServer.h" diff --git a/engine/server/library/serverKeyShare/include/public/serverKeyShare/KeyShare.h b/engine/server/library/serverKeyShare/include/public/serverKeyShare/KeyShare.h new file mode 100644 index 00000000..5fa1ec15 --- /dev/null +++ b/engine/server/library/serverKeyShare/include/public/serverKeyShare/KeyShare.h @@ -0,0 +1 @@ +#include "../../src/shared/KeyShare.h" diff --git a/engine/server/library/serverKeyShare/src/CMakeLists.txt b/engine/server/library/serverKeyShare/src/CMakeLists.txt new file mode 100644 index 00000000..f5867b21 --- /dev/null +++ b/engine/server/library/serverKeyShare/src/CMakeLists.txt @@ -0,0 +1,32 @@ + +set(SHARED_SOURCES + shared/FirstServerKeyShare.h + shared/KeyServer.cpp + shared/KeyServer.h + shared/KeyShare.cpp + shared/KeyShare.h +) + +if(WIN32) + set(PLATFORM_SOURCES + win32/FirstServerKeyShare.cpp + ) +else() + set(PLATFORM_SOURCES "") +endif() + +include_directories( + ${CMAKE_CURRENT_SOURCE_DIR}/shared + ${SWG_ENGINE_SOURCE_DIR}/shared/library/sharedDebug/include/public + ${SWG_ENGINE_SOURCE_DIR}/shared/library/sharedFoundation/include/public + ${SWG_ENGINE_SOURCE_DIR}/shared/library/sharedFoundationTypes/include/public + ${SWG_ENGINE_SOURCE_DIR}/shared/library/sharedMemoryManager/include/public + ${SWG_ENGINE_SOURCE_DIR}/shared/library/sharedRandom/include/public + ${SWG_EXTERNALS_SOURCE_DIR}/ours/library/archive/include + ${SWG_EXTERNALS_SOURCE_DIR}/ours/library/crypto/include +) + +add_library(serverKeyShare STATIC + ${SHARED_SOURCES} + ${PLATFORM_SOURCES} +) diff --git a/engine/server/library/serverKeyShare/src/shared/FirstServerKeyShare.h b/engine/server/library/serverKeyShare/src/shared/FirstServerKeyShare.h new file mode 100644 index 00000000..5875846a --- /dev/null +++ b/engine/server/library/serverKeyShare/src/shared/FirstServerKeyShare.h @@ -0,0 +1,22 @@ +// ====================================================================== +// +// FirstServerKeyShare.h +// copyright (c) 2001 Sony Online Entertainment +// +// ====================================================================== + +#ifndef INCLUDED_FirstServerKeyShare_H +#define INCLUDED_FirstServerKeyShare_H + +// ====================================================================== + +#include "sharedFoundation/FirstSharedFoundation.h" +#include "sharedDebug/FirstSharedDebug.h" +#include "sharedMemoryManager/FirstSharedMemoryManager.h" +#include "serverKeyShare/KeyServer.h" +#include "sharedRandom/RandomGenerator.h" + +// ====================================================================== + +#endif + diff --git a/engine/server/library/serverKeyShare/src/shared/KeyServer.cpp b/engine/server/library/serverKeyShare/src/shared/KeyServer.cpp new file mode 100644 index 00000000..aef212f2 --- /dev/null +++ b/engine/server/library/serverKeyShare/src/shared/KeyServer.cpp @@ -0,0 +1,66 @@ +// KeyServer.cpp +// copyright 2000 Verant Interactive +// Author: Justin Randall + + +//----------------------------------------------------------------------- + +#include "serverKeyShare/FirstServerKeyShare.h" +#include "sharedFoundation/Clock.h" +#include "serverKeyShare/KeyServer.h" +#include "sharedRandom/RandomGenerator.h" +#include + +//----------------------------------------------------------------------- + +KeyServer::KeyServer(const unsigned int newKeyCount, const real newUpdateRate) : +KeyShare(newKeyCount), +lastUpdateTime(CONST_REAL(0.0)), +randomNumberGenerator(0), +updateRate(newUpdateRate) +{ + randomNumberGenerator = new RandomGenerator(unsigned(time(0))); //lint !e732 //loss of sign ok. + NOT_NULL(randomNumberGenerator); + Key k; + for(unsigned int i = 0; i < getKeyCount(); i ++) + { + for(unsigned int j = 0; j < KeyShareConstants::keyLength; j ++) + { + k.value[j] = static_cast(randomNumberGenerator->random(255)); + } + setKey(k, i); + } +} + +//----------------------------------------------------------------------- + +KeyServer::~KeyServer() +{ + delete randomNumberGenerator; +} + +//----------------------------------------------------------------------- + +bool KeyServer::update(void) +{ + bool result = false; +#if 0 // disable key rotation + lastUpdateTime += Clock::frameTime(); + if(lastUpdateTime > updateRate) + { + lastUpdateTime = CONST_REAL(0.0); + KeyShare::Key k; + for(unsigned int j = 0; j < KeyShareConstants::keyLength; j ++) + { + k.value[j] = static_cast(randomNumberGenerator->random(255)); + } + pushKey(k); + result = true; + } +#endif // disable key rotation + + return result; +} + +//----------------------------------------------------------------------- + diff --git a/engine/server/library/serverKeyShare/src/shared/KeyServer.h b/engine/server/library/serverKeyShare/src/shared/KeyServer.h new file mode 100644 index 00000000..0b79a8c1 --- /dev/null +++ b/engine/server/library/serverKeyShare/src/shared/KeyServer.h @@ -0,0 +1,37 @@ +// KeyServer.h +// copyright 2000 Verant Interactive +// Author: Justin Randall + +#ifndef _KeyServer_H +#define _KeyServer_H + +//----------------------------------------------------------------------- + +#include "serverKeyShare/KeyShare.h" + +//----------------------------------------------------------------------- + +class RandomGenerator; + +//----------------------------------------------------------------------- + +class KeyServer : public KeyShare +{ +public: + explicit KeyServer (const unsigned int newKeyCount=2, const real newUpdateRate = CONST_REAL(60000.0)); + ~KeyServer (); + bool update(void); + +private: + real lastUpdateTime; + RandomGenerator * randomNumberGenerator; + real updateRate; + + KeyServer(const KeyServer&); + KeyServer& operator= (const KeyServer&); +}; + +//----------------------------------------------------------------------- + +#endif // _KeyServer_H + diff --git a/engine/server/library/serverKeyShare/src/shared/KeyShare.cpp b/engine/server/library/serverKeyShare/src/shared/KeyShare.cpp new file mode 100644 index 00000000..ef5ee7df --- /dev/null +++ b/engine/server/library/serverKeyShare/src/shared/KeyShare.cpp @@ -0,0 +1,425 @@ +// KeyShare.cpp +// copyright 2000 Verant Interactive +// Author: Justin Randall + + +//----------------------------------------------------------------------- + +#include "serverKeyShare/FirstServerKeyShare.h" +#include "MD5Hash.h" +#include "TwofishDecryptor.h" +#include "TwofishEncryptor.h" +#include "serverKeyShare/KeyShare.h" + +//----------------------------------------------------------------------- + +KeyShare::Token::Token(const unsigned char * const newCipherData, + const uint32 newDataLen, + const unsigned char newDigest[KeyShareConstants::keyLength], + const uint32 newCipherDataLen) : +cipherData(0), +cipherDataLen(newCipherDataLen), +dataLen(newDataLen) +{ + cipherData = new unsigned char[cipherDataLen]; + NOT_NULL(cipherData); + memcpy(cipherData, newCipherData, cipherDataLen); + memcpy(digest, newDigest, KeyShareConstants::keyLength); +} + +//----------------------------------------------------------------------- + +KeyShare::Token::Token(const Token & source) : +cipherData(0), +cipherDataLen(source.cipherDataLen), +dataLen(source.dataLen) +{ + cipherData = new unsigned char[cipherDataLen]; + NOT_NULL(cipherData); + memcpy(cipherData, source.cipherData, cipherDataLen); + memcpy(digest, source.digest, KeyShareConstants::keyLength); +} + +//----------------------------------------------------------------------- + +KeyShare::Token::Token(Archive::ReadIterator & source) : +cipherData(0), +cipherDataLen(0), +dataLen(0) +{ + Archive::get(source, cipherDataLen); + + // if the cipher data has been tampered with, don't unpack it. The vaidation will fail + if(cipherDataLen < 64) + { + Archive::get(source, dataLen); + cipherData = new unsigned char[cipherDataLen]; + unsigned int i; + for(i = 0; i < cipherDataLen; i ++) + { + Archive::get(source, cipherData[i]); + } + for(i = 0; i < KeyShareConstants::keyLength; i ++) + { + Archive::get(source, digest[i]); + } + } + else + { + cipherData = new unsigned char[64]; + cipherDataLen = 64; + dataLen = 0; + } +} + +//----------------------------------------------------------------------- + +KeyShare::Token::~Token() +{ + delete[] cipherData; +} + +//----------------------------------------------------------------------- + +const uint32 KeyShare::Token::getCipherDataLen(void) const +{ + return cipherDataLen; +} + +//----------------------------------------------------------------------- + +const unsigned char * const KeyShare::Token::getData(void) const +{ + return cipherData; +} + +//----------------------------------------------------------------------- + +const uint32 KeyShare::Token::getDataLen(void) const +{ + return dataLen; +} + +//----------------------------------------------------------------------- + +const unsigned char * const KeyShare::Token::getDigest(void) const +{ + return digest; +} + +//----------------------------------------------------------------------- + +void KeyShare::Token::pack(Archive::ByteStream & target) const +{ + Archive::put(target, cipherDataLen); + Archive::put(target, dataLen); + unsigned int i; + for(i = 0; i < cipherDataLen; i ++) + { + Archive::put(target, cipherData[i]); + } + for(i = 0; i < KeyShareConstants::keyLength; i ++) + { + Archive::put(target, digest[i]); + } +} + +//----------------------------------------------------------------------- + +KeyShare::KeyShare(const unsigned int newKeyCount) : +decryptors(0), +encryptors(0), +hasher(0), +keyCount(newKeyCount), +keys(0) +{ + keys = new Key[keyCount]; + decryptors = new Crypto::TwofishDecryptor *[keyCount]; + encryptors = new Crypto::TwofishEncryptor *[keyCount]; + + NOT_NULL(keys); + NOT_NULL(decryptors); + NOT_NULL(encryptors); + + for(unsigned int i = 0; i < keyCount; i ++) + { + decryptors[i] = 0; + encryptors[i] = 0; + } + hasher = new Crypto::MD5Hash; +} + +//----------------------------------------------------------------------- + +KeyShare::~KeyShare() +{ + delete[] keys; + for(unsigned int i = 0; i < keyCount; i ++) + { + delete decryptors[i]; + delete encryptors[i]; + } + + delete[] decryptors; + delete[] encryptors; + delete hasher; +} + +//----------------------------------------------------------------------- + +bool KeyShare::decipherToken(const KeyShare::Token & token, unsigned char * clearTextData, uint32 & dataLen) const +{ + bool result = false; + if(dataLen < token.getDataLen()) + { + dataLen = token.getDataLen(); + } + else + { + unsigned int cipherDataLen = token.getCipherDataLen(); + unsigned char * cipherText = new unsigned char[cipherDataLen]; + unsigned char * clearText = new unsigned char[cipherDataLen]; + + NOT_NULL(cipherText); + NOT_NULL(clearText); + + memcpy(cipherText, token.getData(), cipherDataLen); + for(unsigned int i = 0; i < keyCount; i ++) + { + DEBUG_REPORT_LOG(true, ("Decrypting with key: ")); + for(uint32 j = 0; j < 16; j ++) + { + DEBUG_REPORT_LOG(true, ("[%3i] ", keys[i].value[j])); + } + DEBUG_REPORT_LOG(true, ("\n")); + // find key index + unsigned char digest[KeyShareConstants::keyLength]; + decrypt(token.getDigest(), KeyShareConstants::keyLength, digest, i); + dataLen = token.getDataLen(); + decrypt(cipherText, cipherDataLen, clearText, i); + memcpy(clearTextData, clearText, dataLen); + + NOT_NULL(hasher); + + result = hasher->verify(digest, clearTextData, dataLen); + if(result) + { + DEBUG_REPORT_LOG(true, ("succeeded\n")); + + break; + } + else + { + DEBUG_REPORT_LOG(true, ("failed\n")); + } + } + delete[] cipherText; + delete[] clearText; + } + return result; +} + +//----------------------------------------------------------------------- + +void KeyShare::decrypt(const unsigned char * const sourceBuffer, const uint32 sourceBufferSize, unsigned char * resultBuffer, const uint32 keyIndex) const +{ + const uint32 blockSize = decryptors[0]->getBlockSize(); + unsigned char * inBlock = new unsigned char [blockSize]; + unsigned char * outBlock = new unsigned char [blockSize]; + + memset(resultBuffer, 0, sourceBufferSize); + + for(uint32 i = 0; i < sourceBufferSize; i += blockSize) + { + if(i + blockSize > sourceBufferSize) + memcpy(inBlock, &sourceBuffer[i], sourceBufferSize - i); + else + memcpy(inBlock, &sourceBuffer[i], blockSize); + decryptors[keyIndex]->process(inBlock, outBlock, blockSize); + memcpy(&resultBuffer[i], outBlock, blockSize); + } + delete[] inBlock; + delete[] outBlock; +} + +//----------------------------------------------------------------------- + +const uint32 KeyShare::encrypt(const unsigned char * const sourceData, const uint32 sourceDataSize, unsigned char ** resultBuffer) const +{ + uint32 i; + const uint32 blockSize = encryptors[0]->getBlockSize(); + + // calculate the size of the encrypted result buffer + if(sourceDataSize % blockSize) + { + // if it's larger than some multiple of the cipher block size .... + i = static_cast((sourceDataSize / blockSize) + 1) * blockSize; + } + else + { + // it's already aligned on the cipher block byte boundary + i = sourceDataSize; + } + *resultBuffer = new unsigned char [i]; + + DEBUG_REPORT_LOG(true, ("Encrypting with key: ")); + for(i = 0; i < 16; i ++) + { + DEBUG_REPORT_LOG(true, ("[%3i] ", keys[0].value[i])); + } + DEBUG_REPORT_LOG(true, ("\n")); + + unsigned char * inBlock = new unsigned char [blockSize]; + unsigned char * outBlock = new unsigned char [blockSize]; + + for(i = 0; i < sourceDataSize; i += 16) + { + memset(inBlock, 0, KeyShareConstants::keyLength); + if(i + blockSize > sourceDataSize) + memcpy(inBlock, &sourceData[i], sourceDataSize - i); + else + memcpy(inBlock, &sourceData[i], KeyShareConstants::keyLength); + encryptors[0]->process(inBlock, outBlock, blockSize); + memcpy(&((*resultBuffer)[i]), outBlock, blockSize); + } + + delete[] inBlock; + delete[] outBlock; + return i; +} + +//----------------------------------------------------------------------- + +bool KeyShare::hasKey(const Key & source) +{ + for(unsigned int i = 0; i < getKeyCount(); i ++) + { + + if(memcmp(source.value, keys[i].value, KeyShareConstants::keyLength) == 0) + return true; + } + return false; +} + +//----------------------------------------------------------------------- + +KeyShare::Token KeyShare::makeToken(const unsigned char * const data, const uint32 dataLen) const +{ + // generate a 128 bit message digest representing the data + unsigned char digest[KeyShareConstants::keyLength]; + + NOT_NULL(hasher); + + hasher->process(data, digest, dataLen); + + // encrypt the digest + unsigned char * cipherDigest; + IGNORE_RETURN(encrypt(digest, KeyShareConstants::keyLength, &cipherDigest)); ///@todo error checking + + // encrypt the cleartext data + unsigned char * cipherData = 0; + uint32 cipherDataLen = encrypt(data, dataLen, &cipherData); + + // build a token using the ciphertext data and digest + Token token(cipherData, dataLen, cipherDigest, cipherDataLen); + + // clean up allocation for cipher text, + // it was deep copied in the token + delete[] cipherData; + delete[] cipherDigest; + + return token; +} + +//----------------------------------------------------------------------- + +void KeyShare::pushKey(const Key & source) +{ + shift(); + setKey(source, 0); +} + +//----------------------------------------------------------------------- + +void KeyShare::setKey(const Key & newKey, const unsigned int index) +{ + DEBUG_FATAL(index >= keyCount, ("Key index out of range\n")); + keys[index] = newKey; + delete decryptors[index]; + delete encryptors[index]; + decryptors[index] = new Crypto::TwofishDecryptor(newKey.value, KeyShareConstants::keyLength); + encryptors[index] = new Crypto::TwofishEncryptor(newKey.value, KeyShareConstants::keyLength); +} + +//----------------------------------------------------------------------- + +void KeyShare::shift(void) +{ + // shift key array + for(unsigned int i = getKeyCount() - 1; i > 0; i --) + { + setKey(getKey(i - 1), i); + } +} + +//----------------------------------------------------------------------- + +AutoVariableKeyShare::AutoVariableKeyShare() : +AutoVariableBase() +{ +} + +//----------------------------------------------------------------------- + +AutoVariableKeyShare::AutoVariableKeyShare(const KeyShare::Key & source) : +AutoVariableBase() +{ + value = source; +} + +//----------------------------------------------------------------------- + +AutoVariableKeyShare::~AutoVariableKeyShare() +{ +} + +//----------------------------------------------------------------------- + +AutoVariableKeyShare & AutoVariableKeyShare::operator=(const KeyShare::Key & source) +{ + value = source; + return *this; +} + +//----------------------------------------------------------------------- + +AutoVariableKeyShare::operator KeyShare::Key & () +{ + return value; +} + +//----------------------------------------------------------------------- + +AutoVariableKeyShare::operator const KeyShare::Key & () const +{ + return value; +} + +//----------------------------------------------------------------------- + +void AutoVariableKeyShare::pack(Archive::ByteStream & target) const +{ + for(unsigned int i = 0; i < KeyShareConstants::keyLength; ++i) + Archive::put(target, value.value[i]); + +} + +//----------------------------------------------------------------------- + +void AutoVariableKeyShare::unpack(Archive::ReadIterator & source) +{ + for(unsigned int i = 0; i < KeyShareConstants::keyLength; ++i) + Archive::get(source, value.value[i]); +} + +//----------------------------------------------------------------------- diff --git a/engine/server/library/serverKeyShare/src/shared/KeyShare.h b/engine/server/library/serverKeyShare/src/shared/KeyShare.h new file mode 100644 index 00000000..32e8f212 --- /dev/null +++ b/engine/server/library/serverKeyShare/src/shared/KeyShare.h @@ -0,0 +1,140 @@ +// KeyShare.h +// copyright 2000 Verant Interactive +// Author: Justin Randall + +#ifndef _KeyShare_H +#define _KeyShare_H + +//----------------------------------------------------------------------- + +#include "Archive/AutoByteStream.h" + +namespace Crypto +{ + class TwofishEncryptor; + class TwofishDecryptor; + class MD5Hash; +} + +namespace KeyShareConstants +{ + const unsigned int keyLength = 16; +} + +//----------------------------------------------------------------------- + +class KeyShare +{ +public: + struct Key + { + unsigned char value[KeyShareConstants::keyLength]; + }; + + /** + An instance of a token ALWAYS contains encrypted data. + */ + class Token + { + public: + Token(const unsigned char * const cipherData, const uint32 dataLen, const unsigned char digest[KeyShareConstants::keyLength], const uint32 cipherDataLen); + Token(const Token & source); + explicit Token(Archive::ReadIterator & source); + ~Token(); + Token& operator = (const Token & source); + const uint32 getCipherDataLen(void) const; + const unsigned char * const getData(void) const; + const uint32 getDataLen(void) const; + const unsigned char * const getDigest(void) const; + void pack(Archive::ByteStream & target) const; + private: + unsigned char * cipherData; + uint32 cipherDataLen; + uint32 dataLen; + unsigned char digest[KeyShareConstants::keyLength]; + Token(); + }; + +public: + explicit KeyShare(const unsigned int newKeyCount=2); + virtual ~KeyShare(); + + bool decipherToken (const Token & token, unsigned char * clearTextData, uint32 & dataLen) const; + const Key& getKey (const unsigned int index) const; + const unsigned int getKeyCount (void) const; + bool hasKey (const Key & source); + Token makeToken (const unsigned char * const data, const uint32 dataLen) const; + const bool isTokenValid (const Token & token) const; + void pushKey (const Key & source); + void setKey (const Key & newKey, const unsigned int index); + const Key & operator[] (const unsigned int index) const; +private: + /** + */ + void decrypt (const unsigned char * const sourceData, const uint32 sourceDataSize, unsigned char * resultBuffer, const uint32 keyIndex) const; + + /** + */ + const uint32 encrypt (const unsigned char * const sourceData, const uint32 sourceDataSize, unsigned char ** resultBuffer) const; + void shift(void); + + Crypto::TwofishDecryptor ** decryptors; + Crypto::TwofishEncryptor ** encryptors; + Crypto::MD5Hash * hasher; + unsigned int keyCount; + Key * keys; + + + KeyShare(const KeyShare&); + KeyShare& operator= (const KeyShare&); +}; + +//----------------------------------------------------------------------- + +inline const KeyShare::Key & KeyShare::operator[](const unsigned int index) const +{ + DEBUG_FATAL(index >= keyCount, ("Key index out of range\n")); + return getKey(index); +} + +//----------------------------------------------------------------------- + +inline const unsigned int KeyShare::getKeyCount(void) const +{ + return keyCount; +} + +//----------------------------------------------------------------------- + +inline const KeyShare::Key& KeyShare::getKey(const unsigned int index) const +{ //lint !e1727 //not defined as inline? @todo + DEBUG_FATAL(index >= keyCount, ("Key index out of range\n")); + return keys[index]; +} + +//----------------------------------------------------------------------- +/** + @brief Extend archive system to support the keyshare key data type +*/ +class AutoVariableKeyShare : public Archive::AutoVariableBase +{ +public: + AutoVariableKeyShare(); + explicit AutoVariableKeyShare(const KeyShare::Key & source); + ~AutoVariableKeyShare(); + AutoVariableKeyShare & operator=(const KeyShare::Key & rhs); + AutoVariableKeyShare & operator=(const AutoVariableKeyShare & rhs); + + operator KeyShare::Key & (); //lint !e1930 // this is the intended behavior of an auto variable + operator const KeyShare::Key & () const; //lint !e1930 // this is the intended behavior of an auto variable + + virtual void pack(Archive::ByteStream & target) const; + virtual void unpack(Archive::ReadIterator & source); +private: + KeyShare::Key value; +}; + +//----------------------------------------------------------------------- + +#endif //_KeyShare_H + diff --git a/engine/server/library/serverKeyShare/src/win32/FirstServerKeyShare.cpp b/engine/server/library/serverKeyShare/src/win32/FirstServerKeyShare.cpp new file mode 100644 index 00000000..541cbe71 --- /dev/null +++ b/engine/server/library/serverKeyShare/src/win32/FirstServerKeyShare.cpp @@ -0,0 +1,8 @@ +// ====================================================================== +// +// FirstServerKeyShare.cpp +// copyright (c) 2001 Sony Online Entertainment +// +// ====================================================================== + +#include "serverKeyShare/FirstServerKeyShare.h"