blob: 364c5d8283f42162523899f0199037184c19f0dc [file] [log] [blame]
package org.bouncycastle.crypto.engines;
import org.bouncycastle.crypto.CipherParameters;
import org.bouncycastle.crypto.DataLengthException;
import org.bouncycastle.crypto.OutputLengthException;
import org.bouncycastle.crypto.StreamCipher;
import org.bouncycastle.crypto.params.KeyParameter;
import org.bouncycastle.crypto.params.ParametersWithIV;
public class VMPCEngine implements StreamCipher
{
/*
* variables to hold the state of the VMPC engine during encryption and
* decryption
*/
protected byte n = 0;
protected byte[] P = null;
protected byte s = 0;
protected byte[] workingIV;
protected byte[] workingKey;
public String getAlgorithmName()
{
return "VMPC";
}
/**
* initialise a VMPC 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 ParametersWithIV))
{
throw new IllegalArgumentException(
"VMPC init parameters must include an IV");
}
ParametersWithIV ivParams = (ParametersWithIV) params;
if (!(ivParams.getParameters() instanceof KeyParameter))
{
throw new IllegalArgumentException(
"VMPC init parameters must include a key");
}
KeyParameter key = (KeyParameter) ivParams.getParameters();
this.workingIV = ivParams.getIV();
if (workingIV == null || workingIV.length < 1 || workingIV.length > 768)
{
throw new IllegalArgumentException("VMPC requires 1 to 768 bytes of IV");
}
this.workingKey = key.getKey();
initKey(this.workingKey, this.workingIV);
}
protected void initKey(byte[] keyBytes, byte[] ivBytes)
{
s = 0;
P = new byte[256];
for (int i = 0; i < 256; i++)
{
P[i] = (byte) i;
}
for (int m = 0; m < 768; m++)
{
s = P[(s + P[m & 0xff] + keyBytes[m % keyBytes.length]) & 0xff];
byte temp = P[m & 0xff];
P[m & 0xff] = P[s & 0xff];
P[s & 0xff] = temp;
}
for (int m = 0; m < 768; m++)
{
s = P[(s + P[m & 0xff] + ivBytes[m % ivBytes.length]) & 0xff];
byte temp = P[m & 0xff];
P[m & 0xff] = P[s & 0xff];
P[s & 0xff] = temp;
}
n = 0;
}
public int 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 OutputLengthException("output buffer too short");
}
for (int i = 0; i < len; i++)
{
s = P[(s + P[n & 0xff]) & 0xff];
byte z = P[(P[(P[s & 0xff]) & 0xff] + 1) & 0xff];
// encryption
byte temp = P[n & 0xff];
P[n & 0xff] = P[s & 0xff];
P[s & 0xff] = temp;
n = (byte) ((n + 1) & 0xff);
// xor
out[i + outOff] = (byte) (in[i + inOff] ^ z);
}
return len;
}
public void reset()
{
initKey(this.workingKey, this.workingIV);
}
public byte returnByte(byte in)
{
s = P[(s + P[n & 0xff]) & 0xff];
byte z = P[(P[(P[s & 0xff]) & 0xff] + 1) & 0xff];
// encryption
byte temp = P[n & 0xff];
P[n & 0xff] = P[s & 0xff];
P[s & 0xff] = temp;
n = (byte) ((n + 1) & 0xff);
// xor
return (byte) (in ^ z);
}
}