/*--- wvcrypt.c -- Simple encryption routines for WinVN.
 *
 *  These simple encryption/decryption routines are used to
 *  protect the user's NNTP server password.
 *  The lack of sophistication is meant to protect the
 *  exportability of this code.	For better stuff, see the
 *  non-anonymous FTP site ripem.msu.edu.
 */

/*
 * $Id: wvcrypt.c 1.4 1994/05/26 22:58:38 jglasser Exp $
 */

#include <time.h>
#include <string.h>
#include <stdlib.h>

/* #define DEBUG 1 */

#define ROTORSIZE 256
#define NSEEDBYTES 4
#define RANBUFSIZE 15
#define RANLAG1	 9
#define RANLAG2	 14

static unsigned char RanbufMaster[RANBUFSIZE] = {
	0, 0, 0, 0, 56, 34, 199, 77, 245, 252, 17, 184, 130, 215, 102 };
static unsigned char *Ranbuf;
static Ranidx;

void MRRMakeRotors(unsigned char *ranbuf, unsigned char *rotorE,
 unsigned char *rotorD);
int MRRRan(unsigned char *Ranbuf);
int prencode (unsigned char *bufin , unsigned int nbytes , char *bufcoded );
int prdecode (char *bufcoded , unsigned char *bufplain , int outbufsize );



/*--- function MRREncrypt ------------------------------------------
 *
 *  Encrypt and printably encode a sequence of bytes.
 *
 *  Entry:	plainText	is the input plaintext (binary bytes).
 * 			len			is the number of bytes in the above.
 * 			cipherText	is an output buffer that must be
 * 							2*len+6 bytes long.
 *
 *  Exit:	cipherText	is a zero-terminated ASCII printable
 * 							string of ciphertext.
 */
void
MRREncrypt(unsigned char *plainText,int len,char *cipherText)
{
	time_t mytime;
	int j;
	unsigned char prev=0;
	unsigned char *cipbuf, *cipbuforig;
	unsigned char ranbuf[RANBUFSIZE], rotorE[ROTORSIZE], rotorD[ROTORSIZE];

	if(!len) {
		cipherText[0] = '\0';
		return;
	}
	cipbuf = cipbuforig = malloc (len+4);

	time(&mytime);
	memcpy(ranbuf,RanbufMaster,RANBUFSIZE);
	for(j=0; j<NSEEDBYTES; j++) {
		cipbuf[j] = ranbuf[j] = (unsigned char) (mytime & 0xff);
		mytime >>= 8;
	}

	MRRMakeRotors(ranbuf,rotorE, rotorD);

	for(j=0; j<len; j++) {
		cipbuf[j+NSEEDBYTES] = prev = rotorE[(*(plainText++)+prev)%ROTORSIZE];
	}

	prencode(cipbuf,len+NSEEDBYTES,cipherText);
	free(cipbuforig);
}

int
MRRDecrypt(char *cipherText,unsigned char *plainText, int plainMax)
{
	unsigned char prev=0;
	unsigned char *cipbuf, *cipbuforig;
	unsigned char ranbuf[RANBUFSIZE], rotorE[ROTORSIZE], rotorD[ROTORSIZE];
	int j, nbytes, ch;

	if(!strlen(cipherText)) {
		plainText[0] = '\0';
		return 0;
	}

	cipbuf = cipbuforig = malloc(plainMax+4);

	nbytes = prdecode(cipherText,cipbuf,plainMax) - NSEEDBYTES;
	memcpy(ranbuf,RanbufMaster,RANBUFSIZE);
	memcpy(ranbuf,cipbuf,NSEEDBYTES);
	MRRMakeRotors(ranbuf,rotorE, rotorD);

	cipbuf += NSEEDBYTES;
	for(j=0; j<nbytes; j++) {
		ch = (int)rotorD[*cipbuf] - (int)prev;
		if(ch < 0) ch += ROTORSIZE;
		prev = *cipbuf;
		*(plainText++) = ch;
		cipbuf++;
	}

	free(cipbuforig);
	return nbytes;
}


