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

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

import java.io.ByteArrayOutputStream;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.KeySpec;
import java.security.spec.X509EncodedKeySpec;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
import javax.crypto.spec.SecretKeySpec;
import org.bouncycastle.crypto.InvalidCipherTextException;
import javax.crypto.BadPaddingException;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.ShortBufferException;
import java.security.InvalidKeyException;
import org.bouncycastle.crypto.CipherParameters;
import java.security.InvalidParameterException;
import org.bouncycastle.crypto.params.ParametersWithRandom;
import org.bouncycastle.crypto.params.ParametersWithUKM;
import org.bouncycastle.crypto.params.ParametersWithSBox;
import org.bouncycastle.crypto.params.ParametersWithIV;
import org.bouncycastle.crypto.params.KeyParameter;
import java.security.InvalidAlgorithmParameterException;
import java.security.SecureRandom;
import javax.crypto.NoSuchPaddingException;
import java.security.NoSuchProviderException;
import java.security.NoSuchAlgorithmException;
import java.security.spec.AlgorithmParameterSpec;
import java.security.Key;
import org.bouncycastle.util.Arrays;
import org.bouncycastle.jcajce.util.BCJcaJceHelper;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.RC5ParameterSpec;
import javax.crypto.spec.RC2ParameterSpec;
import javax.crypto.spec.PBEParameterSpec;
import org.bouncycastle.jcajce.spec.GOST28147WrapParameterSpec;
import org.bouncycastle.jcajce.util.JcaJceHelper;
import org.bouncycastle.crypto.Wrapper;
import java.security.AlgorithmParameters;
import javax.crypto.CipherSpi;

public abstract class BaseWrapCipher extends CipherSpi implements PBE
{
    private Class[] availableSpecs;
    protected int pbeType;
    protected int pbeHash;
    protected int pbeKeySize;
    protected int pbeIvSize;
    protected AlgorithmParameters engineParams;
    protected Wrapper wrapEngine;
    private int ivSize;
    private byte[] iv;
    private ErasableOutputStream wrapStream;
    private boolean forWrapping;
    private final JcaJceHelper helper;
    
    protected BaseWrapCipher() {
        this.availableSpecs = new Class[] { GOST28147WrapParameterSpec.class, PBEParameterSpec.class, RC2ParameterSpec.class, RC5ParameterSpec.class, IvParameterSpec.class };
        this.pbeType = 2;
        this.pbeHash = 1;
        this.engineParams = null;
        this.wrapEngine = null;
        this.wrapStream = null;
        this.helper = new BCJcaJceHelper();
    }
    
    protected BaseWrapCipher(final Wrapper wrapper) {
        this(wrapper, 0);
    }
    
    protected BaseWrapCipher(final int n, final Wrapper wrapper) {
        this(n, wrapper, 0);
    }
    
    protected BaseWrapCipher(final Wrapper wrapEngine, final int ivSize) {
        this.availableSpecs = new Class[] { GOST28147WrapParameterSpec.class, PBEParameterSpec.class, RC2ParameterSpec.class, RC5ParameterSpec.class, IvParameterSpec.class };
        this.pbeType = 2;
        this.pbeHash = 1;
        this.engineParams = null;
        this.wrapEngine = null;
        this.wrapStream = null;
        this.helper = new BCJcaJceHelper();
        this.wrapEngine = wrapEngine;
        this.ivSize = ivSize;
    }
    
    protected BaseWrapCipher(final int pbeKeySize, final Wrapper wrapEngine, final int ivSize) {
        this.availableSpecs = new Class[] { GOST28147WrapParameterSpec.class, PBEParameterSpec.class, RC2ParameterSpec.class, RC5ParameterSpec.class, IvParameterSpec.class };
        this.pbeType = 2;
        this.pbeHash = 1;
        this.engineParams = null;
        this.wrapEngine = null;
        this.wrapStream = null;
        this.helper = new BCJcaJceHelper();
        this.pbeKeySize = pbeKeySize;
        this.wrapEngine = wrapEngine;
        this.ivSize = ivSize;
    }
    
    @Override
    protected int engineGetBlockSize() {
        return 0;
    }
    
    @Override
    protected byte[] engineGetIV() {
        return Arrays.clone(this.iv);
    }
    
    @Override
    protected int engineGetKeySize(final Key key) {
        return key.getEncoded().length * 8;
    }
    
    @Override
    protected int engineGetOutputSize(final int n) {
        return -1;
    }
    
    @Override
    protected AlgorithmParameters engineGetParameters() {
        if (this.engineParams == null && this.iv != null) {
            String s = this.wrapEngine.getAlgorithmName();
            if (s.indexOf(47) >= 0) {
                s = s.substring(0, s.indexOf(47));
            }
            try {
                (this.engineParams = this.createParametersInstance(s)).init(new IvParameterSpec(this.iv));
            }
            catch (final Exception ex) {
                throw new RuntimeException(ex.toString());
            }
        }
        return this.engineParams;
    }
    
