1    package org.bouncycastle.crypto.engines;
2    
3    import org.bouncycastle.crypto.StreamCipher;
4    import org.bouncycastle.crypto.CipherParameters;
5    import org.bouncycastle.crypto.DataLengthException;
6    import org.bouncycastle.crypto.params.KeyParameter;
7    
8    public class RC4Engine implements StreamCipher
9    {
10       private final static int STATE_LENGTH = 256;
11   
12       /*
13        * variables to hold the state of the RC4 engine
14        * during encryption and decryption
15        */
16   
17       private byte[]      engineState = null;
18       private int         x = 0;
19       private int         y = 0;
20       private byte[]      workingKey = null;
21   
22       /**
23        * initialise a RC4 cipher.
24        *
25        * @param forEncryption whether or not we are for encryption.
26        * @param params the parameters required to set up the cipher.
27        * @exception IllegalArgumentException if the params argument is
28        * inappropriate.
29        */
30       public void init(
31           boolean             forEncryption, 
32           CipherParameters     params
33       )
34       {
35           if (params instanceof KeyParameter)
36           {
37               /* 
38                * RC4 encryption and decryption is completely
39                * symmetrical, so the 'forEncryption' is 
40                * irrelevant.
41                */
42               workingKey = ((KeyParameter)params).getKey();
43               setKey(workingKey);
44   
45               return;
46           }
47   
48           throw new IllegalArgumentException("invalid parameter passed to RC4 init - " + params.getClass().getName());
49       }
50   
51       public String getAlgorithmName()
52       {
53           return "RC4";
54       }
55   
56       public byte returnByte(byte in)
57       {
58           x = (x + 1) & 0xff;
59           y = (engineState[x] + y) & 0xff;
60   
61           // swap
62           byte tmp = engineState[x];
63           engineState[x] = engineState[y];
64           engineState[y] = tmp;
65   
66           // xor
67           return (byte)(in ^ engineState[(engineState[x] + engineState[y]) & 0xff]);
68       }
69   
70       public void processBytes(
71           byte[]     in, 
72           int     inOff, 
73           int     len, 
74           byte[]     out, 
75           int     outOff
76       )
77       {
78           if ((inOff + len) > in.length)
79           {
80               throw new DataLengthException("input buffer too short");
81           }
82   
83           if ((outOff + len) > out.length)
84           {
85               throw new DataLengthException("output buffer too short");
86           }
87   
88           for (int i = 0; i < len ; i++)
89           {
90               x = (x + 1) & 0xff;
91               y = (engineState[x] + y) & 0xff;
92   
93               // swap
94               byte tmp = engineState[x];
95               engineState[x] = engineState[y];
96               engineState[y] = tmp;
97   
98               // xor
99               out[i+outOff] = (byte)(in[i + inOff]
100                      ^ engineState[(engineState[x] + engineState[y]) & 0xff]);
101          }
102      }
103  
104      public void reset()
105      {
106          setKey(workingKey);
107      }
108  
109      // Private implementation
110  
111      private void setKey(byte[] keyBytes)
112      {
113          workingKey = keyBytes;
114  
115          // System.out.println("the key length is ; "+ workingKey.length);
116  
117          x = 0;
118          y = 0;
119  
120          if (engineState == null)
121          {
122              engineState = new byte[STATE_LENGTH];
123          }
124  
125          // reset the state of the engine
126          for (int i=0; i < STATE_LENGTH; i++)
127          {
128              engineState[i] = (byte)i;
129          }
130          
131          int i1 = 0;
132          int i2 = 0;
133  
134          for (int i=0; i < STATE_LENGTH; i++)
135          {
136              i2 = ((keyBytes[i1] & 0xff) + engineState[i] + i2) & 0xff;
137              // do the byte-swap inline
138              byte tmp = engineState[i];
139              engineState[i] = engineState[i2];
140              engineState[i2] = tmp;
141              i1 = (i1+1) % keyBytes.length; 
142          }
143      }
144  }
145