/*--- function MRRMakeRotors -----------------------------------------
 *
 *  Set up the rotors used for encryption/decryption.
 *
 *  Entry:	Ranbuf is a buffer of "random" bytes.
 *
 *  Exit:	rotorE and rotorD are the encryption and
 * 			corresponding decryption rotors.
 */
void
MRRMakeRotors(unsigned char *ranbuf, unsigned char *rotorE,
 unsigned char *rotorD)
{
	int j, k, idx;
	unsigned char ch;

	/* Make a standard reflexive rotor. */
	for(j=0; j<ROTORSIZE; j++) {
		rotorE[j] = j;
	}
	Ranidx = 0;

	/* Permute the rotor */
	for(k=2; k; k--) for(j=ROTORSIZE-1; j; j--) {
		idx = MRRRan(ranbuf);
		/* This code omitted: idx = idx%(j+1); */
		ch = rotorE[j];
		rotorE[j] = rotorE[idx];
		rotorE[idx] = ch;
	}

	/* Create rotorD as the inverse of rotorE */
	for(j=0; j<ROTORSIZE; j++) {
		rotorD[rotorE[j]] = j;
	}

#ifdef DEBUGMRR
	printf("rotorE:\n");
	for(j=0; j<ROTORSIZE; j++) {
		printf("%4d",rotorE[j]);
		if((j+1)%16 == 0) printf("\n");
	}
	printf("rotorD:\n");
	for(j=0; j<ROTORSIZE; j++) {
		printf("%4d",rotorD[j]);
		if((j+1)%16 == 0) printf("\n");
	}
#endif
}

/*--- function MRRRan --------------------------------------------------
 *
 *  Return a pseudorandom number in the range 0-255.
 *  Uses an additive linear shift register.
 *
 *  Entry:	Ranbuf	is a buffer of RANBUFSIZE random bytes.
 * 			Ranidx	is an index into this buffer.
 *
 *  Exit:	Ranidx has been decremented.
 * 			Returns the next pseudorandom number.
 */
int
MRRRan(unsigned char *Ranbuf)
{
	int idx1, idx2;

	if(--Ranidx < 0) Ranidx = RANBUFSIZE-1;
	idx1 = Ranidx+RANLAG1;
	if(idx1>=RANBUFSIZE) idx1 -= RANBUFSIZE;
	idx2 = Ranidx+RANLAG2;
	if(idx2>=RANBUFSIZE) idx2 -= RANBUFSIZE;

	Ranbuf[Ranidx] = 0xff & (Ranbuf[idx1] + Ranbuf[idx2] + Ranbuf[Ranidx]);

	return Ranbuf[Ranidx];
}


/*--- prencode.c -- File containing routines to convert a buffer
 *  of bytes to/from RFC 1113 printable encoding format.
 *
 *  This technique is similar to the familiar Unix uuencode
 *  format in that it maps 6 binary bits to one ASCII
 *  character (or more aptly, 3 binary bytes to 4 ASCII
 *  characters).  However, RFC 1113 does not use the same
 *  mapping to printable characters as uuencode.
 *
 *  Mark Riordan   12 August 1990 and 17 Feb 1991.
 *  This code is hereby placed in the public domain.
 */


static char six2pr[64] = {
'A','B','C','D','E','F','G','H','I','J','K','L','M',
'N','O','P','Q','R','S','T','U','V','W','X','Y','Z',
'a','b','c','d','e','f','g','h','i','j','k','l','m',
'n','o','p','q','r','s','t','u','v','w','x','y','z',
'0','1','2','3','4','5','6','7','8','9','+','/'        };

/*--- function prencode -----------------------------------------------
 *
 *   Encode a single line of binary data to a standard format that
 *   uses only printing ASCII characters (but takes up 33% more bytes).
 *
 *    Entry    bufin    points to a buffer of bytes.  If nbytes is not
 *                      a multiple of three, then the byte just beyond
 *                      the last byte in the buffer must be 0.
 *             nbytes   is the number of bytes in that buffer.
 *             bufcoded points to an output buffer.  Be sure that this
 *                      can hold at least 1 + (4*nbytes)/3 characters.
 *
 *    Exit     bufcoded contains the coded line.  The first 4*nbytes/3 bytes
 *                      contain printing ASCII characters representing
 *                      those binary bytes. This may include one or
 *                      two '=' characters used as padding at the end.
 *                      The last byte is a zero byte.
 *             Returns the number of ASCII characters in "bufcoded".
 */
