port 2D step 5a: EterPack/EterPackManager + EterBase file units against the real 40250 packs

- Mirror copies: EterPack (EterPack, EterPackManager, EterPackCursor, CSHybridCrypt policy, Inline.h)
  and EterBase logic units tea/lzo/Timer/Stl/Random, with PORT-tagged width fixes (LONG index
  fields, static_assert(sizeof(TEterPackIndex) == 192), tea_word = Win32 unsigned long).
- TEA pack keys are extracted from the reference EterPack.cpp at configure time into
  build/.../EterPackKeys.generated.h; nothing key-bearing is tracked.
- Platform EterBase: FileBase (stdio, case-insensitive fallback), MappedFile (mmap /
  MapViewOfFile), CRC32, Debug, Utils StringPath/StringLowers.
- Win32Crt: CreateDirectory/DeleteFile/_access/MAKEFOURCC; Win32MinMax.h min/max (force-included
  on MinGW, whose windows.h omits them in C++).
- tests/port_eterpack_test: registers pack/Index like PackInitialize and checks 119
  registrations / 103 packs + root / 54891 entries / 52609 paths / 2282 overrides with
  first-registered-wins, and that exactly the 4 short-layout SECURITY files fail to load.
- 2R count corrected (136 -> 119 registrations, 103 distinct packs + root).

