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.KeyParameter;
import org.bouncycastle.crypto.params.ParametersWithIV;
import org.bouncycastle.crypto.params.ParametersWithRandom;
import org.bouncycastle.crypto.params.ParametersWithSBox;

An implementation of the GOST CFB mode with CryptoPro key meshing as described in RFC 4357.
/** * An implementation of the GOST CFB mode with CryptoPro key meshing as described in RFC 4357. */
public class GCFBBlockCipher extends StreamBlockCipher { private static final byte[] C = { 0x69, 0x00, 0x72, 0x22, 0x64, (byte)0xC9, 0x04, 0x23, (byte)0x8D, 0x3A, (byte)0xDB, (byte)0x96, 0x46, (byte)0xE9, 0x2A, (byte)0xC4, 0x18, (byte)0xFE, (byte)0xAC, (byte)0x94, 0x00, (byte)0xED, 0x07, 0x12, (byte)0xC0, (byte)0x86, (byte)0xDC, (byte)0xC2, (byte)0xEF, 0x4C, (byte)0xA9, 0x2B }; private final CFBBlockCipher cfbEngine; private KeyParameter key; private long counter = 0; private boolean forEncryption; public GCFBBlockCipher(BlockCipher engine) { super(engine); this.cfbEngine = new CFBBlockCipher(engine, engine.getBlockSize() * 8); } public void init(boolean forEncryption, CipherParameters params) throws IllegalArgumentException { counter = 0; cfbEngine.init(forEncryption, params); this.forEncryption = forEncryption; if (params instanceof ParametersWithIV) { params = ((ParametersWithIV)params).getParameters(); } if (params instanceof ParametersWithRandom) { params = ((ParametersWithRandom)params).getParameters(); } if (params instanceof ParametersWithSBox) { params = ((ParametersWithSBox)params).getParameters(); } key = (KeyParameter)params; } public String getAlgorithmName() { String name = cfbEngine.getAlgorithmName(); return name.substring(0, name.indexOf('/')) + "/G" + name.substring(name.indexOf('/') + 1); } public int getBlockSize() { return cfbEngine.getBlockSize(); } public int processBlock(byte[] in, int inOff, byte[] out, int outOff) throws DataLengthException, IllegalStateException { this.processBytes(in, inOff, cfbEngine.getBlockSize(), out, outOff); return cfbEngine.getBlockSize(); } protected byte calculateByte(byte b) { if (counter > 0 && counter % 1024 == 0) { BlockCipher base = cfbEngine.getUnderlyingCipher(); base.init(false, key); byte[] nextKey = new byte[32]; base.processBlock(C, 0, nextKey, 0); base.processBlock(C, 8, nextKey, 8); base.processBlock(C, 16, nextKey, 16); base.processBlock(C, 24, nextKey, 24); key = new KeyParameter(nextKey); base.init(true, key); byte[] iv = cfbEngine.getCurrentIV(); base.processBlock(iv, 0, iv, 0); cfbEngine.init(forEncryption, new ParametersWithIV(key, iv)); } counter++; return cfbEngine.calculateByte(b); } public void reset() { counter = 0; cfbEngine.reset(); } }