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

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

import org.bouncycastle.crypto.engines.DESedeEngine;
import org.bouncycastle.crypto.paddings.PaddedBufferedBlockCipher;
import org.bouncycastle.crypto.BlockCipher;
import org.bouncycastle.crypto.modes.CBCBlockCipher;
import org.bouncycastle.crypto.engines.AESEngine;
import org.bouncycastle.crypto.Mac;
import org.bouncycastle.crypto.DerivationFunction;
import org.bouncycastle.crypto.BasicAgreement;
import org.bouncycastle.crypto.macs.HMac;
import org.bouncycastle.crypto.generators.KDF2BytesGenerator;
import org.bouncycastle.crypto.agreement.XDHBasicAgreement;
import org.bouncycastle.crypto.Digest;
import org.bouncycastle.crypto.util.DigestFactory;
import javax.crypto.ShortBufferException;
import javax.crypto.BadPaddingException;
import javax.crypto.IllegalBlockSizeException;
import org.bouncycastle.crypto.AsymmetricCipherKeyPairGenerator;
import org.bouncycastle.crypto.InvalidCipherTextException;
import org.bouncycastle.crypto.KeyParser;
import org.bouncycastle.crypto.parsers.XIESPublicKeyParser;
import org.bouncycastle.crypto.generators.EphemeralKeyPairGenerator;
import org.bouncycastle.crypto.params.X448PublicKeyParameters;
import org.bouncycastle.crypto.KeyEncoder;
import org.bouncycastle.crypto.KeyGenerationParameters;
import org.bouncycastle.crypto.generators.X448KeyPairGenerator;
import org.bouncycastle.crypto.generators.X25519KeyPairGenerator;
import org.bouncycastle.crypto.params.X25519PrivateKeyParameters;
import org.bouncycastle.crypto.params.X25519PublicKeyParameters;
import org.bouncycastle.jcajce.provider.util.BadBlockException;
import org.bouncycastle.crypto.CipherParameters;
import org.bouncycastle.crypto.params.ParametersWithIV;
import org.bouncycastle.crypto.params.IESWithCipherParameters;
import java.security.PrivateKey;
import java.security.PublicKey;
import org.bouncycastle.jcajce.provider.asymmetric.util.IESUtil;
import java.security.InvalidKeyException;
import java.security.InvalidAlgorithmParameterException;
import javax.crypto.NoSuchPaddingException;
import org.bouncycastle.crypto.params.ECKeyParameters;
import java.security.NoSuchAlgorithmException;
import org.bouncycastle.util.Strings;
import java.security.spec.AlgorithmParameterSpec;
import org.bouncycastle.jcajce.interfaces.XDHKey;
import java.security.Key;
import org.bouncycastle.crypto.BufferedBlockCipher;
import org.bouncycastle.jcajce.util.BCJcaJceHelper;
import java.security.SecureRandom;
import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
import org.bouncycastle.jce.spec.IESParameterSpec;
import java.security.AlgorithmParameters;
import java.io.ByteArrayOutputStream;
import org.bouncycastle.crypto.engines.IESEngine;
import org.bouncycastle.jcajce.util.JcaJceHelper;
import org.bouncycastle.jcajce.provider.asymmetric.util.BaseCipherSpi;

public class IESCipher extends BaseCipherSpi
{
    private final JcaJceHelper helper;
    private int ivLength;
    private IESEngine engine;
    private int state;
    private ByteArrayOutputStream buffer;
    private AlgorithmParameters engineParam;
    private IESParameterSpec engineSpec;
    private AsymmetricKeyParameter key;
    private SecureRandom random;
    private boolean dhaesMode;
    private AsymmetricKeyParameter otherKeyParameter;
    
    public IESCipher(final IESEngine engine) {
        this.helper = new BCJcaJceHelper();
        this.state = -1;
        this.buffer = new ByteArrayOutputStream();
        this.engineParam = null;
        this.engineSpec = null;
        this.dhaesMode = false;
        this.otherKeyParameter = null;
        this.engine = engine;
        this.ivLength = 0;
    }
    
    public IESCipher(final IESEngine engine, final int ivLength) {
        this.helper = new BCJcaJceHelper();
        this.state = -1;
        this.buffer = new ByteArrayOutputStream();
        this.engineParam = null;
        this.engineSpec = null;
        this.dhaesMode = false;
        this.otherKeyParameter = null;
        this.engine = engine;
        this.ivLength = ivLength;
    }
    
