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

package org.bouncycastle.jcajce.provider.symmetric.util;

import org.bouncycastle.crypto.paddings.PaddedBufferedBlockCipher;
import org.bouncycastle.crypto.paddings.PKCS7Padding;
import org.bouncycastle.crypto.InvalidCipherTextException;
import java.lang.reflect.Constructor;
import org.bouncycastle.crypto.OutputLengthException;
import javax.crypto.BadPaddingException;
import javax.crypto.IllegalBlockSizeException;
import org.bouncycastle.crypto.DataLengthException;
import javax.crypto.ShortBufferException;
import org.bouncycastle.util.Arrays;
import java.nio.ByteBuffer;
import java.security.InvalidParameterException;
import org.bouncycastle.crypto.params.ParametersWithRandom;
import org.bouncycastle.crypto.CryptoServicesRegistrar;
import org.bouncycastle.crypto.params.FPEParameters;
import org.bouncycastle.jcajce.spec.FPEParameterSpec;
import org.bouncycastle.crypto.params.RC5Parameters;
import javax.crypto.spec.RC5ParameterSpec;
import org.bouncycastle.crypto.params.RC2Parameters;
import javax.crypto.spec.RC2ParameterSpec;
import org.bouncycastle.crypto.CipherParameters;
import org.bouncycastle.crypto.params.ParametersWithSBox;
import org.bouncycastle.jcajce.spec.GOST28147ParameterSpec;
import org.bouncycastle.jcajce.spec.AEADParameterSpec;
import org.bouncycastle.crypto.params.KeyParameter;
import org.bouncycastle.jcajce.spec.RepeatedSecretKeySpec;
import org.bouncycastle.jcajce.PKCS12KeyWithParameters;
import javax.crypto.interfaces.PBEKey;
import org.bouncycastle.jcajce.PBKDF2KeyWithParameters;
import org.bouncycastle.jcajce.PBKDF2Key;
import org.bouncycastle.jcajce.PBKDF1KeyWithParameters;
import org.bouncycastle.jcajce.PBKDF1Key;
import org.bouncycastle.jcajce.PKCS12Key;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import javax.crypto.SecretKey;
import java.security.SecureRandom;
import org.bouncycastle.crypto.paddings.TBCPadding;
import org.bouncycastle.crypto.paddings.ISO7816d4Padding;
import org.bouncycastle.crypto.paddings.X923Padding;
import org.bouncycastle.crypto.paddings.ISO10126d2Padding;
import org.bouncycastle.crypto.paddings.BlockCipherPadding;
import org.bouncycastle.crypto.paddings.ZeroBytePadding;
import javax.crypto.NoSuchPaddingException;
import org.bouncycastle.crypto.modes.GCMBlockCipher;
import org.bouncycastle.crypto.modes.KGCMBlockCipher;
import org.bouncycastle.crypto.modes.GCMSIVBlockCipher;
import org.bouncycastle.crypto.modes.EAXBlockCipher;
import org.bouncycastle.crypto.modes.OCBBlockCipher;
import org.bouncycastle.crypto.modes.CCMBlockCipher;
import org.bouncycastle.crypto.modes.KCCMBlockCipher;
import org.bouncycastle.crypto.modes.CTSBlockCipher;
import org.bouncycastle.crypto.modes.GCFBBlockCipher;
import org.bouncycastle.crypto.modes.GOFBBlockCipher;
import org.bouncycastle.crypto.modes.KCTRBlockCipher;
import org.bouncycastle.crypto.engines.DSTU7624Engine;
import org.bouncycastle.crypto.DefaultBufferedBlockCipher;
import org.bouncycastle.crypto.modes.SICBlockCipher;
import org.bouncycastle.crypto.fpe.FPEFF3_1Engine;
import org.bouncycastle.crypto.fpe.FPEEngine;
import org.bouncycastle.crypto.fpe.FPEFF1Engine;
import org.bouncycastle.crypto.modes.OpenPGPCFBBlockCipher;
import org.bouncycastle.crypto.modes.PGPCFBBlockCipher;
import org.bouncycastle.crypto.modes.CFBBlockCipher;
import org.bouncycastle.crypto.modes.OFBBlockCipher;
import org.bouncycastle.crypto.modes.CBCBlockCipher;
import org.bouncycastle.util.Strings;
import java.security.NoSuchAlgorithmException;
import javax.crypto.spec.IvParameterSpec;
import org.bouncycastle.internal.asn1.cms.GCMParameters;
import org.bouncycastle.asn1.DEROctetString;
import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
import java.security.spec.AlgorithmParameterSpec;
import java.security.AlgorithmParameters;
import java.security.Key;
import org.bouncycastle.crypto.BufferedBlockCipher;
import org.bouncycastle.crypto.modes.AEADCipher;
import org.bouncycastle.crypto.modes.AEADBlockCipher;
import javax.crypto.spec.PBEParameterSpec;
import org.bouncycastle.crypto.params.AEADParameters;
import org.bouncycastle.crypto.params.ParametersWithIV;
import org.bouncycastle.crypto.BlockCipher;

public class BaseBlockCipher extends BaseWrapCipher implements PBE
{
    private static final int BUF_SIZE = 512;
    private static final Class[] availableSpecs;
    private BlockCipher baseEngine;
    private BlockCipherProvider engineProvider;
    private GenericBlockCipher cipher;
    private ParametersWithIV ivParam;
    private AEADParameters aeadParams;
    private int keySizeInBits;
    private int scheme;
    private int digest;
    private int ivLength;
    private boolean padded;
    private boolean fixedIv;
    private PBEParameterSpec pbeSpec;
    private String pbeAlgorithm;
    private String modeName;
    
    protected BaseBlockCipher(final BlockCipher baseEngine) {
        this.scheme = -1;
        this.ivLength = 0;
        this.fixedIv = true;
        this.pbeSpec = null;
        this.pbeAlgorithm = null;
        this.modeName = null;
        this.baseEngine = baseEngine;
        this.cipher = new BufferedGenericBlockCipher(baseEngine);
    }
    
    protected BaseBlockCipher(final BlockCipher baseEngine, final int scheme, final int digest, final int keySizeInBits, final int ivLength) {
        this.scheme = -1;
        this.ivLength = 0;
        this.fixedIv = true;
        this.pbeSpec = null;
        this.pbeAlgorithm = null;
        this.modeName = null;
        this.baseEngine = baseEngine;
        this.scheme = scheme;
        this.digest = digest;
        this.keySizeInBits = keySizeInBits;
        this.ivLength = ivLength;
        this.cipher = new BufferedGenericBlockCipher(baseEngine);
    }
    
    protected BaseBlockCipher(final BlockCipherProvider engineProvider) {
        this.scheme = -1;
        this.ivLength = 0;
        this.fixedIv = true;
        this.pbeSpec = null;
        this.pbeAlgorithm = null;
        this.modeName = null;
        this.baseEngine = engineProvider.get();
        this.engineProvider = engineProvider;
        this.cipher = new BufferedGenericBlockCipher(engineProvider.get());
    }
    
