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

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

import org.bouncycastle.crypto.InvalidCipherTextException;
import org.bouncycastle.jcajce.provider.util.BadBlockException;
import javax.crypto.ShortBufferException;
import javax.crypto.BadPaddingException;
import javax.crypto.IllegalBlockSizeException;
import org.bouncycastle.crypto.CipherParameters;
import java.security.InvalidParameterException;
import org.bouncycastle.crypto.params.ParametersWithRandom;
import java.security.InvalidAlgorithmParameterException;
import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
import java.security.PrivateKey;
import java.security.InvalidKeyException;
import javax.crypto.interfaces.DHPrivateKey;
import java.security.PublicKey;
import javax.crypto.interfaces.DHPublicKey;
import java.security.SecureRandom;
import org.bouncycastle.crypto.encodings.ISO9796d1Encoding;
import org.bouncycastle.crypto.encodings.PKCS1Encoding;
import java.security.NoSuchAlgorithmException;
import org.bouncycastle.util.Strings;
import javax.crypto.interfaces.DHKey;
import org.bouncycastle.jce.interfaces.ElGamalKey;
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.ElGamalEngine;
import javax.crypto.NoSuchPaddingException;
import org.bouncycastle.jcajce.provider.util.DigestFactory;
import java.security.spec.MGF1ParameterSpec;
import javax.crypto.spec.OAEPParameterSpec;
import java.security.AlgorithmParameters;
import java.security.spec.AlgorithmParameterSpec;
import org.bouncycastle.crypto.AsymmetricBlockCipher;
import org.bouncycastle.jcajce.provider.asymmetric.util.BaseCipherSpi;

public class CipherSpi extends BaseCipherSpi
{
    private AsymmetricBlockCipher cipher;
    private AlgorithmParameterSpec paramSpec;
    private AlgorithmParameters engineParams;
    private ErasableOutputStream bOut;
    