    public int engineGetBlockSize() {
        final BufferedBlockCipher cipher = this.engine.getCipher();
        return (cipher == null) ? 0 : cipher.getBlockSize();
    }
    
    public int engineGetKeySize(final Key key) {
        if (!(key instanceof XDHKey)) {
            throw new IllegalArgumentException("not an XDH key");
        }
        final String algorithm = key.getAlgorithm();
        if ("X25519".equalsIgnoreCase(algorithm)) {
            return 256;
        }
        if ("X448".equalsIgnoreCase(algorithm)) {
            return 448;
        }
        throw new IllegalArgumentException("unknown XDH key algorithm " + algorithm);
    }
    
    public byte[] engineGetIV() {
        if (this.engineSpec != null) {
            return this.engineSpec.getNonce();
        }
        return null;
    }
    
    public AlgorithmParameters engineGetParameters() {
        if (this.engineParam == null && this.engineSpec != null) {
            try {
                (this.engineParam = this.helper.createAlgorithmParameters("IES")).init(this.engineSpec);
            }
            catch (final Exception ex) {
                throw new RuntimeException(ex.toString());
            }
        }
        return this.engineParam;
    }
    
    public void engineSetMode(final String str) throws NoSuchAlgorithmException {
        final String upperCase = Strings.toUpperCase(str);
        if (upperCase.equals("NONE")) {
            this.dhaesMode = false;
        }
        else {
            if (!upperCase.equals("DHAES")) {
                throw new IllegalArgumentException("can't support mode " + str);
            }
            this.dhaesMode = true;
        }
    }
    
    public int engineGetOutputSize(final int n) {
        if (this.key == null) {
            throw new IllegalStateException("cipher not initialised");
        }
        final int macSize = this.engine.getMac().getMacSize();
        int n2;
        if (this.otherKeyParameter == null) {
            n2 = 2 * ((((ECKeyParameters)this.key).getParameters().getCurve().getFieldSize() + 7) / 8);
        }
        else {
            n2 = 0;
        }
        final int n3 = this.buffer.size() + n;
        int n4;
        if (this.engine.getCipher() == null) {
            n4 = n3;
        }
        else if (this.state == 1 || this.state == 3) {
            n4 = this.engine.getCipher().getOutputSize(n3);
        }
        else {
            if (this.state != 2 && this.state != 4) {
                throw new IllegalStateException("cipher not initialised");
            }
            n4 = this.engine.getCipher().getOutputSize(n3 - macSize - n2);
        }
        if (this.state == 1 || this.state == 3) {
            return macSize + n2 + n4;
        }
        if (this.state == 2 || this.state == 4) {
            return n4;
        }
        throw new IllegalStateException("cipher not initialised");
    }
    
    public void engineSetPadding(final String s) throws NoSuchPaddingException {
        final String upperCase = Strings.toUpperCase(s);
        if (!upperCase.equals("NOPADDING")) {
            if (!upperCase.equals("PKCS5PADDING")) {
                if (!upperCase.equals("PKCS7PADDING")) {
                    throw new NoSuchPaddingException("padding not available with IESCipher");
                }
            }
        }
    }
    
    public void engineInit(final int n, final Key key, final AlgorithmParameters engineParam, final SecureRandom secureRandom) throws InvalidKeyException, InvalidAlgorithmParameterException {
        AlgorithmParameterSpec parameterSpec = null;
        if (engineParam != null) {
            try {
                parameterSpec = engineParam.getParameterSpec(IESParameterSpec.class);
            }
            catch (final Exception ex) {
                throw new InvalidAlgorithmParameterException("cannot recognise parameters: " + ex.toString());
            }
        }
        this.engineParam = engineParam;
        this.engineInit(n, key, parameterSpec, secureRandom);
    }
    