    protected BaseBlockCipher(final int keySizeInBits, final BlockCipherProvider engineProvider) {
        this.scheme = -1;
        this.ivLength = 0;
        this.fixedIv = true;
        this.pbeSpec = null;
        this.pbeAlgorithm = null;
        this.modeName = null;
        this.baseEngine = engineProvider.get();
        this.engineProvider = engineProvider;
        this.keySizeInBits = keySizeInBits;
        this.cipher = new BufferedGenericBlockCipher(engineProvider.get());
    }
    
    protected BaseBlockCipher(final AEADBlockCipher aeadBlockCipher) {
        this(0, aeadBlockCipher);
    }
    
    protected BaseBlockCipher(final int keySizeInBits, final AEADBlockCipher aeadBlockCipher) {
        this.scheme = -1;
        this.ivLength = 0;
        this.fixedIv = true;
        this.pbeSpec = null;
        this.pbeAlgorithm = null;
        this.modeName = null;
        this.keySizeInBits = keySizeInBits;
        this.baseEngine = aeadBlockCipher.getUnderlyingCipher();
        if (aeadBlockCipher.getAlgorithmName().indexOf("GCM") >= 0) {
            this.ivLength = 12;
        }
        else {
            this.ivLength = this.baseEngine.getBlockSize();
        }
        this.cipher = new AEADGenericBlockCipher(aeadBlockCipher);
    }
    
    protected BaseBlockCipher(final AEADCipher aeadCipher, final boolean fixedIv, final int ivLength) {
        this.scheme = -1;
        this.ivLength = 0;
        this.fixedIv = true;
        this.pbeSpec = null;
        this.pbeAlgorithm = null;
        this.modeName = null;
        this.baseEngine = null;
        this.fixedIv = fixedIv;
        this.ivLength = ivLength;
        this.cipher = new AEADGenericBlockCipher(aeadCipher);
    }
    
    protected BaseBlockCipher(final AEADBlockCipher aeadBlockCipher, final boolean b, final int n) {
        this(0, aeadBlockCipher, b, n);
    }
    
    protected BaseBlockCipher(final int keySizeInBits, final AEADBlockCipher aeadBlockCipher, final boolean fixedIv, final int ivLength) {
        this.scheme = -1;
        this.ivLength = 0;
        this.fixedIv = true;
        this.pbeSpec = null;
        this.pbeAlgorithm = null;
        this.modeName = null;
        this.keySizeInBits = keySizeInBits;
        this.baseEngine = aeadBlockCipher.getUnderlyingCipher();
        this.fixedIv = fixedIv;
        this.ivLength = ivLength;
        this.cipher = new AEADGenericBlockCipher(aeadBlockCipher);
    }
    
    protected BaseBlockCipher(final BlockCipher blockCipher, final int n) {
        this(blockCipher, true, n);
    }
    
    protected BaseBlockCipher(final int keySizeInBits, final BlockCipher baseEngine, final int n) {
        this.scheme = -1;
        this.ivLength = 0;
        this.fixedIv = true;
        this.pbeSpec = null;
        this.pbeAlgorithm = null;
        this.modeName = null;
        this.keySizeInBits = keySizeInBits;
        this.baseEngine = baseEngine;
        this.fixedIv = true;
        this.cipher = new BufferedGenericBlockCipher(baseEngine);
        this.ivLength = n / 8;
    }
    
    protected BaseBlockCipher(final BlockCipher baseEngine, final boolean fixedIv, final int n) {
        this.scheme = -1;
        this.ivLength = 0;
        this.fixedIv = true;
        this.pbeSpec = null;
        this.pbeAlgorithm = null;
        this.modeName = null;
        this.baseEngine = baseEngine;
        this.fixedIv = fixedIv;
        this.cipher = new BufferedGenericBlockCipher(baseEngine);
        this.ivLength = n / 8;
    }
    
    protected BaseBlockCipher(final BufferedBlockCipher bufferedBlockCipher, final int n) {
        this(bufferedBlockCipher, true, n);
    }
    
    protected BaseBlockCipher(final int keySizeInBits, final BufferedBlockCipher bufferedBlockCipher, final int n) {
        this.scheme = -1;
        this.ivLength = 0;
        this.fixedIv = true;
        this.pbeSpec = null;
        this.pbeAlgorithm = null;
        this.modeName = null;
        this.keySizeInBits = keySizeInBits;
        this.baseEngine = bufferedBlockCipher.getUnderlyingCipher();
        this.cipher = new BufferedGenericBlockCipher(bufferedBlockCipher);
        this.fixedIv = true;
        this.ivLength = n / 8;
    }
    
    protected BaseBlockCipher(final BufferedBlockCipher bufferedBlockCipher, final boolean fixedIv, final int n) {
        this.scheme = -1;
        this.ivLength = 0;
        this.fixedIv = true;
        this.pbeSpec = null;
        this.pbeAlgorithm = null;
        this.modeName = null;
        this.baseEngine = bufferedBlockCipher.getUnderlyingCipher();
        this.cipher = new BufferedGenericBlockCipher(bufferedBlockCipher);
        this.fixedIv = fixedIv;
        this.ivLength = n / 8;
    }
    
    @Override
    protected int engineGetBlockSize() {
        if (this.baseEngine == null) {
            return -1;
        }
        return this.baseEngine.getBlockSize();
    }
    
    @Override
    protected byte[] engineGetIV() {
        if (this.aeadParams != null) {
            return this.aeadParams.getNonce();
        }
        return (byte[])((this.ivParam != null) ? this.ivParam.getIV() : null);
    }
    
    @Override
    protected int engineGetKeySize(final Key key) {
        return key.getEncoded().length * 8;
    }
    
    @Override
    protected int engineGetOutputSize(final int n) {
        return this.cipher.getOutputSize(n);
    }
    
