/* PORTED TO JAVA BY ROBERT NEILD November 1999 */

/* PC1 Cipher Algorithm ( Pukall Cipher 1 ) */
/* By Alexander PUKALL 1991 */
/* free code no restriction to use */
/* please include the name of the Author in the final software */
/* the Key is 128 bits */

/* Only the K zone change in the two routines */
/* You can create a single routine with the two parts in it */

import java.io.*; 

public class PC1_InputStream extends FilterInputStream 
{ 
      char ax,bx,cx,dx,si,tmp,x1a2,res,i,inter,cfc,cfd,compte;
      char x1a0[] = new char[8];
      byte cle[] = new byte[17];                            // Hold key
      
      /**
       * Creates a PC1_InputStream. Decodes an input stream of encoded data.
       * @param in  The input stream to decode.
       * @param password 16 byte encryption key
       */    
 
      public PC1_InputStream (InputStream in, byte[] password) {
	 super(in);
	 
	 System.arraycopy(password,0,cle,0,Math.min(16,password.length));
      }

      private void assemble()
      {
	 
	 x1a0[0]= (char) (( cle[0]*256 )+ cle[1]);

	 code();
	 inter=res;
	 
	 x1a0[1]= (char) (x1a0[0] ^ ( (cle[2]*256) + cle[3] ));
	 code();
	 inter=(char) (inter^res);
	 
	 x1a0[2]= (char) (x1a0[1] ^ ( (cle[4]*256) + cle[5] ));
	 code();
	 inter=(char) (inter^res);
	 
	 x1a0[3]= (char) (x1a0[2] ^ ( (cle[6]*256) + cle[7] ));
	 code();
	 inter=(char) (inter^res);
	 
	 x1a0[4]= (char) (x1a0[3] ^ ( (cle[8]*256) + cle[9] ));
	 code();
	 inter=(char) (inter^res);
	 
	 x1a0[5]= (char) (x1a0[4] ^ ( (cle[10]*256) + cle[11] ));
	 code();
	 inter=(char) (inter^res);
	 
	 x1a0[6]= (char) (x1a0[5] ^ ( (cle[12]*256) + cle[13] ));
	 code();
	 inter=(char) (inter^res);
	 
	 x1a0[7]= (char) (  x1a0[6] ^ ( (cle[14]*256) + cle[15] ) );
	 code();
	 inter=(char) (inter^res);
	 
	 i=0;
      }
      
      void code()
      {
	 dx=(char) (x1a2+i);
	 ax=x1a0[i];
	 
	 cx=0x015a;
	 bx=0x4e35;
	 
	 tmp=ax;
	 ax=si;
	 si=tmp;
	 
	 tmp=ax;
	 ax=dx;
	 dx=tmp;

	 if (ax!=0)  {
	    ax=(char) (ax*bx);
	 }
	 
	 tmp=ax;
	 ax=cx;
	 cx=tmp;

	 if (ax!=0) {
	    ax=(char) (ax*si);
	    cx=(char) (ax+cx);
	 }

	 tmp=ax;
	 ax=si;
	 si=tmp;
	 ax=(char) (ax*bx);
	 dx=(char) (cx+dx);
	 
	 ax=(char) (ax+1);
	 
	 x1a2=dx;
	 x1a0[i]=ax;
	 
	 res=(char) (ax^dx);
	 i=(char) (i+1);
      }

      /**
       * Returns a plain byte, which has been unencrypted from the underlying
       * InputStream.
       * @see java.io.FilterInputStream
       */    
      
      public int read() throws IOException {
	 int c = in.read();   

	 if (c==-1)          /* EOF */
	    return -1;
	 
	 
	 assemble();

	 cfc=(char) (inter>>8);

	 cfd=(char) (inter&255); /* cfc^cfd = random byte */
	    
	 /* K ZONE !!!!!!!!!!!!! */
	 /* here the mix of c and cle[compte] is after the decryption of c */
	    
	 c = c ^ (cfc^cfd);
	   
	 for (compte=0;compte<=15;compte++)
	 {
	    /* we mix the plaintext byte with the key */
	    cle[compte]=(byte) (cle[compte]^ c);
	 }

	 return c;      /* write the decrypted byte */
      }
      
}