package org.bouncycastle.crypto.engines; import org.bouncycastle.crypto.CipherParameters; import org.bouncycastle.crypto.DataLengthException; import org.bouncycastle.crypto.StreamCipher; import org.bouncycastle.crypto.params.KeyParameter; public class RC4Engine implements StreamCipher { private final static int STATE_LENGTH = 256; /* * variables to hold the state of the RC4 engine * during encryption and decryption */ private byte[] engineState = null; private int x = 0; private int y = 0; private byte[] workingKey = null; /** * initialise a RC4 cipher. * * @param forEncryption whether or not we are for encryption. * @param params the parameters required to set up the cipher. * @exception IllegalArgumentException if the params argument is * inappropriate. */ public void init( boolean forEncryption, CipherParameters params ) { if (params instanceof KeyParameter) { /* * RC4 encryption and decryption is completely * symmetrical, so the 'forEncryption' is * irrelevant. */ workingKey = ((KeyParameter)params).getKey(); setKey(workingKey); return; } throw new IllegalArgumentException("invalid parameter passed to RC4 init - " + params.getClass().getName()); } public String getAlgorithmName() { return "RC4"; } public byte returnByte(byte in) { x = (x + 1) & 0xff; y = (engineState[x] + y) & 0xff; // swap byte tmp = engineState[x]; engineState[x] = engineState[y]; engineState[y] = tmp; // xor return (byte)(in ^ engineState[(engineState[x] + engineState[y]) & 0xff]); } public void processBytes( byte[] in, int inOff, int len, byte[] out, int outOff) { if ((inOff + len) > in.length) { throw new DataLengthException("input buffer too short"); } if ((outOff + len) > out.length) { throw new DataLengthException("output buffer too short"); } for (int i = 0; i < len ; i++) { x = (x + 1) & 0xff; y = (engineState[x] + y) & 0xff; // swap byte tmp = engineState[x]; engineState[x] = engineState[y]; engineState[y] = tmp; // xor out[i+outOff] = (byte)(in[i + inOff] ^ engineState[(engineState[x] + engineState[y]) & 0xff]); } } public void reset() { setKey(workingKey); } // Private implementation private void setKey(byte[] keyBytes) { workingKey = keyBytes; // System.out.println("the key length is ; "+ workingKey.length); x = 0; y = 0; if (engineState == null) { engineState = new byte[STATE_LENGTH]; } // reset the state of the engine for (int i=0; i < STATE_LENGTH; i++) { engineState[i] = (byte)i; } int i1 = 0; int i2 = 0; for (int i=0; i < STATE_LENGTH; i++) { i2 = ((keyBytes[i1] & 0xff) + engineState[i] + i2) & 0xff; // do the byte-swap inline byte tmp = engineState[i]; engineState[i] = engineState[i2]; engineState[i2] = tmp; i1 = (i1+1) % keyBytes.length; } } }