    @Override
    protected AlgorithmParameters engineGetParameters() {
        if (this.engineParams == null) {
            if (this.pbeSpec != null) {
                try {
                    (this.engineParams = this.createParametersInstance(this.pbeAlgorithm)).init(this.pbeSpec);
                    return this.engineParams;
                }
                catch (final Exception ex) {
                    ex.printStackTrace();
                    return null;
                }
            }
            if (this.aeadParams != null) {
                if (this.baseEngine == null) {
                    try {
                        (this.engineParams = this.createParametersInstance(PKCSObjectIdentifiers.id_alg_AEADChaCha20Poly1305.getId())).init(new DEROctetString(this.aeadParams.getNonce()).getEncoded());
                        return this.engineParams;
                    }
                    catch (final Exception ex2) {
                        throw new RuntimeException(ex2.toString());
                    }
                }
                try {
                    (this.engineParams = this.createParametersInstance("GCM")).init(new GCMParameters(this.aeadParams.getNonce(), this.aeadParams.getMacSize() / 8).getEncoded());
                    return this.engineParams;
                }
                catch (final Exception ex3) {
                    throw new RuntimeException(ex3.toString());
                }
            }
            if (this.ivParam != null) {
                String s = this.cipher.getUnderlyingCipher().getAlgorithmName();
                if (s.indexOf(47) >= 0) {
                    s = s.substring(0, s.indexOf(47));
                }
                try {
                    (this.engineParams = this.createParametersInstance(s)).init(new IvParameterSpec(this.ivParam.getIV()));
                }
                catch (final Exception ex4) {
                    throw new RuntimeException(ex4.toString());
                }
            }
        }
        return this.engineParams;
    }
    
    @Override
    protected void engineSetMode(final String s) throws NoSuchAlgorithmException {
        if (this.baseEngine == null) {
            throw new NoSuchAlgorithmException("no mode supported for this algorithm");
        }
        this.modeName = Strings.toUpperCase(s);
        if (this.modeName.equals("ECB")) {
            this.ivLength = 0;
            this.cipher = new BufferedGenericBlockCipher(this.baseEngine);
        }
        else if (this.modeName.equals("CBC")) {
            this.ivLength = this.baseEngine.getBlockSize();
            this.cipher = new BufferedGenericBlockCipher(CBCBlockCipher.newInstance(this.baseEngine));
        }
        else if (this.modeName.startsWith("OFB")) {
            this.ivLength = this.baseEngine.getBlockSize();
            if (this.modeName.length() != 3) {
                this.cipher = new BufferedGenericBlockCipher(new OFBBlockCipher(this.baseEngine, Integer.parseInt(this.modeName.substring(3))));
            }
            else {
                this.cipher = new BufferedGenericBlockCipher(new OFBBlockCipher(this.baseEngine, 8 * this.baseEngine.getBlockSize()));
            }
        }
        else if (this.modeName.startsWith("CFB")) {
            this.ivLength = this.baseEngine.getBlockSize();
            if (this.modeName.length() != 3) {
                this.cipher = new BufferedGenericBlockCipher(CFBBlockCipher.newInstance(this.baseEngine, Integer.parseInt(this.modeName.substring(3))));
            }
            else {
                this.cipher = new BufferedGenericBlockCipher(CFBBlockCipher.newInstance(this.baseEngine, 8 * this.baseEngine.getBlockSize()));
            }
        }
        else if (this.modeName.startsWith("PGPCFB")) {
            final boolean equals = this.modeName.equals("PGPCFBWITHIV");
            if (!equals && this.modeName.length() != 6) {
                throw new NoSuchAlgorithmException("no mode support for " + this.modeName);
            }
            this.ivLength = this.baseEngine.getBlockSize();
            this.cipher = new BufferedGenericBlockCipher(new PGPCFBBlockCipher(this.baseEngine, equals));
        }
        else if (this.modeName.equals("OPENPGPCFB")) {
            this.ivLength = 0;
            this.cipher = new BufferedGenericBlockCipher(new OpenPGPCFBBlockCipher(this.baseEngine));
        }
        else if (this.modeName.equals("FF1")) {
            this.ivLength = 0;
            this.cipher = new BufferedFPEBlockCipher(new FPEFF1Engine(this.baseEngine));
        }
        else if (this.modeName.equals("FF3-1")) {
            this.ivLength = 0;
            this.cipher = new BufferedFPEBlockCipher(new FPEFF3_1Engine(this.baseEngine));
        }
        else if (this.modeName.equals("SIC")) {
            this.ivLength = this.baseEngine.getBlockSize();
            if (this.ivLength < 16) {
                throw new IllegalArgumentException("Warning: SIC-Mode can become a twotime-pad if the blocksize of the cipher is too small. Use a cipher with a block size of at least 128 bits (e.g. AES)");
            }
            this.fixedIv = false;
            this.cipher = new BufferedGenericBlockCipher(new DefaultBufferedBlockCipher(SICBlockCipher.newInstance(this.baseEngine)));
        }
        else if (this.modeName.equals("CTR")) {
            this.ivLength = this.baseEngine.getBlockSize();
            this.fixedIv = false;
            if (this.baseEngine instanceof DSTU7624Engine) {
                this.cipher = new BufferedGenericBlockCipher(new DefaultBufferedBlockCipher(new KCTRBlockCipher(this.baseEngine)));
            }
            else {
                this.cipher = new BufferedGenericBlockCipher(new DefaultBufferedBlockCipher(SICBlockCipher.newInstance(this.baseEngine)));
            }
        }
        else if (this.modeName.equals("GOFB")) {
            this.ivLength = this.baseEngine.getBlockSize();
            this.cipher = new BufferedGenericBlockCipher(new DefaultBufferedBlockCipher(new GOFBBlockCipher(this.baseEngine)));
        }
        else if (this.modeName.equals("GCFB")) {
            this.ivLength = this.baseEngine.getBlockSize();
            this.cipher = new BufferedGenericBlockCipher(new DefaultBufferedBlockCipher(new GCFBBlockCipher(this.baseEngine)));
        }
        else if (this.modeName.equals("CTS")) {
            this.ivLength = this.baseEngine.getBlockSize();
            this.cipher = new BufferedGenericBlockCipher(new CTSBlockCipher(CBCBlockCipher.newInstance(this.baseEngine)));
        }
        else if (this.modeName.equals("CCM")) {
            this.ivLength = 12;
            if (this.baseEngine instanceof DSTU7624Engine) {
                this.cipher = new AEADGenericBlockCipher(new KCCMBlockCipher(this.baseEngine));
            }
            else {
                this.cipher = new AEADGenericBlockCipher(CCMBlockCipher.newInstance(this.baseEngine));
            }
        }
        else if (this.modeName.equals("OCB")) {
            if (this.engineProvider == null) {
                throw new NoSuchAlgorithmException("can't support mode " + s);
            }
            this.ivLength = 15;
            this.cipher = new AEADGenericBlockCipher(new OCBBlockCipher(this.baseEngine, this.engineProvider.get()));
        }
        else if (this.modeName.equals("EAX")) {
            this.ivLength = this.baseEngine.getBlockSize();
            this.cipher = new AEADGenericBlockCipher(new EAXBlockCipher(this.baseEngine));
        }
        else if (this.modeName.equals("GCM-SIV")) {
            this.ivLength = 12;
            this.cipher = new AEADGenericBlockCipher(new GCMSIVBlockCipher(this.baseEngine));
        }
        else {
            if (!this.modeName.equals("GCM")) {
                throw new NoSuchAlgorithmException("can't support mode " + s);
            }
            if (this.baseEngine instanceof DSTU7624Engine) {
                this.ivLength = this.baseEngine.getBlockSize();
                this.cipher = new AEADGenericBlockCipher(new KGCMBlockCipher(this.baseEngine));
            }
            else {
                this.ivLength = 12;
                this.cipher = new AEADGenericBlockCipher(GCMBlockCipher.newInstance(this.baseEngine));
            }
        }
    }
    
