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

package org.bouncycastle.jcajce.provider.asymmetric.rsa;

import org.bouncycastle.crypto.InvalidCipherTextException;
import org.bouncycastle.jcajce.provider.util.BadBlockException;
import org.bouncycastle.crypto.tls.TlsRsaKeyExchange;
import javax.crypto.ShortBufferException;
import javax.crypto.BadPaddingException;
import javax.crypto.IllegalBlockSizeException;
import java.security.spec.InvalidParameterSpecException;
import java.security.InvalidParameterException;
import org.bouncycastle.crypto.CryptoServicesRegistrar;
import org.bouncycastle.crypto.params.ParametersWithRandom;
import org.bouncycastle.crypto.params.RSAKeyParameters;
import java.security.InvalidAlgorithmParameterException;
import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
import java.security.InvalidKeyException;
import java.security.SecureRandom;
import org.bouncycastle.crypto.encodings.ISO9796d1Encoding;
import java.security.NoSuchAlgorithmException;
import org.bouncycastle.util.Strings;
import java.security.interfaces.RSAPublicKey;
import java.security.interfaces.RSAPrivateKey;
import java.security.Key;
import org.bouncycastle.crypto.Digest;
import org.bouncycastle.crypto.encodings.OAEPEncoding;
import javax.crypto.spec.PSource;
import org.bouncycastle.crypto.engines.RSABlindedEngine;
import org.bouncycastle.jcajce.provider.util.DigestFactory;
import java.security.spec.MGF1ParameterSpec;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.OAEPParameterSpec;
import org.bouncycastle.jcajce.util.BCJcaJceHelper;
import org.bouncycastle.crypto.CipherParameters;
import org.bouncycastle.jcajce.spec.TLSRSAPremasterSecretParameterSpec;
import java.security.AlgorithmParameters;
import java.security.spec.AlgorithmParameterSpec;
import org.bouncycastle.crypto.AsymmetricBlockCipher;
import org.bouncycastle.jcajce.util.JcaJceHelper;
import org.bouncycastle.jcajce.provider.asymmetric.util.BaseCipherSpi;

public class CipherSpi extends BaseCipherSpi
{
    private final JcaJceHelper helper;
    private AsymmetricBlockCipher cipher;
    private AlgorithmParameterSpec paramSpec;
    private AlgorithmParameters engineParams;
    private boolean publicKeyOnly;
    private boolean privateKeyOnly;
    private ErasableOutputStream bOut;
    private TLSRSAPremasterSecretParameterSpec tlsRsaSpec;
    private CipherParameters param;
    
    public CipherSpi(final AsymmetricBlockCipher cipher) {
        this.helper = new BCJcaJceHelper();
        this.publicKeyOnly = false;
        this.privateKeyOnly = false;
        this.bOut = new ErasableOutputStream();
        this.tlsRsaSpec = null;
        this.param = null;
        this.cipher = cipher;
    }
    
    public CipherSpi(final OAEPParameterSpec oaepParameterSpec) {
        this.helper = new BCJcaJceHelper();
        this.publicKeyOnly = false;
        this.privateKeyOnly = false;
        this.bOut = new ErasableOutputStream();
        this.tlsRsaSpec = null;
        this.param = null;
        try {
            this.initFromSpec(oaepParameterSpec);
        }
        catch (final NoSuchPaddingException ex) {
            throw new IllegalArgumentException(ex.getMessage());
        }
    }
    
    public CipherSpi(final boolean publicKeyOnly, final boolean privateKeyOnly, final AsymmetricBlockCipher cipher) {
        this.helper = new BCJcaJceHelper();
        this.publicKeyOnly = false;
        this.privateKeyOnly = false;
        this.bOut = new ErasableOutputStream();
        this.tlsRsaSpec = null;
        this.param = null;
        this.publicKeyOnly = publicKeyOnly;
        this.privateKeyOnly = privateKeyOnly;
        this.cipher = cipher;
    }
    
    private void initFromSpec(final OAEPParameterSpec paramSpec) throws NoSuchPaddingException {
        final MGF1ParameterSpec mgf1ParameterSpec = (MGF1ParameterSpec)paramSpec.getMGFParameters();
        final Digest digest = DigestFactory.getDigest(mgf1ParameterSpec.getDigestAlgorithm());
        if (digest == null) {
            throw new NoSuchPaddingException("no match on OAEP constructor for digest algorithm: " + mgf1ParameterSpec.getDigestAlgorithm());
        }
        this.cipher = new OAEPEncoding(new RSABlindedEngine(), digest, ((PSource.PSpecified)paramSpec.getPSource()).getValue());
        this.paramSpec = paramSpec;
    }
    
    @Override
    protected int engineGetBlockSize() {
        try {
            return this.cipher.getInputBlockSize();
        }
        catch (final NullPointerException ex) {
            throw new IllegalStateException("RSA Cipher not initialised");
        }
    }
    
