// 
// Decompiled by Procyon v0.6.0
// 

package org.bouncycastle.crypto.modes;

import org.bouncycastle.crypto.DataLengthException;
import org.bouncycastle.util.Arrays;
import org.bouncycastle.crypto.params.ParametersWithIV;
import org.bouncycastle.crypto.CipherParameters;
import org.bouncycastle.crypto.BlockCipher;
import org.bouncycastle.crypto.DefaultMultiBlockCipher;

public class CBCBlockCipher extends DefaultMultiBlockCipher implements CBCModeCipher
{
    private byte[] IV;
    private byte[] cbcV;
    private byte[] cbcNextV;
    private int blockSize;
    private BlockCipher cipher;
    private boolean encrypting;
    
    public static CBCModeCipher newInstance(final BlockCipher blockCipher) {
        return new CBCBlockCipher(blockCipher);
    }
    
    @Deprecated
    public CBCBlockCipher(final BlockCipher cipher) {
        this.cipher = null;
        this.cipher = cipher;
        this.blockSize = cipher.getBlockSize();
        this.IV = new byte[this.blockSize];
        this.cbcV = new byte[this.blockSize];
        this.cbcNextV = new byte[this.blockSize];
    }
    
    @Override
    public BlockCipher getUnderlyingCipher() {
        return this.cipher;
    }
    
    @Override
    public void init(final boolean encrypting, CipherParameters parameters) throws IllegalArgumentException {
        final boolean encrypting2 = this.encrypting;
        this.encrypting = encrypting;
        if (parameters instanceof ParametersWithIV) {
            final ParametersWithIV parametersWithIV = (ParametersWithIV)parameters;
            final byte[] iv = parametersWithIV.getIV();
            if (iv.length != this.blockSize) {
                throw new IllegalArgumentException("initialisation vector must be the same length as block size");
            }
            System.arraycopy(iv, 0, this.IV, 0, iv.length);
            parameters = parametersWithIV.getParameters();
        }
        else {
            Arrays.fill(this.IV, (byte)0);
        }
        this.reset();
        if (parameters != null) {
            this.cipher.init(encrypting, parameters);
        }
        else if (encrypting2 != encrypting) {
            throw new IllegalArgumentException("cannot change encrypting state without providing key.");
        }
    }
    
    @Override
    public String getAlgorithmName() {
        return this.cipher.getAlgorithmName() + "/CBC";
    }
    
    @Override
    public int getBlockSize() {
        return this.cipher.getBlockSize();
    }
    
    @Override
    public int processBlock(final byte[] array, final int n, final byte[] array2, final int n2) throws DataLengthException, IllegalStateException {
        return this.encrypting ? this.encryptBlock(array, n, array2, n2) : this.decryptBlock(array, n, array2, n2);
    }
    
    @Override
    public void reset() {
        System.arraycopy(this.IV, 0, this.cbcV, 0, this.IV.length);
        Arrays.fill(this.cbcNextV, (byte)0);
        this.cipher.reset();
    }
    
    private int encryptBlock(final byte[] array, final int n, final byte[] array2, final int n2) throws DataLengthException, IllegalStateException {
        if (n + this.blockSize > array.length) {
            throw new DataLengthException("input buffer too short");
        }
        for (int i = 0; i < this.blockSize; ++i) {
            final byte[] cbcV = this.cbcV;
            final int n3 = i;
            cbcV[n3] ^= array[n + i];
        }
        final int processBlock = this.cipher.processBlock(this.cbcV, 0, array2, n2);
        System.arraycopy(array2, n2, this.cbcV, 0, this.cbcV.length);
        return processBlock;
    }
    
    private int decryptBlock(final byte[] array, final int n, final byte[] array2, final int n2) throws DataLengthException, IllegalStateException {
        if (n + this.blockSize > array.length) {
            throw new DataLengthException("input buffer too short");
        }
        System.arraycopy(array, n, this.cbcNextV, 0, this.blockSize);
        final int processBlock = this.cipher.processBlock(array, n, array2, n2);
        for (int i = 0; i < this.blockSize; ++i) {
            final int n3 = n2 + i;
            array2[n3] ^= this.cbcV[i];
        }
        final byte[] cbcV = this.cbcV;
        this.cbcV = this.cbcNextV;
        this.cbcNextV = cbcV;
        return processBlock;
    }
}
