aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorfusion32 <marcopuzziello@gmail.com>2025-07-03 10:10:27 -0300
committerfusion32 <marcopuzziello@gmail.com>2025-07-03 10:46:43 -0300
commite65dc561acab7a9e49f59777f16cb040c574c64d (patch)
tree2206c066b97b1948a4b98647c91a369d3b9863c8 /src
parente782274ade8d7307e157a997d6ac3bcaa4130024 (diff)
downloadgame-e65dc561acab7a9e49f59777f16cb040c574c64d.tar.gz
game-e65dc561acab7a9e49f59777f16cb040c574c64d.zip
implement `crypto.cc` with OpenSSL + fix config loading
I considered reversing the original `vlong` implementation but OpenSSL is just simpler, more secure, and most importantly, is already there. I also made it so the key is now stored in a PEM file (`tibia.pem`) so it can be easily swapped without re-compiling. This was the last piece to build and run a working executable, except that we now need to implement a query manager for the server to communicate with. I was able to fix a small problem with loading the config before hitting the problem with the query manager, which was expected. The next task should be getting a query manager up and running before starting phase two of upgrading and fixing the server itself.
Diffstat (limited to 'src')
-rw-r--r--src/communication.cc14
-rw-r--r--src/config.cc6
-rw-r--r--src/crypto.cc107
-rw-r--r--src/crypto.hh38
-rw-r--r--src/main.cc2
-rw-r--r--src/query.hh2
6 files changed, 120 insertions, 49 deletions
diff --git a/src/communication.cc b/src/communication.cc
index 9f0e6e5..a15e20a 100644
--- a/src/communication.cc
+++ b/src/communication.cc
@@ -23,16 +23,6 @@
#define MAX_COMMUNICATION_THREADS 1100
#define COMMUNICATION_THREAD_STACK_SIZE ((int)KB(64))
-static const char RSA_PRIME_P[] =
- "1201758001370723323398753778257470257713354828752713123415294815"
- "0506251412291888866940292054989907714155267326586216043845592229"
- "084368540020196135619327879";
-
-static const char RSA_PRIME_Q[] =
- "1189892136861686835188050824611210139447876026576932541274639840"
- "5473436969889506919017477758618276066588858607419440134394668095"
- "105156501566867770737187273";
-
static int TERMINALVERSION[3] = {770, 770, 770};
static int TCPSocket;
static ThreadHandle AcceptorThread;
@@ -1533,7 +1523,9 @@ void InitCommunication(void){
AcceptorThreadPID = 0;
ActiveConnections = 0;
QueryManagerConnectionPool.init();
- PrivateKey.init(RSA_PRIME_P, RSA_PRIME_Q);
+
+ // TODO(fusion): This is arbitrary, should probably be set in the config.
+ PrivateKey.initFromFile("tibia.pem");
OpenSocket();
if(TCPSocket == -1){
diff --git a/src/config.cc b/src/config.cc
index 257985b..b9c33bc 100644
--- a/src/config.cc
+++ b/src/config.cc
@@ -106,6 +106,7 @@ void ReadConfig(void){
MANAGER_DATABASE.Database[0] = 0;
char FileName[4096];
+#if 0
if(const char *Home = getenv("home")){
snprintf(FileName, sizeof(FileName), "%s/.tibia", Home);
}else if(const char *Home = getenv("HOME")){
@@ -113,6 +114,9 @@ void ReadConfig(void){
}else{
snprintf(FileName, sizeof(FileName), ".tibia");
}
+#else
+ snprintf(FileName, sizeof(FileName), ".tibia");
+#endif
if(!FileExists(FileName)){
throw "cannot find config-file";
@@ -130,7 +134,7 @@ void ReadConfig(void){
}
char Identifier[MAX_IDENT_LENGTH];
- strcpy(Identifier, Script.readIdentifier());
+ strcpy(Identifier, Script.getIdentifier());
Script.readSymbol('=');
// TODO(fusion): Ughh... Get rid of all `strcpy`s. A malicious configuration
diff --git a/src/crypto.cc b/src/crypto.cc
index b6df866..aa53dc3 100644
--- a/src/crypto.cc
+++ b/src/crypto.cc
@@ -1,3 +1,108 @@
#include "crypto.hh"
-// TODO
+#include <openssl/err.h>
+#include <openssl/pem.h>
+
+static void DumpOpenSSLErrors(const char *Where, const char *What){
+ error("OpenSSL error(s) while executing %s at %s:\n", What, Where);
+ ERR_print_errors_cb(
+ [](const char *str, usize len, void *u) -> int {
+ // NOTE(fusion): These error strings already have trailing newlines,
+ // for whatever reason.
+ error("> %s", str);
+ return 1;
+ }, NULL);
+}
+
+// TRSAPrivateKey
+// =============================================================================
+TRSAPrivateKey::TRSAPrivateKey(void){
+ m_RSA = NULL;
+}
+
+TRSAPrivateKey::~TRSAPrivateKey(void){
+ if(m_RSA){
+ RSA_free(m_RSA);
+ m_RSA = NULL;
+ }
+}
+
+void TRSAPrivateKey::initFromFile(const char *FileName){
+ if(m_RSA != NULL){
+ error("TRSAPrivateKey::init: Key already initialized.\n");
+ return;
+ }
+
+ FILE *File = fopen(FileName, "rb");
+ if(File == NULL){
+ error("TRSAPrivateKey::initFromFile: Failed to open \"%s\".\n", FileName);
+ return;
+ }
+
+ m_RSA = PEM_read_RSAPrivateKey(File, NULL, NULL, NULL);
+ fclose(File);
+
+ if(m_RSA == NULL){
+ error("TRSAPrivateKey::initFromFile: Failed to read key from \"%s\".\n", FileName);
+ DumpOpenSSLErrors("TRSAPrivateKey::initFromFile", "PEM_read_RSAPrivateKey");
+ }else if(RSA_size(m_RSA) != 128){
+ error("TRSAPrivateKey::initFromFile: File \"%s\" doesn't contain a 1024-bit key", FileName);
+ RSA_free(m_RSA);
+ m_RSA = NULL;
+ }
+}
+
+void TRSAPrivateKey::decrypt(uint8 *Data){
+ if(m_RSA == NULL){
+ error("TRSAPrivateKey::decrypt: Key not initialized.\n");
+ return;
+ }
+
+ // TODO(fusion): Pass in the length of `Data` for checking.
+ ASSERT(RSA_size(m_RSA) == 128);
+
+ if(RSA_private_decrypt(128, Data, Data, m_RSA, RSA_NO_PADDING) == -1){
+ DumpOpenSSLErrors("TRSAPrivateKey::decrypt", "RSA_private_decrypt");
+ }
+}
+
+// TXTEASymmetricKey
+// =============================================================================
+void TXTEASymmetricKey::init(TReadBuffer *Buffer){
+ m_SymmetricKey[0] = Buffer->readQuad();
+ m_SymmetricKey[1] = Buffer->readQuad();
+ m_SymmetricKey[2] = Buffer->readQuad();
+ m_SymmetricKey[3] = Buffer->readQuad();
+}
+
+void TXTEASymmetricKey::encrypt(uint8 *Data){
+ // TODO(fusion): This assumes both data endpoints have the same byte order.
+ // It's unlikely that there is anything other than little-endian but we
+ // should use a few helping functions to ensure compatibility.
+ uint32 Sum = 0x00000000UL;
+ uint32 Delta = 0x9E3779B9UL;
+ uint32 V0 = *(uint32*)(&Data[0]);
+ uint32 V1 = *(uint32*)(&Data[4]);
+ for(int i = 0; i < 32; i += 1){
+ V0 += (((V1 << 4) ^ (V1 >> 5)) + V1) ^ (Sum + m_SymmetricKey[Sum & 3]);
+ Sum += Delta;
+ V1 += (((V0 << 4) ^ (V0 >> 5)) + V0) ^ (Sum + m_SymmetricKey[(Sum >> 11) & 3]);
+ }
+ *(uint32*)(&Data[0]) = V0;
+ *(uint32*)(&Data[4]) = V1;
+}
+
+void TXTEASymmetricKey::decrypt(uint8 *Data){
+ // TODO(fusion): Same as above.
+ uint32 Sum = 0xC6EF3720UL;
+ uint32 Delta = 0x9E3779B9UL;
+ uint32 V0 = *(uint32*)(&Data[0]);
+ uint32 V1 = *(uint32*)(&Data[4]);
+ for(int i = 0; i < 32; i += 1){
+ V1 -= (((V0 << 4) ^ (V0 >> 5)) + V0) ^ (Sum + m_SymmetricKey[(Sum >> 11) & 3]);
+ Sum -= Delta;
+ V0 -= (((V1 << 4) ^ (V1 >> 5)) + V1) ^ (Sum + m_SymmetricKey[Sum & 3]);
+ }
+ *(uint32*)(&Data[0]) = V0;
+ *(uint32*)(&Data[4]) = V1;
+}
diff --git a/src/crypto.hh b/src/crypto.hh
index 7159a37..b649ad8 100644
--- a/src/crypto.hh
+++ b/src/crypto.hh
@@ -2,52 +2,20 @@
#define TIBIA_CRYPTO_HH_ 1
#include "common.hh"
-
-// TODO(fusion): We might want to scrap this implementation and use OpenSSL instead.
-
-struct vlong_flex_unit{
- uint32 n;
- uint32 *a;
- uint32 z;
-};
-
-struct vlong_value: vlong_flex_unit {
- uint32 share;
-};
-
-struct vlong{
- vlong_value *value;
- int negative;
-};
-
-struct vlong_montgomery{
- vlong R;
- vlong R1;
- vlong m;
- vlong n1;
- vlong T;
- vlong k;
- uint32 N;
-};
+#include <openssl/rsa.h>
struct TRSAPrivateKey{
TRSAPrivateKey(void);
~TRSAPrivateKey(void);
- void init(const char *PrimeP, const char *PrimeQ);
+ void initFromFile(const char *FileName);
void decrypt(uint8 *Data); // single 128 bytes block
// DATA
// =================
- vlong m_PrimeP;
- vlong m_PrimeQ;
- vlong m_U;
- vlong m_DP;
- vlong m_DQ;
+ RSA *m_RSA;
};
struct TXTEASymmetricKey{
- TXTEASymmetricKey(void);
- ~TXTEASymmetricKey(void);
void init(TReadBuffer *Buffer);
void encrypt(uint8 *Data); // single 8 bytes block
void decrypt(uint8 *Data); // single 8 bytes block
diff --git a/src/main.cc b/src/main.cc
index 92e7e73..2089641 100644
--- a/src/main.cc
+++ b/src/main.cc
@@ -216,7 +216,7 @@ void LoadWorldConfig(void){
static void InitAll(void){
try{
ReadConfig();
- //SetQueryManagerLoginData(1, WorldName);
+ SetQueryManagerLoginData(1, WorldName);
LoadWorldConfig();
InitSHM(!BeADaemon);
LockGame();
diff --git a/src/query.hh b/src/query.hh
index dbba9dc..6ac6afb 100644
--- a/src/query.hh
+++ b/src/query.hh
@@ -187,4 +187,6 @@ struct TQueryManagerPoolConnection{
TQueryManagerConnection *QueryManagerConnection;
};
+void SetQueryManagerLoginData(int Type, const char *Data);
+
#endif //TIBIA_QUERY_HH_