/*
   CyberRax Programming (C) 2002 - 2003
   TITLE:   ARCFOUR.H--
   DESCRIPTION:   Simple yet secure ARCFOUR encryption algorithm
   LAST MODIFIED:  21 Aug 2003
   PROCEDURES DEFINED IN THIS FILE:
      void ARCFOUR_INIT(password,size_of_password,boxbuffer[,rounds]);
      void ARCFOUR_CRYPT(inputbuffer,sizeofdata,outputbuffer)
      byte ARCFOUR_BYTE()
      byte ARCFOUR_BURN()
   DEFINITION AFFECTING THIS CODE:
      KeepInsert1	- don't undefine Insert1
      Insert1		 - insert ARCFOUR_BYTE() instead of calling in
			     ARCFOUR_CRYPT()
			   It's meaningful to *not* define this *only* if you
			     use ARCFOUR_BYTE() by itself (for example to
			     generate a random number), otherwise it's
			     *strongly* recommneded to define Insert1 to get
			     smaller and faster code. This behaviour (as
			     opposed to 'call instead of inserting' which
			     would make more sense) is kept only to be
			     compatible with the general usage of InsertX
			     definitions.
      _MAXIMUM_SECURITY_ - allow to specify the number of rounds by key
			     initialization.
			   Without this definition key setup uses 1 round.
			   If used *must* be definied *before* including this
			     headerfile.
   NOTES:   ARCFOUR is the proposed general name for RSA Laboratories' RC4
	      algorithm to avoid using "RC4" (which is a registerred
	      trademark).
	    This source is based on the pseudo (and slightly on the actual C)
	      code presented by Kalle Kaukonen and Rodney Thayer in their
	      Internet-Draft "A Stream Cipher Encryption Algorithm 'Arcfour'".
	      Some optimization ideas (and code) has been taken from TINYARC4
	      (by unknown, as I can't find any note about the author
	    Some weaknesses have been found by RC4 recently so this code here
	      implements an idea used in Arnold G. Reinhold's CipherSaber-2 -
	      multiple rounds by key initialization (can be activated through
	      defining '_MAXIMUM_SECURITY_'). If you need to be compatible
	      with the RC4 specification then use 1 round, if you need
	      security then use 2 or more (preferably over 20).
           This code here (with 1 round) has been successfully tested against
             the testvectors given in the above mentioned document, so I guess
             I've implemented everything correctly, but I wouldn't bet my life
             on it (and neighter should you  :)
*/

? ifndef _ARCFOUR_
? define _ARCFOUR_	TRUE


? ifndef _NUMBERS_
  ? include "NUMBERS.H--"
? endif

? ifndef _JSTUFF_
  ? include "JSTUFF.H--"
? endif

? ifndef _BYTES_
  ? include "BYTES.H--"
? endif


? warning			TRUE


? define ArcFourBoxesLen	size256 * TWO

// Used variables
: word SAddr = NOT_USED_BEFORE, S2Addr;
:byte i, j;

? ifdef speed
  :byte S_ORIG[size256] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
			    0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
			    0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
			    0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F,
			    0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
			    0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F,
			    0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
			    0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F,
			    0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
			    0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F,
			    0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
			    0x58, 0x59, 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F,
			    0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
			    0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F,
			    0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,
			    0x78, 0x79, 0x7A, 0x7B, 0x7C, 0x7D, 0x7E, 0x7F,
			    0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
			    0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F,
			    0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97,
			    0x98, 0x99, 0x9A, 0x9B, 0x9C, 0x9D, 0x9E, 0x9F,
			    0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7,
			    0xA8, 0xA9, 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF,
			    0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7,
			    0xB8, 0xB9, 0xBA, 0xBB, 0xBC, 0xBD, 0xBE, 0xBF,
			    0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7,
			    0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF,
			    0xD0, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7,
			    0xD8, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE, 0xDF,
			    0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7,
			    0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF,
			    0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7,
			    0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF };
? endif


? ifdef MAXIMUM_SECURITY
  void ARCFOUR_INIT (SI, BL, DI, CX);   // ver. 1.03, slightly optimized
