/*
**	Command & Conquer Renegade(tm)
**	Copyright 2025 Electronic Arts Inc.
**
**	This program 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 3 of the License, or
**	(at your option) any later version.
**
**	This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
*/

/****************************************************************************\
*        C O N F I D E N T I A L --- W E S T W O O D   S T U D I O S         *
******************************************************************************
Project Name: Generic Game Results Server
File Name   : wencrypt.cpp
Author      : Joe Howes
Start Date  : Jul 9, 1999
Last Update : Jul 20, 1999
------------------------------------------------------------------------------
 
A simple encryption system for game results packets.  Specifically designed
to work only with unsigned char buffers of least 15 bytes long.
 
\****************************************************************************/
#include <string.h>
#include <sys/types.h>
#include <time.h>

#include <stdlib.h>

#include <malloc.h>
#include <winsock2.h>
#include "wencrypt.h"
#include "wnet/packet.h"

/*extern "C" {
	extern unsigned char* PrepareEncryptedPacket(unsigned char* packet, int* size);
}*/
/*------------------------------------------------------------------------------.
| FUNCTION:  PrepareEncryptedPacket()											|
| If an error occurs, a NULL will be returned and the size value will be a		|
| negative integer corresponding to an error in wencrypt.h.						|
| JUL 20/99:  In the Windows WOLAPI, the function RequestGameresSend requires	|
| a buffer that can be passed to the constructor of PacketClass.  Thus we need	|
| to wrap our encrypted buffer in a packet with one big data field and pass		|
| it's Comms Packet output back.												|
`------------------------------------------------------------------------------*/
unsigned char* PrepareEncryptedPacket(unsigned char* packet, int* size)
{
	unsigned char*	rlebuf = NULL;
	unsigned char*	encbuf = NULL;
	PacketClass		wrap;
	static char		field[5] = { "CNTL" };


	rlebuf = (unsigned char *) malloc((*size)*2);


	// Run length encode
	RLEncode(packet, rlebuf, size);                         // Does not allocate any memory


	// XOR encrypt
	encbuf = SimpleEncrypt(rlebuf, size);
	if( rlebuf != NULL )  free(rlebuf);
	if( encbuf == NULL )  return NULL;                     // SimpleEncrypt fills the error in
	
 
    // Now we have to wrap the encrypted data inside a valid PacketClass blob
	wrap.Add_Field((char*)"CNTL", (void*)encbuf, *size);
	free(encbuf);


	return (unsigned char*)(wrap.Create_Comms_Packet(*size));
}


/*------------------------------------------------------------------------------.
| FUNCTION:  DecryptPacket() 													|
| If an error occurs, a NULL will be returned and the size value will be a      |
| negative integer corresponding to an error in wencrypt.h                      |
`------------------------------------------------------------------------------*/
#ifndef __CLIENT__
unsigned char*  DecryptPacket(unsigned char* encbuf, int* size)
{
	unsigned char* rlebuf;
	unsigned char* packet;


        rlebuf = SimpleDecrypt(encbuf, size);
	if( rlebuf == NULL )
		return NULL;					// SimpleDecrypt fills the error in
 
 
        packet = RLDecode(rlebuf, size); 		        // Allocates memory!!
	if( packet == NULL )
		return NULL;					// RLDecode fills the error in


	return packet;
}
#endif


