package org.bouncycastle.crypto.modes;
import org.bouncycastle.crypto.BlockCipher;
import org.bouncycastle.crypto.CipherParameters;
import org.bouncycastle.crypto.DataLengthException;
import org.bouncycastle.crypto.StreamBlockCipher;
import org.bouncycastle.crypto.params.ParametersWithIV;
import org.bouncycastle.util.Arrays;
public class G3413OFBBlockCipher
extends StreamBlockCipher
{
private int m;
private int blockSize;
private byte[] R;
private byte[] R_init;
private byte[] Y;
private BlockCipher cipher;
private int byteCount;
private boolean initialized = false;
public G3413OFBBlockCipher(BlockCipher cipher)
{
super(cipher);
this.blockSize = cipher.getBlockSize();
this.cipher = cipher;
Y = new byte[blockSize];
}
public void init(boolean forEncryption, CipherParameters params)
throws IllegalArgumentException
{
if (params instanceof ParametersWithIV)
{
ParametersWithIV ivParam = (ParametersWithIV)params;
byte[] iv = ivParam.getIV();
if (iv.length < blockSize)
{
throw new IllegalArgumentException("Parameter m must blockSize <= m");
}
this.m = iv.length;
initArrays();
R_init = Arrays.clone(iv);
System.arraycopy(R_init, 0, R, 0, R_init.length);
if (ivParam.getParameters() != null)
{
cipher.init(true, ivParam.getParameters());
}
}
else
{
setupDefaultParams();
initArrays();
System.arraycopy(R_init, 0, R, 0, R_init.length);
if (params != null)
{
cipher.init(true, params);
}
}
initialized = true;
}
private void initArrays()
{
R = new byte[m];
R_init = new byte[m];
}
private void setupDefaultParams()
{
this.m = 2 * blockSize;
}
public String getAlgorithmName()
{
return cipher.getAlgorithmName() + "/OFB";
}
public int getBlockSize()
{
return blockSize;
}
public int processBlock(byte[] in, int inOff, byte[] out, int outOff)
throws DataLengthException, IllegalStateException
{
processBytes(in, inOff, blockSize, out, outOff);
return blockSize;
}
protected byte calculateByte(byte in)
{
if (byteCount == 0)
{
generateY();
}
byte rv = (byte)(Y[byteCount] ^ in);
byteCount++;
if (byteCount == getBlockSize())
{
byteCount = 0;
generateR();
}
return rv;
}
private void generateY()
{
byte[] msb = GOST3413CipherUtil.MSB(R, blockSize);
cipher.processBlock(msb, 0, Y, 0);
}
private void generateR()
{
byte[] buf = GOST3413CipherUtil.LSB(R, m - blockSize);
System.arraycopy(buf, 0, R, 0, buf.length);
System.arraycopy(Y, 0, R, buf.length, m - buf.length);
}
public void reset()
{
if (initialized)
{
System.arraycopy(R_init, 0, R, 0, R_init.length);
Arrays.clear(Y);
byteCount = 0;
cipher.reset();
}
}
}