    @Override
    protected void engineSetPadding(final String str) throws NoSuchPaddingException {
        if (this.baseEngine == null) {
            throw new NoSuchPaddingException("no padding supported for this algorithm");
        }
        final String upperCase = Strings.toUpperCase(str);
        if (upperCase.equals("NOPADDING")) {
            if (this.cipher.wrapOnNoPadding()) {
                this.cipher = new BufferedGenericBlockCipher(new DefaultBufferedBlockCipher(this.cipher.getUnderlyingCipher()));
            }
        }
        else if (upperCase.equals("WITHCTS") || upperCase.equals("CTSPADDING") || upperCase.equals("CS3PADDING")) {
            this.cipher = new BufferedGenericBlockCipher(new CTSBlockCipher(this.cipher.getUnderlyingCipher()));
        }
        else {
            this.padded = true;
            if (this.isAEADModeName(this.modeName)) {
                throw new NoSuchPaddingException("Only NoPadding can be used with AEAD modes.");
            }
            if (upperCase.equals("PKCS5PADDING") || upperCase.equals("PKCS7PADDING")) {
                this.cipher = new BufferedGenericBlockCipher(this.cipher.getUnderlyingCipher());
            }
            else if (upperCase.equals("ZEROBYTEPADDING")) {
                this.cipher = new BufferedGenericBlockCipher(this.cipher.getUnderlyingCipher(), new ZeroBytePadding());
            }
            else if (upperCase.equals("ISO10126PADDING") || upperCase.equals("ISO10126-2PADDING")) {
                this.cipher = new BufferedGenericBlockCipher(this.cipher.getUnderlyingCipher(), new ISO10126d2Padding());
            }
            else if (upperCase.equals("X9.23PADDING") || upperCase.equals("X923PADDING")) {
                this.cipher = new BufferedGenericBlockCipher(this.cipher.getUnderlyingCipher(), new X923Padding());
            }
            else if (upperCase.equals("ISO7816-4PADDING") || upperCase.equals("ISO9797-1PADDING")) {
                this.cipher = new BufferedGenericBlockCipher(this.cipher.getUnderlyingCipher(), new ISO7816d4Padding());
            }
            else {
                if (!upperCase.equals("TBCPADDING")) {
                    throw new NoSuchPaddingException("Padding " + str + " unknown.");
                }
                this.cipher = new BufferedGenericBlockCipher(this.cipher.getUnderlyingCipher(), new TBCPadding());
            }
        }
    }
    