    @Override
    protected int engineGetKeySize(final Key key) {
        if (key instanceof RSAPrivateKey) {
            return ((RSAPrivateKey)key).getModulus().bitLength();
        }
        if (key instanceof RSAPublicKey) {
            return ((RSAPublicKey)key).getModulus().bitLength();
        }
        throw new IllegalArgumentException("not an RSA key!");
    }
    
    @Override
    protected int engineGetOutputSize(final int n) {
        if (this.tlsRsaSpec != null) {
            return 48;
        }
        try {
            return this.cipher.getOutputBlockSize();
        }
        catch (final NullPointerException ex) {
            throw new IllegalStateException("RSA Cipher not initialised");
        }
    }
    
    @Override
    protected AlgorithmParameters engineGetParameters() {
        if (this.engineParams == null && this.paramSpec != null) {
            try {
                (this.engineParams = this.helper.createAlgorithmParameters("OAEP")).init(this.paramSpec);
            }
            catch (final Exception ex) {
                throw new RuntimeException(ex.toString());
            }
        }
        return this.engineParams;
    }
    
    @Override
    protected void engineSetMode(final String str) throws NoSuchAlgorithmException {
        final String upperCase = Strings.toUpperCase(str);
        if (upperCase.equals("NONE") || upperCase.equals("ECB")) {
            return;
        }
        if (upperCase.equals("1")) {
            this.privateKeyOnly = true;
            this.publicKeyOnly = false;
            return;
        }
        if (upperCase.equals("2")) {
            this.privateKeyOnly = false;
            this.publicKeyOnly = true;
            return;
        }
        throw new NoSuchAlgorithmException("can't support mode " + str);
    }
    
    @Override
    protected void engineSetPadding(final String str) throws NoSuchPaddingException {
        final String upperCase = Strings.toUpperCase(str);
        if (upperCase.equals("NOPADDING")) {
            this.cipher = new RSABlindedEngine();
        }
        else if (upperCase.equals("PKCS1PADDING")) {
            this.cipher = new CustomPKCS1Encoding(new RSABlindedEngine());
        }
        else if (upperCase.equals("ISO9796-1PADDING")) {
            this.cipher = new ISO9796d1Encoding(new RSABlindedEngine());
        }
        else if (upperCase.equals("OAEPWITHMD5ANDMGF1PADDING")) {
            this.initFromSpec(new OAEPParameterSpec("MD5", "MGF1", new MGF1ParameterSpec("MD5"), PSource.PSpecified.DEFAULT));
        }
        else if (upperCase.equals("OAEPPADDING")) {
            this.initFromSpec(OAEPParameterSpec.DEFAULT);
        }
        else if (upperCase.equals("OAEPWITHSHA1ANDMGF1PADDING") || upperCase.equals("OAEPWITHSHA-1ANDMGF1PADDING")) {
            this.initFromSpec(OAEPParameterSpec.DEFAULT);
        }
        else if (upperCase.equals("OAEPWITHSHA224ANDMGF1PADDING") || upperCase.equals("OAEPWITHSHA-224ANDMGF1PADDING")) {
            this.initFromSpec(new OAEPParameterSpec("SHA-224", "MGF1", new MGF1ParameterSpec("SHA-224"), PSource.PSpecified.DEFAULT));
        }
        else if (upperCase.equals("OAEPWITHSHA256ANDMGF1PADDING") || upperCase.equals("OAEPWITHSHA-256ANDMGF1PADDING")) {
            this.initFromSpec(new OAEPParameterSpec("SHA-256", "MGF1", MGF1ParameterSpec.SHA256, PSource.PSpecified.DEFAULT));
        }
        else if (upperCase.equals("OAEPWITHSHA384ANDMGF1PADDING") || upperCase.equals("OAEPWITHSHA-384ANDMGF1PADDING")) {
            this.initFromSpec(new OAEPParameterSpec("SHA-384", "MGF1", MGF1ParameterSpec.SHA384, PSource.PSpecified.DEFAULT));
        }
        else if (upperCase.equals("OAEPWITHSHA512ANDMGF1PADDING") || upperCase.equals("OAEPWITHSHA-512ANDMGF1PADDING")) {
            this.initFromSpec(new OAEPParameterSpec("SHA-512", "MGF1", MGF1ParameterSpec.SHA512, PSource.PSpecified.DEFAULT));
        }
        else if (upperCase.equals("OAEPWITHSHA3-224ANDMGF1PADDING")) {
            this.initFromSpec(new OAEPParameterSpec("SHA3-224", "MGF1", new MGF1ParameterSpec("SHA3-224"), PSource.PSpecified.DEFAULT));
        }
        else if (upperCase.equals("OAEPWITHSHA3-256ANDMGF1PADDING")) {
            this.initFromSpec(new OAEPParameterSpec("SHA3-256", "MGF1", new MGF1ParameterSpec("SHA3-256"), PSource.PSpecified.DEFAULT));
        }
        else if (upperCase.equals("OAEPWITHSHA3-384ANDMGF1PADDING")) {
            this.initFromSpec(new OAEPParameterSpec("SHA3-384", "MGF1", new MGF1ParameterSpec("SHA3-384"), PSource.PSpecified.DEFAULT));
        }
        else {
            if (!upperCase.equals("OAEPWITHSHA3-512ANDMGF1PADDING")) {
                throw new NoSuchPaddingException(str + " unavailable with RSA.");
            }
            this.initFromSpec(new OAEPParameterSpec("SHA3-512", "MGF1", new MGF1ParameterSpec("SHA3-512"), PSource.PSpecified.DEFAULT));
        }
    }
    
