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

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

import org.bouncycastle.crypto.agreement.MQVBasicAgreement;
import org.bouncycastle.crypto.agreement.kdf.DHKEKGenerator;
import org.bouncycastle.crypto.generators.KDF2BytesGenerator;
import org.bouncycastle.crypto.agreement.kdf.ConcatenationKDFGenerator;
import org.bouncycastle.crypto.util.DigestFactory;
import org.bouncycastle.jcajce.spec.DHDomainParameterSpec;
import org.bouncycastle.crypto.params.DHPublicKeyParameters;
import org.bouncycastle.crypto.params.DHParameters;
import org.bouncycastle.crypto.params.DHPrivateKeyParameters;
import org.bouncycastle.jcajce.spec.UserKeyingMaterialSpec;
import org.bouncycastle.crypto.params.DHMQVPrivateParameters;
import org.bouncycastle.crypto.params.DHUPrivateParameters;
import java.security.PrivateKey;
import java.security.InvalidAlgorithmParameterException;
import javax.crypto.spec.DHParameterSpec;
import javax.crypto.interfaces.DHPrivateKey;
import java.security.SecureRandom;
import java.security.spec.AlgorithmParameterSpec;
import java.security.NoSuchAlgorithmException;
import javax.crypto.spec.SecretKeySpec;
import javax.crypto.SecretKey;
import javax.crypto.ShortBufferException;
import org.bouncycastle.crypto.params.DHMQVPublicParameters;
import org.bouncycastle.crypto.CipherParameters;
import org.bouncycastle.crypto.params.DHUPublicParameters;
import java.security.PublicKey;
import java.security.InvalidKeyException;
import javax.crypto.interfaces.DHPublicKey;
import java.security.Key;
import org.bouncycastle.util.BigIntegers;
import org.bouncycastle.crypto.DerivationFunction;
import org.bouncycastle.jcajce.spec.MQVParameterSpec;
import org.bouncycastle.jcajce.spec.DHUParameterSpec;
import org.bouncycastle.crypto.BasicAgreement;
import org.bouncycastle.crypto.agreement.DHUnifiedAgreement;
import java.math.BigInteger;
import org.bouncycastle.jcajce.provider.asymmetric.util.BaseAgreementSpi;

public class KeyAgreementSpi extends BaseAgreementSpi
{
    private static final BigInteger ONE;
    private static final BigInteger TWO;
    private final DHUnifiedAgreement unifiedAgreement;
    private final BasicAgreement mqvAgreement;
    private DHUParameterSpec dheParameters;
    private MQVParameterSpec mqvParameters;
    private BigInteger x;
    private BigInteger p;
    private BigInteger g;
    private byte[] result;
    
    public KeyAgreementSpi() {
        this("Diffie-Hellman", null);
    }
    
    public KeyAgreementSpi(final String s, final DerivationFunction derivationFunction) {
        super(s, derivationFunction);
        this.unifiedAgreement = null;
        this.mqvAgreement = null;
    }
    
    public KeyAgreementSpi(final String s, final DHUnifiedAgreement unifiedAgreement, final DerivationFunction derivationFunction) {
        super(s, derivationFunction);
        this.unifiedAgreement = unifiedAgreement;
        this.mqvAgreement = null;
    }
    
    public KeyAgreementSpi(final String s, final BasicAgreement mqvAgreement, final DerivationFunction derivationFunction) {
        super(s, derivationFunction);
        this.unifiedAgreement = null;
        this.mqvAgreement = mqvAgreement;
    }
    
    protected byte[] bigIntToBytes(final BigInteger bigInteger) {
        return BigIntegers.asUnsignedByteArray((this.p.bitLength() + 7) / 8, bigInteger);
    }
    
