1package org.bouncycastle.crypto.engines;
2
3import org.bouncycastle.crypto.CipherParameters;
4import org.bouncycastle.crypto.DataLengthException;
5import org.bouncycastle.crypto.StreamCipher;
6import org.bouncycastle.crypto.params.KeyParameter;
7
8public 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        if ((inOff + len) > in.length)
78        {
79            throw new DataLengthException("input buffer too short");
80        }
81
82        if ((outOff + len) > out.length)
83        {
84            throw new DataLengthException("output buffer too short");
85        }
86
87        for (int i = 0; i < len ; i++)
88        {
89            x = (x + 1) & 0xff;
90            y = (engineState[x] + y) & 0xff;
91
92            // swap
93            byte tmp = engineState[x];
94            engineState[x] = engineState[y];
95            engineState[y] = tmp;
96
97            // xor
98            out[i+outOff] = (byte)(in[i + inOff]
99                    ^ engineState[(engineState[x] + engineState[y]) & 0xff]);
100        }
101    }
102
103    public void reset()
104    {
105        setKey(workingKey);
106    }
107
108    // Private implementation
109
110    private void setKey(byte[] keyBytes)
111    {
112        workingKey = keyBytes;
113
114        // System.out.println("the key length is ; "+ workingKey.length);
115
116        x = 0;
117        y = 0;
118
119        if (engineState == null)
120        {
121            engineState = new byte[STATE_LENGTH];
122        }
123
124        // reset the state of the engine
125        for (int i=0; i < STATE_LENGTH; i++)
126        {
127            engineState[i] = (byte)i;
128        }
129
130        int i1 = 0;
131        int i2 = 0;
132
133        for (int i=0; i < STATE_LENGTH; i++)
134        {
135            i2 = ((keyBytes[i1] & 0xff) + engineState[i] + i2) & 0xff;
136            // do the byte-swap inline
137            byte tmp = engineState[i];
138            engineState[i] = engineState[i2];
139            engineState[i2] = tmp;
140            i1 = (i1+1) % keyBytes.length;
141        }
142    }
143}
144