Not runtime-reachable yet (asset_io pack backend is step 5b), so port-map statuses stay TODO.
port_gate: macos/android/ios/windows PASS.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
shenlei
2026-09-23 00:56:39 +09:00
co-authored by Claude Opus 5
parent f210f47076
commit 1f1004da79
31 changed files with 4640 additions and 142 deletions
File diff suppressed because it is too large Load Diff
+7 -6
View File
@@ -23,7 +23,7 @@ namespace eterpack
const DWORD c_IndexCC = MAKEFOURCC('E', 'P', 'K', 'D');
const DWORD c_Version = 2;
// FourCC + Version + m_indexCount
const DWORD c_HeaderSize = sizeof(DWORD) + sizeof(DWORD) + sizeof(long);
const DWORD c_HeaderSize = sizeof(DWORD) + sizeof(DWORD) + sizeof(LONG); // PORT: LONG (32-bit) for Win32 long
};
enum EEterPackTypes
@@ -46,20 +46,21 @@ enum EEterPackTypes
#pragma pack(push, 4)
typedef struct SEterPackIndex
{
long id;
LONG id; // PORT: fixed-width Win32 long
char filename[FILENAME_MAX_LEN + 1];
DWORD filename_crc;
long real_data_size;
long data_size;
LONG real_data_size; // PORT: fixed-width Win32 long
LONG data_size; // PORT: fixed-width Win32 long
#ifdef CHECKSUM_CHECK_MD5
BYTE MD5Digest[16];
#else
DWORD data_crc;
#endif
long data_position;
LONG data_position; // PORT: fixed-width Win32 long
char compressed_type;
} TEterPackIndex;
#pragma pack(pop)
static_assert(sizeof(TEterPackIndex) == 192, "TEterPackIndex on-disk layout"); // PORT: on-disk struct
typedef std::unordered_map<DWORD, TEterPackIndex *> TDataPositionMap;
typedef std::list<TEterPackIndex *> TFreeIndexList;
@@ -170,7 +171,7 @@ class CEterPack
char* m_file_data;
unsigned m_file_size;
long m_indexCount;
LONG m_indexCount; // PORT: read as 32-bit from the index header
bool m_bEncrypted;
char m_dbName[DBNAME_MAX_LEN+1];
@@ -0,0 +1,59 @@
#include "StdAfx.h"
#include "EterPackCursor.h"
#include "Inline.h"
CEterPackCursor::CEterPackCursor(CEterPack* pack) : m_pPack(pack), m_pData(NULL), m_ReadPoint(0)
{
}
CEterPackCursor::~CEterPackCursor()
{
Close();
}
bool CEterPackCursor::Open(const char* filename)
{
assert(m_pPack != NULL);
char tmpFilename[MAX_PATH + 1];
strncpy(tmpFilename, filename, MAX_PATH);
inlineConvertPackFilename(tmpFilename);
if (!m_pPack->Get(m_file, tmpFilename, &m_pData))
return false;
return true;
}
void CEterPackCursor::Close()
{
m_file.Destroy();
m_pData = NULL;
m_ReadPoint = 0;
}
void CEterPackCursor::Seek(long offset)
{
m_ReadPoint = max(0, min(Size(), offset));
}
bool CEterPackCursor::Read(LPVOID data, long size)
{
if (m_file.IsNull())
return false;
if (m_ReadPoint + size > Size())
return false;
memcpy(data, (char*) m_pData + m_ReadPoint, size);
m_ReadPoint += size;
return true;
}
long CEterPackCursor::Size()
{
if (m_file.IsNull())
return 0;
return m_file.Size();
}
@@ -0,0 +1,25 @@
#ifndef __INC_ETERPACKCURSOR_H__
#define __INC_ETERPACKCURSOR_H__
#include "EterPack.h"
class CEterPackCursor
{
public:
CEterPackCursor(CEterPack * pack);
~CEterPackCursor();
bool Open(const char* filename);
void Close();
void Seek(long offset);
bool Read(LPVOID data, long size);
long Size();
private:
CEterPack * m_pPack;
CMappedFile m_file;
LPCVOID m_pData;
long m_ReadPoint;
};
#endif
@@ -0,0 +1,621 @@
#include "StdAfx.h"
#include <io.h>
#include <assert.h>
#include "EterPackManager.h"
#include "EterPackPolicy_CSHybridCrypt.h"
#include "../EterBase/Debug.h"
#include "../EterBase/CRC32.h"
#define PATH_ABSOLUTE_YMIRWORK1 "d:/ymir work/"
#define PATH_ABSOLUTE_YMIRWORK2 "d:\\ymir work\\"
#ifdef __THEMIDA__
#include <ThemidaSDK.h>
#endif
CEterPack* CEterPackManager::FindPack(const char* c_szPathName)
{
std::string strFileName;
if (0 == ConvertFileName(c_szPathName, strFileName))
{
return &m_RootPack;
}
else
{
for (TEterPackMap::iterator itor = m_DirPackMap.begin(); itor != m_DirPackMap.end(); ++itor)
{
const std::string & c_rstrName = itor->first;
CEterPack * pEterPack = itor->second;
if (CompareName(c_rstrName.c_str(), c_rstrName.length(), strFileName.c_str()))
{
return pEterPack;
}
}
}
return NULL;
}
void CEterPackManager::SetCacheMode()
{
m_isCacheMode=true;
}
void CEterPackManager::SetRelativePathMode()
{
m_bTryRelativePath = true;
}
// StringPath std::string 버전
int CEterPackManager::ConvertFileName(const char * c_szFileName, std::string & rstrFileName)
{
rstrFileName = c_szFileName;
stl_lowers(rstrFileName);
int iCount = 0;
for (DWORD i = 0; i < rstrFileName.length(); ++i)
{
if (rstrFileName[i] == '/')
++iCount;
else if (rstrFileName[i] == '\\')
{
rstrFileName[i] = '/';
++iCount;
}
}
return iCount;
}
bool CEterPackManager::CompareName(const char * c_szDirectoryName, DWORD /*dwLength*/, const char * c_szFileName)
{
const char * c_pszSrc = c_szDirectoryName;
const char * c_pszCmp = c_szFileName;
while (*c_pszSrc)
{
if (*(c_pszSrc++) != *(c_pszCmp++))
return false;
if (!*c_pszCmp)
return false;
}
return true;
}
void CEterPackManager::LoadStaticCache(const char* c_szFileName)
{
if (!m_isCacheMode)
return;
std::string strFileName;
if (0 == ConvertFileName(c_szFileName, strFileName))
{
return;
}
DWORD dwFileNameHash = GetCRC32(strFileName.c_str(), strFileName.length());
std::unordered_map<DWORD, SCache>::iterator f = m_kMap_dwNameKey_kCache.find(dwFileNameHash);
if (m_kMap_dwNameKey_kCache.end() != f)
return;
CMappedFile kMapFile;
const void* c_pvData;
if (!Get(kMapFile, c_szFileName, &c_pvData))
return;
SCache kNewCache;
kNewCache.m_dwBufSize = kMapFile.Size();
kNewCache.m_abBufData = new BYTE[kNewCache.m_dwBufSize];
memcpy(kNewCache.m_abBufData, c_pvData, kNewCache.m_dwBufSize);
m_kMap_dwNameKey_kCache.insert(std::unordered_map<DWORD, SCache>::value_type(dwFileNameHash, kNewCache));
}
CEterPackManager::SCache* CEterPackManager::__FindCache(DWORD dwFileNameHash)
{
std::unordered_map<DWORD, SCache>::iterator f=m_kMap_dwNameKey_kCache.find(dwFileNameHash);
if (m_kMap_dwNameKey_kCache.end()==f)
return NULL;
return &f->second;
}
void CEterPackManager::__ClearCacheMap()
{
std::unordered_map<DWORD, SCache>::iterator i;
for (i = m_kMap_dwNameKey_kCache.begin(); i != m_kMap_dwNameKey_kCache.end(); ++i)
delete [] i->second.m_abBufData;
m_kMap_dwNameKey_kCache.clear();
}
struct TimeChecker
{
TimeChecker(const char* name) : name(name)
{
baseTime = timeGetTime();
}
~TimeChecker()
{
printf("load %s (%d)\n", name, timeGetTime() - baseTime);
}
const char* name;
DWORD baseTime;
};
bool CEterPackManager::Get(CMappedFile & rMappedFile, const char * c_szFileName, LPCVOID * pData)
{
//TimeChecker timeChecker(c_szFileName);
//Logf(1, "Load %s\n", c_szFileName);
if (m_iSearchMode == SEARCH_FILE_FIRST)
{
if (GetFromFile(rMappedFile, c_szFileName, pData))
{
return true;
}
return GetFromPack(rMappedFile, c_szFileName, pData);
}
if (GetFromPack(rMappedFile, c_szFileName, pData))
{
return true;
}
return GetFromFile(rMappedFile, c_szFileName, pData);
}
struct FinderLock
{
FinderLock(CRITICAL_SECTION& cs) : p_cs(&cs)
{
EnterCriticalSection(p_cs);
}
~FinderLock()
{
LeaveCriticalSection(p_cs);
}
CRITICAL_SECTION* p_cs;
};
bool CEterPackManager::GetFromPack(CMappedFile & rMappedFile, const char * c_szFileName, LPCVOID * pData)
{
FinderLock lock(m_csFinder);
static std::string strFileName;
if (0 == ConvertFileName(c_szFileName, strFileName))
{
return m_RootPack.Get(rMappedFile, strFileName.c_str(), pData);
}
else
{
DWORD dwFileNameHash = GetCRC32(strFileName.c_str(), strFileName.length());
SCache* pkCache = __FindCache(dwFileNameHash);
if (pkCache)
{
rMappedFile.Link(pkCache->m_dwBufSize, pkCache->m_abBufData);
return true;
}
CEterFileDict::Item* pkFileItem = m_FileDict.GetItem(dwFileNameHash, strFileName.c_str());
if (pkFileItem)
if (pkFileItem->pkPack)
{
bool r = pkFileItem->pkPack->Get2(rMappedFile, strFileName.c_str(), pkFileItem->pkInfo, pData);
//pkFileItem->pkPack->ClearDataMemoryMap();
return r;
}
}
#ifdef _DEBUG
TraceError("CANNOT_FIND_PACK_FILE [%s]", strFileName.c_str());
#endif
return false;
}
const time_t g_tCachingInterval = 10; // 10초
void CEterPackManager::ArrangeMemoryMappedPack()
{
//time_t curTime = time(NULL);
//CEterFileDict::TDict dict = m_FileDict.GetDict();
//for (CEterFileDict::TDict::iterator it = dict.begin(); it != dict.end(); ++it)
//{
// CEterFileDict::Item &rFileItem = it->second;
// CEterPack* pkPack = rFileItem.pkPack;
// if (pkPack)
// {
// if (curTime - pkPack->GetLastAccessTime() > g_tCachingInterval)
// {
// pkPack->ClearDataMemoryMap();
// }
// }
//}
}
bool CEterPackManager::GetFromFile(CMappedFile & rMappedFile, const char * c_szFileName, LPCVOID * pData)
{
#ifndef _DEBUG
//const char *pcExt = strchr(c_szFileName, '.');
//if (pcExt &&
// _strnicmp(pcExt, ".py", 3) == 0 && // python 스크립트 중
// stricmp(c_szFileName, "logininfo.py") != 0 && // 로그인 정보 파일이 아니고
// strnicmp(c_szFileName, "locale", 6) != 0
// )
//{
// return false;
//}
#endif
//if(m_bTryRelativePath) {
// if (strnicmp(c_szFileName, PATH_ABSOLUTE_YMIRWORK1, strlen(PATH_ABSOLUTE_YMIRWORK1)) == 0 || strnicmp(c_szFileName, PATH_ABSOLUTE_YMIRWORK2, strlen(PATH_ABSOLUTE_YMIRWORK2)) == 0) {
// if(rMappedFile.Create(c_szFileName+strlen(PATH_ABSOLUTE_YMIRWORK1), pData, 0, 0))
// {
// return true;
// }
// }
//}
return rMappedFile.Create(c_szFileName, pData, 0, 0) ? true : false;
}
bool CEterPackManager::isExistInPack(const char * c_szFileName)
{
std::string strFileName;
if (0 == ConvertFileName(c_szFileName, strFileName))
{
return m_RootPack.IsExist(strFileName.c_str());
}
else
{
DWORD dwFileNameHash = GetCRC32(strFileName.c_str(), strFileName.length());
CEterFileDict::Item* pkFileItem = m_FileDict.GetItem(dwFileNameHash, strFileName.c_str());
if (pkFileItem)
if (pkFileItem->pkPack)
return pkFileItem->pkPack->IsExist(strFileName.c_str());
}
// NOTE : 매치 되는 팩이 없다면 false - [levites]
return false;
}
bool CEterPackManager::isExist(const char * c_szFileName)
{
if (m_iSearchMode == SEARCH_PACK_FIRST)
{
if (isExistInPack(c_szFileName))
return true;
return _access(c_szFileName, 0) == 0 ? true : false;
}
//if(m_bTryRelativePath) {
// if (strnicmp(c_szFileName, PATH_ABSOLUTE_YMIRWORK1, strlen(PATH_ABSOLUTE_YMIRWORK1)) == 0 || strnicmp(c_szFileName, PATH_ABSOLUTE_YMIRWORK2, strlen(PATH_ABSOLUTE_YMIRWORK2)) == 0) {
// if(access(c_szFileName+strlen(PATH_ABSOLUTE_YMIRWORK1), 0) == 0)
// return true;
// }
//}
if (_access(c_szFileName, 0) == 0)
return true;
return isExistInPack(c_szFileName);
}
void CEterPackManager::RegisterRootPack(const char * c_szName)
{
if (!m_RootPack.Create(m_FileDict, c_szName, ""))
{
TraceError("%s: Pack file does not exist", c_szName);
}
}
const char * CEterPackManager::GetRootPackFileName()
{
return m_RootPack.GetDBName();
}
bool CEterPackManager::DecryptPackIV(DWORD dwPanamaKey)
{
TEterPackMap::iterator itor = m_PackMap.begin();
while (itor != m_PackMap.end())
{
itor->second->DecryptIV(dwPanamaKey);
itor++;
}
return true;
}
bool CEterPackManager::RegisterPackWhenPackMaking(const char * c_szName, const char * c_szDirectory, CEterPack* pPack)
{
m_PackMap.insert(TEterPackMap::value_type(c_szName, pPack));
m_PackList.push_front(pPack);
m_DirPackMap.insert(TEterPackMap::value_type(c_szDirectory, pPack));
return true;
}
bool CEterPackManager::RegisterPack(const char * c_szName, const char * c_szDirectory, const BYTE* c_pbIV)
{
CEterPack * pEterPack = NULL;
{
TEterPackMap::iterator itor = m_PackMap.find(c_szName);
if (m_PackMap.end() == itor)
{
bool bReadOnly = true;
pEterPack = new CEterPack;
if (pEterPack->Create(m_FileDict, c_szName, c_szDirectory, bReadOnly, c_pbIV))
{
m_PackMap.insert(TEterPackMap::value_type(c_szName, pEterPack));
}
else
{
#ifdef _DEBUG
Tracef("The eterpack doesn't exist [%s]\n", c_szName);
#endif
delete pEterPack;
pEterPack = NULL;
return false;
}
}
else
{
pEterPack = itor->second;
}
}
if (c_szDirectory && c_szDirectory[0] != '*')
{
TEterPackMap::iterator itor = m_DirPackMap.find(c_szDirectory);
if (m_DirPackMap.end() == itor)
{
m_PackList.push_front(pEterPack);
m_DirPackMap.insert(TEterPackMap::value_type(c_szDirectory, pEterPack));
}
}
return true;
}
void CEterPackManager::SetSearchMode(bool bPackFirst)
{
m_iSearchMode = bPackFirst ? SEARCH_PACK_FIRST : SEARCH_FILE_FIRST;
}
int CEterPackManager::GetSearchMode()
{
return m_iSearchMode;
}
CEterPackManager::CEterPackManager() : m_bTryRelativePath(false), m_iSearchMode(SEARCH_FILE_FIRST), m_isCacheMode(false)
{
InitializeCriticalSection(&m_csFinder);
}
CEterPackManager::~CEterPackManager()
{
__ClearCacheMap();
TEterPackMap::iterator i = m_PackMap.begin();
TEterPackMap::iterator e = m_PackMap.end();
while (i != e)
{
delete i->second;
i++;
}
DeleteCriticalSection(&m_csFinder);
}
void CEterPackManager::RetrieveHybridCryptPackKeys(const BYTE *pStream)
{
////dump file format
//total packagecnt (4byte)
// for packagecntpackage
// db name hash ( stl.h stringhash )
// extension cnt( 4byte)
// for extension cnt
// ext hash ( stl.h stringhash )
// key-16byte
// iv-16byte
int iMemOffset = 0;
int iPackageCnt;
DWORD dwPackageNameHash;
memcpy( &iPackageCnt, pStream + iMemOffset, sizeof(int) );
iMemOffset += sizeof(iPackageCnt);
for( int i = 0; i < iPackageCnt; ++i )
{
int iRecvedCryptKeySize = 0;
memcpy( &iRecvedCryptKeySize, pStream + iMemOffset, sizeof(iRecvedCryptKeySize) );
iRecvedCryptKeySize -= sizeof(dwPackageNameHash); // 서버에서 받은 key stream에는 filename hash가 포함되어 있으므로, hash 사이즈 만큼 배줌.
iMemOffset += sizeof(iRecvedCryptKeySize);
memcpy( &dwPackageNameHash, pStream + iMemOffset, sizeof(dwPackageNameHash) );
iMemOffset += sizeof(dwPackageNameHash);
TEterPackMap::const_iterator cit;
for( cit = m_PackMap.begin(); cit != m_PackMap.end(); ++cit )
{
auto ssvv = std::string(cit->first);
std::string noPathName = CFileNameHelper::NoPath(ssvv);
if( dwPackageNameHash == stringhash().GetHash(noPathName) )
{
EterPackPolicy_CSHybridCrypt* pCryptPolicy = cit->second->GetPackPolicy_HybridCrypt();
int iHavedCryptKeySize = pCryptPolicy->ReadCryptKeyInfoFromStream( pStream + iMemOffset );
if (iRecvedCryptKeySize != iHavedCryptKeySize)
{
TraceError("CEterPackManager::RetrieveHybridCryptPackKeys cryptokey length of file(%s) is not matched. received(%d) != haved(%d)", noPathName.c_str(), iRecvedCryptKeySize, iHavedCryptKeySize);
}
break;
}
}
iMemOffset += iRecvedCryptKeySize;
}
}
void CEterPackManager::RetrieveHybridCryptPackSDB( const BYTE* pStream )
{
//cnt
//for cnt
//DWORD dwPackageIdentifier;
//DWORD dwFileIdentifier;
//std::vector<BYTE> vecSDBStream;
int iReadOffset = 0;
int iSDBInfoCount = 0;
memcpy( &iSDBInfoCount, pStream+iReadOffset, sizeof(int) );
iReadOffset += sizeof(int);
for( int i = 0; i < iSDBInfoCount; ++i )
{
DWORD dwPackgeIdentifier;
memcpy( &dwPackgeIdentifier, pStream+iReadOffset, sizeof(DWORD) );
iReadOffset += sizeof(DWORD);
TEterPackMap::const_iterator cit;
for( cit = m_PackMap.begin(); cit != m_PackMap.end(); ++cit )
{
auto ssvv = std::string(cit->first);
std::string noPathName = CFileNameHelper::NoPath(ssvv);
if( dwPackgeIdentifier == stringhash().GetHash(noPathName) )
{
EterPackPolicy_CSHybridCrypt* pCryptPolicy = cit->second->GetPackPolicy_HybridCrypt();
iReadOffset += pCryptPolicy->ReadSupplementatyDataBlockFromStream( pStream+iReadOffset );
break;
}
}
}
}
void CEterPackManager::WriteHybridCryptPackInfo(const char* pFileName)
{
//NOTE : this file format contains a little bit of redundant data.
//however it`s better for seperating cryptkey & supplementary data block.
//dump file format
//SDB data offset(4)
// about cryptkey
//total packagecnt (4byte)
// for packagecnt
// db name hash 4byte( stl.h stringhash )
// extension cnt( 4byte)
// for extension cnt
// ext hash ( stl.h stringhash )
// key-16byte
// iv-16byte
//about SDB data
//total packagecnt (4byte)
// for packagecnt
// db name hash 4byte( stl.h stringhash ) +child node size(4byte)
// sdb file cnt( 4byte )
// for sdb file cnt
// filename hash ( stl.h stringhash )
// related map name size(4), relate map name
// sdb block size( 1byte )
// sdb blocks
CFileBase keyFile;
if( !keyFile.Create( pFileName, CFileBase::FILEMODE_WRITE) )
{
//TODO : write log
return;
}
int iKeyPackageCount = 0;
//write later ( SDB Offset & PackageCnt for Key )
keyFile.SeekCur(2*sizeof(int));
TEterPackMap::const_iterator cit;
for( cit = m_PackMap.begin(); cit != m_PackMap.end(); ++cit )
{
EterPackPolicy_CSHybridCrypt* pPolicy = cit->second->GetPackPolicy_HybridCrypt();
if( !pPolicy || !pPolicy->IsContainingCryptKey() )
continue;
iKeyPackageCount++;
auto ssvv = std::string(cit->first);
std::string noPathName = CFileNameHelper::NoPath(ssvv);
DWORD dwPackNamehash = stringhash().GetHash(noPathName);
CMakePackLog::GetSingleton().Writef("CEterPackManager::WriteHybridCryptPackInfo PackName : %s, Hash : %x", noPathName.c_str(), dwPackNamehash);
keyFile.Write( &dwPackNamehash, sizeof(DWORD) );
pPolicy->WriteCryptKeyToFile( keyFile );
}
//Write SDB Data
int iSDBDataOffset = keyFile.GetPosition();
int iSDBPackageCnt = 0;
//Write SDB PackageCnt Later
keyFile.SeekCur(sizeof(int));
for( cit = m_PackMap.begin(); cit != m_PackMap.end(); ++cit )
{
EterPackPolicy_CSHybridCrypt* pPolicy = cit->second->GetPackPolicy_HybridCrypt();
if( !pPolicy || !pPolicy->IsContainingSDBFile() )
continue;
iSDBPackageCnt++;
auto ssvv1 = std::string(cit->first);
auto noPathName = CFileNameHelper::NoPath(ssvv1);
DWORD dwPackNamehash = stringhash().GetHash(noPathName);
keyFile.Write( &dwPackNamehash, sizeof(DWORD) );
int iSDBSizeWriteOffset = keyFile.GetPosition();
keyFile.SeekCur(sizeof(int));
pPolicy->WriteSupplementaryDataBlockToFile( keyFile );
int iSDBSizeAfterWrite = keyFile.GetPosition();
keyFile.Seek(iSDBSizeWriteOffset);
int iSDBSize = iSDBSizeAfterWrite-(iSDBSizeWriteOffset+4);
keyFile.Write( &iSDBSize, sizeof(int) );
keyFile.Seek(iSDBSizeAfterWrite);
}
//write sdb data start offset & package cnt
keyFile.Seek(0);
keyFile.Write( &iSDBDataOffset, sizeof(int));
keyFile.Write( &iKeyPackageCount, sizeof(int));
keyFile.Seek(iSDBDataOffset);
keyFile.Write( &iSDBPackageCnt, sizeof(int));
keyFile.Close();
}
@@ -0,0 +1,503 @@
#include "StdAfx.h"
#include "EterPackPolicy_CSHybridCrypt.h"
#include "../EterBase/Stl.h"
#include "../EterBase/Filename.h"
#include "../EterBase/FileBase.h"
#include "../EterBase/CRC32.h"
#include "../EterBase/lzo.h"
#include "../EterBase/Random.h"
#include <cryptopp/modes.h>
#include <cryptopp/osrng.h>
using namespace CryptoPP;
#define CIPHER_MODE CTR_Mode
#ifdef __THEMIDA__
#include <ThemidaSDK.h>
#endif
//Cipher
//Block Size
//Key Length
//
//Default Minimum Maximum
//AES(Rijndael) 16 16 16 32
//Blowfish 8 16 0 56
//Camellia 16 16 16 32
//CAST-128 8 16 5 16
//CAST-256 16 16 16 32
//DES 8 8 8 8
//DES-EDE2 8 16 16 16
//DES-EDE3 8 24 24 24
//DES-XEX3 8 24 24 24
//GOST 8 32 32 32
//IDEA 8 16 16 16
//MARS 16 16 16 56
//RC2 8 16 1 128
//RC5 8 16 0 255
//RC6 16 16 0 255
//SAFER-K 8 16 8 16
//SAFER-SK 8 16 8 16
//Serpent 16 16 1 32
//SHACAL-2 32 16 1 64
//SHARK-E 8 16 1 16
//SKIPJACK 8 10 1 10
//3-Way 12 12 1 12
//Twofish 16 16 0 32
//XTEA 8 16 1 16
inline std::string GetFileExt( std::string& rfileName )
{
stl_lowers(rfileName);
return CFileNameHelper::GetExtension(rfileName);
}
EterPackPolicy_CSHybridCrypt::~EterPackPolicy_CSHybridCrypt()
{
m_mapHybridCryptKey.clear();
m_mapSDBMap.clear();
}
bool EterPackPolicy_CSHybridCrypt::IsContainingCryptKey() const
{
return (m_mapHybridCryptKey.size() > 0) ? true : false;
}
bool EterPackPolicy_CSHybridCrypt::GenerateCryptKey( std::string& rfileName )
{
#ifdef __THEMIDA__
VM_START
#endif
//make lower & extract ext
std::string extName = GetFileExt(rfileName);
stl_lowers(extName);
DWORD dwExtHash = stringhash().GetHash(extName);
TCSHybridCryptKeyMap::const_iterator cit = m_mapHybridCryptKey.find( dwExtHash );
if( cit != m_mapHybridCryptKey.end() )
{
//TODO : log already registered
return false;
}
static AutoSeededRandomPool rnd;
TCSHybridCryptKey info;
{
rnd.GenerateBlock( &(info.uEncryptKey.key[0]), sizeof(info.uEncryptKey) );
rnd.GenerateBlock( &(info.uEncryptIV.iv[0]), sizeof(info.uEncryptIV) );
//for test
/* memset( &info.uEncryptKey.key, 0x10, sizeof(info.uEncryptKey) );
memset( &info.uEncryptIV.iv, 0x10, sizeof(info.uEncryptIV) ); */
}
m_mapHybridCryptKey[dwExtHash] = info;
#ifdef __THEMIDA__
VM_END
#endif
return true;
}
bool EterPackPolicy_CSHybridCrypt::GetPerFileCryptKey( std::string& rfileName, eHybridCipherAlgorithm& eAlgorithm, TEncryptKey& key, TEncryptIV& iv )
{
#ifdef __THEMIDA__
VM_START
#endif
std::string fileNamelower = rfileName;
stl_lowers(fileNamelower);
std::string extName = GetFileExt(fileNamelower);
TCSHybridCryptKeyMap::const_iterator cit = m_mapHybridCryptKey.find( stringhash().GetHash(extName));
if( cit == m_mapHybridCryptKey.end() )
{
//TODO : log no file ext info
return false;
}
DWORD dwfileNameCrc = GetCRC32(fileNamelower.c_str(), fileNamelower.size());
//make file specific algorithm & key & iv
eAlgorithm = (eHybridCipherAlgorithm)(dwfileNameCrc % Num_Of_Ciphers);
::memcpy(key.key, cit->second.uEncryptKey.key, sizeof(key) );
::memcpy(iv.iv, cit->second.uEncryptIV.iv, sizeof(iv) );
//Themida Warning
for( int i = 0; i < (sizeof(key)/sizeof(dwfileNameCrc)); ++i)
{
*((DWORD*)key.key + i) ^= dwfileNameCrc;
}
for( int i = 0; i < (sizeof(iv)/sizeof(dwfileNameCrc)); ++i)
{
*((DWORD*)iv.iv + i) ^= dwfileNameCrc;
}
#ifdef __THEMIDA__
VM_END
#endif
return true;
}
bool EterPackPolicy_CSHybridCrypt::EncryptMemory( std::string& rfileName, IN const BYTE* pSrcData, IN int iSrcLen, OUT CLZObject& zObj )
{
#ifdef __THEMIDA__
VM_START
#endif
eHybridCipherAlgorithm eAlgorithm;
TEncryptKey key;
TEncryptIV iv;
if( !GetPerFileCryptKey( rfileName, eAlgorithm, key, iv ) )
{
return false;
}
// start cipher
std::string strCipher;
//NOTE : ciphered stream size could be different from original size if you choose diffrent cipher mode & algorithm
//( i.e ECB or CBC mode )
strCipher.reserve(iSrcLen);
if( eAlgorithm == e_Cipher_Camellia )
{
// Encryptor
CIPHER_MODE<Camellia>::Encryption Encryptor;
Encryptor.SetKeyWithIV(key.keyCamellia, sizeof(key.keyCamellia), iv.ivCamellia, sizeof(iv.ivCamellia));
ArraySource(pSrcData, iSrcLen, true,
new StreamTransformationFilter(Encryptor, new CryptoPP::StringSink(strCipher)));
}
else if( eAlgorithm == e_Cipher_Twofish )
{
// Encryptor
CIPHER_MODE<Twofish>::Encryption Encryptor;
Encryptor.SetKeyWithIV(key.keyTwofish, sizeof(key.keyTwofish), iv.ivTwofish, sizeof(iv.ivTwofish));
ArraySource(pSrcData, iSrcLen, true,
new StreamTransformationFilter(Encryptor, new CryptoPP::StringSink(strCipher)));
}
else if( eAlgorithm == e_Cipher_XTEA )
{
// Encryptor
CIPHER_MODE<XTEA>::Encryption Encryptor;
Encryptor.SetKeyWithIV(key.keyXTEA, sizeof(key.keyXTEA), iv.ivXTEA, sizeof(iv.ivXTEA));
ArraySource(pSrcData, iSrcLen, true,
new StreamTransformationFilter(Encryptor, new CryptoPP::StringSink(strCipher)));
}
if (strCipher.length() != iSrcLen)
{
//TODO: size error log
return false;
}
zObj.AllocBuffer(iSrcLen);
memcpy(zObj.GetBuffer(), strCipher.c_str(), strCipher.length() );
#ifdef __THEMIDA__
VM_END
#endif
return true;
}
bool EterPackPolicy_CSHybridCrypt::DecryptMemory( std::string& rfilename, IN const BYTE* pEncryptedData, IN int iEncryptedLen, OUT CLZObject& zObj )
{
#ifdef __THEMIDA__
VM_START
#endif
eHybridCipherAlgorithm eAlgorithm;
TEncryptKey key;
TEncryptIV iv;
if( !GetPerFileCryptKey( rfilename, eAlgorithm, key, iv ) )
{
return false;
}
// start decipher
std::string strDecipher;
//NOTE : ciphered stream size could be different from original size if you choose diffrent cipher mode & algorithm
//( i.e ECB or CBC mode )
strDecipher.reserve(iEncryptedLen);
if( eAlgorithm == e_Cipher_Camellia )
{
// Decryptor
CIPHER_MODE<Camellia>::Decryption Decryptor;
Decryptor.SetKeyWithIV(key.keyCamellia, sizeof(key.keyCamellia), iv.ivCamellia, sizeof(iv.ivCamellia));
ArraySource(pEncryptedData, iEncryptedLen, true,
new StreamTransformationFilter(Decryptor, new CryptoPP::StringSink(strDecipher)));
}
else if( eAlgorithm == e_Cipher_Twofish )
{
// Decryptor
CIPHER_MODE<Twofish>::Decryption Decryptor;
Decryptor.SetKeyWithIV(key.keyTwofish, sizeof(key.keyTwofish), iv.ivTwofish, sizeof(iv.ivTwofish));
ArraySource(pEncryptedData, iEncryptedLen, true,
new StreamTransformationFilter(Decryptor, new CryptoPP::StringSink(strDecipher)));
}
else if( eAlgorithm == e_Cipher_XTEA )
{
// Decryptor
CIPHER_MODE<XTEA>::Decryption Decryptor;
Decryptor.SetKeyWithIV(key.keyXTEA, sizeof(key.keyXTEA), iv.ivXTEA, sizeof(iv.ivXTEA));
ArraySource(pEncryptedData, iEncryptedLen, true,
new StreamTransformationFilter(Decryptor, new CryptoPP::StringSink(strDecipher)));
}
if (strDecipher.length() != iEncryptedLen)
{
//TODO: size error log
return false;
}
zObj.AllocBuffer(iEncryptedLen);
memcpy(zObj.GetBuffer(), strDecipher.c_str(), strDecipher.length() );
#ifdef __THEMIDA__
VM_END
#endif
return true;
}
void EterPackPolicy_CSHybridCrypt::WriteCryptKeyToFile( CFileBase& rFile )
{
// ext cnt 4byte
// for ext hash ( crc32 )
// key-16byte
// iv-16byte
DWORD dwCryptKeySize = m_mapHybridCryptKey.size();
rFile.Write( &dwCryptKeySize, sizeof(DWORD) );
TCSHybridCryptKeyMap::const_iterator cit;
for( cit = m_mapHybridCryptKey.begin(); cit != m_mapHybridCryptKey.end(); ++cit )
{
DWORD extNamehash = cit->first;
const TCSHybridCryptKey& CryptKey = cit->second;
rFile.Write( &extNamehash, sizeof(DWORD) );
rFile.Write( CryptKey.uEncryptKey.key, sizeof(TEncryptKey) );
rFile.Write( CryptKey.uEncryptIV.iv, sizeof(TEncryptIV) );
}
}
int EterPackPolicy_CSHybridCrypt::ReadCryptKeyInfoFromStream( IN const BYTE* pStream )
{
int iStreamOffset = 0;
DWORD dwCryptoInfoSize;
memcpy(&dwCryptoInfoSize, pStream, sizeof(DWORD) );
iStreamOffset += sizeof(DWORD);
DWORD dwExtHash;
m_mapHybridCryptKey.clear();
for( int i = 0; i < dwCryptoInfoSize; ++i )
{
memcpy(&dwExtHash, pStream + iStreamOffset, sizeof(DWORD) );
iStreamOffset += sizeof(DWORD);
TCSHybridCryptKey info;
{
memcpy(info.uEncryptKey.key, pStream + iStreamOffset, sizeof(TEncryptKey) );
iStreamOffset += sizeof(TEncryptKey);
memcpy(info.uEncryptIV.iv, pStream + iStreamOffset, sizeof(TEncryptIV) );
iStreamOffset += sizeof(TEncryptIV);
}
m_mapHybridCryptKey[dwExtHash] = info;
}
return iStreamOffset;
}
bool EterPackPolicy_CSHybridCrypt::GenerateSupplementaryDataBlock(std::string& rfilename, const std::string& strMapName, IN const BYTE* pSrcData, IN int iSrcLen, OUT LPBYTE& pDestData, OUT int& iDestLen )
{
#ifdef __THEMIDA__
VM_START
#endif
std::string fileNamelower = rfilename;
stl_lowers( fileNamelower );
DWORD dwFileNameHash = stringhash().GetHash(fileNamelower);
TSupplementaryDataBlockMap::const_iterator cit = m_mapSDBMap.find( dwFileNameHash );
if( cit != m_mapSDBMap.end() )
{
//TODO : log already registered
return false;
}
//TODO : Find Better Method for deciding SDB Postion & Size
//prevent stream copy duplication
TSupplementaryDataBlockInfo info;
m_mapSDBMap[dwFileNameHash] = info;
std::string& strRelatedMapName = m_mapSDBMap[dwFileNameHash].strRelatedMapName;
std::vector<BYTE>& sdbVector = m_mapSDBMap[dwFileNameHash].vecStream;
//fill the data!!
{
strRelatedMapName = strMapName;
int iSDBSize = random_range( 64, 128 );
if( iSrcLen < iSDBSize )
{
iSDBSize = iSrcLen - 1;
if( iSDBSize <= 0 )
{
//TODO : is there 1byte file exist???
return false;
}
}
sdbVector.resize( iSDBSize );
iDestLen = iSrcLen - iSDBSize;
pDestData = (LPBYTE)pSrcData;
memcpy( &sdbVector[0], pDestData + iDestLen, iSDBSize );
}
#ifdef __THEMIDA__
VM_END
#endif
return true;
}
bool EterPackPolicy_CSHybridCrypt::GetSupplementaryDataBlock( std::string& rfilename, OUT LPBYTE& pSDB, OUT int& iSDBSize )
{
#ifdef __THEMIDA__
VM_START
#endif
std::string fileNamelower = rfilename;
stl_lowers( fileNamelower );
DWORD dwFileNameHash = stringhash().GetHash(fileNamelower);
TSupplementaryDataBlockMap::const_iterator cit = m_mapSDBMap.find( dwFileNameHash );
if( cit == m_mapSDBMap.end() )
{
//TODO : log already registered
return false;
}
const std::vector<BYTE>& vecSDB = cit->second.vecStream;
iSDBSize = vecSDB.size();
if(iSDBSize <= 0)
{
pSDB = NULL;
return false;
}
pSDB = (BYTE*)&vecSDB[0];
#ifdef __THEMIDA__
VM_END
#endif
return true;
}
bool EterPackPolicy_CSHybridCrypt::IsContainingSDBFile() const
{
return m_mapSDBMap.size() > 0 ? true : false;
}
void EterPackPolicy_CSHybridCrypt::WriteSupplementaryDataBlockToFile( CFileBase& rFile )
{
//about SDB data
// sdb file cnt( 4byte )
// for sdb file cnt
// filename hash ( stl.h stringhash )
// related map name size(4), relate map name
// sdb block size( 1byte )
// sdb blocks
DWORD dwSDBMapSize = m_mapSDBMap.size();
rFile.Write( &dwSDBMapSize, sizeof(DWORD) );
TSupplementaryDataBlockMap::const_iterator cit;
for( cit = m_mapSDBMap.begin(); cit != m_mapSDBMap.end(); ++cit )
{
DWORD dwFileNamehash = cit->first;
rFile.Write( &dwFileNamehash, sizeof(DWORD) );
const std::string strRelatedMapName = cit->second.strRelatedMapName;
DWORD dwMapNameSize = strRelatedMapName.size();
rFile.Write( &dwMapNameSize, sizeof(DWORD) );
rFile.Write( strRelatedMapName.c_str(), dwMapNameSize );
const std::vector<BYTE>& sdbVector = cit->second.vecStream;
BYTE bSDBSize = (BYTE)(sdbVector.size());
rFile.Write( &bSDBSize, sizeof(bSDBSize) );
if( bSDBSize > 0 )
rFile.Write( &sdbVector[0], bSDBSize );
}
}
int EterPackPolicy_CSHybridCrypt::ReadSupplementatyDataBlockFromStream( IN const BYTE* pStream )
{
#ifdef __THEMIDA__
VM_START
#endif
//DWORD dwFileIdentifier;
//std::vector<BYTE> vecSDBStream;
DWORD dwFileNameHash;
BYTE bSDBSize;
int iStreamOffset = 0;
memcpy(&dwFileNameHash, pStream + iStreamOffset, sizeof(DWORD) );
iStreamOffset += sizeof(DWORD);
memcpy(&bSDBSize, pStream + iStreamOffset, sizeof(BYTE) );
iStreamOffset += sizeof(BYTE);
// NOTE : related map name isn`t required in client. so we don`t recv it from stream to reduce packet size.
TSupplementaryDataBlockInfo info;
{
info.vecStream.resize( bSDBSize );
memcpy(&info.vecStream[0], pStream + iStreamOffset, bSDBSize );
iStreamOffset += bSDBSize;
m_mapSDBMap[dwFileNameHash] = info;
}
#ifdef __THEMIDA__
VM_END
#endif
return iStreamOffset;
}
@@ -0,0 +1,94 @@
#ifndef __INC_ETERPACKLIB_ETERPACKPOLICY_CSHYBRIDCRYPT_H__
#define __INC_ETERPACKLIB_ETERPACKPOLICY_CSHYBRIDCRYPT_H__
#include <unordered_map>
#include <cryptopp/cryptlib.h>
#include <cryptopp/camellia.h>
#include <cryptopp/twofish.h>
#include <cryptopp/tea.h>
enum eHybridCipherAlgorithm
{
e_Cipher_Camellia,
e_Cipher_Twofish,
e_Cipher_XTEA,
Num_Of_Ciphers
};
class CFileBase;
class CLZObject;
//THEMIDA
class EterPackPolicy_CSHybridCrypt
{
public:
~EterPackPolicy_CSHybridCrypt();
bool GenerateCryptKey( std::string& rfileName );
bool EncryptMemory( std::string& rfilename, IN const BYTE* pSrcData, IN int iSrcLen, OUT CLZObject& zObj );
bool DecryptMemory( std::string& rfilename, IN const BYTE* pSrcData, IN int iSrcLen, OUT CLZObject& zObj );
bool IsContainingCryptKey() const;
//Supplementary Data Block (SDB)
bool GenerateSupplementaryDataBlock(std::string& rfilename, const std::string& strMapName, IN const BYTE* pSrcData, IN int iSrcLen, OUT LPBYTE& pDestData, OUT int& iDestLen );
bool GetSupplementaryDataBlock( std::string& rfilename, OUT LPBYTE& pSDB, OUT int& iSDBSize );
bool IsContainingSDBFile() const;
// Read/Write IO
void WriteCryptKeyToFile( CFileBase& rFile );
int ReadCryptKeyInfoFromStream( IN const BYTE* pStream );
void WriteSupplementaryDataBlockToFile( CFileBase& rFile );
int ReadSupplementatyDataBlockFromStream( IN const BYTE* pStream );
protected:
typedef union UEncryptKey
{
BYTE key[16];
BYTE keyCamellia[ CryptoPP::Camellia::DEFAULT_KEYLENGTH];
BYTE keyTwofish [ CryptoPP::Twofish::DEFAULT_KEYLENGTH];
BYTE keyXTEA [ CryptoPP::XTEA::DEFAULT_KEYLENGTH];
} TEncryptKey;
typedef union UEncryptIV
{
BYTE iv[16];
BYTE ivCamellia [ CryptoPP::Camellia::BLOCKSIZE];
BYTE ivTwofish [ CryptoPP::Twofish::BLOCKSIZE];
BYTE ivXTEA [ CryptoPP::XTEA::BLOCKSIZE];
} TEncryptIV;
typedef struct SCSHybridCryptKey
{
TEncryptKey uEncryptKey;
TEncryptIV uEncryptIV;
} TCSHybridCryptKey;
typedef std::unordered_map<DWORD, TCSHybridCryptKey> TCSHybridCryptKeyMap;
TCSHybridCryptKeyMap m_mapHybridCryptKey;
typedef struct SSupplementaryDataBlockInfo
{
std::string strRelatedMapName;
std::vector<BYTE> vecStream;
} TSupplementaryDataBlockInfo;
typedef std::unordered_map<DWORD, TSupplementaryDataBlockInfo> TSupplementaryDataBlockMap; //key filename hash
TSupplementaryDataBlockMap m_mapSDBMap;
private:
bool GetPerFileCryptKey( std::string& rfileName, eHybridCipherAlgorithm& eAlgorithm, TEncryptKey& key, TEncryptIV& iv );
};
#endif // __INC_ETERPACKLIB_ETERPACKPOLICY_CSHYBRIDCRYPT_H__
+34
View File
@@ -0,0 +1,34 @@
#ifndef __INC_ETERPACK_INLINE_H__
#define __INC_ETERPACK_INLINE_H__
inline void inlinePathCreate(const char* path)
{
char dir[64];
const char* p, *k;
p = path + 3;
while (NULL != (k = strchr(p, '/')))
{
memset(dir, 0, sizeof(dir));
strncpy(dir, path, k - path);
CreateDirectory(dir, NULL);
p = k + 1;
}
}
inline void inlineConvertPackFilename(char* name)
{
char * p = name;
while (*p)
{
if (*p == '\\')
*p = '/';
else
*p = (int) tolower(*p);
p++;
}
}
#endif
+2
View File
@@ -0,0 +1,2 @@
#include "StdAfx.h"