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

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

import org.bouncycastle.crypto.util.DigestFactory;
import org.bouncycastle.crypto.macs.HMac;
import org.bouncycastle.crypto.Digest;
import javax.crypto.ShortBufferException;
import javax.crypto.IllegalBlockSizeException;
import org.bouncycastle.math.ec.ECPoint;
import org.bouncycastle.math.ec.ECCurve;
import org.bouncycastle.crypto.EphemeralKeyPair;
import org.bouncycastle.crypto.params.ECDomainParameters;
import javax.crypto.BadPaddingException;
import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
import org.bouncycastle.util.Arrays;
import org.bouncycastle.crypto.params.KeyParameter;
import org.bouncycastle.crypto.DerivationParameters;
import org.bouncycastle.crypto.params.KDFParameters;
import org.bouncycastle.crypto.CipherParameters;
import org.bouncycastle.crypto.AsymmetricCipherKeyPairGenerator;
import org.bouncycastle.crypto.generators.EphemeralKeyPairGenerator;
import org.bouncycastle.crypto.params.ECPublicKeyParameters;
import org.bouncycastle.crypto.KeyEncoder;
import org.bouncycastle.crypto.KeyGenerationParameters;
import org.bouncycastle.crypto.params.ECKeyGenerationParameters;
import org.bouncycastle.crypto.generators.ECKeyPairGenerator;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.InvalidKeyException;
import java.security.InvalidAlgorithmParameterException;
import org.bouncycastle.jce.spec.IESParameterSpec;
import javax.crypto.NoSuchPaddingException;
import org.bouncycastle.crypto.params.ECKeyParameters;
import java.security.NoSuchAlgorithmException;
import java.security.spec.AlgorithmParameterSpec;
import org.bouncycastle.jce.interfaces.ECKey;
import java.security.Key;
import org.bouncycastle.jcajce.util.BCJcaJceHelper;
import java.security.SecureRandom;
import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
import org.bouncycastle.jcajce.spec.IESKEMParameterSpec;
import java.security.AlgorithmParameters;
import java.io.ByteArrayOutputStream;
import org.bouncycastle.crypto.engines.IESEngine;
import org.bouncycastle.crypto.Mac;
import org.bouncycastle.crypto.generators.KDF2BytesGenerator;
import org.bouncycastle.crypto.agreement.ECDHCBasicAgreement;
import org.bouncycastle.jcajce.util.JcaJceHelper;
import org.bouncycastle.asn1.x9.X9IntegerConverter;
import org.bouncycastle.jcajce.provider.asymmetric.util.BaseCipherSpi;

public class IESKEMCipher extends BaseCipherSpi
{
    private static final X9IntegerConverter converter;
    private final JcaJceHelper helper;
    private final ECDHCBasicAgreement agreement;
    private final KDF2BytesGenerator kdf;
    private final Mac hMac;
    private final int macKeyLength;
    private final int macLength;
    private int ivLength;
    private IESEngine engine;
    private int state;
    private ByteArrayOutputStream buffer;
    private AlgorithmParameters engineParam;
    private IESKEMParameterSpec engineSpec;
    private AsymmetricKeyParameter key;
    private SecureRandom random;
    private boolean dhaesMode;
    private AsymmetricKeyParameter otherKeyParameter;
    
    public IESKEMCipher(final ECDHCBasicAgreement agreement, final KDF2BytesGenerator kdf, final Mac hMac, final int macKeyLength, final int macLength) {
        this.helper = new BCJcaJceHelper();
        this.state = -1;
        this.buffer = new ByteArrayOutputStream();
        this.engineParam = null;
        this.engineSpec = null;
        this.dhaesMode = false;
        this.otherKeyParameter = null;
        this.agreement = agreement;
        this.kdf = kdf;
        this.hMac = hMac;
        this.macKeyLength = macKeyLength;
        this.macLength = macLength;
    }
    
    public int engineGetBlockSize() {
        return 0;
    }
    
    public int engineGetKeySize(final Key key) {
        if (key instanceof ECKey) {
            return ((ECKey)key).getParameters().getCurve().getFieldSize();
        }
        throw new IllegalArgumentException("not an EC key");
    }
    
