/*
**	$Id: termCrypt.c,v 1.3 92/08/15 20:13:50 olsen Sta Locker: olsen $
**	$Revision: 1.3 $
**	$Date: 92/08/15 20:13:50 $
**
**	Data encryption routines
**
**	Copyright © 1990-1992 by Olaf `Olsen' Barthel & MXM
**		All Rights Reserved
*/

#include "termGlobal.h"

	/* The width of the cell ring is defined here. A word of warning: never
	 * encrypt a file and forget the access password, since it will be
	 * extremely hard to break the code (with 30 cells there are 256^30
	 * possible values each cell may be initialized to which means that
	 * you could have to try more than 1.7e+72 combinations for each single
	 * password character before actually finding the matching password).
	 * See `Random sequence generation by cellular automata' by Steven
	 * Wolfram (Institute for Advanced Study; Advances in Applied
	 * Mathematics) for more information.
	 */

#define CELL_WIDTH 30

	/* The cell ring and the ring index pointers. */

STATIC UBYTE Cell[2][CELL_WIDTH + 2],From,To;

	/* The pattern the cell ring will be filled with. The pattern
	 * may seem familiar: these are the first 32 digits of Pi.
	 */

STATIC UBYTE Pi[32] = { 3,1,4,1,5,9,2,6,5,3,5,8,9,7,9,3,2,3,8,4,6,2,6,4,3,3,8,3,2,7,9,5 };

	/* Automaton():
	 *
	 *	A cellular automaton working on a ring of celles, producing
	 *	random data in each single cell .
	 */

STATIC UBYTE
Automaton()
{
	register WORD i;

		/* Operate on the cell ring... */

	for(i = 1 ; i <= CELL_WIDTH ; i++)
		Cell[To][i] = Cell[From][i - 1] ^ (Cell[From][i] | Cell[From][i + 1]);

		/* Operate on first and last element. */

	Cell[To][0]			= Cell[From][CELL_WIDTH + 1] ^ (Cell[From][0]              | Cell[From][1]);
	Cell[To][CELL_WIDTH + 1]	= Cell[From][CELL_WIDTH]     ^ (Cell[From][CELL_WIDTH + 1] | Cell[From][0]);

		/* Swap cell rings. */

	To	 = From;
	From	^= 1;

		/* Return contents of first cell. */

	return(Cell[From][0]);
}

	/* Encrypt(UBYTE *Source,UBYTE *Destination,UBYTE *Key):
	 *
	 *	Encrypt data using cellular automaton as a random number generator.
	 */

UBYTE *
Encrypt(UBYTE *Source,WORD SourceLen,UBYTE *Destination,UBYTE *Key,WORD KeyLen,BYTE Prefill)
{
	register WORD i,j;

		/* Set up cell ring index pointers. */

	From	= 0;
	To	= 1;

	if(Prefill)
	{
		memcpy(&Cell[0][KeyLen],Pi,32 - KeyLen);

		memcpy(Cell[0],Key,KeyLen);
	}
	else
	{
			/* Initialize the cell ring with the key contents. */

		for(i = 0 , j = KeyLen - 1 ; i < CELL_WIDTH + 2 ; i++)
		{
			Cell[0][i] = Key[j];

			if(j)
				j--;
			else
				j = KeyLen - 1;
		}
	}

		/* Encrypt the source data. */

	for(i = 0 ; i < SourceLen ; i++)
		Destination[i] = ((WORD)Source[i] + Automaton()) % 256;

		/* Return result. */

	return(Destination);
}

	/* Decrypt(UBYTE *Source,UBYTE *Destination,UBYTE *Key):
	 *
	 *	Decrypt data using cellular automaton as a random number generator.
	 */

