//
// Copyright 2020 Electronic Arts Inc.
//
// TiberianDawn.DLL and RedAlert.dll and corresponding 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 3 of the License, or (at your option) any later version.

// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed 
// in the hope that it will be useful, but with permitted additional restrictions 
// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT 
// distributed with this program. You should have received a copy of the 
// GNU General Public License along with permitted additional restrictions 
// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection

/* $Header: /CounterStrike/PKPIPE.CPP 1     3/03/97 10:25a Joe_bostic $ */
/***********************************************************************************************
 ***              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 : Command & Conquer                                            *
 *                                                                                             *
 *                                                                                             *
 *                    File Name : PKPIPE.CPP                                                   *
 *                                                                                             *
 *                   Programmer : Joe L. Bostic                                                *
 *                                                                                             *
 *                   Start Date : 07/07/96                                                     *
 *                                                                                             *
 *                  Last Update : July 12, 1996 [JLB]                                          *
 *                                                                                             *
 *---------------------------------------------------------------------------------------------*
 * Functions:                                                                                  *
 *   PKPipe::Encrypted_Key_Length -- Fetch the encrypted key length.                           *
 *   PKPipe::Key -- Submit a key to enable processing of data flow.                            *
 *   PKPipe::PKPipe -- Constructor for the public key pipe object.                             *
 *   PKPipe::Plain_Key_Length -- Returns the number of bytes to encrypt key.                   *
 *   PKPipe::Put -- Submit data to the pipe for processing.                                    *
 *   PKPipe::Put_To -- Chains one pipe to another.                                             *
 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */


#include	"pkpipe.h"


/***********************************************************************************************
 * PKPipe::PKPipe -- Constructor for the public key pipe object.                               *
 *                                                                                             *
 *    This will construct the public key pipe object.                                          *
 *                                                                                             *
 * INPUT:   control  -- The method used to process the data flow (encrypt or decrypt).         *
 *                                                                                             *
 *          rnd      -- Reference to a random number generate used to create the internal      *
 *                      blowfish key.                                                          *
 *                                                                                             *
 * OUTPUT:  none                                                                               *
 *                                                                                             *
 * WARNINGS:   none                                                                            *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   07/11/1996 JLB : Created.                                                                 *
 *=============================================================================================*/
PKPipe::PKPipe(CryptControl control, RandomStraw & rnd) :
	IsGettingKey(true),
	Rand(rnd),
	BF((control == ENCRYPT) ? BlowPipe::ENCRYPT : BlowPipe::DECRYPT),
	Control(control),
	CipherKey(NULL),
	Counter(0),
	BytesLeft(0)
{
}


/***********************************************************************************************
 * PKPipe::Put_To -- Chains one pipe to another.                                               *
 *                                                                                             *
 *    This handles linking of one pipe to this pipe. Data will flow from this PKPipe to the    *
 *    pipe segment specified. Special handling is done so that piping actually flows to the    *
 *    embedded blowfish pipe and then flows to the designated pipe.                            *
 *                                                                                             *
 * INPUT:   pipe  -- Pointer to the pipe that this pipe segment is to send data to.            *
 *                                                                                             *
 * OUTPUT:  none                                                                               *
 *                                                                                             *
 * WARNINGS:   none                                                                            *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   07/12/1996 JLB : Created.                                                                 *
 *=============================================================================================*/
void PKPipe::Put_To(Pipe * pipe)
{
	if (BF.ChainTo != pipe) {
		if (pipe != NULL && pipe->ChainFrom != NULL) {
			pipe->ChainFrom->Put_To(NULL);
			pipe->ChainFrom = NULL;
		}

		if (BF.ChainTo != NULL) {
			BF.ChainTo->ChainFrom = NULL;
		}
		BF.ChainTo = pipe;
		if (pipe != NULL) {
			pipe->ChainFrom = &BF;
		}
		BF.ChainFrom = this;
		ChainTo = &BF;
	}
}


/***********************************************************************************************
 * PKPipe::Key -- Submit a key to enable processing of data flow.                              *
 *                                                                                             *
 *    This routine must be called with a valid key pointer in order for encryption/description *
 *    to be performed on the data stream. Prior to calling this routine or after calling this  *
 *    routine with a NULL pointer, the data stream will pass through this pipe without         *
 *    modification.                                                                            *
 *                                                                                             *
 * INPUT:   key   -- Pointer to the key to use for processing. Pass NULL if process is to be   *
 *                   terminated.                                                               *
 *                                                                                             *
 * OUTPUT:  none                                                                               *
 *                                                                                             *
 * WARNINGS:   none                                                                            *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   07/07/1996 JLB : Created.                                                                 *
 *=============================================================================================*/
void PKPipe::Key(PKey const * key)
{
	if (key == NULL) {
		Flush();
		IsGettingKey = false;
	}
	CipherKey = key;

	if (CipherKey != NULL) {
		IsGettingKey = true;
		if (Control == DECRYPT) {
			Counter = BytesLeft = Encrypted_Key_Length();
		}
	}
}