    @Override
    protected Key engineDoPhase(final Key key, final boolean b) throws InvalidKeyException, IllegalStateException {
        if (this.x == null) {
            throw new IllegalStateException("Diffie-Hellman not initialised.");
        }
        if (!(key instanceof DHPublicKey)) {
            throw new InvalidKeyException("DHKeyAgreement doPhase requires DHPublicKey");
        }
        final DHPublicKey dhPublicKey = (DHPublicKey)key;
        if (!dhPublicKey.getParams().getG().equals(this.g) || !dhPublicKey.getParams().getP().equals(this.p)) {
            throw new InvalidKeyException("DHPublicKey not for this KeyAgreement!");
        }
        final BigInteger y = ((DHPublicKey)key).getY();
        if (y == null || y.compareTo(KeyAgreementSpi.TWO) < 0 || y.compareTo(this.p.subtract(KeyAgreementSpi.ONE)) >= 0) {
            throw new InvalidKeyException("Invalid DH PublicKey");
        }
        if (this.unifiedAgreement != null) {
            if (!b) {
                throw new IllegalStateException("unified Diffie-Hellman can use only two key pairs");
            }
            this.result = this.unifiedAgreement.calculateAgreement(new DHUPublicParameters(this.generatePublicKeyParameter((PublicKey)key), this.generatePublicKeyParameter(this.dheParameters.getOtherPartyEphemeralKey())));
            return null;
        }
        else if (this.mqvAgreement != null) {
            if (!b) {
                throw new IllegalStateException("MQV Diffie-Hellman can use only two key pairs");
            }
            this.result = this.bigIntToBytes(this.mqvAgreement.calculateAgreement(new DHMQVPublicParameters(this.generatePublicKeyParameter((PublicKey)key), this.generatePublicKeyParameter(this.mqvParameters.getOtherPartyEphemeralKey()))));
            return null;
        }
        else {
            final BigInteger modPow = y.modPow(this.x, this.p);
            if (modPow.compareTo(KeyAgreementSpi.ONE) == 0) {
                throw new InvalidKeyException("Shared key can't be 1");
            }
            this.result = this.bigIntToBytes(modPow);
            if (b) {
                return null;
            }
            return new BCDHPublicKey(modPow, dhPublicKey.getParams());
        }
    }
    
    @Override
    protected byte[] engineGenerateSecret() throws IllegalStateException {
        if (this.x == null) {
            throw new IllegalStateException("Diffie-Hellman not initialised.");
        }
        return super.engineGenerateSecret();
    }
    
    @Override
    protected int engineGenerateSecret(final byte[] array, final int n) throws IllegalStateException, ShortBufferException {
        if (this.x == null) {
            throw new IllegalStateException("Diffie-Hellman not initialised.");
        }
        return super.engineGenerateSecret(array, n);
    }
    
    @Override
    protected SecretKey engineGenerateSecret(final String algorithm) throws NoSuchAlgorithmException {
        if (this.x == null) {
            throw new IllegalStateException("Diffie-Hellman not initialised.");
        }
        if (algorithm.equals("TlsPremasterSecret")) {
            return new SecretKeySpec(BaseAgreementSpi.trimZeroes(this.result), algorithm);
        }
        return super.engineGenerateSecret(algorithm);
    }
    