UBYTE *
Decrypt(UBYTE *Source,WORD SourceLen,UBYTE *Destination,UBYTE *Key,WORD KeyLen,BYTE Prefill)
{
	register WORD i,j,Code;

		/* Set up cell ring index pointers. */

	From	= 0;
	To	= 1;

	if(Prefill)
	{
		memcpy(&Cell[0][KeyLen],Pi,32 - KeyLen);

		memcpy(Cell[0],Key,KeyLen);
	}
	else
	{
			/* Initialize the cell ring with the key contents. */

		for(i = 0 , j = KeyLen - 1 ; i < CELL_WIDTH + 2 ; i++)
		{
			Cell[0][i] = Key[j];

			if(j)
				j--;
			else
				j = KeyLen - 1;
		}
	}

		/* Decrypt the source data. */

	for(i = 0 ; i < SourceLen ; i++)
	{
		if((Code = Source[i] - Automaton()) < 0)
			Code = 256 + Code;

		Destination[i] = Code;
	}

		/* Return result. */

	return(Destination);
}

	/* CryptPanel(UBYTE *Buffer):
	 *
	 *	Ask the user to enter an access password, do not display
	 *	it while typing.
	 */

BYTE
CryptPanel(UBYTE *Buffer)
{
	STATIC struct IntuiText IText[] =
	{
		1,0,JAM1,30,19,&DefaultFont,NULL,	&IText[1],
		1,0,JAM1,46,28,&DefaultFont,NULL,	NULL
	};

	struct Window	*PanelWindow;
	BYTE		 Result = FALSE,FgPen;

	IText[0] . IText = LocaleString(MSG_TERMCRYPT_PLEASE_ENTER_ACCESS_PASSWORD_TXT);
	IText[1] . IText = LocaleString(MSG_TERMCRYPT_PRESS_RETURN_WHEN_DONE_TXT);

	switch(Config . ColourMode)
	{
		case COLOUR_EIGHT:	FgPen = 7;
					break;

		case COLOUR_SIXTEEN:	FgPen = 15;
					break;

		case COLOUR_AMIGA:	if(Config . DisableBlinking & TERMINAL_FASTER)
						FgPen = 1;
					else
						FgPen = 2;

					break;

		case COLOUR_MONO:	FgPen = 1;

					break;
	}

	IText[0] . FrontPen = IText[1] . FrontPen = FgPen;

	if(PanelWindow = OpenWindowTags(NULL,
		WA_Width,	294,
		WA_Height,	46,

		WA_Left,	(Screen -> Width - 294) >> 1,
		WA_Top,		(Screen -> Height - 46) >> 1,
		WA_Activate,	TRUE,
		WA_DragBar,	TRUE,
		WA_DepthGadget,	TRUE,
		WA_CloseGadget,	TRUE,
		WA_RMBTrap,	TRUE,
		WA_CustomScreen,Screen,

		WA_IDCMP,	IDCMP_CLOSEWINDOW | IDCMP_VANILLAKEY,

		WA_Title,	LocaleString(MSG_TERMCRYPT_ENTER_PASSWORD_TXT),
	TAG_DONE))
	{
		struct IntuiMessage	*Massage;
		ULONG			 Class,Code;
		BYTE			 Terminated	= FALSE,
					 Count		= 0;

		PrintIText(PanelWindow -> RPort,&IText[0],0,0);

		DrawBevelBox(PanelWindow -> RPort,8,13,PanelWindow -> Width - 16,PanelWindow -> Height - (13 + 5),
			GT_VisualInfo,	VisualInfo,
			GTBB_Recessed,	TRUE,
		TAG_DONE);

		while(!Terminated)
		{
			WaitPort(PanelWindow -> UserPort);

			while(!Terminated && (Massage = (struct IntuiMessage *)GetMsg(PanelWindow -> UserPort)))
			{
				Class	= Massage -> Class;
				Code	= Massage -> Code;

				ReplyMsg(Massage);

				if(Class == IDCMP_CLOSEWINDOW)
					Terminated = TRUE;

				if(Class == IDCMP_VANILLAKEY)
				{
					switch(Code)
					{
						case '\033':	Terminated = TRUE;
								break;

						case '\r':	Terminated = TRUE;

								if(Count)
									Result = TRUE;

								break;

						case '\b':	if(Count)
									Buffer[Count--] = 0;
								else
									DisplayBeep(PanelWindow -> WScreen);

								break;

						default:	if((Code >= ' ' && Code < 127) || Code >= 160)
								{
									Buffer[Count++] = Code;

									if(Count == 20)
										Terminated = Result = TRUE;
								}
								else
									DisplayBeep(PanelWindow -> WScreen);

								break;
					}
				}
			}
		}

		CloseWindow(PanelWindow);
	}

	return(Result);
}
