/*
** xpkSICR - SImple CRypt library
** example code for a DiskProtection compatible XPK sublibrary
** xor every longword with the password value
**
** This code is based on various other XPK examples and modified 1996 by 
** Patrick Ohly. It can be compiled with SAS/C. It may be used for your
** own libraries, as long as you mention DiskProtection and my name.
** You should clearly state that a DiskProtection compatible xpklibrary
** should not be used with XPK when distributing it!
**
** DiskProtection compatible means:
** - a valid XPK sublibrary
** - stored in libs:compressors
** - XPKIF_NEEDPASSWD set
** - XPKIF_LOOSY not set
** - size of a chunk does not increase when encrypted
**
** The last point is a problem with the XPK standard:
** xpkmaster.library expects sublibraries to save the mode
** with every encrypted/packed chunk and to be able to recognize
** wrong passwords, i.e. checksums are nescessary, too.
** Both is not possible with DiskProtection, because every diskblock
** has to be stored in place and there is no space for any extra byte.
**
** However, you _can_ use different modes with DiskProtection:
** In contrast to the xpkmaster.library DiskProtection provides the
** correct mode on decryption, too. You also don't have to bother with checking
** the password. DiskProtection does this itself.
*/

#include <proto/exec.h>
#include <exec/memory.h>
#include <limits.h>

#include <libraries/xpksub.h>

/*
** only one mode in this example
** speed is guessed
*/
static struct XpkMode SicrMode =
{
	NULL, 100, XPKMF_A3000SPEED, 0,0, 500,500, 0, USHRT_MAX/1024, "simple" 
};

static struct XpkInfo SicrInfo = {
	0,			/* info version */
	1,			/* lib  version */
	0,			/* master vers  */
	0,			/* ModesVersion	*/
	"SICR",			/* short name   */
	"Simple Crypt 0.1",	/* long name    */
	"simply xor's every long word",	/* description  */
	(ULONG)'SICR',			/* 4 letter ID  */
	XPKIF_PK_CHUNK  |	/* flags        */
	XPKIF_UP_CHUNK  |
	XPKIF_ENCRYPTION|
	XPKIF_NEEDPASSWD,
	0x40000000, /* 1GB */	/* max in chunk */
	0,			/* min in chunk */
	32768,			/* def in chunk */
	"Encrypting",		/* pk message   */
	"Decrypting",		/* up message   */
	"Encrypted",		/* pk past msg  */
	"Decrypted",		/* up past msg  */
	0,			/* def mode     */
	0,			/* pad          */
	&SicrMode		/* modes        */
};


/*
** Returns the info structure of our encryptor
**/
struct XpkInfo * __saveds __asm
SicrXpksPackerInfo(void)
{
	return &SicrInfo;
}


/*
** This forces the next chunk to be decryptable independent from the
** previous one. This is always the case in SICR.
**/
long __saveds __asm
SicrXpksPackReset(register __a0 XPARAMS* xpar)
{
	return XPKERR_OK;
}

/*
** help function:
** calculate value from password
** taken from FEAL (special thanks to Christian von Roques)
*/

typedef unsigned char    Word8;
typedef unsigned short   Word16;
typedef unsigned long    Word32;
typedef struct { unsigned long lft, rgt; } Word64;

static Word64 password (APTR *s)
{
	Word8* p=(Word8*)s;
	Word64 x;
	Word32 t;

	x.lft = 0;
	x.rgt = 0;

	while (p && *p)
	{
		t = x.lft;
		x.lft = x.rgt;
		x.rgt = ((t << 9) | (t >> 23)) + (Word32) *p;
		p++;
	}
	return(x);
}


/*
** Encrypt a chunk
**
** xpar->Sub[0-3] can be stored to save private data,
** e.g. pointers to allocated resources
**
** This fields will be zero for the first chunk, so
** you can initialize encryption then, store your data
** there and set them to zero again when cleaning up.
**
** One xpar block will never be used for both encryption
** and decryption without cleaning up before changing the
** mode of operation.
**/
long __saveds __asm
SicrXpksPackChunk(register __a0 XPARAMS *xpar)
{
	if(xpar->InLen <= xpar->OutBufLen)
	{
		ULONG  *r = (ULONG*)xpar->InBuf, 
		       *w = (ULONG*)xpar->OutBuf,
		       *e = (ULONG *)&(((UBYTE *)r)[xpar->InLen]);
		ULONG xor = xpar->Sub[0];
		Word64 x;
		
		if(!xor)
		{
			/* first hunk, calculate password */
			x = password(xpar->Password);
			xor  = x.lft ^ x.rgt;
			/* should better not be zero :-) */
			if(!xor)
				xor = 0xFFFFFFFF;
			xpar->Sub[0] = xor;
		}
  
		/* encrypt */
		for(;r < e; r++, w++)
			*w = *r ^ xor;

		/* must be set! */
		xpar->OutLen = xpar->InLen;
		return XPKERR_OK;
	}
	else
	{
		/* error, should never occur with DiskProtection */
		xpar->OutLen = 0;
		return XPKERR_SMALLBUF;
	}
}

/*
** free all resource allocated in XircXpksPackChunk
*/
void __saveds __asm
SicrXpksPackFree(register __a0 XPARAMS* xpar)
{
	/* xpar block could be used again with different password,
	** thus this must be set to zero again
	*/
	xpar->Sub[0] = NULL;
}

/*
** Decrypt a chunk
**/
long __saveds __asm
SicrXpksUnpackChunk(register __a0 XPARAMS *xpar)
{
	/* with SICR encryption is the same as decryption */
	return SicrXpksPackChunk(xpar);
}

/*
** free all resource allocated in XircXpksPackChunk
*/
void __saveds __asm
SicrXpksUnpackFree(register __a0 XPARAMS* xpar)
{
	/* see above... */
	xpar->Sub[0] = NULL;
}
