aboutsummaryrefslogtreecommitdiff
path: root/src/crypto.cc
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/crypto.cc
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/crypto.cc')
-rw-r--r--src/crypto.cc107
1 files changed, 106 insertions, 1 deletions
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;
+}