    @Override
    protected void doInitFromKey(final Key key, final AlgorithmParameterSpec algorithmParameterSpec, final SecureRandom secureRandom) throws InvalidKeyException, InvalidAlgorithmParameterException {
        if (!(key instanceof DHPrivateKey)) {
            throw new InvalidKeyException("DHKeyAgreement requires DHPrivateKey for initialisation");
        }
        final DHPrivateKey dhPrivateKey = (DHPrivateKey)key;
        if (algorithmParameterSpec != null) {
            if (algorithmParameterSpec instanceof DHParameterSpec) {
                final DHParameterSpec dhParameterSpec = (DHParameterSpec)algorithmParameterSpec;
                this.p = dhParameterSpec.getP();
                this.g = dhParameterSpec.getG();
                this.dheParameters = null;
                this.ukmParameters = null;
            }
            else if (algorithmParameterSpec instanceof DHUParameterSpec) {
                if (this.unifiedAgreement == null) {
                    throw new InvalidAlgorithmParameterException("agreement algorithm not DHU based");
                }
                this.p = dhPrivateKey.getParams().getP();
                this.g = dhPrivateKey.getParams().getG();
                this.dheParameters = (DHUParameterSpec)algorithmParameterSpec;
                this.ukmParameters = ((DHUParameterSpec)algorithmParameterSpec).getUserKeyingMaterial();
                if (this.dheParameters.getEphemeralPublicKey() != null) {
                    this.unifiedAgreement.init(new DHUPrivateParameters(this.generatePrivateKeyParameter(dhPrivateKey), this.generatePrivateKeyParameter(this.dheParameters.getEphemeralPrivateKey()), this.generatePublicKeyParameter(this.dheParameters.getEphemeralPublicKey())));
                }
                else {
                    this.unifiedAgreement.init(new DHUPrivateParameters(this.generatePrivateKeyParameter(dhPrivateKey), this.generatePrivateKeyParameter(this.dheParameters.getEphemeralPrivateKey())));
                }
            }
            else if (algorithmParameterSpec instanceof MQVParameterSpec) {
                if (this.mqvAgreement == null) {
                    throw new InvalidAlgorithmParameterException("agreement algorithm not MQV based");
                }
                this.p = dhPrivateKey.getParams().getP();
                this.g = dhPrivateKey.getParams().getG();
                this.mqvParameters = (MQVParameterSpec)algorithmParameterSpec;
                this.ukmParameters = ((MQVParameterSpec)algorithmParameterSpec).getUserKeyingMaterial();
                if (this.mqvParameters.getEphemeralPublicKey() != null) {
                    this.mqvAgreement.init(new DHMQVPrivateParameters(this.generatePrivateKeyParameter(dhPrivateKey), this.generatePrivateKeyParameter(this.mqvParameters.getEphemeralPrivateKey()), this.generatePublicKeyParameter(this.mqvParameters.getEphemeralPublicKey())));
                }
                else {
                    this.mqvAgreement.init(new DHMQVPrivateParameters(this.generatePrivateKeyParameter(dhPrivateKey), this.generatePrivateKeyParameter(this.mqvParameters.getEphemeralPrivateKey())));
                }
            }
            else {
                if (!(algorithmParameterSpec instanceof UserKeyingMaterialSpec)) {
                    throw new InvalidAlgorithmParameterException("DHKeyAgreement only accepts DHParameterSpec");
                }
                if (this.kdf == null) {
                    throw new InvalidAlgorithmParameterException("no KDF specified for UserKeyingMaterialSpec");
                }
                this.p = dhPrivateKey.getParams().getP();
                this.g = dhPrivateKey.getParams().getG();
                this.dheParameters = null;
                this.ukmParameters = ((UserKeyingMaterialSpec)algorithmParameterSpec).getUserKeyingMaterial();
            }
        }
        else {
            this.p = dhPrivateKey.getParams().getP();
            this.g = dhPrivateKey.getParams().getG();
        }
        this.x = dhPrivateKey.getX();
        this.result = this.bigIntToBytes(this.x);
    }
    
    @Override
    protected void engineInit(final Key key, final SecureRandom secureRandom) throws InvalidKeyException {
        if (!(key instanceof DHPrivateKey)) {
            throw new InvalidKeyException("DHKeyAgreement requires DHPrivateKey");
        }
        final DHPrivateKey dhPrivateKey = (DHPrivateKey)key;
        this.p = dhPrivateKey.getParams().getP();
        this.g = dhPrivateKey.getParams().getG();
        this.x = dhPrivateKey.getX();
        this.result = this.bigIntToBytes(this.x);
    }
    
    @Override
    protected byte[] doCalcSecret() {
        return this.result;
    }
    
    private DHPrivateKeyParameters generatePrivateKeyParameter(final PrivateKey privateKey) throws InvalidKeyException {
        if (!(privateKey instanceof DHPrivateKey)) {
            throw new InvalidKeyException("private key not a DHPrivateKey");
        }
        if (privateKey instanceof BCDHPrivateKey) {
            return ((BCDHPrivateKey)privateKey).engineGetKeyParameters();
        }
        final DHPrivateKey dhPrivateKey = (DHPrivateKey)privateKey;
        final DHParameterSpec params = dhPrivateKey.getParams();
        return new DHPrivateKeyParameters(dhPrivateKey.getX(), new DHParameters(params.getP(), params.getG(), null, params.getL()));
    }
    
    private DHPublicKeyParameters generatePublicKeyParameter(final PublicKey publicKey) throws InvalidKeyException {
        if (!(publicKey instanceof DHPublicKey)) {
            throw new InvalidKeyException("public key not a DHPublicKey");
        }
        if (publicKey instanceof BCDHPublicKey) {
            return ((BCDHPublicKey)publicKey).engineGetKeyParameters();
        }
        final DHPublicKey dhPublicKey = (DHPublicKey)publicKey;
        final DHParameterSpec params = dhPublicKey.getParams();
        if (params instanceof DHDomainParameterSpec) {
            return new DHPublicKeyParameters(dhPublicKey.getY(), ((DHDomainParameterSpec)params).getDomainParameters());
        }
        return new DHPublicKeyParameters(dhPublicKey.getY(), new DHParameters(params.getP(), params.getG(), null, params.getL()));
    }
    