int
prencode(bufin, nbytes, bufcoded)
unsigned char *bufin;
unsigned int nbytes;
char *bufcoded;
{
/* ENC is the basic 1 character encoding function to make a char printing */
#define ENC(c) six2pr[c]

   register char *outptr = bufcoded;
   unsigned int i;

   for (i=0; i<nbytes; i += 3) {
      *(outptr++) = ENC(*bufin >> 2);            /* c1 */
      *(outptr++) = ENC(((*bufin << 4) & 060) | ((bufin[1] >> 4) & 017));  /* c2 */
      *(outptr++) = ENC(((bufin[1] << 2) & 074) | ((bufin[2] >> 6) & 03)); /* c3 */
      *(outptr++) = ENC(bufin[2] & 077);         /* c4 */

      bufin += 3;
   }

   /* If nbytes was not a multiple of 3, then we have encoded too
    * many characters.  Adjust appropriately.
    */
   if(i == nbytes+1) {
      /* There were only 2 bytes in that last group */
      outptr[-1] = '=';
   } else if(i == nbytes+2) {
      /* There was only 1 byte in that last group */
      outptr[-1] = '=';
      outptr[-2] = '=';
   }
   *outptr = '\0';
   return(outptr - bufcoded);
}

/*--- function prdecode ------------------------------------------------
 *
 *  Decode an ASCII-encoded buffer back to its original binary form.
 *
 * 	Entry 	bufcoded 	points to a encoded string.	It is
 *                         terminated by any character not in
 *                         the printable character table six2pr, but
 *                         leading whitespace is stripped.
 *             bufplain    points to the output buffer; must be big
 *                         enough to hold the decoded string (generally
 *                         shorter than the encoded string) plus
 *                         as many as two extra bytes used during
 *                         the decoding process.
 *             outbufsize  is the maximum number of bytes that
 *                         can fit in bufplain.
 *
 *    Exit     Returns the number of binary bytes decoded.
 *             bufplain    contains these bytes.
 */
int
prdecode(bufcoded,bufplain,outbufsize)
char *bufcoded;
unsigned char *bufplain;
int outbufsize;
{
/* single character decode */
#define DEC(c) pr2six[(int)c]
#define MAXVAL 63

   int nbytesdecoded, j;
   register char *bufin = bufcoded;
   register unsigned char *bufout = bufplain;
   register int nprbytes;
	unsigned char pr2six[256];


	/* Initialize the mapping table.
    * This code should work even on non-ASCII machines.
    */
	for(j=0; j<256; j++) pr2six[j] = MAXVAL+1;

	for(j=0; j<64; j++) pr2six[(int)six2pr[j]] = (unsigned char) j;

   /* Strip leading whitespace. */

   while(*bufcoded==' ' || *bufcoded == '\t') bufcoded++;

   /* Figure out how many characters are in the input buffer.
    * If this would decode into more bytes than would fit into
    * the output buffer, adjust the number of input bytes downwards.
    */
   bufin = bufcoded;
   while(pr2six[(int)*(bufin++)] <= MAXVAL);
   nprbytes = bufin - bufcoded - 1;
   nbytesdecoded = ((nprbytes+3)/4) * 3;
   if(nbytesdecoded > outbufsize) {
      nprbytes = (outbufsize*4)/3;
   }

   bufin = bufcoded;
   
   while (nprbytes > 0) {
      *(bufout++) = (unsigned char) (DEC(*bufin) << 2 | DEC(bufin[1]) >> 4);
      *(bufout++) = (unsigned char) (DEC(bufin[1]) << 4 | DEC(bufin[2]) >> 2);
      *(bufout++) = (unsigned char) (DEC(bufin[2]) << 6 | DEC(bufin[3]));
      bufin += 4;
      nprbytes -= 4;
   }
   
   if(nprbytes & 03) {
      if(pr2six[(int)bufin[-2]] > MAXVAL) {
         nbytesdecoded -= 2;
      } else {
         nbytesdecoded -= 1;
      }
   }

   return(nbytesdecoded);
}