? else
  void ARCFOUR_INIT (SI, BL, DI);
? endif



inline void ARCFOUR_INIT ()
/* Initiates the encryption system with the given password.
   Note:   DS must equal to original segment of program (usually CS)
   Input:  DS:SI = buffer with password
	      BL = size of password (ie psw's max length is 255 characters)
	   DS:DI = ArcFourBoxesLen (512) bytes long buffer for boxes
           If '_MAXIMUM_SECURITY_' is definied:
	     CX = number of rounds
   Output: AX, BX, CX, DX, SI, DI destroyed
   Uses:   COPYx() and STOREx() from BYTES.H--
	   6 - 10 bytes of stack
   Size:   min. 104 bytes
*/
{
$PUSH ES,BP

? ifdef MAXIMUM_SECURITY
  $PUSH CX
? endif

SAddr = DI;
S2Addr = DI + size256;

ES = DS;

? ifdef codesize
  AX = NULL;
  do {
     $STOSB
     AL++;
     } while (NOTZEROFLAG);
? else
  $PUSH SI
  ? ifdef cpu > 2
    COPYD(DI,#S_ORIG,size64);
  ? else
    COPYW(DI,#S_ORIG,size128);
  ? endif
  $POP SI
? endif

//DI = S2Addr;
CX = size256;
DX = SI;

KEYSETUP_LOOP:
BH = NULL;
CX_LOOP_1:
IF (BH++ > BL)
   {
   SI = DX;
   GOTO KEYSETUP_LOOP;
   }
? ifdef codesize
  $MOVSB
  $LOOP CX_LOOP_1;
? else
  ? ifdef cpu < 4
    $MOVSB
  ? else
    AL = DSBYTE[SI];
    SI++;
    ESBYTE[DI] = AL;
    DI++;
  ? endif
  ? ifdef cpu < 3
    $LOOP CX_LOOP_1;
  ? else
    CX--;
    IF (NOTZEROFLAG)
       GOTO CX_LOOP_1;
  ? endif
? endif

ROUND_LOOP:
BX = AX = NULL;
CX = size256;
SI = SAddr;
DI = SI;
BP = S2Addr;

				// BL equ j, SI & DI equ #S[i],
CX_LOOP_2:			//   BP equ #S2[i]
? ifdef codesize
  $LODSB			// AL = S[i]
? else
  ? ifdef cpu > 3
    AL = DSBYTE[SI];
    SI++;
  ? else
    $LODSB
  ? endif
? endif
BL += AL;
BL += DSBYTE[BP];			// j = j + S[i] + S2[i]
$PUSH BX
BX += SAddr;
AL >< DSBYTE[BX];			// S[i] >< S[j]
$POP BX
? ifdef codesize
  $STOSB
? else
  ? ifdef cpu < 4
    $STOSB
  ? else
    ESBYTE[DI] = AL;
    DI++;
  ? endif
? endif

BP++;
? ifdef codesize
  $LOOP CX_LOOP_2;
? else
  ? ifdef  cpu < 3
    $LOOP CX_LOOP_2;
  ? else
    CX--;
    IF (NOTZEROFLAG)
       GOTO CX_LOOP_2;
  ? endif
? endif

? ifdef MAXIMUM_SECURITY
  $POP CX
  CX--;
  IF (NOTZEROFLAG)
     {
     $PUSH CX
     GOTO ROUND_LOOP;
     }
? endif

? ifdef Insert1
  @
? endif
? ifdef cpu > 2
  ? ifdef speed
    STORED(S2Addr,NULL,size64);
  ? else
    STOREW(S2Addr,NULL,size128);
   ? endif
? else
  STOREW(S2Addr,NULL,size128);
? endif

i = AL;
j = AL;
$POP BP,ES
}



inline byte ARCFOUR_BYTE ()   // ver. 1.01, shortened by 1 byte
/* Generates a random number which is to be XORed with plaintext to encrypt
     or with ciphertext to decrypt
   Notes:  DS must equal to the original segment of program (usually CS)
           used by ARCFOUR_CRYPT()
           can be used as random number generator (but returns only 1 byte)
   Input:  nothing
   Output: AL = random number
           BX, CX, SI, DI destroyed
   Uses: 2 - 4 bytes of stack
   Size: ca. 43 bytes
*/
{
$PUSH BP
DSBYTE[#i]++;				// i++

BX = i;
SI = SAddr;
? ifdef speed
  DI = SI;
? endif

AX = NULL;
AL = j + DSBYTE[SI + BX];		// j += S[i]
j = AL;

? ifdef codesize
  BP >< AX;
? else
  BP = AX;
? endif

AL = DSBYTE[SI + BX];
? ifdef codesize
  AL >< DSBYTE[SI + BP];
? else
  AL >< DSBYTE[DI + BP];
? endif
DSBYTE[SI + BX] = AL;			// S[j] >< S[i], AL = i

AL = DSBYTE[SI + BX];
? ifdef codesize
  AH = NULL;
  AL += DSBYTE[SI + BP];		// AL (t) = S[i] + S[j]
  BP >< AX;
  AL = DSBYTE[SI + BP];			// AL = S[t]
? else
  AL += DSBYTE[DI + BP];
  DI += AX;
  AL = DSBYTE[DI];			// AL = S[t]
? endif
$POP BP
}



inline void ARCFOUR_CRYPT (SI, CX, DI)   // ver. 1.02, slightly optimized
/* The main routine which en-/decrypts the given plaintext
   Note: DS must equal to the original segment of program (usually CS)
   Input:  DS:SI = buffer with (plain/cipher)text
	      CX = size of data to (en/de)crypt in bytes
	   ES:DI = buffer where to put (cipher/plain)text
	   'Insert1' definied to insert ARCFOUR_BYTE() instead of calling
   Output: outputbuffer filled
	   AX, BX, CX, SI, DI destroyed
   Uses:   ARCFOUR_BYTE() from ARCFOUR.H--
	   6 - 12 bytes of stack
   Size: ca. 54 bytes
*/
{
? ifdef codesize

   loop(CX)
      {
      $PUSH CX,SI
      ? ifdef Insert1
	 @
      ?endif
      ARCFOUR_BYTE();
      $POP SI,CX
      AX >< BX;
      $LODSB
      AL ^= BL;
      $STOSB
      }

? else

    CRYPT_LOOP:
     $PUSH CX,SI,DI
     ARCFOUR_BYTE();
     $POP DI,SI,CX
     ? ifdef cpu < 4
       BL = AL;
       $LODSB
     ? else
       BL = DSBYTE[SI];
       SI++;
     ? endif
     AL ^= BL;
     ? ifdef cpu < 4
       $STOSB
     ? else
       ESBYTE[DI] = AL;
       DI++;
     ? endif
     ? ifdef cpu < 3
       $LOOP CRYPT_LOOP;
     ? else
       CX--;
       IF (NOTZEROFLAG)
	  GOTO CRYPT_LOOP;
     ? endif
?endif

@UNDEFINEINSERT1();
}



inline byte ARCFOUR_BURN ()
/* Wipes S-boxes
   It is not neccessary, but should be done before terminating (the password
     is wiped from the S1 box in ARCFOUR_INIT() anyway). It is, of
     course, menaingless, if the password isn't cleared as well  :)
   Input:  nothing
	   'Insert1' definied to insert STORED()/STOREW() instead of calling
   Output: AL = 0 if boxes wiped
	   AL = 1 if boxes weren't initialized
	   AX, CX, DI destroyed
   Uses:   STOREx() from BYTES.H--
	   2 - 6 bytes of stack
   Size:   min. 14 bytes
*/
{
IF (DSWORD[SAddr] == NOT_USED_BEFORE)
   return(1);

? ifdef Insert1
  @
? endif
? ifdef speed
  ? ifdef cpu > 2
    STORED(SAddr,ZERO,ArcFourBoxesLen/sizeDWORD);
  ? else
    STOREW(SAddr,ZERO,ArcFourBoxesLen/sizeWORD);
  ? endif
? else
  STOREW(SAddr,ZERO,ArcFourBoxesLen/sizeWORD);
? endif
}


? endif


/* end of ARCFOUR.H-- */