/*------------------------------------------------------------------------------.
| FUNCTION:  RLEncode()															|
| -  src is a buffer conatining the uncompressed data.  						|
| -  dst is a buffer which should be twice the size of the src buffer.			|
| -  size indicates the size of the source buffer at the start of the function,	|
|    and is filled with the length of the dest buffer on return.				|
|																				|
| NOTE:  As is the nature of RLE, it is possible the "compressed" buffer		|
| will be bigger than the uncompressed one by one byte.  Actually in the		|
| absolute worst case it can be bigger than that if the escape code occurs		|
| often enough in the buffer and needs to be stuffed several times.  I *think*	|
| it's impossible for the compressed buffer to ever get twice as big as the		|
| original, being that we choose the lowest frequency value as the escape, but	|
| there will never be a memory overrun this way, and gameres packets are tiny.	|
`------------------------------------------------------------------------------*/
void RLEncode(unsigned char* src, unsigned char* dst, int* size)
{
	int 		i, j, k;
	int		frequencies[256];
	int		lowest;
	unsigned char	escape;
	unsigned char 	currtally = 1;
	unsigned char	curr;


	// Determine what we should use for the escape code.  Ideally it's a value
	// that doesn't occur in the buffer, but we'll settle for the value that
	// appears the least number of times.
	memset(frequencies, 0, 256 * sizeof(int));
	for(i = 0; i < *size; frequencies[src[i++]]++);
	lowest = frequencies[0];
	escape = 0;
	for(i = 0; i < 256; i++)
	{
		if( frequencies[i] == 0 )
		{
			escape = (unsigned char)i;
			break;
		}
		if( frequencies[i] < lowest )
		{
			lowest = frequencies[i];
			escape = (unsigned char)i;
		}
	}
	dst[0] = escape;


	// Build the dest buffer
	curr = src[0];
	for(i = 1, j = 1; i <= *size; i++)
	{
		if( src[i] == curr && i < *size )
			currtally++;
		else
		{
			if( currtally > 3 )
			{
				dst[j++] = escape;
				dst[j++] = currtally;
				dst[j++] = curr;
			}
			else
			{
				// Stuff the escape character
				for(k = 0; k < currtally; k++)
				{
					if( curr == escape )
						dst[j++] = curr;
					dst[j++] = curr;
				}
			}
			curr = src[i];
			currtally = 1;
		}
	}
	*size = j;
}


/*------------------------------------------------------------------------------.
| FUNCTION:  RLDecode()                                                         |
| Nothing special...just remember byte 0 is the escape and not part of the 		|
| original buffer.  If there is an error, the returned pointer will be NULL		|
| and the size will contain a negative number indicating the error code.		|
`------------------------------------------------------------------------------*/
#ifndef __CLIENT__
unsigned char* RLDecode(unsigned char* src, int* size)
{
	int		i, j, k;
	int		dstsize = 0;
	unsigned char 	escape = src[0];
	unsigned char*	dst;


	// How big will the resultant buffer be?
	for(i = 1; i < *size; i++)
	{
		if( src[i] == escape && i < (*size-1) )
		{
			if( src[i+1] == escape )
			{
				dstsize++;			// Stuffed escape
				i++;
			}
			else
			{
				dstsize += (int)src[i+1];	// Add the occurences
				i += 2;				// Skip the token
			}
		}	
		else if( src[i] == escape && i == (*size-1) )
		{
			*size = ERR_SPURIOUS_ESCAPE;
			return NULL;
		}
		else
			dstsize++;
	}

	
	// Build the new buffer
	dst = malloc( dstsize );
	j = 0;
	for(i = 1; i < *size; i++)
	{
		if( src[i] == escape && i < (*size-1) )
		{
			if( src[i+1] == escape )
			{
				dst[j++] = src[i];		// Stuffed escape
				i++;
			}
			else
			{
				for(k = 0; k < (int)src[i+1]; k++)
					dst[j+k] = src[i+2];
				j += k;
				i += 2;				// Skip the token
			}
		}	
		else if( src[i] == escape && i == (*size-1) )
		{
			*size = ERR_SPURIOUS_ESCAPE;
			delete[] dst;
			return NULL;
		}
		else
			dst[j++] = src[i];
	}

	*size = dstsize;
	return dst;
}
#endif


/*--------------------------------------------------------------------------------------.
| FUNCTION:  ran3()                                                                     |
| From 'Numerical Recipies In C', Chapter 7, 'Portable Random Number Generators'.       |
`--------------------------------------------------------------------------------------*/
float ran3(long* idnum)
{
        static int      inext, inextp;
        static long     ma[66];
        static int      iff = 0;
        long            mj, mk;
        int             i, ii, k;
 
 
        if( *idnum < 0 || iff == 0 )
        {
                iff = 1;
                mj = MSEED - (*idnum < 0 ? -*idnum : *idnum);
                mj %= MBIG;
                ma[55] = mj;
                mk = 1;
                for(i = 1; i <= 54; i++)
                {
                        ii = (21*i) % 55;
                        ma[ii] = mk;
                        mk = mj - mk;
                        if( mk < MZ )  mk += MBIG;
                        mj = ma[ii];
                }
                for(k = 1; k <=4; k++)
                {
                        for(i = 1; i <= 55; i++)
                        {
                                ma[i] -= ma[1+(i+30) % 55];
                                if( ma[i] < MZ )  ma[i] += MBIG;
                        }
                }
                inext = 0;
                inextp = 31;
                *idnum = 1;
        }
        if( ++inext == 56 )  inext = 1;
        if( ++inextp == 56 )  inextp = 1;
        mj = ma[inext] - ma[inextp];
        if( mj < MZ )  mj += MBIG;
        ma[inext] = mj;
        return (float) (mj*FAC );
}