    public byte[] engineGetIV() {
        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 {
        throw new NoSuchAlgorithmException("can't support mode " + str);
    }
    
    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 {
        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;
        this.engineSpec = (IESKEMParameterSpec)algorithmParameterSpec;
        if (state == 1 || state == 3) {
            if (!(key instanceof PublicKey)) {
                throw new InvalidKeyException("must be passed recipient's public EC key for encryption");
            }
            this.key = ECUtils.generatePublicKeyParameter((PublicKey)key);
        }
        else {
            if (state != 2 && state != 4) {
                throw new InvalidKeyException("must be passed EC key");
            }
            if (!(key instanceof PrivateKey)) {
                throw new InvalidKeyException("must be passed recipient's private EC key for decryption");
            }
            this.key = ECUtils.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);
        }
        this.buffer.toByteArray();
        this.buffer.reset();
        final ECDomainParameters parameters = ((ECKeyParameters)this.key).getParameters();
        if (this.state == 1 || this.state == 3) {
            final ECKeyPairGenerator ecKeyPairGenerator = new ECKeyPairGenerator();
            ecKeyPairGenerator.init(new ECKeyGenerationParameters(parameters, this.random));
            final EphemeralKeyPair generate = new EphemeralKeyPairGenerator(ecKeyPairGenerator, new KeyEncoder() {
                final /* synthetic */ boolean val$usePointCompression = IESKEMCipher.this.engineSpec.hasUsePointCompression();
                
                @Override
                public byte[] getEncoded(final AsymmetricKeyParameter asymmetricKeyParameter) {
                    return ((ECPublicKeyParameters)asymmetricKeyParameter).getQ().getEncoded(this.val$usePointCompression);
                }
            }).generate();
            this.agreement.init(generate.getKeyPair().getPrivate());
            final byte[] integerToBytes = IESKEMCipher.converter.integerToBytes(this.agreement.calculateAgreement(this.key), IESKEMCipher.converter.getByteLength(parameters.getCurve()));
            final byte[] array = new byte[len + this.macKeyLength];
            this.kdf.init(new KDFParameters(integerToBytes, this.engineSpec.getRecipientInfo()));
            this.kdf.generateBytes(array, 0, array.length);
            final byte[] array2 = new byte[len + this.macLength];
            for (int i = 0; i != len; ++i) {
                array2[i] = (byte)(b[off + i] ^ array[i]);
            }
            final KeyParameter keyParameter = new KeyParameter(array, len, array.length - len);
            this.hMac.init(keyParameter);
            this.hMac.update(array2, 0, len);
            final byte[] array3 = new byte[this.hMac.getMacSize()];
            this.hMac.doFinal(array3, 0);
            Arrays.clear(keyParameter.getKey());
            Arrays.clear(array);
            System.arraycopy(array3, 0, array2, len, this.macLength);
            return Arrays.concatenate(generate.getEncodedPublicKey(), array2);
        }
        if (this.state != 2 && this.state != 4) {
            throw new IllegalStateException("cipher not initialised");
        }
        final ECPrivateKeyParameters ecPrivateKeyParameters = (ECPrivateKeyParameters)this.key;
        final ECCurve curve = ecPrivateKeyParameters.getParameters().getCurve();
        final int n = (curve.getFieldSize() + 7) / 8;
        int n2;
        if (b[off] == 4) {
            n2 = 1 + 2 * n;
        }
        else {
            n2 = 1 + n;
        }
        final int n3 = len - (n2 + this.macLength);
        final ECPoint decodePoint = curve.decodePoint(Arrays.copyOfRange(b, off, off + n2));
        this.agreement.init(this.key);
        final byte[] integerToBytes2 = IESKEMCipher.converter.integerToBytes(this.agreement.calculateAgreement(new ECPublicKeyParameters(decodePoint, ecPrivateKeyParameters.getParameters())), IESKEMCipher.converter.getByteLength(parameters.getCurve()));
        final byte[] array4 = new byte[n3 + this.macKeyLength];
        this.kdf.init(new KDFParameters(integerToBytes2, this.engineSpec.getRecipientInfo()));
        this.kdf.generateBytes(array4, 0, array4.length);
        final byte[] array5 = new byte[n3];
        for (int j = 0; j != array5.length; ++j) {
            array5[j] = (byte)(b[off + n2 + j] ^ array4[j]);
        }
        final KeyParameter keyParameter2 = new KeyParameter(array4, n3, array4.length - n3);
        this.hMac.init(keyParameter2);
        this.hMac.update(b, off + n2, array5.length);
        final byte[] array6 = new byte[this.hMac.getMacSize()];
        this.hMac.doFinal(array6, 0);
        Arrays.clear(keyParameter2.getKey());
        Arrays.clear(array4);
        if (!Arrays.constantTimeAreEqual(this.macLength, array6, 0, b, off + (len - this.macLength))) {
            throw new BadPaddingException("mac field");
        }
        return array5;
    }
    
    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;
    }
    
    static {
        converter = new X9IntegerConverter();
    }
    
    public static class KEM extends IESKEMCipher
    {
        public KEM(final Digest digest, final Digest digest2, final int n, final int n2) {
            super(new ECDHCBasicAgreement(), new KDF2BytesGenerator(digest), new HMac(digest2), n, n2);
        }
    }
    
    public static class KEMwithSHA256 extends KEM
    {
        public KEMwithSHA256() {
            super(DigestFactory.createSHA256(), DigestFactory.createSHA256(), 32, 16);
        }
    }
}