    static {
        ONE = BigInteger.valueOf(1L);
        TWO = BigInteger.valueOf(2L);
    }
    
    public static class DHUwithSHA1CKDF extends KeyAgreementSpi
    {
        public DHUwithSHA1CKDF() {
            super("DHUwithSHA1CKDF", new DHUnifiedAgreement(), new ConcatenationKDFGenerator(DigestFactory.createSHA1()));
        }
    }
    
    public static class DHUwithSHA1KDF extends KeyAgreementSpi
    {
        public DHUwithSHA1KDF() {
            super("DHUwithSHA1KDF", new DHUnifiedAgreement(), new KDF2BytesGenerator(DigestFactory.createSHA1()));
        }
    }
    
    public static class DHUwithSHA224CKDF extends KeyAgreementSpi
    {
        public DHUwithSHA224CKDF() {
            super("DHUwithSHA224CKDF", new DHUnifiedAgreement(), new ConcatenationKDFGenerator(DigestFactory.createSHA224()));
        }
    }
    
    public static class DHUwithSHA224KDF extends KeyAgreementSpi
    {
        public DHUwithSHA224KDF() {
            super("DHUwithSHA224KDF", new DHUnifiedAgreement(), new KDF2BytesGenerator(DigestFactory.createSHA224()));
        }
    }
    
    public static class DHUwithSHA256CKDF extends KeyAgreementSpi
    {
        public DHUwithSHA256CKDF() {
            super("DHUwithSHA256CKDF", new DHUnifiedAgreement(), new ConcatenationKDFGenerator(DigestFactory.createSHA256()));
        }
    }
    
    public static class DHUwithSHA256KDF extends KeyAgreementSpi
    {
        public DHUwithSHA256KDF() {
            super("DHUwithSHA256KDF", new DHUnifiedAgreement(), new KDF2BytesGenerator(DigestFactory.createSHA256()));
        }
    }
    
    public static class DHUwithSHA384CKDF extends KeyAgreementSpi
    {
        public DHUwithSHA384CKDF() {
            super("DHUwithSHA384CKDF", new DHUnifiedAgreement(), new ConcatenationKDFGenerator(DigestFactory.createSHA384()));
        }
    }
    
    public static class DHUwithSHA384KDF extends KeyAgreementSpi
    {
        public DHUwithSHA384KDF() {
            super("DHUwithSHA384KDF", new DHUnifiedAgreement(), new KDF2BytesGenerator(DigestFactory.createSHA384()));
        }
    }
    
    public static class DHUwithSHA512CKDF extends KeyAgreementSpi
    {
        public DHUwithSHA512CKDF() {
            super("DHUwithSHA512CKDF", new DHUnifiedAgreement(), new ConcatenationKDFGenerator(DigestFactory.createSHA512()));
        }
    }
    
    public static class DHUwithSHA512KDF extends KeyAgreementSpi
    {
        public DHUwithSHA512KDF() {
            super("DHUwithSHA512KDF", new DHUnifiedAgreement(), new KDF2BytesGenerator(DigestFactory.createSHA512()));
        }
    }
    
    public static class DHwithRFC2631KDF extends KeyAgreementSpi
    {
        public DHwithRFC2631KDF() {
            super("DHwithRFC2631KDF", new DHKEKGenerator(DigestFactory.createSHA1()));
        }
    }
    
    public static class DHwithSHA1CKDF extends KeyAgreementSpi
    {
        public DHwithSHA1CKDF() {
            super("DHwithSHA1CKDF", new ConcatenationKDFGenerator(DigestFactory.createSHA1()));
        }
    }
    
    public static class DHwithSHA1KDF extends KeyAgreementSpi
    {
        public DHwithSHA1KDF() {
            super("DHwithSHA1CKDF", new KDF2BytesGenerator(DigestFactory.createSHA1()));
        }
    }
    
    public static class DHwithSHA224CKDF extends KeyAgreementSpi
    {
        public DHwithSHA224CKDF() {
            super("DHwithSHA224CKDF", new ConcatenationKDFGenerator(DigestFactory.createSHA224()));
        }
    }
    
    public static class DHwithSHA224KDF extends KeyAgreementSpi
    {
        public DHwithSHA224KDF() {
            super("DHwithSHA224CKDF", new KDF2BytesGenerator(DigestFactory.createSHA224()));
        }
    }
    