    protected final AlgorithmParameters createParametersInstance(final String s) throws NoSuchAlgorithmException, NoSuchProviderException {
        return this.helper.createAlgorithmParameters(s);
    }
    
    @Override
    protected void engineSetMode(final String str) throws NoSuchAlgorithmException {
        throw new NoSuchAlgorithmException("can't support mode " + str);
    }
    
    @Override
    protected void engineSetPadding(final String str) throws NoSuchPaddingException {
        throw new NoSuchPaddingException("Padding " + str + " unknown.");
    }
    
    @Override
    protected void engineInit(final int n, final Key key, final AlgorithmParameterSpec algorithmParameterSpec, final SecureRandom secureRandom) throws InvalidKeyException, InvalidAlgorithmParameterException {
        CipherParameters cipherParameters;
        if (key instanceof BCPBEKey) {
            final BCPBEKey bcpbeKey = (BCPBEKey)key;
            if (algorithmParameterSpec instanceof PBEParameterSpec) {
                cipherParameters = Util.makePBEParameters(bcpbeKey, algorithmParameterSpec, this.wrapEngine.getAlgorithmName());
            }
            else {
                if (bcpbeKey.getParam() == null) {
                    throw new InvalidAlgorithmParameterException("PBE requires PBE parameters to be set.");
                }
                cipherParameters = bcpbeKey.getParam();
            }
        }
        else {
            cipherParameters = new KeyParameter(key.getEncoded());
        }
        if (algorithmParameterSpec instanceof IvParameterSpec) {
            this.iv = ((IvParameterSpec)algorithmParameterSpec).getIV();
            cipherParameters = new ParametersWithIV(cipherParameters, this.iv);
        }
        if (algorithmParameterSpec instanceof GOST28147WrapParameterSpec) {
            final GOST28147WrapParameterSpec gost28147WrapParameterSpec = (GOST28147WrapParameterSpec)algorithmParameterSpec;
            final byte[] sBox = gost28147WrapParameterSpec.getSBox();
            if (sBox != null) {
                cipherParameters = new ParametersWithSBox(cipherParameters, sBox);
            }
            cipherParameters = new ParametersWithUKM(cipherParameters, gost28147WrapParameterSpec.getUKM());
        }
        if (cipherParameters instanceof KeyParameter && this.ivSize != 0 && (n == 3 || n == 1)) {
            secureRandom.nextBytes(this.iv = new byte[this.ivSize]);
            cipherParameters = new ParametersWithIV(cipherParameters, this.iv);
        }
        if (secureRandom != null) {
            cipherParameters = new ParametersWithRandom(cipherParameters, secureRandom);
        }
        try {
            switch (n) {
                case 3: {
                    this.wrapEngine.init(true, cipherParameters);
                    this.wrapStream = null;
                    this.forWrapping = true;
                    break;
                }
                case 4: {
                    this.wrapEngine.init(false, cipherParameters);
                    this.wrapStream = null;
                    this.forWrapping = false;
                    break;
                }
                case 1: {
                    this.wrapEngine.init(true, cipherParameters);
                    this.wrapStream = new ErasableOutputStream();
                    this.forWrapping = true;
                    break;
                }
                case 2: {
                    this.wrapEngine.init(false, cipherParameters);
                    this.wrapStream = new ErasableOutputStream();
                    this.forWrapping = false;
                    break;
                }
                default: {
                    throw new InvalidParameterException("Unknown mode parameter passed to init.");
                }
            }
        }
        catch (final Exception ex) {
            throw new InvalidKeyOrParametersException(ex.getMessage(), ex);
        }
    }
    