    public void engineInit(final int state, final Key key, final AlgorithmParameterSpec algorithmParameterSpec, final SecureRandom random) throws InvalidAlgorithmParameterException, InvalidKeyException {
        this.otherKeyParameter = null;
        if (algorithmParameterSpec == null && this.ivLength == 0) {
            this.engineSpec = IESUtil.guessParameterSpec(this.engine.getCipher(), null);
        }
        else {
            if (!(algorithmParameterSpec instanceof IESParameterSpec)) {
                throw new InvalidAlgorithmParameterException("must be passed IES parameters");
            }
            this.engineSpec = (IESParameterSpec)algorithmParameterSpec;
        }
        final byte[] nonce = this.engineSpec.getNonce();
        if (this.ivLength != 0 && (nonce == null || nonce.length != this.ivLength)) {
            throw new InvalidAlgorithmParameterException("NONCE in IES Parameters needs to be " + this.ivLength + " bytes long");
        }
        if (state == 1 || state == 3) {
            if (!(key instanceof PublicKey)) {
                throw new InvalidKeyException("must be passed recipient's public XDH key for encryption");
            }
            this.key = EdECUtil.generatePublicKeyParameter((PublicKey)key);
        }
        else {
            if (state != 2 && state != 4) {
                throw new InvalidKeyException("must be passed XDH key");
            }
            if (!(key instanceof PrivateKey)) {
                throw new InvalidKeyException("must be passed recipient's private XDH key for decryption");
            }
            this.key = EdECUtil.generatePrivateKeyParameter((PrivateKey)key);
        }
        this.random = random;
        this.state = state;
        this.buffer.reset();
    }
    
