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