    @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, this.availableSpecs);
            if (spec == null) {
                throw new InvalidAlgorithmParameterException("can't handle parameter " + engineParams.toString());
            }
        }
        this.engineParams = engineParams;
        this.engineInit(n, key, spec, secureRandom);
    }
    
    @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 InvalidKeyOrParametersException(ex.getMessage(), ex);
        }
    }
    
    @Override
    protected byte[] engineUpdate(final byte[] b, final int off, final int len) {
        if (this.wrapStream == null) {
            throw new IllegalStateException("not supported in a wrapping mode");
        }
        this.wrapStream.write(b, off, len);
        return null;
    }
    
    @Override
    protected int engineUpdate(final byte[] b, final int off, final int len, final byte[] array, final int n) throws ShortBufferException {
        if (this.wrapStream == null) {
            throw new IllegalStateException("not supported in a wrapping mode");
        }
        this.wrapStream.write(b, off, len);
        return 0;
    }
    
    @Override
    protected byte[] engineDoFinal(final byte[] b, final int off, final int len) throws IllegalBlockSizeException, BadPaddingException {
        if (this.wrapStream == null) {
            throw new IllegalStateException("not supported in a wrapping mode");
        }
        if (b != null) {
            this.wrapStream.write(b, off, len);
        }
        try {
            if (this.forWrapping) {
                try {
                    return this.wrapEngine.wrap(this.wrapStream.getBuf(), 0, this.wrapStream.size());
                }
                catch (final Exception ex) {
                    throw new IllegalBlockSizeException(ex.getMessage());
                }
            }
            try {
                return this.wrapEngine.unwrap(this.wrapStream.getBuf(), 0, this.wrapStream.size());
            }
            catch (final InvalidCipherTextException ex2) {
                throw new BadPaddingException(ex2.getMessage());
            }
        }
        finally {
            this.wrapStream.erase();
        }
    }
    
    @Override
    protected int engineDoFinal(final byte[] b, final int off, final int len, final byte[] array, final int n) throws IllegalBlockSizeException, BadPaddingException, ShortBufferException {
        if (this.wrapStream == null) {
            throw new IllegalStateException("not supported in a wrapping mode");
        }
        this.wrapStream.write(b, off, len);
        try {
            byte[] array2 = null;
            Label_0122: {
                if (this.forWrapping) {
                    try {
                        array2 = this.wrapEngine.wrap(this.wrapStream.getBuf(), 0, this.wrapStream.size());
                        break Label_0122;
                    }
                    catch (final Exception ex) {
                        throw new IllegalBlockSizeException(ex.getMessage());
                    }
                }
                try {
                    array2 = this.wrapEngine.unwrap(this.wrapStream.getBuf(), 0, this.wrapStream.size());
                }
                catch (final InvalidCipherTextException ex2) {
                    throw new BadPaddingException(ex2.getMessage());
                }
            }
            if (n + array2.length > array.length) {
                throw new ShortBufferException("output buffer too short for input.");
            }
            System.arraycopy(array2, 0, array, n, array2.length);
            return array2.length;
        }
        finally {
            this.wrapStream.erase();
        }
    }
    
    @Override
    protected byte[] engineWrap(final Key key) throws IllegalBlockSizeException, InvalidKeyException {
        final byte[] encoded = key.getEncoded();
        if (encoded == null) {
            throw new InvalidKeyException("Cannot wrap key, null encoding.");
        }
        try {
            if (this.wrapEngine == null) {
                return this.engineDoFinal(encoded, 0, encoded.length);
            }
            return this.wrapEngine.wrap(encoded, 0, encoded.length);
        }
        catch (final BadPaddingException ex) {
            throw new IllegalBlockSizeException(ex.getMessage());
        }
    }
    
    @Override
    protected Key engineUnwrap(final byte[] array, final String algorithm, final int i) throws InvalidKeyException, NoSuchAlgorithmException {
        byte[] encodedKey;
        try {
            if (this.wrapEngine == null) {
                encodedKey = this.engineDoFinal(array, 0, array.length);
            }
            else {
                encodedKey = this.wrapEngine.unwrap(array, 0, array.length);
            }
        }
        catch (final InvalidCipherTextException ex) {
            throw new InvalidKeyException(ex.getMessage());
        }
        catch (final BadPaddingException ex2) {
            throw new InvalidKeyException(ex2.getMessage());
        }
        catch (final IllegalBlockSizeException ex3) {
            throw new InvalidKeyException(ex3.getMessage());
        }
        if (i == 3) {
            return new SecretKeySpec(encodedKey, algorithm);
        }
        if (algorithm.equals("") && i == 2) {
            try {
                final PrivateKeyInfo instance = PrivateKeyInfo.getInstance(encodedKey);
                final PrivateKey privateKey = BouncyCastleProvider.getPrivateKey(instance);
                if (privateKey != null) {
                    return privateKey;
                }
                throw new InvalidKeyException("algorithm " + instance.getPrivateKeyAlgorithm().getAlgorithm() + " not supported");
            }
            catch (final Exception ex4) {
                throw new InvalidKeyException("Invalid key encoding.");
            }
        }
        try {
            final KeyFactory keyFactory = this.helper.createKeyFactory(algorithm);
            if (i == 1) {
                return keyFactory.generatePublic(new X509EncodedKeySpec(encodedKey));
            }
            if (i == 2) {
                return keyFactory.generatePrivate(new PKCS8EncodedKeySpec(encodedKey));
            }
        }
        catch (final NoSuchProviderException ex5) {
            throw new InvalidKeyException("Unknown key type " + ex5.getMessage());
        }
        catch (final InvalidKeySpecException ex6) {
            throw new InvalidKeyException("Unknown key type " + ex6.getMessage());
        }
        throw new InvalidKeyException("Unknown key type " + i);
    }
    
    protected static final class ErasableOutputStream extends ByteArrayOutputStream
    {
        public ErasableOutputStream() {
        }
        
        public byte[] getBuf() {
            return this.buf;
        }
        
        public void erase() {
            Arrays.fill(this.buf, (byte)0);
            this.reset();
        }
    }
    
    protected static class InvalidKeyOrParametersException extends InvalidKeyException
    {
        private final Throwable cause;
        
        InvalidKeyOrParametersException(final String msg, final Throwable cause) {
            super(msg);
            this.cause = cause;
        }
        
        @Override
        public Throwable getCause() {
            return this.cause;
        }
    }
}