    @Override
    protected void engineInit(final int i, final Key key, final AlgorithmParameterSpec algorithmParameterSpec, final SecureRandom secureRandom) throws InvalidKeyException, InvalidAlgorithmParameterException {
        this.pbeSpec = null;
        this.pbeAlgorithm = null;
        this.engineParams = null;
        this.aeadParams = null;
        if (!(key instanceof SecretKey)) {
            throw new InvalidKeyException("Key for algorithm " + ((key != null) ? key.getAlgorithm() : null) + " not suitable for symmetric enryption.");
        }
        if (algorithmParameterSpec == null && this.baseEngine != null && this.baseEngine.getAlgorithmName().startsWith("RC5-64")) {
            throw new InvalidAlgorithmParameterException("RC5 requires an RC5ParametersSpec to be passed in.");
        }
        CipherParameters cipherParameters = null;
        Label_1006: {
            if (this.scheme != 2) {
                if (!(key instanceof PKCS12Key)) {
                    if (key instanceof PBKDF1Key) {
                        final PBKDF1Key pbkdf1Key = (PBKDF1Key)key;
                        if (algorithmParameterSpec instanceof PBEParameterSpec) {
                            this.pbeSpec = (PBEParameterSpec)algorithmParameterSpec;
                        }
                        if (pbkdf1Key instanceof PBKDF1KeyWithParameters && this.pbeSpec == null) {
                            this.pbeSpec = new PBEParameterSpec(((PBKDF1KeyWithParameters)pbkdf1Key).getSalt(), ((PBKDF1KeyWithParameters)pbkdf1Key).getIterationCount());
                        }
                        cipherParameters = Util.makePBEParameters(pbkdf1Key.getEncoded(), 0, this.digest, this.keySizeInBits, this.ivLength * 8, this.pbeSpec, this.cipher.getAlgorithmName());
                        if (cipherParameters instanceof ParametersWithIV) {
                            this.ivParam = (ParametersWithIV)cipherParameters;
                        }
                        break Label_1006;
                    }
                    if (key instanceof PBKDF2Key) {
                        final PBKDF2Key pbkdf2Key = (PBKDF2Key)key;
                        if (algorithmParameterSpec instanceof PBEParameterSpec) {
                            this.pbeSpec = (PBEParameterSpec)algorithmParameterSpec;
                        }
                        if (pbkdf2Key instanceof PBKDF2KeyWithParameters && this.pbeSpec == null) {
                            this.pbeSpec = new PBEParameterSpec(((PBKDF2KeyWithParameters)pbkdf2Key).getSalt(), ((PBKDF2KeyWithParameters)pbkdf2Key).getIterationCount());
                        }
                        cipherParameters = Util.makePBEParameters(pbkdf2Key.getEncoded(), 1, 9, this.keySizeInBits, 0, this.pbeSpec, this.cipher.getAlgorithmName());
                        if (cipherParameters instanceof ParametersWithIV) {
                            this.ivParam = (ParametersWithIV)cipherParameters;
                        }
                        break Label_1006;
                    }
                    if (key instanceof BCPBEKey) {
                        final BCPBEKey bcpbeKey = (BCPBEKey)key;
                        if (bcpbeKey.getOID() != null) {
                            this.pbeAlgorithm = bcpbeKey.getOID().getId();
                        }
                        else {
                            this.pbeAlgorithm = bcpbeKey.getAlgorithm();
                        }
                        if (bcpbeKey.getParam() != null) {
                            cipherParameters = this.adjustParameters(algorithmParameterSpec, bcpbeKey.getParam());
                        }
                        else {
                            if (!(algorithmParameterSpec instanceof PBEParameterSpec)) {
                                throw new InvalidAlgorithmParameterException("PBE requires PBE parameters to be set.");
                            }
                            this.pbeSpec = (PBEParameterSpec)algorithmParameterSpec;
                            cipherParameters = Util.makePBEParameters(bcpbeKey, algorithmParameterSpec, this.cipher.getUnderlyingCipher().getAlgorithmName());
                        }
                        if (cipherParameters instanceof ParametersWithIV) {
                            this.ivParam = (ParametersWithIV)cipherParameters;
                        }
                        break Label_1006;
                    }
                    if (key instanceof PBEKey) {
                        final PBEKey pbeKey = (PBEKey)key;
                        this.pbeSpec = (PBEParameterSpec)algorithmParameterSpec;
                        if (pbeKey instanceof PKCS12KeyWithParameters && this.pbeSpec == null) {
                            this.pbeSpec = new PBEParameterSpec(pbeKey.getSalt(), pbeKey.getIterationCount());
                        }
                        cipherParameters = Util.makePBEParameters(pbeKey.getEncoded(), this.scheme, this.digest, this.keySizeInBits, this.ivLength * 8, this.pbeSpec, this.cipher.getAlgorithmName());
                        if (cipherParameters instanceof ParametersWithIV) {
                            this.ivParam = (ParametersWithIV)cipherParameters;
                        }
                        break Label_1006;
                    }
                    if (key instanceof RepeatedSecretKeySpec) {
                        cipherParameters = null;
                        break Label_1006;
                    }
                    if (this.scheme == 0 || this.scheme == 4 || this.scheme == 1 || this.scheme == 5) {
                        throw new InvalidKeyException("Algorithm requires a PBE key");
                    }
                    cipherParameters = new KeyParameter(key.getEncoded());
                    break Label_1006;
                }
            }
            SecretKey secretKey;
            try {
                secretKey = (SecretKey)key;
            }
            catch (final Exception ex) {
                throw new InvalidKeyException("PKCS12 requires a SecretKey/PBEKey");
            }
            if (algorithmParameterSpec instanceof PBEParameterSpec) {
                this.pbeSpec = (PBEParameterSpec)algorithmParameterSpec;
            }
            if (secretKey instanceof PBEKey && this.pbeSpec == null) {
                final PBEKey pbeKey2 = (PBEKey)secretKey;
                if (pbeKey2.getSalt() == null) {
                    throw new InvalidAlgorithmParameterException("PBEKey requires parameters to specify salt");
                }
                this.pbeSpec = new PBEParameterSpec(pbeKey2.getSalt(), pbeKey2.getIterationCount());
            }
            if (this.pbeSpec == null && !(secretKey instanceof PBEKey)) {
                throw new InvalidKeyException("Algorithm requires a PBE key");
            }
            this.pbeAlgorithm = "PKCS12PBE";
            if (key instanceof BCPBEKey) {
                final CipherParameters param = ((BCPBEKey)key).getParam();
                if (param instanceof ParametersWithIV) {
                    cipherParameters = param;
                }
                else {
                    if (param != null) {
                        throw new InvalidKeyException("Algorithm requires a PBE key suitable for PKCS12");
                    }
                    cipherParameters = Util.makePBEParameters(secretKey.getEncoded(), 2, this.digest, this.keySizeInBits, this.ivLength * 8, this.pbeSpec, this.cipher.getAlgorithmName());
                }
            }
            else {
                cipherParameters = Util.makePBEParameters(secretKey.getEncoded(), 2, this.digest, this.keySizeInBits, this.ivLength * 8, this.pbeSpec, this.cipher.getAlgorithmName());
            }
            if (cipherParameters instanceof ParametersWithIV) {
                this.ivParam = (ParametersWithIV)cipherParameters;
            }
        }
        AlgorithmParameterSpec parameterSpec = algorithmParameterSpec;
        if (algorithmParameterSpec instanceof PBEParameterSpec) {
            parameterSpec = ((PBEParameterSpec)algorithmParameterSpec).getParameterSpec();
            if (parameterSpec instanceof IvParameterSpec && ((IvParameterSpec)parameterSpec).getIV().length == 0) {
                parameterSpec = algorithmParameterSpec;
            }
        }
        if (parameterSpec instanceof AEADParameterSpec) {
            if (!this.isAEADModeName(this.modeName) && !(this.cipher instanceof AEADGenericBlockCipher)) {
                throw new InvalidAlgorithmParameterException("AEADParameterSpec can only be used with AEAD modes.");
            }
            final AEADParameterSpec aeadParameterSpec = (AEADParameterSpec)parameterSpec;
            KeyParameter keyParameter;
            if (cipherParameters instanceof ParametersWithIV) {
                keyParameter = (KeyParameter)((ParametersWithIV)cipherParameters).getParameters();
            }
            else {
                keyParameter = (KeyParameter)cipherParameters;
            }
            final AEADParameters aeadParams = new AEADParameters(keyParameter, aeadParameterSpec.getMacSizeInBits(), aeadParameterSpec.getNonce(), aeadParameterSpec.getAssociatedData());
            this.aeadParams = aeadParams;
            cipherParameters = aeadParams;
        }
        else if (parameterSpec instanceof IvParameterSpec) {
            if (this.ivLength != 0) {
                final IvParameterSpec ivParameterSpec = (IvParameterSpec)parameterSpec;
                if (ivParameterSpec.getIV().length != this.ivLength && !(this.cipher instanceof AEADGenericBlockCipher) && this.fixedIv) {
                    throw new InvalidAlgorithmParameterException("IV must be " + this.ivLength + " bytes long.");
                }
                if (cipherParameters instanceof ParametersWithIV) {
                    cipherParameters = new ParametersWithIV(((ParametersWithIV)cipherParameters).getParameters(), ivParameterSpec.getIV());
                }
                else {
                    cipherParameters = new ParametersWithIV(cipherParameters, ivParameterSpec.getIV());
                }
                this.ivParam = (ParametersWithIV)cipherParameters;
            }
            else if (this.modeName != null && this.modeName.equals("ECB")) {
                throw new InvalidAlgorithmParameterException("ECB mode does not use an IV");
            }
        }
        else if (parameterSpec instanceof GOST28147ParameterSpec) {
            final GOST28147ParameterSpec gost28147ParameterSpec = (GOST28147ParameterSpec)parameterSpec;
            cipherParameters = new ParametersWithSBox(new KeyParameter(key.getEncoded()), ((GOST28147ParameterSpec)parameterSpec).getSBox());
            if (gost28147ParameterSpec.getIV() != null && this.ivLength != 0) {
                if (cipherParameters instanceof ParametersWithIV) {
                    cipherParameters = new ParametersWithIV(((ParametersWithIV)cipherParameters).getParameters(), gost28147ParameterSpec.getIV());
                }
                else {
                    cipherParameters = new ParametersWithIV(cipherParameters, gost28147ParameterSpec.getIV());
                }
                this.ivParam = (ParametersWithIV)cipherParameters;
            }
        }
        else if (parameterSpec instanceof RC2ParameterSpec) {
            final RC2ParameterSpec rc2ParameterSpec = (RC2ParameterSpec)parameterSpec;
            cipherParameters = new RC2Parameters(key.getEncoded(), ((RC2ParameterSpec)parameterSpec).getEffectiveKeyBits());
            if (rc2ParameterSpec.getIV() != null && this.ivLength != 0) {
                if (cipherParameters instanceof ParametersWithIV) {
                    cipherParameters = new ParametersWithIV(((ParametersWithIV)cipherParameters).getParameters(), rc2ParameterSpec.getIV());
                }
                else {
                    cipherParameters = new ParametersWithIV(cipherParameters, rc2ParameterSpec.getIV());
                }
                this.ivParam = (ParametersWithIV)cipherParameters;
            }
        }
        else if (parameterSpec instanceof RC5ParameterSpec) {
            final RC5ParameterSpec rc5ParameterSpec = (RC5ParameterSpec)parameterSpec;
            cipherParameters = new RC5Parameters(key.getEncoded(), ((RC5ParameterSpec)parameterSpec).getRounds());
            if (!this.baseEngine.getAlgorithmName().startsWith("RC5")) {
                throw new InvalidAlgorithmParameterException("RC5 parameters passed to a cipher that is not RC5.");
            }
            if (this.baseEngine.getAlgorithmName().equals("RC5-32")) {
                if (rc5ParameterSpec.getWordSize() != 32) {
                    throw new InvalidAlgorithmParameterException("RC5 already set up for a word size of 32 not " + rc5ParameterSpec.getWordSize() + ".");
                }
            }
            else if (this.baseEngine.getAlgorithmName().equals("RC5-64") && rc5ParameterSpec.getWordSize() != 64) {
                throw new InvalidAlgorithmParameterException("RC5 already set up for a word size of 64 not " + rc5ParameterSpec.getWordSize() + ".");
            }
            if (rc5ParameterSpec.getIV() != null && this.ivLength != 0) {
                if (cipherParameters instanceof ParametersWithIV) {
                    cipherParameters = new ParametersWithIV(((ParametersWithIV)cipherParameters).getParameters(), rc5ParameterSpec.getIV());
                }
                else {
                    cipherParameters = new ParametersWithIV(cipherParameters, rc5ParameterSpec.getIV());
                }
                this.ivParam = (ParametersWithIV)cipherParameters;
            }
        }
        else if (parameterSpec instanceof FPEParameterSpec) {
            final FPEParameterSpec fpeParameterSpec = (FPEParameterSpec)parameterSpec;
            cipherParameters = new FPEParameters((KeyParameter)cipherParameters, fpeParameterSpec.getRadixConverter(), fpeParameterSpec.getTweak(), fpeParameterSpec.isUsingInverseFunction());
        }
        else if (GcmSpecUtil.isGcmSpec(parameterSpec)) {
            if (!this.isAEADModeName(this.modeName) && !(this.cipher instanceof AEADGenericBlockCipher)) {
                throw new InvalidAlgorithmParameterException("GCMParameterSpec can only be used with AEAD modes.");
            }
            KeyParameter keyParameter2;
            if (cipherParameters instanceof ParametersWithIV) {
                keyParameter2 = (KeyParameter)((ParametersWithIV)cipherParameters).getParameters();
            }
            else {
                keyParameter2 = (KeyParameter)cipherParameters;
            }
            final AEADParameters aeadParameters = GcmSpecUtil.extractAeadParameters(keyParameter2, parameterSpec);
            this.aeadParams = aeadParameters;
            cipherParameters = aeadParameters;
        }
        else if (parameterSpec != null && !(parameterSpec instanceof PBEParameterSpec)) {
            throw new InvalidAlgorithmParameterException("unknown parameter type.");
        }
        if (this.ivLength != 0 && !(cipherParameters instanceof ParametersWithIV) && !(cipherParameters instanceof AEADParameters)) {
            SecureRandom secureRandom2 = secureRandom;
            if (secureRandom2 == null) {
                secureRandom2 = CryptoServicesRegistrar.getSecureRandom();
            }
            if (i == 1 || i == 3) {
                final byte[] bytes = new byte[this.ivLength];
                secureRandom2.nextBytes(bytes);
                cipherParameters = new ParametersWithIV(cipherParameters, bytes);
                this.ivParam = (ParametersWithIV)cipherParameters;
            }
            else if (this.cipher.getUnderlyingCipher().getAlgorithmName().indexOf("PGPCFB") < 0) {
                throw new InvalidAlgorithmParameterException("no IV set when one expected");
            }
        }
        if (secureRandom != null && this.padded) {
            cipherParameters = new ParametersWithRandom(cipherParameters, secureRandom);
        }
        try {
            switch (i) {
                case 1:
                case 3: {
                    this.cipher.init(true, cipherParameters);
                    break;
                }
                case 2:
                case 4: {
                    this.cipher.init(false, cipherParameters);
                    break;
                }
                default: {
                    throw new InvalidParameterException("unknown opmode " + i + " passed");
                }
            }
            if (this.cipher instanceof AEADGenericBlockCipher && this.aeadParams == null) {
                this.aeadParams = new AEADParameters((KeyParameter)this.ivParam.getParameters(), ((AEADGenericBlockCipher)this.cipher).cipher.getMac().length * 8, this.ivParam.getIV());
            }
        }
        catch (final IllegalArgumentException cause) {
            throw new InvalidAlgorithmParameterException(cause.getMessage(), cause);
        }
        catch (final Exception ex2) {
            throw new InvalidKeyOrParametersException(ex2.getMessage(), ex2);
        }
    }
    