    @Override
    protected void engineInit(final int i, final Key key, final AlgorithmParameterSpec paramSpec, final SecureRandom secureRandom) throws InvalidKeyException, InvalidAlgorithmParameterException {
        this.tlsRsaSpec = null;
        if (paramSpec == null || paramSpec instanceof OAEPParameterSpec || paramSpec instanceof TLSRSAPremasterSecretParameterSpec) {
            if (key instanceof RSAPublicKey) {
                if (this.privateKeyOnly && i == 1) {
                    throw new InvalidKeyException("mode 1 requires RSAPrivateKey");
                }
                this.param = RSAUtil.generatePublicKeyParameter((RSAPublicKey)key);
            }
            else {
                if (!(key instanceof RSAPrivateKey)) {
                    throw new InvalidKeyException("unknown key type passed to RSA");
                }
                if (this.publicKeyOnly && i == 1) {
                    throw new InvalidKeyException("mode 2 requires RSAPublicKey");
                }
                this.param = RSAUtil.generatePrivateKeyParameter((RSAPrivateKey)key);
            }
            if (paramSpec instanceof OAEPParameterSpec) {
                final OAEPParameterSpec oaepParameterSpec = (OAEPParameterSpec)paramSpec;
                this.paramSpec = paramSpec;
                if (!oaepParameterSpec.getMGFAlgorithm().equalsIgnoreCase("MGF1") && !oaepParameterSpec.getMGFAlgorithm().equals(PKCSObjectIdentifiers.id_mgf1.getId())) {
                    throw new InvalidAlgorithmParameterException("unknown mask generation function specified");
                }
                if (!(oaepParameterSpec.getMGFParameters() instanceof MGF1ParameterSpec)) {
                    throw new InvalidAlgorithmParameterException("unkown MGF parameters");
                }
                final Digest digest = DigestFactory.getDigest(oaepParameterSpec.getDigestAlgorithm());
                if (digest == null) {
                    throw new InvalidAlgorithmParameterException("no match on digest algorithm: " + oaepParameterSpec.getDigestAlgorithm());
                }
                final MGF1ParameterSpec mgf1ParameterSpec = (MGF1ParameterSpec)oaepParameterSpec.getMGFParameters();
                final Digest digest2 = DigestFactory.getDigest(mgf1ParameterSpec.getDigestAlgorithm());
                if (digest2 == null) {
                    throw new InvalidAlgorithmParameterException("no match on MGF digest algorithm: " + mgf1ParameterSpec.getDigestAlgorithm());
                }
                this.cipher = new OAEPEncoding(new RSABlindedEngine(), digest, digest2, ((PSource.PSpecified)oaepParameterSpec.getPSource()).getValue());
            }
            else if (paramSpec instanceof TLSRSAPremasterSecretParameterSpec) {
                if (!(this.param instanceof RSAKeyParameters) || !((RSAKeyParameters)this.param).isPrivate()) {
                    throw new InvalidKeyException("RSA private key required for TLS decryption");
                }
                this.tlsRsaSpec = (TLSRSAPremasterSecretParameterSpec)paramSpec;
            }
            if (secureRandom != null) {
                this.param = new ParametersWithRandom(this.param, secureRandom);
            }
            else {
                this.param = new ParametersWithRandom(this.param, CryptoServicesRegistrar.getSecureRandom());
            }
            this.bOut.reset();
            switch (i) {
                case 1:
                case 3: {
                    this.cipher.init(true, this.param);
                    break;
                }
                case 2:
                case 4: {
                    this.cipher.init(false, this.param);
                    break;
                }
                default: {
                    throw new InvalidParameterException("unknown opmode " + i + " passed to RSA");
                }
            }
            return;
        }
        throw new InvalidAlgorithmParameterException("unknown parameter type: " + paramSpec.getClass().getName());
    }
    