    public 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 IllegalArgumentException("cannot handle supplied parameter spec: " + ex.getMessage());
        }
    }
    
    public byte[] engineUpdate(final byte[] b, final int off, final int len) {
        this.buffer.write(b, off, len);
        return null;
    }
    
    public int engineUpdate(final byte[] b, final int off, final int len, final byte[] array, final int n) {
        this.buffer.write(b, off, len);
        return 0;
    }
    
    public byte[] engineDoFinal(final byte[] b, final int off, final int len) throws IllegalBlockSizeException, BadPaddingException {
        if (len != 0) {
            this.buffer.write(b, off, len);
        }
        final byte[] byteArray = this.buffer.toByteArray();
        this.buffer.reset();
        CipherParameters cipherParameters = new IESWithCipherParameters(this.engineSpec.getDerivationV(), this.engineSpec.getEncodingV(), this.engineSpec.getMacKeySize(), this.engineSpec.getCipherKeySize());
        final byte[] nonce = this.engineSpec.getNonce();
        if (nonce != null) {
            cipherParameters = new ParametersWithIV(cipherParameters, nonce);
        }
        if (this.otherKeyParameter != null) {
            try {
                if (this.state == 1 || this.state == 3) {
                    this.engine.init(true, this.otherKeyParameter, this.key, cipherParameters);
                }
                else {
                    this.engine.init(false, this.key, this.otherKeyParameter, cipherParameters);
                }
                return this.engine.processBlock(byteArray, 0, byteArray.length);
            }
            catch (final Exception ex) {
                throw new BadBlockException("unable to process block", ex);
            }
        }
        final boolean b2 = this.key instanceof X25519PublicKeyParameters || this.key instanceof X25519PrivateKeyParameters;
        final int n = b2 ? 256 : 448;
        if (this.state == 1 || this.state == 3) {
            final AsymmetricCipherKeyPairGenerator asymmetricCipherKeyPairGenerator = b2 ? new X25519KeyPairGenerator() : new X448KeyPairGenerator();
            asymmetricCipherKeyPairGenerator.init(new KeyGenerationParameters(this.random, n));
            final EphemeralKeyPairGenerator ephemeralKeyPairGenerator = new EphemeralKeyPairGenerator(asymmetricCipherKeyPairGenerator, new KeyEncoder() {
                @Override
                public byte[] getEncoded(final AsymmetricKeyParameter asymmetricKeyParameter) {
                    return b2 ? ((X25519PublicKeyParameters)asymmetricKeyParameter).getEncoded() : ((X448PublicKeyParameters)asymmetricKeyParameter).getEncoded();
                }
            });
            try {
                this.engine.init(this.key, cipherParameters, ephemeralKeyPairGenerator);
                return this.engine.processBlock(byteArray, 0, byteArray.length);
            }
            catch (final Exception ex2) {
                throw new BadBlockException("unable to process block", ex2);
            }
        }
        if (this.state != 2) {
            if (this.state != 4) {
                throw new IllegalStateException("cipher not initialised");
            }
        }
        try {
            this.engine.init(this.key, cipherParameters, new XIESPublicKeyParser(b2));
            return this.engine.processBlock(byteArray, 0, byteArray.length);
        }
        catch (final InvalidCipherTextException ex3) {
            throw new BadBlockException("unable to process block", ex3);
        }
        throw new IllegalStateException("cipher not initialised");
    }
    
    public int engineDoFinal(final byte[] array, final int n, final int n2, final byte[] array2, final int n3) throws ShortBufferException, IllegalBlockSizeException, BadPaddingException {
        final byte[] engineDoFinal = this.engineDoFinal(array, n, n2);
        System.arraycopy(engineDoFinal, 0, array2, n3, engineDoFinal.length);
        return engineDoFinal.length;
    }
    
    public static class XIES extends IESCipher
    {
        public XIES() {
            this(DigestFactory.createSHA1(), DigestFactory.createSHA1());
        }
        
        public XIES(final Digest digest, final Digest digest2) {
            super(new IESEngine(new XDHBasicAgreement(), new KDF2BytesGenerator(digest), new HMac(digest2)));
        }
    }
    
    public static class XIESwithAESCBC extends XIESwithCipher
    {
        public XIESwithAESCBC() {
            super(CBCBlockCipher.newInstance(AESEngine.newInstance()), 16);
        }
    }
    
    public static class XIESwithCipher extends IESCipher
    {
        public XIESwithCipher(final BlockCipher blockCipher, final int n) {
            this(blockCipher, n, DigestFactory.createSHA1(), DigestFactory.createSHA1());
        }
        
        public XIESwithCipher(final BlockCipher blockCipher, final int n, final Digest digest, final Digest digest2) {
            super(new IESEngine(new XDHBasicAgreement(), new KDF2BytesGenerator(digest), new HMac(digest2), new PaddedBufferedBlockCipher(blockCipher)), n);
        }
    }
    
    public static class XIESwithDESedeCBC extends XIESwithCipher
    {
        public XIESwithDESedeCBC() {
            super(CBCBlockCipher.newInstance(new DESedeEngine()), 8);
        }
    }
    
    public static class XIESwithSHA256 extends XIES
    {
        public XIESwithSHA256() {
            super(DigestFactory.createSHA256(), DigestFactory.createSHA256());
        }
    }
    
    public static class XIESwithSHA256andAESCBC extends XIESwithCipher
    {
        public XIESwithSHA256andAESCBC() {
            super(CBCBlockCipher.newInstance(AESEngine.newInstance()), 16, DigestFactory.createSHA256(), DigestFactory.createSHA256());
        }
    }
    
    public static class XIESwithSHA256andDESedeCBC extends XIESwithCipher
    {
        public XIESwithSHA256andDESedeCBC() {
            super(CBCBlockCipher.newInstance(new DESedeEngine()), 8, DigestFactory.createSHA256(), DigestFactory.createSHA256());
        }
    }
    
    public static class XIESwithSHA384 extends XIES
    {
        public XIESwithSHA384() {
            super(DigestFactory.createSHA384(), DigestFactory.createSHA384());
        }
    }
    
    public static class XIESwithSHA384andAESCBC extends XIESwithCipher
    {
        public XIESwithSHA384andAESCBC() {
            super(CBCBlockCipher.newInstance(AESEngine.newInstance()), 16, DigestFactory.createSHA384(), DigestFactory.createSHA384());
        }
    }
    
    public static class XIESwithSHA384andDESedeCBC extends XIESwithCipher
    {
        public XIESwithSHA384andDESedeCBC() {
            super(CBCBlockCipher.newInstance(new DESedeEngine()), 8, DigestFactory.createSHA384(), DigestFactory.createSHA384());
        }
    }
    
    public static class XIESwithSHA512 extends XIES
    {
        public XIESwithSHA512() {
            super(DigestFactory.createSHA512(), DigestFactory.createSHA512());
        }
    }
    
    public static class XIESwithSHA512andAESCBC extends XIESwithCipher
    {
        public XIESwithSHA512andAESCBC() {
            super(CBCBlockCipher.newInstance(AESEngine.newInstance()), 16, DigestFactory.createSHA512(), DigestFactory.createSHA512());
        }
    }
    
    public static class XIESwithSHA512andDESedeCBC extends XIESwithCipher
    {
        public XIESwithSHA512andDESedeCBC() {
            super(CBCBlockCipher.newInstance(new DESedeEngine()), 8, DigestFactory.createSHA512(), DigestFactory.createSHA512());
        }
    }
}