    private CipherParameters adjustParameters(final AlgorithmParameterSpec algorithmParameterSpec, CipherParameters cipherParameters) {
        if (cipherParameters instanceof ParametersWithIV) {
            final CipherParameters parameters = ((ParametersWithIV)cipherParameters).getParameters();
            if (algorithmParameterSpec instanceof IvParameterSpec) {
                this.ivParam = new ParametersWithIV(parameters, ((IvParameterSpec)algorithmParameterSpec).getIV());
                cipherParameters = this.ivParam;
            }
            else if (algorithmParameterSpec instanceof GOST28147ParameterSpec) {
                final GOST28147ParameterSpec gost28147ParameterSpec = (GOST28147ParameterSpec)algorithmParameterSpec;
                cipherParameters = new ParametersWithSBox(cipherParameters, gost28147ParameterSpec.getSBox());
                if (gost28147ParameterSpec.getIV() != null && this.ivLength != 0) {
                    this.ivParam = new ParametersWithIV(parameters, gost28147ParameterSpec.getIV());
                    cipherParameters = this.ivParam;
                }
            }
        }
        else if (algorithmParameterSpec instanceof IvParameterSpec) {
            this.ivParam = new ParametersWithIV(cipherParameters, ((IvParameterSpec)algorithmParameterSpec).getIV());
            cipherParameters = this.ivParam;
        }
        else if (algorithmParameterSpec instanceof GOST28147ParameterSpec) {
            final GOST28147ParameterSpec gost28147ParameterSpec2 = (GOST28147ParameterSpec)algorithmParameterSpec;
            cipherParameters = new ParametersWithSBox(cipherParameters, gost28147ParameterSpec2.getSBox());
            if (gost28147ParameterSpec2.getIV() != null && this.ivLength != 0) {
                cipherParameters = new ParametersWithIV(cipherParameters, gost28147ParameterSpec2.getIV());
            }
        }
        return cipherParameters;
    }
    