    public static class DHwithSHA256CKDF extends KeyAgreementSpi
    {
        public DHwithSHA256CKDF() {
            super("DHwithSHA256CKDF", new ConcatenationKDFGenerator(DigestFactory.createSHA256()));
        }
    }
    
    public static class DHwithSHA256KDF extends KeyAgreementSpi
    {
        public DHwithSHA256KDF() {
            super("DHwithSHA256CKDF", new KDF2BytesGenerator(DigestFactory.createSHA256()));
        }
    }
    
    public static class DHwithSHA384CKDF extends KeyAgreementSpi
    {
        public DHwithSHA384CKDF() {
            super("DHwithSHA384CKDF", new ConcatenationKDFGenerator(DigestFactory.createSHA384()));
        }
    }
    
    public static class DHwithSHA384KDF extends KeyAgreementSpi
    {
        public DHwithSHA384KDF() {
            super("DHwithSHA384KDF", new KDF2BytesGenerator(DigestFactory.createSHA384()));
        }
    }
    
    public static class DHwithSHA512CKDF extends KeyAgreementSpi
    {
        public DHwithSHA512CKDF() {
            super("DHwithSHA512CKDF", new ConcatenationKDFGenerator(DigestFactory.createSHA512()));
        }
    }
    
    public static class DHwithSHA512KDF extends KeyAgreementSpi
    {
        public DHwithSHA512KDF() {
            super("DHwithSHA512KDF", new KDF2BytesGenerator(DigestFactory.createSHA512()));
        }
    }
    
    public static class MQVwithSHA1CKDF extends KeyAgreementSpi
    {
        public MQVwithSHA1CKDF() {
            super("MQVwithSHA1CKDF", new MQVBasicAgreement(), new ConcatenationKDFGenerator(DigestFactory.createSHA1()));
        }
    }
    
    public static class MQVwithSHA1KDF extends KeyAgreementSpi
    {
        public MQVwithSHA1KDF() {
            super("MQVwithSHA1KDF", new MQVBasicAgreement(), new KDF2BytesGenerator(DigestFactory.createSHA1()));
        }
    }
    
    public static class MQVwithSHA224CKDF extends KeyAgreementSpi
    {
        public MQVwithSHA224CKDF() {
            super("MQVwithSHA224CKDF", new MQVBasicAgreement(), new ConcatenationKDFGenerator(DigestFactory.createSHA224()));
        }
    }
    
    public static class MQVwithSHA224KDF extends KeyAgreementSpi
    {
        public MQVwithSHA224KDF() {
            super("MQVwithSHA224KDF", new MQVBasicAgreement(), new KDF2BytesGenerator(DigestFactory.createSHA224()));
        }
    }
    
    public static class MQVwithSHA256CKDF extends KeyAgreementSpi
    {
        public MQVwithSHA256CKDF() {
            super("MQVwithSHA256CKDF", new MQVBasicAgreement(), new ConcatenationKDFGenerator(DigestFactory.createSHA256()));
        }
    }
    
    public static class MQVwithSHA256KDF extends KeyAgreementSpi
    {
        public MQVwithSHA256KDF() {
            super("MQVwithSHA256KDF", new MQVBasicAgreement(), new KDF2BytesGenerator(DigestFactory.createSHA256()));
        }
    }
    
    public static class MQVwithSHA384CKDF extends KeyAgreementSpi
    {
        public MQVwithSHA384CKDF() {
            super("MQVwithSHA384CKDF", new MQVBasicAgreement(), new ConcatenationKDFGenerator(DigestFactory.createSHA384()));
        }
    }
    
    public static class MQVwithSHA384KDF extends KeyAgreementSpi
    {
        public MQVwithSHA384KDF() {
            super("MQVwithSHA384KDF", new MQVBasicAgreement(), new KDF2BytesGenerator(DigestFactory.createSHA384()));
        }
    }
    
    public static class MQVwithSHA512CKDF extends KeyAgreementSpi
    {
        public MQVwithSHA512CKDF() {
            super("MQVwithSHA512CKDF", new MQVBasicAgreement(), new ConcatenationKDFGenerator(DigestFactory.createSHA512()));
        }
    }
    
    public static class MQVwithSHA512KDF extends KeyAgreementSpi
    {
        public MQVwithSHA512KDF() {
            super("MQVwithSHA512KDF", new MQVBasicAgreement(), new KDF2BytesGenerator(DigestFactory.createSHA512()));
        }
    }
}