/*------------------------------------------------------------------------------.
| FUNCTION:  GenerateKey()														|
| Make a key the same length as the buffer.  Gameres packets are typically		|
| < 600 bytes so period isn't a concern.										|
`------------------------------------------------------------------------------*/
void GenerateKey(unsigned char* key, int len, long seed)
{
	int     i;
        float   running;
	long	num;
 
 
        if( seed > 0 )  seed *= -1;             // Initial val must be negative
        running = ran3(&seed);
 
        for(i = 0; i < len; i++)
        {
                num = (long)(running * (float)255.0);
                if( num < 0 )  num *= -1;
		key[i] = (unsigned char)num;
                running = ran3(&seed);
        }
}

/*--------------------------------------------------------------.
| FUNCTON:  SimpleEncrypt()										|
| Ok, not entirely simple but it's not bad.  We generate the	|
| key, then prepare a new buffer which will contain the 		|
| encrypted data.  Byte 6 of the buffer is a random offset into	|
| bytes 10 - 256 which will contain the four byte seed value	|
| used to construct the key.									|
| If there is an error, the returned pointer will be NULL and 	|
| the len value will be filled with a negative number 			|
| indicating the error code.									|
`--------------------------------------------------------------*/
unsigned char* SimpleEncrypt(const unsigned char* src, int* len)
{
	int		i, j;
	unsigned char*	key;
	unsigned char*	dst;
	unsigned char	offset = 0;
	long		offsetseed = 0;
	unsigned long	netseed;
	long		seed;
	float		limit;


	key = (unsigned char *) malloc( *len );

	dst = (unsigned char *) malloc( (*len)+5 );

	if( *len < 15 )
	{
		*len = -2;
		free( key );
		free( dst );
		return NULL;
	}
	
	// Generate the random stuff
	seed = (long)time(NULL);
	if( seed > 0 )  seed *= -1;			// Don't check for seed == 0 because a ligit
							// packet builder should never set a seed of
							// 0 and it's another mistake a cracker could 
							// possibly make.
	netseed = htonl((unsigned long)seed);
	GenerateKey(key, *len, seed);

	// We want the offset to be somewhere between byte 10 and byte (255-14), or
	// (*len - 4), whichever is smaller
	offsetseed = (long)time(NULL);
	if( offsetseed > 0 )  offsetseed *= -1;
	limit = ((*len) < 241) ? (float)((*len)-14) : (float)241.0;
	offset = (unsigned char)(ran3(&offsetseed) * limit);
	offset += (unsigned char)10;

	
	// Construct the buffer
	for(i = 0, j = 0; i < *len; i++)
	{  
		if( j == 5 )
			dst[j++] = offset;

		if( j == (int)offset )
		{
			memcpy(&dst[j], &netseed, 4);
			j += 4;
		}

		dst[j++]  = src[i] ^ key[i];
	}


	*len += 5;
	free( key );
	return dst;
}


/*--------------------------------------------------------------.
| FUNCTON:  SimpleDecrypt()										|
| We look for the offset, grab the seed, generate a key, then	|
| decrypt.														|
`--------------------------------------------------------------*/
#ifndef __CLIENT__
unsigned char* SimpleDecrypt(unsigned char* src, int* len)
{
	int             i, j;
        unsigned char*  key;
        unsigned char*  dst;
        unsigned char   offset = 0;
        unsigned long  	netseed;
        long   		seed;



	key = malloc( *len );

	dst = malloc((*len) - 5 );


	if( *len < 20 )
	{
		*len = -2;
		delete[] key;
		delete[] dst;
		return NULL;
	}
 
	
	// Generate the key
	offset = src[5];
	memcpy(&netseed, &src[offset], 4);
	seed = (long)ntohl(netseed);
	if( seed > 0 )
	{
		*len = -3;
		delete[] key;
		delete[] dst;
		return NULL;
	}
	GenerateKey(key, (*len)-5, seed);


        // Construct the buffer
        for(i = 0, j = 0; i < *len; i++)
        {
                if( i == 5 )
			continue;
                else if( i >= (int)offset && i <= (int)(offset+(unsigned char)3) )
			continue;
                else
		{
                        dst[j] = src[i] ^ key[j];
			j++;
		}
        }
 
 
	*len -= 5;
	delete[] key;
        return dst;
}
#endif