    @Override
    protected void engineInit(final int n, final Key key, final AlgorithmParameters engineParams, final SecureRandom secureRandom) throws InvalidKeyException, InvalidAlgorithmParameterException {
        AlgorithmParameterSpec spec = null;
        if (engineParams != null) {
            spec = SpecUtil.extractSpec(engineParams, BaseBlockCipher.availableSpecs);
            if (spec == null) {
                throw new InvalidAlgorithmParameterException("can't handle parameter " + engineParams.toString());
            }
        }
        this.engineInit(n, key, spec, secureRandom);
        this.engineParams = engineParams;
    }
    
    @Override
    protected void engineInit(final int n, final Key key, final SecureRandom secureRandom) throws InvalidKeyException {
        try {
            this.engineInit(n, key, (AlgorithmParameterSpec)null, secureRandom);
        }
        catch (final InvalidAlgorithmParameterException ex) {
            throw new InvalidKeyException(ex.getMessage());
        }
    }
    
    @Override
    protected void engineUpdateAAD(final byte[] array, final int n, final int n2) {
        this.cipher.updateAAD(array, n, n2);
    }
    
    @Override
    protected void engineUpdateAAD(final ByteBuffer byteBuffer) {
        int i = byteBuffer.remaining();
        if (i >= 1) {
            if (byteBuffer.hasArray()) {
                this.engineUpdateAAD(byteBuffer.array(), byteBuffer.arrayOffset() + byteBuffer.position(), i);
                byteBuffer.position();
            }
            else if (i <= 512) {
                final byte[] dst = new byte[i];
                byteBuffer.get(dst);
                this.engineUpdateAAD(dst, 0, dst.length);
                Arrays.fill(dst, (byte)0);
            }
            else {
                final byte[] dst2 = new byte[512];
                do {
                    final int min = Math.min(dst2.length, i);
                    byteBuffer.get(dst2, 0, min);
                    this.engineUpdateAAD(dst2, 0, min);
                    i -= min;
                } while (i > 0);
                Arrays.fill(dst2, (byte)0);
            }
        }
    }
    
    @Override
    protected byte[] engineUpdate(final byte[] array, final int n, final int n2) {
        final int updateOutputSize = this.cipher.getUpdateOutputSize(n2);
        if (updateOutputSize <= 0) {
            this.cipher.processBytes(array, n, n2, null, 0);
            return null;
        }
        final byte[] array2 = new byte[updateOutputSize];
        final int processBytes = this.cipher.processBytes(array, n, n2, array2, 0);
        if (processBytes == 0) {
            return null;
        }
        if (processBytes != array2.length) {
            final byte[] array3 = new byte[processBytes];
            System.arraycopy(array2, 0, array3, 0, processBytes);
            return array3;
        }
        return array2;
    }
    
    @Override
    protected int engineUpdate(final byte[] array, final int n, final int n2, final byte[] array2, final int n3) throws ShortBufferException {
        if (n3 + this.cipher.getUpdateOutputSize(n2) > array2.length) {
            throw new ShortBufferException("output buffer too short for input.");
        }
        try {
            return this.cipher.processBytes(array, n, n2, array2, n3);
        }
        catch (final DataLengthException ex) {
            throw new IllegalStateException(ex.toString());
        }
    }
    
    @Override
    protected byte[] engineDoFinal(final byte[] array, final int n, final int n2) throws IllegalBlockSizeException, BadPaddingException {
        int processBytes = 0;
        final byte[] array2 = new byte[this.engineGetOutputSize(n2)];
        if (n2 != 0) {
            processBytes = this.cipher.processBytes(array, n, n2, array2, 0);
        }
        int n3;
        try {
            n3 = processBytes + this.cipher.doFinal(array2, processBytes);
        }
        catch (final DataLengthException ex) {
            throw new IllegalBlockSizeException(ex.getMessage());
        }
        if (n3 == array2.length) {
            return array2;
        }
        if (n3 > array2.length) {
            throw new IllegalBlockSizeException("internal buffer overflow");
        }
        final byte[] array3 = new byte[n3];
        System.arraycopy(array2, 0, array3, 0, n3);
        return array3;
    }
    
    @Override
    protected int engineDoFinal(final byte[] array, final int n, final int n2, final byte[] array2, final int n3) throws IllegalBlockSizeException, BadPaddingException, ShortBufferException {
        int processBytes = 0;
        if (n3 + this.engineGetOutputSize(n2) > array2.length) {
            throw new ShortBufferException("output buffer too short for input.");
        }
        try {
            if (n2 != 0) {
                processBytes = this.cipher.processBytes(array, n, n2, array2, n3);
            }
            return processBytes + this.cipher.doFinal(array2, n3 + processBytes);
        }
        catch (final OutputLengthException ex) {
            throw new IllegalBlockSizeException(ex.getMessage());
        }
        catch (final DataLengthException ex2) {
            throw new IllegalBlockSizeException(ex2.getMessage());
        }
    }
    
    private boolean isAEADModeName(final String anObject) {
        return "CCM".equals(anObject) || "EAX".equals(anObject) || "GCM".equals(anObject) || "GCM-SIV".equals(anObject) || "OCB".equals(anObject);
    }
    
    static {
        availableSpecs = new Class[] { RC2ParameterSpec.class, RC5ParameterSpec.class, GcmSpecUtil.gcmSpecClass, GOST28147ParameterSpec.class, IvParameterSpec.class, PBEParameterSpec.class };
    }
    
    private static class AEADGenericBlockCipher implements GenericBlockCipher
    {
        private static final Constructor aeadBadTagConstructor;
        private AEADCipher cipher;
        
        private static Constructor findExceptionConstructor(final Class clazz) {
            try {
                return clazz.getConstructor(String.class);
            }
            catch (final Exception ex) {
                return null;
            }
        }
        
        AEADGenericBlockCipher(final AEADCipher cipher) {
            this.cipher = cipher;
        }
        
        @Override
        public void init(final boolean b, final CipherParameters cipherParameters) throws IllegalArgumentException {
            this.cipher.init(b, cipherParameters);
        }
        
        @Override
        public String getAlgorithmName() {
            if (this.cipher instanceof AEADBlockCipher) {
                return ((AEADBlockCipher)this.cipher).getUnderlyingCipher().getAlgorithmName();
            }
            return this.cipher.getAlgorithmName();
        }
        