    public CipherSpi(final AsymmetricBlockCipher cipher) {
        this.bOut = new ErasableOutputStream();
        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 ElGamalEngine(), digest, ((PSource.PSpecified)paramSpec.getPSource()).getValue());
        this.paramSpec = paramSpec;
    }
    
    @Override
    protected int engineGetBlockSize() {
        return this.cipher.getInputBlockSize();
    }
    
    @Override
    protected int engineGetKeySize(final Key key) {
        if (key instanceof ElGamalKey) {
            return ((ElGamalKey)key).getParameters().getP().bitLength();
        }
        if (key instanceof DHKey) {
            return ((DHKey)key).getParams().getP().bitLength();
        }
        throw new IllegalArgumentException("not an ElGamal key!");
    }
    
    @Override
    protected int engineGetOutputSize(final int n) {
        return this.cipher.getOutputBlockSize();
    }
    
    @Override
    protected AlgorithmParameters engineGetParameters() {
        if (this.engineParams == null && this.paramSpec != null) {
            try {
                (this.engineParams = this.createParametersInstance("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;
        }
        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 ElGamalEngine();
        }
        else if (upperCase.equals("PKCS1PADDING")) {
            this.cipher = new PKCS1Encoding(new ElGamalEngine());
        }
        else if (upperCase.equals("ISO9796-1PADDING")) {
            this.cipher = new ISO9796d1Encoding(new ElGamalEngine());
        }
        else if (upperCase.equals("OAEPPADDING")) {
            this.initFromSpec(OAEPParameterSpec.DEFAULT);
        }
        else if (upperCase.equals("OAEPWITHMD5ANDMGF1PADDING")) {
            this.initFromSpec(new OAEPParameterSpec("MD5", "MGF1", new MGF1ParameterSpec("MD5"), PSource.PSpecified.DEFAULT));
        }
        else if (upperCase.equals("OAEPWITHSHA1ANDMGF1PADDING")) {
            this.initFromSpec(OAEPParameterSpec.DEFAULT);
        }
        else if (upperCase.equals("OAEPWITHSHA224ANDMGF1PADDING")) {
            this.initFromSpec(new OAEPParameterSpec("SHA-224", "MGF1", new MGF1ParameterSpec("SHA-224"), PSource.PSpecified.DEFAULT));
        }
        else if (upperCase.equals("OAEPWITHSHA256ANDMGF1PADDING")) {
            this.initFromSpec(new OAEPParameterSpec("SHA-256", "MGF1", MGF1ParameterSpec.SHA256, PSource.PSpecified.DEFAULT));
        }
        else if (upperCase.equals("OAEPWITHSHA384ANDMGF1PADDING")) {
            this.initFromSpec(new OAEPParameterSpec("SHA-384", "MGF1", MGF1ParameterSpec.SHA384, PSource.PSpecified.DEFAULT));
        }
        else if (upperCase.equals("OAEPWITHSHA512ANDMGF1PADDING")) {
            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 ElGamal.");
            }
            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 {
        CipherParameters cipherParameters;
        if (key instanceof DHPublicKey) {
            cipherParameters = ElGamalUtil.generatePublicKeyParameter((PublicKey)key);
        }
        else {
            if (!(key instanceof DHPrivateKey)) {
                throw new InvalidKeyException("unknown key type passed to ElGamal");
            }
            cipherParameters = ElGamalUtil.generatePrivateKeyParameter((PrivateKey)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 ElGamalEngine(), digest, digest2, ((PSource.PSpecified)oaepParameterSpec.getPSource()).getValue());
        }
        else if (paramSpec != null) {
            throw new InvalidAlgorithmParameterException("unknown parameter type.");
        }
        if (secureRandom != null) {
            cipherParameters = new ParametersWithRandom(cipherParameters, secureRandom);
        }
        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 to ElGamal");
            }
        }
    }
    
    @Override
    protected void engineInit(final int n, final Key key, final AlgorithmParameters algorithmParameters, final SecureRandom secureRandom) throws InvalidKeyException, InvalidAlgorithmParameterException {
        throw new InvalidAlgorithmParameterException("can't handle parameters in ElGamal");
    }
    
    @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) {
        this.bOut.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) {
        this.bOut.write(b, off, len);
        return 0;
    }
    
    @Override
    protected byte[] engineDoFinal(final byte[] b, final int off, final int len) throws IllegalBlockSizeException, BadPaddingException {
        if (b != null) {
            this.bOut.write(b, off, len);
        }
        if (this.cipher instanceof ElGamalEngine) {
            if (this.bOut.size() > this.cipher.getInputBlockSize() + 1) {
                throw new ArrayIndexOutOfBoundsException("too much data for ElGamal block");
            }
        }
        else if (this.bOut.size() > this.cipher.getInputBlockSize()) {
            throw new ArrayIndexOutOfBoundsException("too much data for ElGamal block");
        }
        return this.getOutput();
    }
    
    @Override
    protected int engineDoFinal(final byte[] b, final int off, final int len, final byte[] array, final int n) throws IllegalBlockSizeException, BadPaddingException, ShortBufferException {
        if (n + this.engineGetOutputSize(len) > array.length) {
            throw new ShortBufferException("output buffer too short for input.");
        }
        if (b != null) {
            this.bOut.write(b, off, len);
        }
        if (this.cipher instanceof ElGamalEngine) {
            if (this.bOut.size() > this.cipher.getInputBlockSize() + 1) {
                throw new ArrayIndexOutOfBoundsException("too much data for ElGamal block");
            }
        }
        else if (this.bOut.size() > this.cipher.getInputBlockSize()) {
            throw new ArrayIndexOutOfBoundsException("too much data for ElGamal block");
        }
        final byte[] output = this.getOutput();
        for (int i = 0; i != output.length; ++i) {
            array[n + i] = output[i];
        }
        return output.length;
    }
    
    private byte[] getOutput() throws BadPaddingException {
        try {
            return 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);
        }
        finally {
            this.bOut.erase();
        }
    }
    
    public static class NoPadding extends CipherSpi
    {
        public NoPadding() {
            super(new ElGamalEngine());
        }
    }
    
    public static class PKCS1v1_5Padding extends CipherSpi
    {
        public PKCS1v1_5Padding() {
            super(new PKCS1Encoding(new ElGamalEngine()));
        }
    }
}