    @Override
    protected void engineInit(final int n, final Key key, final AlgorithmParameters engineParams, final SecureRandom secureRandom) throws InvalidKeyException, InvalidAlgorithmParameterException {
        AlgorithmParameterSpec parameterSpec = null;
        if (engineParams != null) {
            try {
                parameterSpec = engineParams.getParameterSpec(OAEPParameterSpec.class);
            }
            catch (final InvalidParameterSpecException cause) {
                throw new InvalidAlgorithmParameterException("cannot recognise parameters: " + cause.toString(), cause);
            }
        }
        this.engineParams = engineParams;
        this.engineInit(n, key, parameterSpec, 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 cause) {
            throw new InvalidKeyException("Eeeek! " + cause.toString(), cause);
        }
    }
    
    @Override
    protected byte[] engineUpdate(final byte[] b, final int off, final int len) {
        if (len > this.getInputLimit() - this.bOut.size()) {
            throw new ArrayIndexOutOfBoundsException("too much data for RSA block");
        }
        this.bOut.write(b, off, len);
        return null;
    }
    
    @Override
    protected int engineUpdate(final byte[] array, final int n, final int n2, final byte[] array2, final int n3) {
        this.engineUpdate(array, n, n2);
        return 0;
    }
    
    @Override
    protected byte[] engineDoFinal(final byte[] array, final int n, final int n2) throws IllegalBlockSizeException, BadPaddingException {
        if (array != null) {
            this.engineUpdate(array, n, n2);
        }
        return this.getOutput();
    }
    
    @Override
    protected int engineDoFinal(final byte[] array, final int n, final int n2, final byte[] array2, final int n3) throws IllegalBlockSizeException, BadPaddingException, ShortBufferException {
        if (n3 > array2.length - this.engineGetOutputSize((array == null) ? 0 : n2)) {
            throw new ShortBufferException("output buffer too short for input.");
        }
        final byte[] engineDoFinal = this.engineDoFinal(array, n, n2);
        System.arraycopy(engineDoFinal, 0, array2, n3, engineDoFinal.length);
        return engineDoFinal.length;
    }
    
    private int getInputLimit() {
        if (this.tlsRsaSpec != null) {
            return TlsRsaKeyExchange.getInputLimit((RSAKeyParameters)((ParametersWithRandom)this.param).getParameters());
        }
        if (this.cipher instanceof RSABlindedEngine) {
            return this.cipher.getInputBlockSize() + 1;
        }
        return this.cipher.getInputBlockSize();
    }
    
    private byte[] getOutput() throws BadPaddingException {
        try {
            if (this.tlsRsaSpec != null) {
                final ParametersWithRandom parametersWithRandom = (ParametersWithRandom)this.param;
                return TlsRsaKeyExchange.decryptPreMasterSecret(this.bOut.getBuf(), 0, this.bOut.size(), (RSAKeyParameters)parametersWithRandom.getParameters(), this.tlsRsaSpec.getProtocolVersion(), parametersWithRandom.getRandom());
            }
            byte[] processBlock;
            try {
                processBlock = this.cipher.processBlock(this.bOut.getBuf(), 0, this.bOut.size());
            }
            catch (final InvalidCipherTextException ex) {
                throw new BadBlockException("unable to decrypt block", ex);
            }
            catch (final ArrayIndexOutOfBoundsException ex2) {
                throw new BadBlockException("unable to decrypt block", ex2);
            }
            if (processBlock == null) {
                throw new BadBlockException("unable to decrypt block", null);
            }
            return processBlock;
        }
        finally {
            this.bOut.erase();
        }
    }
    
    public static class ISO9796d1Padding extends CipherSpi
    {
        public ISO9796d1Padding() {
            super(new ISO9796d1Encoding(new RSABlindedEngine()));
        }
    }
    
    public static class NoPadding extends CipherSpi
    {
        public NoPadding() {
            super(new RSABlindedEngine());
        }
    }
    
    public static class OAEPPadding extends CipherSpi
    {
        public OAEPPadding() {
            super(OAEPParameterSpec.DEFAULT);
        }
    }
    
    public static class PKCS1v1_5Padding extends CipherSpi
    {
        public PKCS1v1_5Padding() {
            super(new CustomPKCS1Encoding(new RSABlindedEngine()));
        }
    }
    
    public static class PKCS1v1_5Padding_PrivateOnly extends CipherSpi
    {
        public PKCS1v1_5Padding_PrivateOnly() {
            super(false, true, new CustomPKCS1Encoding(new RSABlindedEngine()));
        }
    }
    
    public static class PKCS1v1_5Padding_PublicOnly extends CipherSpi
    {
        public PKCS1v1_5Padding_PublicOnly() {
            super(true, false, new CustomPKCS1Encoding(new RSABlindedEngine()));
        }
    }
}