/***********************************************************************************************
 * PKPipe::Put -- Submit data to the pipe for processing.                                      *
 *                                                                                             *
 *    This routine (if processing as been enabled by a previous key submission) will           *
 *    encrypt or decrypt the data stream that passes through it. When encrypting, the data     *
 *    stream will increase in size by about 10% (bit it varies according to the key used).     *
 *                                                                                             *
 * INPUT:   source   -- Pointer to the data to be submitted to the pipe stream.                *
 *                                                                                             *
 *          length   -- The number of bytes submitted.                                         *
 *                                                                                             *
 * OUTPUT:  Returns with the actual number of byte output at the final end of the pipe.        *
 *                                                                                             *
 * WARNINGS:   none                                                                            *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   07/07/1996 JLB : Created.                                                                 *
 *=============================================================================================*/
int PKPipe::Put(void const * source, int length)
{
	/*
	**	If the parameter seem illegal, then pass the pipe request to the
	**	next pipe in the chain and let them deal with it.
	*/
	if (source == NULL || length < 1 || CipherKey == NULL) {
		return(Pipe::Put(source, length));
	}

	int total = 0;

	/*
	**	Perform a special process if the this is the first part of the data flow. The special
	**	key must be processed first. After this initial key processing, the rest of the data flow
	**	is processed by the blowfish pipe and ignored by the PKPipe.
	*/
	if (IsGettingKey) {

		/*
		**	When encrypting, first make the key block and then pass the data through the
		**	normal blowfish processor.
		*/
		if (Control == ENCRYPT) {

			/*
			**	Generate the largest blowfish key possible.
			*/
			char buffer[MAX_KEY_BLOCK_SIZE];
			memset(buffer, '\0', sizeof(buffer));
			Rand.Get(buffer, BLOWFISH_KEY_SIZE);

			/*
			**	Encrypt the blowfish key (along with any necessary pad bytes).
			*/
			int didput = CipherKey->Encrypt(buffer, Plain_Key_Length(), Buffer);
			total += Pipe::Put(Buffer, didput);
			BF.Key(buffer, BLOWFISH_KEY_SIZE);

			IsGettingKey = false;

		} else {

			/*
			**	First try to accumulate a full key.
			*/
			int toget = (BytesLeft < length) ? BytesLeft : length;
			memmove(&Buffer[Counter-BytesLeft], source, toget);
			length -= toget;
			BytesLeft -= toget;
			source = (char *)source + toget;

			/*
			**	If a full key has been accumulated, then decrypt it and feed the
			**	key to the blowfish engine.
			*/
			if (BytesLeft == 0) {
				char buffer[MAX_KEY_BLOCK_SIZE];
				CipherKey->Decrypt(Buffer, Counter, buffer);
				BF.Key(buffer, BLOWFISH_KEY_SIZE);

				IsGettingKey = false;
			}
		}
	}

	/*
	**	If there are any remaining bytes to pipe through, then
	**	pipe them through now -- they will be processed by the
	**	blowfish engine.
	*/
	total += Pipe::Put(source, length);
	return(total);
}


/***********************************************************************************************
 * PKPipe::Encrypted_Key_Length -- Fetch the encrypted key length.                             *
 *                                                                                             *
 *    This returns the total number of bytes (after encryption) that the blowfish key will     *
 *    consume. It should be possible to get a block of this size, then pass it to the          *
 *    public key decrypter and the result will be the full blowfish key.                       *
 *                                                                                             *
 * INPUT:   none                                                                               *
 *                                                                                             *
 * OUTPUT:  Returns with the number of bytes that the encrypted blowfish key required.         *
 *                                                                                             *
 * WARNINGS:   none                                                                            *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   07/11/1996 JLB : Created.                                                                 *
 *=============================================================================================*/
int PKPipe::Encrypted_Key_Length(void) const
{
	if (CipherKey == NULL) return(0);
	return(CipherKey->Block_Count(BLOWFISH_KEY_SIZE) * CipherKey->Crypt_Block_Size());
}


/***********************************************************************************************
 * PKPipe::Plain_Key_Length -- Returns the number of bytes to encrypt key.                     *
 *                                                                                             *
 *    This is the number of plain (unencrypted) bytes that the blowfish key will take up. This *
 *    is actually the number of plain blocks minimum that can contain the full blowfish        *
 *    key. The public key cryptography system encrypts in whole blocks only.                   *
 *                                                                                             *
 * INPUT:   none                                                                               *
 *                                                                                             *
 * OUTPUT:  Returns with the total number of bytes that will contain the full blowfish key     *
 *          and still be an even block size for the public key cryptography process.           *
 *                                                                                             *
 * WARNINGS:   This value is probably be larger than the actual blowfish key length.           *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   07/11/1996 JLB : Created.                                                                 *
 *=============================================================================================*/
int PKPipe::Plain_Key_Length(void) const
{
	if (CipherKey == NULL) return(0);
	return(CipherKey->Block_Count(BLOWFISH_KEY_SIZE) * CipherKey->Plain_Block_Size());
}