Add support for server list encryption and tell the master server to return the server list encrypted

This commit is contained in:
smallmodel 2025-02-22 20:36:32 +01:00
parent 512831dea9
commit 08c718d232
No known key found for this signature in database
GPG key ID: 9F2D623CEDF08512
4 changed files with 312 additions and 23 deletions

View file

@ -11,6 +11,7 @@ file(GLOB SRCS_common
"./hashtable.c"
"./md5c.c"
"./gutil.c"
"./gcrypt.c"
)
file(GLOB SRCS_gcdkey

198
code/gamespy/gcrypt.c Normal file
View file

@ -0,0 +1,198 @@
/*
===========================================================================
Copyright (C) 2025 the OpenMoHAA team
This file is part of OpenMoHAA source code.
OpenMoHAA source code 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 2 of the License,
or (at your option) any later version.
OpenMoHAA source code 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 OpenMoHAA source code; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
===========================================================================
*/
#include "gcrypt.h"
#include "../qcommon/q_platform.h"
#include "../qcommon/q_shared.h"
static unsigned int rotl32(unsigned int value, unsigned int count)
{
return (value << count) | (value >> (-count & 31));
}
static unsigned int crypt_seek(GCryptInfo *info, unsigned int n1, unsigned int n2)
{
int part;
int i;
int keyIndex;
int xorKey;
int current;
current = 0x8000;
xorKey = n1;
keyIndex = 0;
i = 1;
info->offset = 0;
while (current > 0) {
xorKey += i;
keyIndex += xorKey;
xorKey += keyIndex;
if (n2 & current) {
part = rotl32(~xorKey, 24);
xorKey = rotl32(info->key[(unsigned char)part] ^ part, 24);
part = rotl32(info->key[(unsigned char)keyIndex] ^ keyIndex, 8);
xorKey ^= info->key[(unsigned char)xorKey];
keyIndex = rotl32(info->key[(unsigned char)part] ^ part, 8);
i += 1 + i;
} else {
info->msg[info->offset] = xorKey;
info->msg[info->offset + 16] = keyIndex;
info->msg[info->offset + 32] = i;
info->offset++;
part = rotl32(keyIndex, 24);
keyIndex = rotl32(info->key[(unsigned char)part] ^ part, 24);
part = rotl32(info->key[(unsigned char)xorKey] ^ xorKey, 8);
keyIndex ^= info->key[(unsigned char)keyIndex];
xorKey = rotl32(info->key[(unsigned char)part] ^ part, 8);
i *= 2;
}
current >>= 1;
}
info->xorKey = xorKey;
info->keyIndex = keyIndex;
info->i = i;
info->start = n1;
return keyIndex ^ xorKey;
}
static void crypt_encrypt(GCryptInfo *info, unsigned int *words, int len)
{
int part;
unsigned int i;
unsigned int w;
int keyIndex;
int xorKey;
int offset;
offset = info->offset;
xorKey = info->xorKey;
keyIndex = info->keyIndex;
i = info->i;
for (w = 0; w < len; w++) {
while (i < 0x10000) {
xorKey += i;
keyIndex += xorKey;
xorKey += keyIndex;
info->msg[offset] = xorKey;
info->msg[offset + 16] = keyIndex;
info->msg[offset + 32] = i;
offset++;
part = rotl32(keyIndex, 24);
keyIndex = rotl32(info->key[(unsigned char)part] ^ part, 24);
part = rotl32(info->key[(unsigned char)xorKey] ^ xorKey, 8);
keyIndex ^= info->key[(unsigned char)keyIndex];
xorKey = rotl32(info->key[(unsigned char)part] ^ part, 8);
i *= 2;
}
words[w] = LittleLong(keyIndex ^ xorKey);
offset--;
if (offset < 0) {
offset = 0;
}
part = rotl32(~info->msg[offset], 24);
xorKey = rotl32(info->key[(unsigned char)part] ^ part, 24);
part = rotl32(info->key[(unsigned char)info->msg[offset + 16]] ^ info->msg[offset + 16], 8);
xorKey ^= info->key[(unsigned char)xorKey];
keyIndex = rotl32(info->key[(unsigned char)part] ^ part, 8);
i = info->msg[offset + 32] + 1 + info->msg[offset + 32];
}
info->offset = offset;
info->xorKey = xorKey;
info->keyIndex = keyIndex;
info->i = i;
}
void init_crypt_key(unsigned char *src, unsigned int len, GCryptInfo *info)
{
int index;
int i, j, k;
int tmp;
info->wordPtr = NULL;
for (k = 0; k < 256; k++) {
info->key[k] = 0;
}
for (i = 0; i < 4; i++) {
for (k = 0; k < 256; k++) {
info->key[k] = (info->key[k] << 8) + k;
}
index = i;
for (j = 0; j < 2; j++) {
for (k = 0; k < 256; k++) {
index = ((info->key[k] & 0xff) + src[k % len] + index) & 0xff;
tmp = info->key[k];
info->key[k] = info->key[index];
info->key[index] = tmp;
}
}
}
for (k = 0; k < 256; k++) {
info->key[k] = LittleLong(info->key[k]);
}
for (k = 0; k < 256; k++) {
info->key[k] ^= k;
}
crypt_seek(info, 0, 0);
}
void crypt_docrypt(GCryptInfo *info, unsigned char *out, int len)
{
int i;
for (i = 0; i < len; i++) {
if (!info->wordPtr || ((char *)info->wordPtr - (char *)info->words) >= (sizeof(info->words) - 1)) {
info->wordPtr = (unsigned char *)info->words;
crypt_encrypt(info, info->words, ARRAY_LEN(info->words));
}
char value[4];
CopyLittleLong(value, info->wordPtr);
memcpy(info->wordPtr, value, sizeof(value));
out[i] ^= *(unsigned char *)info->wordPtr;
info->wordPtr++;
}
}

40
code/gamespy/gcrypt.h Normal file
View file

@ -0,0 +1,40 @@
/*
===========================================================================
Copyright (C) 2025 the OpenMoHAA team
This file is part of OpenMoHAA source code.
OpenMoHAA source code 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 2 of the License,
or (at your option) any later version.
OpenMoHAA source code 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 OpenMoHAA source code; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
===========================================================================
*/
#pragma once
typedef struct {
int key[256];
int msg[48];
int offset;
int xorKey;
int keyIndex;
int i;
int start;
unsigned int words[16];
unsigned char *wordPtr;
} GCryptInfo;
void init_crypt_key(unsigned char *src, unsigned int len, GCryptInfo *info);
void crypt_docrypt(GCryptInfo *info, unsigned char *out, int len);

View file

@ -45,6 +45,9 @@ Fax(714)549-0757
#include <string.h>
#include <stdlib.h>
// Added in 2.0
#include "gcrypt.h"
#define MSHOST MASTER_SERVER_HOST
#define MSPORT 28900
#define SERVER_GROWBY 64
@ -87,6 +90,10 @@ struct GServerListImplementation
unsigned long lanstarttime;
GQueryType querytype;
HashTable keylist;
// Added in 2.0
GCryptInfo cryptinfo;
int encryptdata;
};
GServerList g_sortserverlist; //global serverlist for sorting info!!
@ -125,6 +132,7 @@ GServerList ServerListNew(const char *gamename, const char *enginename, const ch
assert(CallBackFn != NULL);
list->instance = instance;
list->sortkey = "";
list->encryptdata = 1;
SocketStartUp();
return list;
}
@ -242,7 +250,7 @@ static GError SendListRequest(GServerList serverlist, char *filter)
{
char data[256], *ptr, result[64];
int len;
int i;
len = recv(serverlist->slsocket, data, sizeof(data) - 1, 0);
if (gsiSocketIsError(len))
@ -254,11 +262,25 @@ static GError SendListRequest(GServerList serverlist, char *filter)
return GE_DATAERROR;
ptr = ptr + strlen(SECURE);
gs_encrypt ( (uchar *) serverlist->seckey, 6, (uchar *)ptr, 6 );
if (serverlist->encryptdata) {
// Added in 2.0
for(i = 0; i < 6; i++) {
ptr[i] ^= serverlist->seckey[i];
}
}
gs_encode ( (uchar *)ptr, 6, (uchar *) result );
//validate to the master
sprintf(data, "\\gamename\\%s\\gamever\\%s\\location\\0\\validate\\%s\\final\\\\queryid\\1.1\\",
serverlist->enginename, ENGINE_VERSION, result); //validate us
if (serverlist->encryptdata) {
// Added in 2.0
// Encrypt data
//validate to the master
sprintf(data, "\\gamename\\%s\\gamever\\%s\\location\\0\\validate\\%s\\enctype\\2\\final\\\\queryid\\1.1\\",
serverlist->enginename, "2", result); //validate us
} else {
//validate to the master
sprintf(data, "\\gamename\\%s\\gamever\\%s\\location\\0\\validate\\%s\\final\\\\queryid\\1.1\\",
serverlist->enginename, ENGINE_VERSION, result); //validate us
}
len = send ( serverlist->slsocket, data, strlen(data), 0 );
if (gsiSocketIsError(len) || len == 0)
@ -337,6 +359,8 @@ GError ServerListUpdate2(GServerList serverlist, gbool async, char *filter, GQue
if (error) return error;
serverlist->nextupdate = 0;
serverlist->abortupdate = 0;
// Added in 2.0
serverlist->cryptinfo.offset = -1;
if (!async)
DoSyncLoop(serverlist);
@ -577,27 +601,53 @@ static GError ServerListReadList(GServerList serverlist)
return GE_NOCONNECT;
}
oldlen += len;
if (serverlist->encryptdata && serverlist->cryptinfo.offset != -1) {
// Added in 2.0
crypt_docrypt(&serverlist->cryptinfo, data + oldlen, len);
}
oldlen += len;
p = data;
while (p - data <= oldlen - 6)
{
if (strncmp(p,"\\final\\",7) == 0 || serverlist->abortupdate)
{
closesocket(serverlist->slsocket);
serverlist->slsocket = INVALID_SOCKET;
oldlen = 0; //clear data so it can be used again
ServerListModeChange(serverlist, sl_querying);
return 0; //get out!!
}
if (oldlen < 6) //no way it could be a full IP, quit
break;
memcpy(&ip,p,4);
p += 4;
memcpy(&port,p,2);
p += 2;
ServerListAddServer(serverlist,ip, ntohs(port), serverlist->querytype );
}
if (!serverlist->encryptdata) {
serverlist->cryptinfo.offset = 0;
} else if (serverlist->cryptinfo.offset == -1) {
// Added in 2.0
if (oldlen > (*p ^ 0xEC)) {
*p ^= 0xEC;
len = strlen(serverlist->seckey);
for (i = 0; i < len; i++) {
p[i + 1] ^= serverlist->seckey[i];
}
init_crypt_key((unsigned char *)p + 1, *p, &serverlist->cryptinfo);
p += *p + 1;
crypt_docrypt(&serverlist->cryptinfo, (unsigned char *)p, oldlen - (p - data));
}
}
if (serverlist->cryptinfo.offset != -1)
{
while (p - data <= oldlen - 6)
{
if (strncmp(p,"\\final\\",7) == 0 || serverlist->abortupdate)
{
closesocket(serverlist->slsocket);
serverlist->slsocket = INVALID_SOCKET;
oldlen = 0; //clear data so it can be used again
ServerListModeChange(serverlist, sl_querying);
return 0; //get out!!
}
if (oldlen < 6) //no way it could be a full IP, quit
break;
memcpy(&ip,p,4);
p += 4;
memcpy(&port,p,2);
p += 2;
ServerListAddServer(serverlist,ip, ntohs(port), serverlist->querytype );
}
}
oldlen = oldlen - (p - data);
memmove(data,p,oldlen); //shift it over
return 0;