        @Override
        public boolean wrapOnNoPadding() {
            return false;
        }
        
        @Override
        public BlockCipher getUnderlyingCipher() {
            if (this.cipher instanceof AEADBlockCipher) {
                return ((AEADBlockCipher)this.cipher).getUnderlyingCipher();
            }
            return null;
        }
        
        @Override
        public int getOutputSize(final int n) {
            return this.cipher.getOutputSize(n);
        }
        
        @Override
        public int getUpdateOutputSize(final int n) {
            return this.cipher.getUpdateOutputSize(n);
        }
        
        @Override
        public void updateAAD(final byte[] array, final int n, final int n2) {
            this.cipher.processAADBytes(array, n, n2);
        }
        
        @Override
        public int processByte(final byte b, final byte[] array, final int n) throws DataLengthException {
            return this.cipher.processByte(b, array, n);
        }
        
        @Override
        public int processBytes(final byte[] array, final int n, final int n2, final byte[] array2, final int n3) throws DataLengthException {
            return this.cipher.processBytes(array, n, n2, array2, n3);
        }
        
        @Override
        public int doFinal(final byte[] array, final int n) throws IllegalStateException, BadPaddingException {
            try {
                return this.cipher.doFinal(array, n);
            }
            catch (final InvalidCipherTextException ex) {
                if (AEADGenericBlockCipher.aeadBadTagConstructor != null) {
                    BadPaddingException ex2 = null;
                    try {
                        ex2 = AEADGenericBlockCipher.aeadBadTagConstructor.newInstance(ex.getMessage());
                    }
                    catch (final Exception ex3) {}
                    if (ex2 != null) {
                        throw ex2;
                    }
                }
                throw new BadPaddingException(ex.getMessage());
            }
        }
        
        static {
            final Class loadClass = ClassUtil.loadClass(BaseBlockCipher.class, "javax.crypto.AEADBadTagException");
            if (loadClass != null) {
                aeadBadTagConstructor = findExceptionConstructor(loadClass);
            }
            else {
                aeadBadTagConstructor = null;
            }
        }
    }
    
    private interface GenericBlockCipher
    {
        void init(final boolean p0, final CipherParameters p1) throws IllegalArgumentException;
        
        boolean wrapOnNoPadding();
        
        String getAlgorithmName();
        
        BlockCipher getUnderlyingCipher();
        
        int getOutputSize(final int p0);
        
        int getUpdateOutputSize(final int p0);
        
        void updateAAD(final byte[] p0, final int p1, final int p2);
        
        int processByte(final byte p0, final byte[] p1, final int p2) throws DataLengthException;
        
        int processBytes(final byte[] p0, final int p1, final int p2, final byte[] p3, final int p4) throws DataLengthException;
        
        int doFinal(final byte[] p0, final int p1) throws IllegalStateException, BadPaddingException;
    }
    
    private static class BufferedFPEBlockCipher implements GenericBlockCipher
    {
        private FPEEngine cipher;
        private ErasableOutputStream eOut;
        
        BufferedFPEBlockCipher(final FPEEngine cipher) {
            this.eOut = new ErasableOutputStream();
            this.cipher = cipher;
        }
        
        @Override
        public void init(final boolean b, final CipherParameters cipherParameters) throws IllegalArgumentException {
            this.cipher.init(b, cipherParameters);
        }
        
        @Override
        public boolean wrapOnNoPadding() {
            return false;
        }
        
        @Override
        public String getAlgorithmName() {
            return this.cipher.getAlgorithmName();
        }
        
        @Override
        public BlockCipher getUnderlyingCipher() {
            throw new IllegalStateException("not applicable for FPE");
        }
        
        @Override
        public int getOutputSize(final int n) {
            return this.eOut.size() + n;
        }
        
        @Override
        public int getUpdateOutputSize(final int n) {
            return 0;
        }
        
        @Override
        public void updateAAD(final byte[] array, final int n, final int n2) {
            throw new UnsupportedOperationException("AAD is not supported in the current mode.");
        }
        
        @Override
        public int processByte(final byte b, final byte[] array, final int n) throws DataLengthException {
            this.eOut.write(b);
            return 0;
        }
        
        @Override
        public int processBytes(final byte[] b, final int off, final int len, final byte[] array, final int n) throws DataLengthException {
            this.eOut.write(b, off, len);
            return 0;
        }
        
        @Override
        public int doFinal(final byte[] array, final int n) throws IllegalStateException, BadPaddingException {
            try {
                return this.cipher.processBlock(this.eOut.getBuf(), 0, this.eOut.size(), array, n);
            }
            finally {
                this.eOut.erase();
            }
        }
    }
    
    private static class BufferedGenericBlockCipher implements GenericBlockCipher
    {
        private BufferedBlockCipher cipher;
        
        BufferedGenericBlockCipher(final BufferedBlockCipher cipher) {
            this.cipher = cipher;
        }
        
        BufferedGenericBlockCipher(final BlockCipher blockCipher) {
            this(blockCipher, new PKCS7Padding());
        }
        
        BufferedGenericBlockCipher(final BlockCipher blockCipher, final BlockCipherPadding blockCipherPadding) {
            this.cipher = new PaddedBufferedBlockCipher(blockCipher, blockCipherPadding);
        }
        
        @Override
        public void init(final boolean b, final CipherParameters cipherParameters) throws IllegalArgumentException {
            this.cipher.init(b, cipherParameters);
        }
        
        @Override
        public boolean wrapOnNoPadding() {
            return !(this.cipher instanceof CTSBlockCipher);
        }
        
        @Override
        public String getAlgorithmName() {
            return this.cipher.getUnderlyingCipher().getAlgorithmName();
        }
        
        @Override
        public BlockCipher getUnderlyingCipher() {
            return this.cipher.getUnderlyingCipher();
        }
        
        @Override
        public int getOutputSize(final int n) {
            return this.cipher.getOutputSize(n);
        }
        
        @Override
        public int getUpdateOutputSize(final int n) {
            return this.cipher.getUpdateOutputSize(n);
        }
        
        @Override
        public void updateAAD(final byte[] array, final int n, final int n2) {
            throw new UnsupportedOperationException("AAD is not supported in the current mode.");
        }
        
        @Override
        public int processByte(final byte b, final byte[] array, final int n) throws DataLengthException {
            return this.cipher.processByte(b, array, n);
        }
        
        @Override
        public int processBytes(final byte[] array, final int n, final int n2, final byte[] array2, final int n3) throws DataLengthException {
            return this.cipher.processBytes(array, n, n2, array2, n3);
        }
        
        @Override
        public int doFinal(final byte[] array, final int n) throws IllegalStateException, BadPaddingException {
            try {
                return this.cipher.doFinal(array, n);
            }
            catch (final InvalidCipherTextException ex) {
                throw new BadPaddingException(ex.getMessage());
            }
        }
    }
}
